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>
This commit is contained in:
Gandalf
2022-03-28 03:27:25 +01:00
committed by GitHub
parent e38eb62316
commit e2a68d3b15
64 changed files with 12655 additions and 5866 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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,

View File

@@ -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
))

View File

@@ -0,0 +1 @@
GLOBAL_LIST_EMPTY(assault_operative_start)

View 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

View 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"

View File

@@ -0,0 +1 @@
#define TRACK_GOLDENEYE 4

View 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)

View File

@@ -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

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)

View File

@@ -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.\""),

View File

@@ -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>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

View File

@@ -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',
)

View 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()

View 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

View 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]!"))

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 B

View 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,
)

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View 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

View File

@@ -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)

View 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

View 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)

View 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)

View 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

View 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

View 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

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

View 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.

View File

@@ -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

View File

@@ -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"

View File

@@ -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

View File

@@ -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 bolts 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

View File

@@ -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"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 380 B

After

Width:  |  Height:  |  Size: 458 B

View File

@@ -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"

View File

@@ -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)

View File

@@ -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

View File

@@ -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"

View 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 &nbsp;
<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>
);
};

View 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',
}}
/>
&nbsp;{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>
);
};

View File

@@ -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;