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) /area/maintenance/port/fore)
"avV" = ( "avV" = (
/obj/structure/table/wood, /obj/structure/table/wood,
/obj/item/reagent_containers/food/snacks/burger/ghost, /obj/item/ectoplasm,
/turf/open/floor/wood, /turf/open/floor/wood,
/area/maintenance/port/fore) /area/maintenance/port/fore)
"avW" = ( "avW" = (
+3 -3
View File
@@ -16329,7 +16329,7 @@
/obj/docking_port/stationary{ /obj/docking_port/stationary{
dwidth = 1; dwidth = 1;
height = 4; height = 4;
id = "pod4_away"; id = "pod_4_away";
name = "recovery ship"; name = "recovery ship";
width = 3 width = 3
}, },
@@ -16339,7 +16339,7 @@
/obj/docking_port/stationary{ /obj/docking_port/stationary{
dwidth = 1; dwidth = 1;
height = 4; height = 4;
id = "pod3_away"; id = "pod_3_away";
name = "recovery ship"; name = "recovery ship";
width = 3 width = 3
}, },
@@ -16489,7 +16489,7 @@
dir = 4; dir = 4;
dwidth = 1; dwidth = 1;
height = 4; height = 4;
id = "pod2_away"; id = "pod_2_away";
name = "recovery ship"; name = "recovery ship";
width = 3 width = 3
}, },
+2 -3
View File
@@ -60,6 +60,5 @@ GLOBAL_LIST_INIT(podstyles, list(\
)) ))
//cit //cit
#define PACK_GOODY_NONE 0 #define PACK_GOODY_NONE 0 // can be bought by cargo and privates
#define PACK_GOODY_PUBLIC 1 //can be bought by both privates and cargo #define PACK_GOODY_PRIVATE 1 // can be bought only by privates
#define PACK_GOODY_PRIVATE 2 //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 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 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_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_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) #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 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 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! //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_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) #define COMSIG_LIVING_STATUS_KNOCKDOWN "living_knockdown" //from base of mob/living/Knockdown() (amount, update, ignore)
@@ -357,7 +362,6 @@
// /obj/item signals // /obj/item signals
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user) #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 COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob)
#define COMPONENT_NO_INTERACT 1 #define COMPONENT_NO_INTERACT 1
#define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob) #define COMSIG_ITEM_ATTACK_OBJ "item_attack_obj" //from base of obj/item/attack_obj(): (/obj, /mob)
@@ -455,6 +459,10 @@
// /datum/mutation signals // /datum/mutation signals
#define COMSIG_HUMAN_MUTATION_LOSS "human_mutation_loss" //from datum/mutation/human/on_losing(): (datum/mutation/human/lost_mutation) #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*******/ /*******Component Specific Signals*******/
//Janitor //Janitor
#define COMSIG_TURF_IS_WET "check_turf_wet" //(): Returns bitflags of wet values. #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///////// //Human Overlays Indexes/////////
//LOTS OF CIT CHANGES HERE. BE CAREFUL WHEN UPSTREAM ADDS MORE LAYERS //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 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 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 #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 HANDS_LAYER 3
#define BODY_FRONT_LAYER 2 #define BODY_FRONT_LAYER 2
#define FIRE_LAYER 1 //If you're on fire #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 //Human Overlay Index Shortcuts for alternate_worn_layer, layers
//Because I *KNOW* somebody will think layer+1 means "above" //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_SLIME "#00ff90"
#define BLOOD_COLOR_LIZARD "#db004D" #define BLOOD_COLOR_LIZARD "#db004D"
#define BLOOD_COLOR_UNIVERSAL "#db3300" #define BLOOD_COLOR_UNIVERSAL "#db3300"
#define BLOOD_COLOR_BUG "#a37c0f" #define BLOOD_COLOR_BUG "#ffc933"
#define BLOOD_COLOR_PLANT "#3d610e" #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 LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
#define LAZYADD(L, I) if(!L) { L = list(); } L += I; #define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYOR(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 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 LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
#define LAZYLEN(L) length(L) #define LAZYLEN(L) length(L)
+17
View File
@@ -11,6 +11,23 @@ GLOBAL_LIST_INIT(bitfields, list(
"TILE_BOUND" = TILE_BOUND, "TILE_BOUND" = TILE_BOUND,
"PIXEL_SCALE" = PIXEL_SCALE "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( "sight" = list(
"SEE_INFRA" = SEE_INFRA, "SEE_INFRA" = SEE_INFRA,
"SEE_SELF" = SEE_SELF, "SEE_SELF" = SEE_SELF,
+47 -9
View File
@@ -85,21 +85,16 @@
makeItemInactive() makeItemInactive()
/obj/screen/storage/volumetric_box/proc/makeItemInactive() /obj/screen/storage/volumetric_box/proc/makeItemInactive()
if(!our_item) return
return
our_item.layer = VOLUMETRIC_STORAGE_ITEM_LAYER
our_item.plane = VOLUMETRIC_STORAGE_ITEM_PLANE
/obj/screen/storage/volumetric_box/proc/makeItemActive() /obj/screen/storage/volumetric_box/proc/makeItemActive()
if(!our_item) return
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
/obj/screen/storage/volumetric_box/center /obj/screen/storage/volumetric_box/center
icon_state = "stored_continue" icon_state = "stored_continue"
var/obj/screen/storage/volumetric_edge/stored_left/left var/obj/screen/storage/volumetric_edge/stored_left/left
var/obj/screen/storage/volumetric_edge/stored_right/right var/obj/screen/storage/volumetric_edge/stored_right/right
var/obj/screen/storage/item_holder/holder
var/pixel_size var/pixel_size
/obj/screen/storage/volumetric_box/center/Initialize(mapload, new_master, our_item) /obj/screen/storage/volumetric_box/center/Initialize(mapload, new_master, our_item)
@@ -110,6 +105,9 @@
/obj/screen/storage/volumetric_box/center/Destroy() /obj/screen/storage/volumetric_box/center/Destroy()
QDEL_NULL(left) QDEL_NULL(left)
QDEL_NULL(right) QDEL_NULL(right)
vis_contents.Cut()
if(holder)
QDEL_NULL(holder)
return ..() return ..()
/obj/screen/storage/volumetric_box/center/proc/on_screen_objects() /obj/screen/storage/volumetric_box/center/proc/on_screen_objects()
@@ -123,13 +121,36 @@
return return
pixel_size = pixels pixel_size = pixels
cut_overlays() cut_overlays()
vis_contents.Cut()
//our icon size is 32 pixels. //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 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 right.pixel_x = ((pixels - VOLUMETRIC_STORAGE_BOX_ICON_SIZE) * 0.5) + VOLUMETRIC_STORAGE_BOX_BORDER_SIZE
add_overlay(left) add_overlay(left)
add_overlay(right) 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 /obj/screen/storage/volumetric_edge
layer = VOLUMETRIC_STORAGE_BOX_LAYER layer = VOLUMETRIC_STORAGE_BOX_LAYER
plane = VOLUMETRIC_STORAGE_BOX_PLANE plane = VOLUMETRIC_STORAGE_BOX_PLANE
@@ -157,3 +178,20 @@
/obj/screen/storage/volumetric_edge/stored_right /obj/screen/storage/volumetric_edge/stored_right
icon_state = "stored_end" icon_state = "stored_end"
appearance_flags = APPEARANCE_UI | KEEP_APART | RESET_TRANSFORM 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) else if(hitsound)
playsound(loc, hitsound, get_clamped_volume(), 1, -1) playsound(loc, hitsound, get_clamped_volume(), 1, -1)
M.lastattacker = user.real_name M.set_last_attacker(user)
M.lastattackerckey = user.ckey
if(force && M == user && user.client) if(force && M == user && user.client)
user.client.give_award(/datum/award/achievement/misc/selfouch, user) 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() detonate()
/datum/component/explodable/proc/on_equip(datum/source, mob/equipper, slot) /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) /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 /// Checks if we're hitting the zone this component is covering
/datum/component/explodable/proc/is_hitting_zone(def_zone) /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. 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" /// Ui objects by person. mob = list(objects)
var/obj/screen/storage/boxes/ui_boxes var/list/ui_by_mob = list()
/// 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
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size. var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
@@ -125,18 +115,16 @@
/datum/component/storage/Destroy() /datum/component/storage/Destroy()
close_all() close_all()
QDEL_NULL(ui_boxes) wipe_ui_objects()
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()
LAZYCLEARLIST(is_using) LAZYCLEARLIST(is_using)
return ..() 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() /datum/component/storage/PreTransfer()
update_actions() update_actions()
@@ -351,13 +339,6 @@
return master._removal_reset(thing) return master._removal_reset(thing)
/datum/component/storage/proc/_remove_and_refresh(datum/source, atom/movable/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! _removal_reset(thing) // THIS NEEDS TO HAPPEN AFTER SO LAYERING DOESN'T BREAK!
refresh_mob_views() refresh_mob_views()
@@ -467,14 +448,14 @@
return return
A.add_fingerprint(M) 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 var/atom/A = parent
if(!istype(M)) if(!istype(M))
return FALSE return FALSE
A.add_fingerprint(M) A.add_fingerprint(M)
if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE))) if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE)))
return FALSE return FALSE
ui_show(M, !ghost) ui_show(M)
/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M) /datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
if(isitem(O)) if(isitem(O))
@@ -596,7 +577,7 @@
return can_be_inserted(I, silent, M) return can_be_inserted(I, silent, M)
/datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/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) /datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE)
return user_show_to_mob(showto, force) return user_show_to_mob(showto, force)
+32 -53
View File
@@ -7,7 +7,7 @@
if(QDELETED(I)) if(QDELETED(I))
continue continue
if(!.[I.type]) if(!.[I.type])
.[I.type] = new /datum/numbered_display(I, 1) .[I.type] = new /datum/numbered_display(I, 1, src)
else else
var/datum/numbered_display/ND = .[I.type] var/datum/numbered_display/ND = .[I.type]
ND.number++ ND.number++
@@ -20,6 +20,8 @@
. = list() . = list()
var/list/accessible_contents = accessible_items() var/list/accessible_contents = accessible_items()
var/adjusted_contents = length(accessible_contents) var/adjusted_contents = length(accessible_contents)
var/obj/screen/storage/close/ui_close
var/obj/screen/storage/boxes/ui_boxes
//Numbered contents display //Numbered contents display
var/list/datum/numbered_display/numbered_contents var/list/datum/numbered_display/numbered_contents
@@ -60,12 +62,13 @@
for(var/obj/O in accessible_items()) for(var/obj/O in accessible_items())
if(QDELETED(O)) if(QDELETED(O))
continue 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" var/obj/screen/storage/item_holder/D = new(null, src, O)
O.screen_loc = "[cx]:[screen_pixel_x],[cy]:[screen_pixel_y]" 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.maptext = ""
O.layer = ABOVE_HUD_LAYER O.layer = ABOVE_HUD_LAYER
O.plane = ABOVE_HUD_PLANE O.plane = ABOVE_HUD_PLANE
. += O . += D
cx++ cx++
if(cx - screen_start_x >= columns) if(cx - screen_start_x >= columns)
cx = screen_start_x cx = screen_start_x
@@ -78,6 +81,9 @@
*/ */
/datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns) /datum/component/storage/proc/orient2hud_volumetric(mob/user, maxcolumns)
. = list() . = 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. // Generate ui_item_blocks for missing ones and render+orient.
var/list/atom/contents = accessible_items() var/list/atom/contents = accessible_items()
@@ -128,14 +134,10 @@
var/first = TRUE var/first = TRUE
var/row = 1 var/row = 1
LAZYINITLIST(ui_item_blocks)
for(var/i in percentage_by_item) for(var/i in percentage_by_item)
I = i I = i
var/percent = percentage_by_item[I] var/percent = percentage_by_item[I]
if(!ui_item_blocks[I]) var/obj/screen/storage/volumetric_box/center/B = new /obj/screen/storage/volumetric_box/center(null, src, 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/pixels_to_use = overrun? MINIMUM_PIXELS_PER_ITEM : max(using_horizontal_pixels * percent, MINIMUM_PIXELS_PER_ITEM) var/pixels_to_use = overrun? MINIMUM_PIXELS_PER_ITEM : max(using_horizontal_pixels * percent, MINIMUM_PIXELS_PER_ITEM)
var/addrow = FALSE var/addrow = FALSE
if(CEILING(pixels_to_use, 1) >= FLOOR(horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING, 1)) if(CEILING(pixels_to_use, 1) >= FLOOR(horizontal_pixels - current_pixel - VOLUMETRIC_STORAGE_EDGE_PADDING, 1))
@@ -143,25 +145,17 @@
addrow = TRUE addrow = TRUE
// now that we have pixels_to_use, place our thing and add it to the returned list. // 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 // add the used pixels to pixel after we place the object
current_pixel += pixels_to_use + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING) current_pixel += pixels_to_use + (first? 0 : VOLUMETRIC_STORAGE_ITEM_PADDING)
first = FALSE //apply padding to everything after this first = FALSE //apply padding to everything after this
// set various things // set various things
B.set_pixel_size(pixels_to_use) B.set_pixel_size(pixels_to_use)
B.layer = VOLUMETRIC_STORAGE_BOX_LAYER
B.plane = VOLUMETRIC_STORAGE_BOX_PLANE
B.name = I.name 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. // finally add our things.
. += B.on_screen_objects() . += B.on_screen_objects()
. += I
// go up a row if needed // go up a row if needed
if(addrow) if(addrow)
@@ -185,18 +179,19 @@
/** /**
* Shows our UI to a mob. * 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) if(!M.client)
return FALSE 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) var/list/cview = getviewsize(M.client.view)
// in tiles // in tiles
var/maxallowedscreensize = cview[1]-8 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 // we got screen size, register signal
RegisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT, .proc/on_logout, override = TRUE) 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 != src)
if(M.active_storage) if(M.active_storage)
M.active_storage.ui_hide(M) M.active_storage.ui_hide(M)
@@ -204,10 +199,14 @@
LAZYOR(is_using, M) LAZYOR(is_using, M)
if(!M.client?.prefs?.no_tetris_storage && volumetric_ui()) if(!M.client?.prefs?.no_tetris_storage && volumetric_ui())
//new volumetric ui bay-style //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 else
//old ui //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 return TRUE
/** /**
@@ -236,8 +235,10 @@
/datum/component/storage/proc/ui_hide(mob/M) /datum/component/storage/proc/ui_hide(mob/M)
if(!M.client) if(!M.client)
return TRUE return TRUE
UnregisterSignal(M, COMSIG_MOB_CLIENT_LOGOUT) UnregisterSignal(M, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_CLIENT_LOGOUT))
M.client.screen -= list(ui_boxes, ui_close, ui_left, ui_continuous) + get_ui_item_objects_hide(M) M.client.screen -= ui_by_mob[M]
var/list/objects = ui_by_mob[M]
QDEL_LIST(objects)
if(M.active_storage == src) if(M.active_storage == src)
M.active_storage = null M.active_storage = null
LAZYREMOVE(is_using, M) LAZYREMOVE(is_using, M)
@@ -250,48 +251,26 @@
var/atom/real_location = real_location() var/atom/real_location = real_location()
return (storage_flags & STORAGE_LIMIT_VOLUME) && (length(real_location.contents) <= MAXIMUM_VOLUMETRIC_ITEMS) && !display_numerical_stacking 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. * Gets our ui_boxes, making it if it doesn't exist.
*/ */
/datum/component/storage/proc/get_ui_boxes() /datum/component/storage/proc/get_ui_boxes()
if(!ui_boxes) return new /obj/screen/storage/boxes(null, src)
ui_boxes = new(null, src)
return ui_boxes
/** /**
* Gets our ui_left, making it if it doesn't exist. * Gets our ui_left, making it if it doesn't exist.
*/ */
/datum/component/storage/proc/get_ui_left() /datum/component/storage/proc/get_ui_left()
if(!ui_left) return new /obj/screen/storage/left(null, src)
ui_left = new(null, src)
return ui_left
/** /**
* Gets our ui_close, making it if it doesn't exist. * Gets our ui_close, making it if it doesn't exist.
*/ */
/datum/component/storage/proc/get_ui_close() /datum/component/storage/proc/get_ui_close()
if(!ui_close) return new /obj/screen/storage/close(null, src)
ui_close = new(null, src)
return ui_close
/** /**
* Gets our ui_continuous, making it if it doesn't exist. * Gets our ui_continuous, making it if it doesn't exist.
*/ */
/datum/component/storage/proc/get_ui_continuous() /datum/component/storage/proc/get_ui_continuous()
if(!ui_continuous) return new /obj/screen/storage/continuous(null, src)
ui_continuous = new(null, src)
return ui_continuous
@@ -384,7 +384,7 @@
var/temp_rate = 1 var/temp_rate = 1
threshold_desc = list( threshold_desc = list(
"Transmission 6" = "Additionally increases temperature adjustment rate and heals those who love toxins", "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) /datum/symptom/heal/plasma/Start(datum/disease/advance/A)
if(!..()) if(!..())
+1 -1
View File
@@ -141,7 +141,7 @@
/obj/item/clothing/head/mob_holder/dropped(mob/user) /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() release()
/obj/item/clothing/head/mob_holder/proc/release() /obj/item/clothing/head/mob_holder/proc/release()
+2 -2
View File
@@ -34,13 +34,13 @@
. = ..() . = ..()
UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND) 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 var/mob/living/L = source
if(L.stat == DEAD) if(L.stat == DEAD)
return return
//we want to delay the effect to be displayed after the mob is petted, not before. //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) if(INTENT_HARM)
addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1) addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1)
if(INTENT_HELP) if(INTENT_HELP)
+7 -8
View File
@@ -129,7 +129,7 @@
/datum/mutation/human/proc/get_spans() /datum/mutation/human/proc/get_spans()
return list() return list()
/mob/living/carbon/proc/update_mutations_overlay() /mob/living/proc/update_mutations_overlay()
return return
/mob/living/carbon/human/update_mutations_overlay() /mob/living/carbon/human/update_mutations_overlay()
@@ -142,13 +142,12 @@
if(overlays_standing[CM.layer_used]) if(overlays_standing[CM.layer_used])
mut_overlay = overlays_standing[CM.layer_used] mut_overlay = overlays_standing[CM.layer_used]
var/mutable_appearance/V = CM.get_visual_indicator() 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) //trying to find its existence defeats the point because if cut_overlays is called it doesn't bother reloading it.
remove_overlay(CM.layer_used) for(var/mutable_appearance/MA in CM.visual_indicators[CM.type])
for(var/mutable_appearance/MA in CM.visual_indicators[CM.type]) mut_overlay.Remove(MA)
mut_overlay.Remove(MA) mut_overlay |= V
mut_overlay |= V overlays_standing[CM.layer_used] = mut_overlay
overlays_standing[CM.layer_used] = mut_overlay apply_overlay(CM.layer_used)
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 /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) if(modified || !power || !owner)
+2 -2
View File
@@ -283,7 +283,7 @@
desc = "Allows a creature to voluntary discard a random appendage." desc = "Allows a creature to voluntary discard a random appendage."
quality = POSITIVE quality = POSITIVE
text_gain_indication = "<span class='notice'>Your joints feel loose.</span>" text_gain_indication = "<span class='notice'>Your joints feel loose.</span>"
instability = 30 instability = 20
power = /obj/effect/proc_holder/spell/self/self_amputation power = /obj/effect/proc_holder/spell/self/self_amputation
energy_coeff = 1 energy_coeff = 1
@@ -316,7 +316,7 @@
return return
var/obj/item/bodypart/BP = pick(parts) var/obj/item/bodypart/BP = pick(parts)
BP.dismember() BP.dismember(harmless=TRUE)
//spider webs //spider webs
/datum/mutation/human/webbing /datum/mutation/human/webbing
+1 -1
View File
@@ -1,6 +1,6 @@
/datum/mutation/human/radioactive /datum/mutation/human/radioactive
name = "Radioactivity" 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 quality = NEGATIVE
text_gain_indication = "<span class='warning'>You can feel it in your bones!</span>" text_gain_indication = "<span class='warning'>You can feel it in your bones!</span>"
time_coeff = 5 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. //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 /datum/mutation/human/space_adaptation
name = "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 quality = POSITIVE
difficulty = 16 difficulty = 16
text_gain_indication = "<span class='notice'>Your body feels warm!</span>" 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/obj/item/sample_object
var/number 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)) if(!istype(sample))
qdel(src) qdel(src)
sample_object = sample sample_object = new /obj/screen/storage/item_holder(null, parent, sample)
number = _number number = _number
+1 -1
View File
@@ -160,7 +160,7 @@
/obj/screen/alert/status_effect/mesmerized /obj/screen/alert/status_effect/mesmerized
name = "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 = 'icons/mob/actions/bloodsucker.dmi'
icon_state = "power_mez" 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++ counter++
return counter >= 8 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 /datum/objective/escape
name = "escape" name = "escape"
explanation_text = "Escape on the shuttle or an escape pod alive and without being in custody." 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 //called on machinery construction (i.e from frame to machinery) but not on initialization
/obj/machinery/proc/on_construction() /obj/machinery/proc/on_construction()
for(var/obj/I in contents)
I.moveToNullspace()
return return
//called on deconstruction before the final deletion //called on deconstruction before the final deletion
+225 -304
View File
@@ -13,7 +13,7 @@
var/temp = "Inactive" var/temp = "Inactive"
var/scantemp_ckey var/scantemp_ckey
var/scantemp_name var/scantemp_name
var/scantemp = "Ready to Scan" var/scantemp = "Inactive"
var/menu = 1 //Which menu screen to display var/menu = 1 //Which menu screen to display
var/datum/data/record/active_record = null var/datum/data/record/active_record = null
var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything. var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything.
@@ -132,7 +132,6 @@
src.diskette = W src.diskette = W
to_chat(user, "<span class='notice'>You insert [W].</span>") to_chat(user, "<span class='notice'>You insert [W].</span>")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0) playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
src.updateUsrDialog()
else if(W.tool_behaviour == TOOL_MULTITOOL) else if(W.tool_behaviour == TOOL_MULTITOOL)
if(istype(W.buffer, clonepod_type)) if(istype(W.buffer, clonepod_type))
if(get_area(W.buffer) != get_area(src)) if(get_area(W.buffer) != get_area(src))
@@ -151,311 +150,233 @@
else else
return ..() 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 = "" /obj/machinery/computer/cloning/proc/Save(mob/user, target)
dat += "<a href='byond://?src=[REF(src)];refresh=1'>Refresh</a>" 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(use_records)
if(scanner && HasEfficientPod() && scanner.scan_level >= AUTOCLONING_MINIMAL_LEVEL) if(scanner && HasEfficientPod() && scanner.scan_level >= AUTOCLONING_MINIMAL_LEVEL)
if(!autoprocess) data["hasAutoprocess"] = TRUE
dat += "<a href='byond://?src=[REF(src)];task=autoprocess'>Autoclone</a>" if(length(records))
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>"
for(var/datum/data/record/R in 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>" var/list/record_entry = list()
if(3) record_entry["name"] = "[R.fields["name"]]"
dat += "<h3>Selected Record</h3>" record_entry["id"] = "[R.fields["id"]]"
dat += "<a href='byond://?src=[REF(src)];menu=2'><< Back</a><br>" var/obj/item/implant/health/H = locate(R.fields["imp"])
if(H && istype(H))
if (!src.active_record) record_entry["damages"] = H.sensehealth(TRUE)
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 />"
else 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>" return data
dat += "<b>Structural Enzymes:</b><br /><span class='highlight'>[src.active_record.fields["SE"]]</span><br>"
if(diskette && diskette.fields) /obj/machinery/computer/cloning/ui_act(action, params)
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)
if(..()) if(..())
return 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 return
updatemodules(TRUE)
if(href_list["task"]) ui = SStgui.try_update_ui(user, src, ui)
switch(href_list["task"]) if(!ui)
if("autoprocess") ui = new(user, src, "CloningConsole", "Cloning System Control")
if(scanner && HasEfficientPod() && scanner.scan_level >= AUTOCLONING_MINIMAL_LEVEL) ui.open()
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
/obj/machinery/computer/cloning/proc/finish_scan(mob/living/L, prev_locked) /obj/machinery/computer/cloning/proc/finish_scan(mob/living/L, prev_locked)
if(!scanner || !L) if(!scanner || !L)
@@ -469,7 +390,6 @@
loading = FALSE loading = FALSE
scanner.locked = prev_locked scanner.locked = prev_locked
src.updateUsrDialog()
playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0)
@@ -568,44 +488,44 @@
var/obj/machinery/clonepod/pod = GetAvailablePod() 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. //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
if(!LAZYLEN(pods)) 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) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(!pod) 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) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else if(pod.occupant) 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) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
else 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) 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) 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) /obj/machinery/computer/cloning/proc/can_scan(datum/dna/dna, mob/living/mob_occupant, experimental = FALSE, datum/bank_account/account)
if(!istype(dna)) 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) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return return
if(!experimental) if(!experimental)
if(mob_occupant.suiciding || mob_occupant.hellbound) 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) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return return
if((HAS_TRAIT(mob_occupant, TRAIT_NOCLONE)) && (src.scanner.scan_level < 2)) 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) playsound(src, 'sound/machines/terminal_alert.ogg', 50, 0)
return return
if (!experimental) if (!experimental)
if(!mob_occupant.ckey || !mob_occupant.client) 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) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return return
if (find_record("ckey", mob_occupant.ckey, records)) 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) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return return
if(SSeconomy.full_ancap && !account) 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) playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0)
return return
return TRUE return TRUE
@@ -618,3 +538,4 @@
icon_keyboard = "med_key" icon_keyboard = "med_key"
circuit = /obj/item/circuitboard/computer/cloning/prototype circuit = /obj/item/circuitboard/computer/cloning/prototype
clonepod_type = /obj/machinery/clonepod/experimental 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 ("answerMessage")
if (!authenticated(usr)) if (!authenticated(usr))
return return
var/answer_index = text2num(params["answer"]) var/answer_index = params["answer"]
var/message_index = text2num(params["message"]) 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) if (!answer_index || !message_index || answer_index < 1 || message_index < 1)
return return
var/datum/comm_message/message = messages[message_index] var/datum/comm_message/message = messages[message_index]
@@ -156,7 +162,11 @@
if ("deleteMessage") if ("deleteMessage")
if (!authenticated(usr)) if (!authenticated(usr))
return 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) if (!message_index)
return return
LAZYREMOVE(messages, LAZYACCESS(messages, message_index)) LAZYREMOVE(messages, LAZYACCESS(messages, message_index))
+20
View File
@@ -21,6 +21,7 @@ Buildable meters
level = 2 level = 2
var/piping_layer = PIPING_LAYER_DEFAULT var/piping_layer = PIPING_LAYER_DEFAULT
var/RPD_type var/RPD_type
var/disposable = TRUE
/obj/item/pipe/directional /obj/item/pipe/directional
RPD_type = PIPE_UNARY RPD_type = PIPE_UNARY
@@ -236,3 +237,22 @@ Buildable meters
/obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT) /obj/item/pipe_meter/proc/setAttachLayer(new_layer = PIPING_LAYER_DEFAULT)
piping_layer = new_layer piping_layer = new_layer
PIPING_LAYER_DOUBLE_SHIFT(src, piping_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) if (. & EMP_PROTECT_SELF)
return return
if(get_charge()) if(get_charge())
use_power(cell.charge*severity/100) use_power((cell.charge/3)*(severity*0.005))
take_damage(severity/3, BURN, "energy", 1) take_damage(severity/3, BURN, "energy", 1)
mecha_log_message("EMP detected", color="red") mecha_log_message("EMP detected", color="red")
+8 -6
View File
@@ -375,12 +375,14 @@ GLOBAL_LIST_INIT(fluid_duct_recipes, list(
. = TRUE . = 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)) 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>") var/obj/item/pipe/P = A
playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1) if(!istype(P) || P.disposable)
if(do_after(user, destroy_speed, target = A)) to_chat(user, "<span class='notice'>You start destroying a pipe...</span>")
activate() playsound(get_turf(src), 'sound/machines/click.ogg', 50, 1)
qdel(A) if(do_after(user, destroy_speed, target = A))
return activate()
qdel(A)
return
if((mode & PAINT_MODE)) if((mode & PAINT_MODE))
if(istype(A, /obj/machinery/atmospherics/pipe) && !istype(A, /obj/machinery/atmospherics/pipe/layer_manifold)) 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) target.apply_effect(EFFECT_STUTTER, stunforce)
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK) SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
if(user) if(user)
target.lastattacker = user.real_name target.set_last_attacker(user)
target.lastattackerckey = user.ckey
target.visible_message("<span class='danger'>[user] has shocked [target] with [src]!</span>", \ target.visible_message("<span class='danger'>[user] has shocked [target] with [src]!</span>", \
"<span class='userdanger'>[user] has shocked you with [src]!</span>") "<span class='userdanger'>[user] has shocked you with [src]!</span>")
log_combat(user, target, "stunned with an electrostaff") log_combat(user, target, "stunned with an electrostaff")
@@ -237,8 +236,7 @@
target.adjustFireLoss(lethal_force) //good against ointment spam target.adjustFireLoss(lethal_force) //good against ointment spam
SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK) SEND_SIGNAL(target, COMSIG_LIVING_MINOR_SHOCK)
if(user) if(user)
target.lastattacker = user.real_name target.set_last_attacker(user)
target.lastattackerckey = user.ckey
target.visible_message("<span class='danger'>[user] has seared [target] with [src]!</span>", \ target.visible_message("<span class='danger'>[user] has seared [target] with [src]!</span>", \
"<span class='userdanger'>[user] has seared you with [src]!</span>") "<span class='userdanger'>[user] has seared you with [src]!</span>")
log_combat(user, target, "burned with an electrostaff") log_combat(user, target, "burned with an electrostaff")
+1 -1
View File
@@ -312,7 +312,7 @@
trap_damage = 0 trap_damage = 0
item_flags = DROPDEL item_flags = DROPDEL
flags_1 = NONE flags_1 = NONE
breakouttime = 50 breakouttime = 25
/obj/item/restraints/legcuffs/beartrap/energy/New() /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" name = "health implant"
activated = 0 activated = 0
var/healthstring = "" 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) if (!imp_in)
return "ERROR" return "ERROR"
else else
if(isliving(imp_in)) if(isliving(imp_in))
var/mob/living/L = 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>" 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" 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_on // Damage when on - not stunning
var/force_off // Damage when off - not stunning var/force_off // Damage when off - not stunning
var/weight_class_on // What is the new size class when turned on var/weight_class_on // What is the new size class when turned on
var/sword_point = TRUE
wound_bonus = 15 wound_bonus = 15
/obj/item/melee/classic_baton/Initialize() /obj/item/melee/classic_baton/Initialize()
. = ..() . = ..()
if(sword_point)
AddElement(/datum/element/sword_point)
// Description for trying to stun when still on cooldown. // Description for trying to stun when still on cooldown.
/obj/item/melee/classic_baton/proc/get_wait_description() /obj/item/melee/classic_baton/proc/get_wait_description()
@@ -402,6 +405,8 @@
weight_class_on = WEIGHT_CLASS_BULKY weight_class_on = WEIGHT_CLASS_BULKY
total_mass = TOTAL_MASS_NORMAL_ITEM total_mass = TOTAL_MASS_NORMAL_ITEM
bare_wound_bonus = 5 bare_wound_bonus = 5
sword_point = FALSE
var/silent = FALSE
/obj/item/melee/classic_baton/telescopic/suicide_act(mob/user) /obj/item/melee/classic_baton/telescopic/suicide_act(mob/user)
var/mob/living/carbon/human/H = user var/mob/living/carbon/human/H = user
@@ -431,6 +436,9 @@
w_class = weight_class_on w_class = weight_class_on
force = force_on force = force_on
attack_verb = list("smacked", "struck", "cracked", "beaten") 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 else
to_chat(user, desc["local_off"]) to_chat(user, desc["local_off"])
icon_state = off_icon_state icon_state = off_icon_state
@@ -439,6 +447,9 @@
w_class = WEIGHT_CLASS_SMALL w_class = WEIGHT_CLASS_SMALL
force = force_off force = force_off
attack_verb = list("hit", "poked") 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) playsound(src.loc, on_sound, 50, 1)
add_fingerprint(user) add_fingerprint(user)
@@ -465,6 +476,7 @@
force_on = 16 force_on = 16
force_off = 5 force_off = 5
weight_class_on = WEIGHT_CLASS_NORMAL weight_class_on = WEIGHT_CLASS_NORMAL
silent = TRUE
/obj/item/melee/classic_baton/telescopic/contractor_baton/get_wait_description() /obj/item/melee/classic_baton/telescopic/contractor_baton/get_wait_description()
return "<span class='danger'>The baton is still charging!</span>" return "<span class='danger'>The baton is still charging!</span>"
@@ -272,7 +272,7 @@ GLOBAL_LIST_INIT(plastitaniumglass_recipes, list(
. = ..() . = ..()
. += GLOB.plastitaniumglass_recipes . += 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.max_integrity *= 2
S.efficiency *= 2 S.efficiency *= 2
+69
View File
@@ -351,6 +351,75 @@
if(!contents.len) if(!contents.len)
. += "[icon_state]_empty" . += "[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// //CIGAR BOX//
///////////// /////////////
+3 -3
View File
@@ -394,15 +394,15 @@
desc = "You want penis enlargement pills?" desc = "You want penis enlargement pills?"
/obj/item/storage/pill_bottle/penis_enlargement/PopulateContents() /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) new /obj/item/reagent_containers/pill/penis_enlargement(src)
/obj/item/storage/pill_bottle/breast_enlargement /obj/item/storage/pill_bottle/breast_enlargement
name = "breast enlargement pills" 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() /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) new /obj/item/reagent_containers/pill/breast_enlargement(src)
/obj/item/storage/pill_bottle/neurine /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/clothing/glasses/phantomthief/syndicate(src)
new /obj/item/reagent_containers/syringe/stimulants(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/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/clothing/glasses/sunglasses/garb(src) //Lets say 2 tc
new /obj/item/card/emag(src) //6 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/under/syndicate/baseball(src) //3tc
new /obj/item/clothing/head/soft/baseball(src) //Lets say 4 tc 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/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? 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) 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/storage/belt/chameleon(src) // Unique but worth at least 2 tc
new /obj/item/card/id/syndicate(src) // 2 tc new /obj/item/card/id/syndicate(src) // 2 tc
new /obj/item/chameleon(src) // 7 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. 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. 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 //So the subtype works
/obj/item/storage/toolbox/emergency/old/ancientbundle/PopulateContents() /obj/item/storage/toolbox/emergency/old/ancientbundle/PopulateContents()
new /obj/item/card/emag(src) new /obj/item/card/emag(src)
new /obj/item/pen/sleepy(src) new /obj/item/pen/sleepy(src)
new /obj/item/reagent_containers/pill/cyanide(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/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/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. 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)
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/instrument/harmonica,
/obj/item/mining_voucher, /obj/item/mining_voucher,
/obj/item/suit_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) /obj/item/storage/wallet/Exited(atom/movable/AM)
. = ..() . = ..()
+1 -2
View File
@@ -209,8 +209,7 @@
L.apply_effect(EFFECT_STUTTER, stamforce) L.apply_effect(EFFECT_STUTTER, stamforce)
SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK) SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
if(user) if(user)
L.lastattacker = user.real_name L.set_last_attacker(user)
L.lastattackerckey = user.ckey
L.visible_message("<span class='danger'>[user] has [disarming? "disarmed" : "stunned"] [L] with [src]!</span>", \ 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>") "<span class='userdanger'>[user] has [disarming? "disarmed" : "stunned"] you with [src]!</span>")
log_combat(user, L, disarming? "disarmed" : "stunned") 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_ready = 0
var/homerun_able = 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 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() /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... force = 18 //Spear damage...
throwforce = 30 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 /obj/item/melee/flyswatter
name = "flyswatter" name = "flyswatter"
desc = "Useful for killing pests of all sizes." desc = "Useful for killing pests of all sizes."
@@ -306,10 +306,12 @@
return return
if(user.transferItemToLoc(W, drop_location())) // so we put in unlit welder too if(user.transferItemToLoc(W, drop_location())) // so we put in unlit welder too
return 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() var/item_is_id = W.GetID()
if(!item_is_id) if(!item_is_id)
open(user) if(!open(user))
togglelock(user)
return
return return
if(item_is_id || !toggle(user)) if(item_is_id || !toggle(user))
togglelock(user) togglelock(user)
@@ -19,3 +19,50 @@
desc = "A sturdier card-locked storage unit used for bulky shipments." desc = "A sturdier card-locked storage unit used for bulky shipments."
max_integrity = 500 // Same as crates. max_integrity = 500 // Same as crates.
melee_min_damage = 25 // Idem. 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 /obj/effect/mob_spawn/human/pirate
name = "space pirate sleeper" 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" job_description = "Space Pirate"
random = TRUE random = TRUE
icon = 'icons/obj/machines/sleeper.dmi' icon = 'icons/obj/machines/sleeper.dmi'
@@ -608,6 +608,54 @@
assignedrole = "Space Pirate" assignedrole = "Space Pirate"
var/rank = "Mate" 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) /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.fully_replace_character_name(new_spawn.real_name,generate_pirate_name())
new_spawn.mind.add_antag_datum(/datum/antagonist/pirate) new_spawn.mind.add_antag_datum(/datum/antagonist/pirate)
@@ -199,8 +199,7 @@
return TRUE return TRUE
/obj/item/gun_control/attack(mob/living/M, mob/living/user) /obj/item/gun_control/attack(mob/living/M, mob/living/user)
M.lastattacker = user.real_name M.set_last_attacker(user)
M.lastattackerckey = user.ckey
M.attacked_by(src, user) M.attacked_by(src, user)
add_fingerprint(user) add_fingerprint(user)
@@ -67,6 +67,9 @@
to_chat(user, "<span class='notice'>You put [I] in [src].</span>") to_chat(user, "<span class='notice'>You put [I] in [src].</span>")
update_icon() update_icon()
/obj/structure/tank_dispenser/attack_robot(mob/user)
return _try_interact(user)
/obj/structure/tank_dispenser/ui_state(mob/user) /obj/structure/tank_dispenser/ui_state(mob/user)
return GLOB.physical_state return GLOB.physical_state
+1 -1
View File
@@ -428,7 +428,7 @@
output += "<option value='[j]'>[j]</option>" output += "<option value='[j]'>[j]</option>"
for(var/j in GLOB.nonhuman_positions) for(var/j in GLOB.nonhuman_positions)
output += "<option value='[j]'>[j]</option>" 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 += "<option value='[j]'>[j]</option>"
output += "</select></td></tr></table>" output += "</select></td></tr></table>"
output += "<b>Reason:<br></b><textarea name='dbbanreason' cols='50'></textarea><br>" 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 += "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 += "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>" 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() var/special_statuses = get_special_statuses()
if(length(special_statuses)) if(length(special_statuses))
+6
View File
@@ -870,6 +870,12 @@
else else
dat += "<td width='20%'><a href='?src=[REF(src)];[HrefToken()];jobban3=changeling;jobban4=[REF(M)]'>Changeling</a></td>" 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 //Nuke Operative
if(jobban_isbanned(M, ROLE_OPERATIVE) || isbanned_dept) 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>" 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)] var/datum/skill_modifier/job/M = GLOB.skill_modifiers[GET_SKILL_MOD_ID(A, type)]
if(istype(M)) if(istype(M))
M.name = "[name] Training" M.name = "[name] Training"
owner.AddComponent(/datum/component/activity)
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src) SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
/datum/antagonist/proc/is_banned(mob/M) /datum/antagonist/proc/is_banned(mob/M)
@@ -141,6 +142,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/datum/team/team = get_team() var/datum/team/team = get_team()
if(team) if(team)
team.remove_member(owner) team.remove_member(owner)
// we don't remove the activity component on purpose--no real point to it
qdel(src) qdel(src)
/datum/antagonist/proc/greet() /datum/antagonist/proc/greet()
@@ -511,8 +511,7 @@
/obj/item/abductor/baton/proc/StunAttack(mob/living/L,mob/living/user) /obj/item/abductor/baton/proc/StunAttack(mob/living/L,mob/living/user)
L.lastattacker = user.real_name L.set_last_attacker(user)
L.lastattackerckey = user.ckey
L.adjustStaminaLoss(35) //because previously it took 5-6 hits to actually "incapacitate" someone for the purposes of the sleep inducement L.adjustStaminaLoss(35) //because previously it took 5-6 hits to actually "incapacitate" someone for the purposes of the sleep inducement
L.DefaultCombatKnockdown(140) L.DefaultCombatKnockdown(140)
@@ -87,7 +87,7 @@
if(!M || !M.current) if(!M || !M.current)
continue continue
if(ishuman(M.current)) 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/turf/T = get_turf(src)
var/list/open_turfs = list() var/list/open_turfs = list()
for(var/turf/open/OT in orange(1, T)) for(var/turf/open/OT in orange(1, T))
@@ -139,7 +139,7 @@
current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump) current.throw_alert("clockinfo", /obj/screen/alert/clockwork/infodump)
var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar var/obj/structure/destructible/clockwork/massive/celestial_gateway/G = GLOB.ark_of_the_clockwork_justiciar
if(G && G.active && ishuman(current)) 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) /datum/antagonist/clockcult/remove_innate_effects(mob/living/mob_override)
var/mob/living/current = owner.current var/mob/living/current = owner.current
+1 -2
View File
@@ -385,8 +385,7 @@
qdel(src) qdel(src)
return return
log_combat(user, M, "used a cult spell on", source.name, "") log_combat(user, M, "used a cult spell on", source.name, "")
M.lastattacker = user.real_name M.set_last_attacker(user)
M.lastattackerckey = user.ckey
/obj/item/melee/blood_magic/afterattack(atom/target, mob/living/carbon/user, proximity) /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 var/mob/living/carbon/human/H = cultist
new /obj/effect/temp_visual/cult/sparks(get_turf(H), H.dir) new /obj/effect/temp_visual/cult/sparks(get_turf(H), H.dir)
var/istate = pick("halo1","halo2","halo3","halo4","halo5","halo6") 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() /datum/team/cult/proc/setup_objectives()
//SAC OBJECTIVE , todo: move this to objective internals //SAC OBJECTIVE , todo: move this to objective internals
+14 -3
View File
@@ -1,6 +1,6 @@
//Revenants: based off of wraiths from Goon //Revenants: based off of wraiths from Goon
//"Ghosts" that are invisible and move like ghosts, cannot take damage while invisible //"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 //Admin-spawn or random event
#define INVISIBILITY_REVENANT 50 #define INVISIBILITY_REVENANT 50
@@ -63,6 +63,7 @@
var/essence_regenerating = TRUE //If the revenant regenerates essence or not var/essence_regenerating = TRUE //If the revenant regenerates essence or not
var/essence_regen_amount = 5 //How much essence regenerates var/essence_regen_amount = 5 //How much essence regenerates
var/essence_accumulated = 0 //How much essence the revenant has stolen 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/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/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. 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) /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/night_vision/revenant(null))
AddSpell(new /obj/effect/proc_holder/spell/targeted/telepathy/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)) AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/revenant/defile(null))
@@ -138,6 +140,7 @@
. = ..() . = ..()
. += "Current essence: [essence]/[essence_regen_cap]E" . += "Current essence: [essence]/[essence_regen_cap]E"
. += "Stolen essence: [essence_accumulated]E" . += "Stolen essence: [essence_accumulated]E"
. += "Unused stolen essence: [essence_excess]E)"
. += "Stolen perfect souls: [perfectsouls]" . += "Stolen perfect souls: [perfectsouls]"
/mob/living/simple_animal/revenant/update_health_hud() /mob/living/simple_animal/revenant/update_health_hud()
@@ -304,16 +307,24 @@
return FALSE return FALSE
return TRUE 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) /mob/living/simple_animal/revenant/proc/change_essence_amount(essence_amt, silent = FALSE, source = null)
if(!src) if(!src)
return return
if(essence + essence_amt <= 0) if(essence + essence_amt < 0)
return return
essence = max(0, essence+essence_amt) essence = max(0, essence+essence_amt)
update_action_buttons_icon()
update_health_hud() update_health_hud()
if(essence_amt > 0) if(essence_amt > 0)
essence_accumulated = max(0, essence_accumulated+essence_amt) essence_accumulated = max(0, essence_accumulated+essence_amt)
essence_excess = max(0, essence_excess+essence_amt)
update_action_buttons_icon()
if(!silent) if(!silent)
if(essence_amt > 0) if(essence_amt > 0)
to_chat(src, "<span class='revennotice'>Gained [essence_amt]E[source ? " from [source]":""].</span>") 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>") to_chat(src, "<span class='revenminor'>You begin siphoning essence from [target]'s soul.</span>")
if(target.stat != DEAD) if(target.stat != DEAD)
to_chat(target, "<span class='warning'>You feel a horribly unpleasant draining sensation as your grip on life weakens...</span>") 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) reveal(46)
stun(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>") 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) if(user.inhibited)
return FALSE return FALSE
if(locked) if(locked)
if(user.essence <= unlock_amount) if(user.essence_excess <= unlock_amount)
return FALSE return FALSE
if(user.essence <= cast_amount) if(user.essence <= cast_amount)
return FALSE return FALSE
@@ -158,7 +160,7 @@
locked = FALSE locked = FALSE
return TRUE return TRUE
if(locked) if(locked)
if(!user.castcheck(-unlock_amount)) if(!user.unlock(unlock_amount))
charge_counter = charge_max charge_counter = charge_max
return FALSE return FALSE
name = "[initial(name)] ([cast_amount]E)" name = "[initial(name)] ([cast_amount]E)"
@@ -185,6 +187,7 @@
range = 5 range = 5
stun = 30 stun = 30
cast_amount = 40 cast_amount = 40
unlock_amount = 25
var/shock_range = 2 var/shock_range = 2
var/shock_damage = 15 var/shock_damage = 15
action_icon_state = "overload_lights" action_icon_state = "overload_lights"
@@ -197,7 +200,7 @@
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/proc/overload(turf/T, mob/user) /obj/effect/proc_holder/spell/aoe_turf/revenant/overload/proc/overload(turf/T, mob/user)
for(var/obj/machinery/light/L in T) for(var/obj/machinery/light/L in T)
if(!L.on) if(!L.on)
return continue
L.visible_message("<span class='warning'><b>\The [L] suddenly flares brightly and begins to spark!</span>") 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 var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(4, 0, L) s.set_up(4, 0, L)
@@ -226,7 +229,7 @@
range = 4 range = 4
stun = 20 stun = 20
reveal = 40 reveal = 40
unlock_amount = 75 unlock_amount = 10
cast_amount = 30 cast_amount = 30
action_icon_state = "defile" action_icon_state = "defile"
@@ -277,7 +280,7 @@
charge_max = 200 charge_max = 200
range = 4 range = 4
cast_amount = 60 cast_amount = 60
unlock_amount = 200 unlock_amount = 125
action_icon_state = "malfunction" 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. //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 charge_max = 200
range = 3 range = 3
cast_amount = 50 cast_amount = 50
unlock_amount = 200 unlock_amount = 75
action_icon_state = "blight" action_icon_state = "blight"
/obj/effect/proc_holder/spell/aoe_turf/revenant/blight/cast(list/targets, mob/living/simple_animal/revenant/user = usr) /obj/effect/proc_holder/spell/aoe_turf/revenant/blight/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
@@ -20,7 +20,7 @@
continue continue
if(!SSpersistence.IsValidDebrisLocation(C.loc, allowed_turf_typecache, allowed_z_cache, C.type, FALSE)) if(!SSpersistence.IsValidDebrisLocation(C.loc, allowed_turf_typecache, allowed_z_cache, C.type, FALSE))
continue continue
weight += 0.05 weight += 0.03
return ..() return ..()
/datum/round_event/ghost_role/slaughter /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" name = "Donk Co Operative"
employer = "Donk Corporation" employer = "Donk Corporation"
weight = 0 weight = 6
chaos = 1 chaos = 1
threat = 2 threat = 2
assassin_prob = 70
/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)
@@ -1,12 +1,12 @@
/datum/traitor_class/human/freeform /datum/traitor_class/human/freeform
name = "Waffle Co Agent" name = "Waffle Co Agent"
employer = "Waffle Company" 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 chaos = 0
/datum/traitor_class/human/freeform/forge_objectives(datum/antagonist/traitor/T) /datum/traitor_class/human/freeform/forge_objectives(datum/antagonist/traitor/T)
var/datum/objective/escape/O = new 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! Make sure to escape alive and not in custody, though!" 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 O.owner = T.owner
T.add_objective(O) T.add_objective(O)
return return
@@ -13,8 +13,12 @@
T.assign_exchange_role(SSticker.mode.exchange_blue) T.assign_exchange_role(SSticker.mode.exchange_blue)
objective_count += 1 //Exchange counts towards number of objectives objective_count += 1 //Exchange counts towards number of objectives
var/toa = CONFIG_GET(number/traitor_objectives_amount) var/toa = CONFIG_GET(number/traitor_objectives_amount)
var/attempts = 0
for(var/i = objective_count, i < toa, i++) 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)) if(!(locate(/datum/objective/escape) in T.objectives))
var/datum/objective/escape/escape_objective = new var/datum/objective/escape/escape_objective = new
escape_objective.owner = T.owner escape_objective.owner = T.owner
@@ -1,12 +1,11 @@
/datum/traitor_class/human/subterfuge /datum/traitor_class/human/subterfuge
name = "MI13 Operative" name = "MI13 Operative"
employer = "MI13" employer = "MI13"
weight = 36 weight = 25
chaos = -5 chaos = -5
var/assassin_prob = 25
/datum/traitor_class/human/subterfuge/forge_single_objective(datum/antagonist/traitor/T) /datum/traitor_class/human/subterfuge/forge_single_objective(datum/antagonist/traitor/T)
.=1
var/assassin_prob = 30
var/datum/game_mode/dynamic/mode var/datum/game_mode/dynamic/mode
if(istype(SSticker.mode,/datum/game_mode/dynamic)) if(istype(SSticker.mode,/datum/game_mode/dynamic))
mode = SSticker.mode mode = SSticker.mode
@@ -16,24 +15,31 @@
kill_objective.owner = T.owner kill_objective.owner = T.owner
kill_objective.find_target() kill_objective.find_target()
T.add_objective(kill_objective) T.add_objective(kill_objective)
return TRUE
else else
var/list/weights = list() var/list/weights = list()
weights["sabo"] = length(subtypesof(/datum/sabotage_objective)) var/datum/objective/sabotage/sabotage_objective = new
weights["steal"] = length(subtypesof(/datum/objective_item/steal)) 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"))) weights["download"] = !(locate(/datum/objective/download) in T.objectives || (T.owner.assigned_role in list("Research Director", "Scientist", "Roboticist")))
switch(pickweight(weights)) switch(pickweight(weights))
if("sabo") if("sabo")
var/datum/objective/sabotage/sabotage_objective = new
sabotage_objective.owner = T.owner
sabotage_objective.find_target()
T.add_objective(sabotage_objective) T.add_objective(sabotage_objective)
qdel(steal_objective)
return TRUE
if("steal") if("steal")
var/datum/objective/steal/steal_objective = new
steal_objective.owner = T.owner
steal_objective.find_target()
T.add_objective(steal_objective) T.add_objective(steal_objective)
qdel(sabotage_objective)
return TRUE
if("download") if("download")
var/datum/objective/download/download_objective = new var/datum/objective/download/download_objective = new
download_objective.owner = T.owner download_objective.owner = T.owner
download_objective.gen_amount_goal() download_objective.gen_amount_goal()
T.add_objective(download_objective) T.add_objective(download_objective)
return TRUE
return FALSE
@@ -7,7 +7,8 @@ GLOBAL_LIST_EMPTY(traitor_classes)
var/chaos = 0 var/chaos = 0
var/threat = 0 var/threat = 0
var/TC = 20 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/min_players = 0
var/list/uplink_filters var/list/uplink_filters
@@ -43,4 +44,6 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/datum/traitor_class/proc/clean_up_traitor(datum/antagonist/traitor/T) /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. // 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) if(traitor_kind)
traitor_kind.remove_innate_effects(owner.current) traitor_kind.remove_innate_effects(owner.current)
traitor_kind.clean_up_traitor(src) traitor_kind.clean_up_traitor(src)
if(traitor_kind.processing)
STOP_PROCESSING(SSprocessing, src)
swap_from_old = TRUE swap_from_old = TRUE
traitor_kind = GLOB.traitor_classes[kind] traitor_kind = GLOB.traitor_classes[kind]
traitor_kind.apply_innate_effects(owner.current) traitor_kind.apply_innate_effects(owner.current)
@@ -33,11 +35,16 @@
for(var/O in objectives) for(var/O in objectives)
qdel(O) qdel(O)
traitor_kind.forge_objectives(src) traitor_kind.forge_objectives(src)
if(traitor_kind.processing)
START_PROCESSING(SSprocessing, src)
if(swap_from_old) if(swap_from_old)
traitor_kind.finalize_traitor(src) traitor_kind.finalize_traitor(src)
traitor_kind.greet(src) traitor_kind.greet(src)
owner.announce_objectives() owner.announce_objectives()
/datum/antagonist/traitor/process()
traitor_kind.on_process(src)
/proc/get_random_traitor_kind(var/list/blacklist = list()) /proc/get_random_traitor_kind(var/list/blacklist = list())
var/chaos_weight = 0 var/chaos_weight = 0
if(istype(SSticker.mode,/datum/game_mode/dynamic)) 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 price_max = 300
stock_max = 3 stock_max = 3
availability_prob = 40 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/special_enabled = FALSE
var/DropPodOnly = FALSE //only usable by the Bluespace Drop Pod via the express cargo console 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? 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/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? 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) /datum/supply_pack/proc/generate(atom/A, datum/bank_account/paying_account)
var/obj/structure/closet/crate/C var/obj/structure/closet/crate/C
if(paying_account) 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]" C.name = "[crate_name] - Purchased by [paying_account.account_holder]"
else else
C = new crate_type(A) 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." 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 cost = 1500
contains = list(/obj/item/gun/energy/laser/hellgun) 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." 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 cost = 500
contains = list(/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/hypospray/medipen/ekit) 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 /datum/supply_pack/materials/cardboard50
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "50 Cardboard Sheets" name = "50 Cardboard Sheets"
desc = "Create a bunch of boxes." desc = "Create a bunch of boxes."
cost = 300 //thrice their export value cost = 300 //thrice their export value
contains = list(/obj/item/stack/sheet/cardboard/fifty) contains = list(/obj/item/stack/sheet/cardboard/fifty)
/datum/supply_pack/materials/glass50 /datum/supply_pack/materials/glass50
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "50 Glass Sheets" name = "50 Glass Sheets"
desc = "Let some nice light in with fifty glass sheets!" desc = "Let some nice light in with fifty glass sheets!"
cost = 300 //double their export value cost = 300 //double their export value
contains = list(/obj/item/stack/sheet/glass/fifty) contains = list(/obj/item/stack/sheet/glass/fifty)
/datum/supply_pack/materials/metal50 /datum/supply_pack/materials/metal50
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "50 Metal Sheets" name = "50 Metal Sheets"
desc = "Any construction project begins with a good stack of fifty metal sheets!" desc = "Any construction project begins with a good stack of fifty metal sheets!"
cost = 300 //double their export value cost = 300 //double their export value
contains = list(/obj/item/stack/sheet/metal/fifty) contains = list(/obj/item/stack/sheet/metal/fifty)
/datum/supply_pack/materials/plasteel20 /datum/supply_pack/materials/plasteel20
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "20 Plasteel Sheets" name = "20 Plasteel Sheets"
desc = "Reinforce the station's integrity with twenty plasteel sheets!" desc = "Reinforce the station's integrity with twenty plasteel sheets!"
cost = 4000 cost = 4000
contains = list(/obj/item/stack/sheet/plasteel/twenty) contains = list(/obj/item/stack/sheet/plasteel/twenty)
/datum/supply_pack/materials/plastic50 /datum/supply_pack/materials/plastic50
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "50 Plastic Sheets" name = "50 Plastic Sheets"
desc = "Build a limitless amount of toys with fifty plastic sheets!" desc = "Build a limitless amount of toys with fifty plastic sheets!"
cost = 200 // double their export cost = 200 // double their export
contains = list(/obj/item/stack/sheet/plastic/twenty) contains = list(/obj/item/stack/sheet/plastic/twenty)
/datum/supply_pack/materials/sandstone30 /datum/supply_pack/materials/sandstone30
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "30 Sandstone Blocks" name = "30 Sandstone Blocks"
desc = "Neither sandy nor stoney, these thirty blocks will still get the job done." desc = "Neither sandy nor stoney, these thirty blocks will still get the job done."
cost = 150 // five times their export cost = 150 // five times their export
contains = list(/obj/item/stack/sheet/mineral/sandstone/thirty) contains = list(/obj/item/stack/sheet/mineral/sandstone/thirty)
/datum/supply_pack/materials/wood20 /datum/supply_pack/materials/wood20
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "20 Wood Planks" name = "20 Wood Planks"
desc = "Turn cargo's boring metal groundwork into beautiful panelled flooring and much more with twenty wooden 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 cost = 400 // 6-7 planks shy from having equal import/export prices
contains = list(/obj/item/stack/sheet/mineral/wood/twenty) contains = list(/obj/item/stack/sheet/mineral/wood/twenty)
/datum/supply_pack/materials/rcdammo /datum/supply_pack/materials/rcdammo
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "Large RCD ammo Single-Pack" 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." desc = "A single large compressed RCD matter pack, to help with any holes or projects people might be working on."
cost = 600 cost = 600
+1 -1
View File
@@ -370,7 +370,7 @@
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
/datum/supply_pack/misc/carpet /datum/supply_pack/misc/carpet
goody = PACK_GOODY_PUBLIC crate_type = /obj/structure/closet/secure_closet/goodies
name = "Classic Carpet Single-Pack" 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." desc = "Plasteel floor tiles getting on your nerves? This 50 units stack of extra soft carpet will tie any room together."
cost = 200 cost = 200
+4 -2
View File
@@ -21,7 +21,7 @@
startWhen = 60 //2 minutes to answer startWhen = 60 //2 minutes to answer
var/datum/comm_message/threat_msg var/datum/comm_message/threat_msg
var/payoff = 0 var/payoff = 0
var/payoff_min = 20000 var/payoff_min = 1000
var/paid_off = FALSE var/paid_off = FALSE
var/pirate_type var/pirate_type
var/ship_template var/ship_template
@@ -66,7 +66,7 @@
SScommunications.send_message(threat_msg,unique = TRUE) SScommunications.send_message(threat_msg,unique = TRUE)
/datum/round_event/pirates/proc/answered() /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) var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
if(D) if(D)
if(D.adjust_money(-payoff)) if(D.adjust_money(-payoff))
@@ -75,6 +75,8 @@
return return
else else
priority_announce("Trying to cheat us? You'll regret this!",sender_override = ship_name) 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) if(!shuttle_spawned)
spawn_shuttle() spawn_shuttle()
else else
@@ -278,6 +278,19 @@
visible_message("[src] finishes cooking!") visible_message("[src] finishes cooking!")
new /obj/item/reagent_containers/food/snacks/meat/steak/goliath(loc) new /obj/item/reagent_containers/food/snacks/meat/steak/goliath(loc)
qdel(src) 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 /obj/item/reagent_containers/food/snacks/meat/slab/meatwheat
name = "meatwheat clump" name = "meatwheat clump"
@@ -395,6 +408,16 @@
trash = null trash = null
tastes = list("meat" = 1, "rock" = 1) tastes = list("meat" = 1, "rock" = 1)
foodtype = MEAT 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 /obj/item/reagent_containers/food/snacks/meat/steak/gondola
name = "gondola steak" name = "gondola steak"
+1
View File
@@ -12,6 +12,7 @@
default_priority = 90 default_priority = 90
icon_state = "eal" icon_state = "eal"
chooseable_roundstart = TRUE
/datum/language/machine/get_random_name() /datum/language/machine/get_random_name()
if(prob(70)) if(prob(70))
+1
View File
@@ -6,6 +6,7 @@
exclaim_verb = "poofs loudly" exclaim_verb = "poofs loudly"
whisper_verb = "puffs quietly" whisper_verb = "puffs quietly"
key = "y" key = "y"
flags = TONGUELESS_SPEECH
sentence_chance = 0 sentence_chance = 0
default_priority = 80 default_priority = 80
syllables = list("poof", "pff", "pFfF", "piff", "puff", "pooof", "pfffff", "piffpiff", "puffpuff", "poofpoof", "pifpafpofpuf") syllables = list("poof", "pff", "pFfF", "piff", "puff", "pooof", "pfffff", "piffpiff", "puffpuff", "poofpoof", "pifpafpofpuf")
+1
View File
@@ -6,6 +6,7 @@
ask_verb = "inquires" ask_verb = "inquires"
exclaim_verb = "declares" exclaim_verb = "declares"
key = "h" key = "h"
flags = TONGUELESS_SPEECH
space_chance = 20 space_chance = 20
syllables = list( syllables = list(
"fii", "sii", "rii", "rel", "maa", "ala", "san", "tol", "tok", "dia", "eres", "fii", "sii", "rii", "rel", "maa", "ala", "san", "tol", "tok", "dia", "eres",
@@ -36,7 +36,7 @@
desc = "It's watching you suspiciously." desc = "It's watching you suspiciously."
/obj/structure/closet/crate/necropolis/tendril/magic/PopulateContents() /obj/structure/closet/crate/necropolis/tendril/magic/PopulateContents()
var/loot = rand(1,9) var/loot = rand(1,10)
switch(loot) switch(loot)
if(1) if(1)
new /obj/item/soulstone/anybody(src) new /obj/item/soulstone/anybody(src)
@@ -57,6 +57,8 @@
new /obj/item/immortality_talisman(src) new /obj/item/immortality_talisman(src)
if(9) if(9)
new /obj/item/gun/magic/wand/book/healing(src) 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() /obj/structure/closet/crate/necropolis/tendril/weapon_armor/PopulateContents()
var/loot = rand(1,11) var/loot = rand(1,11)
@@ -128,7 +130,7 @@
new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src) new /obj/item/disk/design_disk/modkit_disc/rapid_repeater(src)
/obj/structure/closet/crate/necropolis/tendril/all/PopulateContents() /obj/structure/closet/crate/necropolis/tendril/all/PopulateContents()
var/loot = rand(1,28) var/loot = rand(1,29)
switch(loot) switch(loot)
if(1) if(1)
new /obj/item/shared_storage/red(src) new /obj/item/shared_storage/red(src)
@@ -192,6 +194,8 @@
new /obj/item/immortality_talisman(src) new /obj/item/immortality_talisman(src)
if(28) if(28)
new /obj/item/gun/magic/wand/book/healing(src) new /obj/item/gun/magic/wand/book/healing(src)
if(29)
new /obj/item/guardiancreator(src)
//KA modkit design discs //KA modkit design discs
/obj/item/disk/design_disk/modkit_disc /obj/item/disk/design_disk/modkit_disc
@@ -981,7 +985,18 @@
switch(random) switch(random)
if(1) 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>") 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.left_eye_color = "fee5a3"
H.right_eye_color = "fee5a3" H.right_eye_color = "fee5a3"
H.set_species(/datum/species/lizard) H.set_species(/datum/species/lizard)
@@ -1094,14 +1109,12 @@
/obj/structure/closet/crate/necropolis/bubblegum/PopulateContents() /obj/structure/closet/crate/necropolis/bubblegum/PopulateContents()
new /obj/item/clothing/suit/space/hostile_environment(src) new /obj/item/clothing/suit/space/hostile_environment(src)
new /obj/item/clothing/head/helmet/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) switch(loot)
if(1) if(1)
new /obj/item/mayhem(src) new /obj/item/mayhem(src)
if(2) if(2)
new /obj/item/book/granter/spell/asura(src) new /obj/item/book/granter/spell/asura(src)
if(3)
new /obj/item/guardiancreator(src)
/obj/structure/closet/crate/necropolis/bubblegum/crusher /obj/structure/closet/crate/necropolis/bubblegum/crusher
name = "bloody bubblegum chest" name = "bloody bubblegum chest"
@@ -297,6 +297,11 @@
icon = 'icons/obj/surgery.dmi' icon = 'icons/obj/surgery.dmi'
icon_state = "posibrain-ipc" 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//////////////////////////////////////// ////////////////////////////////////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) /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 var/hit_percent = (100-blocked)/100
if(!forced && hit_percent <= 0) if(!forced && hit_percent <= 0)
return 0 return 0
@@ -47,6 +47,7 @@
owner.cut_overlays() //we dont show our normal sprite, we show a puddle sprite 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) var/obj/effect/puddle_effect = new puddle_into_effect(get_turf(owner), owner.dir)
puddle_effect.color = mutcolor 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 H.Stun(in_transformation_duration, ignore_canstun = TRUE) //cant move while transforming
//series of traits that make up the puddle behaviour //series of traits that make up the puddle behaviour
@@ -75,6 +76,7 @@
puddle_overlay.color = mutcolor puddle_overlay.color = mutcolor
tracked_overlay = puddle_overlay tracked_overlay = puddle_overlay
owner.add_overlay(puddle_overlay) owner.add_overlay(puddle_overlay)
owner.update_antag_overlays()
transforming = FALSE transforming = FALSE
UpdateButtonIcon() UpdateButtonIcon()
@@ -89,6 +91,7 @@
H.cut_overlay(tracked_overlay) H.cut_overlay(tracked_overlay)
var/obj/effect/puddle_effect = new puddle_from_effect(get_turf(owner), owner.dir) var/obj/effect/puddle_effect = new puddle_from_effect(get_turf(owner), owner.dir)
puddle_effect.color = tracked_overlay.color 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) H.Stun(out_transformation_duration, ignore_canstun = TRUE)
sleep(out_transformation_duration) sleep(out_transformation_duration)
REMOVE_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT) REMOVE_TRAIT(H, TRAIT_PARALYSIS_L_ARM, SLIMEPUDDLE_TRAIT)
@@ -106,5 +109,6 @@
is_puddle = FALSE is_puddle = FALSE
if(squeak) if(squeak)
squeak.RemoveComponent() squeak.RemoveComponent()
H.regenerate_icons()
transforming = FALSE transforming = FALSE
UpdateButtonIcon() 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, \ "<span class='userdanger'>[user] [atk_verb]ed you!</span>", null, COMBAT_MESSAGE_RANGE, null, \
user, "<span class='danger'>You [atk_verb]ed [target]!</span>") user, "<span class='danger'>You [atk_verb]ed [target]!</span>")
target.lastattacker = user.real_name target.set_last_attacker(user)
target.lastattackerckey = user.ckey
user.dna.species.spec_unarmedattacked(user, target) user.dna.species.spec_unarmedattacked(user, target)
if(user.limb_destroyer) if(user.limb_destroyer)
@@ -2058,7 +2057,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
log_combat(user, target, "shoved", append_message) 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) /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 var/hit_percent = (100-(blocked+armor))/100
hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100 hit_percent = (hit_percent * (100-H.physiology.damage_resistance))/100
if(!forced && hit_percent <= 0) if(!forced && hit_percent <= 0)
@@ -7,6 +7,9 @@
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH) species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,WINGCOLOR,HAS_FLESH)
mutantlungs = /obj/item/organ/lungs/slime mutantlungs = /obj/item/organ/lungs/slime
mutant_heart = /obj/item/organ/heart/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") 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) inherent_traits = list(TRAIT_TOXINLOVER)
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime
@@ -22,18 +25,12 @@
heatmod = 0.5 // = 1/4x heat damage heatmod = 0.5 // = 1/4x heat damage
burnmod = 0.5 // = 1/2x generic burn damage burnmod = 0.5 // = 1/2x generic burn damage
species_language_holder = /datum/language_holder/jelly species_language_holder = /datum/language_holder/jelly
mutant_brain = /obj/item/organ/brain/jelly
tail_type = "mam_tail" tail_type = "mam_tail"
wagging_type = "mam_waggingtail" wagging_type = "mam_waggingtail"
species_category = SPECIES_CATEGORY_JELLY species_category = SPECIES_CATEGORY_JELLY
ass_image = 'icons/ass/assslime.png' 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) /datum/species/jelly/on_species_loss(mob/living/carbon/C)
C.faction -= "slime" C.faction -= "slime"
if(ishuman(C)) 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 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.dna.features["tail_lizard"] = "Smooth"
C.update_body() C.update_body()
if(C.dna.features["legs"] != "digitigrade") if(C.dna.features["legs"] != "Digitigrade")
C.dna.features["legs"] = "digitigrade" C.dna.features["legs"] = "Digitigrade"
for(var/obj/item/bodypart/leggie in C.bodyparts) for(var/obj/item/bodypart/leggie in C.bodyparts)
if(leggie.body_zone == BODY_ZONE_L_LEG || leggie.body_zone == BODY_ZONE_R_LEG) if(leggie.body_zone == BODY_ZONE_L_LEG || leggie.body_zone == BODY_ZONE_R_LEG)
leggie.update_limb(FALSE, C) leggie.update_limb(FALSE, C)
if(C.dna.features["mam_snouts"] != "Sharp")
C.dna.features["mam_snouts"] = "Sharp"
C.update_body()
return ..() return ..()
@@ -100,10 +100,29 @@ There are several things that need to be remembered:
update_mutations_overlay() update_mutations_overlay()
//damage overlays //damage overlays
update_damage_overlays() update_damage_overlays()
//antagonism
update_antag_overlays()
/* --------------------------------------- */ /* --------------------------------------- */
//vvvvvv UPDATE_INV PROCS vvvvvv //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() /mob/living/carbon/human/update_inv_w_uniform()
if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER)) if(!HAS_TRAIT(src, TRAIT_HUMAN_NO_RENDER))
remove_overlay(UNIFORM_LAYER) remove_overlay(UNIFORM_LAYER)
+1 -1
View File
@@ -2,7 +2,7 @@
/** /**
* Applies damage to this mob * Applies damage to this mob
* *
* Sends [COMSIG_MOB_APPLY_DAMGE] * Sends [COMSIG_MOB_APPLY_DAMAGE]
* *
* Arguuments: * Arguuments:
* * damage - amount of damage * * damage - amount of damage
+2
View File
@@ -55,6 +55,8 @@
/mob/living/death(gibbed) /mob/living/death(gibbed)
SEND_SIGNAL(src, COMSIG_LIVING_PREDEATH, gibbed)
stat = DEAD stat = DEAD
unset_machine() unset_machine()
timeofdeath = world.time 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) /mob/living/on_attack_hand(mob/user, act_intent = user.a_intent, attackchain_flags)
..() //Ignoring parent return value here. ..() //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)) 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") log_combat(user, src, "attempted to touch")
visible_message("<span class='warning'>[user] attempted to touch [src]!</span>", visible_message("<span class='warning'>[user] attempted to touch [src]!</span>",
@@ -561,3 +561,9 @@
/mob/living/proc/getFireLoss_nonProsthetic() /mob/living/proc/getFireLoss_nonProsthetic()
return getFireLoss() 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 //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(..()) if(..())
update_inv_hands() update_inv_hands()
if(I == head) if(I == head)
@@ -24,6 +24,7 @@
melee_damage_upper = 25 melee_damage_upper = 25
attack_verb_continuous = "slashes" attack_verb_continuous = "slashes"
attack_verb_simple = "slash" attack_verb_simple = "slash"
gold_core_spawnable = HOSTILE_SPAWN
speak_emote = list("hisses") speak_emote = list("hisses")
bubble_icon = "alien" bubble_icon = "alien"
a_intent = INTENT_HARM a_intent = INTENT_HARM
@@ -36,7 +37,6 @@
see_in_dark = 8 see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
unique_name = 1 unique_name = 1
gold_core_spawnable = NO_SPAWN
death_sound = 'sound/voice/hiss6.ogg' death_sound = 'sound/voice/hiss6.ogg'
deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..." deathmessage = "lets out a waning guttural screech, green blood bubbling from its maw..."
@@ -106,6 +106,7 @@
/obj/item/stack/sheet/animalhide/xeno = 1) /obj/item/stack/sheet/animalhide/xeno = 1)
projectiletype = /obj/item/projectile/neurotox projectiletype = /obj/item/projectile/neurotox
projectilesound = 'sound/weapons/pierce.ogg' projectilesound = 'sound/weapons/pierce.ogg'
gold_core_spawnable = NO_SPAWN
status_flags = 0 status_flags = 0
unique_name = 0 unique_name = 0
var/sterile = 1 var/sterile = 1
@@ -1,5 +1,3 @@
#define MEDAL_PREFIX "Bubblegum"
/* /*
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 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. 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. It leaves blood trails behind wherever it goes, its clones do as well.
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. It tries to strike at its target through any bloodpools under them; if it fails to do that.
To make this possible, it sprays streams of blood at random. 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.
From these blood pools Bubblegum may summon slaughterlings - weak, low-damage minions designed to impede the target's progress. 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: 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 spellblade that can slice off limbs at range 1. A bottle that, when activated, drives everyone nearby into a frenzy
2. 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.
3. A super double-barrel shotgun that shoots both shells at the same time.
Difficulty: Hard Difficulty: Hard
@@ -42,15 +41,20 @@ Difficulty: Hard
melee_damage_lower = 40 melee_damage_lower = 40
melee_damage_upper = 40 melee_damage_upper = 40
speed = 1 speed = 1
move_to_delay = 10 move_to_delay = 5
ranged_cooldown_time = 10 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 ranged = 1
pixel_x = -32 pixel_x = -32
gender = MALE gender = MALE
del_on_death = 1 del_on_death = 1
crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher) crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher)
loot = list(/obj/structure/closet/crate/necropolis/bubblegum) loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
blood_volume = BLOOD_VOLUME_MAXIMUM //BLEED FOR ME
var/charging = 0 var/charging = 0
var/enrage_till = null
achievement_type = /datum/award/achievement/boss/bubblegum_kill achievement_type = /datum/award/achievement/boss/bubblegum_kill
crusher_achievement_type = /datum/award/achievement/boss/bubblegum_crusher 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." desc = "You're not quite sure how a signal can be bloody."
invisibility = 100 invisibility = 100
/mob/living/simple_animal/hostile/megafauna/bubblegum/BiologicalLife(seconds, times_fired) /mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE, only_robotic = FALSE, only_organic = TRUE)
if(!(. = ..())) . = ..()
return if(. > 0 && prob(25))
move_to_delay = clamp(round((health/maxHealth) * 10), 3, 10) 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() /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) if(charging)
return return
ranged_cooldown = world.time + ranged_cooldown_time ranged_cooldown = world.time + 50
if(!try_bloodattack())
blood_warp()
blood_warp() if(health > maxHealth * 0.5)
if(prob(50 + anger_modifier))
if(prob(25)) charge(delay = 6)
INVOKE_ASYNC(src, .proc/blood_spray) charge(delay = 4) // The FitnessGram Pacer Test is a multistage aerobic capacity test that progressively gets more difficult as it continues.
charge(delay = 2)
else if(prob(5+anger_modifier/2)) SetRecoveryTime(15)
slaughterlings()
else
if(health > maxHealth/2 && !client)
INVOKE_ASYNC(src, .proc/charge)
else 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() /mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize()
. = ..() . = ..()
for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_list) if(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination))
if(B != src) return
return INITIALIZE_HINT_QDEL //There can be only one 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 var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
AddSpell(bloodspell) AddSpell(bloodspell)
if(istype(loc, /obj/effect/dummy/phased_mob/slaughter)) if(istype(loc, /obj/effect/dummy/phased_mob/slaughter))
@@ -109,52 +137,78 @@ Difficulty: Hard
SSshuttle.shuttle_purchase_requirements_met |= "bubblegum" 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) /mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(charging) if(!charging)
return ..()
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget() /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 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) /mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance)
if(charging) if(!charging)
return ..()
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/MoveToTarget(list/possible_targets)
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Move() /mob/living/simple_animal/hostile/megafauna/bubblegum/Move()
if(!stat)
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
if(charging) if(charging)
new/obj/effect/temp_visual/decoy/fading(loc,src) new /obj/effect/temp_visual/decoy/fading(loc,src)
DestroySurroundings() DestroySurroundings()
. = ..() ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Moved()
new /obj/effect/decal/cleanable/blood(src.loc)
if(charging) if(charging)
DestroySurroundings() DestroySurroundings()
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge() /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(atom/chargeat = target, delay = 3, chargepast = 2)
charge() if(!chargeat)
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)
return 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 charging = 1
DestroySurroundings() DestroySurroundings()
walk(src, 0) 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) 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) animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3)
sleep(5) sleep(delay)
throw_at(T, get_dist(src, T), 1, src, 0) 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 charging = 0
Goto(target, move_to_delay, minimum_distance)
/** /**
* Attack by override for bubblegum * Attack by override for bubblegum
@@ -176,36 +230,147 @@ Difficulty: Hard
if(isturf(A) || isobj(A) && A.density) if(isturf(A) || isobj(A) && A.density)
A.ex_act(EXPLODE_HEAVY) A.ex_act(EXPLODE_HEAVY)
DestroySurroundings() 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) /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood()
if(!charging) var/list/targets = ListTargets()
return ..() . = 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)) /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack()
var/mob/living/L = hit_atom var/list/targets = get_mobs_on_blood()
L.visible_message("<span class='danger'>[src] slams into [L]!</span>", "<span class='userdanger'>[src] slams into you!</span>") if(targets.len)
L.apply_damage(40, BRUTE) INVOKE_ASYNC(src, .proc/bloodattack, targets, prob(50))
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1) return TRUE
shake_camera(L, 4, 3) return FALSE
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)
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() /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/obj/effect/decal/cleanable/blood/found_bloodpool
var/list/pools = list() pools = get_pools(get_turf(target), 2)
var/can_jaunt = FALSE pools_to_remove = get_pools(get_turf(target), 1)
for(var/obj/effect/decal/cleanable/blood/nearby in view(src,2)) pools -= pools_to_remove
can_jaunt = TRUE
break
if(!can_jaunt)
return
for(var/obj/effect/decal/cleanable/blood/nearby in view(get_turf(target),2))
pools += nearby
if(pools.len) if(pools.len)
shuffle_inplace(pools) shuffle_inplace(pools)
found_bloodpool = pick(pools) found_bloodpool = pick(pools)
@@ -215,48 +380,110 @@ Difficulty: Hard
forceMove(get_turf(found_bloodpool)) forceMove(get_turf(found_bloodpool))
playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1) playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1)
visible_message("<span class='danger'>And springs back out!</span>") 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() /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage_end(var/newcolor = rgb(149, 10, 10))
visible_message("<span class='danger'>[src] sprays a stream of gore!</span>") retreat_distance = initial(retreat_distance)
var/turf/E = get_edge_target_turf(src, src.dir) minimum_distance = initial(minimum_distance)
var/range = 10 change_move_delay()
var/turf/previousturf = get_turf(src) remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, newcolor)
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/slaughterlings() /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/is_enraged()
visible_message("<span class='danger'>[src] summons a shoal of slaughterlings!</span>") return (enrage_till > world.time)
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/asteroid/hivelordbrood/slaughter /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/change_move_delay(var/newmove = initial(move_to_delay))
name = "slaughterling" move_to_delay = newmove
desc = "Though not yet strong enough to create a true physical form, it's nonetheless determined to murder you." 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
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/asteroid/hivelordbrood/slaughter/CanPass(atom/movable/mover, turf/target) /mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range)
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) . = 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 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 pixel_x = -16
crusher_loot = list(/obj/structure/closet/crate/necropolis/dragon/crusher) crusher_loot = list(/obj/structure/closet/crate/necropolis/dragon/crusher)
loot = list(/obj/structure/closet/crate/necropolis/dragon) 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) guaranteed_butcher_results = list(/obj/item/stack/sheet/animalhide/ashdrake = 10)
var/swooping = NONE var/swooping = NONE
var/swoop_cooldown = 0 var/swoop_cooldown = 0
+3
View File
@@ -69,3 +69,6 @@
/mob/proc/update_inv_ears() /mob/proc/update_inv_ears()
return 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) apply_effect(EFFECT_STUTTER, G.stunforce)
SEND_SIGNAL(src, COMSIG_LIVING_MINOR_SHOCK) SEND_SIGNAL(src, COMSIG_LIVING_MINOR_SHOCK)
lastattacker = H.real_name set_last_attacker(H)
lastattackerckey = H.ckey
log_combat(H, src, "stunned") log_combat(H, src, "stunned")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) 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." speaking = "[DisplayTimeText(i, TRUE)] remain before causality stabilization."
else else
speaking = "[i*0.1]..." 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) sleep(10)
explode() explode()
@@ -715,7 +715,8 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
//Oh shit it's bad, time to freak out //Oh shit it's bad, time to freak out
if(damage > emergency_point) 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 lastwarning = REALTIMEOFDAY
if(!has_reached_emergency) if(!has_reached_emergency)
investigate_log("has reached the emergency point for the first time.", INVESTIGATE_SUPERMATTER) investigate_log("has reached the emergency point for the first time.", INVESTIGATE_SUPERMATTER)
@@ -73,3 +73,11 @@
name = ".38 DumDum bullet casing" name = ".38 DumDum bullet casing"
desc = "A .38 DumDum bullet casing." desc = "A .38 DumDum bullet casing."
projectile_type = /obj/item/projectile/bullet/c38/dumdum 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) 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 wound_falloff_tile = -1
embed_falloff_tile = -5 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() /obj/machinery/computer/pandemic/proc/eject_beaker()
if(beaker) if(beaker)
var/obj/item/reagent_containers/B = beaker
beaker.forceMove(drop_location()) beaker.forceMove(drop_location())
beaker = null beaker = null
update_icon() update_icon()
return B
return null
/obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui) /obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui) ui = SStgui.try_update_ui(user, src, ui)
@@ -237,14 +240,17 @@
. = TRUE //no afterattack . = TRUE //no afterattack
if(stat & (NOPOWER|BROKEN)) if(stat & (NOPOWER|BROKEN))
return return
var/obj/item/reagent_containers/B
if(beaker) if(beaker)
to_chat(user, "<span class='warning'>A container is already loaded into [src]!</span>") B = eject_beaker() //now with 100% more swapping
return
if(!user.transferItemToLoc(I, src)) if(!user.transferItemToLoc(I, src))
return return
if(B)
if(user && Adjacent(user) && user.can_hold_items())
user.put_in_hands(B)
beaker = I 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() update_icon()
else else
return ..() return ..()
@@ -330,7 +330,7 @@
name = "Hollow Water" name = "Hollow Water"
description = "An ubiquitous chemical substance that is composed of hydrogen and oxygen, but it looks kinda hollow." description = "An ubiquitous chemical substance that is composed of hydrogen and oxygen, but it looks kinda hollow."
color = "#88878777" color = "#88878777"
taste_description = "emptyiness" taste_description = "emptiness"
/datum/reagent/water/holywater /datum/reagent/water/holywater

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