Merge remote-tracking branch 'Upstream/master'

This commit is contained in:
The0bserver
2021-04-20 02:59:03 -04:00
141 changed files with 2397 additions and 1070 deletions
+1 -1
View File
@@ -10444,7 +10444,7 @@
/area/maintenance/port/fore)
"avV" = (
/obj/structure/table/wood,
/obj/item/reagent_containers/food/snacks/burger/ghost,
/obj/item/ectoplasm,
/turf/open/floor/wood,
/area/maintenance/port/fore)
"avW" = (
+3 -3
View File
@@ -16329,7 +16329,7 @@
/obj/docking_port/stationary{
dwidth = 1;
height = 4;
id = "pod4_away";
id = "pod_4_away";
name = "recovery ship";
width = 3
},
@@ -16339,7 +16339,7 @@
/obj/docking_port/stationary{
dwidth = 1;
height = 4;
id = "pod3_away";
id = "pod_3_away";
name = "recovery ship";
width = 3
},
@@ -16489,7 +16489,7 @@
dir = 4;
dwidth = 1;
height = 4;
id = "pod2_away";
id = "pod_2_away";
name = "recovery ship";
width = 3
},
+2 -3
View File
@@ -60,6 +60,5 @@ GLOBAL_LIST_INIT(podstyles, list(\
))
//cit
#define PACK_GOODY_NONE 0
#define PACK_GOODY_PUBLIC 1 //can be bought by both privates and cargo
#define PACK_GOODY_PRIVATE 2 //can be bought only by privates
#define PACK_GOODY_NONE 0 // can be bought by cargo and privates
#define PACK_GOODY_PRIVATE 1 // can be bought only by privates
+9 -1
View File
@@ -266,6 +266,7 @@
#define COMSIG_MOB_GET_VISIBLE_MESSAGE "mob_get_visible_message" //from base of atom/visible_message(): (atom/A, msg, range, ignored_mobs)
#define COMPONENT_NO_VISIBLE_MESSAGE 1 //exactly what's said on the tin.
#define COMSIG_MOB_ANTAG_ON_GAIN "mob_antag_on_gain" //from base of /datum/antagonist/on_gain(): (antag_datum)
#define COMSIG_MOB_APPLY_DAMAGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness)
#define COMSIG_MOB_SPELL_CAN_CAST "mob_spell_can_cast" //from base of /obj/effect/proc_holder/spell/can_cast(): (spell)
#define COMSIG_MOB_SWAP_HANDS "mob_swap_hands" //from base of mob/swap_hand(): (obj/item)
@@ -303,6 +304,10 @@
#define COMSIG_LIVING_ACTIVE_PARRY_START "active_parry_start" //from base of mob/living/initiate_parry_sequence(): (parrying_method, datum/parrying_item_mob_or_art, list/backup_items, list/override)
#define COMPONENT_PREVENT_PARRY_START 1
#define COMSIG_LIVING_ATTACKER_SET "living_attacker_set" // from base of /mob/living/set_last_attacker(): (attacker)
#define COMSIG_LIVING_SET_AS_ATTACKER "living_set_as_attacker" // from base of /mob/living/set_last_attacker(): (target)
//ALL OF THESE DO NOT TAKE INTO ACCOUNT WHETHER AMOUNT IS 0 OR LOWER AND ARE SENT REGARDLESS!
#define COMSIG_LIVING_STATUS_STUN "living_stun" //from base of mob/living/Stun() (amount, update, ignore)
#define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" //from base of mob/living/Knockdown() (amount, update, ignore)
@@ -357,7 +362,6 @@
// /obj/item signals
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
#define COMSIG_MOB_APPLY_DAMGE "mob_apply_damage" //from base of /mob/living/proc/apply_damage(): (damage, damagetype, def_zone)
#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob)
#define COMPONENT_NO_INTERACT 1
#define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob)
@@ -455,6 +459,10 @@
// /datum/mutation signals
#define COMSIG_HUMAN_MUTATION_LOSS "human_mutation_loss" //from datum/mutation/human/on_losing(): (datum/mutation/human/lost_mutation)
///from base of mob/living/death(): (gibbed)
// Sent before any of the other death code has run, mob is still alive.
#define COMSIG_LIVING_PREDEATH "living_predeath"
/*******Component Specific Signals*******/
//Janitor
#define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values.
+4 -3
View File
@@ -30,7 +30,8 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
//Human Overlays Indexes/////////
//LOTS OF CIT CHANGES HERE. BE CAREFUL WHEN UPSTREAM ADDS MORE LAYERS
#define MUTATIONS_LAYER 33 //mutations. Tk headglows, cold resistance glow, etc
#define MUTATIONS_LAYER 34 //mutations. Tk headglows, cold resistance glow, etc
#define ANTAG_LAYER 33 //stuff for things like cultism indicators (clock cult glow, cultist red halos, whatever else new that comes up)
#define GENITALS_BEHIND_LAYER 32 //Some genitalia needs to be behind everything, such as with taurs (Taurs use body_behind_layer
#define BODY_BEHIND_LAYER 31 //certain mutantrace features (tail when looking south) that must appear behind the body parts
#define BODYPARTS_LAYER 30 //Initially "AUGMENTS", this was repurposed to be a catch-all bodyparts flag
@@ -63,7 +64,7 @@ Will print: "/mob/living/carbon/human/death" (you can optionally embed it in a s
#define HANDS_LAYER 3
#define BODY_FRONT_LAYER 2
#define FIRE_LAYER 1 //If you're on fire
#define TOTAL_LAYERS 33 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_;
#define TOTAL_LAYERS 34 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_;
//Human Overlay Index Shortcuts for alternate_worn_layer, layers
//Because I *KNOW* somebody will think layer+1 means "above"
@@ -158,7 +159,7 @@ GLOBAL_LIST_EMPTY(bloody_footprints_cache)
#define BLOOD_COLOR_SLIME "#00ff90"
#define BLOOD_COLOR_LIZARD "#db004D"
#define BLOOD_COLOR_UNIVERSAL "#db3300"
#define BLOOD_COLOR_BUG "#a37c0f"
#define BLOOD_COLOR_BUG "#ffc933"
#define BLOOD_COLOR_PLANT "#3d610e"
+1 -1
View File
@@ -15,7 +15,7 @@
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
#define LAZYFIND(L, V) L ? L.Find(V) : 0
#define LAZYFIND(L, V) (L ? L.Find(V) : 0)
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
#define LAZYLEN(L) length(L)
+17
View File
@@ -11,6 +11,23 @@ GLOBAL_LIST_INIT(bitfields, list(
"TILE_BOUND" = TILE_BOUND,
"PIXEL_SCALE" = PIXEL_SCALE
),
"area_flags" = list(
"ABDUCTOR_PROOF" = ABDUCTOR_PROOF,
"BLOBS_ALLOWED" = BLOBS_ALLOWED,
"BLOCK_SUICIDE" = BLOCK_SUICIDE,
// "CULT_PERMITTED" = CULT_PERMITTED,
"FLORA_ALLOWED" = FLORA_ALLOWED,
"HIDDEN_AREA" = HIDDEN_AREA,
"MEGAFAUNA_SPAWN_ALLOWED" = MEGAFAUNA_SPAWN_ALLOWED,
"MOB_SPAWN_ALLOWED" = MOB_SPAWN_ALLOWED,
"NO_ALERTS" = NO_ALERTS,
"NOTELEPORT" = NOTELEPORT,
"CAVES_ALLOWED" = CAVES_ALLOWED,
"UNIQUE_AREA" = UNIQUE_AREA,
"VALID_TERRITORY" = VALID_TERRITORY,
"XENOBIOLOGY_COMPATIBLE" = XENOBIOLOGY_COMPATIBLE,
"NO_ALERTS" = NO_ALERTS,
) ,
"sight" = list(
"SEE_INFRA" = SEE_INFRA,
"SEE_SELF" = SEE_SELF,
+47 -9
View File
@@ -85,21 +85,16 @@
makeItemInactive()
/obj/screen/storage/volumetric_box/proc/makeItemInactive()
if(!our_item)
return
our_item.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
our_item.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
return
/obj/screen/storage/volumetric_box/proc/makeItemActive()
if(!our_item)
return
our_item.layer = VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER //make sure we display infront of the others!
our_item.plane = VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE
return
/obj/screen/storage/volumetric_box/center
icon_state = "stored_continue"
var/obj/screen/storage/volumetric_edge/stored_left/left
var/obj/screen/storage/volumetric_edge/stored_right/right
var/obj/screen/storage/item_holder/holder
var/pixel_size
/obj/screen/storage/volumetric_box/center/Initialize(mapload, new_master, our_item)
@@ -110,6 +105,9 @@
/obj/screen/storage/volumetric_box/center/Destroy()
QDEL_NULL(left)
QDEL_NULL(right)
vis_contents.Cut()
if(holder)
QDEL_NULL(holder)
return ..()
/obj/screen/storage/volumetric_box/center/proc/on_screen_objects()
@@ -123,13 +121,36 @@
return
pixel_size = pixels
cut_overlays()
vis_contents.Cut()
//our icon size is 32 pixels.
transform = matrix((pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE, 0, 0, 0, 1, 0)
var/multiplier = (pixels - (VOLUMETRIC_STORAGE_BOX_BORDER_SIZE * 2)) / VOLUMETRIC_STORAGE_BOX_ICON_SIZE
transform = matrix(multiplier, 0, 0, 0, 1, 0)
if(our_item)
if(holder)
qdel(holder)
holder = new(null, src, our_item)
holder.transform = matrix(1 / multiplier, 0, 0, 0, 1, 0)
holder.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
holder.appearance_flags &= ~RESET_TRANSFORM
makeItemInactive()
vis_contents += holder
left.pixel_x = -((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) - VOLUMETRIC_STORAGE_BOX_BORDER_SIZE
right.pixel_x = ((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) + VOLUMETRIC_STORAGE_BOX_BORDER_SIZE
add_overlay(left)
add_overlay(right)
/obj/screen/storage/volumetric_box/center/makeItemInactive()
if(!holder)
return
holder.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
holder.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
/obj/screen/storage/volumetric_box/center/makeItemActive()
if(!holder)
return
holder.our_item.layer = VOLUMETRIC_STORAGE_ACTIVE_ITEM_LAYER //make sure we display infront of the others!
holder.our_item.plane = VOLUMETRIC_STORAGE_ACTIVE_ITEM_PLANE
/obj/screen/storage/volumetric_edge
layer = VOLUMETRIC_STORAGE_BOX_LAYER
plane = VOLUMETRIC_STORAGE_BOX_PLANE
@@ -157,3 +178,20 @@
/obj/screen/storage/volumetric_edge/stored_right
icon_state = "stored_end"
appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM
/obj/screen/storage/item_holder
var/obj/item/our_item
vis_flags = NONE
/obj/screen/storage/item_holder/Initialize(mapload, new_master, obj/item/I)
. = ..()
our_item = I
vis_contents += I
/obj/screen/storage/item_holder/Destroy()
vis_contents.Cut()
our_item = null
return ..()
/obj/screen/storage/item_holder/Click(location, control, params)
return our_item.Click(location, control, params)
+1 -2
View File
@@ -94,8 +94,7 @@
else if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), 1, -1)
M.lastattacker = user.real_name
M.lastattackerckey = user.ckey
M.set_last_attacker(user)
if(force && M == user && user.client)
user.client.give_award(/datum/award/achievement/misc/selfouch, user)
+63
View File
@@ -0,0 +1,63 @@
/datum/component/activity
var/activity_level = 0
var/not_moved_counter = 0
var/list/historical_activity_levels = list()
/datum/component/activity/Initialize()
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE
var/mob/living/L = parent
RegisterSignal(L, COMSIG_LIVING_SET_AS_ATTACKER, .proc/on_set_as_attacker)
RegisterSignal(L, COMSIG_LIVING_ATTACKER_SET, .proc/on_attacker_set)
RegisterSignal(L, COMSIG_MOB_DEATH, .proc/on_death)
RegisterSignal(L, COMSIG_EXIT_AREA, .proc/on_exit_area)
RegisterSignal(L, COMSIG_LIVING_LIFE, .proc/on_life)
RegisterSignal(L, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE, COMSIG_MOB_APPLY_DAMAGE), .proc/minor_activity)
/datum/component/activity/proc/log_activity()
historical_activity_levels[world.time] = activity_level
/datum/component/activity/proc/minor_activity(datum/source)
activity_level += 1
/datum/component/activity/proc/on_attacker_set(datum/source, mob/attacker)
activity_level += 10
if(attacker?.mind)
activity_level += 10
log_activity()
/datum/component/activity/proc/on_set_as_attacker(datum/source, mob/target)
activity_level += 10
if(target?.mind)
activity_level += 20
log_activity()
/datum/component/activity/proc/on_death(datum/source)
activity_level += 100 // dying means you're doing SOMETHING
log_activity()
/datum/component/activity/proc/on_exit_area(datum/source)
activity_level += 1
not_moved_counter = 0
/datum/component/activity/proc/on_life(datum/source, seconds, times_fired)
var/mob/living/L = source
if(L.stat >= UNCONSCIOUS) // can't expect the unconscious to move
return
not_moved_counter += seconds
var/should_log = FALSE
switch(not_moved_counter)
if(60 to 120)
activity_level -= 1
if(120 to 600)
activity_level -= 5
if(600 to 1200)
activity_level -= 10
should_log = TRUE
if(1200 to INFINITY)
activity_level -= 20
should_log = TRUE
activity_level = max(activity_level, 0)
if(should_log)
log_activity()
+2 -2
View File
@@ -56,10 +56,10 @@
detonate()
/datum/component/explodable/proc/on_equip(datum/source, mob/equipper, slot)
RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMGE, .proc/explodable_attack_zone, TRUE)
RegisterSignal(equipper, COMSIG_MOB_APPLY_DAMAGE, .proc/explodable_attack_zone, TRUE)
/datum/component/explodable/proc/on_drop(datum/source, mob/user)
UnregisterSignal(user, COMSIG_MOB_APPLY_DAMGE)
UnregisterSignal(user, COMSIG_MOB_APPLY_DAMAGE)
/// Checks if we're hitting the zone this component is covering
/datum/component/explodable/proc/is_hitting_zone(def_zone)
+12 -31
View File
@@ -47,18 +47,8 @@
var/display_numerical_stacking = FALSE //stack things of the same type and show as a single object with a number.
/// "legacy"/default view mode's storage "boxes"
var/obj/screen/storage/boxes/ui_boxes
/// New volumetric storage display mode's left side
var/obj/screen/storage/left/ui_left
/// New volumetric storage display mode's center 'blocks'
var/obj/screen/storage/continuous/ui_continuous
/// The close button, used in all modes. Frames right side in volumetric mode.
var/obj/screen/storage/close/ui_close
/// Associative list of list(item = screen object) for volumetric storage item screen blocks
var/list/ui_item_blocks
var/current_maxscreensize
/// Ui objects by person. mob = list(objects)
var/list/ui_by_mob = list()
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
@@ -125,18 +115,16 @@
/datum/component/storage/Destroy()
close_all()
QDEL_NULL(ui_boxes)
QDEL_NULL(ui_close)
QDEL_NULL(ui_continuous)
QDEL_NULL(ui_left)
// DO NOT USE QDEL_LIST_ASSOC.
if(ui_item_blocks)
for(var/i in ui_item_blocks)
qdel(ui_item_blocks[i]) //qdel the screen object not the item
ui_item_blocks.Cut()
wipe_ui_objects()
LAZYCLEARLIST(is_using)
return ..()
/datum/component/storage/proc/wipe_ui_objects()
for(var/i in ui_by_mob)
var/list/objects = ui_by_mob[i]
QDEL_LIST(objects)
ui_by_mob.Cut()
/datum/component/storage/PreTransfer()
update_actions()
@@ -351,13 +339,6 @@
return master._removal_reset(thing)
/datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/thing)
if(LAZYACCESS(ui_item_blocks, thing))
var/obj/screen/storage/volumetric_box/center/C = ui_item_blocks[thing]
for(var/i in can_see_contents()) //runtimes result if mobs can access post deletion.
var/mob/M = i
M.client?.screen -= C.on_screen_objects()
ui_item_blocks -= thing
qdel(C)
_removal_reset(thing) // THIS NEEDS TO HAPPEN AFTER SO LAYERING DOESN'T BREAK!
refresh_mob_views()
@@ -467,14 +448,14 @@
return
A.add_fingerprint(M)
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, ghost = FALSE)
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE)
var/atom/A = parent
if(!istype(M))
return FALSE
A.add_fingerprint(M)
if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE)))
return FALSE
ui_show(M, !ghost)
ui_show(M)
/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
if(isitem(O))
@@ -596,7 +577,7 @@
return can_be_inserted(I, silent, M)
/datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M)
return user_show_to_mob(M, TRUE, TRUE)
return user_show_to_mob(M, TRUE)
/datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE)
return user_show_to_mob(showto, force)
+32 -53
View File
@@ -7,7 +7,7 @@
if(QDELETED(I))
continue
if(!.[I.type])
.[I.type] = new /datum/numbered_display(I, 1)
.[I.type] = new /datum/numbered_display(I, 1, src)
else
var/datum/numbered_display/ND = .[I.type]
ND.number++
@@ -20,6 +20,8 @@
. = list()
var/list/accessible_contents = accessible_items()
var/adjusted_contents = length(accessible_contents)
var/obj/screen/storage/close/ui_close
var/obj/screen/storage/boxes/ui_boxes
//Numbered contents display
var/list/datum/numbered_display/numbered_contents
@@ -60,12 +62,13 @@
for(var/obj/O in accessible_items())
if(QDELETED(O))
continue
O.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
var/obj/screen/storage/item_holder/D = new(null, src, O)
D.mouse_opacity = MOUSE_OPACITY_OPAQUE //This is here so storage items that spawn with contents correctly have the "click around item to equip"
D.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]"
O.maptext = ""
O.layer = ABOVE_HUD_LAYER
O.plane = ABOVE_HUD_PLANE
. += O
. += D
cx++
if(cx - screen_start_x >= columns)
cx = screen_start_x
@@ -78,6 +81,9 @@
*/
/datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns)
. = list()
var/obj/screen/storage/left/ui_left
var/obj/screen/storage/continuous/ui_continuous
var/obj/screen/storage/close/ui_close
// Generate ui_item_blocks for missing ones and render+orient.
var/list/atom/contents = accessible_items()
@@ -128,14 +134,10 @@
var/first = TRUE
var/row = 1
LAZYINITLIST(ui_item_blocks)
for(var/i in percentage_by_item)
I = i
var/percent = percentage_by_item[I]
if(!ui_item_blocks[I])
ui_item_blocks[I] = new /obj/screen/storage/volumetric_box/center(null, src, I)
var/obj/screen/storage/volumetric_box/center/B = ui_item_blocks[I]
var/obj/screen/storage/volumetric_box/center/B = new /obj/screen/storage/volumetric_box/center(null, src, I)
var/pixels_to_use = overrun? MINIMUM_PIXELS_PER_ITEM : max(using_horizontal_pixels * percent, MINIMUM_PIXELS_PER_ITEM)
var/addrow = FALSE
if(CEILING(pixels_to_use, 1) >= FLOOR(horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING, 1))
@@ -143,25 +145,17 @@
addrow = TRUE
// now that we have pixels_to_use, place our thing and add it to the returned list.
B.screen_loc = I.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]"
B.screen_loc = "[screen_start_x]:[round(current_pixel + (pixels_to_use * 0.5) + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING), 1)],[screen_start_y+row-1]:[screen_pixel_y]"
// add the used pixels to pixel after we place the object
current_pixel += pixels_to_use + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING)
first = FALSE //apply padding to everything after this
// set various things
B.set_pixel_size(pixels_to_use)
B.layer = VOLUMETRIC_STORAGE_BOX_LAYER
B.plane = VOLUMETRIC_STORAGE_BOX_PLANE
B.name = I.name
I.mouse_opacity = MOUSE_OPACITY_ICON
I.maptext = ""
I.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
I.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
// finally add our things.
. += B.on_screen_objects()
. += I
// go up a row if needed
if(addrow)
@@ -185,18 +179,19 @@
/**
* Shows our UI to a mob.
*/
/datum/component/storage/proc/ui_show(mob/M, set_screen_size = TRUE)
/datum/component/storage/proc/ui_show(mob/M)
if(!M.client)
return FALSE
if(ui_by_mob[M] || LAZYFIND(is_using, M))
// something went horribly wrong
// hide first
ui_hide(M)
var/list/cview = getviewsize(M.client.view)
// in tiles
var/maxallowedscreensize = cview[1]-8
if(set_screen_size)
current_maxscreensize = maxallowedscreensize
else if(current_maxscreensize)
maxallowedscreensize = current_maxscreensize
// we got screen size, register signal
RegisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT, .proc/on_logout, override = TRUE)
RegisterSignal(M, COMSIG_PARENT_QDELETING, .proc/on_logout, override = TRUE)
if(M.active_storage != src)
if(M.active_storage)
M.active_storage.ui_hide(M)
@@ -204,10 +199,14 @@
LAZYOR(is_using, M)
if(!M.client?.prefs?.no_tetris_storage && volumetric_ui())
//new volumetric ui bay-style
M.client.screen |= orient2hud_volumetric(M, maxallowedscreensize)
var/list/objects = orient2hud_volumetric(M, maxallowedscreensize)
M.client.screen |= objects
ui_by_mob[M] = objects
else
//old ui
M.client.screen |= orient2hud_legacy(M, maxallowedscreensize)
var/list/objects = orient2hud_legacy(M, maxallowedscreensize)
M.client.screen |= objects
ui_by_mob[M] = objects
return TRUE
/**
@@ -236,8 +235,10 @@
/datum/component/storage/proc/ui_hide(mob/M)
if(!M.client)
return TRUE
UnregisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT)
M.client.screen -= list(ui_boxes, ui_close, ui_left, ui_continuous) + get_ui_item_objects_hide(M)
UnregisterSignal(M, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_CLIENT_LOGOUT))
M.client.screen -= ui_by_mob[M]
var/list/objects = ui_by_mob[M]
QDEL_LIST(objects)
if(M.active_storage == src)
M.active_storage = null
LAZYREMOVE(is_using, M)
@@ -250,48 +251,26 @@
var/atom/real_location = real_location()
return (storage_flags & STORAGE_LIMIT_VOLUME) && (length(real_location.contents) <= MAXIMUM_VOLUMETRIC_ITEMS) && !display_numerical_stacking
/**
* Gets the ui item objects to ui_hide.
*/
/datum/component/storage/proc/get_ui_item_objects_hide(mob/M)
if(!volumetric_ui() || M.client?.prefs?.no_tetris_storage)
var/atom/real_location = real_location()
return real_location.contents
else
. = list()
for(var/i in ui_item_blocks)
// get both the box and the item
. += ui_item_blocks[i]
. += i
/**
* Gets our ui_boxes, making it if it doesn't exist.
*/
/datum/component/storage/proc/get_ui_boxes()
if(!ui_boxes)
ui_boxes = new(null, src)
return ui_boxes
return new /obj/screen/storage/boxes(null, src)
/**
* Gets our ui_left, making it if it doesn't exist.
*/
/datum/component/storage/proc/get_ui_left()
if(!ui_left)
ui_left = new(null, src)
return ui_left
return new /obj/screen/storage/left(null, src)
/**
* Gets our ui_close, making it if it doesn't exist.
*/
/datum/component/storage/proc/get_ui_close()
if(!ui_close)
ui_close = new(null, src)
return ui_close
return new /obj/screen/storage/close(null, src)
/**
* Gets our ui_continuous, making it if it doesn't exist.
*/
/datum/component/storage/proc/get_ui_continuous()
if(!ui_continuous)
ui_continuous = new(null, src)
return ui_continuous
return new /obj/screen/storage/continuous(null, src)
@@ -384,7 +384,7 @@
var/temp_rate = 1
threshold_desc = list(
"Transmission 6" = "Additionally increases temperature adjustment rate and heals those who love toxins",
"Resistance 7" = "Increases healing speed.",
"Stage Speed 7" = "Increases healing speed.",
)
/datum/symptom/heal/plasma/Start(datum/disease/advance/A)
if(!..())
+1 -1
View File
@@ -141,7 +141,7 @@
/obj/item/clothing/head/mob_holder/dropped(mob/user)
. = ..()
if(held_mob && !ismob(loc))//don't release on soft-drops
if(held_mob && !ismob(loc) && !istype(loc,/obj/item/storage))//don't release on soft-drops
release()
/obj/item/clothing/head/mob_holder/proc/release()
+2 -2
View File
@@ -34,13 +34,13 @@
. = ..()
UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND)
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user, act_intent)
var/mob/living/L = source
if(L.stat == DEAD)
return
//we want to delay the effect to be displayed after the mob is petted, not before.
switch(user.a_intent)
switch(act_intent)
if(INTENT_HARM)
addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1)
if(INTENT_HELP)
+7 -8
View File
@@ -129,7 +129,7 @@
/datum/mutation/human/proc/get_spans()
return list()
/mob/living/carbon/proc/update_mutations_overlay()
/mob/living/proc/update_mutations_overlay()
return
/mob/living/carbon/human/update_mutations_overlay()
@@ -142,13 +142,12 @@
if(overlays_standing[CM.layer_used])
mut_overlay = overlays_standing[CM.layer_used]
var/mutable_appearance/V = CM.get_visual_indicator()
if(!mut_overlay.Find(V)) //either we lack the visual indicator or we have the wrong one
remove_overlay(CM.layer_used)
for(var/mutable_appearance/MA in CM.visual_indicators[CM.type])
mut_overlay.Remove(MA)
mut_overlay |= V
overlays_standing[CM.layer_used] = mut_overlay
apply_overlay(CM.layer_used)
remove_overlay(CM.layer_used) //trying to find its existence defeats the point because if cut_overlays is called it doesn't bother reloading it.
for(var/mutable_appearance/MA in CM.visual_indicators[CM.type])
mut_overlay.Remove(MA)
mut_overlay |= V
overlays_standing[CM.layer_used] = mut_overlay
apply_overlay(CM.layer_used)
/datum/mutation/human/proc/modify() //called when a genome is applied so we can properly update some stats without having to remove and reapply the mutation from someone
if(modified || !power || !owner)
+2 -2
View File
@@ -283,7 +283,7 @@
desc = "Allows a creature to voluntary discard a random appendage."
quality = POSITIVE
text_gain_indication = "<span class='notice'>Your joints feel loose.</span>"
instability = 30
instability = 20
power = /obj/effect/proc_holder/spell/self/self_amputation
energy_coeff = 1
@@ -316,7 +316,7 @@
return
var/obj/item/bodypart/BP = pick(parts)
BP.dismember()
BP.dismember(harmless=TRUE)
//spider webs
/datum/mutation/human/webbing
+1 -1
View File
@@ -1,6 +1,6 @@
/datum/mutation/human/radioactive
name = "Radioactivity"
desc = "A volatile mutation that causes the host to sent out deadly beta radiation. This affects both the hosts and their surroundings."
desc = "A volatile mutation that causes the host to send out deadly beta radiation. This affects both the hosts and their surroundings."
quality = NEGATIVE
text_gain_indication = "<span class='warning'>You can feel it in your bones!</span>"
time_coeff = 5
+1 -1
View File
@@ -1,7 +1,7 @@
//Cold Resistance gives your entire body an orange halo, and makes you immune to the effects of vacuum and cold.
/datum/mutation/human/space_adaptation
name = "Space Adaptation"
desc = "A strange mutation that renders the host immune to the vacuum if space. Will still need an oxygen supply."
desc = "A strange mutation that renders the host immune to the vacuum of space. Will still need an oxygen supply."
quality = POSITIVE
difficulty = 16
text_gain_indication = "<span class='notice'>Your body feels warm!</span>"
+2 -2
View File
@@ -3,8 +3,8 @@
var/obj/item/sample_object
var/number
/datum/numbered_display/New(obj/item/sample, _number = 1)
/datum/numbered_display/New(obj/item/sample, _number = 1, datum/component/storage/parent)
if(!istype(sample))
qdel(src)
sample_object = sample
sample_object = new /obj/screen/storage/item_holder(null, parent, sample)
number = _number
+1 -1
View File
@@ -160,7 +160,7 @@
/obj/screen/alert/status_effect/mesmerized
name = "Mesmerized"
desc = "You cant tear your sight from who is in front of you... their gaze is simply too enthralling.."
desc = "You can't tear your sight from who is in front of you... their gaze is simply too enthralling.."
icon = 'icons/mob/actions/bloodsucker.dmi'
icon_state = "power_mez"
+18
View File
@@ -412,6 +412,24 @@ If not set, defaults to check_completion instead. Set it. It's used by cryo.
counter++
return counter >= 8
/datum/objective/freedom
name = "freedom"
explanation_text = "Don't get captured by nanotrasen."
team_explanation_text = "Have all members of your team free of nanotrasen custody."
/datum/objective/freedom/check_completion()
var/list/datum/mind/owners = get_owners()
for(var/m in owners)
var/datum/mind/M = m
if(!considered_alive(M))
return FALSE
if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
return FALSE
var/turf/location = get_turf(M.current)
if(!location || istype(location, /turf/open/floor/plasteel/shuttle/red) || istype(location, /turf/open/floor/mineral/plastitanium/red/brig)) // Fails if they are in the shuttle brig
return FALSE
return TRUE
/datum/objective/escape
name = "escape"
explanation_text = "Escape on the shuttle or an escape pod alive and without being in custody."
+2
View File
@@ -519,6 +519,8 @@ Class Procs:
//called on machinery construction (i.e from frame to machinery) but not on initialization
/obj/machinery/proc/on_construction()
for(var/obj/I in contents)
I.moveToNullspace()
return
//called on deconstruction before the final deletion
+225 -304
View File
@@ -13,7 +13,7 @@
var/temp = "Inactive"
var/scantemp_ckey
var/scantemp_name
var/scantemp = "Ready to Scan"
var/scantemp = "Inactive"
var/menu = 1 //Which menu screen to display
var/datum/data/record/active_record = null
var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything.
@@ -132,7 +132,6 @@
src.diskette = W
to_chat(user, "<span class='notice'>You insert [W].</span>")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
src.updateUsrDialog()
else if(W.tool_behaviour == TOOL_MULTITOOL)
if(istype(W.buffer, clonepod_type))
if(get_area(W.buffer) != get_area(src))
@@ -151,311 +150,233 @@
else
return ..()
/obj/machinery/computer/cloning/ui_interact(mob/user)
/obj/machinery/computer/cloning/AltClick(mob/user)
. = ..()
EjectDisk(user)
updatemodules(TRUE)
/obj/machinery/computer/cloning/proc/EjectDisk(mob/user)
if(diskette)
scantemp = "Disk Ejected"
diskette.forceMove(drop_location())
usr.put_in_active_hand(diskette)
diskette = null
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
var/dat = ""
dat += "<a href='byond://?src=[REF(src)];refresh=1'>Refresh</a>"
/obj/machinery/computer/cloning/proc/Save(mob/user, target)
var/datum/data/record/GRAB = null
for(var/datum/data/record/record in records)
if(record.fields["id"] == target)
GRAB = record
break
else
continue
if(!GRAB || !GRAB.fields)
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
scantemp = "Failed saving to disk: Data Corruption"
return FALSE
if(!diskette || diskette.read_only)
scantemp = !diskette ? "Failed saving to disk: No disk." : "Failed saving to disk: Disk refuses override attempt."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
diskette.fields = GRAB.fields.Copy()
diskette.name = "data disk - '[src.diskette.fields["name"]]'"
scantemp = "Saved to disk successfully."
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
/obj/machinery/computer/cloning/proc/DeleteRecord(mob/user, target)
var/datum/data/record/GRAB = null
for(var/datum/data/record/record in records)
if(record.fields["id"] == target)
GRAB = record
break
else
continue
if(!GRAB)
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
scantemp = "Cannot delete: Data Corrupted."
return FALSE
var/obj/item/card/id/C = usr.get_idcard(hand_first = TRUE)
if(istype(C) || istype(C, /obj/item/pda) || istype(C, /obj/item/modular_computer/tablet))
if(check_access(C))
scantemp = "[GRAB.fields["name"]] => Record deleted."
records.Remove(GRAB)
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
var/obj/item/circuitboard/computer/cloning/board = circuit
board.records = records
return TRUE
scantemp = "Cannot delete: Access Denied."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
/obj/machinery/computer/cloning/proc/Load(mob/user)
if(!diskette || !istype(diskette.fields) || !diskette.fields["name"] || !diskette.fields)
scantemp = "Failed loading: Load error."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
for(var/datum/data/record/R in records)
if(R.fields["key"] == diskette.fields["key"])
scantemp = "Failed loading: Data already exists!"
return FALSE
var/datum/data/record/R = new(src)
for(var/key in diskette.fields)
R.fields[key] = diskette.fields[key]
records += R
scantemp = "Loaded into internal storage successfully."
var/obj/item/circuitboard/computer/cloning/board = circuit
board.records = records
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
/obj/machinery/computer/cloning/proc/Clone(mob/user, target)
var/datum/data/record/C = find_record("id", target, records)
//Look for that player! They better be dead!
if(C)
var/obj/machinery/clonepod/pod = GetAvailablePod()
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
if(!LAZYLEN(pods))
temp = "Error: No Clonepods detected."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(!pod)
temp = "Error: No Clonepods available."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(!CONFIG_GET(flag/revival_cloning))
temp = "Error: Unable to initiate cloning cycle."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(pod.occupant)
temp = "Warning: Cloning cycle already in progress."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["blood_type"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["quirks"], C.fields["bank_account"], C.fields["traumas"]))
temp = "Notice: [C.fields["name"]] => Cloning cycle in progress..."
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
records.Remove(C)
else
temp = "Error: [C.fields["name"]] => Initialisation failure."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else
temp = "Failed to clone: Data corrupted."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
. = TRUE
/obj/machinery/computer/cloning/proc/Toggle_lock(mob/user)
if(!scanner.is_operational())
return
if(!scanner.locked && !scanner.occupant) //I figured out that if you're fast enough, you can lock an open pod
return
scanner.locked = !scanner.locked
playsound(src, scanner.locked ? 'sound/machines/terminal_prompt_deny.ogg' : 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
. = TRUE
/obj/machinery/computer/cloning/proc/Scan(mob/user)
if(!scanner.is_operational() || !scanner.occupant)
return
scantemp = "[scantemp_name] => Scanning..."
loading = TRUE
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
say("Initiating scan...")
var/prev_locked = scanner.locked
scanner.locked = TRUE
addtimer(CALLBACK(src, .proc/finish_scan, scanner.occupant, prev_locked), 2 SECONDS)
. = TRUE
/obj/machinery/computer/cloning/proc/Toggle_autoprocess(mob/user)
autoprocess = !autoprocess
if(autoprocess)
START_PROCESSING(SSmachines, src)
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
else
STOP_PROCESSING(SSmachines, src)
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
. = TRUE
/obj/machinery/computer/cloning/ui_data(mob/user)
var/list/data = list()
data["useRecords"] = use_records
var/list/records_to_send = list()
if(use_records)
if(scanner && HasEfficientPod() && scanner.scan_level >= AUTOCLONING_MINIMAL_LEVEL)
if(!autoprocess)
dat += "<a href='byond://?src=[REF(src)];task=autoprocess'>Autoclone</a>"
else
dat += "<a href='byond://?src=[REF(src)];task=stopautoprocess'>Stop autoclone</a>"
else
dat += "<span class='linkOff'>Autoclone</span>"
dat += "<h3>Cloning Pod Status</h3>"
dat += "<div class='statusDisplay'>[temp]&nbsp;</div>"
switch(src.menu)
if(1)
// Modules
if (isnull(src.scanner) || !LAZYLEN(pods))
dat += "<h3>Modules</h3>"
//dat += "<a href='byond://?src=[REF(src)];relmodules=1'>Reload Modules</a>"
if (isnull(src.scanner))
dat += "<font class='bad'>ERROR: No Scanner detected!</font><br>"
if (!LAZYLEN(pods))
dat += "<font class='bad'>ERROR: No Pod detected</font><br>"
// Scanner
if (!isnull(src.scanner))
var/mob/living/scanner_occupant = get_mob_or_brainmob(scanner.occupant)
dat += "<h3>Scanner Functions</h3>"
dat += "<div class='statusDisplay'>"
if(!scanner_occupant)
dat += "Scanner Unoccupied"
else if(loading)
dat += "[scanner_occupant] => Scanning..."
else
if(use_records)
if(scanner_occupant.ckey != scantemp_ckey || scanner_occupant.name != scantemp_name)
scantemp = "Ready to Scan"
scantemp_ckey = scanner_occupant.ckey
scantemp_name = scanner_occupant.name
else
scantemp = "Ready to Clone"
dat += "[scanner_occupant] => [scantemp]"
dat += "</div>"
if(scanner_occupant)
dat += "<a href='byond://?src=[REF(src)];scan=1'>[use_records ? "Start Scan" : "Clone"]</a>"
dat += "<br><a href='byond://?src=[REF(src)];lock=1'>[scanner.locked ? "Unlock Scanner" : "Lock Scanner"]</a>"
else
dat += "<span class='linkOff'>[use_records ? "Start Scan" : "Clone"]</span>"
if(use_records)
// Database
dat += "<h3>Database Functions</h3>"
if (src.records.len && src.records.len > 0)
dat += "<a href='byond://?src=[REF(src)];menu=2'>View Records ([src.records.len])</a><br>"
else
dat += "<span class='linkOff'>View Records (0)</span><br>"
if (src.diskette)
dat += "<a href='byond://?src=[REF(src)];disk=eject'>Eject Disk</a><br>"
if(2)
dat += "<h3>Current records</h3>"
dat += "<a href='byond://?src=[REF(src)];menu=1'><< Back</a><br><br>"
data["hasAutoprocess"] = TRUE
if(length(records))
for(var/datum/data/record/R in records)
dat += "<h4>[R.fields["name"]]</h4>Scan ID [R.fields["id"]] <a href='byond://?src=[REF(src)];view_rec=[R.fields["id"]]'>View Record</a>"
if(3)
dat += "<h3>Selected Record</h3>"
dat += "<a href='byond://?src=[REF(src)];menu=2'><< Back</a><br>"
if (!src.active_record)
dat += "<font class='bad'>Record not found.</font>"
else
dat += "<h4>[src.active_record.fields["name"]]</h4>"
dat += "Scan ID [src.active_record.fields["id"]] <a href='byond://?src=[REF(src)];clone=[active_record.fields["id"]]'>Clone</a><br>"
var/obj/item/implant/health/H = locate(active_record.fields["imp"])
if ((H) && (istype(H)))
dat += "<b>Health Implant Data:</b><br />[H.sensehealth()]<br><br />"
var/list/record_entry = list()
record_entry["name"] = "[R.fields["name"]]"
record_entry["id"] = "[R.fields["id"]]"
var/obj/item/implant/health/H = locate(R.fields["imp"])
if(H && istype(H))
record_entry["damages"] = H.sensehealth(TRUE)
else
dat += "<font class='bad'>Unable to locate Health Implant.</font><br /><br />"
record_entry["damages"] = FALSE
record_entry["UI"] = "[R.fields["UI"]]"
record_entry["UE"] = "[R.fields["UE"]]"
record_entry["blood_type"] = "[R.fields["blood_type"]]"
records_to_send += list(record_entry)
data["records"] = records_to_send
else
data["records"] = list()
if(diskette && diskette.fields)
var/list/disk_data = list()
disk_data["name"] = "[diskette.fields["name"]]"
disk_data["id"] = "[diskette.fields["id"]]"
disk_data["UI"] = "[diskette.fields["UI"]]"
disk_data["UE"] = "[diskette.fields["UE"]]"
disk_data["blood_type"] = "[diskette.fields["blood_type"]]"
data["diskData"] = disk_data
else
data["diskData"] = list()
else
data["hasAutoprocess"] = FALSE
data["autoprocess"] = autoprocess
var/list/lack_machine = list()
if(isnull(src.scanner))
lack_machine += "ERROR: No Scanner Detected!"
if(!LAZYLEN(pods))
lack_machine += "ERROR: No Pod Detected!"
data["lacksMachine"] = lack_machine
data["temp"] = temp
var/build_temp = null
var/mob/living/scanner_occupant = get_mob_or_brainmob(scanner?.occupant)
if(scanner_occupant?.ckey != scantemp_ckey || scanner_occupant?.name != scantemp_name)
if(use_records)
build_temp = "Ready to Scan"
scantemp_ckey = scanner_occupant?.ckey
scantemp_name = scanner_occupant?.name
else
build_temp = "Ready to Clone"
scantemp = "[scanner_occupant] => [build_temp]"
data["scanTemp"] = scantemp
data["scannerLocked"] = scanner?.locked
data["hasOccupant"] = scanner?.occupant
data["recordsLength"] = "View Records ([length(records)])"
dat += "<b>Unique Identifier:</b><br /><span class='highlight'>[src.active_record.fields["UI"]]</span><br>"
dat += "<b>Structural Enzymes:</b><br /><span class='highlight'>[src.active_record.fields["SE"]]</span><br>"
return data
if(diskette && diskette.fields)
dat += "<div class='block'>"
dat += "<h4>Inserted Disk</h4>"
dat += "<b>Contents:</b> "
var/list/L = list()
if(diskette.fields["UI"])
L += "Unique Identifier"
if(diskette.fields["UE"] && diskette.fields["name"] && diskette.fields["blood_type"])
L += "Unique Enzymes"
if(diskette.fields["SE"])
L += "Structural Enzymes"
dat += english_list(L, "Empty", " + ", " + ")
dat += "<br /><a href='byond://?src=[REF(src)];disk=load'>Load from Disk</a>"
dat += "<br /><a href='byond://?src=[REF(src)];disk=save'>Save to Disk</a>"
dat += "</div>"
dat += "<font size=1><a href='byond://?src=[REF(src)];del_rec=1'>Delete Record</a></font>"
if(4)
if (!src.active_record)
src.menu = 2
dat = "[src.temp]<br>"
dat += "<h3>Confirm Record Deletion</h3>"
dat += "<b><a href='byond://?src=[REF(src)];del_rec=1'>Scan card to confirm.</a></b><br>"
dat += "<b><a href='byond://?src=[REF(src)];menu=3'>Cancel</a></b>"
var/datum/browser/popup = new(user, "cloning", "Cloning System Control")
popup.set_content(dat)
popup.open()
/obj/machinery/computer/cloning/Topic(href, href_list)
/obj/machinery/computer/cloning/ui_act(action, params)
if(..())
return
switch(action)
if("toggle_autoprocess")
Toggle_autoprocess(usr)
if("scan")
Scan(usr)
if("toggle_lock")
Toggle_lock(usr)
if("clone")
Clone(usr, params["target"])
if("delrecord")
DeleteRecord(usr, params["target"])
if("save")
Save(usr, params["target"])
if("load")
Load(usr)
if("eject")
EjectDisk(usr)
if(loading)
/obj/machinery/computer/cloning/ui_interact(mob/user, datum/tgui/ui)
if(..())
return
if(href_list["task"])
switch(href_list["task"])
if("autoprocess")
if(scanner && HasEfficientPod() && scanner.scan_level >= AUTOCLONING_MINIMAL_LEVEL)
autoprocess = TRUE
START_PROCESSING(SSmachines, src)
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
if("stopautoprocess")
autoprocess = FALSE
STOP_PROCESSING(SSmachines, src)
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
src.updateUsrDialog()
. = TRUE
else if ((href_list["scan"]) && !isnull(scanner) && scanner.is_operational())
scantemp = ""
loading = TRUE
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
say("Initiating scan...")
var/prev_locked = scanner.locked
scanner.locked = TRUE
src.updateUsrDialog()
addtimer(CALLBACK(src, .proc/finish_scan, scanner.occupant, prev_locked), 2 SECONDS)
. = TRUE
//No locking an open scanner.
else if ((href_list["lock"]) && !isnull(scanner) && scanner.is_operational())
if ((!scanner.locked) && (scanner.occupant))
scanner.locked = TRUE
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else
scanner.locked = FALSE
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
src.updateUsrDialog()
. = TRUE
else if (href_list["refresh"])
src.updateUsrDialog()
playsound(src, "terminal_type", 25, 0)
. = TRUE
if(. || !use_records)
return
if(href_list["view_rec"])
playsound(src, "terminal_type", 25, 0)
src.active_record = find_record("id", href_list["view_rec"], records)
if(active_record)
if(!active_record.fields["ckey"])
records -= active_record
active_record = null
src.temp = "<font class='bad'>Record Corrupt</font>"
else
src.menu = 3
else
src.temp = "Record missing."
src.updateUsrDialog()
. = TRUE
else if (href_list["del_rec"])
if ((!src.active_record) || (src.menu < 3))
return
if (src.menu == 3) //If we are viewing a record, confirm deletion
src.temp = "Delete record?"
src.menu = 4
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
else if (src.menu == 4)
var/obj/item/card/id/C = usr.get_active_held_item()
if (istype(C)||istype(C, /obj/item/pda))
if(src.check_access(C))
src.temp = "[src.active_record.fields["name"]] => Record deleted."
src.records.Remove(active_record)
active_record = null
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
src.menu = 2
var/obj/item/circuitboard/computer/cloning/board = circuit
board.records = records
else
src.temp = "<font class='bad'>Access Denied.</font>"
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
. = TRUE
else if (href_list["disk"] && use_records) //Load or eject.
switch(href_list["disk"])
if("load")
if (!diskette || !istype(diskette.fields) || !diskette.fields["name"] || !diskette.fields)
src.temp = "<font class='bad'>Load error.</font>"
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
if (!src.active_record)
src.temp = "<font class='bad'>Record error.</font>"
src.menu = 1
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
for(var/key in diskette.fields)
src.active_record.fields[key] = diskette.fields[key]
src.temp = "Load successful."
src.updateUsrDialog()
var/obj/item/circuitboard/computer/cloning/board = circuit
board.records = records
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
if("eject")
if(src.diskette)
src.diskette.forceMove(drop_location())
src.diskette = null
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
if("save")
if(!diskette || diskette.read_only || !active_record || !active_record.fields)
src.temp = "<font class='bad'>Save error.</font>"
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
diskette.fields = active_record.fields.Copy()
diskette.name = "data disk - '[src.diskette.fields["name"]]'"
src.temp = "Save successful."
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
. = TRUE
else if (href_list["clone"])
var/datum/data/record/C = find_record("id", href_list["clone"], records)
//Look for that player! They better be dead!
if(C)
var/obj/machinery/clonepod/pod = GetAvailablePod()
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
if(!LAZYLEN(pods))
temp = "<font class='bad'>No Clonepods detected.</font>"
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(!pod)
temp = "<font class='bad'>No Clonepods available.</font>"
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(!CONFIG_GET(flag/revival_cloning))
temp = "<font class='bad'>Unable to initiate cloning cycle.</font>"
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(pod.occupant)
temp = "<font class='bad'>Cloning cycle already in progress.</font>"
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["blood_type"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["quirks"], C.fields["bank_account"], C.fields["traumas"]))
temp = "[C.fields["name"]] => <font class='good'>Cloning cycle in progress...</font>"
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
records.Remove(C)
if(active_record == C)
active_record = null
menu = 1
src.updateUsrDialog()
else
temp = "[C.fields["name"]] => <font class='bad'>Initialisation failure.</font>"
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else
temp = "<font class='bad'>Data corruption.</font>"
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
. = TRUE
else if (href_list["menu"] && use_records)
menu = text2num(href_list["menu"])
src.updateUsrDialog()
playsound(src, "terminal_type", 25, 0)
. = TRUE
updatemodules(TRUE)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "CloningConsole", "Cloning System Control")
ui.open()
/obj/machinery/computer/cloning/proc/finish_scan(mob/living/L, prev_locked)
if(!scanner || !L)
@@ -469,7 +390,6 @@
loading = FALSE
scanner.locked = prev_locked
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
@@ -568,44 +488,44 @@
var/obj/machinery/clonepod/pod = GetAvailablePod()
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
if(!LAZYLEN(pods))
temp = "<font class='bad'>No Clonepods detected.</font>"
temp = "No Clonepods detected."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(!pod)
temp = "<font class='bad'>No Clonepods available.</font>"
temp = "No Clonepods available."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(pod.occupant)
temp = "<font class='bad'>Cloning cycle already in progress.</font>"
temp = "Cloning cycle already in progress."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else
pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction)
temp = "[mob_occupant.real_name] => <font class='good'>Cloning data sent to pod.</font>"
temp = "[mob_occupant.real_name] => Cloning data sent to pod."
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
/obj/machinery/computer/cloning/proc/can_scan(datum/dna/dna, mob/living/mob_occupant, experimental = FALSE, datum/bank_account/account)
if(!istype(dna))
scantemp = "<font class='bad'>Unable to locate valid genetic data.</font>"
scantemp = "Unable to locate valid genetic data."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
if(!experimental)
if(mob_occupant.suiciding || mob_occupant.hellbound)
scantemp = "<font class='bad'>Subject's brain is not responding to scanning stimuli.</font>"
scantemp = "Subject's brain is not responding to scanning stimuli."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
if((HAS_TRAIT(mob_occupant, TRAIT_NOCLONE)) && (src.scanner.scan_level < 2))
scantemp = "<font class='bad'>Subject no longer contains the fundamental materials required to create a living clone.</font>"
scantemp = "Subject no longer contains the fundamental materials required to create a living clone."
playsound(src, 'sound/machines/terminal_alert.ogg', 50, 0)
return
if (!experimental)
if(!mob_occupant.ckey || !mob_occupant.client)
scantemp = "<font class='bad'>Mental interface failure.</font>"
scantemp = "Mental interface failure."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
if (find_record("ckey", mob_occupant.ckey, records))
scantemp = "<font class='average'>Subject already in database.</font>"
scantemp = "Subject already in database."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
if(SSeconomy.full_ancap && !account)
scantemp = "<font class='average'>Subject is either missing an ID card with a bank account on it, or does not have an account to begin with. Please ensure the ID card is on the body before attempting to scan.</font>"
scantemp = "Subject is either missing an ID card with a bank account on it, or does not have an account to begin with. Please ensure the ID card is on the body before attempting to scan."
playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return
return TRUE
@@ -618,3 +538,4 @@
icon_keyboard = "med_key"
circuit = /obj/item/circuitboard/computer/cloning/prototype
clonepod_type = /obj/machinery/clonepod/experimental
use_records = FALSE //Wait, so you tell me it lacks records but you never set it as false?
+13 -3
View File
@@ -102,8 +102,14 @@
if ("answerMessage")
if (!authenticated(usr))
return
var/answer_index = text2num(params["answer"])
var/message_index = text2num(params["message"])
var/answer_index = params["answer"]
var/message_index = params["message"]
// If either of these aren't numbers, then bad voodoo.
if(!isnum(answer_index) || !isnum(message_index))
message_admins("[ADMIN_LOOKUPFLW(usr)] provided an invalid index type when replying to a message on [src] [ADMIN_JMP(src)]. This should not happen. Please check with a maintainer and/or consult tgui logs.")
CRASH("Non-numeric index provided when answering comms console message.")
if (!answer_index || !message_index || answer_index < 1 || message_index < 1)
return
var/datum/comm_message/message = messages[message_index]
@@ -156,7 +162,11 @@
if ("deleteMessage")
if (!authenticated(usr))
return
var/message_index = text2num(params["message"])
var/message_index = params["message"]
if(!isnum(message_index))
message_admins("[ADMIN_LOOKUPFLW(usr)] provided an invalid index type when deleting a message on [src] [ADMIN_JMP(src)]. This should not happen. Please check with a maintainer and/or consult tgui logs.")
CRASH("Non-numeric index provided when deleting comms console message.")
if (!message_index)
return
LAZYREMOVE(messages, LAZYACCESS(messages, message_index))
+20
View File
@@ -21,6 +21,7 @@ Buildable meters
level = 2
var/piping_layer = PIPING_LAYER_DEFAULT
var/RPD_type
var/disposable = TRUE
/obj/item/pipe/directional
RPD_type = PIPE_UNARY
@@ -236,3 +237,22 @@ Buildable meters
/obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT)
piping_layer = new_layer
PIPING_LAYER_DOUBLE_SHIFT(src, piping_layer)
/obj/item/pipe/bluespace
pipe_type = /obj/machinery/atmospherics/pipe/bluespace
var/bluespace_network_name = "default"
icon_state = "bluespace"
disposable = FALSE
/obj/item/pipe/bluespace/attack_self(mob/user)
var/new_name = input(user, "Enter identifier for bluespace pipe network", "bluespace pipe", bluespace_network_name) as text|null
if(!isnull(new_name))
bluespace_network_name = new_name
/obj/item/pipe/bluespace/make_from_existing(obj/machinery/atmospherics/pipe/bluespace/make_from)
bluespace_network_name = make_from.bluespace_network_name
return ..()
/obj/item/pipe/bluespace/build_pipe(obj/machinery/atmospherics/pipe/bluespace/A)
A.bluespace_network_name = bluespace_network_name
return ..()
+1 -1
View File
@@ -140,7 +140,7 @@
if (. & EMP_PROTECT_SELF)
return
if(get_charge())
use_power(cell.charge*severity/100)
use_power((cell.charge/3)*(severity*0.005))
take_damage(severity/3, BURN, "energy", 1)
mecha_log_message("EMP detected", color="red")
+8 -6
View File
@@ -375,12 +375,14 @@ GLOBAL_LIST_INIT(fluid_duct_recipes, list(
. = TRUE
if((mode & DESTROY_MODE) && istype(A, /obj/item/pipe) || istype(A, /obj/structure/disposalconstruct) || istype(A, /obj/structure/c_transit_tube) || istype(A, /obj/structure/c_transit_tube_pod) || istype(A, /obj/item/pipe_meter))
to_chat(user, "<span class='notice'>You start destroying a pipe...</span>")
playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1)
if(do_after(user, destroy_speed, target = A))
activate()
qdel(A)
return
var/obj/item/pipe/P = A
if(!istype(P) || P.disposable)
to_chat(user, "<span class='notice'>You start destroying a pipe...</span>")
playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1)
if(do_after(user, destroy_speed, target = A))
activate()
qdel(A)
return
if((mode & PAINT_MODE))
if(istype(A, /obj/machinery/atmospherics/pipe) && !istype(A, /obj/machinery/atmospherics/pipe/layer_manifold))
+2 -4
View File
@@ -210,8 +210,7 @@
target.apply_effect(EFFECT_STUTTER, stunforce)
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
if(user)
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
target.set_last_attacker(user)
target.visible_message("<span class='danger'>[user] has shocked [target] with [src]!</span>", \
"<span class='userdanger'>[user] has shocked you with [src]!</span>")
log_combat(user, target, "stunned with an electrostaff")
@@ -237,8 +236,7 @@
target.adjustFireLoss(lethal_force) //good against ointment spam
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
if(user)
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
target.set_last_attacker(user)
target.visible_message("<span class='danger'>[user] has seared [target] with [src]!</span>", \
"<span class='userdanger'>[user] has seared you with [src]!</span>")
log_combat(user, target, "burned with an electrostaff")
+1 -1
View File
@@ -312,7 +312,7 @@
trap_damage = 0
item_flags = DROPDEL
flags_1 = NONE
breakouttime = 50
breakouttime = 25
/obj/item/restraints/legcuffs/beartrap/energy/New()
..()
@@ -0,0 +1,74 @@
/datum/deathrattle_group
var/name
var/list/datum/weakref/implant_refs = list()
/datum/deathrattle_group/New()
// Give the group a unique name for debugging, and possible future
// use for making custom linked groups.
name = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]"
/datum/deathrattle_group/proc/rattle(obj/item/implant/deathrattle/origin, mob/living/owner)
var/name = owner.mind ? owner.mind.name : owner.real_name
var/area = get_area_name(get_turf(owner))
for(var/r in implant_refs)
var/datum/weakref/R = r
var/obj/item/implant/deathrattle/implant = R.resolve()
if(!implant || implant == origin)
continue
// Not all the implants may be actually implanted in people.
if(!implant.imp_in)
continue
// Deliberately the same message framing as nanite message + ghost deathrattle
var/mob/living/recipient = implant.imp_in
to_chat(recipient, "<i>You hear a strange, robotic voice in your head...</i> \"<span class='robot'><b>[name]</b> has died at <b>[area]</b>.</span>\"")
SEND_SOUND(recipient, pick(
'sound/items/knell1.ogg',
'sound/items/knell2.ogg',
'sound/items/knell3.ogg',
'sound/items/knell4.ogg',
))
/datum/deathrattle_group/proc/register(obj/item/implant/deathrattle/implant)
implant.group = src
implant_refs += WEAKREF(implant)
/obj/item/implant/deathrattle
name = "deathrattle implant"
desc = "Hope no one else dies, prepare for when they do."
activated = FALSE
var/datum/deathrattle_group/group = null
/obj/item/implant/deathrattle/Destroy()
group = null
return ..()
/obj/item/implant/deathrattle/can_be_implanted_in(mob/living/target)
// Can be implanted in anything that's a mob. Syndicate cyborgs, talking fish, humans...
return TRUE
/obj/item/implant/deathrattle/proc/on_predeath(datum/source, gibbed)
SIGNAL_HANDLER
if(group)
group.rattle(origin = src, owner = source)
/obj/item/implant/deathrattle/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE)
. = ..()
if(.)
RegisterSignal(target, COMSIG_LIVING_PREDEATH, .proc/on_predeath)
if(!group)
to_chat(target, "<i>You hear a strange, robotic voice in your head...</i> \"<span class='robot'>Warning: No other linked implants detected.</span>\"")
/obj/item/implantcase/deathrattle
name = "implant case - 'Deathrattle'"
desc = "A glass case containing a deathrattle implant."
imp_type = /obj/item/implant/deathrattle
@@ -139,14 +139,25 @@
name = "health implant"
activated = 0
var/healthstring = ""
var/list/raw_data = list()
/obj/item/implant/health/proc/sensehealth()
/obj/item/implant/health/proc/sensehealth(get_list = FALSE)
if (!imp_in)
return "ERROR"
else
if(isliving(imp_in))
var/mob/living/L = imp_in
healthstring = "<small>Oxygen Deprivation Damage => [round(L.getOxyLoss())]<br />Fire Damage => [round(L.getFireLoss())]<br />Toxin Damage => [round(L.getToxLoss())]<br />Brute Force Damage => [round(L.getBruteLoss())]</small>"
if (!healthstring)
raw_data = list() //Reset list
raw_data["oxy"] = list("[round(L.getOxyLoss())]") //Suffocation
raw_data["burn"] = list("[round(L.getFireLoss())]") //Burn
raw_data["tox"] = list("[round(L.getToxLoss())]") //Tox
raw_data["brute"] = list("[round(L.getBruteLoss())]") //Brute
if(!healthstring) //I have no idea who made it go this order but okay.
healthstring = "ERROR"
return healthstring
if(!length(raw_data))
raw_data = list("ERROR")
if(!get_list)
return healthstring
else
return raw_data
+12
View File
@@ -271,11 +271,14 @@
var/force_on // Damage when on - not stunning
var/force_off // Damage when off - not stunning
var/weight_class_on // What is the new size class when turned on
var/sword_point = TRUE
wound_bonus = 15
/obj/item/melee/classic_baton/Initialize()
. = ..()
if(sword_point)
AddElement(/datum/element/sword_point)
// Description for trying to stun when still on cooldown.
/obj/item/melee/classic_baton/proc/get_wait_description()
@@ -402,6 +405,8 @@
weight_class_on = WEIGHT_CLASS_BULKY
total_mass = TOTAL_MASS_NORMAL_ITEM
bare_wound_bonus = 5
sword_point = FALSE
var/silent = FALSE
/obj/item/melee/classic_baton/telescopic/suicide_act(mob/user)
var/mob/living/carbon/human/H = user
@@ -431,6 +436,9 @@
w_class = weight_class_on
force = force_on
attack_verb = list("smacked", "struck", "cracked", "beaten")
AddElement(/datum/element/sword_point)
if(!silent)
user?.visible_message("<span class='warning'>[user] extends [src] with a flick of their wrist!</span>")
else
to_chat(user, desc["local_off"])
icon_state = off_icon_state
@@ -439,6 +447,9 @@
w_class = WEIGHT_CLASS_SMALL
force = force_off
attack_verb = list("hit", "poked")
RemoveElement(/datum/element/sword_point)
if(!silent)
user?.visible_message("<span class='warning'>[user] collapses [src] back down!</span>")
playsound(src.loc, on_sound, 50, 1)
add_fingerprint(user)
@@ -465,6 +476,7 @@
force_on = 16
force_off = 5
weight_class_on = WEIGHT_CLASS_NORMAL
silent = TRUE
/obj/item/melee/classic_baton/telescopic/contractor_baton/get_wait_description()
return "<span class='danger'>The baton is still charging!</span>"
@@ -272,7 +272,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
. = ..()
. += GLOB.plastitaniumglass_recipes
/obj/item/stack/sheet/titaniumglass/on_solar_construction(obj/machinery/power/solar/S)
/obj/item/stack/sheet/plastitaniumglass/on_solar_construction(obj/machinery/power/solar/S)
S.max_integrity *= 2
S.efficiency *= 2
+69
View File
@@ -351,6 +351,75 @@
if(!contents.len)
. += "[icon_state]_empty"
//Derringer "Cigarettes"//
/obj/item/storage/fancy/cigarettes/derringer
name = "\improper Robust packet"
desc = "Smoked by the robust."
icon_state = "robust"
spawn_type = /obj/item/gun/ballistic/derringer/traitor
/obj/item/storage/fancy/cigarettes/derringer/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_items = 6
STR.can_hold = typecacheof(list(/obj/item/clothing/mask/cigarette, /obj/item/lighter, /obj/item/gun/ballistic/derringer, /obj/item/ammo_casing/c38, /obj/item/ammo_casing/a357, /obj/item/ammo_casing/g4570))
/obj/item/storage/fancy/cigarettes/derringer/AltClick(mob/living/carbon/user)
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return
var/obj/item/W = (locate(/obj/item/ammo_casing/a357) in contents) || (locate(/obj/item/clothing/mask/cigarette) in contents) || locate(/obj/item/ammo_casing/g4570) //Easy access smokes and bullets
if(W && contents.len > 0)
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, W, user)
user.put_in_hands(W)
contents -= W
to_chat(user, "<span class='notice'>You take \a [W] out of the pack.</span>")
else
to_chat(user, "<span class='notice'>There are no items left in the pack.</span>")
/obj/item/storage/fancy/cigarettes/derringer/PopulateContents()
new spawn_type(src)
new /obj/item/ammo_casing/a357(src)
new /obj/item/ammo_casing/a357(src)
new /obj/item/ammo_casing/a357(src)
new /obj/item/ammo_casing/a357(src)
new /obj/item/clothing/mask/cigarette/syndicate(src)
//For traitors with luck/class
/obj/item/storage/fancy/cigarettes/derringer/gold
name = "\improper Robust Gold packet"
desc = "Smoked by the truly robust."
icon_state = "robustg"
spawn_type = /obj/item/gun/ballistic/derringer/gold
//For operatives, bound in a ka-tet.
/obj/item/storage/fancy/cigarettes/derringer/midworld
name = "\improper Midworld's Lime Bend"
desc = "The wheel of Ka turns, Gunslinger."
icon_state = "slime"
spawn_type = /obj/item/gun/ballistic/derringer/nukeop
/obj/item/storage/fancy/cigarettes/derringer/midworld/PopulateContents()
new spawn_type(src)
new /obj/item/ammo_casing/g4570(src)
new /obj/item/ammo_casing/g4570(src)
new /obj/item/ammo_casing/g4570(src)
new /obj/item/ammo_casing/g4570(src)
new /obj/item/clothing/mask/cigarette/xeno(src)
//For Cargomen, looking for a good deal on arms, with no quarrels as to where they're from.
/obj/item/storage/fancy/cigarettes/derringer/smuggled
name = "\improper Shady Jim's Super Slims packet"
desc = "If you get caught with this, we don't know you, capiche?"
icon_state = "shadyjim"
spawn_type = /obj/item/gun/ballistic/derringer
/obj/item/storage/fancy/cigarettes/derringer/smuggled/PopulateContents()
new spawn_type(src)
new /obj/item/ammo_casing/c38/lethal(src)
new /obj/item/ammo_casing/c38/lethal(src)
new /obj/item/ammo_casing/c38/lethal(src)
new /obj/item/ammo_casing/c38/lethal(src)
new /obj/item/clothing/mask/cigarette/shadyjims (src)
/////////////
//CIGAR BOX//
/////////////
+3 -3
View File
@@ -394,15 +394,15 @@
desc = "You want penis enlargement pills?"
/obj/item/storage/pill_bottle/penis_enlargement/PopulateContents()
for(var/i in 1 to 7)
for(var/i in 1 to 10)
new /obj/item/reagent_containers/pill/penis_enlargement(src)
/obj/item/storage/pill_bottle/breast_enlargement
name = "breast enlargement pills"
desc = "Made by Fermichem - They have a woman with breasts larger than she is on them. The warming states not to take more than 10u at a time."
desc = "Made by Fermichem - The bottle shows a woman with breasts larger than she is on them. The warning states to not take more than 10 units at a time."
/obj/item/storage/pill_bottle/breast_enlargement/PopulateContents()
for(var/i in 1 to 7)
for(var/i in 1 to 10)
new /obj/item/reagent_containers/pill/breast_enlargement(src)
/obj/item/storage/pill_bottle/neurine
+26 -7
View File
@@ -62,7 +62,7 @@
new /obj/item/clothing/glasses/phantomthief/syndicate(src)
new /obj/item/reagent_containers/syringe/stimulants(src)
if("baseball") // 42~ tc
if("baseball") // 44~ tc
new /obj/item/melee/baseball_bat/ablative/syndi(src) //Lets say 12 tc, lesser sleeping carp
new /obj/item/clothing/glasses/sunglasses/garb(src) //Lets say 2 tc
new /obj/item/card/emag(src) //6 tc
@@ -72,6 +72,7 @@
new /obj/item/clothing/under/syndicate/baseball(src) //3tc
new /obj/item/clothing/head/soft/baseball(src) //Lets say 4 tc
new /obj/item/reagent_containers/hypospray/medipen/stimulants/baseball(src) //lets say 5tc
new /obj/item/melee/baseball_bat/telescopic(src) // 2 tc
if("implant") // 67+ tc holy shit what the fuck this is a lottery disguised as fun boxes isn't it?
new /obj/item/implanter/freedom(src)
@@ -154,18 +155,18 @@
new /obj/item/storage/belt/chameleon(src) // Unique but worth at least 2 tc
new /obj/item/card/id/syndicate(src) // 2 tc
new /obj/item/chameleon(src) // 7 tc
if("ancient") //A kit so old, it's probably older than you. //This bundle is filled with the entire unlink contents traitors had access to in 2006, from OpenSS13. Notably the esword was not a choice but existed in code.
new /obj/item/storage/toolbox/emergency/old/ancientbundle(src) //Items fit neatly into a classic toolbox just to remind you what the theme is.
/obj/item/storage/toolbox/emergency/old/ancientbundle //So the subtype works
/obj/item/storage/toolbox/emergency/old/ancientbundle/PopulateContents()
new /obj/item/card/emag(src)
new /obj/item/pen/sleepy(src)
new /obj/item/card/emag(src)
new /obj/item/pen/sleepy(src)
new /obj/item/reagent_containers/pill/cyanide(src)
new /obj/item/chameleon(src) //its not the original cloaking device, but it will do.
new /obj/item/gun/ballistic/revolver(src)
new /obj/item/gun/ballistic/revolver(src)
new /obj/item/implanter/freedom(src)
new /obj/item/stack/telecrystal(src) //The failsafe/self destruct isn't an item we can physically include in the kit, but 1 TC is technically enough to buy the equivalent.
@@ -545,3 +546,21 @@
. = ..()
new /obj/item/cardpack/syndicate(src)
new /obj/item/cardpack/syndicate(src)
/obj/item/storage/box/syndie_kit/imp_deathrattle
name = "deathrattle implant box"
desc = "Contains eight linked deathrattle implants."
/obj/item/storage/box/syndie_kit/imp_deathrattle/PopulateContents()
new /obj/item/implanter(src)
var/datum/deathrattle_group/group = new
var/implants = list()
for(var/j in 1 to 8)
var/obj/item/implantcase/deathrattle/case = new (src)
implants += case.imp
for(var/i in implants)
group.register(i)
desc += " The implants are registered to the \"[group.name]\" group."
+2 -1
View File
@@ -46,7 +46,8 @@
/obj/item/instrument/harmonica,
/obj/item/mining_voucher,
/obj/item/suit_voucher,
/obj/item/reagent_containers/pill))
/obj/item/reagent_containers/pill,
/obj/item/gun/ballistic/derringer))
/obj/item/storage/wallet/Exited(atom/movable/AM)
. = ..()
+1 -2
View File
@@ -209,8 +209,7 @@
L.apply_effect(EFFECT_STUTTER, stamforce)
SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
if(user)
L.lastattacker = user.real_name
L.lastattackerckey = user.ckey
L.set_last_attacker(user)
L.visible_message("<span class='danger'>[user] has [disarming? "disarmed" : "stunned"] [L] with [src]!</span>", \
"<span class='userdanger'>[user] has [disarming? "disarmed" : "stunned"] you with [src]!</span>")
log_combat(user, L, disarming? "disarmed" : "stunned")
+62
View File
@@ -911,6 +911,16 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
var/homerun_ready = 0
var/homerun_able = 0
total_mass = 2.7 //a regular wooden major league baseball bat weighs somewhere between 2 to 3.4 pounds, according to google
var/on_sound
var/on = TRUE // Are we on or off
var/on_icon_state // What is our sprite when turned on
var/off_icon_state // What is our sprite when turned off
var/on_item_state // What is our in-hand sprite when turned on
var/force_on // Damage when on
var/force_off // Damage when off
var/throwforce_on // Damage when on
var/throwforce_off // Damage when off
var/weight_class_on // What is the new size class when turned on
/obj/item/melee/baseball_bat/Initialize()
. = ..()
@@ -992,6 +1002,58 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
force = 18 //Spear damage...
throwforce = 30
/obj/item/melee/baseball_bat/proc/get_on_description()
. = list()
.["local_on"] = "<span class ='warning'>You extend the bat.</span>"
.["local_off"] = "<span class ='notice'>You collapse the bat.</span>"
return .
/obj/item/melee/baseball_bat/telescopic
name = "telescopic baseball bat"
desc = "A stealthy telescopic bat that can fit in a pocket when collapsed."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "baseball_bat_telescopic_0"
lefthand_file = 'icons/mob/inhands/weapons/melee_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/melee_righthand.dmi'
item_state = null
w_class = WEIGHT_CLASS_SMALL
item_flags = NONE
force = 5
throwforce = 10
on = FALSE
on_sound = 'sound/weapons/batonextend.ogg'
on_icon_state = "baseball_bat_telescopic_1"
off_icon_state = "baseball_bat_telescopic_0"
on_item_state = "baseball_bat_telescopic"
force_on = 15
force_off = 5
throwforce_on = 20
throwforce_off = 10
weight_class_on = WEIGHT_CLASS_HUGE
total_mass = TOTAL_MASS_NORMAL_ITEM
/obj/item/melee/baseball_bat/telescopic/attack_self(mob/user)
on = !on
var/list/desc = get_on_description()
if(on)
to_chat(user, desc["local_on"])
icon_state = on_icon_state
item_state = on_item_state
w_class = weight_class_on
force = force_on
throwforce = throwforce_on
attack_verb = list("beat", "smacked")
else
to_chat(user, desc["local_off"])
icon_state = off_icon_state
item_state = null //no sprite for concealment even when in hand
w_class = WEIGHT_CLASS_SMALL
force = force_off
throwforce = throwforce_off
attack_verb = list("drubbed", "beaned")
playsound(src.loc, on_sound, 50, 1)
add_fingerprint(user)
/obj/item/melee/flyswatter
name = "flyswatter"
desc = "Useful for killing pests of all sizes."
@@ -306,10 +306,12 @@
return
if(user.transferItemToLoc(W, drop_location())) // so we put in unlit welder too
return
else if(!opened && user.a_intent == INTENT_HELP && !W.tool_behaviour)
else if(!opened && user.a_intent == INTENT_HELP)
var/item_is_id = W.GetID()
if(!item_is_id)
open(user)
if(!open(user))
togglelock(user)
return
return
if(item_is_id || !toggle(user))
togglelock(user)
@@ -19,3 +19,50 @@
desc = "A sturdier card-locked storage unit used for bulky shipments."
max_integrity = 500 // Same as crates.
melee_min_damage = 25 // Idem.
/obj/structure/closet/secure_closet/goodies/owned
name = "private locker"
desc = "A locker designed to only open for who purchased its contents."
///Account of the person buying the crate if private purchasing.
var/datum/bank_account/buyer_account
///Department of the person buying the crate if buying via the NIRN app.
var/datum/bank_account/department/department_account
///Is the secure crate opened or closed?
var/privacy_lock = TRUE
///Is the crate being bought by a person, or a budget card?
var/department_purchase = FALSE
/obj/structure/closet/secure_closet/goodies/owned/examine(mob/user)
. = ..()
. += "<span class='notice'>It's locked with a privacy lock, and can only be unlocked by the buyer's ID.</span>"
/obj/structure/closet/secure_closet/goodies/owned/Initialize(mapload, datum/bank_account/_buyer_account)
. = ..()
buyer_account = _buyer_account
if(istype(buyer_account, /datum/bank_account/department))
department_purchase = TRUE
department_account = buyer_account
/obj/structure/closet/secure_closet/goodies/owned/togglelock(mob/living/user, silent)
if(privacy_lock)
if(!broken)
var/obj/item/card/id/id_card = user.get_idcard(TRUE)
if(id_card)
if(id_card.registered_account)
if(id_card.registered_account == buyer_account || (department_purchase && (id_card.registered_account?.account_job?.paycheck_department) == (department_account.department_id)))
if(iscarbon(user))
add_fingerprint(user)
locked = !locked
user.visible_message("<span class='notice'>[user] unlocks [src]'s privacy lock.</span>",
"<span class='notice'>You unlock [src]'s privacy lock.</span>")
privacy_lock = FALSE
update_icon()
else if(!silent)
to_chat(user, "<span class='notice'>Bank account does not match with buyer!</span>")
else if(!silent)
to_chat(user, "<span class='notice'>No linked bank account detected!</span>")
else if(!silent)
to_chat(user, "<span class='notice'>No ID detected!</span>")
else if(!silent)
to_chat(user, "<span class='warning'>[src] is broken!</span>")
else ..()
@@ -590,7 +590,7 @@
/obj/effect/mob_spawn/human/pirate
name = "space pirate sleeper"
desc = "A cryo sleeper smelling faintly of rum."
desc = "A cryo sleeper smelling faintly of rum. The sleeper looks unstable. <i>Perhaps the pirate within can be killed with the right tools...</i>"
job_description = "Space Pirate"
random = TRUE
icon = 'icons/obj/machines/sleeper.dmi'
@@ -608,6 +608,54 @@
assignedrole = "Space Pirate"
var/rank = "Mate"
/obj/effect/mob_spawn/human/pirate/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
if(user.mind.has_antag_datum(/datum/antagonist/pirate))
to_chat(user, "<span class='notice'>Your shipmate sails within their dreams for now. Perhaps they may wake up eventually.</span>")
else
to_chat(user, "<span class='notice'>If you want to kill the pirate off, something to pry open the sleeper might be the best way to do it.</span>")
/obj/effect/mob_spawn/human/pirate/attackby(obj/item/W, mob/user, params)
if(W.tool_behaviour == TOOL_CROWBAR && user.a_intent != INTENT_HARM)
if(user.mind.has_antag_datum(/datum/antagonist/pirate))
to_chat(user,"<span class='warning'>Why would you want to do that to your shipmate? That'd kill them.</span>")
return
user.visible_message("<span class='warning'>[user] start to pry open [src]...</span>",
"<span class='notice'>You start to pry open [src]...</span>",
"<span class='italics'>You hear prying...</span>")
W.play_tool_sound(src)
if(do_after(user, 100*W.toolspeed, target = src))
user.visible_message("<span class='warning'>[user] pries open [src], disrupting the sleep of the pirate within and killing them.</span>",
"<span class='notice'>You pry open [src], disrupting the sleep of the pirate within and killing them.</span>",
"<span class='italics'>You hear prying, followed by the death rattling of bones.</span>")
log_game("[key_name(user)] has successfully pried open [src] and disabled a space pirate spawner.")
W.play_tool_sound(src)
playsound(src.loc, 'modular_citadel/sound/voice/scream_skeleton.ogg', 50, 1, 4, 1.2)
if(rank == "Captain")
new /obj/effect/mob_spawn/human/pirate/corpse/captain(get_turf(src))
else
new /obj/effect/mob_spawn/human/pirate/corpse(get_turf(src))
qdel(src)
else
..()
/obj/effect/mob_spawn/human/pirate/corpse //occurs when someone pries a pirate out of their sleeper.
mob_name = "Dead Space Pirate"
death = TRUE
instant = TRUE
random = FALSE
/obj/effect/mob_spawn/human/pirate/corpse/Destroy()
return ..()
/obj/effect/mob_spawn/human/pirate/corpse/captain
rank = "Captain"
mob_name = "Dead Space Pirate Captain"
outfit = /datum/outfit/pirate/space/captain
/obj/effect/mob_spawn/human/pirate/special(mob/living/new_spawn)
new_spawn.fully_replace_character_name(new_spawn.real_name,generate_pirate_name())
new_spawn.mind.add_antag_datum(/datum/antagonist/pirate)
@@ -199,8 +199,7 @@
return TRUE
/obj/item/gun_control/attack(mob/living/M, mob/living/user)
M.lastattacker = user.real_name
M.lastattackerckey = user.ckey
M.set_last_attacker(user)
M.attacked_by(src, user)
add_fingerprint(user)
@@ -67,6 +67,9 @@
to_chat(user, "<span class='notice'>You put [I] in [src].</span>")
update_icon()
/obj/structure/tank_dispenser/attack_robot(mob/user)
return _try_interact(user)
/obj/structure/tank_dispenser/ui_state(mob/user)
return GLOB.physical_state
+1 -1
View File
@@ -428,7 +428,7 @@
output += "<option value='[j]'>[j]</option>"
for(var/j in GLOB.nonhuman_positions)
output += "<option value='[j]'>[j]</option>"
for(var/j in list(ROLE_TRAITOR, ROLE_CHANGELING, ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_WIZARD))
for(var/j in list(ROLE_TRAITOR, ROLE_CHANGELING, ROLE_OPERATIVE, ROLE_REV, ROLE_CULTIST, ROLE_WIZARD, ROLE_HERETIC))
output += "<option value='[j]'>[j]</option>"
output += "</select></td></tr></table>"
output += "<b>Reason:<br></b><textarea name='dbbanreason' cols='50'></textarea><br>"
+4
View File
@@ -101,6 +101,10 @@ GLOBAL_VAR(antag_prototypes)
out += "Mind currently owned by key: [key] [active?"(synced)":"(not synced)"]<br>"
out += "Assigned role: [assigned_role]. <a href='?src=[REF(src)];role_edit=1'>Edit</a><br>"
out += "Faction and special role: <b><font color='red'>[special_role]</font></b><br>"
var/datum/component/activity/activity = current.GetComponent(/datum/component/activity)
if(activity)
out += "Activity level: [activity.activity_level]<br>"
out += "Hasn't changed areas in approximately [activity.not_moved_counter] seconds"
var/special_statuses = get_special_statuses()
if(length(special_statuses))
+6
View File
@@ -870,6 +870,12 @@
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=changeling;jobban4=[REF(M)]'>Changeling</a></td>"
//Heretic
if(jobban_isbanned(M, ROLE_HERETIC) || isbanned_dept)
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=heretic;jobban4=[REF(M)]'><font color=red>Heretic</font></a></td>"
else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=heretic;jobban4=[REF(M)]'>Heretic</a></td>"
//Nuke Operative
if(jobban_isbanned(M, ROLE_OPERATIVE) || isbanned_dept)
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=operative;jobban4=[REF(M)]'><font color=red>Nuke Operative</font></a></td>"
@@ -111,6 +111,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/datum/skill_modifier/job/M = GLOB.skill_modifiers[GET_SKILL_MOD_ID(A, type)]
if(istype(M))
M.name = "[name] Training"
owner.AddComponent(/datum/component/activity)
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
/datum/antagonist/proc/is_banned(mob/M)
@@ -141,6 +142,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/datum/team/team = get_team()
if(team)
team.remove_member(owner)
// we don't remove the activity component on purpose--no real point to it
qdel(src)
/datum/antagonist/proc/greet()
@@ -511,8 +511,7 @@
/obj/item/abductor/baton/proc/StunAttack(mob/living/L,mob/living/user)
L.lastattacker = user.real_name
L.lastattackerckey = user.ckey
L.set_last_attacker(user)
L.adjustStaminaLoss(35) //because previously it took 5-6 hits to actually "incapacitate" someone for the purposes of the sleep inducement
L.DefaultCombatKnockdown(140)
@@ -87,7 +87,7 @@
if(!M || !M.current)
continue
if(ishuman(M.current))
M.current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
M.current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -ANTAG_LAYER))
var/turf/T = get_turf(src)
var/list/open_turfs = list()
for(var/turf/open/OT in orange(1, T))
@@ -139,7 +139,7 @@
current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G && G.active && ishuman(current))
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -MUTATIONS_LAYER))
current.add_overlay(mutable_appearance('icons/effects/genetics.dmi', "servitude", -ANTAG_LAYER))
/datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
var/mob/living/current = owner.current
+1 -2
View File
@@ -385,8 +385,7 @@
qdel(src)
return
log_combat(user, M, "used a cult spell on", source.name, "")
M.lastattacker = user.real_name
M.lastattackerckey = user.ckey
M.set_last_attacker(user)
/obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity)
. = ..()
+1 -1
View File
@@ -332,7 +332,7 @@
var/mob/living/carbon/human/H = cultist
new /obj/effect/temp_visual/cult/sparks(get_turf(H), H.dir)
var/istate = pick("halo1","halo2","halo3","halo4","halo5","halo6")
H.add_overlay(mutable_appearance('icons/effects/32x64.dmi', istate, -BODY_FRONT_LAYER))
H.add_overlay(mutable_appearance('icons/effects/32x64.dmi', istate, -ANTAG_LAYER))
/datum/team/cult/proc/setup_objectives()
//SAC OBJECTIVE , todo: move this to objective internals
+14 -3
View File
@@ -1,6 +1,6 @@
//Revenants: based off of wraiths from Goon
//"Ghosts" that are invisible and move like ghosts, cannot take damage while invisible
//Don't hear deadchat and are NOT normal ghosts
//Can hear deadchat, but are NOT normal ghosts and do NOT have x-ray vision
//Admin-spawn or random event
#define INVISIBILITY_REVENANT 50
@@ -63,6 +63,7 @@
var/essence_regenerating = TRUE //If the revenant regenerates essence or not
var/essence_regen_amount = 5 //How much essence regenerates
var/essence_accumulated = 0 //How much essence the revenant has stolen
var/essence_excess = 0 //How much stolen essence available for unlocks
var/revealed = FALSE //If the revenant can take damage from normal sources.
var/unreveal_time = 0 //How long the revenant is revealed for, is about 2 seconds times this var.
var/unstun_time = 0 //How long the revenant is stunned for, is about 2 seconds times this var.
@@ -76,6 +77,7 @@
/mob/living/simple_animal/revenant/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_SIXTHSENSE, INNATE_TRAIT)
AddSpell(new /obj/effect/proc_holder/spell/targeted/night_vision/revenant(null))
AddSpell(new /obj/effect/proc_holder/spell/targeted/telepathy/revenant(null))
AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/defile(null))
@@ -138,6 +140,7 @@
. = ..()
. += "Current essence: [essence]/[essence_regen_cap]E"
. += "Stolen essence: [essence_accumulated]E"
. += "Unused stolen essence: [essence_excess]E)"
. += "Stolen perfect souls: [perfectsouls]"
/mob/living/simple_animal/revenant/update_health_hud()
@@ -304,16 +307,24 @@
return FALSE
return TRUE
/mob/living/simple_animal/revenant/proc/unlock(essence_cost)
if(essence_excess < essence_cost)
return FALSE
essence_excess -= essence_cost
update_action_buttons_icon()
return TRUE
/mob/living/simple_animal/revenant/proc/change_essence_amount(essence_amt, silent = FALSE, source = null)
if(!src)
return
if(essence + essence_amt <= 0)
if(essence + essence_amt < 0)
return
essence = max(0, essence+essence_amt)
update_action_buttons_icon()
update_health_hud()
if(essence_amt > 0)
essence_accumulated = max(0, essence_accumulated+essence_amt)
essence_excess = max(0, essence_excess+essence_amt)
update_action_buttons_icon()
if(!silent)
if(essence_amt > 0)
to_chat(src, "<span class='revennotice'>Gained [essence_amt]E[source ? " from [source]":""].</span>")
@@ -61,6 +61,8 @@
to_chat(src, "<span class='revenminor'>You begin siphoning essence from [target]'s soul.</span>")
if(target.stat != DEAD)
to_chat(target, "<span class='warning'>You feel a horribly unpleasant draining sensation as your grip on life weakens...</span>")
if(target.stat == SOFT_CRIT)
target.Stun(46)
reveal(46)
stun(46)
target.visible_message("<span class='warning'>[target] suddenly rises slightly into the air, [target.p_their()] skin turning an ashy gray.</span>")
@@ -144,7 +146,7 @@
if(user.inhibited)
return FALSE
if(locked)
if(user.essence <= unlock_amount)
if(user.essence_excess <= unlock_amount)
return FALSE
if(user.essence <= cast_amount)
return FALSE
@@ -158,7 +160,7 @@
locked = FALSE
return TRUE
if(locked)
if(!user.castcheck(-unlock_amount))
if(!user.unlock(unlock_amount))
charge_counter = charge_max
return FALSE
name = "[initial(name)] ([cast_amount]E)"
@@ -185,6 +187,7 @@
range = 5
stun = 30
cast_amount = 40
unlock_amount = 25
var/shock_range = 2
var/shock_damage = 15
action_icon_state = "overload_lights"
@@ -197,7 +200,7 @@
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/proc/overload(turf/T, mob/user)
for(var/obj/machinery/light/L in T)
if(!L.on)
return
continue
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</span>")
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(4, 0, L)
@@ -226,7 +229,7 @@
range = 4
stun = 20
reveal = 40
unlock_amount = 75
unlock_amount = 10
cast_amount = 30
action_icon_state = "defile"
@@ -277,7 +280,7 @@
charge_max = 200
range = 4
cast_amount = 60
unlock_amount = 200
unlock_amount = 125
action_icon_state = "malfunction"
//A note to future coders: do not replace this with an EMP because it will wreck malf AIs and everyone will hate you.
@@ -324,7 +327,7 @@
charge_max = 200
range = 3
cast_amount = 50
unlock_amount = 200
unlock_amount = 75
action_icon_state = "blight"
/obj/effect/proc_holder/spell/aoe_turf/revenant/blight/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
@@ -20,7 +20,7 @@
continue
if(!SSpersistence.IsValidDebrisLocation(C.loc, allowed_turf_typecache, allowed_z_cache, C.type, FALSE))
continue
weight += 0.05
weight += 0.03
return ..()
/datum/round_event/ghost_role/slaughter
@@ -1,30 +1,7 @@
/datum/traitor_class/human/assassin
/datum/traitor_class/human/subterfuge/assassin
name = "Donk Co Operative"
employer = "Donk Corporation"
weight = 0
weight = 6
chaos = 1
threat = 2
/datum/traitor_class/human/assassin/forge_single_objective(datum/antagonist/traitor/T)
.=1
var/permakill_prob = 20
var/datum/game_mode/dynamic/mode
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
permakill_prob = max(0,mode.threat_level-50)
var/list/active_ais = active_ais()
if(active_ais.len && prob(100/GLOB.joined_player_list.len))
var/datum/objective/destroy/destroy_objective = new
destroy_objective.owner = T.owner
destroy_objective.find_target()
T.add_objective(destroy_objective)
else if(prob(permakill_prob))
var/datum/objective/assassinate/kill_objective = new
kill_objective.owner = T.owner
kill_objective.find_target()
T.add_objective(kill_objective)
else
var/datum/objective/assassinate/once/kill_objective = new
kill_objective.owner = T.owner
kill_objective.find_target()
T.add_objective(kill_objective)
assassin_prob = 70
@@ -1,12 +1,12 @@
/datum/traitor_class/human/freeform
name = "Waffle Co Agent"
employer = "Waffle Company"
weight = 0 // should not spawn in unless admins bus something in the traitor panel with setting traitor classes
weight = 5
chaos = 0
/datum/traitor_class/human/freeform/forge_objectives(datum/antagonist/traitor/T)
var/datum/objective/escape/O = new
O.explanation_text = "You have no explicit goals! While we don't approve of mindless slaughter, you may antagonize nanotrasen any way you wish! Make sure to escape alive and not in custody, though!"
var/datum/objective/freedom/O = new
O.explanation_text = "You have no explicit goals! While we don't approve of mindless slaughter, you may antagonize nanotrasen any way you wish! Don't get captured or killed, but if you've done nothing, you'll be in trouble!"
O.owner = T.owner
T.add_objective(O)
return
@@ -13,8 +13,12 @@
T.assign_exchange_role(SSticker.mode.exchange_blue)
objective_count += 1 //Exchange counts towards number of objectives
var/toa = CONFIG_GET(number/traitor_objectives_amount)
var/attempts = 0
for(var/i = objective_count, i < toa, i++)
forge_single_objective(T)
var/success = FALSE
while(!success && attempts < max(toa*10, 100))
success = forge_single_objective(T)
attempts += 1
if(!(locate(/datum/objective/escape) in T.objectives))
var/datum/objective/escape/escape_objective = new
escape_objective.owner = T.owner
@@ -1,12 +1,11 @@
/datum/traitor_class/human/subterfuge
name = "MI13 Operative"
employer = "MI13"
weight = 36
weight = 25
chaos = -5
var/assassin_prob = 25
/datum/traitor_class/human/subterfuge/forge_single_objective(datum/antagonist/traitor/T)
.=1
var/assassin_prob = 30
var/datum/game_mode/dynamic/mode
if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode
@@ -16,24 +15,31 @@
kill_objective.owner = T.owner
kill_objective.find_target()
T.add_objective(kill_objective)
return TRUE
else
var/list/weights = list()
weights["sabo"] = length(subtypesof(/datum/sabotage_objective))
weights["steal"] = length(subtypesof(/datum/objective_item/steal))
var/datum/objective/sabotage/sabotage_objective = new
sabotage_objective.owner = T.owner
if(sabotage_objective.find_target())
weights["sabo"] = length(subtypesof(/datum/objective_item/steal))
var/datum/objective/steal/steal_objective = new
steal_objective.owner = T.owner
if(steal_objective.find_target())
weights["steal"] = length(subtypesof(/datum/objective_item/steal))
weights["download"] = !(locate(/datum/objective/download) in T.objectives || (T.owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
switch(pickweight(weights))
if("sabo")
var/datum/objective/sabotage/sabotage_objective = new
sabotage_objective.owner = T.owner
sabotage_objective.find_target()
T.add_objective(sabotage_objective)
qdel(steal_objective)
return TRUE
if("steal")
var/datum/objective/steal/steal_objective = new
steal_objective.owner = T.owner
steal_objective.find_target()
T.add_objective(steal_objective)
qdel(sabotage_objective)
return TRUE
if("download")
var/datum/objective/download/download_objective = new
download_objective.owner = T.owner
download_objective.gen_amount_goal()
T.add_objective(download_objective)
return TRUE
return FALSE
@@ -7,7 +7,8 @@ GLOBAL_LIST_EMPTY(traitor_classes)
var/chaos = 0
var/threat = 0
var/TC = 20
/// Minimum players for this to randomly roll via get_random_traitor_class().
var/processing = FALSE
/// Minimum players for this to randomly roll via get_random_traitor_kind().
var/min_players = 0
var/list/uplink_filters
@@ -43,4 +44,6 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/datum/traitor_class/proc/clean_up_traitor(datum/antagonist/traitor/T)
// Any effects that need to be cleaned up if traitor class is being swapped.
/datum/traitor_class/proc/on_process(/datum/antagonist/traitor/T)
// only for processing traitor classes; runs once an SSprocessing tick
@@ -26,6 +26,8 @@
if(traitor_kind)
traitor_kind.remove_innate_effects(owner.current)
traitor_kind.clean_up_traitor(src)
if(traitor_kind.processing)
STOP_PROCESSING(SSprocessing, src)
swap_from_old = TRUE
traitor_kind = GLOB.traitor_classes[kind]
traitor_kind.apply_innate_effects(owner.current)
@@ -33,11 +35,16 @@
for(var/O in objectives)
qdel(O)
traitor_kind.forge_objectives(src)
if(traitor_kind.processing)
START_PROCESSING(SSprocessing, src)
if(swap_from_old)
traitor_kind.finalize_traitor(src)
traitor_kind.greet(src)
owner.announce_objectives()
/datum/antagonist/traitor/process()
traitor_kind.on_process(src)
/proc/get_random_traitor_kind(var/list/blacklist = list())
var/chaos_weight = 0
if(istype(SSticker.mode,/datum/game_mode/dynamic))
@@ -0,0 +1,86 @@
GLOBAL_LIST_EMPTY(bluespace_pipe_networks)
/obj/machinery/atmospherics/pipe/bluespace
name = "bluespace pipe"
desc = "Transmits gas across large distances of space. Developed using bluespace technology."
icon = 'icons/obj/atmospherics/pipes/bluespace.dmi'
icon_state = "map"
pipe_state = "bluespace"
dir = SOUTH
initialize_directions = SOUTH
device_type = UNARY
can_buckle = FALSE
construction_type = /obj/item/pipe/bluespace
var/bluespace_network_name
/obj/machinery/atmospherics/pipe/bluespace/New()
icon_state = "pipe"
if(bluespace_network_name) // in case someone maps one in for some reason
if(!GLOB.bluespace_pipe_networks[bluespace_network_name])
GLOB.bluespace_pipe_networks[bluespace_network_name] = list()
GLOB.bluespace_pipe_networks[bluespace_network_name] |= src
..()
/obj/machinery/atmospherics/pipe/bluespace/on_construction()
. = ..()
if(bluespace_network_name)
if(!GLOB.bluespace_pipe_networks[bluespace_network_name])
GLOB.bluespace_pipe_networks[bluespace_network_name] = list()
GLOB.bluespace_pipe_networks[bluespace_network_name] |= src
/obj/machinery/atmospherics/pipe/bluespace/Destroy()
if(GLOB.bluespace_pipe_networks[bluespace_network_name])
GLOB.bluespace_pipe_networks[bluespace_network_name] -= src
for(var/p in GLOB.bluespace_pipe_networks[bluespace_network_name])
var/obj/machinery/atmospherics/pipe/bluespace/P = p
QDEL_NULL(P.parent)
P.build_network()
return ..()
/obj/machinery/atmospherics/pipe/bluespace/examine(user)
. = ..()
. += "<span class='notice'>This one is connected to the \"[html_encode(bluespace_network_name)]\" network.</span>"
/obj/machinery/atmospherics/pipe/bluespace/SetInitDirections()
initialize_directions = dir
/obj/machinery/atmospherics/pipe/bluespace/pipeline_expansion()
return ..() + GLOB.bluespace_pipe_networks[bluespace_network_name] - src
/obj/machinery/atmospherics/pipe/bluespace/hide()
update_icon()
/obj/machinery/atmospherics/pipe/bluespace/update_icon(showpipe)
underlays.Cut()
var/turf/T = loc
if(level == 2 || !T.intact)
showpipe = TRUE
plane = GAME_PLANE
else
showpipe = FALSE
plane = FLOOR_PLANE
if(!showpipe)
return //no need to update the pipes if they aren't showing
var/connected = 0 //Direction bitset
for(var/i in 1 to device_type) //adds intact pieces
if(nodes[i])
var/obj/machinery/atmospherics/node = nodes[i]
var/image/img = get_pipe_underlay("pipe_intact", get_dir(src, node), node.pipe_color)
underlays += img
connected |= img.dir
for(var/direction in GLOB.cardinals)
if((initialize_directions & direction) && !(connected & direction))
underlays += get_pipe_underlay("pipe_exposed", direction)
/obj/machinery/atmospherics/pipe/bluespace/paint()
return FALSE
/obj/machinery/atmospherics/pipe/bluespace/proc/get_pipe_underlay(state, dir, color = null)
if(color)
. = getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', state, dir, color)
else
. = getpipeimage('icons/obj/atmospherics/components/binary_devices.dmi', state, dir)
@@ -48,3 +48,12 @@
price_max = 300
stock_max = 3
availability_prob = 40
/datum/blackmarket_item/weapon/derringer
name = ".38 Derringer"
desc = "Compact safety! Now at a premium!"
item = /obj/item/gun/ballistic/derringer
price_min = 500
price_max = 1500
stock_max = 3
availability_prob = 30
+5 -1
View File
@@ -15,13 +15,17 @@
var/special_enabled = FALSE
var/DropPodOnly = FALSE //only usable by the Bluespace Drop Pod via the express cargo console
var/admin_spawned = FALSE //Can only an admin spawn this crate?
// this might be all in all unnecessary with current code if some changes are made
var/goody = PACK_GOODY_NONE //Small items can be grouped into a single crate.They also come in a closet/lockbox instead of a full crate, so the 700 min doesn't apply
var/can_private_buy = TRUE //Can it be purchased privately by each crewmember?
/datum/supply_pack/proc/generate(atom/A, datum/bank_account/paying_account)
var/obj/structure/closet/crate/C
if(paying_account)
C = new /obj/structure/closet/crate/secure/owned(A, paying_account)
if(ispath(crate_type, /obj/structure/closet/secure_closet/goodies)) // lets ensure private orders don't come in crates when the original one comes in lockers
C = new /obj/structure/closet/secure_closet/goodies/owned(A, paying_account) // that would lead to infinite money exploits
else
C = new /obj/structure/closet/crate/secure/owned(A, paying_account)
C.name = "[crate_name] - Purchased by [paying_account.account_holder]"
else
C = new crate_type(A)
+32
View File
@@ -232,3 +232,35 @@
desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids."
cost = 1500
contains = list(/obj/item/gun/energy/laser/hellgun)
/datum/supply_pack/security/armory/derringerclassic
name = "Holdout Crate"
crate_name = "dented crate"
desc = "Hey kid.. c'mere. Boss says we need to offload these, to any buyer, no questions asked. You pay us, we give you three of these guns, no strings attached. Locks are to ensure they get to PAYING customers."
cost = 2000
contraband = TRUE
can_private_buy = TRUE
contains = list(/obj/item/storage/fancy/cigarettes/derringer/smuggled,
/obj/item/storage/fancy/cigarettes/derringer/smuggled,
/obj/item/storage/fancy/cigarettes/derringer/smuggled,
/obj/item/storage/wallet)
/datum/supply_pack/security/armory/esoteric_arms
name = "Esoteric Armory Shipment"
desc = "Well.. you're an agent of taste, I can tell that much. For the right price.. we could see our way clear to send you one of our more... unique weapons."
hidden = TRUE
cost = 10000
can_private_buy = TRUE
crate_name = "dusty crate"
var/num_contained = 1
contains = list(/obj/item/gun/ballistic/shotgun/leveraction,
/obj/item/storage/fancy/cigarettes/derringer/gold,
/obj/item/gun/ballistic/revolver/nagant,
/obj/item/gun/ballistic/automatic/pistol/APS,
/obj/item/gun/ballistic/revolver/golden)
/datum/supply_pack/security/armory/esoteric_arms/fill(obj/structure/closet/crate/C)
var/list/L = contains.Copy()
for(var/i in 1 to num_contained)
var/item = pick_n_take(L)
new item(C)
+1
View File
@@ -81,3 +81,4 @@
desc = "Contains one standard epinephrine medipen and one standard emergency first-aid kit medipen. For when you want to prepare for the worst."
cost = 500
contains = list(/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/hypospray/medipen/ekit)
+8 -8
View File
@@ -14,56 +14,56 @@
//////////////////////////////////////////////////////////////////////////////
/datum/supply_pack/materials/cardboard50
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "50 Cardboard Sheets"
desc = "Create a bunch of boxes."
cost = 300 //thrice their export value
contains = list(/obj/item/stack/sheet/cardboard/fifty)
/datum/supply_pack/materials/glass50
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "50 Glass Sheets"
desc = "Let some nice light in with fifty glass sheets!"
cost = 300 //double their export value
contains = list(/obj/item/stack/sheet/glass/fifty)
/datum/supply_pack/materials/metal50
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "50 Metal Sheets"
desc = "Any construction project begins with a good stack of fifty metal sheets!"
cost = 300 //double their export value
contains = list(/obj/item/stack/sheet/metal/fifty)
/datum/supply_pack/materials/plasteel20
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "20 Plasteel Sheets"
desc = "Reinforce the station's integrity with twenty plasteel sheets!"
cost = 4000
contains = list(/obj/item/stack/sheet/plasteel/twenty)
/datum/supply_pack/materials/plastic50
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "50 Plastic Sheets"
desc = "Build a limitless amount of toys with fifty plastic sheets!"
cost = 200 // double their export
contains = list(/obj/item/stack/sheet/plastic/twenty)
/datum/supply_pack/materials/sandstone30
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "30 Sandstone Blocks"
desc = "Neither sandy nor stoney, these thirty blocks will still get the job done."
cost = 150 // five times their export
contains = list(/obj/item/stack/sheet/mineral/sandstone/thirty)
/datum/supply_pack/materials/wood20
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "20 Wood Planks"
desc = "Turn cargo's boring metal groundwork into beautiful panelled flooring and much more with twenty wooden planks!"
cost = 400 // 6-7 planks shy from having equal import/export prices
contains = list(/obj/item/stack/sheet/mineral/wood/twenty)
/datum/supply_pack/materials/rcdammo
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "Large RCD ammo Single-Pack"
desc = "A single large compressed RCD matter pack, to help with any holes or projects people might be working on."
cost = 600
+1 -1
View File
@@ -370,7 +370,7 @@
//////////////////////////////////////////////////////////////////////////////
/datum/supply_pack/misc/carpet
goody = PACK_GOODY_PUBLIC
crate_type = /obj/structure/closet/secure_closet/goodies
name = "Classic Carpet Single-Pack"
desc = "Plasteel floor tiles getting on your nerves? This 50 units stack of extra soft carpet will tie any room together."
cost = 200
+4 -2
View File
@@ -21,7 +21,7 @@
startWhen = 60 //2 minutes to answer
var/datum/comm_message/threat_msg
var/payoff = 0
var/payoff_min = 20000
var/payoff_min = 1000
var/paid_off = FALSE
var/pirate_type
var/ship_template
@@ -66,7 +66,7 @@
SScommunications.send_message(threat_msg,unique = TRUE)
/datum/round_event/pirates/proc/answered()
if(threat_msg && threat_msg.answered == 1)
if(threat_msg?.answered == 1)
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
if(D)
if(D.adjust_money(-payoff))
@@ -75,6 +75,8 @@
return
else
priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name)
else if(threat_msg?.answered == 2)
priority_announce("You won't pay? Fine then, we'll take those credits by force!",sender_override = ship_name)
if(!shuttle_spawned)
spawn_shuttle()
else
@@ -278,6 +278,19 @@
visible_message("[src] finishes cooking!")
new /obj/item/reagent_containers/food/snacks/meat/steak/goliath(loc)
qdel(src)
/obj/item/reagent_containers/food/snacks/meat/slab/dragon
name = "ash drake meat"
desc = "Meat from an ash drake. It's probably not a good idea to eat this raw."
list_reagents = list(/datum/reagent/consumable/nutriment = 3, /datum/reagent/toxin = 5, /datum/reagent/consumable/cooking_oil = 3)
icon_state = "goliathmeat"
tastes = list("meat" = 1)
foodtype = RAW | MEAT | TOXIC
/obj/item/reagent_containers/food/snacks/meat/slab/dragon/burn()
visible_message("[src] finishes cooking!")
new /obj/item/reagent_containers/food/snacks/meat/steak/dragon(loc)
qdel(src)
/obj/item/reagent_containers/food/snacks/meat/slab/meatwheat
name = "meatwheat clump"
@@ -395,6 +408,16 @@
trash = null
tastes = list("meat" = 1, "rock" = 1)
foodtype = MEAT
/obj/item/reagent_containers/food/snacks/meat/steak/dragon
name = "dragon steak"
desc = "Spicy."
resistance_flags = LAVA_PROOF | FIRE_PROOF
icon_state = "goliathsteak"
list_reagents = list(/datum/reagent/consumable/nutriment = 5, /datum/reagent/consumable/capsaicin = 3)
trash = null
tastes = list("meat" = 1, "fire" = 1)
foodtype = MEAT
/obj/item/reagent_containers/food/snacks/meat/steak/gondola
name = "gondola steak"
+1
View File
@@ -12,6 +12,7 @@
default_priority = 90
icon_state = "eal"
chooseable_roundstart = TRUE
/datum/language/machine/get_random_name()
if(prob(70))
+1
View File
@@ -6,6 +6,7 @@
exclaim_verb = "poofs loudly"
whisper_verb = "puffs quietly"
key = "y"
flags = TONGUELESS_SPEECH
sentence_chance = 0
default_priority = 80
syllables = list("poof", "pff", "pFfF", "piff", "puff", "pooof", "pfffff", "piffpiff", "puffpuff", "poofpoof", "pifpafpofpuf")
+1
View File
@@ -6,6 +6,7 @@
ask_verb = "inquires"
exclaim_verb = "declares"
key = "h"
flags = TONGUELESS_SPEECH
space_chance = 20
syllables = list(
"fii", "sii", "rii", "rel", "maa", "ala", "san", "tol", "tok", "dia", "eres",
@@ -36,7 +36,7 @@
desc = "It's watching you suspiciously."
/obj/structure/closet/crate/necropolis/tendril/magic/PopulateContents()
var/loot = rand(1,9)
var/loot = rand(1,10)
switch(loot)
if(1)
new /obj/item/soulstone/anybody(src)
@@ -57,6 +57,8 @@
new /obj/item/immortality_talisman(src)
if(9)
new /obj/item/gun/magic/wand/book/healing(src)
if(10)
new /obj/item/guardiancreator(src)
/obj/structure/closet/crate/necropolis/tendril/weapon_armor/PopulateContents()
var/loot = rand(1,11)
@@ -128,7 +130,7 @@
new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src)
/obj/structure/closet/crate/necropolis/tendril/all/PopulateContents()
var/loot = rand(1,28)
var/loot = rand(1,29)
switch(loot)
if(1)
new /obj/item/shared_storage/red(src)
@@ -192,6 +194,8 @@
new /obj/item/immortality_talisman(src)
if(28)
new /obj/item/gun/magic/wand/book/healing(src)
if(29)
new /obj/item/guardiancreator(src)
//KA modkit design discs
/obj/item/disk/design_disk/modkit_disc
@@ -981,7 +985,18 @@
switch(random)
if(1)
to_chat(user, "<span class='danger'>Your appearance morphs to that of a very small humanoid ash dragon! You get to look like a freak without the cool abilities.</span>")
H.dna.features = list("mcolor" = "A02720", "tail_lizard" = "Dark Tiger", "tail_human" = "None", "snout" = "Sharp", "horns" = "Curled", "ears" = "None", "wings" = "None", "frills" = "None", "spines" = "Long", "body_markings" = "Dark Tiger Body", "legs" = "Digitigrade")
H.dna.features["mcolor"] = "A02720"
H.dna.features["tail_lizard"] = "Dark Tiger"
H.dna.features["tail_human"] = "None"
H.dna.features["mam_snout"] = "Sharp"
H.dna.features["horns"] = "Curled"
H.dna.features["ears"] = "None"
H.dna.features["wings"] = "None"
H.dna.features["frills"] = "None"
H.dna.features["spines"] = "Long"
H.dna.features["body_markings"] = "Dark Tiger Body"
H.dna.features["legs"] = "Digitigrade"
H.dna.features["taur_body"] = "None"
H.left_eye_color = "fee5a3"
H.right_eye_color = "fee5a3"
H.set_species(/datum/species/lizard)
@@ -1094,14 +1109,12 @@
/obj/structure/closet/crate/necropolis/bubblegum/PopulateContents()
new /obj/item/clothing/suit/space/hostile_environment(src)
new /obj/item/clothing/head/helmet/space/hostile_environment(src)
var/loot = rand(1,3)
var/loot = rand(1,2)
switch(loot)
if(1)
new /obj/item/mayhem(src)
if(2)
new /obj/item/book/granter/spell/asura(src)
if(3)
new /obj/item/guardiancreator(src)
/obj/structure/closet/crate/necropolis/bubblegum/crusher
name = "bloody bubblegum chest"
@@ -297,6 +297,11 @@
icon = 'icons/obj/surgery.dmi'
icon_state = "posibrain-ipc"
/obj/item/organ/brain/slime
name = "slime nucleus"
desc = "A slimey membranous mass from a slimeperson."
icon_state = "brain-s"
////////////////////////////////////TRAUMAS////////////////////////////////////////
@@ -1,7 +1,7 @@
/mob/living/carbon/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE)
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone)
SEND_SIGNAL(src, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone)
var/hit_percent = (100-blocked)/100
if(!forced && hit_percent <= 0)
return 0
@@ -47,6 +47,7 @@
owner.cut_overlays() //we dont show our normal sprite, we show a puddle sprite
var/obj/effect/puddle_effect = new puddle_into_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor
puddle_effect.transform = H.transform //copy mob size for consistent meltdown appearance
H.Stun(in_transformation_duration, ignore_canstun = TRUE) //cant move while transforming
//series of traits that make up the puddle behaviour
@@ -75,6 +76,7 @@
puddle_overlay.color = mutcolor
tracked_overlay = puddle_overlay
owner.add_overlay(puddle_overlay)
owner.update_antag_overlays()
transforming = FALSE
UpdateButtonIcon()
@@ -89,6 +91,7 @@
H.cut_overlay(tracked_overlay)
var/obj/effect/puddle_effect = new puddle_from_effect(get_turf(owner), owner.dir)
puddle_effect.color = tracked_overlay.color
puddle_effect.transform = H.transform //copy mob size for consistent transform size
H.Stun(out_transformation_duration, ignore_canstun = TRUE)
sleep(out_transformation_duration)
REMOVE_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
@@ -106,5 +109,6 @@
is_puddle = FALSE
if(squeak)
squeak.RemoveComponent()
H.regenerate_icons()
transforming = FALSE
UpdateButtonIcon()
@@ -1620,8 +1620,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
"<span class='userdanger'>[user] [atk_verb]ed you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
user, "<span class='danger'>You [atk_verb]ed [target]!</span>")
target.lastattacker = user.real_name
target.lastattackerckey = user.ckey
target.set_last_attacker(user)
user.dna.species.spec_unarmedattacked(user, target)
if(user.limb_destroyer)
@@ -2058,7 +2057,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
log_combat(user, target, "shoved", append_message)
/datum/species/proc/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = SHARP_NONE)
SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal
SEND_SIGNAL(H, COMSIG_MOB_APPLY_DAMAGE, damage, damagetype, def_zone, wound_bonus, bare_wound_bonus, sharpness) // make sure putting wound_bonus here doesn't screw up other signals or uses for this signal
var/hit_percent = (100-(blocked+armor))/100
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
if(!forced && hit_percent <= 0)
@@ -7,6 +7,9 @@
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH)
mutantlungs = /obj/item/organ/lungs/slime
mutant_heart = /obj/item/organ/heart/slime
mutantstomach = /obj/item/organ/stomach/slime
mutantliver = /obj/item/organ/liver/slime
mutant_brain = /obj/item/organ/brain/slime
mutant_bodyparts = list("mcolor" = "FFFFFF", "mam_tail" = "None", "mam_ears" = "None", "mam_snouts" = "None", "taur" = "None", "deco_wings" = "None", "legs" = "Plantigrade")
inherent_traits = list(TRAIT_TOXINLOVER)
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime
@@ -22,18 +25,12 @@
heatmod = 0.5 // = 1/4x heat damage
burnmod = 0.5 // = 1/2x generic burn damage
species_language_holder = /datum/language_holder/jelly
mutant_brain = /obj/item/organ/brain/jelly
tail_type = "mam_tail"
wagging_type = "mam_waggingtail"
species_category = SPECIES_CATEGORY_JELLY
ass_image = 'icons/ass/assslime.png'
/obj/item/organ/brain/jelly
name = "slime nucleus"
desc = "A slimey membranous mass from a slime person"
icon_state = "brain-slime"
/datum/species/jelly/on_species_loss(mob/living/carbon/C)
C.faction -= "slime"
if(ishuman(C))
@@ -62,9 +62,12 @@
if((C.dna.features["spines"] != "None" ) && (C.dna.features["tail_lizard"] == "None")) //tbh, it's kinda ugly for them not to have a tail yet have floating spines
C.dna.features["tail_lizard"] = "Smooth"
C.update_body()
if(C.dna.features["legs"] != "digitigrade")
C.dna.features["legs"] = "digitigrade"
if(C.dna.features["legs"] != "Digitigrade")
C.dna.features["legs"] = "Digitigrade"
for(var/obj/item/bodypart/leggie in C.bodyparts)
if(leggie.body_zone == BODY_ZONE_L_LEG || leggie.body_zone == BODY_ZONE_R_LEG)
leggie.update_limb(FALSE, C)
if(C.dna.features["mam_snouts"] != "Sharp")
C.dna.features["mam_snouts"] = "Sharp"
C.update_body()
return ..()
@@ -100,10 +100,29 @@ There are several things that need to be remembered:
update_mutations_overlay()
//damage overlays
update_damage_overlays()
//antagonism
update_antag_overlays()
/* --------------------------------------- */
//vvvvvv UPDATE_INV PROCS vvvvvv
/mob/living/carbon/human/update_antag_overlays()
remove_overlay(ANTAG_LAYER)
var/datum/antagonist/cult/D = src?.mind?.has_antag_datum(/datum/antagonist/cult) //check for cultism
if(D && D.cult_team?.cult_ascendent == TRUE)
var/istate = pick("halo1","halo2","halo3","halo4","halo5","halo6")
var/mutable_appearance/new_cult_overlay = mutable_appearance('icons/effects/32x64.dmi', istate, -ANTAG_LAYER)
overlays_standing[ANTAG_LAYER] = new_cult_overlay
var/datum/antagonist/clockcult/C = src?.mind?.has_antag_datum(/datum/antagonist/clockcult) //check for clockcultism - surely one can't be both cult and clockie, right?
if(C)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G && G.active && ishuman(src))
var/mutable_appearance/new_cult_overlay = mutable_appearance('icons/effects/genetics.dmi', "servitude", -ANTAG_LAYER)
overlays_standing[ANTAG_LAYER] = new_cult_overlay
apply_overlay(ANTAG_LAYER)
/mob/living/carbon/human/update_inv_w_uniform()
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(UNIFORM_LAYER)
+1 -1
View File
@@ -2,7 +2,7 @@
/**
* Applies damage to this mob
*
* Sends [COMSIG_MOB_APPLY_DAMGE]
* Sends [COMSIG_MOB_APPLY_DAMAGE]
*
* Arguuments:
* * damage - amount of damage
+2
View File
@@ -55,6 +55,8 @@
/mob/living/death(gibbed)
SEND_SIGNAL(src, COMSIG_LIVING_PREDEATH, gibbed)
stat = DEAD
unset_machine()
timeofdeath = world.time
+7 -1
View File
@@ -274,7 +274,7 @@
/mob/living/on_attack_hand(mob/user, act_intent = user.a_intent, attackchain_flags)
..() //Ignoring parent return value here.
SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user)
SEND_SIGNAL(src, COMSIG_MOB_ATTACK_HAND, user, act_intent)
if((user != src) && act_intent != INTENT_HELP && (mob_run_block(user, 0, user.name, ATTACK_TYPE_UNARMED | ATTACK_TYPE_MELEE | ((attackchain_flags & ATTACK_IS_PARRY_COUNTERATTACK)? ATTACK_TYPE_PARRY_COUNTERATTACK : NONE), null, user, check_zone(user.zone_selected), null) & BLOCK_SUCCESS))
log_combat(user, src, "attempted to touch")
visible_message("<span class='warning'>[user] attempted to touch [src]!</span>",
@@ -561,3 +561,9 @@
/mob/living/proc/getFireLoss_nonProsthetic()
return getFireLoss()
/mob/living/proc/set_last_attacker(mob/attacker)
lastattacker = attacker.real_name
lastattackerckey = attacker.ckey
SEND_SIGNAL(src, COMSIG_LIVING_ATTACKER_SET, attacker)
SEND_SIGNAL(attacker, COMSIG_LIVING_SET_AS_ATTACKER, src)
@@ -6,7 +6,7 @@
//Drone hands
/mob/living/simple_animal/drone/doUnEquip(obj/item/I, force, silent = FALSE)
/mob/living/simple_animal/drone/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE)
if(..())
update_inv_hands()
if(I == head)
@@ -24,6 +24,7 @@
melee_damage_upper = 25
attack_verb_continuous = "slashes"
attack_verb_simple = "slash"
gold_core_spawnable = HOSTILE_SPAWN
speak_emote = list("hisses")
bubble_icon = "alien"
a_intent = INTENT_HARM
@@ -36,7 +37,6 @@
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
unique_name = 1
gold_core_spawnable = NO_SPAWN
death_sound = 'sound/voice/hiss6.ogg'
deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..."
@@ -106,6 +106,7 @@
/obj/item/stack/sheet/animalhide/xeno = 1)
projectiletype = /obj/item/projectile/neurotox
projectilesound = 'sound/weapons/pierce.ogg'
gold_core_spawnable = NO_SPAWN
status_flags = 0
unique_name = 0
var/sterile = 1
@@ -1,5 +1,3 @@
#define MEDAL_PREFIX "Bubblegum"
/*
BUBBLEGUM
@@ -7,17 +5,18 @@ BUBBLEGUM
Bubblegum spawns randomly wherever a lavaland creature is able to spawn. It is the most powerful slaughter demon in existence.
Bubblegum's footsteps are heralded by shaking booms, proving its tremendous size.
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage.
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power
It often charges, dealing massive damage to anything unfortunate enough to be standing where it's aiming.
Whenever it isn't chasing something down, it will sink into nearby blood pools (if possible) and springs out of the closest one to its target.
To make this possible, it sprays streams of blood at random.
From these blood pools Bubblegum may summon slaughterlings - weak, low-damage minions designed to impede the target's progress.
It leaves blood trails behind wherever it goes, its clones do as well.
It tries to strike at its target through any bloodpools under them; if it fails to do that.
If it does warp it will enter an enraged state, becoming immune to all projectiles, becoming much faster, and dealing damage and knockback to anything that gets in the cloud around it.
It may summon clones charging from all sides, one of these charges being bubblegum himself.
It can charge at its target, and also heavily damaging anything directly hit in the charge.
If at half health it will start to charge from all sides with clones.
When Bubblegum dies, it leaves behind a H.E.C.K. suit+helmet as well as a chest that can contain three things:
1. A spellblade that can slice off limbs at range
2. A bottle that, when activated, drives everyone nearby into a frenzy
3. A super double-barrel shotgun that shoots both shells at the same time.
When Bubblegum dies, it leaves behind a H.E.C.K. mining suit as well as a chest that can contain three things:
1. A bottle that, when activated, drives everyone nearby into a frenzy
2. A scroll that teaches the reader a martial art that sacrifices health for raw demonic power with their bare hands.
Difficulty: Hard
@@ -42,15 +41,20 @@ Difficulty: Hard
melee_damage_lower = 40
melee_damage_upper = 40
speed = 1
move_to_delay = 10
ranged_cooldown_time = 10
move_to_delay = 5
retreat_distance = 5
minimum_distance = 5
rapid_melee = 8 // every 1/4 second
melee_queue_distance = 20 // as far as possible really, need this because of blood warp
ranged = 1
pixel_x = -32
gender = MALE
del_on_death = 1
crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher)
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
blood_volume = BLOOD_VOLUME_MAXIMUM //BLEED FOR ME
var/charging = 0
var/enrage_till = null
achievement_type = /datum/award/achievement/boss/bubblegum_kill
crusher_achievement_type = /datum/award/achievement/boss/bubblegum_crusher
@@ -67,36 +71,60 @@ Difficulty: Hard
desc = "You're not quite sure how a signal can be bloody."
invisibility = 100
/mob/living/simple_animal/hostile/megafauna/bubblegum/BiologicalLife(seconds, times_fired)
if(!(. = ..()))
return
move_to_delay = clamp(round((health/maxHealth) * 10), 3, 10)
/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, only_robotic = FALSE, only_organic = TRUE)
. = ..()
if(. > 0 && prob(25))
var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc)
if(prob(40))
step(B, pick(GLOB.cardinals))
else
B.setDir(pick(GLOB.cardinals))
/obj/effect/decal/cleanable/blood/gibs/bubblegum
name = "thick blood"
desc = "Thick, splattered blood."
random_icon_states = list("gib3", "gib5", "gib6")
bloodiness = 20
/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire()
anger_modifier = clamp(((maxHealth - health)/50),0,20)
anger_modifier = clamp(((maxHealth - health)/60),0,20)
if(charging)
return
ranged_cooldown = world.time + ranged_cooldown_time
ranged_cooldown = world.time + 50
if(!try_bloodattack())
blood_warp()
blood_warp()
if(prob(25))
INVOKE_ASYNC(src, .proc/blood_spray)
else if(prob(5+anger_modifier/2))
slaughterlings()
else
if(health > maxHealth/2 && !client)
INVOKE_ASYNC(src, .proc/charge)
if(health > maxHealth * 0.5)
if(prob(50 + anger_modifier))
charge(delay = 6)
charge(delay = 4) // The FitnessGram Pacer Test is a multistage aerobic capacity test that progressively gets more difficult as it continues.
charge(delay = 2)
SetRecoveryTime(15)
else
INVOKE_ASYNC(src, .proc/triple_charge)
hallucination_charge_around(times = 6, delay = 10 - anger_modifier / 5)
SetRecoveryTime(10)
else
if(prob(50 - anger_modifier))
hallucination_charge_around(times = 4, delay = 9)
hallucination_charge_around(times = 4, delay = 8)
hallucination_charge_around(times = 4, delay = 7)
SetRecoveryTime(15)
else
for(var/i = 1 to 5)
INVOKE_ASYNC(src, .proc/hallucination_charge_around, 2, 10, 2, 0)
sleep(5)
SetRecoveryTime(10)
/mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize()
. = ..()
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_list)
if(B != src)
return INITIALIZE_HINT_QDEL //There can be only one
if(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination))
return
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_living_list) if(B != src)
return INITIALIZE_HINT_QDEL //There can be only one
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
AddSpell(bloodspell)
if(istype(loc, /obj/effect/dummy/phased_mob/slaughter))
@@ -109,52 +137,78 @@ Difficulty: Hard
SSshuttle.shuttle_purchase_requirements_met |= "bubblegum"
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(charging)
return
..()
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget()
if(charging)
if(!charging)
. = ..()
if(.)
recovery_time = world.time + 20 // can only attack melee once every 2 seconds but rapid_melee gives higher priority
/mob/living/simple_animal/hostile/megafauna/bubblegum/bullet_act(obj/item/projectile/P)
if(is_enraged())
visible_message("<span class='danger'>[src] deflects the projectile; [p_they()] can't be hit with ranged weapons while enraged!</span>", "<span class='userdanger'>You deflect the projectile!</span>")
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 300, 1)
return 0
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/ex_act(severity, target)
if(severity >= EXPLODE_LIGHT)
return
..()
severity = EXPLODE_LIGHT // puny mortals
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination))
return 1
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance)
if(charging)
return
..()
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/MoveToTarget(list/possible_targets)
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Move()
if(!stat)
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
if(charging)
new/obj/effect/temp_visual/decoy/fading(loc,src)
new /obj/effect/temp_visual/decoy/fading(loc,src)
DestroySurroundings()
. = ..()
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Moved()
new /obj/effect/decal/cleanable/blood(src.loc)
if(charging)
DestroySurroundings()
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge()
charge()
sleep(10)
charge()
sleep(10)
charge()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge()
var/turf/T = get_turf(target)
if(!T || T == loc)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(atom/chargeat = target, delay = 3, chargepast = 2)
if(!chargeat)
return
new /obj/effect/temp_visual/dragon_swoop(T)
var/chargeturf = get_turf(chargeat)
if(!chargeturf)
return
var/dir = get_dir(src, chargeturf)
var/turf/T = get_ranged_target_turf(chargeturf, dir, chargepast)
if(!T)
return
new /obj/effect/temp_visual/dragon_swoop/bubblegum(T)
charging = 1
DestroySurroundings()
walk(src, 0)
setDir(get_dir(src, T))
setDir(dir)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src)
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 5)
sleep(5)
throw_at(T, get_dist(src, T), 1, src, 0)
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3)
sleep(delay)
var/movespeed = 0.7
walk_towards(src, T, movespeed)
sleep(get_dist(src, T) * movespeed)
walk(src, 0) // cancel the movement
try_bloodattack()
charging = 0
Goto(target, move_to_delay, minimum_distance)
/**
* Attack by override for bubblegum
@@ -176,36 +230,147 @@ Difficulty: Hard
if(isturf(A) || isobj(A) && A.density)
A.ex_act(EXPLODE_HEAVY)
DestroySurroundings()
if(isliving(A))
var/mob/living/L = A
L.visible_message("<span class='danger'>[src] slams into [L]!</span>", "<span class='userdanger'>[src] tramples you into the ground!</span>")
src.forceMove(get_turf(L))
L.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE)
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1)
shake_camera(L, 4, 3)
shake_camera(src, 2, 3)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!charging)
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood()
var/list/targets = ListTargets()
. = list()
for(var/mob/living/L in targets)
var/list/bloodpool = get_pools(get_turf(L), 0)
if(bloodpool.len && (!faction_check_mob(L) || L.stat == DEAD))
. += L
else if(isliving(hit_atom))
var/mob/living/L = hit_atom
L.visible_message("<span class='danger'>[src] slams into [L]!</span>", "<span class='userdanger'>[src] slams into you!</span>")
L.apply_damage(40, BRUTE)
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1)
shake_camera(L, 4, 3)
shake_camera(src, 2, 3)
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
L.throw_at(throwtarget, 3)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack()
var/list/targets = get_mobs_on_blood()
if(targets.len)
INVOKE_ASYNC(src, .proc/bloodattack, targets, prob(50))
return TRUE
return FALSE
charging = 0
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodattack(list/targets, handedness)
var/mob/living/target_one = pick_n_take(targets)
var/turf/target_one_turf = get_turf(target_one)
var/mob/living/target_two
if(targets.len)
target_two = pick_n_take(targets)
var/turf/target_two_turf = get_turf(target_two)
if(target_two.stat != CONSCIOUS || prob(10))
bloodgrab(target_two_turf, handedness)
else
bloodsmack(target_two_turf, handedness)
if(target_one)
var/list/pools = get_pools(get_turf(target_one), 0)
if(pools.len)
target_one_turf = get_turf(target_one)
if(target_one_turf)
if(target_one.stat != CONSCIOUS || prob(10))
bloodgrab(target_one_turf, !handedness)
else
bloodsmack(target_one_turf, !handedness)
if(!target_two && target_one)
var/list/poolstwo = get_pools(get_turf(target_one), 0)
if(poolstwo.len)
target_one_turf = get_turf(target_one)
if(target_one_turf)
if(target_one.stat != CONSCIOUS || prob(10))
bloodgrab(target_one_turf, handedness)
else
bloodsmack(target_one_turf, handedness)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodsmack(turf/T, handedness)
if(handedness)
new /obj/effect/temp_visual/bubblegum_hands/rightsmack(T)
else
new /obj/effect/temp_visual/bubblegum_hands/leftsmack(T)
sleep(4)
for(var/mob/living/L in T)
if(!faction_check_mob(L))
to_chat(L, "<span class='userdanger'>[src] rends you!</span>")
playsound(T, attack_sound, 100, 1, -1)
var/limb_to_hit = L.get_bodypart(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
L.apply_damage(10, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
sleep(3)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodgrab(turf/T, handedness)
if(handedness)
new /obj/effect/temp_visual/bubblegum_hands/rightpaw(T)
new /obj/effect/temp_visual/bubblegum_hands/rightthumb(T)
else
new /obj/effect/temp_visual/bubblegum_hands/leftpaw(T)
new /obj/effect/temp_visual/bubblegum_hands/leftthumb(T)
sleep(6)
for(var/mob/living/L in T)
if(!faction_check_mob(L))
if(L.stat != CONSCIOUS)
to_chat(L, "<span class='userdanger'>[src] drags you through the blood!</span>")
playsound(T, 'sound/magic/enter_blood.ogg', 100, 1, -1)
var/turf/targetturf = get_step(src, dir)
L.forceMove(targetturf)
playsound(targetturf, 'sound/magic/exit_blood.ogg', 100, 1, -1)
addtimer(CALLBACK(src, .proc/devour, L), 2)
sleep(1)
/obj/effect/temp_visual/dragon_swoop/bubblegum
duration = 10
/obj/effect/temp_visual/bubblegum_hands
icon = 'icons/effects/bubblegum.dmi'
duration = 9
/obj/effect/temp_visual/bubblegum_hands/rightthumb
icon_state = "rightthumbgrab"
/obj/effect/temp_visual/bubblegum_hands/leftthumb
icon_state = "leftthumbgrab"
/obj/effect/temp_visual/bubblegum_hands/rightpaw
icon_state = "rightpawgrab"
layer = BELOW_MOB_LAYER
/obj/effect/temp_visual/bubblegum_hands/leftpaw
icon_state = "leftpawgrab"
layer = BELOW_MOB_LAYER
/obj/effect/temp_visual/bubblegum_hands/rightsmack
icon_state = "rightsmack"
/obj/effect/temp_visual/bubblegum_hands/leftsmack
icon_state = "leftsmack"
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp()
if(Adjacent(target) || (enrage_till + 30 > world.time))
return FALSE
var/list/can_jaunt = get_pools(get_turf(src), 1)
if(!can_jaunt.len)
return FALSE
var/list/pools = get_pools(get_turf(target), 2)
var/list/pools_to_remove = get_pools(get_turf(target), 1)
pools -= pools_to_remove
if(!pools.len)
return FALSE
var/obj/effect/temp_visual/decoy/DA = new /obj/effect/temp_visual/decoy(loc,src)
DA.color = "#FF0000"
var/oldtransform = DA.transform
DA.transform = matrix()*2
animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 3)
sleep(3)
qdel(DA)
var/obj/effect/decal/cleanable/blood/found_bloodpool
var/list/pools = list()
var/can_jaunt = FALSE
for(var/obj/effect/decal/cleanable/blood/nearby in view(src,2))
can_jaunt = TRUE
break
if(!can_jaunt)
return
for(var/obj/effect/decal/cleanable/blood/nearby in view(get_turf(target),2))
pools += nearby
pools = get_pools(get_turf(target), 2)
pools_to_remove = get_pools(get_turf(target), 1)
pools -= pools_to_remove
if(pools.len)
shuffle_inplace(pools)
found_bloodpool = pick(pools)
@@ -215,48 +380,110 @@ Difficulty: Hard
forceMove(get_turf(found_bloodpool))
playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1)
visible_message("<span class='danger'>And springs back out!</span>")
blood_enrage()
return TRUE
return FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage(var/boost_time = 30)
enrage_till = world.time + boost_time
retreat_distance = null
minimum_distance = 1
change_move_delay(3.75)
var/newcolor = rgb(149, 10, 10)
add_atom_colour(newcolor, TEMPORARY_COLOUR_PRIORITY)
var/datum/callback/cb = CALLBACK(src, .proc/blood_enrage_end)
addtimer(cb, boost_time)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_spray()
visible_message("<span class='danger'>[src] sprays a stream of gore!</span>")
var/turf/E = get_edge_target_turf(src, src.dir)
var/range = 10
var/turf/previousturf = get_turf(src)
for(var/turf/J in getline(src,E))
if(!range)
break
new /obj/effect/temp_visual/dir_setting/bloodsplatter(previousturf, get_dir(previousturf, J))
if(!previousturf.CanAtmosPass(J))
break
playsound(J,'sound/effects/splat.ogg', 100, 1, -1)
new /obj/effect/decal/cleanable/blood(J)
range--
previousturf = J
sleep(1)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage_end(var/newcolor = rgb(149, 10, 10))
retreat_distance = initial(retreat_distance)
minimum_distance = initial(minimum_distance)
change_move_delay()
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, newcolor)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/slaughterlings()
visible_message("<span class='danger'>[src] summons a shoal of slaughterlings!</span>")
for(var/obj/effect/decal/cleanable/blood/H in range(src, 10))
if(prob(25))
new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter(H.loc)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/is_enraged()
return (enrage_till > world.time)
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter
name = "slaughterling"
desc = "Though not yet strong enough to create a true physical form, it's nonetheless determined to murder you."
icon_state = "bloodbrood"
icon_living = "bloodbrood"
icon_aggro = "bloodbrood"
attack_verb_continuous = "pierces"
attack_verb_simple = "pierce"
color = "#C80000"
density = FALSE
faction = list("mining", "boss")
weather_immunities = list("lava","ash")
has_field_of_vision = FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/change_move_delay(var/newmove = initial(move_to_delay))
move_to_delay = newmove
handle_automated_action() // need to recheck movement otherwise move_to_delay won't update until the next checking aka will be wrong speed for a bit
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/slaughter/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum))
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range)
. = list()
for(var/obj/effect/decal/cleanable/nearby in view(T, range))
if(nearby.can_bloodcrawl_in())
. += nearby
/obj/effect/decal/cleanable/blood/bubblegum
bloodiness = 0
/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in()
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/hallucination_charge_around(var/times = 4, var/delay = 6, var/chargepast = 0, var/useoriginal = 1)
var/startingangle = rand(1, 360)
if(!target)
return
var/turf/chargeat = get_turf(target)
var/srcplaced = 0
for(var/i = 1 to times)
var/ang = (startingangle + 360/times * i)
if(!chargeat)
return
var/turf/place = locate(chargeat.x + cos(ang) * times, chargeat.y + sin(ang) * times, chargeat.z)
if(!place)
continue
if(!srcplaced && useoriginal)
forceMove(place)
srcplaced = 1
continue
var/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/B = new /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination(src.loc)
B.forceMove(place)
INVOKE_ASYNC(B, .proc/charge, chargeat, delay, chargepast)
if(useoriginal)
charge(chargeat, delay, chargepast)
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination
name = "bubblegum's hallucination"
desc = "Is that really just a hallucination?"
health = 1
maxHealth = 1
alpha = 127.5
crusher_loot = null
loot = null
deathmessage = "Explodes into a pool of blood!"
deathsound = 'sound/effects/splat.ogg'
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize()
..()
toggle_ai(AI_OFF)
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/charge(atom/chargeat = target, delay = 3, chargepast = 2)
..()
qdel(src)
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Destroy()
new /obj/effect/decal/cleanable/blood(get_turf(src))
. = ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) // hallucinations should not be stopping bubblegum or eachother
return 1
return 0
return ..()
#undef MEDAL_PREFIX
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Life()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, only_robotic = FALSE, only_organic = TRUE)
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/OpenFire()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/AttackingTarget()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/grant_achievement(medaltype,scoretype)
return
@@ -60,7 +60,7 @@ Difficulty: Medium
pixel_x = -16
crusher_loot = list(/obj/structure/closet/crate/necropolis/dragon/crusher)
loot = list(/obj/structure/closet/crate/necropolis/dragon)
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30, /obj/item/reagent_containers/food/snacks/meat/slab/dragon = 5)
guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/ashdrake = 10)
var/swooping = NONE
var/swoop_cooldown = 0
+3
View File
@@ -69,3 +69,6 @@
/mob/proc/update_inv_ears()
return
/mob/proc/update_antag_overlays()
return
+1 -2
View File
@@ -267,8 +267,7 @@ They *could* go in their appropriate files, but this is supposed to be modular
apply_effect(EFFECT_STUTTER, G.stunforce)
SEND_SIGNAL(src, COMSIG_LIVING_MINOR_SHOCK)
lastattacker = H.real_name
lastattackerckey = H.ckey
set_last_attacker(H)
log_combat(H, src, "stunned")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
@@ -390,7 +390,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
speaking = "[DisplayTimeText(i, TRUE)] remain before causality stabilization."
else
speaking = "[i*0.1]..."
radio.talk_into(src, speaking, common_channel)
radio.talk_into(src, speaking, common_channel, list(SPAN_COMMAND)) // IT GOT WORSE, LOUD TIME
sleep(10)
explode()
@@ -715,7 +715,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
//Oh shit it's bad, time to freak out
if(damage > emergency_point)
radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity()]%", common_channel)
// it's bad, LETS YELL
radio.talk_into(src, "[emergency_alert] Integrity: [get_integrity()]%", common_channel, list(SPAN_YELL))
lastwarning = REALTIMEOFDAY
if(!has_reached_emergency)
investigate_log("has reached the emergency point for the first time.", INVESTIGATE_SUPERMATTER)
@@ -73,3 +73,11 @@
name = ".38 DumDum bullet casing"
desc = "A .38 DumDum bullet casing."
projectile_type = /obj/item/projectile/bullet/c38/dumdum
//.45-70 GOVT (Gunslinger's Derringer)
/obj/item/ammo_casing/g4570
name= ".45-70 Govt bullet casing"
desc = "An exceedingly rare .45-70 Govt bullet casing."
caliber = "45-70g"
projectile_type = /obj/item/projectile/bullet/g4570
@@ -0,0 +1,30 @@
/obj/item/ammo_box/magazine/internal/derringer
name = "derringer muzzle"
ammo_type = /obj/item/ammo_casing/c38
caliber = "38"
max_ammo = 2
multiload = FALSE
/obj/item/ammo_box/magazine/internal/derringer/ammo_count(countempties = 1)
if (!countempties)
var/boolets = 0
for(var/obj/item/ammo_casing/bullet in stored_ammo)
if(bullet.BB)
boolets++
return boolets
else
return ..()
/obj/item/ammo_box/magazine/internal/derringer/a357
name = "\improper derringer muzzle"
ammo_type = /obj/item/ammo_casing/a357
caliber = "357"
max_ammo = 2
multiload = FALSE
/obj/item/ammo_box/magazine/internal/derringer/g4570
name = "\improper derringer muzzle"
ammo_type = /obj/item/ammo_casing/g4570
caliber = "45-70g"
max_ammo = 2
multiload = FALSE
@@ -0,0 +1,74 @@
/obj/item/gun/ballistic/derringer
name = "\improper .38 Derringer"
desc = "A easily concealable derringer. Uses .38 ammo"
icon = 'icons/obj/guns/projectile.dmi'
icon_state = "derringer"
mag_type = /obj/item/ammo_box/magazine/internal/derringer
obj_flags = UNIQUE_RENAME
fire_sound = 'sound/weapons/revolvershot.ogg'
casing_ejector = FALSE
w_class = WEIGHT_CLASS_TINY
/obj/item/gun/ballistic/derringer/Initialize()
..()
transform *= 0.8 //Spriter too lazy to make icons smaller than default revolvers, local coder hacks in solution.
/obj/item/gun/ballistic/derringer/get_ammo(countchambered = FALSE, countempties = TRUE)
var/boolets = 0 //legacy var name maturity
if (chambered && countchambered)
boolets++
if (magazine)
boolets += magazine.ammo_count(countempties)
return boolets
/obj/item/gun/ballistic/derringer/attackby(obj/item/A, mob/user, params)
. = ..()
if(.)
return
var/num_loaded = magazine.attackby(A, user, params, 1)
if(num_loaded)
to_chat(user, "<span class='notice'>You load [num_loaded] shell\s into \the [src].</span>")
playsound(user, 'sound/weapons/bulletinsert.ogg', 60, 1)
A.update_icon()
update_icon()
chamber_round(0)
/obj/item/gun/ballistic/derringer/attack_self(mob/living/user)
var/num_unloaded = 0
while (get_ammo() > 0)
var/obj/item/ammo_casing/CB
CB = magazine.get_round(0)
chambered = null
CB.forceMove(drop_location())
CB.update_icon()
num_unloaded++
if (num_unloaded)
to_chat(user, "<span class='notice'>You break open \the [src] and unload [num_unloaded] bullets\s.</span>")
else
to_chat(user, "<span class='warning'>[src] is empty!</span>")
/obj/item/gun/ballistic/derringer/examine(mob/user)
. = ..()
var/live_ammo = get_ammo(FALSE, FALSE)
. += "[live_ammo ? live_ammo : "None"] of those are live rounds."
/obj/item/gun/ballistic/derringer/traitor
name = "\improper .357 Syndicate Derringer"
desc = "An easily concealable derriger, if not for the bright red and black. Uses .357 ammo"
icon_state = "derringer_syndie"
mag_type = /obj/item/ammo_box/magazine/internal/derringer/a357
/obj/item/gun/ballistic/derringer/gold
name = "\improper Golden Derringer"
desc = "The golden sheen is somewhat counterintuitive as a stealth weapon, but it looks cool. Uses .357 ammo"
icon_state = "derringer_gold"
mag_type = /obj/item/ammo_box/magazine/internal/derringer/a357
fire_sound = 'sound/weapons/resonator_blast.ogg'
/obj/item/gun/ballistic/derringer/nukeop
name = "\improper Gunslinger's Derringer"
desc = "Sandalwood grip, wellkempt blue-grey steel barrels, and a crash like thunder itself. Uses the exceedingly rare 45-70 Govt. ammo"
icon_state = "derringer"
mag_type = /obj/item/ammo_box/magazine/internal/derringer/g4570
fire_sound = 'sound/weapons/gunshotshotgunshot.ogg'
@@ -138,3 +138,13 @@
embedding = list(embed_chance=90, fall_chance=2, jostle_chance=5, ignore_throwspeed_threshold=TRUE, pain_stam_pct=0.4, pain_mult=5, jostle_pain_mult=6, rip_time=10)
wound_falloff_tile = -1
embed_falloff_tile = -5
//.45-70 GOVT (Gunslinger's Derringer)
//0bserver here. For all that is holy, do me a flavor, and do NOT allow people easy access to this ammo. This is meant for extremely lucky traitors, and nuclear operatives.
/obj/item/projectile/bullet/g4570
name = ".45-70 Govt bullet"
damage = 60
armour_penetration = 40
wound_bonus = -80
@@ -139,9 +139,12 @@
/obj/machinery/computer/pandemic/proc/eject_beaker()
if(beaker)
var/obj/item/reagent_containers/B = beaker
beaker.forceMove(drop_location())
beaker = null
update_icon()
return B
return null
/obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
@@ -237,14 +240,17 @@
. = TRUE //no afterattack
if(stat & (NOPOWER|BROKEN))
return
var/obj/item/reagent_containers/B
if(beaker)
to_chat(user, "<span class='warning'>A container is already loaded into [src]!</span>")
return
B = eject_beaker() //now with 100% more swapping
if(!user.transferItemToLoc(I, src))
return
if(B)
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(B)
beaker = I
to_chat(user, "<span class='notice'>You insert [I] into [src].</span>")
if(B) to_chat(user, "<span class='notice'>You remove [B] and insert [I] into [src].</span>")
else to_chat(user, "<span class='notice'>You insert [I] into [src].</span>")
update_icon()
else
return ..()
@@ -330,7 +330,7 @@
name = "Hollow Water"
description = "An ubiquitous chemical substance that is composed of hydrogen and oxygen, but it looks kinda hollow."
color = "#88878777"
taste_description = "emptyiness"
taste_description = "emptiness"
/datum/reagent/water/holywater

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