Assault Operatives (#12126)
* Initial commit * 0 * shuttle and maps * Equipping * Minor fixes, gyrojet removed, elite suits removed * E * reviews * Update base_alarm.dm * nnfghghg * more fix * Update dynamic_rulsesets_roundstart.dm * Update modular_skyrat/modules/assault_operatives/code/assault_operatives.dm Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> * Update modular_skyrat/modules/assault_operatives/code/vending_machine.dm Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> * fyux * Update assault_operatives_outfits.dm * E * Update modular_skyrat/modules/assault_operatives/code/goldeneye.dm Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> * Update modular_skyrat/modules/assault_operatives/code/interrogator.dm Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> * Update modular_skyrat/modules/assault_operatives/code/interrogator.dm Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> * more review * E * uber update * Update AntagInfoAssaultops.tsx * Update AntagInfoAssaultops.tsx * nyooom * E * ICARUS SUNBEAM * fioxes * Update sunbeam.dm * Update goldeneye.dm * Update vending_machine.dm * Update assault_operatives_outfits.dm * Update goldeneye.dm * e * eee * Update assault_operatives_outfits.dm * Update assault_operatives.dm * Update assault_operatives.dm * Update goldeneye_cruiser.dmm * fix * 0 * haha funneee * armament system * more updates * f * e * 0 * e * more based * 0 * e * CQC PLUS CAN SUCK MY COCK * Update __armament_bodyarmor.dm * Update assault_operatives.dm * e * MG9 balance * Update armament_utility.dm * Update sunbeam.dm * fixes * Update CentCom_skyrat_z2.dmm * Update armament_station.dm * Update modular_skyrat/modules/armaments/code/armament_entries.dm Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> * Update modular_skyrat/modules/armaments/code/armament_entries.dm Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> * Update modular_skyrat/modules/assault_operatives/code/assault_operatives.dm Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> * e * Update modular_skyrat/modules/assault_operatives/code/assault_operatives.dm Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com> * Update assaultops_armament_station.dm * reviuew * Update misc_items.dm * Update vending_machine.dm Co-authored-by: GoldenAlpharex <58045821+GoldenAlpharex@users.noreply.github.com> Co-authored-by: Zonespace <41448081+Zonespace27@users.noreply.github.com>
3123
_maps/shuttles/skyrat/goldeneye_cruiser.dmm
Normal file
@@ -39,8 +39,10 @@
|
||||
#define ROLE_SPACE_DRAGON "Space Dragon"
|
||||
#define ROLE_SPIDER "Spider"
|
||||
#define ROLE_WIZARD_MIDROUND "Wizard (Midround)"
|
||||
//SKYRAT EDIT: Cortical Borers
|
||||
// SKYRAT EDIT ADDITION
|
||||
#define ROLE_BORER "Borer"
|
||||
#define ROLE_ASSAULT_OPERATIVE "Assault Operative"
|
||||
// SKYRAT EDIT END
|
||||
|
||||
// Latejoin roles
|
||||
#define ROLE_HERETIC_SMUGGLER "Heretic Smuggler"
|
||||
@@ -120,6 +122,9 @@ GLOBAL_LIST_INIT(special_roles, list(
|
||||
ROLE_THIEF = 0,
|
||||
ROLE_TRAITOR = 0,
|
||||
ROLE_WIZARD = 14,
|
||||
// SKYRAT EDIT ADDITION
|
||||
ROLE_ASSAULT_OPERATIVE = 14,
|
||||
// SKYRAT EDIT END
|
||||
|
||||
// Midround
|
||||
ROLE_ABDUCTOR = 0,
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
#define ANNOUNCER_ERTYES "announcer_ertyes"
|
||||
#define ANNOUNCER_MUTANTS "announcer_mutants"
|
||||
#define ANNOUNCER_KLAXON "announcer_klaxon"
|
||||
#define ANNOUNCER_ICARUS "announcer_icarus"
|
||||
//SKYRAT EDIT END
|
||||
|
||||
|
||||
@@ -188,7 +189,8 @@ GLOBAL_LIST_INIT(announcer_keys, list(
|
||||
ANNOUNCER_SPOOKY,
|
||||
ANNOUNCER_ERTYES,
|
||||
ANNOUNCER_MUTANTS,
|
||||
ANNOUNCER_KLAXON
|
||||
ANNOUNCER_KLAXON,
|
||||
ANNOUNCER_ICARUS,
|
||||
//SKYRAT EDIT END
|
||||
))
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
GLOBAL_LIST_EMPTY(assault_operative_start)
|
||||
17
code/__DEFINES/~skyrat_defines/antagonists.dm
Normal file
@@ -0,0 +1,17 @@
|
||||
/// Full operative win - goldeneye activated and all ops alive
|
||||
#define ASSAULT_RESULT_WIN 0
|
||||
/// Partial operative win - Goldeneye was activated and some ops alive
|
||||
#define ASSAULT_RESULT_PARTIAL_WIN 1
|
||||
/// Stalemate - Goldeneye not activated and ops still alive
|
||||
#define ASSAULT_RESULT_STALEMATE 2
|
||||
/// Hearty win - Goldeneye activated but no ops alive
|
||||
#define ASSAULT_RESULT_HEARTY_WIN 3
|
||||
/// Crew win - Goldeneye not activated and no ops alive
|
||||
#define ASSAULT_RESULT_LOSS 4
|
||||
|
||||
// Conditions for ops to be considered alive
|
||||
#define ASSAULTOPS_ALL_DEAD 0
|
||||
#define ASSAULTOPS_PARTLY_DEAD 1
|
||||
#define ASSAULTOPS_ALL_ALIVE 2
|
||||
|
||||
#define GOLDENEYE_REQUIRED_KEYS_MAXIMUM 3
|
||||
10
code/__DEFINES/~skyrat_defines/armaments.dm
Normal file
@@ -0,0 +1,10 @@
|
||||
// Armament categories
|
||||
#define ARMAMENT_CATEGORY_STANDARD "Standard Equipment"
|
||||
#define ARMAMENT_CATEGORY_STANDARD_LIMIT 1
|
||||
|
||||
// Armament subcategories
|
||||
#define ARMAMENT_SUBCATEGORY_NONE "Uncategorised"
|
||||
|
||||
/// To identify the limit of the category type in the associative list. Techical stuff.
|
||||
#define CATEGORY_LIMIT "Category Limit"
|
||||
#define CATEGORY_ENTRY "Category Entry"
|
||||
1
code/__DEFINES/~skyrat_defines/pinpointers.dm
Normal file
@@ -0,0 +1 @@
|
||||
#define TRACK_GOLDENEYE 4
|
||||
2
code/__DEFINES/~skyrat_defines/turfs.dm
Normal file
@@ -0,0 +1,2 @@
|
||||
/// A turf flag, much higher than what the other turf flags are at because I don't want to cause conflicts by accident.
|
||||
#define CAN_DECAY_BREAK_1 (1<<23)
|
||||
@@ -274,6 +274,10 @@ GLOBAL_LIST_EMPTY(crematoriums)
|
||||
qdel(M)
|
||||
|
||||
for(var/obj/O in conts) //conts defined above, ignores crematorium and tray
|
||||
// SKYRAT EDIT ADDITION
|
||||
if(istype(O, /obj/item/goldeneye_key))
|
||||
continue
|
||||
// SKYRAT EDIT END
|
||||
qdel(O)
|
||||
|
||||
if(!locate(/obj/effect/decal/cleanable/ash) in get_step(src, dir))//prevent pile-up
|
||||
|
||||
@@ -98,6 +98,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
|
||||
/client/proc/spawn_mob_spawner, // SKYRAT EDIT ADDITION
|
||||
/client/proc/request_more_opfor, //SKYRAT EDIT ADDITION
|
||||
/client/proc/fix_say, // SKYRAT EDIT ADDITION
|
||||
/client/proc/spawn_sunbeam, // SKYRAT EDIT ADDITION
|
||||
/client/proc/cmd_select_equipment,
|
||||
/client/proc/cmd_admin_gib_self,
|
||||
/client/proc/drop_bomb,
|
||||
|
||||
@@ -358,7 +358,8 @@
|
||||
ROLE_SYNDICATE,
|
||||
ROLE_TRAITOR,
|
||||
ROLE_WIZARD,
|
||||
ROLE_BORER,//SKYRAT EDIT: Cortical Borers
|
||||
ROLE_BORER, //SKYRAT EDIT
|
||||
ROLE_ASSAULT_OPERATIVE, //SKYRAT EDIT
|
||||
),
|
||||
"Skyrat Ban Options" = list(
|
||||
BAN_PACIFICATION,
|
||||
|
||||
@@ -12,6 +12,10 @@
|
||||
msg += "\"01000001 01001001\"."
|
||||
if(TRACK_INFILTRATOR)
|
||||
msg += "\"vasvygengbefuvc\"."
|
||||
/// SKYRAT EDIT BEGIN
|
||||
if(TRACK_GOLDENEYE)
|
||||
msg += "\"goldeneye_key\"."
|
||||
/// SKYRAT EDIT END
|
||||
else
|
||||
msg = "Its tracking indicator is blank."
|
||||
. += msg
|
||||
@@ -50,6 +54,10 @@
|
||||
target = A
|
||||
if(TRACK_INFILTRATOR)
|
||||
target = SSshuttle.getShuttle("syndicate")
|
||||
// SKYRAT EDIT ADDITION
|
||||
if(TRACK_GOLDENEYE)
|
||||
target = SSgoldeneye.goldeneye_keys[1] // Track the first goldeneye key in existence.
|
||||
// SKYRAT EDIT END
|
||||
..()
|
||||
|
||||
/obj/item/pinpointer/nuke/proc/switch_mode_to(new_mode)
|
||||
|
||||
@@ -1109,6 +1109,10 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/power/supermatter_crystal/Bumped(atom/movable/hit_object)
|
||||
// SKYRAT EDIT ADDITION
|
||||
if(istype(hit_object, /obj/item/goldeneye_key))
|
||||
return FALSE
|
||||
// SKYRAT EDIT ADDITION
|
||||
if(isliving(hit_object))
|
||||
hit_object.visible_message(span_danger("\The [hit_object] slams into \the [src] inducing a resonance... [hit_object.p_their()] body starts to glow and burst into flames before flashing into dust!"),
|
||||
span_userdanger("You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\""),
|
||||
|
||||
@@ -325,6 +325,9 @@
|
||||
/obj/item/clothing/suit/hooded/cultrobes/eldritch
|
||||
mutant_variants = NONE
|
||||
|
||||
/obj/item/clothing/suit/armor/vest/marine
|
||||
mutant_variants = NONE
|
||||
|
||||
//FEET>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
welcome_sounds = list('modular_skyrat/modules/alerts/sound/ai/default/welcome.ogg')
|
||||
alert_sounds = list('modular_skyrat/modules/alerts/sound/alert2.ogg')
|
||||
command_report_sounds = list('modular_skyrat/modules/alerts/sound/ai/default/commandreport.ogg')
|
||||
event_sounds = list(ANNOUNCER_AIMALF = 'sound/ai/default/aimalf.ogg',
|
||||
event_sounds = list(
|
||||
ANNOUNCER_AIMALF = 'sound/ai/default/aimalf.ogg',
|
||||
ANNOUNCER_ALIENS = 'modular_skyrat/modules/alerts/sound/ai/default/lifesigns.ogg',
|
||||
ANNOUNCER_ANIMES = 'modular_skyrat/modules/alerts/sound/ai/default/animes.ogg',
|
||||
ANNOUNCER_INTERCEPT = 'modular_skyrat/modules/alerts/sound/alert2.ogg',
|
||||
@@ -37,5 +38,6 @@
|
||||
ANNOUNCER_SPOOKY = 'modular_skyrat/modules/alerts/sound/ai/default/admin_horror_music.ogg',
|
||||
ANNOUNCER_ERTYES = 'modular_skyrat/modules/alerts/sound/ai/default/yesert.ogg',
|
||||
ANNOUNCER_MUTANTS = 'modular_skyrat/modules/alerts/sound/ai/default/hazdet.ogg',
|
||||
ANNOUNCER_KLAXON = 'modular_skyrat/master_files/sound/blackmesa/siren1_long.ogg'
|
||||
ANNOUNCER_KLAXON = 'modular_skyrat/master_files/sound/blackmesa/siren1_long.ogg',
|
||||
ANNOUNCER_ICARUS = 'modular_skyrat/modules/assault_operatives/sound/icarus_alarm.ogg',
|
||||
)
|
||||
|
||||
49
modular_skyrat/modules/armaments/code/armament_component.dm
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
* This is the componentised version of the armaments vendor.
|
||||
*
|
||||
* It's intended to be used with NPC vendors, or atoms that otherwise aren't vending machines.
|
||||
*/
|
||||
|
||||
/datum/component/armament
|
||||
/// The types of armament datums we wish to add to this component.
|
||||
var/list/products
|
||||
/// What access do we require to use this machine?
|
||||
var/list/required_access
|
||||
/// Our parent machine.
|
||||
var/atom/parent_atom
|
||||
|
||||
/datum/component/armament/Initialize(list/required_products, list/required_access)
|
||||
if(!required_products)
|
||||
stack_trace("No products specified for armament")
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
if(!isatom(parent))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
parent_atom = parent
|
||||
|
||||
products = required_products
|
||||
|
||||
|
||||
RegisterSignal(parent, COMSIG_ATOM_ATTACK_HAND, .proc/on_attack_hand)
|
||||
|
||||
|
||||
/datum/component/armament/proc/on_attack_hand(datum/source, mob/living/user)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(!user)
|
||||
return
|
||||
|
||||
if(!user.can_interact_with(parent_atom))
|
||||
return
|
||||
|
||||
INVOKE_ASYNC(src, .proc/ui_interact, user)
|
||||
|
||||
/datum/component/armament/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ArmamentStation")
|
||||
ui.open()
|
||||
|
||||
|
||||
|
||||
108
modular_skyrat/modules/armaments/code/armament_entries.dm
Normal file
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* Armament entries
|
||||
*
|
||||
* These are basic entries that are compiled into the global list of armaments.
|
||||
* It is strongly suggested that if you wish to make your own armaments station, you
|
||||
* create your own entries.
|
||||
*
|
||||
* Armament stations are capable of having a restricted list of products, which you should fill if you plan on making
|
||||
* your own station. This is the products variable. If you plan on using the premade list, you can leave this empty.
|
||||
*
|
||||
* Create your own file with all of the entries if you do wish to make your own custom armaments vendor.
|
||||
*
|
||||
* @author Gandalf2k15
|
||||
*/
|
||||
|
||||
GLOBAL_LIST_INIT(armament_entries, build_armament_list())
|
||||
// Do not touch this.
|
||||
/proc/build_armament_list()
|
||||
var/list/armament_dataset = list()
|
||||
for(var/datum/armament_entry/armament_entry as anything in subtypesof(/datum/armament_entry))
|
||||
// Set up our categories so we can add items to them
|
||||
if(initial(armament_entry.category))
|
||||
var/category = initial(armament_entry.category)
|
||||
if(!(category in armament_dataset))
|
||||
// We instansiate the category list so we can add items to it later
|
||||
armament_dataset[category] = list(CATEGORY_ENTRY, CATEGORY_LIMIT)
|
||||
armament_dataset[category][CATEGORY_ENTRY] = list()
|
||||
// These can be considered abstract types, thus do not need to be added.
|
||||
if(isnull(initial(armament_entry.item_type)))
|
||||
continue
|
||||
var/datum/armament_entry/spawned_armament_entry = new armament_entry()
|
||||
// Datums without a name will assume the items name
|
||||
spawned_armament_entry.name ||= initial(spawned_armament_entry.item_type.name)
|
||||
// ditto for the description
|
||||
spawned_armament_entry.description ||= initial(spawned_armament_entry.item_type.desc)
|
||||
// Make our icon cache for the UI.
|
||||
spawned_armament_entry.create_icon_cache()
|
||||
// Now that we've set up our datum, we can add it to the correct category
|
||||
if(spawned_armament_entry.category)
|
||||
if(spawned_armament_entry.subcategory)
|
||||
// Check to see if we've already made the subcategory.
|
||||
if(!(spawned_armament_entry.subcategory in armament_dataset[spawned_armament_entry.category][CATEGORY_ENTRY]))
|
||||
armament_dataset[spawned_armament_entry.category][CATEGORY_ENTRY][spawned_armament_entry.subcategory] = list()
|
||||
// Finally, we add the entry into the list.
|
||||
armament_dataset[spawned_armament_entry.category][CATEGORY_ENTRY][spawned_armament_entry.subcategory] += spawned_armament_entry
|
||||
else
|
||||
// Unset subcategories default to the NONE category.
|
||||
if(!(ARMAMENT_SUBCATEGORY_NONE in armament_dataset[spawned_armament_entry.category][CATEGORY_ENTRY]))
|
||||
armament_dataset[spawned_armament_entry.category][CATEGORY_ENTRY][ARMAMENT_SUBCATEGORY_NONE] = list()
|
||||
armament_dataset[spawned_armament_entry.category][CATEGORY_ENTRY][ARMAMENT_SUBCATEGORY_NONE] += spawned_armament_entry
|
||||
// Set the category item limit.
|
||||
armament_dataset[spawned_armament_entry.category][CATEGORY_LIMIT] = spawned_armament_entry.category_item_limit
|
||||
else
|
||||
// Because of how the UI system works, categories cannot exist with nothing in them, so we
|
||||
// only set the OTHER category if something can go inside it! This seems like a copy paste job, but it needs to be here.
|
||||
if(!(ARMAMENT_CATEGORY_STANDARD in armament_dataset))
|
||||
armament_dataset[ARMAMENT_CATEGORY_STANDARD] = list(CATEGORY_ENTRY, CATEGORY_LIMIT)
|
||||
armament_dataset[ARMAMENT_CATEGORY_STANDARD][CATEGORY_LIMIT] = ARMAMENT_CATEGORY_STANDARD_LIMIT
|
||||
// We don't have home :( add us to the other category.
|
||||
if(spawned_armament_entry.subcategory)
|
||||
// Check to see if we've already made the subcategory.
|
||||
if(!(spawned_armament_entry.subcategory in armament_dataset[ARMAMENT_CATEGORY_STANDARD][CATEGORY_ENTRY]))
|
||||
armament_dataset[ARMAMENT_CATEGORY_STANDARD][CATEGORY_ENTRY][spawned_armament_entry.subcategory] = list()
|
||||
// Finally, we add the entry into the list.
|
||||
armament_dataset[ARMAMENT_CATEGORY_STANDARD][CATEGORY_ENTRY][spawned_armament_entry.subcategory] += spawned_armament_entry
|
||||
else
|
||||
// Unset subcategories default to the NONE category.
|
||||
if(!(ARMAMENT_SUBCATEGORY_NONE in armament_dataset[ARMAMENT_CATEGORY_STANDARD][CATEGORY_ENTRY]))
|
||||
armament_dataset[ARMAMENT_CATEGORY_STANDARD][CATEGORY_ENTRY][ARMAMENT_SUBCATEGORY_NONE] = list()
|
||||
armament_dataset[ARMAMENT_CATEGORY_STANDARD][CATEGORY_ENTRY][ARMAMENT_SUBCATEGORY_NONE] += spawned_armament_entry
|
||||
return armament_dataset
|
||||
|
||||
//////////////////////
|
||||
// ARMAMENT ENTRIES //
|
||||
//////////////////////
|
||||
/datum/armament_entry
|
||||
/// The name of the equipment used in the listing, if not set, it will use the items name.
|
||||
var/name
|
||||
/// The description of the equipment used in the listing, if not set, it will use the items description.
|
||||
var/description
|
||||
/// The item path that we refer to when equipping. If left empty, it will be considered abstract.
|
||||
var/obj/item_type
|
||||
/// Category of the item. This is used to group items together in the UI.
|
||||
var/category = ARMAMENT_CATEGORY_STANDARD
|
||||
/// This is an abstract variable, only set this for base category types. It should not be overriden by subtypes. Set to 0 for infinite.
|
||||
var/category_item_limit = 0
|
||||
/// Our subcategory, where the item will be listed.
|
||||
var/subcategory = ARMAMENT_SUBCATEGORY_NONE
|
||||
/// The points cost of this item.
|
||||
var/cost = 0
|
||||
/// Defines what slot we will try to equip this item to.
|
||||
var/slot_to_equip = ITEM_SLOT_HANDS
|
||||
/// Our cached image.
|
||||
var/cached_base64
|
||||
/// The maximum amount of this item that can be equipped.
|
||||
var/max_purchase = 1
|
||||
|
||||
/datum/armament_entry/proc/create_icon_cache() // TODO: Make this use overlays.
|
||||
var/obj/item/test_item = new item_type()
|
||||
cached_base64 = icon2base64(getFlatIcon(test_item, no_anim = TRUE))
|
||||
qdel(test_item)
|
||||
|
||||
/// This proc handles how the item should be equipped to the player. This needs to return either TRUE or FALSE, TRUE being that it was able to equip the item.
|
||||
/datum/armament_entry/proc/equip_to_human(mob/living/carbon/human/equipping_human, obj/item/item_to_equip)
|
||||
return equipping_human.equip_to_slot_if_possible(item_to_equip, slot_to_equip)
|
||||
|
||||
/datum/armament_entry/proc/after_equip(turf/safe_drop_location, obj/item/item_to_equip)
|
||||
return TRUE
|
||||
209
modular_skyrat/modules/armaments/code/armament_station.dm
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
* Armament Station
|
||||
*
|
||||
* These are the stations designed to be used by players to outfit themselves.
|
||||
* They contain a "products" variable which you can populate with your own set of armament entries.
|
||||
*
|
||||
* If you plan on making your own station, it is strongly recommended you use your own armament entries for whatever it is you're doing.
|
||||
*
|
||||
* Never directly edit an armament entry as this will be carried through all other vendors.
|
||||
*
|
||||
* @author Gandalf2k15
|
||||
*/
|
||||
|
||||
/obj/machinery/armament_station
|
||||
name = "Armament Outfitting Station"
|
||||
desc = "A versatile station for equipping your weapons."
|
||||
icon = 'icons/obj/vending.dmi'
|
||||
icon_state = "liberationstation"
|
||||
density = TRUE
|
||||
/// Used to keep track of what categories have been used
|
||||
var/list/used_categories = list()
|
||||
/// Used to keep track of what items have been purchased
|
||||
var/list/purchased_items = list()
|
||||
/// If set, will limit this station to the products within this list.
|
||||
var/list/products
|
||||
/// The points card that is currently inserted.
|
||||
var/obj/item/armament_points_card/inserted_card
|
||||
|
||||
/obj/machinery/armament_station/attackby(obj/item/weapon, mob/user, params)
|
||||
. = ..()
|
||||
if(istype(weapon, /obj/item/armament_points_card))
|
||||
var/obj/item/inserting_card = weapon
|
||||
if(inserted_card)
|
||||
to_chat(user, span_warning("There is already a card inserted into [src]!"))
|
||||
return
|
||||
inserted_card = inserting_card
|
||||
inserted_card.forceMove(src)
|
||||
to_chat(user, span_notice("You insert [inserting_card] into [src]!"))
|
||||
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 70)
|
||||
|
||||
/obj/machinery/armament_station/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
ui = new(user, src, "ArmamentStation")
|
||||
ui.open()
|
||||
|
||||
// This data proc may look complex. That's because it is.
|
||||
/obj/machinery/armament_station/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["card_inserted"] = inserted_card ? TRUE : FALSE
|
||||
data["card_name"] = "unknown"
|
||||
data["card_points"] = 0
|
||||
if(inserted_card)
|
||||
data["card_points"] = inserted_card.points
|
||||
data["card_name"] = inserted_card.name
|
||||
|
||||
data["armaments_list"] = list()
|
||||
for(var/armament_category as anything in GLOB.armament_entries)
|
||||
var/list/armament_subcategories = list()
|
||||
for(var/subcategory as anything in GLOB.armament_entries[armament_category][CATEGORY_ENTRY])
|
||||
var/list/subcategory_items = list()
|
||||
for(var/datum/armament_entry/armament_entry as anything in GLOB.armament_entries[armament_category][CATEGORY_ENTRY][subcategory])
|
||||
if(products && !(armament_entry.type in products))
|
||||
continue
|
||||
subcategory_items += list(list(
|
||||
"ref" = REF(armament_entry),
|
||||
"icon" = armament_entry.cached_base64,
|
||||
"name" = armament_entry.name,
|
||||
"cost" = armament_entry.cost,
|
||||
"quantity" = armament_entry.max_purchase,
|
||||
"purchased" = purchased_items[armament_entry] ? purchased_items[armament_entry] : 0,
|
||||
"description" = armament_entry.description,
|
||||
"armament_category" = armament_entry.category,
|
||||
"equipment_subcategory" = armament_entry.subcategory,
|
||||
))
|
||||
if(!LAZYLEN(subcategory_items))
|
||||
continue
|
||||
armament_subcategories += list(list(
|
||||
"subcategory" = subcategory,
|
||||
"items" = subcategory_items,
|
||||
))
|
||||
if(!LAZYLEN(armament_subcategories))
|
||||
continue
|
||||
data["armaments_list"] += list(list(
|
||||
"category" = armament_category,
|
||||
"category_limit" = GLOB.armament_entries[armament_category][CATEGORY_LIMIT],
|
||||
"category_uses" = used_categories[armament_category],
|
||||
"subcategories" = armament_subcategories,
|
||||
))
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/armament_station/ui_act(action, list/params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
|
||||
switch(action)
|
||||
if("equip_item")
|
||||
var/datum/armament_entry/armament_entry
|
||||
for(var/category in GLOB.armament_entries)
|
||||
for(var/subcategory in GLOB.armament_entries[category][CATEGORY_ENTRY])
|
||||
armament_entry = locate(params["armament_ref"]) in GLOB.armament_entries[category][CATEGORY_ENTRY][subcategory]
|
||||
if(armament_entry)
|
||||
break
|
||||
if(armament_entry)
|
||||
break
|
||||
if(!armament_entry)
|
||||
return
|
||||
if(products && !(armament_entry.type in products))
|
||||
return
|
||||
select_armament(usr, armament_entry)
|
||||
if("eject_card")
|
||||
eject_card(usr)
|
||||
|
||||
/obj/machinery/armament_station/proc/eject_card(mob/user)
|
||||
if(!inserted_card)
|
||||
to_chat(user, span_warning("No card inserted!"))
|
||||
return
|
||||
inserted_card.forceMove(drop_location())
|
||||
user.put_in_hands(inserted_card)
|
||||
inserted_card = null
|
||||
to_chat(user, span_notice("Card ejected!"))
|
||||
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 70)
|
||||
|
||||
/obj/machinery/armament_station/proc/select_armament(mob/user, datum/armament_entry/armament_entry)
|
||||
if(!inserted_card)
|
||||
to_chat(user, span_warning("No card inserted!"))
|
||||
return
|
||||
if(used_categories[armament_entry.category] >= GLOB.armament_entries[armament_entry.category][CATEGORY_LIMIT])
|
||||
to_chat(user, span_warning("Category limit reached!"))
|
||||
return
|
||||
if(purchased_items[armament_entry] >= armament_entry.max_purchase)
|
||||
to_chat(user, span_warning("Item limit reached!"))
|
||||
return
|
||||
if(!ishuman(user))
|
||||
return
|
||||
if(!inserted_card.use_points(armament_entry.cost))
|
||||
to_chat(user, span_warning("Not enough points!"))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/human_to_equip = user
|
||||
|
||||
var/obj/item/new_item = new armament_entry.item_type(drop_location())
|
||||
|
||||
used_categories[armament_entry.category]++
|
||||
purchased_items[armament_entry]++
|
||||
|
||||
playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3)
|
||||
|
||||
if(armament_entry.equip_to_human(human_to_equip, new_item))
|
||||
to_chat(user, span_notice("Equipped directly to your person."))
|
||||
playsound(src, 'sound/items/equip/toolbelt_equip.ogg', 100)
|
||||
armament_entry.after_equip(drop_location(), new_item)
|
||||
|
||||
/**
|
||||
* Armament points card
|
||||
*
|
||||
* To be used with the armaments vendor.
|
||||
*/
|
||||
/obj/item/armament_points_card
|
||||
name = "armament points card"
|
||||
desc = "A points card that can be used at an Armaments Station or Armaments Dealer."
|
||||
icon = 'modular_skyrat/modules/armaments/icons/armaments.dmi'
|
||||
icon_state = "armament_card"
|
||||
/// How many points does this card have to use at the vendor?
|
||||
var/points = 10
|
||||
|
||||
/obj/item/armament_points_card/Initialize(mapload)
|
||||
. = ..()
|
||||
maptext = span_maptext("<div align='center' valign='middle' style='position:relative'>[points]</div>")
|
||||
|
||||
/obj/item/armament_points_card/examine(mob/user)
|
||||
. = ..()
|
||||
. += span_notice("It has [points] points left.")
|
||||
|
||||
/obj/item/armament_points_card/proc/use_points(points_to_use)
|
||||
if(points_to_use > points)
|
||||
return FALSE
|
||||
|
||||
points -= points_to_use
|
||||
|
||||
update_maptext()
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/item/armament_points_card/proc/update_maptext()
|
||||
maptext = span_maptext("<div align='center' valign='middle' style='position:relative'>[points]</div>")
|
||||
|
||||
/obj/item/armament_points_card/attackby(obj/item/attacking_item, mob/user, params)
|
||||
. = ..()
|
||||
if(istype(attacking_item, /obj/item/armament_points_card))
|
||||
var/obj/item/armament_points_card/attacking_card = attacking_item
|
||||
if(!attacking_card.points)
|
||||
to_chat(user, span_warning("No points left on [attacking_card]!"))
|
||||
return
|
||||
var/points_to_transfer = clamp(tgui_input_number(user, "How many points do you want to transfer?", "Transfer Points", 1, attacking_card.points, 1), 0, attacking_card.points)
|
||||
|
||||
if(!points_to_transfer)
|
||||
return
|
||||
|
||||
if(attacking_card.loc != user) // Preventing exploits.
|
||||
return
|
||||
|
||||
if(attacking_card.use_points(points_to_transfer))
|
||||
points += points_to_transfer
|
||||
update_maptext()
|
||||
to_chat(user, span_notice("You transfer [points_to_transfer] onto [src]!"))
|
||||
BIN
modular_skyrat/modules/armaments/icons/armaments.dmi
Normal file
|
After Width: | Height: | Size: 351 B |
74
modular_skyrat/modules/assault_operatives/code/areas.dm
Normal file
@@ -0,0 +1,74 @@
|
||||
/area/shuttle/syndicate/cruiser
|
||||
name = "Syndicate Cruiser"
|
||||
|
||||
/area/shuttle/syndicate/cruiser/bridge
|
||||
name = "Syndicate Cruiser Control"
|
||||
color = COLOR_BLUE
|
||||
|
||||
/area/shuttle/syndicate/cruiser/medical
|
||||
name = "Syndicate Cruiser Medbay"
|
||||
color = COLOR_LIGHT_PINK
|
||||
|
||||
/area/shuttle/syndicate/cruiser/armory
|
||||
name = "Syndicate Cruiser Armory"
|
||||
color = COLOR_ORANGE
|
||||
|
||||
/area/shuttle/syndicate/cruiser/eva
|
||||
name = "Syndicate Cruiser EVA"
|
||||
color = COLOR_GREEN
|
||||
|
||||
/area/shuttle/syndicate/cruiser/hallway
|
||||
|
||||
/area/shuttle/syndicate/cruiser/airlock
|
||||
name = "Syndicate Cruiser Airlock"
|
||||
color = COLOR_RED
|
||||
|
||||
/area/shuttle/syndicate/cruiser/brig
|
||||
name = "Syndicate Cruiser Brig"
|
||||
color = COLOR_BLACK
|
||||
|
||||
/area/shuttle/syndicate/cruiser/engineering
|
||||
name = "Syndicate Cruiser Engineering"
|
||||
color = COLOR_YELLOW
|
||||
|
||||
/area/shuttle/syndicate/frigate
|
||||
name = "Syndicate Frigate"
|
||||
|
||||
/area/cruiser_dock
|
||||
name = "GoldenEye Satellite"
|
||||
icon_state = "syndie-ship"
|
||||
requires_power = FALSE
|
||||
has_gravity = STANDARD_GRAVITY
|
||||
area_flags = VALID_TERRITORY | UNIQUE_AREA | NOTELEPORT
|
||||
ambientsounds = AMBIENCE_GENERIC
|
||||
|
||||
/area/cruiser_dock/brig
|
||||
name = "Cruiser Dock Prison"
|
||||
color = COLOR_BLUE
|
||||
ambientsounds = AMBIENCE_CREEPY
|
||||
/obj/machinery/door/poddoor/shutters
|
||||
smoothing_groups = list(SMOOTH_GROUP_SHUTTERS)
|
||||
|
||||
/turf/closed/wall/r_wall/syndicate/cruiser
|
||||
canSmoothWith = list(SMOOTH_GROUP_SYNDICATE_WALLS, SMOOTH_GROUP_PLASTITANIUM_WALLS, SMOOTH_GROUP_AIRLOCK, SMOOTH_GROUP_SHUTTLE_PARTS, SMOOTH_GROUP_SHUTTERS)
|
||||
|
||||
/obj/effect/landmark/start/assaultop
|
||||
name = "assaultop"
|
||||
icon = 'icons/effects/landmarks_static.dmi'
|
||||
icon_state = "snukeop_spawn"
|
||||
delete_after_roundstart = FALSE
|
||||
|
||||
/obj/effect/landmark/start/assaultop/Initialize()
|
||||
. = ..()
|
||||
GLOB.assault_operative_start += get_turf(src)
|
||||
|
||||
/obj/effect/spawner/armory_spawn/assaultops
|
||||
name = "gun spawner"
|
||||
guns = list(
|
||||
/obj/item/gun/ballistic/automatic/m16,
|
||||
/obj/item/gun/ballistic/automatic/pistol/deagle,
|
||||
/obj/item/gun/ballistic/automatic/sniper_rifle/modular/syndicate,
|
||||
/obj/item/gun/ballistic/automatic/mp40,
|
||||
/obj/item/gun/ballistic/automatic/c20r,
|
||||
/obj/item/gun/ballistic/automatic/m90,
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
// BODYARMOR
|
||||
#define ARMAMENT_CATEGORY_ARMOR_BODY "Body Armor"
|
||||
#define ARMAMENT_CATEGORY_ARMOR_BODY_LIMIT 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/bodyarmor
|
||||
category = ARMAMENT_CATEGORY_ARMOR_BODY
|
||||
category_item_limit = ARMAMENT_CATEGORY_ARMOR_BODY_LIMIT
|
||||
slot_to_equip = ITEM_SLOT_OCLOTHING
|
||||
|
||||
/datum/armament_entry/assault_operatives/bodyarmor/normal
|
||||
item_type = /obj/item/clothing/suit/armor/vest
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/bodyarmor/bulletproof
|
||||
item_type = /obj/item/clothing/suit/armor/bulletproof
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/bodyarmor/laserproof
|
||||
item_type = /obj/item/clothing/suit/armor/laserproof
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/bodyarmor/swat
|
||||
item_type = /obj/item/clothing/suit/armor/swat
|
||||
cost = 6
|
||||
|
||||
/datum/armament_entry/assault_operatives/bodyarmor/marine
|
||||
item_type = /obj/item/clothing/suit/armor/vest/marine
|
||||
cost = 10
|
||||
|
||||
/datum/armament_entry/assault_operatives/bodyarmor/elite_modsuit
|
||||
item_type = /obj/item/mod/control/pre_equipped/elite
|
||||
cost = 15
|
||||
|
||||
/datum/armament_entry/assault_operatives/bodyarmor/elite_modsuit_flamer
|
||||
item_type = /obj/item/mod/control/pre_equipped/elite/flamethrower
|
||||
cost = 18
|
||||
@@ -0,0 +1,78 @@
|
||||
#define ARMAMENT_CATEGORY_PRIMARY "Primary Weapons"
|
||||
#define ARMAMENT_CATEGORY_PRIMARY_LIMIT 1
|
||||
#define ARMAMENT_SUBCATEGORY_SUBMACHINEGUN "Submachine Guns"
|
||||
#define ARMAMENT_SUBCATEGORY_ASSAULTRIFLE "Assault Rifles"
|
||||
#define ARMAMENT_SUBCATEGORY_SPECIAL "Special Weapons"
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary
|
||||
category = ARMAMENT_CATEGORY_PRIMARY
|
||||
category_item_limit = ARMAMENT_CATEGORY_PRIMARY_LIMIT
|
||||
slot_to_equip = ITEM_SLOT_SUITSTORE
|
||||
cost = 10
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/submachinegun
|
||||
subcategory = ARMAMENT_SUBCATEGORY_SUBMACHINEGUN
|
||||
mags_to_spawn = 4
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/submachinegun/p90
|
||||
item_type = /obj/item/gun/ballistic/automatic/p90
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/submachinegun/wildcat
|
||||
item_type = /obj/item/gun/ballistic/automatic/cfa_wildcat
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/submachinegun/lynx
|
||||
item_type = /obj/item/gun/ballistic/automatic/cfa_lynx
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/submachinegun/mp40
|
||||
item_type = /obj/item/gun/ballistic/automatic/mp40
|
||||
mags_to_spawn = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/submachinegun/ppsh
|
||||
item_type = /obj/item/gun/ballistic/automatic/ppsh
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/submachinegun/c20r
|
||||
item_type = /obj/item/gun/ballistic/automatic/c20r
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/assaultrifle
|
||||
subcategory = ARMAMENT_SUBCATEGORY_ASSAULTRIFLE
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/assaultrifle/akm
|
||||
item_type = /obj/item/gun/ballistic/automatic/akm
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/assaultrifle/m16
|
||||
item_type = /obj/item/gun/ballistic/automatic/m16
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/assaultrifle/stg
|
||||
item_type = /obj/item/gun/ballistic/automatic/stg
|
||||
cost = 12
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/assaultrifle/fg42
|
||||
item_type = /obj/item/gun/ballistic/automatic/fg42
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/special
|
||||
subcategory = ARMAMENT_SUBCATEGORY_SPECIAL
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/special/l6saw
|
||||
item_type = /obj/item/gun/ballistic/automatic/l6_saw
|
||||
cost = 15
|
||||
mags_to_spawn = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/special/mg9
|
||||
item_type = /obj/item/gun/ballistic/automatic/mg34/mg42
|
||||
cost = 15
|
||||
mags_to_spawn = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/special/smartgun
|
||||
item_type = /obj/item/gun/ballistic/automatic/smartgun
|
||||
cost = 12
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/special/rocket_launcher
|
||||
item_type = /obj/item/gun/ballistic/rocketlauncher/unrestricted
|
||||
cost = 15
|
||||
|
||||
/datum/armament_entry/assault_operatives/primary/special/rocket_launcher/after_equip(turf/safe_drop_location, obj/item/item_to_equip)
|
||||
var/obj/item/storage/box/ammo_box/spawned_box = new(safe_drop_location)
|
||||
spawned_box.name = "ROCKETS - [item_to_equip.name]"
|
||||
for(var/i in 1 to 5)
|
||||
new /obj/item/ammo_casing/caseless/rocket(spawned_box)
|
||||
@@ -0,0 +1,62 @@
|
||||
|
||||
#define ARMAMENT_CATEGORY_SECONDARY "Secondary Weapons"
|
||||
#define ARMAMENT_CATEGORY_SECONDARY_LIMIT 1
|
||||
#define ARMAMENT_SUBCATEGORY_PISTOL "Pistols"
|
||||
|
||||
// SECONDARY WEAPONS
|
||||
/datum/armament_entry/assault_operatives/secondary
|
||||
category = ARMAMENT_CATEGORY_SECONDARY
|
||||
category_item_limit = ARMAMENT_CATEGORY_SECONDARY_LIMIT
|
||||
cost = 5
|
||||
mags_to_spawn = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol
|
||||
subcategory = ARMAMENT_SUBCATEGORY_PISTOL
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/m1911
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/m1911
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/aps
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/aps
|
||||
cost = 7
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/luger
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/luger
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/syndicate
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/deagle
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/deagle
|
||||
cost = 9
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/deagle_gold
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/deagle/gold
|
||||
cost = 9
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/deagle_camo
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/deagle/camo
|
||||
cost = 9
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/robohand
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/robohand
|
||||
cost = 10
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/automag
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/automag
|
||||
cost = 8
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/energy_gun
|
||||
item_type = /obj/item/gun/energy/e_gun
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/taser
|
||||
item_type = /obj/item/gun/energy/e_gun/advtaser
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/pistol/pepperball
|
||||
item_type = /obj/item/gun/ballistic/automatic/pistol/pepperball
|
||||
cost = 4
|
||||
|
||||
/datum/armament_entry/assault_operatives/secondary/cqc
|
||||
item_type = /obj/item/book/granter/martial/cqc
|
||||
cost = 10
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
// EXPLOSIVES
|
||||
#define ARMAMENT_CATEGORY_EXPLOSIVES "Explosives"
|
||||
#define ARMAMENT_CATEGORY_EXPLOSIVESLIMIT 4
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives
|
||||
category = ARMAMENT_CATEGORY_EXPLOSIVES
|
||||
category_item_limit = ARMAMENT_CATEGORY_EXPLOSIVESLIMIT
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/minibomb
|
||||
item_type = /obj/item/grenade/syndieminibomb
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/frag
|
||||
item_type = /obj/item/grenade/frag
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/emp_grenade
|
||||
item_type = /obj/item/grenade/empgrenade
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/flashbang
|
||||
item_type = /obj/item/grenade/flashbang
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/smoke
|
||||
item_type = /obj/item/grenade/smokebomb
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/c4
|
||||
item_type = /obj/item/grenade/c4
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/x4
|
||||
item_type = /obj/item/grenade/c4/x4
|
||||
cost = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/bag_of_c4
|
||||
name = "bag of c4"
|
||||
item_type = /obj/item/storage/backpack/duffelbag/syndie/c4
|
||||
cost = 10
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/bag_of_x4
|
||||
name = "bag of x4"
|
||||
item_type = /obj/item/storage/backpack/duffelbag/syndie/x4
|
||||
cost = 6
|
||||
|
||||
/datum/armament_entry/assault_operatives/explosives/bomb
|
||||
name = "Syndicate bomb"
|
||||
item_type = /obj/item/sbeacondrop/bomb
|
||||
cost = 6
|
||||
@@ -0,0 +1,33 @@
|
||||
// HELMETS
|
||||
#define ARMAMENT_CATEGORY_ARMOR_HEAD "Headgear"
|
||||
#define ARMAMENT_CATEGORY_ARMOR_HEAD_LIMIT 1
|
||||
#define ARMAMENT_SUBCATEGORY_HELMET "Helmets"
|
||||
#define ARMAMENT_SUBCATEGORY_BERETS "Berets"
|
||||
|
||||
/datum/armament_entry/assault_operatives/headgear
|
||||
category = ARMAMENT_CATEGORY_ARMOR_HEAD
|
||||
category_item_limit = ARMAMENT_CATEGORY_ARMOR_HEAD_LIMIT
|
||||
slot_to_equip = ITEM_SLOT_HEAD
|
||||
|
||||
/datum/armament_entry/assault_operatives/headgear/helmet
|
||||
subcategory = ARMAMENT_SUBCATEGORY_HELMET
|
||||
|
||||
/datum/armament_entry/assault_operatives/headgear/helmet/normal
|
||||
item_type = /obj/item/clothing/head/helmet
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/headgear/helmet/bulletproof
|
||||
item_type = /obj/item/clothing/head/helmet/alt
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/headgear/helmet/syndicate
|
||||
item_type = /obj/item/clothing/head/helmet/swat
|
||||
cost = 7
|
||||
|
||||
/datum/armament_entry/assault_operatives/headgear/helmet/syndicate
|
||||
item_type = /obj/item/clothing/head/helmet/swat
|
||||
cost = 7
|
||||
|
||||
/datum/armament_entry/assault_operatives/headgear/helmet/syndicate_beret
|
||||
item_type = /obj/item/clothing/head/hos/beret/syndicate
|
||||
cost = 6
|
||||
@@ -0,0 +1,78 @@
|
||||
#define ARMAMENT_CATEGORY_MEDICAL "Medical Supplies"
|
||||
#define ARMAMENT_CATEGORY_MEDICAL_LIMIT 5
|
||||
#define ARMAMENT_SUBCATEGORY_MEDKIT "Medkits"
|
||||
#define ARMAMENT_SUBCATEGORY_INJECTOR "Injectors"
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical
|
||||
category = ARMAMENT_CATEGORY_MEDICAL
|
||||
category_item_limit = ARMAMENT_CATEGORY_MEDICAL_LIMIT
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/medkit
|
||||
subcategory = ARMAMENT_SUBCATEGORY_MEDKIT
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/medkit/basic
|
||||
item_type = /obj/item/storage/medkit/regular
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/medkit/brute
|
||||
item_type = /obj/item/storage/medkit/brute
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/medkit/toxin
|
||||
item_type = /obj/item/storage/medkit/toxin
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/medkit/fire
|
||||
item_type = /obj/item/storage/medkit/fire
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/medkit/o2
|
||||
item_type = /obj/item/storage/medkit/o2
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/medkit/tactical
|
||||
item_type = /obj/item/storage/medkit/tactical
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/surgery_bag
|
||||
item_type = /obj/item/storage/backpack/duffelbag/syndie/surgery
|
||||
cost = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/injector
|
||||
subcategory = ARMAMENT_SUBCATEGORY_INJECTOR
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/injector/bloodloss
|
||||
item_type = /obj/item/reagent_containers/hypospray/medipen/blood_loss
|
||||
cost = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/injector/atropine
|
||||
item_type = /obj/item/reagent_containers/hypospray/medipen/atropine
|
||||
cost = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/injector/salacid
|
||||
item_type = /obj/item/reagent_containers/hypospray/medipen/salacid
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/injector/oxandrolone
|
||||
item_type = /obj/item/reagent_containers/hypospray/medipen/oxandrolone
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/injector/oxandrolone
|
||||
item_type = /obj/item/reagent_containers/hypospray/medipen/oxandrolone
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/injector/stimulant
|
||||
item_type = /obj/item/reagent_containers/hypospray/medipen/stimulants
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/injector/bag
|
||||
item_type = /obj/item/storage/bag/medpens
|
||||
cost = 6
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/beamgun
|
||||
item_type = /obj/item/gun/medbeam
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/medical/defib
|
||||
item_type = /obj/item/defibrillator/compact/loaded
|
||||
cost = 3
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
// MELEE WEAPONS
|
||||
#define ARMAMENT_CATEGORY_MELEE "Melee Weapons"
|
||||
#define ARMAMENT_CATEGORY_MELEE_LIMIT 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/melee
|
||||
category = ARMAMENT_CATEGORY_MELEE
|
||||
category_item_limit = ARMAMENT_CATEGORY_MELEE_LIMIT
|
||||
|
||||
/datum/armament_entry/assault_operatives/melee/combat_knife
|
||||
item_type = /obj/item/knife/combat
|
||||
cost = 7
|
||||
|
||||
/datum/armament_entry/assault_operatives/melee/survival_knife
|
||||
item_type = /obj/item/knife/combat/survival
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/melee/energy
|
||||
item_type = /obj/item/melee/energy/sword
|
||||
cost = 10
|
||||
|
||||
/datum/armament_entry/assault_operatives/melee/baton
|
||||
item_type = /obj/item/melee/baton/security/loaded
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/melee/baton_telescopic
|
||||
item_type = /obj/item/melee/baton/telescopic
|
||||
cost = 5
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
#define ARMAMENT_CATEGORY_UTILITY "Utility Supplies"
|
||||
#define ARMAMENT_CATEGORY_UTILITY_LIMIT 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility
|
||||
category = ARMAMENT_CATEGORY_UTILITY
|
||||
category_item_limit = ARMAMENT_CATEGORY_UTILITY_LIMIT
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/survival_pack
|
||||
item_type = /obj/item/storage/box/nri_survival_pack
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/ration_pack
|
||||
item_type = /obj/item/storage/box/rations
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/nightvisions
|
||||
item_type = /obj/item/clothing/glasses/night
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/thermals
|
||||
item_type = /obj/item/clothing/glasses/thermal
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/sunglasses
|
||||
item_type = /obj/item/clothing/glasses/sunglasses
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/doorjack
|
||||
item_type = /obj/item/card/emag/doorjack
|
||||
name = "Doorjack"
|
||||
description = "Hacks open doors permanently."
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/emag
|
||||
item_type = /obj/item/card/emag/doorjack
|
||||
name = "Emag"
|
||||
description = "Disrupts electronics."
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/toolbox
|
||||
item_type = /obj/item/storage/toolbox/syndicate
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility
|
||||
item_type = /obj/item/storage/toolbox/syndicate
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/dehy_carp
|
||||
item_type = /obj/item/toy/plush/carpplushie/dehy_carp
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/eshield
|
||||
item_type = /obj/item/shield/energy
|
||||
cost = 5
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/tactical_shield
|
||||
item_type = /obj/item/shield/riot/pointman
|
||||
cost = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/noslip
|
||||
name = "Chameleon No-Slips"
|
||||
item_type = /obj/item/clothing/shoes/chameleon/noslip
|
||||
description = "No-slip chameleon shoes, for when you plan on running through hell and back."
|
||||
cost = 2
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/suppressor
|
||||
item_type = /obj/item/suppressor
|
||||
cost = 1
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/holoparasite
|
||||
item_type = /obj/item/guardiancreator/tech/choose/traitor
|
||||
cost = 9
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/launchpad
|
||||
name = "Briefcase Launchpad"
|
||||
item_type = /obj/item/storage/briefcase/launchpad
|
||||
description = "A briefcase containing a launchpad, a device able to teleport items and people to and from targets up to eight tiles away from the briefcase. \
|
||||
Also includes a remote control, disguised as an ordinary folder. Touch the briefcase with the remote to link it."
|
||||
cost = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/utility/syndiejaws
|
||||
name = "Syndicate Jaws of Life"
|
||||
item_type = /obj/item/crowbar/power/syndicate
|
||||
description = "Based on a Nanotrasen model, this powerful tool can be used as both a crowbar and a pair of wirecutters. \
|
||||
In its crowbar configuration, it can be used to force open airlocks. Very useful for entering the station or its departments."
|
||||
cost = 3
|
||||
@@ -0,0 +1,32 @@
|
||||
// VENDOR
|
||||
/obj/machinery/armament_station/assault_operatives
|
||||
name = "Military Grade Armament Station"
|
||||
|
||||
req_access = list(ACCESS_SYNDICATE)
|
||||
|
||||
|
||||
/obj/machinery/armament_station/assault_operatives/Initialize(mapload)
|
||||
. = ..()
|
||||
products = subtypesof(/datum/armament_entry/assault_operatives)
|
||||
|
||||
|
||||
// POINTS CARDS
|
||||
/obj/item/armament_points_card/assaultops
|
||||
points = 40
|
||||
|
||||
// ARMAMENT ENTRIES
|
||||
|
||||
#define ARMAMENT_CATEGORY_OTHER "Miscellaneous"
|
||||
#define ARMAMENT_CATEGORY_OTHER_LIMIT 3
|
||||
|
||||
/datum/armament_entry/assault_operatives
|
||||
var/mags_to_spawn = 3
|
||||
|
||||
/datum/armament_entry/assault_operatives/after_equip(turf/safe_drop_location, obj/item/item_to_equip)
|
||||
if(!istype(item_to_equip, /obj/item/gun/ballistic))
|
||||
var/obj/item/gun/ballistic/spawned_ballistic_gun = item_to_equip
|
||||
if(spawned_ballistic_gun.magazine && !istype(spawned_ballistic_gun.magazine, /obj/item/ammo_box/magazine/internal))
|
||||
var/obj/item/storage/box/ammo_box/spawned_box = new(safe_drop_location)
|
||||
spawned_box.name = "ammo box - [spawned_ballistic_gun.name]"
|
||||
for(var/i in 1 to mags_to_spawn)
|
||||
new spawned_ballistic_gun.mag_type (spawned_box)
|
||||
@@ -0,0 +1,273 @@
|
||||
#define OBJECTIVE_COUNT 5
|
||||
|
||||
/**
|
||||
* ASSAULT OPERATIVE ANTAG DATUM
|
||||
*/
|
||||
|
||||
/datum/antagonist/assault_operative
|
||||
name = ROLE_ASSAULT_OPERATIVE
|
||||
job_rank = ROLE_ASSAULT_OPERATIVE
|
||||
roundend_category = "assault operatives"
|
||||
antagpanel_category = "Assault Operatives"
|
||||
antag_hud_name = "synd"
|
||||
antag_moodlet = /datum/mood_event/focused
|
||||
show_to_ghosts = TRUE
|
||||
hijack_speed = 2
|
||||
preview_outfit = /datum/outfit/syndicate
|
||||
ui_name = "AntagInfoAssaultops"
|
||||
/// The default outfit given BEFORE they choose their equipment.
|
||||
var/assault_operative_default_outfit = /datum/outfit/assaultops
|
||||
/// The team linked to this antagonist datum.
|
||||
var/datum/team/assault_operatives/assault_team
|
||||
/// Should we move the operative to a designated spawn point?
|
||||
var/send_to_spawnpoint = TRUE
|
||||
//If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
|
||||
var/always_new_team = FALSE
|
||||
var/spawn_text = "Your mission is to assault NTSS13 and get all of the GoldenEye keys that you can from the heads of staff that reside there. \
|
||||
Use your pinpointer to locate these after you have extracted the GoldenEye key from the head of staff. It will be sent in by droppod. \
|
||||
You must then upload the key to the GoldenEye upload terminal on this GoldenEye station. After you have completed your mission, \
|
||||
The GoldenEye defence network will fall, and we will gain access to Nanotrasen's military systems. Good luck agent."
|
||||
/// A link to our internal pinpointer.
|
||||
var/datum/status_effect/goldeneye_pinpointer/pinpointer
|
||||
|
||||
/datum/antagonist/assault_operative/Destroy()
|
||||
QDEL_NULL(pinpointer)
|
||||
return ..()
|
||||
|
||||
/datum/antagonist/assault_operative/apply_innate_effects(mob/living/mob_override)
|
||||
add_team_hud(mob_override || owner.current, /datum/antagonist/assault_operative)
|
||||
|
||||
/datum/antagonist/assault_operative/get_team()
|
||||
return assault_team
|
||||
|
||||
/datum/antagonist/assault_operative/greet()
|
||||
owner.current.playsound_local(get_turf(owner.current), 'modular_skyrat/modules/assault_operatives/sound/assault_operatives_greet.ogg', 30, 0, use_reverb = FALSE)
|
||||
to_chat(owner, span_big("You are an assault operative!"))
|
||||
to_chat(owner, span_red(spawn_text))
|
||||
owner.announce_objectives()
|
||||
|
||||
/datum/antagonist/assault_operative/on_gain()
|
||||
give_alias()
|
||||
. = ..()
|
||||
equip_operative()
|
||||
forge_objectives()
|
||||
if(send_to_spawnpoint)
|
||||
move_to_spawnpoint()
|
||||
|
||||
/datum/antagonist/assault_operative/create_team(datum/team/assault_operatives/new_team)
|
||||
if(!new_team)
|
||||
if(!always_new_team)
|
||||
for(var/datum/antagonist/assault_operative/assault_operative in GLOB.antagonists)
|
||||
if(!assault_operative.owner)
|
||||
stack_trace("Antagonist datum without owner in GLOB.antagonists: [assault_operative]")
|
||||
continue
|
||||
if(assault_operative.assault_team)
|
||||
assault_team = assault_operative.assault_team
|
||||
return
|
||||
assault_team = new /datum/team/assault_operatives
|
||||
assault_team.add_member(owner)
|
||||
assault_team.update_objectives()
|
||||
return
|
||||
if(!istype(new_team))
|
||||
stack_trace("Wrong team type passed to [type] initialization.")
|
||||
assault_team = new_team
|
||||
assault_team.add_member(owner)
|
||||
|
||||
// UI systems
|
||||
/datum/antagonist/assault_operative/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
data["required_keys"] = SSgoldeneye.required_keys
|
||||
|
||||
data["uploaded_keys"] = SSgoldeneye.uploaded_keys
|
||||
|
||||
data["available_targets"] = get_available_targets()
|
||||
data["extracted_targets"] = get_extracted_targets()
|
||||
|
||||
data["goldeneye_keys"] = get_goldeneye_keys()
|
||||
|
||||
data["objectives"] = get_objectives()
|
||||
return data
|
||||
|
||||
/datum/antagonist/assault_operative/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch(action)
|
||||
if("track_key")
|
||||
var/obj/item/goldeneye_key/selected_key = locate(params["key_ref"]) in SSgoldeneye.goldeneye_keys
|
||||
if(!selected_key)
|
||||
return
|
||||
pinpointer.set_target(selected_key)
|
||||
|
||||
/datum/antagonist/assault_operative/proc/get_available_targets()
|
||||
var/list/available_targets_data = list()
|
||||
for(var/datum/mind/iterating_mind in SSjob.get_all_heads())
|
||||
if(iterating_mind in SSgoldeneye.goldeneye_extracted_minds)
|
||||
continue
|
||||
available_targets_data += list(list(
|
||||
"name" = iterating_mind.name,
|
||||
"job" = iterating_mind.assigned_role.title,
|
||||
))
|
||||
return available_targets_data
|
||||
|
||||
/datum/antagonist/assault_operative/proc/get_extracted_targets()
|
||||
var/list/extracted_targets_data = list()
|
||||
for(var/datum/mind/iterating_mind in SSgoldeneye.goldeneye_extracted_minds)
|
||||
extracted_targets_data += list(list(
|
||||
"name" = iterating_mind.name,
|
||||
"job" = iterating_mind.assigned_role.title,
|
||||
))
|
||||
return extracted_targets_data
|
||||
|
||||
/datum/antagonist/assault_operative/proc/get_goldeneye_keys()
|
||||
var/list/goldeneye_keys = list()
|
||||
for(var/obj/item/goldeneye_key/iterating_key in SSgoldeneye.goldeneye_keys)
|
||||
var/turf/location = get_turf(iterating_key)
|
||||
goldeneye_keys += list(list(
|
||||
"coord_x" = location.x,
|
||||
"coord_y" = location.y,
|
||||
"coord_z" = location.z,
|
||||
"selected" = pinpointer?.target == iterating_key,
|
||||
"name" = iterating_key.goldeneye_tag,
|
||||
"ref" = REF(iterating_key),
|
||||
))
|
||||
return goldeneye_keys
|
||||
|
||||
|
||||
/datum/antagonist/assault_operative/proc/forge_objectives()
|
||||
if(assault_team)
|
||||
objectives |= assault_team.objectives
|
||||
|
||||
/datum/antagonist/assault_operative/proc/give_alias()
|
||||
var/chosen_name = sanitize_text(tgui_input_text(owner.current, "Please input your desired name!", "Name", "Randy Random"))
|
||||
if(!chosen_name)
|
||||
owner.current.real_name = random_unique_name()
|
||||
return
|
||||
owner.current.real_name = chosen_name
|
||||
|
||||
/datum/antagonist/assault_operative/proc/equip_operative()
|
||||
if(!ishuman(owner.current))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/human_target = owner.current
|
||||
|
||||
if(human_target.dna.species.id == "plasmaman" )
|
||||
human_target.set_species(/datum/species/human)
|
||||
to_chat(human_target, span_userdanger("You are now a human!"))
|
||||
|
||||
for(var/obj/item/item in human_target.get_equipped_items(TRUE))
|
||||
qdel(item)
|
||||
|
||||
var/obj/item/organ/brain/human_brain = human_target.getorganslot(BRAIN)
|
||||
human_brain.destroy_all_skillchips() // get rid of skillchips to prevent runtimes
|
||||
human_target.equipOutfit(assault_operative_default_outfit)
|
||||
human_target.regenerate_icons()
|
||||
|
||||
pinpointer = human_target.apply_status_effect(/datum/status_effect/goldeneye_pinpointer)
|
||||
|
||||
return TRUE
|
||||
|
||||
/datum/antagonist/assault_operative/proc/move_to_spawnpoint()
|
||||
var/team_number = 1
|
||||
if(assault_team)
|
||||
team_number = assault_team.members.Find(owner)
|
||||
owner.current.forceMove(GLOB.assault_operative_start[((team_number - 1) % GLOB.assault_operative_start.len) + 1])
|
||||
|
||||
/datum/antagonist/assault_operative/get_preview_icon()
|
||||
if (!preview_outfit)
|
||||
return null
|
||||
|
||||
var/icon/final_icon = icon('modular_skyrat/modules/assault_operatives/icons/goldeneye.dmi', "goldeneye_key")
|
||||
|
||||
return finish_preview_icon(final_icon)
|
||||
|
||||
/**
|
||||
* ASSAULT OPERATIVE TEAM DATUM
|
||||
*/
|
||||
|
||||
/datum/team/assault_operatives
|
||||
/// Our core objective, it's obviously goldeneye.
|
||||
var/core_objective = /datum/objective/goldeneye
|
||||
|
||||
/datum/team/assault_operatives/proc/update_objectives()
|
||||
if(core_objective)
|
||||
var/datum/objective/new_objective = new core_objective
|
||||
new_objective.team = src
|
||||
objectives += new_objective
|
||||
|
||||
/datum/team/assault_operatives/proc/operatives_dead()
|
||||
var/total_operatives = LAZYLEN(members)
|
||||
var/alive_operatives = 0
|
||||
for(var/datum/mind/iterating_mind in members)
|
||||
if(ishuman(iterating_mind.current) && (iterating_mind.current.stat != DEAD))
|
||||
alive_operatives++
|
||||
if(!alive_operatives)
|
||||
return ASSAULTOPS_ALL_DEAD
|
||||
if(alive_operatives >= total_operatives)
|
||||
return ASSAULTOPS_ALL_ALIVE
|
||||
return ASSAULTOPS_PARTLY_DEAD
|
||||
|
||||
|
||||
/datum/team/assault_operatives/roundend_report()
|
||||
var/list/parts = list()
|
||||
parts += "<span class='header'>Assault Operatives:</span>"
|
||||
|
||||
switch(get_result())
|
||||
if(ASSAULT_RESULT_WIN)
|
||||
parts += span_greentext("Assault Operatives Major Victory!")
|
||||
parts += "<B>The Assault Operatives have successfully subverted and activated GoldenEye, and they all survived!</B>"
|
||||
if(ASSAULT_RESULT_PARTIAL_WIN)
|
||||
parts += span_greentext("Assault Operatives Minor Victory!")
|
||||
parts += "<B>The Assault Operatives have successfully subverted and activated GoldenEye, but only some survived!</B>"
|
||||
if(ASSAULT_RESULT_HEARTY_WIN)
|
||||
parts += span_greentext("Assault Operatives Hearty Victory!")
|
||||
parts += "<B>The Assault Operatives have successfully subverted and activated GoldenEye, but they all died!</B>"
|
||||
if(ASSAULT_RESULT_LOSS)
|
||||
parts += span_redtext("Crew Victory!")
|
||||
parts += "<B>The Research Staff of [station_name()] have killed all of the assault operatives and stopped them activating GoldenEye!</B>"
|
||||
if(ASSAULT_RESULT_STALEMATE)
|
||||
parts += "<span class='neutraltext big'>Stalemate!</span>"
|
||||
parts += "<B>The assault operatives have failed to activate GoldenEye and are still alive!</B>"
|
||||
else
|
||||
parts += "<span class='neutraltext big'>Neutral Victory</span>"
|
||||
parts += "<B>Mission aborted!</B>"
|
||||
parts += span_redtext("GoldenEye keys uploaded: [SSgoldeneye.uploaded_keys]/[SSgoldeneye.required_keys]")
|
||||
|
||||
var/text = "<br><span class='header'>The assault operatives were:</span>"
|
||||
text += printplayerlist(members)
|
||||
text += "<br>"
|
||||
|
||||
parts += text
|
||||
|
||||
return "<div class='panel redborder'>[parts.Join("<br>")]</div>"
|
||||
|
||||
/datum/team/assault_operatives/proc/get_result()
|
||||
var/goldeneye_activated = SSgoldeneye.goldeneye_activated
|
||||
var/operatives_dead_status = operatives_dead()
|
||||
|
||||
if(goldeneye_activated && operatives_dead_status == ASSAULTOPS_ALL_ALIVE)
|
||||
return ASSAULT_RESULT_WIN
|
||||
else if(goldeneye_activated && operatives_dead_status == ASSAULTOPS_PARTLY_DEAD)
|
||||
return ASSAULT_RESULT_PARTIAL_WIN
|
||||
else if(goldeneye_activated && operatives_dead_status == ASSAULTOPS_ALL_DEAD)
|
||||
return ASSAULT_RESULT_HEARTY_WIN
|
||||
else if(!goldeneye_activated && operatives_dead_status == ASSAULTOPS_ALL_DEAD)
|
||||
return ASSAULT_RESULT_LOSS
|
||||
else if(!goldeneye_activated && operatives_dead_status)
|
||||
return ASSAULT_RESULT_STALEMATE
|
||||
|
||||
/**
|
||||
* ASSAULT OPERATIVE JOB TYPE
|
||||
*/
|
||||
/datum/job/assault_operative
|
||||
title = ROLE_ASSAULT_OPERATIVE
|
||||
|
||||
|
||||
/datum/job/assault_operative/get_roundstart_spawn_point()
|
||||
return pick(GLOB.assault_operative_start)
|
||||
|
||||
/datum/job/assault_operative/get_latejoin_spawn_point()
|
||||
return pick(GLOB.assault_operative_start)
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
|
||||
|
||||
//KITS
|
||||
/datum/outfit/assaultops
|
||||
name = "Assault Ops - Default"
|
||||
|
||||
mask = /obj/item/clothing/mask/gas/alt
|
||||
uniform = /obj/item/clothing/under/syndicate/camo
|
||||
shoes = /obj/item/clothing/shoes/combat
|
||||
gloves = /obj/item/clothing/gloves/combat
|
||||
back = /obj/item/storage/backpack/fireproof
|
||||
ears = /obj/item/radio/headset/syndicate/alt
|
||||
id = /obj/item/card/id/advanced/chameleon
|
||||
belt = /obj/item/storage/belt/military
|
||||
head = /obj/item/clothing/head/flatcap
|
||||
|
||||
backpack_contents = list(/obj/item/storage/box/syndie_kit/chameleon, /obj/item/armament_points_card/assaultops)
|
||||
|
||||
id_trim = /datum/id_trim/chameleon/operative
|
||||
|
||||
|
||||
/datum/outfit/assaultops/post_equip(mob/living/carbon/human/equipping_human)
|
||||
var/obj/item/radio/radio = equipping_human.ears
|
||||
radio.set_frequency(FREQ_SYNDICATE)
|
||||
radio.freqlock = TRUE
|
||||
radio.command = TRUE
|
||||
|
||||
var/obj/item/implant/weapons_auth/weapons_authorisation = new/obj/item/implant/weapons_auth(equipping_human)
|
||||
weapons_authorisation.implant(equipping_human)
|
||||
|
||||
equipping_human.faction |= ROLE_SYNDICATE
|
||||
|
||||
equipping_human.update_icons()
|
||||
77
modular_skyrat/modules/assault_operatives/code/base_alarm.dm
Normal file
@@ -0,0 +1,77 @@
|
||||
/obj/machinery/base_alarm
|
||||
name = "base alarm"
|
||||
desc = "Pull this to alert the guards!"
|
||||
icon = 'modular_skyrat/modules/assault_operatives/icons/alarm.dmi'
|
||||
icon_state = "alarm"
|
||||
max_integrity = 250
|
||||
integrity_failure = 0.4
|
||||
use_power = NO_POWER_USE
|
||||
resistance_flags = FIRE_PROOF
|
||||
|
||||
light_power = 0
|
||||
light_range = 4
|
||||
light_color = COLOR_VIVID_RED
|
||||
|
||||
//Trick to get the glowing overlay visible from a distance
|
||||
luminosity = 1
|
||||
|
||||
/// Is the alarm currently playing? WAIT WHY IS THIS NOT A LOOPING SOUND
|
||||
var/alarm_playing = FALSE
|
||||
/// Are we triggered?
|
||||
var/triggered = FALSE
|
||||
/// Currently connected alarms.
|
||||
var/list/obj/machinery/base_alarm/alarms = list()
|
||||
/// The area that we use to trigger other alarms.
|
||||
var/area/myarea = null
|
||||
|
||||
/obj/machinery/base_alarm/Initialize()
|
||||
. = ..()
|
||||
update_icon()
|
||||
myarea = get_area(src)
|
||||
for(var/obj/machinery/base_alarm/alarm in myarea)
|
||||
alarms.Add(alarm)
|
||||
|
||||
/obj/machinery/base_alarm/Destroy()
|
||||
LAZYREMOVE(alarms, src)
|
||||
return ..()
|
||||
|
||||
/obj/machinery/base_alarm/attack_hand(mob/user)
|
||||
add_fingerprint(user)
|
||||
to_chat(user, span_notice("You trigger [src]!"))
|
||||
playsound(src, 'sound/machines/pda_button1.ogg', 100)
|
||||
trigger_alarm()
|
||||
|
||||
/obj/machinery/base_alarm/attack_ai(mob/user)
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/base_alarm/attack_robot(mob/user)
|
||||
return attack_hand(user)
|
||||
|
||||
/obj/machinery/base_alarm/proc/trigger_alarm()
|
||||
if(triggered)
|
||||
reset()
|
||||
else
|
||||
alarm()
|
||||
|
||||
/obj/machinery/base_alarm/proc/alarm()
|
||||
for(var/obj/machinery/base_alarm/iterating_alarm in alarms)
|
||||
iterating_alarm.icon_state = "alarm_on"
|
||||
iterating_alarm.set_light(l_power = 2)
|
||||
iterating_alarm.triggered = TRUE
|
||||
if(!iterating_alarm.alarm_playing)
|
||||
iterating_alarm.alarm_playing = TRUE
|
||||
playsound(iterating_alarm, 'modular_skyrat/modules/assault_operatives/sound/goldeneyealarm.ogg', 30)
|
||||
addtimer(CALLBACK(iterating_alarm, .proc/alarm_sound), 65)
|
||||
|
||||
/obj/machinery/base_alarm/proc/alarm_sound()
|
||||
if(!triggered)
|
||||
alarm_playing = FALSE
|
||||
else
|
||||
playsound(src, 'modular_skyrat/modules/assault_operatives/sound/goldeneyealarm.ogg', 30)
|
||||
addtimer(CALLBACK(src, .proc/alarm_sound), 65)
|
||||
|
||||
/obj/machinery/base_alarm/proc/reset(mob/user)
|
||||
for(var/obj/machinery/base_alarm/iterating_alarm in alarms)
|
||||
iterating_alarm.icon_state = "alarm"
|
||||
iterating_alarm.set_light(l_power = 0)
|
||||
iterating_alarm.triggered = FALSE
|
||||
@@ -0,0 +1,61 @@
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// ASSAULT OPERATIVES //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/assault_operatives
|
||||
name = "Assault Operatives"
|
||||
antag_flag = ROLE_ASSAULT_OPERATIVE
|
||||
antag_datum = /datum/antagonist/assault_operative
|
||||
minimum_required_age = 14
|
||||
restricted_roles = list(
|
||||
JOB_CAPTAIN,
|
||||
JOB_HEAD_OF_SECURITY,
|
||||
JOB_HEAD_OF_SECURITY,
|
||||
JOB_CHIEF_MEDICAL_OFFICER,
|
||||
JOB_CHIEF_ENGINEER,
|
||||
)
|
||||
required_candidates = 5
|
||||
weight = 3
|
||||
cost = 20
|
||||
requirements = list(90, 90, 90, 80, 60, 40, 30, 20, 10, 10)
|
||||
flags = HIGH_IMPACT_RULESET
|
||||
antag_cap = list("denominator" = 18, "offset" = 1)
|
||||
var/datum/team/assault_operatives/assault_operatives_team
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/assault_operatives/ready(population, forced = FALSE)
|
||||
required_candidates = get_antag_cap(population)
|
||||
. = ..()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/assault_operatives/pre_execute(population)
|
||||
. = ..()
|
||||
// If ready() did its job, candidates should have 5 or more members in it
|
||||
var/operatives = get_antag_cap(population)
|
||||
for(var/operatives_number in 1 to operatives)
|
||||
if(candidates.len <= 0)
|
||||
break
|
||||
var/mob/candidate = pick_n_take(candidates)
|
||||
assigned += candidate.mind
|
||||
candidate.mind.set_assigned_role(SSjob.GetJobType(/datum/job/assault_operative))
|
||||
candidate.mind.special_role = ROLE_ASSAULT_OPERATIVE
|
||||
GLOB.pre_setup_antags += candidate.mind
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/assault_operatives/execute()
|
||||
assault_operatives_team = new()
|
||||
for(var/datum/mind/iterating_mind in assigned)
|
||||
GLOB.pre_setup_antags -= iterating_mind
|
||||
var/datum/antagonist/assault_operative/new_op = new antag_datum()
|
||||
iterating_mind.add_antag_datum(new_op, assault_operatives_team)
|
||||
if(assault_operatives_team.members.len)
|
||||
assault_operatives_team.update_objectives()
|
||||
SSgoldeneye.required_keys = get_goldeneye_key_count()
|
||||
return TRUE
|
||||
log_game("DYNAMIC: [ruletype] [name] failed to get any eligible assault operatives. Refunding [cost] threat.")
|
||||
return FALSE
|
||||
|
||||
|
||||
/// Returns the required goldeneye keys for activation. This is to make sure we don't have an impossible to achieve goal. However, there has to be at least one key.
|
||||
/datum/dynamic_ruleset/roundstart/assault_operatives/proc/get_goldeneye_key_count()
|
||||
return clamp(LAZYLEN(SSjob.get_all_heads()), 1, GOLDENEYE_REQUIRED_KEYS_MAXIMUM)
|
||||
257
modular_skyrat/modules/assault_operatives/code/goldeneye.dm
Normal file
@@ -0,0 +1,257 @@
|
||||
GLOBAL_LIST_EMPTY(goldeneye_pinpointers)
|
||||
|
||||
#define ICARUS_IGNITION_TIME 20 SECONDS
|
||||
#define PINPOINTER_PING_TIME 4 SECONDS
|
||||
|
||||
/**
|
||||
* GoldenEye defence network
|
||||
*
|
||||
* Contains: Subsystem, Keycard, Terminal and Objective
|
||||
*/
|
||||
|
||||
SUBSYSTEM_DEF(goldeneye)
|
||||
name = "GoldenEye"
|
||||
init_order = INIT_ORDER_DEFAULT
|
||||
flags = SS_NO_FIRE
|
||||
/// A tracked list of all our keys.
|
||||
var/list/goldeneye_keys = list()
|
||||
/// A list of minds that have been extracted and thus cannot be extracted again.
|
||||
var/list/goldeneye_extracted_minds = list()
|
||||
/// How many keys have been uploaded to GoldenEye.
|
||||
var/uploaded_keys = 0
|
||||
/// How many keys do we need to activate GoldenEye? Can be overriden by Dynamic if there aren't enough heads of staff.
|
||||
var/required_keys = GOLDENEYE_REQUIRED_KEYS_MAXIMUM
|
||||
/// Have we been activated?
|
||||
var/goldeneye_activated = FALSE
|
||||
/// How long until ICARUS fires?
|
||||
var/ignition_time = ICARUS_IGNITION_TIME
|
||||
|
||||
/// A safe proc for adding a targets mind to the tracked extracted minds.
|
||||
/datum/controller/subsystem/goldeneye/proc/extract_mind(datum/mind/target_mind)
|
||||
goldeneye_extracted_minds += target_mind
|
||||
|
||||
/// A safe proc for registering a new key to the goldeneye system.
|
||||
/datum/controller/subsystem/goldeneye/proc/upload_key()
|
||||
uploaded_keys++
|
||||
check_condition()
|
||||
|
||||
/// Checks our activation condition after an upload has occured.
|
||||
/datum/controller/subsystem/goldeneye/proc/check_condition()
|
||||
if(uploaded_keys >= required_keys)
|
||||
activate()
|
||||
return
|
||||
priority_announce("UNAUTHORISED KEYCARD UPLOAD DETECTED. [uploaded_keys]/[required_keys] KEYCARDS UPLOADED.", "GoldenEye Defence Network")
|
||||
|
||||
/// Activates goldeneye.
|
||||
/datum/controller/subsystem/goldeneye/proc/activate()
|
||||
var/message = "/// GOLDENEYE DEFENCE NETWORK BREACHED /// \n \
|
||||
Unauthorised GoldenEye Defence Network access detected. \n \
|
||||
ICARUS online. \n \
|
||||
Targeting system override detected... \n \
|
||||
New target: /NTSS13/ \n \
|
||||
ICARUS firing protocols activated. \n \
|
||||
ETA to fire: [ignition_time / 10] seconds."
|
||||
|
||||
priority_announce(message, "GoldenEye Defence Network", ANNOUNCER_ICARUS)
|
||||
goldeneye_activated = TRUE
|
||||
|
||||
addtimer(CALLBACK(src, .proc/fire_icarus), ignition_time)
|
||||
|
||||
|
||||
/datum/controller/subsystem/goldeneye/proc/fire_icarus()
|
||||
var/datum/round_event_control/icarus_sunbeam/event_to_start = new()
|
||||
event_to_start.runEvent()
|
||||
|
||||
/// Checks if a mind(target_mind) is a head and if they aren't in the goldeneye_extracted_minds list.
|
||||
/datum/controller/subsystem/goldeneye/proc/check_goldeneye_target(datum/mind/target_mind)
|
||||
var/list/heads_list = SSjob.get_all_heads()
|
||||
for(var/datum/mind/iterating_mind as anything in heads_list)
|
||||
if(target_mind == iterating_mind) // We have a match, let's check if they've already been extracted.
|
||||
if(target_mind in goldeneye_extracted_minds) // They've already been extracted, no double extracts!
|
||||
return FALSE
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// Goldeneye key
|
||||
/obj/item/goldeneye_key
|
||||
name = "\improper GoldenEye Authentication Keycard"
|
||||
desc = "A high profile authentication keycard to Nanotrasen's GoldenEye defence network. It seems indestructable."
|
||||
icon = 'modular_skyrat/modules/assault_operatives/icons/goldeneye.dmi'
|
||||
icon_state = "goldeneye_key"
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
max_integrity = INFINITY
|
||||
/// A unique tag that is used to identify this key.
|
||||
var/goldeneye_tag = "G00000"
|
||||
/// Flavour text for who's mind is in the key.
|
||||
var/extract_name = "NO DATA"
|
||||
|
||||
/obj/item/goldeneye_key/Initialize(mapload)
|
||||
. = ..()
|
||||
SSgoldeneye.goldeneye_keys += src
|
||||
goldeneye_tag = "G[rand(10000, 99999)]"
|
||||
name = "\improper GoldenEye Authentication Keycard: [goldeneye_tag]"
|
||||
AddComponent(/datum/component/gps, goldeneye_tag)
|
||||
|
||||
/obj/item/goldeneye_key/examine(mob/user)
|
||||
. = ..()
|
||||
. += "The DNA data link belongs to: [extract_name]"
|
||||
|
||||
/obj/item/goldeneye_key/Destroy(force)
|
||||
SSgoldeneye.goldeneye_keys -= src
|
||||
return ..()
|
||||
|
||||
// Upload terminal
|
||||
/obj/machinery/goldeneye_upload_terminal
|
||||
name = "\improper GoldenEye Defnet Upload Terminal"
|
||||
desc = "An ominous terminal with some ports and keypads, the screen is scrolling with illegible nonsense. It has a strange marking on the side, a red ring with a gold circle within."
|
||||
icon = 'modular_skyrat/modules/assault_operatives/icons/goldeneye.dmi'
|
||||
icon_state = "goldeneye_terminal"
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
density = TRUE
|
||||
/// Is the system currently in use? Used to prevent spam and abuse.
|
||||
var/uploading = FALSE
|
||||
|
||||
|
||||
/obj/machinery/goldeneye_upload_terminal/attackby(obj/item/weapon, mob/user, params)
|
||||
. = ..()
|
||||
if(uploading)
|
||||
return
|
||||
if(!is_station_level(z))
|
||||
say("CONNECTION TO GOLDENEYE NOT DETECTED: Please return to comms range.")
|
||||
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 100)
|
||||
return
|
||||
if(!istype(weapon, /obj/item/goldeneye_key))
|
||||
say("AUTHENTICATION ERROR: Please do not insert foreign objects into terminal.")
|
||||
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 100)
|
||||
return
|
||||
var/obj/item/goldeneye_key/inserting_key = weapon
|
||||
say("GOLDENEYE KEYCARD ACCEPTED: Please wait while the keycard is verified...")
|
||||
playsound(src, 'sound/machines/nuke/general_beep.ogg', 100)
|
||||
uploading = TRUE
|
||||
if(do_after(user, 10 SECONDS, src))
|
||||
say("GOLDENEYE KEYCARD AUTHENTICATED!")
|
||||
playsound(src, 'sound/machines/nuke/confirm_beep.ogg', 100)
|
||||
SSgoldeneye.upload_key()
|
||||
uploading = FALSE
|
||||
qdel(inserting_key)
|
||||
else
|
||||
say("GOLDENEYE KEYCARD VERIFICATION FAILED: Please try again.")
|
||||
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 100)
|
||||
uploading = FALSE
|
||||
|
||||
// Pinpointer
|
||||
/obj/item/pinpointer/nuke/goldeneye
|
||||
name = "\improper GoldenEye Keycard Pinpointer"
|
||||
desc = "A handheld tracking device that locks onto certain signals. This one is configured to locate any GoldenEye keycards."
|
||||
icon_state = "pinpointer_syndicate"
|
||||
worn_icon_state = "pinpointer_black"
|
||||
active = TRUE
|
||||
mode = TRACK_GOLDENEYE
|
||||
|
||||
/obj/item/pinpointer/nuke/goldeneye/Initialize(mapload)
|
||||
. = ..()
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
|
||||
/obj/item/pinpointer/nuke/goldeneye/attack_self(mob/living/user)
|
||||
if(!LAZYLEN(SSgoldeneye.goldeneye_keys))
|
||||
to_chat(user, span_danger("ERROR! No GoldenEye keys detected!"))
|
||||
return
|
||||
target = tgui_input_list(user, "Select GoldenEye keycard to track", "GoldenEye keycard", SSgoldeneye.goldeneye_keys)
|
||||
if(target)
|
||||
to_chat(user, span_notice("Set to track: [target.name]"))
|
||||
|
||||
/obj/item/pinpointer/nuke/goldeneye/scan_for_target()
|
||||
if(QDELETED(target))
|
||||
target = null
|
||||
|
||||
// Objective
|
||||
/datum/objective/goldeneye
|
||||
name = "subvert goldeneye"
|
||||
objective_name = "Subvert GoldenEye"
|
||||
explanation_text = "Extract all of the required GoldenEye Authentication Keys from the heads of staff and activate GoldenEye."
|
||||
martyr_compatible = TRUE
|
||||
|
||||
/datum/objective/goldeneye/check_completion()
|
||||
if(SSgoldeneye.goldeneye_activated)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
// Internal pinpointer
|
||||
|
||||
|
||||
/atom/movable/screen/alert/status_effect/goldeneye_pinpointer
|
||||
name = "Target Integrated Pinpointer"
|
||||
desc = "Even stealthier than a normal implant, it points to a selected GoldenEye keycard."
|
||||
icon = 'icons/obj/device.dmi'
|
||||
icon_state = "pinon"
|
||||
|
||||
/datum/status_effect/goldeneye_pinpointer
|
||||
id = "goldeneye_pinpointer"
|
||||
duration = -1
|
||||
tick_interval = PINPOINTER_PING_TIME
|
||||
alert_type = /atom/movable/screen/alert/status_effect/goldeneye_pinpointer
|
||||
/// The range until you're considered 'close'
|
||||
var/range_mid = 8
|
||||
/// The range until you're considered 'too far away'
|
||||
var/range_far = 16
|
||||
/// The target we are pointing towards, refreshes every tick.
|
||||
var/obj/item/target
|
||||
/// Our linked antagonist datum, if any.
|
||||
var/datum/antagonist/assault_operative/linked_antagonist
|
||||
|
||||
/datum/status_effect/goldeneye_pinpointer/New(list/arguments)
|
||||
GLOB.goldeneye_pinpointers += src
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/goldeneye_pinpointer/Destroy()
|
||||
GLOB.goldeneye_pinpointers -= src
|
||||
if(linked_antagonist)
|
||||
linked_antagonist.pinpointer = null
|
||||
linked_antagonist = null
|
||||
return ..()
|
||||
|
||||
/datum/status_effect/goldeneye_pinpointer/tick()
|
||||
if(!owner)
|
||||
qdel(src)
|
||||
return
|
||||
point_to_target()
|
||||
|
||||
///Show the distance and direction of a scanned target
|
||||
/datum/status_effect/goldeneye_pinpointer/proc/point_to_target()
|
||||
if(QDELETED(target))
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
target = null
|
||||
return
|
||||
if(!target)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
|
||||
var/turf/here = get_turf(owner)
|
||||
var/turf/there = get_turf(target)
|
||||
|
||||
if(!here || !there)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
if(here.z != there.z)
|
||||
linked_alert.icon_state = "pinonnull"
|
||||
return
|
||||
if(!get_dist_euclidian(here,there))
|
||||
linked_alert.icon_state = "pinondirect"
|
||||
return
|
||||
linked_alert.setDir(get_dir(here, there))
|
||||
|
||||
var/dist = (get_dist(here, there))
|
||||
if(dist >= 1 && dist <= range_mid)
|
||||
linked_alert.icon_state = "pinonclose"
|
||||
else if(dist > range_mid && dist <= range_far)
|
||||
linked_alert.icon_state = "pinonmedium"
|
||||
else if(dist > range_far)
|
||||
linked_alert.icon_state = "pinonfar"
|
||||
|
||||
|
||||
/datum/status_effect/goldeneye_pinpointer/proc/set_target(obj/item/new_target)
|
||||
target = new_target
|
||||
to_chat(owner, span_redtext("Integrated pinpointer set to: [target.name]"))
|
||||
|
||||
#undef ICARUS_IGNITION_TIME
|
||||
#undef PINPOINTER_PING_TIME
|
||||
217
modular_skyrat/modules/assault_operatives/code/interrogator.dm
Normal file
@@ -0,0 +1,217 @@
|
||||
#define STAGE_PROCESS_TIME_LOWER 30 SECONDS
|
||||
#define STAGE_PROCESS_TIME_UPPER 1 MINUTES
|
||||
#define ALERT_CREW_TIME 25 SECONDS
|
||||
|
||||
/**
|
||||
* The interrorgator, a piece of machinery used in assault ops to extract GoldenEye keys from heads of staff.
|
||||
*
|
||||
* This device has 3 stages.
|
||||
*
|
||||
* This device has a few requirements to function:
|
||||
* 1. Must be on station Z-level
|
||||
* 2. Must be a head of staff with a linked interrogate objective
|
||||
* 3. Must be alive
|
||||
* 4. Must not be a duplicate key
|
||||
*
|
||||
* After a key has been extracted, it will send a pod somewhere into maintenance, and the syndicates will know about it straight away.
|
||||
*/
|
||||
/obj/machinery/interrogator
|
||||
name = "In-TERROR-gator"
|
||||
desc = "A morraly corrupt piece of machinery used to extract the human mind into a GoldenEye authentication key. The process is said to be one of the most painful experiences someone can endure. Alt+click to start the process."
|
||||
icon = 'modular_skyrat/modules/assault_operatives/icons/goldeneye.dmi'
|
||||
icon_state = "interrogator_open"
|
||||
state_open = FALSE
|
||||
density = TRUE
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
||||
/// Is the door locked?
|
||||
var/locked = FALSE
|
||||
/// Is the system currently processing?
|
||||
var/processing = FALSE
|
||||
/// The link to our timer ID so we can override it if need be.
|
||||
var/timer_id
|
||||
/// The human occupant currently inside. Used for easier referencing later on.
|
||||
var/mob/living/carbon/human/human_occupant
|
||||
|
||||
/obj/machinery/interrogator/examine(mob/user)
|
||||
. = ..()
|
||||
. += "It requies a direct link to a Nanotrasen defence network, stay near a Nanotrasen comms sat!"
|
||||
|
||||
/obj/machinery/interrogator/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(!can_interact(user))
|
||||
return
|
||||
if(user == occupant)
|
||||
return
|
||||
if(!processing)
|
||||
attempt_extract(user)
|
||||
else
|
||||
stop_extract(user)
|
||||
|
||||
/obj/machinery/interrogator/interact(mob/user)
|
||||
if(user == occupant)
|
||||
return
|
||||
if(state_open)
|
||||
close_machine()
|
||||
return
|
||||
if(!processing && !locked)
|
||||
open_machine()
|
||||
return
|
||||
|
||||
/obj/machinery/interrogator/update_icon_state()
|
||||
. = ..()
|
||||
if(occupant)
|
||||
icon_state = processing ? "interrogator_on" : "interrogator_off"
|
||||
else
|
||||
icon_state = state_open ? "interrogator_open" : "interrogator_closed"
|
||||
|
||||
/obj/machinery/interrogator/Destroy()
|
||||
if(timer_id)
|
||||
deltimer(timer_id)
|
||||
timer_id = null
|
||||
human_occupant = null
|
||||
return ..()
|
||||
|
||||
/obj/machinery/interrogator/container_resist_act(mob/living/user)
|
||||
if(!locked)
|
||||
open_machine()
|
||||
|
||||
/obj/machinery/interrogator/open_machine(drop)
|
||||
..()
|
||||
human_occupant = null
|
||||
|
||||
/obj/machinery/interrogator/proc/stop_extract()
|
||||
processing = FALSE
|
||||
locked = FALSE
|
||||
human_occupant = null
|
||||
playsound(src, 'sound/machines/buzz-two.ogg', 100)
|
||||
balloon_alert_to_viewers("process aborted!")
|
||||
if(timer_id)
|
||||
deltimer(timer_id)
|
||||
timer_id = null
|
||||
update_appearance()
|
||||
|
||||
/obj/machinery/interrogator/proc/check_requirements()
|
||||
if(!human_occupant)
|
||||
return FALSE
|
||||
if(state_open)
|
||||
return FALSE
|
||||
if(!is_station_level(z))
|
||||
return FALSE
|
||||
if(human_occupant.stat == DEAD && !HAS_TRAIT(human_occupant, TRAIT_DNR))
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/interrogator/proc/attempt_extract(mob/user)
|
||||
if(!occupant)
|
||||
balloon_alert_to_viewers("no occupant!")
|
||||
return
|
||||
if(state_open)
|
||||
balloon_alert_to_viewers("door open!")
|
||||
return
|
||||
if(!is_station_level(z))
|
||||
balloon_alert_to_viewers("no comms link!")
|
||||
return
|
||||
if(!ishuman(occupant))
|
||||
balloon_alert_to_viewers("invalid target DNA!")
|
||||
return
|
||||
human_occupant = occupant
|
||||
if(human_occupant.stat == DEAD && !HAS_TRAIT(human_occupant, TRAIT_DNR))
|
||||
balloon_alert_to_viewers("occupant is dead!")
|
||||
return
|
||||
if(!SSgoldeneye.check_goldeneye_target(human_occupant.mind)) // Preventing abuse by method of duplication.
|
||||
balloon_alert_to_viewers("no GoldenEye data!")
|
||||
playsound(src, 'sound/machines/scanbuzz.ogg', 100)
|
||||
return
|
||||
|
||||
start_extract()
|
||||
|
||||
/obj/machinery/interrogator/proc/start_extract()
|
||||
to_chat(human_occupant, span_userdanger("You feel dread wash over you as you hear the door on [src] lock!"))
|
||||
locked = TRUE
|
||||
processing = TRUE
|
||||
say("Starting DNA data extraction!")
|
||||
timer_id = addtimer(CALLBACK(src, .proc/stage_one), rand(STAGE_PROCESS_TIME_LOWER, STAGE_PROCESS_TIME_UPPER), TIMER_STOPPABLE|TIMER_UNIQUE) //Random times so crew can't anticipate exactly when it will drop.
|
||||
update_appearance()
|
||||
|
||||
/obj/machinery/interrogator/proc/stage_one()
|
||||
if(!check_requirements())
|
||||
say("Critical error! Aborting.")
|
||||
playsound(src, 'sound/machines/scanbuzz.ogg', 100)
|
||||
return
|
||||
to_chat(human_occupant, span_danger("As [src] whirrs to life you feel some cold metal restraints deploy around you, you can't move!"))
|
||||
playsound(loc, 'sound/items/rped.ogg', 60)
|
||||
say("Stage one complete!")
|
||||
minor_announce("SECURITY BREACH DETECTED, NETWORK COMPROMISED! READING COORDINATES...", "GoldenEye Defence Network")
|
||||
timer_id = addtimer(CALLBACK(src, .proc/stage_two), rand(STAGE_PROCESS_TIME_LOWER, STAGE_PROCESS_TIME_UPPER), TIMER_STOPPABLE|TIMER_UNIQUE)
|
||||
|
||||
/obj/machinery/interrogator/proc/stage_two()
|
||||
if(!check_requirements())
|
||||
say("Critical error! Aborting.")
|
||||
playsound(src, 'sound/machines/scanbuzz.ogg', 100)
|
||||
return
|
||||
to_chat(human_occupant, span_userdanger("You feel a sharp pain as a drill penetrates your skull, it's unbearable!"))
|
||||
human_occupant.emote("scream")
|
||||
human_occupant.apply_damage(30, BRUTE, BODY_ZONE_HEAD)
|
||||
playsound(src, 'sound/effects/wounds/blood1.ogg', 100)
|
||||
playsound(src, 'sound/items/drill_use.ogg', 100)
|
||||
say("Stage two complete!")
|
||||
minor_announce("SECURITY BREACH DETECTED, NETWORK COMPROMISED! COORDINATES: [x], [y], [z]", "GoldenEye Defence Network")
|
||||
timer_id = addtimer(CALLBACK(src, .proc/stage_three), rand(STAGE_PROCESS_TIME_LOWER, STAGE_PROCESS_TIME_UPPER), TIMER_STOPPABLE|TIMER_UNIQUE)
|
||||
|
||||
/obj/machinery/interrogator/proc/stage_three()
|
||||
if(!check_requirements())
|
||||
say("Critical error! Aborting.")
|
||||
playsound(src, 'sound/machines/scanbuzz.ogg', 100)
|
||||
return
|
||||
to_chat(human_occupant, span_userdanger("You feel something penetrating your brain, it feels as though your childhood memories are fading! Please, make it stop! After a moment of silence, you realize you can't remember what happened to you!"))
|
||||
human_occupant.emote("scream")
|
||||
human_occupant.apply_damage(20, BRUTE, BODY_ZONE_HEAD)
|
||||
human_occupant.Jitter(3 MINUTES)
|
||||
human_occupant.Unconscious(1 MINUTES)
|
||||
playsound(src, 'sound/effects/dismember.ogg', 100)
|
||||
playsound(src, 'sound/machines/ping.ogg', 100)
|
||||
say("Process complete! A key is being sent aboard! Crew will shortly detect the keycard!")
|
||||
send_keycard()
|
||||
processing = FALSE
|
||||
locked = FALSE
|
||||
update_appearance()
|
||||
addtimer(CALLBACK(src, .proc/announce_creation), ALERT_CREW_TIME)
|
||||
|
||||
/obj/machinery/interrogator/proc/announce_creation()
|
||||
priority_announce("CRITICAL SECURITY BREACH DETECTED! A GoldenEye authentication keycard has been illegally extracted and is being sent in somewhere on the station!", "GoldenEye Defence Network")
|
||||
for(var/obj/item/pinpointer/nuke/disk_pinpointers in GLOB.pinpointer_list)
|
||||
disk_pinpointers.switch_mode_to(TRACK_GOLDENEYE) //Pinpointer will track the newly created goldeneye key.
|
||||
|
||||
/obj/machinery/interrogator/proc/send_keycard()
|
||||
var/turf/landingzone = find_drop_turf()
|
||||
var/obj/item/goldeneye_key/new_key
|
||||
if(!landingzone)
|
||||
new_key = new(src)
|
||||
else
|
||||
new_key = new
|
||||
new_key.extract_name = human_occupant.real_name
|
||||
// Add them to the goldeneye extracted list. This list is capable of having nulls.
|
||||
SSgoldeneye.extract_mind(human_occupant.mind)
|
||||
var/obj/structure/closet/supplypod/pod = new
|
||||
new /obj/effect/pod_landingzone(landingzone, pod, new_key)
|
||||
for(var/datum/status_effect/goldeneye_pinpointer/iterating_pinpointer in GLOB.goldeneye_pinpointers)
|
||||
iterating_pinpointer.set_target(new_key)
|
||||
|
||||
notify_ghosts("GoldenEye key launched!", source = new_key, action = NOTIFY_ORBIT, header = "Something's Interesting!")
|
||||
|
||||
/obj/machinery/interrogator/proc/find_drop_turf()
|
||||
var/list/possible_turfs = list()
|
||||
|
||||
var/obj/structure/test_structure = new() // This is apparently the most intuative way to check if a turf is able to support entering.
|
||||
|
||||
for(var/area/maintenance/maint_area in world)
|
||||
for(var/turf/floor in maint_area)
|
||||
if(!is_station_level(floor.z))
|
||||
continue
|
||||
if(floor.Enter(test_structure))
|
||||
possible_turfs += floor
|
||||
qdel(test_structure)
|
||||
|
||||
//Pick a turf to spawn at if we can
|
||||
if(length(possible_turfs))
|
||||
return pick(possible_turfs)
|
||||
23
modular_skyrat/modules/assault_operatives/code/misc_items.dm
Normal file
@@ -0,0 +1,23 @@
|
||||
/obj/item/storage/bag/medpens
|
||||
name = "medpen pouch"
|
||||
desc = "A pouch containing several different types of lifesaving medipens."
|
||||
icon = 'modular_skyrat/modules/modular_items/icons/storage.dmi'
|
||||
icon_state = "medpen_pouch"
|
||||
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
|
||||
|
||||
/obj/item/storage/bag/medpens/ComponentInitialize()
|
||||
. = ..()
|
||||
var/datum/component/storage/storage_component = GetComponent(/datum/component/storage)
|
||||
storage_component.max_w_class = WEIGHT_CLASS_NORMAL
|
||||
storage_component.max_combined_w_class = 30
|
||||
storage_component.max_items = 4
|
||||
storage_component.display_numerical_stacking = FALSE
|
||||
storage_component.can_hold = typecacheof(list(/obj/item/reagent_containers/hypospray))
|
||||
|
||||
|
||||
/obj/item/storage/bag/medpens/PopulateContents()
|
||||
new /obj/item/reagent_containers/hypospray/medipen/oxandrolone(src)
|
||||
new /obj/item/reagent_containers/hypospray/medipen/salacid(src)
|
||||
new /obj/item/reagent_containers/hypospray/medipen/salbutamol(src)
|
||||
new /obj/item/reagent_containers/hypospray/medipen/stimulants(src)
|
||||
|
||||
45
modular_skyrat/modules/assault_operatives/code/shuttle.dm
Normal file
@@ -0,0 +1,45 @@
|
||||
/obj/machinery/computer/shuttle/goldeneye_cruiser
|
||||
name = "goldeneye cruiser helm"
|
||||
desc = "The terminal used to control the goldeneye cruiser."
|
||||
shuttleId = "goldeneye_cruiser"
|
||||
possible_destinations = "goldeneye_cruiser_custom;goldeneye_cruiser_dock;syndicate_away;syndicate_z5;syndicate_ne;syndicate_nw;syndicate_n;syndicate_se;syndicate_sw;syndicate_s;syndicate_cruiser_dock;whiteship_away;whiteship_home;whiteship_z4;whiteship_lavaland;ferry_away"
|
||||
circuit = /obj/item/circuitboard/computer/syndicate_shuttle
|
||||
icon_screen = "syndishuttle"
|
||||
icon_keyboard = "syndie_key"
|
||||
light_color = COLOR_SOFT_RED
|
||||
req_access = list(ACCESS_SYNDICATE)
|
||||
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/machinery/computer/shuttle/goldeneye_cruiser/launch_check(mob/user)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/computer/shuttle/goldeneye_cruiser/allowed(mob/to_check)
|
||||
if(issilicon(to_check) && !(ROLE_SYNDICATE in to_check.faction))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/machinery/computer/shuttle/goldeneye_cruiser/recall
|
||||
name = "goldeneye shuttle recall terminal"
|
||||
desc = "Use this if your friends left you behind."
|
||||
possible_destinations = "goldeneye_cruiser_dock"
|
||||
|
||||
/obj/machinery/computer/camera_advanced/shuttle_docker/goldeneye_cruiser
|
||||
name = "goldeneye cruiser navigation computer"
|
||||
desc = "Used to designate a precise transit location for the goldeneye cruiser."
|
||||
icon_screen = "syndishuttle"
|
||||
icon_keyboard = "syndie_key"
|
||||
shuttlePortId = "goldeneye_cruiser_dock"
|
||||
shuttleId = "goldeneye_cruiser"
|
||||
jump_to_ports = list("syndicate_n" = 1, "whiteship_away" = 1, "whiteship_home" = 1, "whiteship_z4" = 1)
|
||||
view_range = 14
|
||||
whitelist_turfs = list(/turf/open/space, /turf/open/floor/plating, /turf/open/lava, /turf/closed/mineral)
|
||||
see_hidden = TRUE
|
||||
x_offset = -10
|
||||
y_offset = 5
|
||||
|
||||
/datum/map_template/shuttle/goldeneye_cruiser
|
||||
name = "goldeneye cruiser"
|
||||
prefix = "_maps/shuttles/skyrat/"
|
||||
port_id = "goldeneye"
|
||||
suffix = "cruiser"
|
||||
who_can_purchase = null
|
||||
170
modular_skyrat/modules/assault_operatives/code/sunbeam.dm
Normal file
@@ -0,0 +1,170 @@
|
||||
#define SUNBEAM_OBLITERATION_RANGE_FIRE 2
|
||||
#define SUNBEAM_OBLITERATION_RANGE_FLATTEN 1
|
||||
#define SUNBEAM_OBLITERATION_COOLDOWN 0.2 SECONDS
|
||||
#define SUNBEAM_MOVEMENT_COOLDOWN 0.3 SECONDS
|
||||
#define SUNBEAM_DEFAULT_SCALE_X 2
|
||||
#define SUNBEAM_DEFAULT_SCALE_Y 2
|
||||
#define SUNBEAM_OVERLAYS 16
|
||||
|
||||
/obj/effect/sunbeam
|
||||
name = "\improper ICARUS Sunbeam"
|
||||
desc = "A beam of light from the sun."
|
||||
icon = 'modular_skyrat/modules/assault_operatives/icons/sunbeam.dmi'
|
||||
icon_state = "sunray_splash"
|
||||
throwforce = 100
|
||||
move_force = INFINITY
|
||||
move_resist = INFINITY
|
||||
pull_force = INFINITY
|
||||
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
|
||||
movement_type = PHASING | FLYING
|
||||
plane = MASSIVE_OBJ_PLANE
|
||||
plane = ABOVE_LIGHTING_PLANE
|
||||
light_range = 6
|
||||
light_color = "#ffbf10"
|
||||
/// A reference to the target we will move towards
|
||||
var/atom/target_atom
|
||||
/// How much do we offset the mid beam?
|
||||
var/beam_offset_y = 32
|
||||
/// Our sound loop.
|
||||
var/datum/looping_sound/sunbeam/soundloop
|
||||
/// Used to control how slowly the beam moves.
|
||||
var/movement_cooldown = SUNBEAM_MOVEMENT_COOLDOWN
|
||||
/// Our obliteration cooldown.
|
||||
var/obliteration_cooldown = SUNBEAM_OBLITERATION_COOLDOWN
|
||||
/// The range of fire to spawn.
|
||||
var/obliteration_range_fire = SUNBEAM_OBLITERATION_RANGE_FIRE
|
||||
/// The range of objects and atoms to be atomised.
|
||||
var/obliteration_range_flatten = SUNBEAM_OBLITERATION_RANGE_FLATTEN
|
||||
|
||||
COOLDOWN_DECLARE(oblirerate_cooldown)
|
||||
COOLDOWN_DECLARE(movement_delay)
|
||||
|
||||
/obj/effect/sunbeam/Initialize(mapload, atom/target, movement_cooldown_override, obliteration_cooldown_override, obliteration_range_fire_override, obliteration_range_flatten_override, scale_x = SUNBEAM_DEFAULT_SCALE_X, scale_y = SUNBEAM_DEFAULT_SCALE_Y)
|
||||
. = ..()
|
||||
if(target)
|
||||
target_atom = target
|
||||
if(movement_cooldown_override)
|
||||
movement_cooldown = movement_cooldown_override
|
||||
if(obliteration_cooldown_override)
|
||||
obliteration_cooldown = obliteration_cooldown_override
|
||||
if(obliteration_range_fire_override)
|
||||
obliteration_range_fire = obliteration_range_fire_override
|
||||
if(obliteration_range_flatten_override)
|
||||
obliteration_range_flatten = obliteration_range_flatten_override
|
||||
|
||||
START_PROCESSING(SSfastprocess, src)
|
||||
update_appearance()
|
||||
if(scale_x || scale_y)
|
||||
var/matrix/our_matrix = matrix()
|
||||
our_matrix.Scale(scale_x, scale_y)
|
||||
transform = our_matrix
|
||||
notify_ghosts("An ICARUS sunbeam has been launched! [target_atom ? "Towards: [target_atom.name]" : ""]", source = src, action = NOTIFY_ORBIT, header = "Somethings burning!")
|
||||
soundloop = new(src, TRUE)
|
||||
|
||||
/obj/effect/sunbeam/Destroy(force)
|
||||
QDEL_NULL(soundloop)
|
||||
return ..()
|
||||
|
||||
/obj/effect/sunbeam/update_overlays()
|
||||
. = ..()
|
||||
for(var/i in 1 to SUNBEAM_OVERLAYS)
|
||||
var/mutable_appearance/beam_overlay = mutable_appearance(icon, "sunray")
|
||||
beam_overlay.pixel_y = beam_offset_y * i
|
||||
. += beam_overlay
|
||||
|
||||
/obj/effect/sunbeam/process(delta_time)
|
||||
if(target_atom && COOLDOWN_FINISHED(src, movement_delay))
|
||||
step_towards(src, target_atom)
|
||||
COOLDOWN_START(src, movement_delay, movement_cooldown)
|
||||
|
||||
if(COOLDOWN_FINISHED(src, oblirerate_cooldown))
|
||||
obliterate()
|
||||
|
||||
if(get_turf(src) == get_turf(target_atom))
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/sunbeam/proc/obliterate()
|
||||
if(obliteration_range_fire)
|
||||
for(var/turf/open/turf_to_incinerate in circle_range(src, obliteration_range_fire))
|
||||
turf_to_incinerate.hotspot_expose(5500)
|
||||
new /obj/effect/hotspot(turf_to_incinerate)
|
||||
|
||||
if(obliteration_range_flatten)
|
||||
for(var/atom/atom_to_obliterate in circle_range(src, obliteration_range_flatten))
|
||||
if(isclosedturf(atom_to_obliterate))
|
||||
SSexplosions.highturf += atom_to_obliterate
|
||||
continue
|
||||
|
||||
if(isfloorturf(atom_to_obliterate))
|
||||
var/turf/open/floor/open_turf = atom_to_obliterate
|
||||
if(open_turf.turf_flags & CAN_DECAY_BREAK_1)
|
||||
open_turf.break_tile_to_plating()
|
||||
|
||||
if(isobj(atom_to_obliterate))
|
||||
var/obj/object_to_obliterate = atom_to_obliterate
|
||||
object_to_obliterate.take_damage(INFINITY, BRUTE, NONE, TRUE, dir, INFINITY)
|
||||
continue
|
||||
|
||||
if(isliving(atom_to_obliterate))
|
||||
var/mob/living/mob_to_obliterate = atom_to_obliterate
|
||||
mob_to_obliterate.apply_damage(200, BURN)
|
||||
continue
|
||||
|
||||
COOLDOWN_START(src, oblirerate_cooldown, obliteration_cooldown)
|
||||
|
||||
/datum/looping_sound/sunbeam
|
||||
mid_sounds = list('modular_skyrat/modules/assault_operatives/sound/sunbeam_loop.ogg' = 1)
|
||||
mid_length = 6.7 SECONDS
|
||||
volume = 100
|
||||
extra_range = 25
|
||||
|
||||
/client/proc/spawn_sunbeam()
|
||||
set category = "Admin.Fun"
|
||||
set name = "Spawn Sunbeam"
|
||||
set desc = "Spawns an ICARUS sunbeam at your location and sends it towards a target."
|
||||
|
||||
var/mob/living/target_mob = tgui_input_list(usr, "Select a mob", "Mob", GLOB.mob_living_list)
|
||||
|
||||
if(!target_mob)
|
||||
return
|
||||
|
||||
var/edit_ranges = tgui_alert(usr, "Change beam specifications?", "Beam Specifications", list("Yes", "No"))
|
||||
|
||||
if(edit_ranges == "Yes")
|
||||
var/edit_range_fire = tgui_input_number(usr, "Fire range in tiles", "Fire Range", SUNBEAM_OBLITERATION_RANGE_FIRE, 20, 0)
|
||||
var/edit_range_flatten = tgui_input_number(usr, "Flatten range in tiles", "Flatten Range", SUNBEAM_OBLITERATION_RANGE_FLATTEN, 20, 0)
|
||||
var/edit_cooldown = tgui_input_number(usr, "Cooldown in seconds", "Cooldown", SUNBEAM_OBLITERATION_COOLDOWN, 20, 0)
|
||||
var/edit_movement_cooldown = tgui_input_number(usr, "Movement cooldown in seconds", "Movement Cooldown", SUNBEAM_MOVEMENT_COOLDOWN, 20, 0)
|
||||
var/edit_scale_x = tgui_input_number(usr, "Scale X", "Scale X", SUNBEAM_DEFAULT_SCALE_X, 20, 0)
|
||||
var/edit_scale_y = tgui_input_number(usr, "Scale Y", "Scale Y", SUNBEAM_DEFAULT_SCALE_Y, 20, 0)
|
||||
|
||||
new /obj/effect/sunbeam(usr, target_mob, edit_movement_cooldown, edit_cooldown, edit_range_fire, edit_range_flatten, edit_scale_x, edit_scale_y)
|
||||
return
|
||||
|
||||
new /obj/effect/sunbeam(usr, target_mob)
|
||||
|
||||
|
||||
/datum/round_event_control/icarus_sunbeam
|
||||
name = "ICARUS Weapons System Ignition"
|
||||
typepath = /datum/round_event/icarus_sunbeam
|
||||
max_occurrences = 0
|
||||
|
||||
/datum/round_event/icarus_sunbeam
|
||||
announceWhen = 1 // Instant announcement
|
||||
|
||||
/datum/round_event/icarus_sunbeam/announce(fake)
|
||||
priority_announce("/// ICARUS SUNBEAM WEAPONS SYSTEM ACTIVATED, USE EXTREME CAUTION! ///", "GoldenEye Defence Network", ANNOUNCER_KLAXON)
|
||||
alert_sound_to_playing('modular_skyrat/modules/assault_operatives/sound/sunbeam_fire.ogg')
|
||||
|
||||
/datum/round_event/icarus_sunbeam/start()
|
||||
var/startside = pick(GLOB.cardinals)
|
||||
var/turf/end_turf = get_edge_target_turf(get_safe_random_station_turf(), turn(startside, 180))
|
||||
var/turf/start_turf = spaceDebrisStartLoc(startside, end_turf.z)
|
||||
new /obj/effect/sunbeam(start_turf, end_turf)
|
||||
|
||||
#undef SUNBEAM_OBLITERATION_RANGE_FIRE
|
||||
#undef SUNBEAM_OBLITERATION_RANGE_FLATTEN
|
||||
#undef SUNBEAM_OBLITERATION_COOLDOWN
|
||||
#undef SUNBEAM_MOVEMENT_COOLDOWN
|
||||
#undef SUNBEAM_DEFAULT_SCALE_X
|
||||
#undef SUNBEAM_DEFAULT_SCALE_Y
|
||||
32
modular_skyrat/modules/assault_operatives/code/turrets.dm
Normal file
@@ -0,0 +1,32 @@
|
||||
//TURRETS
|
||||
/obj/machinery/porta_turret/assaultops
|
||||
use_power = IDLE_POWER_USE
|
||||
req_access = list(ACCESS_SYNDICATE)
|
||||
faction = list(ROLE_SYNDICATE)
|
||||
mode = TURRET_STUN
|
||||
max_integrity = 200
|
||||
base_icon_state = "syndie"
|
||||
stun_projectile = /obj/projectile/energy/electrode
|
||||
stun_projectile_sound = 'sound/weapons/taser.ogg'
|
||||
lethal_projectile = /obj/projectile/beam/laser/heavylaser
|
||||
lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
|
||||
|
||||
/obj/machinery/porta_turret/assaultops/assess_perp(mob/living/carbon/human/perp)
|
||||
return 10
|
||||
|
||||
/obj/machinery/porta_turret/assaultops/shuttle
|
||||
scan_range = 9
|
||||
lethal_projectile = /obj/projectile/bullet/a357
|
||||
lethal_projectile_sound = 'modular_skyrat/modules/aesthetics/guns/sound/sniperrifle.ogg'
|
||||
stun_projectile = /obj/projectile/energy/electrode
|
||||
stun_projectile_sound = 'sound/weapons/taser.ogg'
|
||||
max_integrity = 600
|
||||
armor = list(MELEE = 50, BULLET = 30, LASER = 30, ENERGY = 30, BOMB = 80, BIO = 0, FIRE = 90, ACID = 90)
|
||||
|
||||
/obj/machinery/porta_turret/assaultops/ComponentInitialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
||||
|
||||
|
||||
/obj/machinery/porta_turret/syndicate/assess_perp(mob/living/carbon/human/perp)
|
||||
return 10 //Syndicate turrets shoot everything not in their faction
|
||||
@@ -0,0 +1,76 @@
|
||||
//VENDING MACHINES
|
||||
/obj/machinery/vending/assaultops_ammo
|
||||
name = "\improper Syndicate Ammo Station"
|
||||
desc = "An ammo vending machine which holds a variety of different ammo mags."
|
||||
icon_state = "liberationstation"
|
||||
vend_reply = "Item dispensed."
|
||||
scan_id = FALSE
|
||||
resistance_flags = FIRE_PROOF
|
||||
onstation = FALSE
|
||||
light_mask = "liberation-light-mask"
|
||||
default_price = 0
|
||||
/// Have we been FILLED?
|
||||
var/filled = FALSE
|
||||
|
||||
/obj/machinery/vending/assaultops_ammo/ui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
fill_ammo(user)
|
||||
ui = new(user, src, "Vending")
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/vending/assaultops_ammo/proc/fill_ammo(mob/user)
|
||||
if(last_shopper == user && filled)
|
||||
return
|
||||
else
|
||||
filled = FALSE
|
||||
|
||||
if(!ishuman(user))
|
||||
return FALSE
|
||||
|
||||
if(!user.mind.has_antag_datum(/datum/antagonist/assault_operative))
|
||||
return FALSE
|
||||
|
||||
//Remove all current items from the vending machine
|
||||
products.Cut()
|
||||
product_records.Cut()
|
||||
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
|
||||
//Find all the ammo we should display
|
||||
for(var/object in human_user.contents)
|
||||
if(istype(object, /obj/item/gun/ballistic))
|
||||
var/obj/item/gun/ballistic/gun = object
|
||||
if(!gun.internal_magazine)
|
||||
products.Add(gun.mag_type)
|
||||
if(istype(object, /obj/item/storage))
|
||||
var/obj/item/storage/storage = object
|
||||
for(var/storage_item in storage.contents)
|
||||
if(istype(storage_item, /obj/item/gun/ballistic))
|
||||
var/obj/item/gun/ballistic/gun = storage_item
|
||||
if(!gun.internal_magazine)
|
||||
products.Add(gun.mag_type)
|
||||
|
||||
//Add our items to the list of products
|
||||
build_inventory(products, product_records, FALSE)
|
||||
|
||||
filled = TRUE
|
||||
|
||||
/obj/machinery/vending/assaultops_ammo/build_inventory(list/productlist, list/recordlist, start_empty = FALSE)
|
||||
default_price = 0
|
||||
extra_price = 0
|
||||
for(var/typepath in productlist)
|
||||
var/amount = 4
|
||||
var/atom/temp = typepath
|
||||
var/datum/data/vending_product/vending_product = new /datum/data/vending_product()
|
||||
|
||||
GLOB.vending_products[typepath] = 1
|
||||
vending_product.name = initial(temp.name)
|
||||
vending_product.product_path = typepath
|
||||
if(!start_empty)
|
||||
vending_product.amount = amount
|
||||
vending_product.max_amount = amount
|
||||
vending_product.custom_price = 0
|
||||
vending_product.custom_premium_price = 0
|
||||
vending_product.age_restricted = FALSE
|
||||
recordlist += vending_product
|
||||
BIN
modular_skyrat/modules/assault_operatives/icons/alarm.dmi
Normal file
|
After Width: | Height: | Size: 445 B |
BIN
modular_skyrat/modules/assault_operatives/icons/goldeneye.dmi
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
modular_skyrat/modules/assault_operatives/icons/radial.dmi
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
BIN
modular_skyrat/modules/assault_operatives/icons/sunbeam.dmi
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
modular_skyrat/modules/assault_operatives/sound/icarus_alarm.ogg
Normal file
BIN
modular_skyrat/modules/assault_operatives/sound/sunbeam_fire.ogg
Normal file
BIN
modular_skyrat/modules/assault_operatives/sound/sunbeam_loop.ogg
Normal file
@@ -1,6 +1,3 @@
|
||||
/// A turf flag, much higher than what the other turf flags are at because I don't want to cause conflicts by accident.
|
||||
#define CAN_DECAY_BREAK_1 (1<<23)
|
||||
|
||||
/turf/open/floor
|
||||
turf_flags = CAN_BE_DIRTY_1 | CAN_DECAY_BREAK_1 // We do it this way because we can then easily pick what we don't want to be broken.
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
/obj/projectile/bullet/a792x57
|
||||
name = "7.92x57 bullet"
|
||||
damage = 45
|
||||
damage = 35
|
||||
armour_penetration = 5
|
||||
wound_bonus = 15
|
||||
wound_falloff_tile = 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/obj/item/gun/ballistic/automatic/fg42
|
||||
name = "\improper FG-42"
|
||||
name = "\improper FGP-90"
|
||||
desc = "A German paratrooper rifle designed to be used at long range chambered in 7.92x57mm. Most likely a reproduction of the original."
|
||||
icon = 'modular_skyrat/modules/gunsgalore/icons/guns/gunsgalore_guns40x32.dmi'
|
||||
icon_state = "fg42"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/obj/item/gun/ballistic/automatic/mg34
|
||||
name = "\improper MG-34"
|
||||
name = "\improper MG-4T"
|
||||
desc = "A reproduction of the German MG-34 general purpose machine gun, this one is a revision from the 2200's and was one of several thousand distributed to SolFed expedition teams. It has been rechambered to fire 7.92mm Mauser instead of 7.62mm NATO."
|
||||
icon = 'modular_skyrat/modules/gunsgalore/icons/guns/gunsgalore_guns40x32.dmi'
|
||||
lefthand_file = 'modular_skyrat/modules/gunsgalore/icons/guns/gunsgalore_lefthand.dmi'
|
||||
@@ -104,7 +104,7 @@
|
||||
#define BARREL_COOLDOWN_RATE 2
|
||||
|
||||
/obj/item/gun/ballistic/automatic/mg34/mg42
|
||||
name = "\improper Armadyne MG-9V GPMG"
|
||||
name = "\improper MG-9V GPMG"
|
||||
desc = "An updated version of the German Maschinengewehr 42 machine gun chambered in 7.92 Mauser, it has a bipod for better stability when deployed. It is a reproduction manufactured by the Oldarms division of the Armadyne Corporation."
|
||||
icon_state = "mg42"
|
||||
base_icon_state = "mg42"
|
||||
@@ -212,5 +212,5 @@
|
||||
icon_state = "mg42_drum"
|
||||
ammo_type = /obj/item/ammo_casing/realistic/a792x57
|
||||
caliber = "a792x57"
|
||||
max_ammo = 250 // It's a lot, but the gun overheats.
|
||||
max_ammo = 150 // It's a lot, but the gun overheats.
|
||||
multiple_sprites = AMMO_BOX_FULL_EMPTY_BASIC
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/obj/item/gun/ballistic/automatic/ppsh
|
||||
name = "\improper PPSh-41"
|
||||
name = "\improper Asha 76"
|
||||
desc = "A reproduction of a simple Soviet SMG chambered in 7.62x25 Tokarev rounds. Its heavy wooden stock and leather breech buffer help absorb the bolt’s heavy recoil, making it great for spraying and praying. Uraaaa!"
|
||||
icon = 'modular_skyrat/modules/gunsgalore/icons/guns/gunsgalore_guns40x32.dmi'
|
||||
icon_state = "ppsh"
|
||||
@@ -26,7 +26,7 @@
|
||||
eject_sound = 'modular_skyrat/modules/gunsgalore/sound/guns/interact/smg_magout.ogg'
|
||||
|
||||
/obj/item/ammo_box/magazine/ppsh
|
||||
name = "ppsh-41 magazine (7.62x25mm)"
|
||||
name = "Asha 76 magazine (7.62x25mm)"
|
||||
icon = 'modular_skyrat/modules/gunsgalore/icons/guns/gunsgalore_items.dmi'
|
||||
icon_state = "ppsh"
|
||||
ammo_type = /obj/item/ammo_casing/realistic/a762x25
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/obj/item/gun/ballistic/automatic/stg
|
||||
name = "\improper Armadyne StG-45"
|
||||
name = "\improper StG-99"
|
||||
desc = "A reproduction of the Sturmgewehr 44 German infantry rifle chambered in 7.92mm, manufactured by the Oldarms division of the Armadyne Corporation."
|
||||
icon = 'modular_skyrat/modules/gunsgalore/icons/guns/gunsgalore_guns40x32.dmi'
|
||||
icon_state = "stg"
|
||||
|
||||
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 458 B |
@@ -37,7 +37,7 @@
|
||||
// Fills the role of a low damage, high magazine capacity magdump gun.
|
||||
/obj/item/gun/ballistic/automatic/cfa_wildcat
|
||||
name = "\improper CFA Wildcat"
|
||||
desc = "An old SMG, this one is chambered in .32, a very common and dirt-cheap cartridge. It has <b><span style='color:purple'>Cantalan Federal Arms</span></b> etched above the magazine well."
|
||||
desc = "An old SMG, this one is chambered in .32, a very common and dirt-cheap cartridge. It has Cantalan Federal Arms etched above the magazine well."
|
||||
icon = 'modular_skyrat/modules/modular_weapons/icons/obj/guns/projectile40x32.dmi'
|
||||
icon_state = "mp5"
|
||||
inhand_icon_state = "arg"
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
/obj/item/gun/ballistic/automatic/cfa_lynx
|
||||
name = "\improper CFA Lynx"
|
||||
desc = "A carbine with a high magazine capacity. Chambered in 4.6x30mm. It has <b><span style='color:purple'>Cantalan Federal Arms</span></b> etched above the magazine well."
|
||||
desc = "A carbine with a high magazine capacity. Chambered in 4.6x30mm. It has Cantalan Federal Arms etched above the magazine well."
|
||||
icon = 'modular_skyrat/modules/modular_weapons/icons/obj/guns/projectile.dmi'
|
||||
icon_state = "cfa-lynx"
|
||||
inhand_icon_state = "arg"
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
fire_sound_volume = 100
|
||||
bolt_wording = "fuckin' slide"
|
||||
reload_time = 0 //FAST AS FUCK BOIS!
|
||||
var/unrestricted = FALSE
|
||||
|
||||
/obj/item/gun/ballistic/automatic/pistol/robohand/unrestricted
|
||||
unrestricted = TRUE
|
||||
|
||||
//Gun actions
|
||||
|
||||
@@ -32,11 +36,12 @@
|
||||
//This is where we are checking if the user has a cybernetic arm to USE the gun. ROBOHAND HAS A ROBO HAND
|
||||
if(ishuman(user))
|
||||
return ..()
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
var/obj/item/bodypart/selected_hand = human_user.get_active_hand()
|
||||
if(selected_hand.status != BODYPART_ROBOTIC)
|
||||
to_chat(user, span_warning("You can't seem to figure out how to use [src], perhaps you need to check the manual?"))
|
||||
return
|
||||
if(!unrestricted)
|
||||
var/mob/living/carbon/human/human_user = user
|
||||
var/obj/item/bodypart/selected_hand = human_user.get_active_hand()
|
||||
if(selected_hand.status != BODYPART_ROBOTIC)
|
||||
to_chat(user, span_warning("You can't seem to figure out how to use [src], perhaps you need to check the manual?"))
|
||||
return
|
||||
. = ..()
|
||||
|
||||
/obj/item/gun/ballistic/automatic/pistol/robohand/insert_magazine(mob/user, obj/item/ammo_box/magazine/inserted_mag, display_message)
|
||||
|
||||
@@ -16,6 +16,7 @@ GLOBAL_LIST_EMPTY(turret_id_refs)
|
||||
if(!length(GLOB.turret_id_refs[system_id]))
|
||||
GLOB.turret_id_refs -= system_id
|
||||
return ..()
|
||||
|
||||
/obj/machinery/turretid
|
||||
var/system_id //The ID system for turrets, will get any turrets with the same ID and put them in controlled turrets
|
||||
|
||||
|
||||
@@ -261,6 +261,8 @@
|
||||
#include "code\__DEFINES\~skyrat_defines\_organ_defines.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\access.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\ammo_defines.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\antagonists.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\armaments.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\augment.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\banning.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\baton_upgrades.dm"
|
||||
@@ -293,6 +295,7 @@
|
||||
#include "code\__DEFINES\~skyrat_defines\mobs.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\obj_flags.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\opposing_force_defines.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\pinpointers.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\pollution.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\projectiles.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\reskin_defines.dm"
|
||||
@@ -306,7 +309,9 @@
|
||||
#include "code\__DEFINES\~skyrat_defines\teshari_clothing_paths.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\tools.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\traits.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\turfs.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\vox_defines.dm"
|
||||
#include "code\__DEFINES\~skyrat_defines\_globalvars\lists\mapping.dm"
|
||||
#include "code\__HELPERS\_lists.dm"
|
||||
#include "code\__HELPERS\_logging.dm"
|
||||
#include "code\__HELPERS\_string_lists.dm"
|
||||
@@ -4636,6 +4641,9 @@
|
||||
#include "modular_skyrat\modules\ammo_workbench\code\ammo_workbench.dm"
|
||||
#include "modular_skyrat\modules\ammo_workbench\code\design_disks.dm"
|
||||
#include "modular_skyrat\modules\apocolypse_of_scythes\code\scythes.dm"
|
||||
#include "modular_skyrat\modules\armaments\code\armament_component.dm"
|
||||
#include "modular_skyrat\modules\armaments\code\armament_entries.dm"
|
||||
#include "modular_skyrat\modules\armaments\code\armament_station.dm"
|
||||
#include "modular_skyrat\modules\ashwalkers\area.dm"
|
||||
#include "modular_skyrat\modules\ashwalkers\code\buildings\ash_clothing_vendor.dm"
|
||||
#include "modular_skyrat\modules\ashwalkers\code\buildings\ash_farming.dm"
|
||||
@@ -4655,6 +4663,27 @@
|
||||
#include "modular_skyrat\modules\ashwalkers\code\items\ash_weapon.dm"
|
||||
#include "modular_skyrat\modules\ashwalkers\code\items\ashwalker_shaman.dm"
|
||||
#include "modular_skyrat\modules\ashwalkers\code\species\Ashwalkers.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\areas.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\assault_operatives.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\assault_operatives_outfits.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\base_alarm.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\dynamic_rulsesets_roundstart.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\goldeneye.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\interrogator.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\misc_items.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\shuttle.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\sunbeam.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\turrets.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\vending_machine.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\__armament_bodyarmor.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\_armament_primary.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\_armaments_secondary.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\armament_explosives.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\armament_headgear.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\armament_medical.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\armament_melee.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\armament_utility.dm"
|
||||
#include "modular_skyrat\modules\assault_operatives\code\armaments\assaultops_armament_station.dm"
|
||||
#include "modular_skyrat\modules\autotransfer\code\autotransfer.dm"
|
||||
#include "modular_skyrat\modules\autotransfer\code\autotransfer_config.dm"
|
||||
#include "modular_skyrat\modules\autotransfer\code\shuttle.dm"
|
||||
|
||||
231
tgui/packages/tgui/interfaces/AntagInfoAssaultops.tsx
Normal file
@@ -0,0 +1,231 @@
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { LabeledList, Stack, Button, Section, ProgressBar, Box, Tabs, Divider } from '../components';
|
||||
import { BooleanLike } from 'common/react';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
|
||||
type Objectives = {
|
||||
count: number;
|
||||
name: string;
|
||||
explanation: string;
|
||||
complete: BooleanLike;
|
||||
}
|
||||
|
||||
type AvailableTargets = {
|
||||
name: string;
|
||||
job: string;
|
||||
}
|
||||
|
||||
type ExtractedTargets = {
|
||||
name: string;
|
||||
job: string;
|
||||
}
|
||||
|
||||
type GoldeneyeKeys = {
|
||||
coord_x: number;
|
||||
coord_y: number;
|
||||
coord_z: number;
|
||||
name: string;
|
||||
ref: string;
|
||||
selected: BooleanLike;
|
||||
}
|
||||
|
||||
type Info = {
|
||||
equipped: Number;
|
||||
required_keys: Number;
|
||||
uploaded_keys: Number;
|
||||
objectives: Objectives[];
|
||||
available_targets: AvailableTargets[];
|
||||
extracted_targets: ExtractedTargets[];
|
||||
goldeneye_keys: GoldeneyeKeys[];
|
||||
};
|
||||
|
||||
export const AntagInfoAssaultops = (props, context) => {
|
||||
const [tab, setTab] = useLocalState(context, 'tab', 1);
|
||||
const { data } = useBackend<Info>(context);
|
||||
const {
|
||||
required_keys,
|
||||
uploaded_keys,
|
||||
objectives,
|
||||
} = data;
|
||||
return (
|
||||
<Window
|
||||
theme="hackerman"
|
||||
width={650}
|
||||
height={650}>
|
||||
<Window.Content>
|
||||
<Stack vertical>
|
||||
<Stack.Item>
|
||||
<Section>
|
||||
<Stack.Item grow={1} align="center">
|
||||
<Box fontSize={0.8} textAlign="right">
|
||||
GoldeneEye Defnet
|
||||
<Box color="green" as="span">
|
||||
Connection Secure
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Section title="GoldenEye Subversion Progress" fontSize="15px">
|
||||
{uploaded_keys >= required_keys ? (
|
||||
<Box fontSize="20px" color="green">
|
||||
GOLDENEYE ACTIVATED, WELL DONE OPERATIVE.
|
||||
</Box>
|
||||
) : (
|
||||
<Stack>
|
||||
<Stack.Item grow>
|
||||
<ProgressBar
|
||||
color="green"
|
||||
value={uploaded_keys}
|
||||
minValue={0}
|
||||
maxValue={required_keys}
|
||||
/>
|
||||
</Stack.Item>
|
||||
<Stack.Item color="yellow" >
|
||||
Required Keycards: {required_keys}
|
||||
</Stack.Item>
|
||||
<Stack.Item color="green">
|
||||
Uploaded Keycards: {uploaded_keys}
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
)}
|
||||
</Section>
|
||||
</Section>
|
||||
<Section title="Objectives">
|
||||
<LabeledList>
|
||||
{objectives.map(objective => (
|
||||
<LabeledList.Item
|
||||
key={objective.count}
|
||||
label={objective.name}
|
||||
color={objective.complete ? 'good' : 'bad'}>
|
||||
{objective.explanation}
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Stack vertical grow mb={1}>
|
||||
<Stack.Item>
|
||||
<Tabs fill>
|
||||
<Tabs.Tab
|
||||
width="100%"
|
||||
selected={tab === 1}
|
||||
onClick={() => setTab(1)}>
|
||||
Targets
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
width="100%"
|
||||
selected={tab === 2}
|
||||
onClick={() => setTab(2)}>
|
||||
GoldenEye Keycards
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
{tab === 1 && (
|
||||
<TargetPrintout />
|
||||
)}
|
||||
{tab === 2 && (
|
||||
<KeyPrintout />
|
||||
)}
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const TargetPrintout = (props, context) => {
|
||||
const { act, data } = useBackend<Info>(context);
|
||||
const {
|
||||
available_targets,
|
||||
extracted_targets,
|
||||
} = data;
|
||||
return (
|
||||
<Section grow>
|
||||
<Box textColor="red" fontSize="20px" mb={1}>Target List</Box>
|
||||
<Stack>
|
||||
<Stack.Item grow>
|
||||
<Section title="Available Targets">
|
||||
<Box textColor="red" mb={2}>
|
||||
These are targets you have not yet extracted a GoldenEye key from.
|
||||
They can be extracted by the in-TERROR-gator.
|
||||
</Box>
|
||||
<LabeledList>
|
||||
{available_targets.map(target => (
|
||||
<LabeledList.Item
|
||||
key={target.name}
|
||||
label={target.name}
|
||||
color="red">
|
||||
{target.job}
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Divider vertical />
|
||||
<Stack.Item grow>
|
||||
<Section title="Extracted Targets">
|
||||
<Box textColor="green" mb={2}>
|
||||
These are targets you have extracted a GoldenEye keycard from.
|
||||
They cannot be extracted again.
|
||||
</Box>
|
||||
<LabeledList>
|
||||
{extracted_targets.map(target => (
|
||||
<LabeledList.Item
|
||||
key={target.name}
|
||||
label={target.name}
|
||||
color="good">
|
||||
{target.job}
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
// Utils have goldeneye key list, current heads of staff, extracted heads
|
||||
// Common target button, track key button
|
||||
|
||||
const KeyPrintout = (props, context) => {
|
||||
const { act, data } = useBackend<Info>(context);
|
||||
const {
|
||||
goldeneye_keys,
|
||||
} = data;
|
||||
return (
|
||||
<Section grow>
|
||||
<Box textColor="red" fontSize="20px">GoldenEye Keycards</Box>
|
||||
<Box mb={1}>
|
||||
A list of GoldenEye keycards currently in existence.
|
||||
Select one to track where it is using your hud.
|
||||
</Box>
|
||||
<Stack vertical fill>
|
||||
<Stack.Item>
|
||||
<Section>
|
||||
<Stack vertical>
|
||||
{goldeneye_keys.map(key => (
|
||||
<Stack.Item key={key.name}>
|
||||
<Button
|
||||
width="100%"
|
||||
textAlign="center"
|
||||
color="yellow"
|
||||
disabled={key.selected}
|
||||
key={key.name}
|
||||
icon="key"
|
||||
content={key.selected ? key.name + ' (' + key.coord_x + ', ' + key.coord_y + ', ' + key.coord_z + ')' + ' (Tracking)' : key.name + ' (' + key.coord_x + ', ' + key.coord_y + ', ' + key.coord_z + ')'}
|
||||
onClick={() => act('track_key', {
|
||||
key_ref: key.ref,
|
||||
})} />
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
160
tgui/packages/tgui/interfaces/ArmamentStation.js
Normal file
@@ -0,0 +1,160 @@
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Section, Stack, Box, Divider, Button, NoticeBox } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const ArmamentStation = (props, context) => {
|
||||
const [category, setCategory] = useLocalState(context, 'category', '');
|
||||
const [weapon, setArmament] = useLocalState(context, 'weapon');
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
armaments_list = [],
|
||||
card_inserted,
|
||||
card_points,
|
||||
card_name,
|
||||
} = data;
|
||||
return (
|
||||
<Window
|
||||
theme="armament"
|
||||
title="Armament Station"
|
||||
width={1000}
|
||||
height={600}>
|
||||
<Window.Content>
|
||||
<Section grow height="100%" title="Armaments Station">
|
||||
{card_inserted ? (
|
||||
<Stack>
|
||||
<Stack.Item grow fill>
|
||||
<Box>
|
||||
<b>Inserted Card:</b> {card_name}
|
||||
</Box>
|
||||
<Box>
|
||||
<b>Remaining Points:</b> {card_points}
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
icon="eject"
|
||||
fontSize="20px"
|
||||
content="Eject Card"
|
||||
onClick={() => act('eject_card')} />
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
) : (
|
||||
<NoticeBox color="bad">
|
||||
No card inserted.
|
||||
</NoticeBox>
|
||||
)}
|
||||
<Divider />
|
||||
<Stack fill grow>
|
||||
<Stack.Item mr={1}>
|
||||
<Section title="Categories">
|
||||
<Stack vertical>
|
||||
{armaments_list.map(armament_category => (
|
||||
<Stack.Item key={armament_category.category}>
|
||||
<Button
|
||||
width="100%"
|
||||
content={armament_category.category + ' (Pick ' + armament_category.category_limit + ')'}
|
||||
selected={category === armament_category.category}
|
||||
onClick={() =>
|
||||
setCategory(armament_category.category)} />
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Divider vertical />
|
||||
<Stack.Item grow mr={1}>
|
||||
<Section title={category} scrollable fill height="480px">
|
||||
{armaments_list.map(armament_category => (
|
||||
armament_category.category === category && (
|
||||
armament_category.subcategories.map(subcat => (
|
||||
<Section
|
||||
key={subcat.subcategory}
|
||||
title={subcat.subcategory}>
|
||||
<Stack vertical>
|
||||
{subcat.items.map(item => (
|
||||
<Stack.Item key={item.ref}>
|
||||
<Button
|
||||
fontSize="15px"
|
||||
textAlign="center"
|
||||
selected={weapon === item.ref}
|
||||
disabled={item.purchased >= item.quantity
|
||||
|| item.purchased >= item.quantity}
|
||||
width="100%"
|
||||
key={item.ref}
|
||||
onClick={() =>
|
||||
setArmament(item.ref)}>
|
||||
<img
|
||||
src={`data:image/jpeg;base64,${item.icon}`}
|
||||
style={{
|
||||
'vertical-align': 'middle',
|
||||
'horizontal-align': 'middle',
|
||||
}}
|
||||
/>
|
||||
{item.name}
|
||||
</Button>
|
||||
</Stack.Item>
|
||||
))}
|
||||
</Stack>
|
||||
</Section>
|
||||
))
|
||||
)
|
||||
))}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
<Divider vertical />
|
||||
<Stack.Item width="20%">
|
||||
<Section title="Selected Armament">
|
||||
{armaments_list.map(armament_category => (
|
||||
armament_category.subcategories.map(subcat => (
|
||||
subcat.items.map(item => (
|
||||
item.ref === weapon && (
|
||||
<Stack vertical>
|
||||
<Stack.Item>
|
||||
<Box key={item.ref}>
|
||||
<img
|
||||
height="100%"
|
||||
width="100%"
|
||||
src={`data:image/jpeg;base64,${item.icon}`}
|
||||
style={{
|
||||
'vertical-align': 'middle',
|
||||
'horizontal-align': 'middle',
|
||||
'-ms-interpolation-mode': 'nearest-neighbor',
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
{item.description}
|
||||
</Stack.Item>
|
||||
<Stack.Item
|
||||
textColor={(item.quantity - item.purchased) <= 0 ? "red" : "green"}>
|
||||
{'Quantity Remaining: ' + (item.quantity - item.purchased)}
|
||||
</Stack.Item>
|
||||
<Stack.Item
|
||||
textColor={(item.cost > card_points || !card_inserted) ? "red" : "green"}>
|
||||
{'Cost: ' + item.cost}
|
||||
</Stack.Item>
|
||||
<Stack.Item>
|
||||
<Button
|
||||
content="Buy"
|
||||
textAlign="center"
|
||||
width="100%"
|
||||
disabled={item.cost > card_points
|
||||
|| item.purchased >= item.quantity}
|
||||
onClick={() => act('equip_item', {
|
||||
armament_ref: item.ref })}
|
||||
/>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
)
|
||||
))
|
||||
))
|
||||
))}
|
||||
</Section>
|
||||
</Stack.Item>
|
||||
</Stack>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { Antagonist, Category } from "../base";
|
||||
import { multiline } from "common/string";
|
||||
|
||||
export const OPERATIVE_MECHANICAL_DESCRIPTION = multiline`
|
||||
Attain all possible GoldenEye authentication keys and use them to activate
|
||||
the GoldenEye. These keys use mindfragments of Nanotrasen heads to generate
|
||||
the key. Use the interrogator to extract these mindfragments.
|
||||
`;
|
||||
|
||||
const AssaultOperative: Antagonist = {
|
||||
key: "assaultoperative",
|
||||
name: "Assault Operative",
|
||||
description: [
|
||||
multiline`
|
||||
Good afternoon 0013, you have been selected to join an elite strike team
|
||||
designated to locating and forging GoldenEye keys. Your mission is to
|
||||
get these keys and use them to turn Nanotrasens GoldenEye defence
|
||||
network against them. The GoldenEye network requires 3 keys to activate.
|
||||
`,
|
||||
|
||||
OPERATIVE_MECHANICAL_DESCRIPTION,
|
||||
],
|
||||
category: Category.Roundstart,
|
||||
};
|
||||
|
||||
export default AssaultOperative;
|
||||