Merge remote-tracking branch 'polaris/master' into pixel_projectiles

This commit is contained in:
kevinz000
2018-12-23 11:31:48 -08:00
70 changed files with 1755 additions and 372 deletions

View File

@@ -5,10 +5,10 @@
var/datum/map_template/template
var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in map_templates
var/map = input(usr, "Choose a Map Template to place at your CURRENT LOCATION","Place Map Template") as null|anything in SSmapping.map_templates
if(!map)
return
template = map_templates[map]
template = SSmapping.map_templates[map]
var/orientation = text2dir(input(usr, "Choose an orientation for this Map Template.", "Orientation") as null|anything in list("North", "South", "East", "West"))
if(!orientation)
@@ -41,10 +41,10 @@
var/datum/map_template/template
var/map = input(usr, "Choose a Map Template to place on a new Z-level.","Place Map Template") as null|anything in map_templates
var/map = input(usr, "Choose a Map Template to place on a new Z-level.","Place Map Template") as null|anything in SSmapping.map_templates
if(!map)
return
template = map_templates[map]
template = SSmapping.map_templates[map]
var/orientation = text2dir(input(usr, "Choose an orientation for this Map Template.", "Orientation") as null|anything in list("North", "South", "East", "West"))
if(!orientation)
@@ -76,7 +76,7 @@
var/datum/map_template/M = new(map, "[map]")
if(M.preload_size(map))
to_chat(usr, "Map template '[map]' ready to place ([M.width]x[M.height])")
map_templates[M.name] = M
SSmapping.map_templates[M.name] = M
message_admins("<span class='adminnotice'>[key_name_admin(usr)] has uploaded a map template ([map])</span>")
else
to_chat(usr, "Map template '[map]' failed to load properly")

View File

@@ -244,42 +244,34 @@ datum/gear/suit/duster
/datum/gear/suit/roles/poncho/cloak/cargo
display_name = "cloak, cargo"
path = /obj/item/clothing/accessory/poncho/roles/cloak/cargo
allowed_roles = list("Cargo Technician","Quartermaster")
/datum/gear/suit/roles/poncho/cloak/mining
display_name = "cloak, mining"
path = /obj/item/clothing/accessory/poncho/roles/cloak/mining
allowed_roles = list("Quartermaster","Shaft Miner")
/datum/gear/suit/roles/poncho/cloak/security
display_name = "cloak, security"
path = /obj/item/clothing/accessory/poncho/roles/cloak/security
allowed_roles = list("Head of Security","Detective","Warden","Security Officer")
/datum/gear/suit/roles/poncho/cloak/service
display_name = "cloak, service"
path = /obj/item/clothing/accessory/poncho/roles/cloak/service
allowed_roles = list("Head of Personnel","Bartender","Botanist","Janitor","Chef","Librarian")
/datum/gear/suit/roles/poncho/cloak/engineer
display_name = "cloak, engineer"
path = /obj/item/clothing/accessory/poncho/roles/cloak/engineer
allowed_roles = list("Chief Engineer","Station Engineer")
/datum/gear/suit/roles/poncho/cloak/atmos
display_name = "cloak, atmos"
path = /obj/item/clothing/accessory/poncho/roles/cloak/atmos
allowed_roles = list("Chief Engineer","Atmospheric Technician")
/datum/gear/suit/roles/poncho/cloak/research
display_name = "cloak, science"
path = /obj/item/clothing/accessory/poncho/roles/cloak/research
allowed_roles = list("Research Director","Scientist", "Roboticist", "Xenobiologist")
/datum/gear/suit/roles/poncho/cloak/medical
display_name = "cloak, medical"
path = /obj/item/clothing/accessory/poncho/roles/cloak/medical
allowed_roles = list("Medical Doctor","Chief Medical Officer","Chemist","Paramedic","Geneticist", "Psychiatrist")
/datum/gear/suit/unathi_robe
display_name = "roughspun robe"
@@ -499,4 +491,4 @@ datum/gear/suit/duster
/datum/gear/suit/snowsuit/cargo
display_name = "snowsuit, supply"
path = /obj/item/clothing/suit/storage/snowsuit/cargo
allowed_roles = list("Quartermaster","Shaft Miner","Cargo Technician","Head of Personnel")
allowed_roles = list("Quartermaster","Shaft Miner","Cargo Technician","Head of Personnel")

View File

@@ -1,7 +1,5 @@
var/list/error_last_seen = list()
// error_cooldown items will either be positive (cooldown time) or negative (silenced error)
// If negative, starts at -1, and goes down by 1 each time that error gets skipped
var/list/error_cooldown = list()
var/total_runtimes = 0
var/total_runtimes_skipped = 0
// The ifdef needs to be down here, since the error viewer references total_runtimes
@@ -10,18 +8,18 @@ var/total_runtimes_skipped = 0
if(!istype(e)) // Something threw an unusual exception
log_error("\[[time_stamp()]] Uncaught exception: [e]")
return ..()
if(!error_last_seen) // A runtime is occurring too early in start-up initialization
if(!GLOB.error_last_seen) // A runtime is occurring too early in start-up initialization
return ..()
total_runtimes++
var/erroruid = "[e.file][e.line]"
var/last_seen = error_last_seen[erroruid]
var/cooldown = error_cooldown[erroruid] || 0
var/last_seen = GLOB.error_last_seen[erroruid]
var/cooldown = GLOB.error_cooldown[erroruid] || 0
if(last_seen == null) // A new error!
error_last_seen[erroruid] = world.time
GLOB.error_last_seen[erroruid] = world.time
last_seen = world.time
if(cooldown < 0)
error_cooldown[erroruid]-- // Used to keep track of skip count for this error
GLOB.error_cooldown[erroruid]-- // Used to keep track of skip count for this error
total_runtimes_skipped++
return // Error is currently silenced, skip handling it
@@ -36,13 +34,13 @@ var/total_runtimes_skipped = 0
spawn(0)
usr = null
sleep(ERROR_SILENCE_TIME)
var/skipcount = abs(error_cooldown[erroruid]) - 1
error_cooldown[erroruid] = 0
var/skipcount = abs(GLOB.error_cooldown[erroruid]) - 1
GLOB.error_cooldown[erroruid] = 0
if(skipcount > 0)
log_error("\[[time_stamp()]] Skipped [skipcount] runtimes in [e.file],[e.line].")
error_cache.logError(e, skipCount = skipcount)
error_last_seen[erroruid] = world.time
error_cooldown[erroruid] = cooldown
GLOB.error_last_seen[erroruid] = world.time
GLOB.error_cooldown[erroruid] = cooldown
// The detailed error info needs some tweaking to make it look nice
var/list/srcinfo = null

View File

@@ -125,7 +125,7 @@
watching_mob = user
GLOB.moved_event.register(watching_mob, src, /obj/machinery/station_map/proc/checkPosition)
GLOB.dir_set_event.register(watching_mob, src, /obj/machinery/station_map/proc/checkPosition)
destroyed_event.register(watching_mob, src, /obj/machinery/station_map/proc/stopWatching)
GLOB.destroyed_event.register(watching_mob, src, /obj/machinery/station_map/proc/stopWatching)
update_use_power(2)
if(bogus)
@@ -154,7 +154,7 @@
M.client.images -= holomap_datum.station_map
GLOB.moved_event.unregister(watching_mob, src)
GLOB.dir_set_event.unregister(watching_mob, src)
destroyed_event.unregister(watching_mob, src)
GLOB.destroyed_event.unregister(watching_mob, src)
watching_mob = null
update_use_power(1)

View File

@@ -0,0 +1,127 @@
// This is a datum attached to objects to make their 'identity' be unknown initially.
// The identitiy and properties of an unidentified object can be determined in-game through a specialized process or by potentially risky trial-and-error.
// This is very similar to a traditional roguelike's identification system, and as such will use certain terms from those to describe them.
// Despite this, unlike a roguelike, objects that do the same thing DO NOT have the same name/appearance/etc.
/datum/identification
var/obj/holder = null // The thing the datum is 'attached' to.
// Holds the true information.
var/true_name = null // The real name of the object. It is copied automatically from holder, on the datum being instantiated.
var/true_desc = null // Ditto, for desc.
var/true_description_info = null // Ditto, for helpful examine panel entries.
var/true_description_fluff = null // Ditto, for lore.
var/true_description_antag = null // Ditto, for antag info (this probably won't get used).
var/identified = IDENTITY_UNKNOWN // Can be IDENTITY_UNKNOWN, IDENTITY_PROPERTIES, IDENTITY_QUALITY, or IDENTITY_FULL.
// Holds what is displayed when not identified sufficently.
var/unidentified_name = null // The name given to the object when not identified. Generated by generate_unidentified_name()
var/unidentified_desc = "You're not too sure what this is."
var/unidentified_description_info = "This object is unidentified, and as such its properties are unknown. Using this object may be dangerous."
// Lists of lists for generating names by combining one from each.
var/list/naming_lists = list()
// What 'identification type' is needed to identify this.
var/identification_type = IDENTITY_TYPE_NONE
/datum/identification/New(obj/new_holder)
ASSERT(new_holder)
holder = new_holder
record_true_identity() // Get all the identifying features from the holder.
update_name() // Then hide them for awhile if needed.
/datum/identification/Destroy()
holder = null
return ..()
// Records the object's inital identifiying features to the datum for future safekeeping.
/datum/identification/proc/record_true_identity()
true_name = holder.name
true_desc = holder.desc
true_description_info = holder.description_info
true_description_fluff = holder.description_fluff
true_description_antag = holder.description_antag
// Formally identifies the holder.
/datum/identification/proc/identify(new_identity = IDENTITY_FULL, mob/user)
if(new_identity & identified) // Already done.
return
identified |= new_identity // Set the bitflag.
if(user)
switch(identified)
if(IDENTITY_QUALITY)
to_chat(user, "<span class='notice'>You've identified \the [holder]'s quality.</span>")
if(IDENTITY_PROPERTIES)
to_chat(user, "<span class='notice'>You've identified \the [holder]'s functionality as a [true_name].</span>")
if(IDENTITY_FULL)
to_chat(user, "<span class='notice'>You've identified \the [holder] as a [true_name], and its quality.</span>")
update_name()
holder.update_icon()
// Reverses identification for whatever reason.
/datum/identification/proc/unidentify(new_identity = IDENTITY_UNKNOWN, mob/user)
identified &= ~new_identity // Unset the bitflag.
update_name()
holder.update_icon()
if(user)
switch(identified) // Give a message based on what's left.
if(IDENTITY_QUALITY)
to_chat(user, span("warning", "You forgot what \the [holder] actually did..."))
if(IDENTITY_PROPERTIES)
to_chat(user, span("warning", "You forgot \the [holder]'s quality..."))
if(IDENTITY_UNKNOWN)
to_chat(user, span("warning", "You forgot everything about \the [holder]."))
// Sets the holder's name to the real name if its properties are identified, or obscures it otherwise.
/datum/identification/proc/update_name()
if(identified & IDENTITY_PROPERTIES)
holder.name = true_name
holder.desc = true_desc
holder.description_info = true_description_info
holder.description_fluff = true_description_fluff
holder.description_antag = true_description_antag
return
if(!unidentified_name)
unidentified_name = generate_unidentified_name()
holder.name = unidentified_name
holder.desc = unidentified_desc
holder.description_info = unidentified_description_info
holder.description_fluff = null
holder.description_antag = null
// Makes a name for an object that is not identified. It picks one string out of each list inside naming_list.
/datum/identification/proc/generate_unidentified_name()
if(!LAZYLEN(naming_lists))
return "unidentified object"
var/list/new_name = list()
for(var/i in naming_lists)
var/list/current_list = i
new_name += pick(current_list)
return new_name.Join(" ")
// Used for tech-based objects.
// Unused for now pending Future Stuff(tm).
/datum/identification/mechanical
naming_lists = list(
list("unidentified", "unknown", "strange", "weird", "unfamiliar", "peculiar", "mysterious", "bizarre", "odd"),
list("device", "apparatus", "gadget", "mechanism", "appliance", "machine", "equipment", "invention", "contraption")
)
identification_type = IDENTITY_TYPE_TECH
// Used for unidentified hypos.
// Their contents can range from genuine medication, expired medicine, illicit drugs, toxins and poisons, and more.
// They are the analog for potions in a traditional roguelike.
/datum/identification/hypo
naming_lists = list(
list("unidentified", "unknown", "unmarked", "blank", "refilled", "custom", "modified", "questionable", "suspicious"),
list("hypospray", "autoinjector")
)
unidentified_desc = "An autoinjector that does not give any indication towards what is inside. \
The case is also sealed tight and the liquids contained cannot be removed except by injecting it into someone. \
Do you feel lucky?"
unidentified_description_info = "A skilled chemist with a specialized machine can identify this autoinjector. \
Blindly using the autoinjector is risky and can be dangerous."
identification_type = IDENTITY_TYPE_CHEMICAL

View File

@@ -0,0 +1,30 @@
// This is on the base /item so badmins can play with it by calling hide_identity().
/obj/item
var/datum/identification/identity = null
var/identity_type = /datum/identification
var/init_hide_identity = FALSE // Set to true to automatically obscure the object on initialization.
/obj/item/Initialize()
if(init_hide_identity)
identity = new identity_type(src)
return ..()
/obj/item/Destroy()
if(identity)
QDEL_NULL(identity)
return ..()
/obj/item/proc/hide_identity() // Mostly for admins to make things secret.
if(!identity)
identity = new identity_type(src)
else
identity.unidentify()
/obj/item/proc/identify(identity_type = IDENTITY_FULL, mob/user)
if(identity)
identity.identify(identity_type, user)
/obj/item/proc/is_identified(identity_type = IDENTITY_FULL)
if(!identity) // No identification datum means nothing to hide.
return TRUE
return identity_type & identity.identified

View File

@@ -188,14 +188,14 @@
// These procs do not relocate the grenade, that's the callers responsibility
/obj/item/integrated_circuit/manipulation/grenade/proc/attach_grenade(var/obj/item/weapon/grenade/G)
attached_grenade = G
destroyed_event.register(attached_grenade, src, /obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade)
GLOB.destroyed_event.register(attached_grenade, src, /obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade)
size += G.w_class
desc += " \An [attached_grenade] is attached to it!"
/obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade()
if(!attached_grenade)
return
destroyed_event.unregister(attached_grenade, src, /obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade)
GLOB.destroyed_event.unregister(attached_grenade, src, /obj/item/integrated_circuit/manipulation/grenade/proc/detach_grenade)
attached_grenade = null
size = initial(size)
desc = initial(desc)

View File

@@ -300,7 +300,7 @@
text_output += "\an [name]"
else
text_output += "\an ["\improper[initial_name]"] labeled '[name]'"
text_output += " which is currently [get_pin_data(IC_INPUT, 1) ? "lit <font color=[led_color]><EFBFBD></font>" : "unlit."]"
text_output += " which is currently [get_pin_data(IC_INPUT, 1) ? "lit <font color=[led_color]>¤</font>" : "unlit."]"
to_chat(user,jointext(text_output,null))
/obj/item/integrated_circuit/output/led/red
@@ -462,8 +462,7 @@
/obj/item/integrated_circuit/output/holographic_projector/proc/destroy_hologram()
hologram.forceMove(src)
qdel(hologram)
QDEL_NULL(hologram)
// holo_beam.End()
// qdel_null(holo_beam)

View File

@@ -1,15 +1,3 @@
var/list/global/map_templates = list()
// Called when the world starts, in world.dm
/proc/load_map_templates()
for(var/T in subtypesof(/datum/map_template))
var/datum/map_template/template = T
if(!(initial(template.mappath))) // If it's missing the actual path its probably a base type or being used for inheritence.
continue
template = new T()
map_templates[template.name] = template
return TRUE
/datum/map_template
var/name = "Default Template Name"
var/desc = "Some text should go here. Maybe."
@@ -27,8 +15,6 @@ var/list/global/map_templates = list()
var/allow_duplicates = FALSE // If false, only one map template will be spawned by the game. Doesn't affect admins spawning then manually.
var/discard_prob = 0 // If non-zero, there is a chance that the map seeding algorithm will skip this template when selecting potential templates to use.
var/static/dmm_suite/maploader = new
/datum/map_template/New(path = null, rename = null)
if(path)
mappath = path
@@ -39,7 +25,7 @@ var/list/global/map_templates = list()
name = rename
/datum/map_template/proc/preload_size(path, orientation = SOUTH)
var/bounds = maploader.load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE, orientation=orientation)
var/bounds = SSmapping.maploader.load_map(file(path), 1, 1, 1, cropMap=FALSE, measureOnly=TRUE, orientation=orientation)
if(bounds)
width = bounds[MAP_MAXX] // Assumes all templates are rectangular, have a single Z level, and begin at 1,1,1
height = bounds[MAP_MAXY]
@@ -91,7 +77,7 @@ var/list/global/map_templates = list()
x = round((world.maxx - width)/2)
y = round((world.maxy - height)/2)
var/list/bounds = maploader.load_map(file(mappath), x, y, no_changeturf = TRUE, orientation=orientation)
var/list/bounds = SSmapping.maploader.load_map(file(mappath), x, y, no_changeturf = TRUE, orientation=orientation)
if(!bounds)
return FALSE
@@ -116,7 +102,7 @@ var/list/global/map_templates = list()
if(annihilate)
annihilate_bounds(old_T, centered, orientation)
var/list/bounds = maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE, orientation = orientation)
var/list/bounds = SSmapping.maploader.load_map(file(mappath), T.x, T.y, T.z, cropMap=TRUE, orientation = orientation)
if(!bounds)
return
@@ -175,8 +161,8 @@ var/list/global/map_templates = list()
var/list/priority_submaps = list() // Submaps that will always be placed.
// Lets go find some submaps to make.
for(var/map in map_templates)
var/datum/map_template/MT = map_templates[map]
for(var/map in SSmapping.map_templates)
var/datum/map_template/MT = SSmapping.map_templates[map]
if(!MT.allow_duplicates && MT.loaded > 0) // This probably won't be an issue but we might as well.
continue
if(!istype(MT, desired_map_template_type)) // Not the type wanted.

View File

@@ -181,6 +181,23 @@ the artifact triggers the rage.
accuracy_dispersion = 3 // Ditto.
evasion = -45 // Too angry to dodge.
// Non-cult version of deep wounds.
// Surprisingly, more dangerous.
/datum/modifier/grievous_wounds
name = "grievous wounds"
desc = "Your wounds are not easily mended."
on_created_text = "<span class='critical'>Your wounds pain you greatly.</span>"
on_expired_text = "<span class='notice'>The pain lulls.</span>"
stacks = MODIFIER_STACK_EXTEND
incoming_healing_percent = 0.50 // 50% less healing.
disable_duration_percent = 1.22 // 22% longer disables.
bleeding_rate_percent = 1.20 // 20% more bleeding.
accuracy_dispersion = 2 // A combination of fear and immense pain or damage reults in a twitching firing arm. Flee.

View File

@@ -8,6 +8,7 @@
var/obj/item/weapon/cell/power_supply //What type of power cell this uses
var/charge_cost = 240 //How much energy is needed to fire.
var/accept_cell_type = /obj/item/weapon/cell/device
var/cell_type = /obj/item/weapon/cell/device/weapon
projectile_type = /obj/item/projectile/beam/practice
@@ -89,13 +90,13 @@
if(self_recharge || battery_lock)
user << "<span class='notice'>[src] does not have a battery port.</span>"
return
if(istype(C, /obj/item/weapon/cell/device))
var/obj/item/weapon/cell/device/P = C
if(istype(C, accept_cell_type))
var/obj/item/weapon/cell/P = C
if(power_supply)
user << "<span class='notice'>[src] already has a power cell.</span>"
else
user.visible_message("[user] is reloading [src].", "<span class='notice'>You start to insert [P] into [src].</span>")
if(do_after(user, 10))
if(do_after(user, 5 * P.w_class))
user.remove_from_mob(P)
power_supply = P
P.loc = src
@@ -175,6 +176,13 @@
icon_state = "[modifystate][ratio]"
else
icon_state = "[initial(icon_state)][ratio]"
else if(power_supply)
if(modifystate)
icon_state = "[modifystate]"
else
icon_state = "[initial(icon_state)]"
if(!ignore_inhands) update_held_icon()
/obj/item/weapon/gun/energy/proc/start_recharge()

View File

@@ -0,0 +1,40 @@
/*
* Contains weapons primarily using the 'grappling hook' projectiles.
*/
/obj/item/weapon/gun/energy/hooklauncher
name = "gravity whip"
desc = "A large, strange gauntlet."
icon_state = "gravwhip"
item_state = "gravwhip"
fire_sound = 'sound/effects/zzzt.ogg'
fire_sound_text = "laser blast"
fire_delay = 15
charge_cost = 300
cell_type = /obj/item/weapon/cell/device/weapon
projectile_type = /obj/item/projectile/energy/hook
// An easily concealable not-ripoff version. It would be silenced, if it didn't make it blatant you're the one using it.
/obj/item/weapon/gun/energy/hooklauncher/ring
name = "ominous ring"
desc = "A small ring with strange symbols engraved upon it."
icon = 'icons/obj/clothing/rings.dmi'
icon_state = "seal-signet"
item_state = "concealed"
w_class = ITEMSIZE_TINY
cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien
battery_lock = TRUE
charge_cost = 400
charge_meter = FALSE
projectile_type = /obj/item/projectile/energy/hook/ring
firemodes = list(
list(mode_name="manipulate", fire_delay=15, projectile_type=/obj/item/projectile/energy/hook/ring, charge_cost = 400),
list(mode_name="battle", fire_delay=8, projectile_type=/obj/item/projectile/beam/xray, charge_cost = 260),
)

View File

@@ -151,6 +151,42 @@
toggle_scope(2.0)
/obj/item/weapon/gun/energy/monorifle
name = "antique mono-rifle"
desc = "An old laser rifle. This one can only fire once before requiring recharging."
description_fluff = "Modeled after ancient hunting rifles, this rifle was dubbed the 'Rainy Day Special' by some, due to its use as some barmens' fight-stopper of choice. One shot is all it takes, or so they say."
icon_state = "eshotgun"
item_state = "shotgun"
fire_sound = 'sound/weapons/gauss_shoot.ogg'
origin_tech = list(TECH_COMBAT = 6, TECH_MATERIAL = 4, TECH_POWER = 3)
projectile_type = /obj/item/projectile/beam/sniper
slot_flags = SLOT_BACK
charge_cost = 1300
fire_delay = 20
force = 8
w_class = ITEMSIZE_LARGE
accuracy = 10
scoped_accuracy = 15
var/scope_multiplier = 1.5
/obj/item/weapon/gun/energy/monorifle/verb/sights()
set category = "Object"
set name = "Aim Down Sights"
set popup_menu = 1
toggle_scope(scope_multiplier)
/obj/item/weapon/gun/energy/monorifle/combat
name = "combat mono-rifle"
desc = "A modernized version of the mono-rifle. This one can fire twice before requiring recharging."
description_fluff = "A modern design produced by a company once working from Saint Columbia, based on the antique mono-rifle 'Rainy Day Special' design."
icon_state = "ecshotgun"
item_state = "cshotgun"
charge_cost = 1000
force = 12
accuracy = 0
scoped_accuracy = 20
////////Laser Tag////////////////////
/obj/item/weapon/gun/energy/lasertag

View File

@@ -186,6 +186,7 @@ obj/item/weapon/gun/energy/staff/focus
desc = "A massive weapon designed to pressure the opposition by raining down a torrent of energy pellets."
icon_state = "dakkalaser"
item_state = "dakkalaser"
wielded_item_state = "dakkalaser-wielded"
fire_sound = 'sound/weapons/Laser.ogg'
w_class = ITEMSIZE_HUGE
charge_cost = 24 // 100 shots, it's a spray and pray (to RNGesus) weapon.
@@ -200,4 +201,94 @@ obj/item/weapon/gun/energy/staff/focus
list(mode_name="single shot", burst = 1, burst_accuracy = list(75), dispersion = list(0), charge_cost = 24),
list(mode_name="five shot burst", burst = 5, burst_accuracy = list(75,75,75,75,75), dispersion = list(1,1,1,1,1)),
list(mode_name="ten shot burst", burst = 10, burst_accuracy = list(75,75,75,75,75,75,75,75,75,75), dispersion = list(2,2,2,2,2,2,2,2,2,2)),
)
)
/obj/item/weapon/gun/energy/maghowitzer
name = "portable MHD howitzer"
desc = "A massive weapon designed to destroy fortifications with a stream of molten tungsten."
description_fluff = "A weapon designed by joint cooperation of NanoTrasen, Hephaestus, and SCG scientists. Everything else is red tape and black highlighters."
description_info = "This weapon requires a wind-up period before being able to fire. Clicking on a target will create a beam between you and its turf, starting the timer. Upon completion, it will fire at the designated location."
icon_state = "mhdhowitzer"
item_state = "mhdhowitzer"
wielded_item_state = "mhdhowitzer-wielded"
fire_sound = 'sound/weapons/emitter2.ogg'
w_class = ITEMSIZE_HUGE
charge_cost = 10000 // Uses large cells, can at max have 3 shots.
projectile_type = /obj/item/projectile/beam/tungsten
cell_type = /obj/item/weapon/cell/high
accept_cell_type = /obj/item/weapon/cell
accuracy = 75
charge_meter = 0
one_handed_penalty = 30
var/power_cycle = FALSE
/obj/item/weapon/gun/energy/maghowitzer/proc/pick_random_target(var/turf/T)
var/foundmob = FALSE
var/foundmobs = list()
for(var/mob/living/L in T.contents)
foundmob = TRUE
foundmobs += L
if(foundmob)
var/return_target = pick(foundmobs)
return return_target
return FALSE
/obj/item/weapon/gun/energy/maghowitzer/attack(atom/A, mob/living/user, def_zone)
if(power_cycle)
to_chat(user, "<span class='notice'>\The [src] is already powering up!</span>")
return 0
var/turf/target_turf = get_turf(A)
var/beameffect = user.Beam(target_turf,icon_state="sat_beam",icon='icons/effects/beam.dmi',time=31, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=3)
if(beameffect)
user.visible_message("<span class='cult'>[user] aims \the [src] at \the [A].</span>")
if(power_supply && power_supply.charge >= charge_cost) //Do a delay for pointblanking too.
power_cycle = TRUE
if(do_after(user, 30))
if(A.loc == target_turf)
..(A, user, def_zone)
else
var/rand_target = pick_random_target(target_turf)
if(rand_target)
..(rand_target, user, def_zone)
else
..(target_turf, user, def_zone)
else
if(beameffect)
qdel(beameffect)
power_cycle = FALSE
else
..(A, user, def_zone) //If it can't fire, just bash with no delay.
/obj/item/weapon/gun/energy/maghowitzer/afterattack(atom/A, mob/living/user, adjacent, params)
if(power_cycle)
to_chat(user, "<span class='notice'>\The [src] is already powering up!</span>")
return 0
var/turf/target_turf = get_turf(A)
var/beameffect = user.Beam(target_turf,icon_state="sat_beam",icon='icons/effects/beam.dmi',time=31, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=3)
if(beameffect)
user.visible_message("<span class='cult'>[user] aims \the [src] at \the [A].</span>")
if(!power_cycle)
power_cycle = TRUE
if(do_after(user, 30))
if(A.loc == target_turf)
..(A, user, adjacent, params)
else
var/rand_target = pick_random_target(target_turf)
if(rand_target)
..(rand_target, user, adjacent, params)
else
..(target_turf, user, adjacent, params)
else
if(beameffect)
qdel(beameffect)
handle_click_empty(user)
power_cycle = FALSE
else
to_chat(user, "<span class='notice'>\The [src] is already powering up!</span>")

View File

@@ -0,0 +1,141 @@
/obj/item/weapon/gun/magnetic/matfed
name = "portable phoron bore"
desc = "A large man-portable tunnel bore, using phorogenic plasma blasts. Point away from user."
description_fluff = "An aging Grayson Manufactories mining tool used for rapidly digging through rock. Mass production was discontinued when many of the devices were stolen and used to break into a high security facility by Boiling Point drones."
description_antag = "This device is exceptional at breaking down walls, though it is incredibly loud when doing so."
description_info = "The projectile of this tool will travel six tiles before dissipating, excavating mineral walls as it does so. It can be reloaded with phoron sheets."
icon_state = "bore"
item_state = "bore"
wielded_item_state = "bore-wielded"
one_handed_penalty = 5
projectile_type = /obj/item/projectile/bullet/magnetic/bore
gun_unreliable = 0
power_cost = 750
load_type = /obj/item/stack/material
var/mat_storage = 0 // How much material is stored inside? Input in multiples of 2000 as per auto/protolathe.
var/max_mat_storage = 8000 // How much material can be stored inside?
var/mat_cost = 500 // How much material is used per-shot?
var/ammo_material = MAT_PHORON
var/loading = FALSE
/obj/item/weapon/gun/magnetic/matfed/examine(mob/user)
. = ..()
show_ammo(user)
/obj/item/weapon/gun/magnetic/matfed/update_icon()
var/list/overlays_to_add = list()
if(removable_components)
if(cell)
overlays_to_add += image(icon, "[icon_state]_cell")
if(capacitor)
overlays_to_add += image(icon, "[icon_state]_capacitor")
if(!cell || !capacitor)
overlays_to_add += image(icon, "[icon_state]_red")
else if(capacitor.charge < power_cost)
overlays_to_add += image(icon, "[icon_state]_amber")
else
overlays_to_add += image(icon, "[icon_state]_green")
if(mat_storage)
overlays_to_add += image(icon, "[icon_state]_loaded")
overlays = overlays_to_add
..()
/obj/item/weapon/gun/magnetic/matfed/attack_hand(var/mob/user) // It doesn't keep a loaded item inside.
if(user.get_inactive_hand() == src)
var/obj/item/removing
if(cell && removable_components)
removing = cell
cell = null
if(removing)
removing.forceMove(get_turf(src))
user.put_in_hands(removing)
user.visible_message("<span class='notice'>\The [user] removes \the [removing] from \the [src].</span>")
playsound(loc, 'sound/machines/click.ogg', 10, 1)
update_icon()
return
. = ..()
/obj/item/weapon/gun/magnetic/matfed/check_ammo()
if(mat_storage - mat_cost >= 0)
return TRUE
return FALSE
/obj/item/weapon/gun/magnetic/matfed/use_ammo()
mat_storage -= mat_cost
/obj/item/weapon/gun/magnetic/matfed/show_ammo(var/mob/user)
if(mat_storage)
to_chat(user, "<span class='notice'>It has [mat_storage] out of [max_mat_storage] units of [ammo_material] loaded.</span>")
/obj/item/weapon/gun/magnetic/matfed/attackby(var/obj/item/thing, var/mob/user)
if(removable_components)
if(istype(thing, /obj/item/weapon/cell))
if(cell)
to_chat(user, "<span class='warning'>\The [src] already has \a [cell] installed.</span>")
return
cell = thing
user.drop_from_inventory(cell)
cell.forceMove(src)
playsound(loc, 'sound/machines/click.ogg', 10, 1)
user.visible_message("<span class='notice'>\The [user] slots \the [cell] into \the [src].</span>")
update_icon()
return
if(thing.is_screwdriver())
if(!capacitor)
to_chat(user, "<span class='warning'>\The [src] has no capacitor installed.</span>")
return
capacitor.forceMove(get_turf(src))
user.put_in_hands(capacitor)
user.visible_message("<span class='notice'>\The [user] unscrews \the [capacitor] from \the [src].</span>")
playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1)
capacitor = null
update_icon()
return
if(istype(thing, /obj/item/weapon/stock_parts/capacitor))
if(capacitor)
to_chat(user, "<span class='warning'>\The [src] already has \a [capacitor] installed.</span>")
return
capacitor = thing
user.drop_from_inventory(capacitor)
capacitor.forceMove(src)
playsound(loc, 'sound/machines/click.ogg', 10, 1)
power_per_tick = (power_cost*0.15) * capacitor.rating
user.visible_message("<span class='notice'>\The [user] slots \the [capacitor] into \the [src].</span>")
update_icon()
return
if(istype(thing, load_type))
loading = TRUE
var/obj/item/stack/material/M = thing
if(!M.material || M.material.name != ammo_material)
return
if(mat_storage + 2000 > max_mat_storage)
to_chat(user, "<span class='warning'>\The [src] cannot hold more [ammo_material].</span>")
return
var/can_hold_val = 0
while(can_hold_val < round(max_mat_storage / 2000))
if(mat_storage + 2000 <= max_mat_storage && do_after(user,1.5 SECONDS))
can_hold_val ++
mat_storage += 2000
playsound(loc, 'sound/effects/phasein.ogg', 15, 1)
else
loading = FALSE
break
M.use(can_hold_val)
user.visible_message("<span class='notice'>\The [user] loads \the [src] with \the [M].</span>")
playsound(loc, 'sound/weapons/flipblade.ogg', 50, 1)
update_icon()
return
. = ..()

View File

@@ -0,0 +1,192 @@
/*
* File containing special 'hook' projectiles. Function is dictated by the launcher's intent.
*/
/obj/item/projectile/energy/hook
name = "graviton sphere"
icon_state = "bluespace"
var/beam_state = "b_beam"
damage = 5
step_delay = 2
damage_type = BURN
check_armour = "energy"
armor_penetration = 15
var/impact_sound = 'sound/effects/uncloak.ogg'
var/crack_sound = 'sound/effects/teleport.ogg'
var/target_distance = null // Shamelessly stolen from arcing projectiles.
var/my_tracking_beam = null // Beam made by the launcher. Tracked here to destroy it in time with the impact.
var/launcher_intent = null // Stores the launcher's intent.
var/disarm_chance = 60 // Chance for a successful disarm hit. The inverse is a throw away from the firer.
var/list/help_messages = list("slaps", "pokes", "nudges", "bumps", "pinches")
var/done_mob_unique = FALSE // Has the projectile already done something to a mob?
/obj/item/projectile/energy/hook/launch(atom/target, target_zone, x_offset=0, y_offset=0, angle_offset=0)
var/expected_distance = get_dist(target, loc)
kill_count = expected_distance // So the hook hits the ground if no mob is hit.
target_distance = expected_distance
if(firer) // Needed to ensure later checks in impact and on hit function.
launcher_intent = firer.a_intent
firer.Beam(src,icon_state=beam_state,icon='icons/effects/beam.dmi',time=60, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=1)
if(launcher_intent)
switch(launcher_intent)
if(I_HURT)
check_armour = "bullet"
damage *= 3
sharp = 1
agony = 20
if(I_GRAB)
check_armour = "melee"
damage_type = HALLOSS
if(I_DISARM)
check_armour = "melee"
if(prob(30)) // A chance for a successful hit to either knock someone down, or cause minor disorientation.
weaken = 1
else
stun = 2
eyeblur = 3
if(I_HELP)
silenced = 1
damage_type = HALLOSS
..() // Does the regular launching stuff.
/obj/item/projectile/energy/hook/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
if(..())
perform_intent_unique(target)
/obj/item/projectile/energy/hook/on_impact(var/atom/A)
perform_intent_unique(get_turf(A))
/obj/item/projectile/energy/hook/proc/ranged_disarm(var/mob/living/carbon/human/H)
if(istype(H))
var/list/holding = list(H.get_active_hand() = 60, H.get_inactive_hand() = 40)
for(var/obj/item/weapon/gun/W in holding) // Guns are complex devices, both of a mechanical and electronic nature. A weird gravity ball or other type of object trying to pull or grab it is likely not safe.
if(W && prob(holding[W]))
var/list/turfs = list()
for(var/turf/T in view())
turfs += T
if(turfs.len)
var/turf/target = pick(turfs)
visible_message("<span class='danger'>[H]'s [W] goes off due to \the [src]!</span>")
return W.afterattack(target,H)
if(!(H.species.flags & NO_SLIP) && prob(50))
var/armor_check = H.run_armor_check(def_zone, "melee")
H.apply_effect(3, WEAKEN, armor_check)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
if(armor_check < 60)
visible_message("<span class='danger'>\The [src] has pushed [H]!</span>")
else
visible_message("<span class='warning'>\The [src] attempted to push [H]!</span>")
return
else
if(H.break_all_grabs(firer))
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
return
for(var/obj/item/I in holding)
if(I)
H.drop_from_inventory(I)
visible_message("<span class='danger'>\The [src] has disarmed [H]!</span>")
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1)
return
/obj/item/projectile/energy/hook/proc/perform_intent_unique(atom/target)
playsound(src.loc, impact_sound, 40, 1)
var/success = FALSE
if(istype(target,/turf))
if(launcher_intent)
if(launcher_intent != I_HELP && !done_mob_unique)
var/target_mob = pick(/mob/living in target.contents)
if(!target_mob)
return
if(Bump(target_mob, forced=1)) //If we hit a turf, try to force an interaction with a mob on the turf.
done_mob_unique = TRUE
success = TRUE
else if(firer)
var/obj/T
if(original in target.contents && istype(original, /obj))
T = original
var/list/possible_targets = list()
for(var/obj/item/I in target.contents)
if(!I.anchored)
possible_targets += I
for(var/obj/structure/S in target.contents)
if(!S.anchored)
possible_targets += S
if(!T)
if(!possible_targets || !possible_targets.len)
return
T = pick(possible_targets)
spawn(2)
playsound(target, crack_sound, 40, 1)
visible_message("<span class='notice'>\The [T] is snatched by \the [src]!</span>")
T.throw_at(get_turf(firer), 7, 1, src)
success = TRUE
else if(isliving(target) && !done_mob_unique)
var/mob/living/L = target
if(launcher_intent)
switch(launcher_intent)
if(I_HELP)
var/message = pick(help_messages)
if(message == "slaps")
spawn(1)
playsound(loc, 'sound/effects/snap.ogg', 50, 1)
visible_message("<span class='notice'>\The [src] [message] [target].</span>")
done_mob_unique = TRUE
success = TRUE
if(I_HURT)
if(prob(10) && istype(L, /mob/living/carbon/human))
to_chat(L, "<span class='warning'>\The [src] rips at your hands!</span>")
ranged_disarm(L)
success = TRUE
done_mob_unique = TRUE
if(I_DISARM)
if(prob(disarm_chance) && istype(L, /mob/living/carbon/human))
ranged_disarm(L)
else
L.visible_message("<span class='danger'>\The [src] sends \the [L] stumbling backwards.</span>")
L.throw_at(get_turf(get_step(L,get_dir(firer,L))), 1, 1, src)
done_mob_unique = TRUE
success = TRUE
if(I_GRAB)
var/turf/STurf = get_turf(L)
spawn(2)
playsound(STurf, crack_sound, 60, 1)
L.visible_message("<span class='critical'>\The [src] rips [L] towards \the [firer]!</span>")
L.throw_at(get_turf(get_step(firer,get_dir(firer,L))), 6, 1, src)
done_mob_unique = TRUE
success = TRUE
else if(istype(target, /obj/structure))
var/obj/structure/S = target
if(!S.anchored)
S.throw_at(get_turf(get_step(firer,get_dir(firer,S))), 4, 1, src)
success = TRUE
qdel(my_tracking_beam)
return success
/*
* Hook subtypes.
*/
/obj/item/projectile/energy/hook/ring
name = "green orb"
icon_state = "green_laser"
beam_state = "n_beam"
damage = 3

View File

@@ -116,4 +116,29 @@
return ..(target, blocked, def_zone)
/obj/item/projectile/bullet/magnetic/fuelrod/supermatter/check_penetrate()
return 1
return 1
/obj/item/projectile/bullet/magnetic/bore
name = "phorogenic blast"
icon_state = "purpleemitter"
damage = 20
incendiary = 1
armor_penetration = 20
penetrating = 0
check_armour = "melee"
irradiate = 20
kill_count = 6
/obj/item/projectile/bullet/magnetic/bore/Bump(atom/A, forced=0)
if(istype(A, /turf/simulated/mineral))
var/turf/simulated/mineral/MI = A
loc = get_turf(A) // Careful.
permutated.Add(A)
MI.GetDrilled(TRUE)
return 0
else if(istype(A, /turf/simulated/wall) || istype(A, /turf/simulated/shuttle/wall)) // Cause a loud, but relatively minor explosion on the wall it hits.
explosion(A, -1, -1, 1, 3)
qdel(src)
return 1
else
..()

View File

@@ -257,3 +257,82 @@
visible_message("<span class='danger'>\The [src] splatters a layer of web on \the [target]!</span>")
new /obj/effect/spider/stickyweb(target.loc)
..()
/obj/item/projectile/beam/tungsten
name = "core of molten tungsten"
icon_state = "energy"
fire_sound = 'sound/weapons/emitter2.ogg'
pass_flags = PASSTABLE | PASSGRILLE
damage = 70
damage_type = BURN
check_armour = "laser"
light_range = 4
light_power = 3
light_color = "#3300ff"
muzzle_type = /obj/effect/projectile/tungsten/muzzle
tracer_type = /obj/effect/projectile/tungsten/tracer
impact_type = /obj/effect/projectile/tungsten/impact
/obj/item/projectile/beam/tungsten/on_hit(var/atom/target, var/blocked = 0)
if(isliving(target))
var/mob/living/L = target
L.add_modifier(/datum/modifier/grievous_wounds, 30 SECONDS)
if(ishuman(L))
var/mob/living/carbon/human/H = L
var/target_armor = H.getarmor(def_zone, check_armour)
var/obj/item/organ/external/target_limb = H.get_organ(def_zone)
var/armor_special = 0
if(target_armor >= 60)
var/turf/T = get_step(H, pick(alldirs - src.dir))
H.throw_at(T, 1, 1, src)
H.apply_damage(20, BURN, def_zone)
if(target_limb)
armor_special = 2
target_limb.fracture()
else if(target_armor >= 45)
H.apply_damage(15, BURN, def_zone)
if(target_limb)
armor_special = 1
target_limb.dislocate()
else if(target_armor >= 30)
H.apply_damage(10, BURN, def_zone)
if(prob(30) && target_limb)
armor_special = 1
target_limb.dislocate()
else if(target_armor >= 15)
H.apply_damage(5, BURN, def_zone)
if(prob(15) && target_limb)
armor_special = 1
target_limb.dislocate()
if(armor_special > 1)
target.visible_message("<span class='cult'>\The [src] slams into \the [target]'s [target_limb], reverberating loudly!</span>")
else if(armor_special)
target.visible_message("<span class='cult'>\The [src] slams into \the [target]'s [target_limb] with a low rumble!</span>")
..()
/obj/item/projectile/beam/tungsten/on_impact(var/atom/A)
if(istype(A,/turf/simulated/shuttle/wall) || istype(A,/turf/simulated/wall) || (istype(A,/turf/simulated/mineral) && A.density) || istype(A,/obj/mecha) || istype(A,/obj/machinery/door))
var/blast_dir = src.dir
A.visible_message("<span class='danger'>\The [A] begins to glow!</span>")
spawn(2 SECONDS)
var/blastloc = get_step(A, blast_dir)
if(blastloc)
explosion(blastloc, -1, -1, 2, 3)
..()
/obj/item/projectile/beam/tungsten/Bump(atom/A, forced=0)
if(istype(A, /obj/structure/window)) //It does not pass through windows. It pulverizes them.
var/obj/structure/window/W = A
W.shatter()
return 0
..()

View File

@@ -32,7 +32,7 @@
/obj/machinery/chem_master/New()
..()
var/datum/reagents/R = new/datum/reagents(900) //Just a huge random number so the buffer should (probably) never dump your reagents.
var/datum/reagents/R = new/datum/reagents(900) //Just a huge random number so the buffer should (probably) never dump your reagents.
reagents = R //There should be a nano ui thingy to warn of this.
R.my_atom = src
@@ -544,3 +544,65 @@
qdel(O)
if (beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
///////////////
///////////////
// Detects reagents inside most containers, and acts as an infinite identification system for reagent-based unidentified objects.
/obj/machinery/chemical_analyzer
name = "chem analyzer"
desc = "Used to precisely scan chemicals and other liquids inside various containers. \
It may also identify the liquid contents of unknown objects."
description_info = "This machine will try to tell you what reagents are inside of something capable of holding reagents. \
It is also used to 'identify' specific reagent-based objects with their properties obscured from inspection by normal means."
icon = 'icons/obj/chemical.dmi'
icon_state = "chem_analyzer"
density = TRUE
anchored = TRUE
use_power = TRUE
idle_power_usage = 20
clicksound = "button"
var/analyzing = FALSE
/obj/machinery/chemical_analyzer/update_icon()
icon_state = "chem_analyzer[analyzing ? "-working":""]"
/obj/machinery/chemical_analyzer/attackby(obj/item/I, mob/living/user)
if(!istype(I))
return ..()
if(default_deconstruction_screwdriver(user, I))
return
if(default_deconstruction_crowbar(user, I))
return
if(istype(I,/obj/item/weapon/reagent_containers))
analyzing = TRUE
update_icon()
to_chat(user, span("notice", "Analyzing \the [I], please stand by..."))
if(!do_after(user, 2 SECONDS, src))
to_chat(user, span("warning", "Sample moved outside of scan range, please try again and remain still."))
analyzing = FALSE
update_icon()
return
// First, identify it if it isn't already.
if(!I.is_identified(IDENTITY_FULL))
var/datum/identification/ID = I.identity
if(ID.identification_type == IDENTITY_TYPE_CHEMICAL) // This only solves chemical-based mysteries.
I.identify(IDENTITY_FULL, user)
// Now tell us everything that is inside.
if(I.reagents && I.reagents.reagent_list.len)
to_chat(user, "<br>") // To add padding between regular chat and the output.
for(var/datum/reagent/R in I.reagents.reagent_list)
if(!R.name)
continue
to_chat(user, span("notice", "Contains [R.volume]u of <b>[R.name]</b>.<br>[R.description]<br>"))
to_chat(user, span("notice", "Scanning of \the [I] complete."))
analyzing = FALSE
update_icon()
return

View File

@@ -489,4 +489,21 @@
description = "A slurry of compounds that contains the basic requirements for life."
taste_description = "salty meat"
reagent_state = LIQUID
color = "#DF9FBF"
color = "#DF9FBF"
// The opposite to healing nanites, exists to make unidentified hypos implied to have nanites not be 100% safe.
/datum/reagent/defective_nanites
name = "Defective Nanites"
id = "defective_nanites"
description = "Miniature medical robots that are malfunctioning and cause bodily harm. Fortunately, they cannot self-replicate."
taste_description = "metal"
reagent_state = SOLID
color = "#333333"
metabolism = REM * 3 // Broken nanomachines go a bit slower.
scannable = 1
/datum/reagent/defective_nanites/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
M.take_organ_damage(2 * removed, 2 * removed)
M.adjustOxyLoss(4 * removed)
M.adjustToxLoss(2 * removed)
M.adjustCloneLoss(2 * removed)

View File

@@ -155,10 +155,26 @@
/datum/reagent/toxin/mold/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
..()
M.adjustToxLoss(strength * removed)
if(prob(5))
M.vomit()
/datum/reagent/toxin/expired_medicine
name = "Expired Medicine"
id = "expired_medicine"
description = "Some form of liquid medicine that is well beyond its shelf date. Administering it now would cause illness."
taste_description = "bitterness"
reagent_state = LIQUID
strength = 5
/datum/reagent/toxin/expired_medicine/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
..()
if(prob(5))
M.vomit()
/datum/reagent/toxin/expired_medicine/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
affect_blood(M, alien, removed * 0.66)
/datum/reagent/toxin/stimm //Homemade Hyperzine
name = "Stimm"
id = "stimm"

View File

@@ -53,20 +53,29 @@
if(!do_after(user, 30, H))
return
do_injection(H, user)
return
// This does the actual injection and transfer.
/obj/item/weapon/reagent_containers/hypospray/proc/do_injection(mob/living/carbon/human/H, mob/living/user)
if(!istype(H) || !istype(user))
return FALSE
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
to_chat(user, "<span class='notice'>You inject [M] with \the [src].</span>")
to_chat(M, "<span class='notice'>You feel a tiny prick!</span>")
to_chat(user, span("notice", "You inject \the [H] with \the [src]."))
to_chat(H, span("warning", "You feel a tiny prick!"))
if(hyposound)
playsound(src, hyposound,25)
playsound(src, hyposound, 25)
if(M.reagents)
if(H.reagents)
var/contained = reagentlist()
var/trans = reagents.trans_to_mob(M, amount_per_transfer_from_this, CHEM_BLOOD)
add_attack_logs(user,M,"Injected with [src.name] containing [contained], trasferred [trans] units")
to_chat(user, "<span class='notice'>[trans] units injected. [reagents.total_volume] units remaining in \the [src].</span>")
var/trans = reagents.trans_to_mob(H, amount_per_transfer_from_this, CHEM_BLOOD)
add_attack_logs(user,H,"Injected with [src.name] containing [contained], trasferred [trans] units")
to_chat(user, span("notice", "[trans] units injected. [reagents.total_volume] units remaining in \the [src]."))
return TRUE
return FALSE
return
//A vial-loaded hypospray. Cartridge-based!
/obj/item/weapon/reagent_containers/hypospray/vial
name = "hypospray mkII"
@@ -143,12 +152,11 @@
flags &= ~OPENCONTAINER
icon_state = "[initial(icon_state)]0"
/obj/item/weapon/reagent_containers/hypospray/autoinjector/attack(mob/M as mob, mob/user as mob)
..()
if(reagents.total_volume <= 0) //Prevents autoinjectors to be refilled.
/obj/item/weapon/reagent_containers/hypospray/autoinjector/do_injection(mob/living/carbon/human/H, mob/living/user)
. = ..()
if(.) // Will occur if successfully injected.
flags &= ~OPENCONTAINER
update_icon()
return
update_icon()
/obj/item/weapon/reagent_containers/hypospray/autoinjector/update_icon()
if(reagents.total_volume > 0)
@@ -168,6 +176,63 @@
icon_state = "green"
filled_reagents = list("anti_toxin" = 5)
// These have a 15u capacity, somewhat higher tech level, and generally more useful chems, but are otherwise the same as the regular autoinjectors.
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector
name = "empty hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity."
icon_state = "autoinjector"
amount_per_transfer_from_this = 15
volume = 15
origin_tech = list(TECH_BIO = 4)
filled_reagents = list("inaprovaline" = 15)
flags = 0 // Removed OPENCONTAINER so you can't extract things to cheese the identification system in unidentified versions.
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute
name = "trauma hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one is made to be used on victims of \
moderate blunt trauma."
filled_reagents = list("bicaridine" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/burn
name = "burn hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one is made to be used on burn victims, \
featuring an optimized chemical mixture to allow for rapid healing."
filled_reagents = list("kelotane" = 7.5, "dermaline" = 7.5)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/toxin
name = "toxin hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one is made to counteract toxins."
filled_reagents = list("anti_toxin" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/oxy
name = "oxy hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one is made to counteract oxygen \
deprivation."
filled_reagents = list("dexalinp" = 10, "tricordrazine" = 5)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity
name = "purity hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This variant excels at \
resolving viruses, infections, radiation, and genetic maladies."
filled_reagents = list("spaceacillin" = 9, "arithrazine" = 5, "ryetalyn" = 1)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/pain
name = "pain hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one contains potent painkillers."
filled_reagents = list("tramadol" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/organ
name = "organ hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity. Organ damage is resolved by this variant."
filled_reagents = list("alkysine" = 3, "imidazoline" = 2, "peridaxon" = 10)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/combat
name = "combat hypo"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This is a more dangerous and potentially \
addictive hypo compared to others, as it contains a potent cocktail of various chemicals to optimize the recipient's combat \
ability."
filled_reagents = list("bicaridine" = 3, "kelotane" = 1.5, "dermaline" = 1.5, "oxycodone" = 3, "hyperzine" = 3, "tricordrazine" = 3)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting
name = "clotting agent"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This variant excels at treating bleeding wounds and internal bleeding."
@@ -177,3 +242,109 @@
name = "bone repair injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. This one excels at treating damage to bones."
filled_reagents = list("inaprovaline" = 5, "osteodaxon" = 10)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/stimm
name = "stimm injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one is filled with a home-made stimulant, with some serious side-effects."
filled_reagents = list("stimm" = 10) // More than 10u will OD.
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/expired
name = "expired injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one has had its contents expire a long time ago, using it now will probably make someone sick, or worse."
filled_reagents = list("expired_medicine" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/soporific
name = "soporific injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one is sometimes used by orderlies, as it has soporifics, which make someone tired and fall asleep."
filled_reagents = list("stoxin" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/cyanide
name = "cyanide injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one contains cyanide, a lethal poison. It being inside a medical autoinjector has certain unsettling implications."
filled_reagents = list("cyanide" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/serotrotium
name = "serotrotium injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one is filled with serotrotium, which causes concentrated production of the serotonin neurotransmitter in humans."
filled_reagents = list("serotrotium" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/space_drugs
name = "illicit injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one contains various illicit drugs, held inside a hypospray to make smuggling easier."
filled_reagents = list("space_drugs" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/cryptobiolin
name = "cryptobiolin injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one contains cryptobiolin, which causes confusion."
filled_reagents = list("cryptobiolin" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/impedrezene
name = "impedrezene injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one has impedrezene inside, a narcotic that impairs higher brain functioning. \
This autoinjector is almost certainly created illegitimately."
filled_reagents = list("impedrezene" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/mindbreaker
name = "mindbreaker injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This one stores the dangerous hallucinogen called 'Mindbreaker', likely put in place \
by illicit groups hoping to hide their product."
filled_reagents = list("mindbreaker" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/psilocybin
name = "psilocybin injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This has psilocybin inside, which is a strong psychotropic derived from certain species of mushroom. \
This autoinjector likely was made by criminal elements to avoid detection from casual inspection."
filled_reagents = list("psilocybin" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/mutagen
name = "unstable mutagen injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This contains unstable mutagen, which makes using this a very bad idea. It will either \
ruin your genetic health, turn you into a Five Points violation, or both!"
filled_reagents = list("mutagen" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/lexorin
name = "lexorin injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
This contains lexorin, a dangerous toxin that stops respiration, and has been \
implicated in several high-profile assassinations in the past."
filled_reagents = list("lexorin" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/healing_nanites
name = "medical nanite injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
The injector stores a slurry of highly advanced and specialized nanomachines designed \
to restore bodily health from within. The nanomachines are short-lived but degrade \
harmlessly, and cannot self-replicate in order to remain Five Points compliant."
filled_reagents = list("healing_nanites" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/defective_nanites
name = "defective nanite injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
The injector stores a slurry of highly advanced and specialized nanomachines that \
are unfortunately malfunctioning, making them unsafe to use inside of a living body. \
Because of the Five Points, these nanites cannot self-replicate."
filled_reagents = list("defective_nanites" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/contaminated
name = "contaminated injector"
desc = "A refined version of the standard autoinjector, allowing greater capacity. \
The hypospray contains a viral agent inside, as well as a liquid substance that encourages \
the growth of the virus inside."
filled_reagents = list("virusfood" = 15)
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/contaminated/do_injection(mob/living/carbon/human/H, mob/living/user)
. = ..()
if(.) // Will occur if successfully injected.
infect_mob_random_lesser(H)
add_attack_logs(user, H, "Infected \the [H] with \the [src], by \the [user].")

View File

@@ -0,0 +1,80 @@
// Here are the paths for all hypos that start unidentified.
// Usually you want to use a random spawner instead of using them directly, unless you're spawning these live for adminbus purposes.
/obj/item/weapon/reagent_containers/hypospray/autoinjector
identity_type = /datum/identification/hypo
// The good.
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/brute/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/burn/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/toxin/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/oxy/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/purity/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/pain/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/organ/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/clotting/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/bonemed/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/combat/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/healing_nanites/unidentified
init_hide_identity = TRUE
// The somewhat bad.
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/stimm/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/space_drugs/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/expired/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/serotrotium/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/cryptobiolin/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/mindbreaker/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/psilocybin/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/soporific/unidentified
init_hide_identity = TRUE
// The very bad.
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/cyanide/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/impedrezene/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/mutagen/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/defective_nanites/unidentified
init_hide_identity = TRUE
/obj/item/weapon/reagent_containers/hypospray/autoinjector/biginjector/contaminated/unidentified
init_hide_identity = TRUE