This commit is contained in:
Putnam3145
2022-03-30 12:35:12 -07:00
1281 changed files with 75215 additions and 68744 deletions
@@ -11,12 +11,12 @@
var/gibs_reagent_id = /datum/reagent/liquidgibs
var/gibs_bloodtype = "A+"
/obj/effect/decal/cleanable/blood/gibs/Initialize(mapload, list/datum/disease/diseases)
/obj/effect/decal/cleanable/blood/gibs/Initialize(mapload, list/datum/disease/diseases, list/blood_data)
. = ..()
if(random_icon_states && (icon_state == initial(icon_state)) && length(random_icon_states) > 0)
icon_state = pick(random_icon_states)
if(gibs_reagent_id)
reagents.add_reagent(gibs_reagent_id, 5)
reagents.add_reagent(gibs_reagent_id, 5, blood_data)
if(gibs_bloodtype)
add_blood_DNA(list("Non-human DNA" = gibs_bloodtype), diseases)
update_icon()
@@ -1,3 +1,5 @@
GLOBAL_LIST(gang_tags)
/obj/effect/decal/cleanable/crayon
name = "rune"
desc = "Graffiti. Damn kids."
@@ -45,6 +47,8 @@
data["pixel_x"] = pixel_x
if(pixel_y != initial(pixel_y))
data["pixel_y"] = pixel_y
/obj/effect/decal/cleanable/crayon/NeverShouldHaveComeHere(turf/T)
return isgroundlessturf(T)
/obj/effect/decal/cleanable/crayon/PersistenceLoad(list/data)
. = ..()
@@ -63,3 +67,18 @@
pixel_x = data["pixel_x"]
if(data["pixel_y"])
pixel_y = data["pixel_y"]
/obj/effect/decal/cleanable/crayon/gang
name = "Leet Like Jeff K gang tag"
desc = "Looks like someone's claimed this area for Leet Like Jeff K."
icon = 'icons/obj/gang/tags.dmi'
layer = BELOW_MOB_LAYER
var/datum/team/gang/my_gang
/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null)
. = ..()
LAZYADD(GLOB.gang_tags, src)
/obj/effect/decal/cleanable/crayon/gang/Destroy()
LAZYREMOVE(GLOB.gang_tags, src)
..()
@@ -31,7 +31,10 @@
var/list/dna_to_add //find the dna to pass to the spawned gibs. do note this can be null if the mob doesn't have blood. add_blood_DNA() has built in null handling.
var/body_coloring = ""
var/list/blood_data_to_add
if(source_mob)
blood_data_to_add = source_mob.get_blood_data()
if(!issilicon(source_mob))
dna_to_add = blood_dna || source_mob.get_blood_dna_list() //ez pz
if(ishuman(source_mob))
@@ -65,7 +68,7 @@
if(gibamounts[i])
for(var/j = 1, j<= gibamounts[i], j++)
var/gibType = gibtypes[i]
gib = new gibType(loc, diseases)
gib = new gibType(loc, diseases, blood_data_to_add)
if(iscarbon(loc))
var/mob/living/carbon/digester = loc
digester.stomach_contents += gib
@@ -4,6 +4,7 @@
duration = 5
randomdir = FALSE
layer = BELOW_MOB_LAYER
color = BLOOD_COLOR_HUMAN // set it to red by default because the actual icons are white
var/splatter_type = "splatter"
/obj/effect/temp_visual/dir_setting/bloodsplatter/Initialize(mapload, set_dir, new_color)
+125 -47
View File
@@ -46,11 +46,23 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
max_integrity = 200
obj_flags = NONE
///Item flags for the item
var/item_flags = NONE
var/hitsound = null
var/usesound = null
var/throwhitsound = null
///Sound played when you hit something with the item
var/hitsound
///Played when the item is used, for example tools
var/usesound
///Used when yate into a mob
var/mob_throw_hit_sound
///Sound used when equipping the item into a valid slot
var/equip_sound
///Sound uses when picking the item up (into your hands)
var/pickup_sound
///Sound uses when dropping the item, or when its thrown.
var/drop_sound
///Whether or not we use stealthy audio levels for this item's attack sounds
var/stealthy_audio = FALSE
/// Weight class for how much storage capacity it uses and how big it physically is meaning storages can't hold it if their maximum weight class isn't as high as it.
var/w_class = WEIGHT_CLASS_NORMAL
@@ -129,7 +141,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
var/datum/dog_fashion/dog_fashion = null
//Tooltip vars
var/force_string //string form of an item's force. Edit this var only to set a custom force string
///string form of an item's force. Edit this var only to set a custom force string
var/force_string
var/last_force_string_check = 0
var/trigger_guard = TRIGGER_GUARD_NONE
@@ -331,15 +344,20 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
return ..()
/obj/item/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
if(!user)
return
if(anchored)
return
if(loc == user && current_equipped_slot && current_equipped_slot != SLOT_HANDS)
if(loc == user && current_equipped_slot && current_equipped_slot != ITEM_SLOT_HANDS)
if(current_equipped_slot in user.check_obscured_slots())
to_chat(user, "<span class='warning'>You are unable to unequip that while wearing other garments over it!</span>")
return FALSE
. = TRUE
if(resistance_flags & ON_FIRE)
var/mob/living/carbon/C = user
var/can_handle_hot = FALSE
@@ -374,6 +392,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
//If the item is in a storage item, take it out
SEND_SIGNAL(loc, COMSIG_TRY_STORAGE_TAKE, src, user.loc, TRUE)
if(QDELETED(src)) //moving it out of the storage to the floor destroyed it.
return
if(throwing)
throwing.finalize(FALSE)
@@ -381,11 +401,12 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(!allow_attack_hand_drop(user) || !user.temporarilyRemoveItemFromInventory(src))
return
remove_outline()
. = FALSE
pickup(user)
add_fingerprint(user)
if(!user.put_in_active_hand(src, FALSE, FALSE))
user.dropItemToGround(src)
return TRUE
/obj/item/proc/allow_attack_hand_drop(mob/user)
return TRUE
@@ -395,7 +416,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
return
if(anchored)
return
if(loc == user && current_equipped_slot && current_equipped_slot != SLOT_HANDS)
if(loc == user && current_equipped_slot && current_equipped_slot != ITEM_SLOT_HANDS)
if(current_equipped_slot in user.check_obscured_slots())
to_chat(user, "<span class='warning'>You are unable to unequip that while wearing other garments over it!</span>")
return FALSE
@@ -453,9 +474,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
item_flags &= ~(IN_INVENTORY)
item_flags &= ~(IN_STORAGE)
SEND_SIGNAL(src, COMSIG_ITEM_DROPPED,user)
remove_outline()
// if(!silent)
// playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
if(!silent)
playsound(src, drop_sound, DROP_SOUND_VOLUME, ignore_walls = FALSE)
user?.update_equipment_speed_mods()
// called just as an item is picked up (loc is not yet changed)
@@ -548,7 +568,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
//sometimes we only want to grant the item's action if it's equipped in a specific slot.
/obj/item/proc/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_IN_BACKPACK || slot == SLOT_LEGCUFFED) //these aren't true slots, so avoid granting actions there
if(slot == ITEM_SLOT_BACKPACK || slot == ITEM_SLOT_LEGCUFFED) //these aren't true slots, so avoid granting actions there
return FALSE
return TRUE
@@ -702,8 +722,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(isliving(hit_atom)) //Living mobs handle hit sounds differently.
var/volume = get_volume_by_throwforce_and_or_w_class()
if (throwforce > 0)
if (throwhitsound)
playsound(hit_atom, throwhitsound, volume, TRUE, -1)
if (mob_throw_hit_sound)
playsound(hit_atom, mob_throw_hit_sound, volume, TRUE, -1)
else if(hitsound)
playsound(hit_atom, hitsound, volume, TRUE, -1)
else
@@ -711,8 +731,8 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
else
playsound(hit_atom, 'sound/weapons/throwtap.ogg', 1, volume, -1)
// else
// playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
else if (drop_sound)
playsound(src, drop_sound, YEET_SOUND_VOLUME, ignore_walls = FALSE)
return hit_atom.hitby(src, 0, itempush, throwingdatum=throwingdatum)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, messy_throw = TRUE)
@@ -797,7 +817,7 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
if(ismob(location))
var/mob/M = location
var/success = FALSE
if(src == M.get_item_by_slot(SLOT_WEAR_MASK))
if(src == M.get_item_by_slot(ITEM_SLOT_MASK))
success = TRUE
if(success)
location = get_turf(M)
@@ -886,47 +906,55 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
openToolTip(user,src,params,title = name,content = "[desc]<br><b>Force:</b> [force_string]",theme = "")
/obj/item/MouseEntered(location, control, params)
. = ..()
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_ENTER, location, control, params)
if((item_flags & IN_INVENTORY || item_flags & IN_STORAGE) && usr?.client.prefs.enable_tips && !QDELETED(src))
var/timedelay = max(usr.client.prefs.tip_delay * 0.01, 0.01) // I heard multiplying is faster, also runtimes from very low/negative numbers
usr.client.tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, usr), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
var/mob/living/L = usr
if(istype(L) && (L.incapacitated() || (current_equipped_slot in L.check_obscured_slots()) || !L.canUnEquip(src)))
apply_outline(_size = 3)
else
apply_outline()
if(get(src, /mob) == usr && !QDELETED(src))
var/mob/living/L = usr
if(usr.client.prefs.enable_tips)
var/timedelay = usr.client.prefs.tip_delay/100
usr.client.tip_timer = addtimer(CALLBACK(src, .proc/openTip, location, control, params, usr), timedelay, TIMER_STOPPABLE)//timer takes delay in deciseconds, but the pref is in milliseconds. dividing by 100 converts it.
if(usr.client.prefs.outline_enabled)
if(istype(L) && L.incapacitated())
apply_outline(COLOR_RED_GRAY) //if they're dead or handcuffed, let's show the outline as red to indicate that they can't interact with that right now
else
apply_outline(usr.client.prefs.outline_color) //if the player's alive and well we send the command with no color set, so it uses the theme's color
/obj/item/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params)
. = ..()
remove_outline()
remove_filter("hover_outline") //get rid of the hover effect in case the mouse exit isn't called if someone drags and drops an item and somthing goes wrong
/obj/item/MouseExited(location,control,params)
/obj/item/MouseExited(location, control, params)
SEND_SIGNAL(src, COMSIG_ITEM_MOUSE_EXIT, location, control, params)
deltimer(usr.client.tip_timer) //delete any in-progress timer if the mouse is moved off the item before it finishes
closeToolTip(usr)
remove_outline()
remove_filter("hover_outline")
/obj/item/proc/apply_outline(colour = null, _size=1)
if(!(item_flags & IN_INVENTORY || item_flags & IN_STORAGE) || QDELETED(src) || isobserver(usr))
/obj/item/proc/apply_outline(outline_color = null)
if(get(src, /mob) != usr || QDELETED(src) || isobserver(usr)) //cancel if the item isn't in an inventory, is being deleted, or if the person hovering is a ghost (so that people spectating you don't randomly make your items glow)
return
if(usr.client)
if(!usr.client.prefs.outline_enabled)
return
if(!colour)
if(usr.client)
colour = usr.client.prefs.outline_color
if(!colour)
colour = COLOR_BLUE_GRAY
else
colour = COLOR_BLUE_GRAY
if(outline_filter)
filters -= outline_filter
outline_filter = filter(type="outline", size=_size, color=colour)
filters += outline_filter
var/theme = lowertext(usr.client.prefs.UI_style)
if(!outline_color) //if we weren't provided with a color, take the theme's color
switch(theme) //yeah it kinda has to be this way
if("midnight")
outline_color = COLOR_THEME_MIDNIGHT
if("plasmafire")
outline_color = COLOR_THEME_PLASMAFIRE
if("retro")
outline_color = COLOR_THEME_RETRO //just as garish as the rest of this theme
if("slimecore")
outline_color = COLOR_THEME_SLIMECORE
if("operative")
outline_color = COLOR_THEME_OPERATIVE
if("clockwork")
outline_color = COLOR_THEME_CLOCKWORK //if you want free gbp go fix the fact that clockwork's tooltip css is glass'
if("glass")
outline_color = COLOR_THEME_GLASS
else //this should never happen, hopefully
outline_color = COLOR_WHITE
if(color)
outline_color = COLOR_WHITE //if the item is recolored then the outline will be too, let's make the outline white so it becomes the same color instead of some ugly mix of the theme and the tint
/obj/item/proc/remove_outline()
if(outline_filter)
filters -= outline_filter
outline_filter = null
add_filter("hover_outline", 1, list("type" = "outline", "size" = 1, "color" = outline_color))
// Called when a mob tries to use the item as a tool.
// Handles most checks.
@@ -1063,6 +1091,19 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
. = ..()
if(var_name == NAMEOF(src, slowdown))
set_slowdown(var_value) //don't care if it's a duplicate edit as slowdown'll be set, do it anyways to force normal behavior.
/obj/item/proc/canStrip(mob/stripper, mob/owner)
SHOULD_BE_PURE(TRUE)
return !HAS_TRAIT(src, TRAIT_NODROP) && !(item_flags & ABSTRACT)
/obj/item/proc/doStrip(mob/stripper, mob/owner)
if(owner.dropItemToGround(src))
if(stripper.can_hold_items())
stripper.put_in_hands(src)
return TRUE
else
return FALSE
/**
* Does the current embedding var meet the criteria for being harmless? Namely, does it explicitly define the pain multiplier and jostle pain mult to be 0? If so, return true.
*
@@ -1195,3 +1236,40 @@ GLOBAL_VAR_INIT(embedpocalypse, FALSE) // if true, all items will be able to emb
pain_stam_pct = (!isnull(embedding["pain_stam_pct"]) ? embedding["pain_stam_pct"] : EMBEDDED_PAIN_STAM_PCT),\
embed_chance_turf_mod = (!isnull(embedding["embed_chance_turf_mod"]) ? embedding["embed_chance_turf_mod"] : EMBED_CHANCE_TURF_MOD))
return TRUE
/**
* * An interrupt for offering an item to other people, called mainly from [/mob/living/carbon/proc/give], in case you want to run your own offer behavior instead.
*
* * Return TRUE if you want to interrupt the offer.
*
* * Arguments:
* * offerer - the person offering the item
*/
/obj/item/proc/on_offered(mob/living/carbon/offerer)
if(SEND_SIGNAL(src, COMSIG_ITEM_OFFERING, offerer) & COMPONENT_OFFER_INTERRUPT)
return TRUE
/**
* * An interrupt for someone trying to accept an offered item, called mainly from [/mob/living/carbon/proc/take], in case you want to run your own take behavior instead.
*
* * Return TRUE if you want to interrupt the taking.
*
* * Arguments:
* * offerer - the person offering the item
* * taker - the person trying to accept the offer
*/
/obj/item/proc/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
if(SEND_SIGNAL(src, COMSIG_ITEM_OFFER_TAKEN, offerer, taker) & COMPONENT_OFFER_INTERRUPT)
return TRUE
/**
* Updates all action buttons associated with this item
*
* Arguments:
* * status_only - Update only current availability status of the buttons to show if they are ready or not to use
* * force - Force buttons update even if the given button icon state has not changed
*/
/obj/item/proc/update_action_buttons(status_only = FALSE, force = FALSE)
for(var/datum/action/current_action as anything in actions)
current_action.UpdateButtonIcon(status_only, force)
+28 -2
View File
@@ -14,6 +14,7 @@
var/response_timer_id = null
var/approval_time = 600
var/allow_unicode = FALSE
var/admin_approved = FALSE
var/static/regex/standard_station_regex
@@ -62,8 +63,32 @@
to_chat(user, "Your name has been sent to your employers for approval.")
// Autoapproves after a certain time
response_timer_id = addtimer(CALLBACK(src, .proc/rename_station, new_name, user.name, user.real_name, key_name(user)), approval_time, TIMER_STOPPABLE)
to_chat(GLOB.admins, "<span class='adminnotice'><b><font color=orange>CUSTOM STATION RENAME:</font></b>[ADMIN_LOOKUPFLW(user)] proposes to rename the [name_type] to [html_encode(new_name)] (will autoapprove in [DisplayTimeText(approval_time)]). [ADMIN_SMITE(user)] (<A HREF='?_src_=holder;[HrefToken(TRUE)];reject_custom_name=[REF(src)]'>REJECT</A>) [ADMIN_CENTCOM_REPLY(user)]</span>")
var/requires_approval = CONFIG_GET(flag/station_name_needs_approval)
response_timer_id = addtimer(CALLBACK(src, .proc/check_state, new_name, user.name, user.real_name, key_name(user)), approval_time, TIMER_STOPPABLE)
to_chat(GLOB.admins, "<span class='adminnotice'><b><font color=orange>CUSTOM STATION RENAME:</font></b>[ADMIN_LOOKUPFLW(user)] proposes to rename the [name_type] to [html_encode(new_name)] ([requires_approval ? "REQUIRES ADMIN APPROVAL and will autodeny" : "will autoapprove"] in [DisplayTimeText(approval_time)]). [ADMIN_SMITE(user)] (<A HREF='?_src_=holder;[HrefToken(TRUE)];reject_custom_name=[REF(src)]'>REJECT</A>)[requires_approval ? " (<A HREF='?_src_=holder;[HrefToken(TRUE)];approve_custom_name=[REF(src)]'>APPROVE</A>)" : ""] [ADMIN_CENTCOM_REPLY(user)]</span>")
/obj/item/station_charter/proc/check_state(designation, uname, ureal_name, ukey)
var/requires_approval = CONFIG_GET(flag/station_name_needs_approval)
if(requires_approval && !admin_approved)
var/turf/T = get_turf(src)
T.visible_message("<span class='warning'>A note appears on [src], stating this sector requires central command approval for its station names, which was not performed in time for this request. Looks like the change has been auto-rejected.</span>")
var/m = "Station rename has been autorejected due to config requiring admin approval."
message_admins(m)
log_admin(m)
else
rename_station(designation, uname, ureal_name, ukey)
response_timer_id = null
admin_approved = FALSE
/obj/item/station_charter/proc/allow_pass(user)
if(!user)
return
if(!response_timer_id)
return
admin_approved = TRUE
var/m = "[key_name(user)] has approved the proposed station name. It can still be denied prior to the timer expiring."
message_admins(m)
log_admin(m)
/obj/item/station_charter/proc/reject_proposed(user)
if(!user)
@@ -80,6 +105,7 @@
deltimer(response_timer_id)
response_timer_id = null
admin_approved = FALSE
/obj/item/station_charter/proc/rename_station(designation, uname, ureal_name, ukey)
set_station_name(designation)
+1 -1
View File
@@ -34,7 +34,7 @@
user.put_in_hands(PA)
/obj/item/chrono_eraser/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_BACK)
if(slot == ITEM_SLOT_BACK)
return 1
/obj/item/gun/energy/chrono_gun
+4 -4
View File
@@ -89,7 +89,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
..()
/obj/item/proc/help_light_cig(mob/living/M)
var/mask_item = M.get_item_by_slot(SLOT_WEAR_MASK)
var/mask_item = M.get_item_by_slot(ITEM_SLOT_MASK)
if(istype(mask_item, /obj/item/clothing/mask/cigarette))
return mask_item
@@ -130,7 +130,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
reagents.add_reagent_list(list_reagents)
if(starts_lit)
light()
AddComponent(/datum/component/knockoff,90,list(BODY_ZONE_PRECISE_MOUTH),list(SLOT_WEAR_MASK))//90% to knock off when wearing a mask
AddComponent(/datum/component/knockoff,90,list(BODY_ZONE_PRECISE_MOUTH),list(ITEM_SLOT_MASK))//90% to knock off when wearing a mask
/obj/item/clothing/mask/cigarette/Destroy()
STOP_PROCESSING(SSobj, src)
@@ -819,7 +819,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/clothing/mask/vape/equipped(mob/user, slot)
. = ..()
if(slot == SLOT_WEAR_MASK)
if(slot == ITEM_SLOT_MASK)
if(!screw)
to_chat(user, "<span class='notice'>You start puffing on the vape.</span>")
reagents.reagents_holder_flags &= ~(NO_REACT)
@@ -830,7 +830,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
/obj/item/clothing/mask/vape/dropped(mob/user)
. = ..()
var/mob/living/carbon/C = user
if(C.get_item_by_slot(SLOT_WEAR_MASK) == src)
if(C.get_item_by_slot(ITEM_SLOT_MASK) == src)
reagents.reagents_holder_flags |= NO_REACT
STOP_PROCESSING(SSobj, src)
+60 -84
View File
@@ -298,7 +298,7 @@
/obj/item/toy/crayon/proc/draw_on(atom/target, mob/user, proximity, params)
var/static/list/punctuation = list("!","?",".",",","/","+","-","=","%","#","&")
var/istagger = HAS_TRAIT(user, TRAIT_TAGGER)
var/cost = 1
if(paint_mode == PAINT_LARGE_HORIZONTAL)
cost = 5
@@ -355,14 +355,14 @@
else if(drawing in graffiti|oriented)
temp = "graffiti"
// If a gang member is using a gang spraycan, it'll behave differently
var/gang_mode = FALSE
if(gang && user.mind && user.mind.has_antag_datum(/datum/antagonist/gang)) //Heres a check.
gang_mode = TRUE // No more runtimes if a non-gang member sprays a gang can, it just works like normal cans.
// discontinue if the area isn't valid for tagging because gang "honour"
var/gang_mode
if(user.mind)
gang_mode = user.mind.has_antag_datum(/datum/antagonist/gang)
if(gang_mode && (!can_claim_for_gang(user, target)))
return
var/graf_rot
if(drawing in oriented)
switch(user.dir)
@@ -390,16 +390,12 @@
audible_message("<span class='notice'>You hear spraying.</span>")
playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5)
var/takes_time = !instant //For order purposes, since I'm maximum bad.
if(gang_mode)
takes_time = TRUE
var/wait_time = 50
if(paint_mode == PAINT_LARGE_HORIZONTAL)
wait_time *= 3
if(takes_time) //This is what deteremines the time it takes to spray a tag in gang mode. 50 is Default.
if(!do_after(user, gang_tag_delay, target = target)) //25 is a good number, but we have gang_tag_delay var now.
if(gang_mode || !instant)
if(!do_after(user, 50, target = target))
return
if(length(text_buffer))
@@ -410,16 +406,15 @@
if(actually_paints)
var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot)
if(gang_mode)
// Double check it wasn't tagged in the meanwhile.
if(!can_claim_for_gang(user, target))
return
tag_for_gang(user, target)
tag_for_gang(user, target, gang_mode)
affected_turfs += target
else
switch(paint_mode)
if(PAINT_NORMAL)
var/obj/effect/decal/cleanable/crayon/C = new(target, paint_color, drawing, temp, graf_rot)
C.add_hiddenprint(user)
if(precision_mode)
C.pixel_x = clamp(precision_x, -(world.icon_size/2), world.icon_size/2)
@@ -432,14 +427,18 @@
var/turf/left = locate(target.x-1,target.y,target.z)
var/turf/right = locate(target.x+1,target.y,target.z)
if(isValidSurface(left) && isValidSurface(right))
var/obj/effect/decal/cleanable/crayon/C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
C.add_hiddenprint(user)
C = new(left, paint_color, drawing, temp, graf_rot, PAINT_LARGE_HORIZONTAL_ICON)
affected_turfs += left
affected_turfs += right
affected_turfs += target
else
to_chat(user, "<span class='warning'>There isn't enough space to paint!</span>")
return
C.add_hiddenprint(user)
if(istagger)
C.AddComponent(/datum/element/art, GOOD_ART)
else
C.AddComponent(/datum/element/art, BAD_ART)
if(!instant)
to_chat(user, "<span class='notice'>You finish drawing \the [temp].</span>")
@@ -462,52 +461,6 @@
reagents.trans_to(t, ., volume_multiplier)
check_empty(user)
//////////////Gang mode stuff/////////////////
/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
// Check area validity.
// Reject space, player-created areas, and non-station z-levels.
var/area/A = get_base_area(target)
if(!A || (!is_station_level(A.z)) || !(A.area_flags & VALID_TERRITORY))
to_chat(user, "<span class='warning'>[A] is unsuitable for tagging.</span>")
return FALSE
var/spraying_over = FALSE
for(var/G in target)
var/obj/effect/decal/cleanable/crayon/gang/gangtag = G
if(istype(gangtag))
var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang)
if(gangtag.gang != GA.gang)
spraying_over = TRUE
break
var/occupying_gang = territory_claimed(A, user)
if(occupying_gang && !spraying_over)
to_chat(user, "<span class='danger'>[A] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!</span>")
return FALSE
// If you pass the gauntlet of checks, you're good to proceed
return TRUE
/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
for(var/datum/team/gang/G in GLOB.gangs)
if(territory.type in (G.territories|G.new_territories))
. = G.name
break
/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target)
//Delete any old markings on this tile, including other gang tags
for(var/obj/effect/decal/cleanable/crayon/old_marking in target)
qdel(old_marking)
var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang)
var/area/territory = get_base_area(target)
new /obj/effect/decal/cleanable/crayon/gang(target,G.gang,"graffiti",0,user) // Heres the gang tag.
to_chat(user, "<span class='notice'>You tagged [territory] for your gang!</span>")
/////////////////Gang end////////////////////
/obj/item/toy/crayon/attack(mob/M, mob/user)
if(edible && (M == user))
to_chat(user, "You take a bite of the [src.name]. Delicious!")
@@ -521,6 +474,49 @@
else
..()
//////////////Gang mode stuff/////////////////
/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target)
var/area/A = get_area(target)
if(!A || (!is_station_level(A.z)))
to_chat(user, "<span class='warning'>[A] is unsuitable for tagging.</span>")
return FALSE
var/spraying_over = FALSE
for(var/obj/effect/decal/cleanable/crayon/gang/G in target)
spraying_over = TRUE
for(var/obj/machinery/power/apc in target)
to_chat(user, "<span class='warning'>You can't tag an APC.</span>")
return FALSE
var/occupying_gang = territory_claimed(A, user)
if(occupying_gang && !spraying_over)
to_chat(user, "<span class='danger'>[A] has already been tagged by a gang! You must find and spray over the old tag first!</span>")
return FALSE
// stolen from oldgang lmao
return TRUE
/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target, datum/antagonist/gang/user_gang)
for(var/obj/effect/decal/cleanable/crayon/old_marking in target)
qdel(old_marking)
var/area/territory = get_area(target)
var/obj/effect/decal/cleanable/crayon/gang/tag = new /obj/effect/decal/cleanable/crayon/gang(target)
tag.my_gang = user_gang.my_gang
tag.icon_state = "[user_gang.gang_id]_tag"
tag.name = "[tag.my_gang.name] gang tag"
tag.desc = "Looks like someone's claimed this area for [tag.my_gang.name]."
to_chat(user, "<span class='notice'>You tagged [territory] for [tag.my_gang.name]!</span>")
/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user)
for(var/obj/effect/decal/cleanable/crayon/gang/G in GLOB.gang_tags)
if(get_area(G) == territory)
return G
/////////////////Gang end////////////////////
/obj/item/toy/crayon/red
icon_state = "crayonred"
paint_color = "#DA0000"
@@ -761,7 +757,7 @@
return
if(isobj(target))
if(isobj(target) && !istype(target, /obj/effect/decal/cleanable/crayon/gang))
if(actually_paints)
if(istype(target, /obj/item/canvas)) //dont color our canvas neon green when im trying to paint please
return
@@ -866,26 +862,6 @@
post_noise = FALSE
reagent_contents = list(/datum/reagent/consumable/nothing = 1, /datum/reagent/toxin/mutetoxin = 1)
/obj/item/toy/crayon/spraycan/gang
charges = 20 // Charges back to 20, which is the default value for them.
gang = TRUE
gang_tag_delay = 15 //Its 50% faster than a regular spraycan, for tagging. After-all they did spend points/meet the boss.
pre_noise = FALSE
post_noise = TRUE // Its even more stealthy just a tad.
/obj/item/toy/crayon/spraycan/gang/Initialize(loc, datum/team/gang/G)
..()
if(G)
gang = G
paint_color = G.color
update_icon()
/obj/item/toy/crayon/spraycan/gang/examine(mob/user)
. = ..()
if(user.mind && user.mind.has_antag_datum(/datum/antagonist/gang) || isobserver(user))
. += "This spraycan has been specially modified with a stage 2 nozzle kit, making it faster."
/obj/item/toy/crayon/spraycan/infinite
name = "infinite spraycan"
charges = -1
+4 -4
View File
@@ -80,13 +80,13 @@
/obj/item/defibrillator/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user)
if(slot_flags == ITEM_SLOT_BACK)
if(user.get_item_by_slot(SLOT_BACK) == src)
if(user.get_item_by_slot(ITEM_SLOT_BACK) == src)
ui_action_click()
else
to_chat(user, "<span class='warning'>Put the defibrillator on your back first!</span>")
else if(slot_flags == ITEM_SLOT_BELT)
if(user.get_item_by_slot(SLOT_BELT) == src)
if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
ui_action_click()
else
to_chat(user, "<span class='warning'>Strap the defibrillator's belt on first!</span>")
@@ -182,7 +182,7 @@
/obj/item/defibrillator/equipped(mob/user, slot)
..()
if((slot_flags == ITEM_SLOT_BACK && slot != SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != SLOT_BELT))
if((slot_flags == ITEM_SLOT_BACK && slot != ITEM_SLOT_BACK) || (slot_flags == ITEM_SLOT_BELT && slot != ITEM_SLOT_BELT))
remove_paddles(user)
update_power()
@@ -423,7 +423,7 @@
if((!req_defib && grab_ghost) || (req_defib && defib.grab_ghost))
H.notify_ghost_cloning("Your heart is being defibrillated!")
H.grab_ghost() // Shove them back in their body.
else if(H.can_defib())
else if(H.can_revive())
H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src)
do_help(H, user)
@@ -369,6 +369,10 @@ GLOBAL_LIST_EMPTY(PDAs)
dat += "<li><a href='byond://?src=[REF(src)];choice=Toggle Door'>[PDAIMG(rdoor)]Toggle Remote Door</a></li>"
if (cartridge.access & CART_DRONEPHONE)
dat += "<li><a href='byond://?src=[REF(src)];choice=Drone Phone'>[PDAIMG(dronephone)]Drone Phone</a></li>"
if (cartridge.access & CART_BARTENDER)
dat += "<li><a href='byond://?src=[REF(src)];choice=Drink Recipe Browser'>[PDAIMG(bucket)]Drink Recipe Browser</a></li>"
if (cartridge.access & CART_CHEMISTRY)
dat += "<li><a href='byond://?src=[REF(src)];choice=Chemistry Recipe Browser'>[PDAIMG(bucket)]Chemistry Recipe Browser</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=3'>[PDAIMG(atmos)]Atmospheric Scan</a></li>"
dat += "<li><a href='byond://?src=[REF(src)];choice=Light'>[PDAIMG(flashlight)][fon ? "Disable" : "Enable"] Flashlight</a></li>"
if (pai)
@@ -705,6 +709,16 @@ GLOBAL_LIST_EMPTY(PDAs)
if(T)
pai.forceMove(T)
//DRINK RECIPE BROWSER=============================
if("Drink Recipe Browser")
if(cartridge && cartridge.access & CART_BARTENDER)
recipe_search(U, GLOB.drink_reactions_list)
//CHEMISTRY RECIPE BROWSER
if("Chemistry Recipe Browser")
if(cartridge && cartridge.access & CART_CHEMISTRY)
recipe_search(U, GLOB.normalized_chemical_reactions_list)
//LINK FUNCTIONS===================================
else//Cartridge menu linking
@@ -222,6 +222,7 @@
/obj/item/pda/bar
name = "bartender PDA"
icon_state = "pda-bartender"
default_cartridge = /obj/item/cartridge/bartender
inserted_item = /obj/item/pen/fountain
/obj/item/pda/atmos
+9 -1
View File
@@ -13,6 +13,8 @@
#define CART_QUARTERMASTER (1<<12)
#define CART_HYDROPONICS (1<<13)
#define CART_DRONEPHONE (1<<14)
#define CART_BARTENDER (1<<15)
#define CART_CHEMISTRY (1<<16)
/obj/item/cartridge
@@ -77,7 +79,7 @@
/obj/item/cartridge/chemistry
name = "\improper ChemWhiz cartridge"
icon_state = "cart-chem"
access = CART_REAGENT_SCANNER
access = CART_REAGENT_SCANNER | CART_CHEMISTRY
bot_access_flags = MED_BOT
/obj/item/cartridge/security
@@ -190,6 +192,12 @@
bot_access_flags = SEC_BOT | MULE_BOT | FLOOR_BOT | CLEAN_BOT | MED_BOT | FIRE_BOT
spam_enabled = 1
/obj/item/cartridge/bartender
name = "\improper B.O.O.Z.E cartridge"
desc = "Now with 12% alcohol!"
icon_state = "cart-bar"
access = CART_BARTENDER
/obj/item/cartridge/captain/New()
..()
radio = new(src)
@@ -19,6 +19,7 @@
w_class = WEIGHT_CLASS_SMALL
slot_flags = ITEM_SLOT_BELT
rad_flags = RAD_NO_CONTAMINATE
item_flags = NOBLUDGEON
custom_materials = list(/datum/material/iron = 150, /datum/material/glass = 150)
var/grace = RAD_GRACE_PERIOD
@@ -35,17 +36,15 @@
. = ..()
START_PROCESSING(SSobj, src)
soundloop = new(list(src), FALSE)
soundloop = new(src, FALSE)
/obj/item/geiger_counter/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(soundloop)
return ..()
/obj/item/geiger_counter/process()
update_icon()
update_sound()
/obj/item/geiger_counter/process(delta_time)
if(!scanning)
current_tick_amount = 0
return
@@ -64,49 +63,55 @@
current_tick_amount = 0
update_appearance()
update_sound()
/obj/item/geiger_counter/examine(mob/user)
. = ..()
if(!scanning)
return
. += "<span class='info'>Alt-click it to clear stored radiation levels.</span>"
. += span_info("Alt-click it to clear stored radiation levels.")
if(obj_flags & EMAGGED)
. += "<span class='warning'>The display seems to be incomprehensible.</span>"
. += span_warning("The display seems to be incomprehensible.")
return
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
. += "<span class='notice'>Ambient radiation level count reports that all is well.</span>"
. += span_notice("Ambient radiation level count reports that all is well.")
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
. += "<span class='disarm'>Ambient radiation levels slightly above average.</span>"
. += span_alert("Ambient radiation levels slightly above average.")
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
. += "<span class='warning'>Ambient radiation levels above average.</span>"
. += span_warning("Ambient radiation levels above average.")
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
. += "<span class='danger'>Ambient radiation levels highly above average.</span>"
. += span_danger("Ambient radiation levels highly above average.")
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
. += "<span class='suicide'>Ambient radiation levels nearing critical level.</span>"
. += span_suicide("Ambient radiation levels nearing critical level.")
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
. += "<span class='boldannounce'>Ambient radiation levels above critical level!</span>"
. += span_boldannounce("Ambient radiation levels above critical level!")
. += "<span class='notice'>The last radiation amount detected was [last_tick_amount]</span>"
. += span_notice("The last radiation amount detected was [last_tick_amount]")
/obj/item/geiger_counter/update_icon_state()
if(!scanning)
icon_state = "geiger_off"
else if(obj_flags & EMAGGED)
return ..()
if(obj_flags & EMAGGED)
icon_state = "geiger_on_emag"
else
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
icon_state = "geiger_on_1"
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
icon_state = "geiger_on_2"
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
icon_state = "geiger_on_3"
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
icon_state = "geiger_on_4"
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
icon_state = "geiger_on_4"
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
icon_state = "geiger_on_5"
return ..()
switch(radiation_count)
if(-INFINITY to RAD_LEVEL_NORMAL)
icon_state = "geiger_on_1"
if(RAD_LEVEL_NORMAL + 1 to RAD_LEVEL_MODERATE)
icon_state = "geiger_on_2"
if(RAD_LEVEL_MODERATE + 1 to RAD_LEVEL_HIGH)
icon_state = "geiger_on_3"
if(RAD_LEVEL_HIGH + 1 to RAD_LEVEL_VERY_HIGH)
icon_state = "geiger_on_4"
if(RAD_LEVEL_VERY_HIGH + 1 to RAD_LEVEL_CRITICAL)
icon_state = "geiger_on_4"
if(RAD_LEVEL_CRITICAL + 1 to INFINITY)
icon_state = "geiger_on_5"
return ..()
/obj/item/geiger_counter/proc/update_sound()
var/datum/looping_sound/geiger/loop = soundloop
@@ -124,21 +129,21 @@
if(amount <= RAD_BACKGROUND_RADIATION || !scanning)
return
current_tick_amount += amount
update_icon()
update_appearance()
/obj/item/geiger_counter/attack_self(mob/user)
scanning = !scanning
update_icon()
to_chat(user, "<span class='notice'>[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src].</span>")
update_appearance()
to_chat(user, span_notice("[icon2html(src, user)] You switch [scanning ? "on" : "off"] [src]."))
/obj/item/geiger_counter/afterattack(atom/target, mob/user)
/obj/item/geiger_counter/afterattack(atom/target, mob/living/user)
. = ..()
if(user.a_intent == INTENT_HELP)
if(!(obj_flags & EMAGGED))
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='notice'>You scan [target]'s radiation levels with [src]...</span>")
user.visible_message(span_notice("[user] scans [target] with [src]."), span_notice("You scan [target]'s radiation levels with [src]..."))
addtimer(CALLBACK(src, .proc/scan, target, user), 20, TIMER_UNIQUE) // Let's not have spamming GetAllContents
else
user.visible_message("<span class='notice'>[user] scans [target] with [src].</span>", "<span class='danger'>You project [src]'s stored radiation into [target]!</span>")
user.visible_message(span_notice("[user] scans [target] with [src]."), span_danger("You project [src]'s stored radiation into [target]!"))
target.rad_act(radiation_count)
radiation_count = 0
return TRUE
@@ -156,57 +161,61 @@
if(isliving(A))
var/mob/living/M = A
if(!M.radiation)
to_chat(user, "<span class='notice'>[icon2html(src, user)] Radiation levels within normal boundaries.</span>")
to_chat(user, span_notice("[icon2html(src, user)] Radiation levels within normal boundaries."))
else
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation] rad.</span>")
to_chat(user, span_boldannounce("[icon2html(src, user)] Subject is irradiated. Radiation levels: [M.radiation]."))
if(rad_strength)
to_chat(user, "<span class='boldannounce'>[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]</span>")
to_chat(user, span_boldannounce("[icon2html(src, user)] Target contains radioactive contamination. Radioactive strength: [rad_strength]"))
else
to_chat(user, "<span class='notice'>[icon2html(src, user)] Target is free of radioactive contamination.</span>")
to_chat(user, span_notice("[icon2html(src, user)] Target is free of radioactive contamination."))
/obj/item/geiger_counter/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_SCREWDRIVER && (obj_flags & EMAGGED))
if(scanning)
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
return 0
user.visible_message("<span class='notice'>[user] unscrews [src]'s maintenance panel and begins fiddling with its innards...</span>", "<span class='notice'>You begin resetting [src]...</span>")
to_chat(user, span_warning("Turn off [src] before you perform this action!"))
return FALSE
user.visible_message(span_notice("[user] unscrews [src]'s maintenance panel and begins fiddling with its innards..."), span_notice("You begin resetting [src]..."))
if(!I.use_tool(src, user, 40, volume=50))
return 0
user.visible_message("<span class='notice'>[user] refastens [src]'s maintenance panel!</span>", "<span class='notice'>You reset [src] to its factory settings!</span>")
return FALSE
user.visible_message(span_notice("[user] refastens [src]'s maintenance panel!"), span_notice("You reset [src] to its factory settings!"))
obj_flags &= ~EMAGGED
radiation_count = 0
update_icon()
return 1
update_appearance()
return TRUE
else
return ..()
/obj/item/geiger_counter/AltClick(mob/living/user)
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
return
return ..()
if(!scanning)
to_chat(usr, "<span class='warning'>[src] must be on to reset its radiation level!</span>")
return TRUE
to_chat(usr, span_warning("[src] must be on to reset its radiation level!"))
return
radiation_count = 0
to_chat(usr, "<span class='notice'>You flush [src]'s radiation counts, resetting it to normal.</span>")
update_icon()
return TRUE
to_chat(usr, span_notice("You flush [src]'s radiation counts, resetting it to normal."))
update_appearance()
/obj/item/geiger_counter/emag_act(mob/user)
. = ..()
if(obj_flags & EMAGGED)
return
if(scanning)
to_chat(user, "<span class='warning'>Turn off [src] before you perform this action!</span>")
to_chat(user, span_warning("Turn off [src] before you perform this action!"))
return
to_chat(user, "<span class='warning'>You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan.</span>")
to_chat(user, span_warning("You override [src]'s radiation storing protocols. It will now generate small doses of radiation, and stored rads are now projected into creatures you scan."))
obj_flags |= EMAGGED
return TRUE
/obj/item/geiger_counter/cyborg
var/mob/listeningTo
/obj/item/geiger_counter/cyborg/cyborg_unequip(mob/user)
if(!scanning)
return
scanning = FALSE
update_appearance()
/obj/item/geiger_counter/cyborg/equipped(mob/user)
. = ..()
if(listeningTo == user)
@@ -217,6 +226,7 @@
listeningTo = user
/obj/item/geiger_counter/cyborg/proc/redirect_rad_act(datum/source, amount)
SIGNAL_HANDLER
rad_act(amount)
/obj/item/geiger_counter/cyborg/dropped(mob/user)
+1 -1
View File
@@ -19,7 +19,7 @@
/obj/item/megaphone/equipped(mob/M, slot)
. = ..()
if (slot == SLOT_HANDS)
if (slot == ITEM_SLOT_HANDS)
RegisterSignal(M, COMSIG_MOB_SAY, .proc/handle_speech)
else
UnregisterSignal(M, COMSIG_MOB_SAY)
@@ -156,7 +156,7 @@
category = list("hacked", "Misc")
/obj/item/electropack/shockcollar/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(loc == user && user.get_item_by_slot(SLOT_NECK))
if(loc == user && user.get_item_by_slot(ITEM_SLOT_NECK))
to_chat(user, "<span class='warning'>The collar is fastened tight! You'll need help taking this off!</span>")
return
return ..()
@@ -167,7 +167,7 @@
if(isliving(loc) && on) //the "on" arg is currently useless
var/mob/living/L = loc
if(!L.get_item_by_slot(SLOT_NECK)) //**properly** stops pocket shockers
if(!L.get_item_by_slot(ITEM_SLOT_NECK)) //**properly** stops pocket shockers
return
if(shock_cooldown == TRUE)
return
@@ -56,7 +56,7 @@ GLOBAL_LIST_INIT(channel_tokens, list(
/obj/item/radio/headset/ComponentInitialize()
. = ..()
if (bowman)
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_EARS))
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
/obj/item/radio/headset/Initialize()
. = ..()
@@ -373,4 +373,4 @@ GLOBAL_LIST_INIT(channel_tokens, list(
name = replacetext(name,"headset", "bowman headset")
desc = "[desc] Protects ears from flashbangs."
bowman = TRUE
AddComponent(/datum/component/wearertargeting/earprotection, list(SLOT_EARS))
AddComponent(/datum/component/wearertargeting/earprotection, list(ITEM_SLOT_EARS))
@@ -24,8 +24,8 @@
/obj/item/reverse_bear_trap/Initialize()
. = ..()
soundloop = new(list(src))
soundloop2 = new(list(src))
soundloop = new(src)
soundloop2 = new(src)
/obj/item/reverse_bear_trap/Destroy()
QDEL_NULL(soundloop)
@@ -33,22 +33,22 @@
STOP_PROCESSING(SSprocessing, src)
return ..()
/obj/item/reverse_bear_trap/process()
/obj/item/reverse_bear_trap/process(delta_time)
if(!ticking)
return
time_left--
time_left -= delta_time
soundloop2.mid_length = max(0.5, time_left - 5) //beepbeepbeepbeepbeep
if(!time_left || !isliving(loc))
if(time_left <= 0 || !isliving(loc))
playsound(src, 'sound/machines/microwave/microwave-end.ogg', 100, FALSE)
soundloop.stop()
soundloop2.stop()
to_chat(loc, "<span class='userdanger'>*ding*</span>")
to_chat(loc, span_userdanger("*ding*"))
addtimer(CALLBACK(src, .proc/snap), 2)
/obj/item/reverse_bear_trap/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(iscarbon(user))
var/mob/living/carbon/C = user
if(C.get_item_by_slot(SLOT_HEAD) == src)
if(C.get_item_by_slot(ITEM_SLOT_HEAD) == src)
if(HAS_TRAIT_FROM(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT) && !struggling)
struggling = TRUE
var/fear_string
@@ -82,26 +82,26 @@
..()
/obj/item/reverse_bear_trap/attack(mob/living/target, mob/living/user)
if(target.get_item_by_slot(SLOT_HEAD))
if(target.get_item_by_slot(ITEM_SLOT_HEAD))
to_chat(user, "<span class='warning'>Remove [target.p_their()] headgear first!</span>")
return
target.visible_message("<span class='warning'>[user] starts forcing [src] onto [target]'s head!</span>", \
"<span class='userdanger'>[target] starts forcing [src] onto your head!</span>", "<i>You hear clanking.</i>")
to_chat(user, "<span class='danger'>You start forcing [src] onto [target]'s head...</span>")
if(!do_after(user, 30, target = target) || target.get_item_by_slot(SLOT_HEAD))
if(!do_after(user, 30, target = target) || target.get_item_by_slot(ITEM_SLOT_HEAD))
return
target.visible_message("<span class='warning'>[user] forces and locks [src] onto [target]'s head!</span>", \
"<span class='userdanger'>[target] locks [src] onto your head!</span>", "<i>You hear a click, and then a timer ticking down.</i>")
to_chat(user, "<span class='danger'>You force [src] onto [target]'s head and click the padlock shut.</span>")
user.dropItemToGround(src)
target.equip_to_slot_if_possible(src, SLOT_HEAD)
target.equip_to_slot_if_possible(src, ITEM_SLOT_HEAD)
arm()
notify_ghosts("[user] put a reverse bear trap on [target]!", source = src, action = NOTIFY_ORBIT, ghost_sound = 'sound/machines/beep.ogg')
/obj/item/reverse_bear_trap/proc/snap()
reset()
var/mob/living/carbon/human/H = loc
if(!istype(H) || H.get_item_by_slot(SLOT_HEAD) != src)
if(!istype(H) || H.get_item_by_slot(ITEM_SLOT_HEAD) != src)
visible_message("<span class='warning'>[src]'s jaws snap open with an ear-piercing crack!</span>")
playsound(src, 'sound/effects/snap.ogg', 75, TRUE)
else
@@ -116,13 +116,22 @@
/obj/item/reverse_bear_trap/proc/reset()
ticking = FALSE
update_appearance(UPDATE_OVERLAYS)
REMOVE_TRAIT(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT)
soundloop.stop()
soundloop2.stop()
STOP_PROCESSING(SSprocessing, src)
/obj/item/reverse_bear_trap/update_overlays()
. = ..()
if(ticking != TRUE)
return
/// note: this timer overlay increments one frame every second (to simulate a clock ticking). If you want to instead have it do a full cycle in a minute, set the 'delay' of each frame of the icon overlay to 75 rather than 10, and the worn overlay to twice that.
// . += "rbt_ticking"
/obj/item/reverse_bear_trap/proc/arm() //hulen
ticking = TRUE
update_appearance(UPDATE_OVERLAYS)
escape_chance = initial(escape_chance) //we keep these vars until re-arm, for tracking purposes
time_left = initial(time_left)
ADD_TRAIT(src, TRAIT_NODROP, REVERSE_BEAR_TRAP_TRAIT)
+7 -4
View File
@@ -325,7 +325,7 @@ GENETICS SCANNER
var/breathes = TRUE
var/blooded = TRUE
if(C.dna && C.dna.species)
if(HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
if(!HAS_TRAIT_FROM(C, TRAIT_AUXILIARY_LUNGS, SPECIES_TRAIT) && HAS_TRAIT_FROM(C, TRAIT_NOBREATH, SPECIES_TRAIT))
breathes = FALSE
if(NOBLOOD in C.dna.species.species_traits)
blooded = FALSE
@@ -380,6 +380,8 @@ GENETICS SCANNER
mutant = TRUE
else if (S.mutantstomach != initial(S.mutantstomach))
mutant = TRUE
else if (S.flying_species != initial(S.flying_species))
mutant = TRUE
msg += "\t<span class='info'>Reported Species: [H.spec_trait_examine_font()][H.dna.custom_species ? H.dna.custom_species : S.name]</font></span>\n"
msg += "\t<span class='info'>Base Species: [H.spec_trait_examine_font()][S.name]</font></span>\n"
@@ -434,12 +436,13 @@ GENETICS SCANNER
if(R)
blood_type = R.name
if((C.scan_blood_volume() + C.integrating_blood) <= (BLOOD_VOLUME_SAFE * C.blood_ratio) && (C.scan_blood_volume() + C.integrating_blood) > (BLOOD_VOLUME_OKAY*C.blood_ratio))
msg += "<span class='danger'>LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
msg += "<span class='danger'>LOW [HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "coolant" : "blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
else if((C.scan_blood_volume() + C.integrating_blood) <= (BLOOD_VOLUME_OKAY * C.blood_ratio))
msg += "<span class='danger'>CRITICAL blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
msg += "<span class='danger'>CRITICAL [HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "coolant" : "blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""].</span> <span class='info'>type: [blood_type]</span>\n"
else
msg += "<span class='info'>Blood level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]</span>\n"
msg += "<span class='info'>[HAS_TRAIT(C, TRAIT_ROBOTIC_ORGANISM) ? "Coolant" : "Blood"] level [blood_percent] %, [C.scan_blood_volume()] cl[C.integrating_blood? ", with [integrated_blood_percent] % of it integrating, [C.integrating_blood] cl " : ""]. type: [blood_type]</span>\n"
var/cyberimp_detect
@@ -208,7 +208,7 @@ effective or pretty fucking useless.
actions_types = list(/datum/action/item_action/toggle)
/obj/item/shadowcloak/ui_action_click(mob/user)
if(user.get_item_by_slot(SLOT_BELT) == src)
if(user.get_item_by_slot(ITEM_SLOT_BELT) == src)
if(!on)
Activate(usr)
else
@@ -216,7 +216,7 @@ effective or pretty fucking useless.
return
/obj/item/shadowcloak/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_BELT)
if(slot == ITEM_SLOT_BELT)
return 1
/obj/item/shadowcloak/proc/Activate(mob/living/carbon/human/user)
@@ -238,11 +238,11 @@ effective or pretty fucking useless.
/obj/item/shadowcloak/dropped(mob/user)
..()
if(user && user.get_item_by_slot(SLOT_BELT) != src)
if(user && user.get_item_by_slot(ITEM_SLOT_BELT) != src)
Deactivate()
/obj/item/shadowcloak/process()
if(user.get_item_by_slot(SLOT_BELT) != src)
if(user.get_item_by_slot(ITEM_SLOT_BELT) != src)
Deactivate()
return
var/turf/T = get_turf(src)
+1 -1
View File
@@ -6,7 +6,7 @@
// 1) Tennis balls, lines 39 - 99
// 2) Chew bones, lines 101 - 138
// 3) Frisbee, lines 140 - 166
*/
*/
/obj/item/toy/fluff
name = "Fluff Item"
+1 -1
View File
@@ -336,7 +336,7 @@
var/obj/item/clothing/magichead = new /obj/item/clothing/mask/horsehead/cursed(user.drop_location())
if(!user.dropItemToGround(user.wear_mask))
qdel(user.wear_mask)
user.equip_to_slot_if_possible(magichead, SLOT_WEAR_MASK, TRUE, TRUE)
user.equip_to_slot_if_possible(magichead, ITEM_SLOT_MASK, TRUE, TRUE)
qdel(src)
else
to_chat(user,"<span class='notice'>I say thee neigh</span>") //It still lives here
@@ -29,7 +29,7 @@
if(target.mind.has_antag_datum(ANTAG_DATUM_VASSAL))
SSticker.mode.remove_vassal(target.mind)
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || target.mind.unconvertable || target.mind.has_antag_datum(/datum/antagonist/gang/boss))
if(target.mind.has_antag_datum(/datum/antagonist/rev/head) || (target.mind.unconvertable))
if(!silent)
target.visible_message("<span class='warning'>[target] seems to resist the implant!</span>", "<span class='warning'>You feel something interfering with your mental conditioning, but you resist it!</span>")
var/obj/item/implanter/I = loc
@@ -20,4 +20,7 @@
imp_type = /obj/item/implant/uplink/precharged
/obj/item/implant/uplink/precharged
starting_tc = 10
starting_tc = TELECRYSTALS_PRELOADED_IMPLANT
/obj/item/implant/uplink/starting
starting_tc = TELECRYSTALS_DEFAULT - UPLINK_IMPLANT_TELECRYSTAL_COST
@@ -189,5 +189,7 @@
return FALSE
brainwash(C, objective)
message_admins("[ADMIN_LOOKUPFLW(user)] brainwashed [key_name_admin(C)] with objective '[objective]'.")
user.log_message("has brainwashed [key_name(C)] with the objective '[objective]' using \the [src]", LOG_ATTACK)
C.log_message("has been brainwashed with the objective '[objective]' by [key_name(user)] using \the [src]", LOG_VICTIM, log_globally = FALSE)
log_game("[key_name(user)] brainwashed [key_name(C)] with objective '[objective]'.")
return TRUE
+1 -1
View File
@@ -188,7 +188,7 @@
icon_state = "inducer-combat"
item_state = "inducer-combat"
w_class = WEIGHT_CLASS_BULKY
slot_flags = SLOT_BELT
slot_flags = ITEM_SLOT_BELT
desc = "A tool for inductively charging internal power cells. This one has been modified and upgraded to be able to charge into guns as well as normal electronics."
cell_type = /obj/item/stock_parts/cell/hyper
powertransfer = 1300
+1 -1
View File
@@ -193,7 +193,7 @@
/obj/item/melee/transforming/energy/sword/saber
possible_colors = list("red" = LIGHT_COLOR_RED, "blue" = LIGHT_COLOR_LIGHT_CYAN, "green" = LIGHT_COLOR_GREEN, "purple" = LIGHT_COLOR_LAVENDER)
unique_reskin = list("Sword" = "sword0", "saber" = "esaber0")
unique_reskin = list("Sword" = "sword0", "Saber" = "esaber0")
var/hacked = FALSE
var/saber = FALSE
@@ -34,6 +34,7 @@
/obj/item/target/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(pinnedLoc)
pinnedLoc.removeTarget(user)
. = ..()
/obj/item/target/syndicate
icon_state = "target_s"
@@ -488,6 +488,9 @@
icon_state = "nanogel"
var/being_applied = FALSE //No doafter stacking.
/obj/item/stack/medical/nanogel/one
amount = 1
/obj/item/stack/medical/nanogel/try_heal(mob/living/M, mob/user, silent = FALSE)
if(being_applied)
to_chat(user, "<span class='warning'>You are already applying [src]!</span>")
+6 -2
View File
@@ -53,7 +53,6 @@
desc = "A satchel that opens into a localized pocket of Blue Space."
icon_state = "holdingsat"
item_state = "holdingsat"
species_exception = list(/datum/species/angel)
/obj/item/storage/backpack/holding/duffel
name = "duffel bag of holding"
@@ -190,7 +189,6 @@
name = "satchel"
desc = "A trendy looking satchel."
icon_state = "satchel-norm"
species_exception = list(/datum/species/angel) //satchels can be equipped since they are on the side, not back
/obj/item/storage/backpack/satchel/leather
name = "leather satchel"
@@ -657,3 +655,9 @@
desc = "Worn by snails as armor and storage compartment."
icon_state = "snailshell"
item_state = "snailshell"
/obj/item/storage/backpack/henchmen
name = "wings"
desc = "Granted to the henchmen who deserve it. This probably doesn't include you."
icon_state = "henchmen"
item_state = "henchmen"
+5 -5
View File
@@ -108,7 +108,7 @@
desc = "This little bugger can be used to store and transport ores."
icon = 'icons/obj/mining.dmi'
icon_state = "satchel"
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_POCKETS
w_class = WEIGHT_CLASS_NORMAL
component_type = /datum/component/storage/concrete/stack
var/spam_protection = FALSE //If this is TRUE, the holder won't receive any messages when they fail to pick up ore through crossing it
@@ -400,7 +400,7 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "bag"
desc = "A bag for storing pills, patches, and bottles."
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKETS
resistance_flags = FLAMMABLE
/obj/item/storage/bag/chemistry/ComponentInitialize()
@@ -420,7 +420,7 @@
icon = 'icons/obj/chemical.dmi'
icon_state = "biobag"
desc = "A bag for the safe transportation and disposal of biowaste and other biological materials."
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_BELT|ITEM_SLOT_POCKETS
resistance_flags = FLAMMABLE
/obj/item/storage/bag/bio/ComponentInitialize()
@@ -451,7 +451,7 @@
desc = "A pouch for your ammo that goes in your pocket."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "ammopouch"
slot_flags = ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_POCKETS
w_class = WEIGHT_CLASS_BULKY
resistance_flags = FLAMMABLE
@@ -472,7 +472,7 @@ Bag for holding materials
desc = "A pouch for sheets and RCD ammunition that manages to hang where you would normally put things in your pocket."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "materialpouch"
slot_flags = ITEM_SLOT_POCKET
slot_flags = ITEM_SLOT_POCKETS
w_class = WEIGHT_CLASS_BULKY
resistance_flags = FLAMMABLE
+1 -1
View File
@@ -73,7 +73,7 @@ GLOBAL_LIST_INIT(bibleitemstates, list("bible", "koran", "scrapbook", "bible",
var/mob/living/carbon/human/H = usr
H.dna.add_mutation(CLOWNMUT)
H.dna.add_mutation(SMILE)
H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), SLOT_WEAR_MASK)
H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/clown_hat(H), ITEM_SLOT_MASK)
GLOB.bible_icon_state = icon_state
GLOB.bibleitemstates = item_state
+2 -2
View File
@@ -248,10 +248,10 @@
return ..()
var/obj/item/clothing/mask/cigarette/cig = locate(/obj/item/clothing/mask/cigarette) in contents
if(cig)
if(!user.wear_mask && !(SLOT_WEAR_MASK in M.check_obscured_slots()))
if(!user.wear_mask && !(ITEM_SLOT_MASK in M.check_obscured_slots()))
var/obj/item/clothing/mask/cigarette/W = cig
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_TAKE, W, M)
M.equip_to_slot_if_possible(W, SLOT_WEAR_MASK)
M.equip_to_slot_if_possible(W, ITEM_SLOT_MASK)
contents -= W
to_chat(user, "<span class='notice'>You take \a [W] out of the pack.</span>")
else
+539
View File
@@ -0,0 +1,539 @@
/// doing nothing/orbiting idly
#define STATE_IDLE 0
/// performing reset animation
#define STATE_RESET 1
/// performing attack animation
#define STATE_ATTACK 2
/// performing animation between attacks
#define STATE_RECOVER 3
/**
* Simple summon weapon code in this file
*
* tl;dr latch onto target, repeatedly proc attacks, animate using transforms,
* no real hitboxes/collisions, think of /datum/component/orbit-adjacent
*/
/obj/item/summon
name = "a horrifying mistake"
desc = "Why does this exist?"
/// datum type
var/host_type
/// number of summons
var/summon_count = 6
/// how long it takes for a "stack" to fall off by itself
var/stack_duration = 5 SECONDS
/// our summon weapon host
var/datum/summon_weapon_host/host
/// range summons will chase to
var/range = 7
/// are we a ranged weapon?
var/melee_only = TRUE
/obj/item/summon/Initialize()
. = ..()
if(host_type)
host = new host_type(src, summon_count, range)
/obj/item/summon/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
if(!host)
return
if(!proximity_flag && melee_only)
return
Target(target)
/obj/item/summon/dropped(mob/user, silent)
. = ..()
addtimer(CALLBACK(src, .proc/check_activation), 0, TIMER_UNIQUE)
/obj/item/summon/equipped(mob/user, slot)
. = ..()
addtimer(CALLBACK(src, .proc/check_activation), 0, TIMER_UNIQUE)
/obj/item/summon/proc/check_activation()
if(!host)
return
if(!isliving(loc))
host.SetMaster(null)
var/mob/living/L = loc
if(!istype(L))
return
if(!L.is_holding(src))
host.SetMaster(src)
host.Suppress()
host.SetMaster(L)
host.Wake()
/obj/item/summon/proc/Target(atom/victim)
if(!host?.CheckTarget(victim))
return
host.AutoTarget(victim, stack_duration)
/obj/item/summon/sword
name = "spectral blade"
desc = "An eldritch blade that summons phantasms to attack one's enemies."
icon = 'icons/obj/items_and_weapons.dmi'
icon_state = "spectral"
item_state = "spectral"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
host_type = /datum/summon_weapon_host/sword
force = 15
sharpness = SHARP_EDGED
/**
* Serves as the master datum for summon weapons
*/
/datum/summon_weapon_host
/// master atom
var/atom/master
/// suppressed?
var/active = TRUE
/// actual projectiles
var/list/datum/summon_weapon/controlled
/// active projectiles - refreshing a projectile reorders the list, so if they all have the same stack durations, you can trust the list to have last-refreshed at [1]
var/list/datum/summon_weapon/attacking
/// idle projectiles
var/list/datum/summon_weapon/idle
/// projectile type
var/weapon_type
/// default stack time
var/stack_time = 5 SECONDS
/// range
var/range = 7
/datum/summon_weapon_host/New(atom/master, count, range)
src.master = master
src.range = range
controlled = list()
attacking = list()
idle = list()
Create(count)
/datum/summon_weapon_host/Destroy()
QDEL_LIST(controlled)
master = null
return ..()
/datum/summon_weapon_host/proc/SetMaster(atom/master, reset_on_failure = TRUE)
var/changed = src.master != master
src.master = master
if(changed)
for(var/datum/summon_weapon/weapon as anything in idle)
weapon.Reset()
if(!master && reset_on_failure)
for(var/datum/summon_weapon/weapon as anything in attacking)
weapon.Reset()
/datum/summon_weapon_host/proc/Create(count)
if(!weapon_type)
return
for(var/i in 1 to min(count, clamp(20 - controlled.len - count, 0, 20)))
var/datum/summon_weapon/weapon = new weapon_type
Associate(weapon)
/datum/summon_weapon_host/proc/Associate(datum/summon_weapon/linking)
if(linking.host && linking.host != src)
linking.host.Disassociate(linking)
linking.host = src
controlled |= linking
linking.Reset()
/datum/summon_weapon_host/proc/Disassociate(datum/summon_weapon/unlinking, reset = TRUE, autodel)
if(unlinking.host == src)
unlinking.host = null
controlled -= unlinking
if(reset)
unlinking.Reset(del_no_host = autodel)
idle -= unlinking
attacking -= unlinking
/datum/summon_weapon_host/proc/AutoTarget(atom/victim, duration = stack_time)
if(!active)
return
var/datum/summon_weapon/weapon = (idle.len && idle[1]) || (attacking.len && attacking[1])
if(!weapon)
return
if(!CheckTarget(victim))
return
weapon.Target(victim)
if(duration)
weapon.ResetIn(duration)
/datum/summon_weapon_host/proc/OnTarget(datum/summon_weapon/weapon, atom/victim)
attacking -= weapon
idle -= weapon
attacking |= weapon
/datum/summon_weapon_host/proc/OnReset(datum/summon_weapon/weapon, atom/victim)
attacking -= weapon
idle |= weapon
/datum/summon_weapon_host/proc/CheckTarget(atom/victim)
if(isitem(victim))
return FALSE
if(QDELETED(victim))
return FALSE
if(victim == master)
return FALSE
if(isliving(victim))
var/mob/living/L = victim
if(L.stat == DEAD)
return FALSE
return TRUE
if(isobj(victim))
var/obj/O = victim
return (O.obj_flags & CAN_BE_HIT)
return FALSE
/datum/summon_weapon_host/proc/Suppress()
active = FALSE
for(var/datum/summon_weapon/weapon as anything in controlled)
weapon.Reset()
/datum/summon_weapon_host/proc/Wake()
active = TRUE
for(var/datum/summon_weapon/weapon as anything in controlled)
weapon.Reset()
/datum/summon_weapon_host/sword
weapon_type = /datum/summon_weapon/sword
/**
* A singular summoned object
*
* How summon weapons work:
*
* Reset() - makes it go back to its master.
* Target() - locks onto a target for a duration
*
* The biggest challenge is synchronizing animations.
* Variables keep track of when things tick, but,
* animations are client-timed, and not server-timed
*
* Animations:
* The weapon can only track its "intended" angle and dist
* "Current" pixel x/y are always calculated relative to a target from the current orbiting atom the physical effect is on
* There's 3 animations,
* MoveTo(location, angle, dist, rotation)
* Orbit(location)
* Rotate(degrees)
*
* And an non-animation that just snaps it to a location,
* HardReset(location)
*/
/datum/summon_weapon
/// name
var/name = "summoned weapon"
/// host
var/datum/summon_weapon_host/host
/// icon file
var/icon = 'icons/effects/summon.dmi'
/// icon state
var/icon_state
/// mutable_appearance to use, will skip making from icon/icon state if so
var/mutable_appearance/appearance
/// the actual effect
var/atom/movable/summon_weapon_effect/atom
/// currently locked attack target
var/atom/victim
/// current angle from victim - clockwise from 0. null if not attacking.
var/angle
/// current distance from victim - pixels
var/dist
/// current rotation - angles clockwise from north
var/rotation
/// rand dist to rotate during reattack phase
var/angle_vary = 45
/// orbit distance from victim - pixels
var/orbit_dist = 72
/// orbit distance variation from victim
var/orbit_dist_vary = 24
/// attack delay in deciseconds - this is time spent between attacks
var/attack_speed = 1.5
/// attack length in deciseconds - this is the attack animation speed in total
var/attack_length = 1.5
/// attack damage
var/attack_damage = 5
/// reset animation duration
var/reset_speed = 2
/// attack damtype
var/attack_type = BRUTE
/// attack sound
var/attack_sound = list(
'sound/weapons/bladeslice.ogg',
'sound/weapons/bladesliceb.ogg'
)
/// attack verb
var/attack_verb = list(
"rended",
"pierced",
"penetrated",
"sliced"
)
/// current state
var/state = STATE_IDLE
/// animation locked until
var/animation_lock
/// animation lock timer
var/animation_timerid
/// reset timerid
var/reset_timerid
/datum/summon_weapon/New(mutable_appearance/appearance_override)
if(appearance_override)
appearance = appearance_override
Setup()
attack_verb = typelist(NAMEOF(src, attack_verb), attack_verb)
attack_sound = typelist(NAMEOF(src, attack_sound), attack_sound)
/datum/summon_weapon/Destroy()
host.Disassociate(src, autodel = FALSE)
QDEL_NULL(atom)
QDEL_NULL(appearance)
return ..()
/datum/summon_weapon/proc/Setup()
atom = new
if(!appearance)
GenerateAppearance()
atom.appearance = appearance
atom.moveToNullspace()
if(host)
Reset()
/datum/summon_weapon/proc/GenerateAppearance()
if(!appearance)
appearance = new
appearance.icon = icon
appearance.icon_state = icon_state
appearance.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
appearance.opacity = FALSE
appearance.plane = GAME_PLANE
appearance.layer = ABOVE_MOB_LAYER
appearance.appearance_flags = KEEP_TOGETHER
appearance.overlays = list(
emissive_appearance(icon, icon_state)
)
/datum/summon_weapon/proc/Reset(immediate = FALSE, del_no_host = TRUE)
angle = null
victim = null
if(reset_timerid)
deltimer(reset_timerid)
reset_timerid = null
host?.OnReset(src)
atom.Release()
state = STATE_RESET
if(!host)
if(del_no_host)
qdel(src)
return
HardReset(null)
atom.moveToNullspace()
return
if(immediate)
if(animation_timerid)
deltimer(animation_timerid)
Act()
else
Wake()
/datum/summon_weapon/proc/ResetIn(ds)
reset_timerid = addtimer(CALLBACK(src, .proc/Reset), ds, TIMER_STOPPABLE)
/datum/summon_weapon/proc/Target(atom/victim)
if(!istype(victim) || !isturf(victim.loc) || (host && !host.CheckTarget(victim)))
Reset()
return
src.victim = victim
host.OnTarget(src, victim)
state = STATE_ATTACK
Wake()
/datum/summon_weapon/proc/Wake()
if(!animation_timerid)
Act()
/datum/summon_weapon/proc/AnimationLock(duration)
if(animation_timerid)
deltimer(animation_timerid)
animation_timerid = addtimer(CALLBACK(src, .proc/Act), duration, TIMER_CLIENT_TIME | TIMER_STOPPABLE)
/datum/summon_weapon/proc/Act()
animation_timerid = null
switch(state)
if(STATE_IDLE)
return
if(STATE_ATTACK)
if(!isturf(victim.loc) || (host && !host.CheckTarget(victim)))
Reset(TRUE)
return
state = STATE_RECOVER
// register hit at the halfway mark
// we can do better math to approximate when the attack will hit but i'm too tired to bother
addtimer(CALLBACK(src, .proc/Hit, victim), attack_length / 2, TIMER_CLIENT_TIME)
// we need to approximate our incoming angle - again, better math exists but why bother
var/incoming_angle = angle
if(isturf(atom.loc) && (atom.loc != victim.loc))
incoming_angle = Get_Angle(atom.loc, victim.loc)
// pierce through target
// we do not want to turn while doing this so we pierce through them visually
incoming_angle += 180
var/outgoing_angle = SIMPLIFY_DEGREES(incoming_angle)
AnimationLock(MoveTo(victim, null, outgoing_angle, orbit_dist + rand(-orbit_dist_vary, orbit_dist_vary), outgoing_angle, attack_length))
if(STATE_RESET)
state = STATE_IDLE
if(!host || !host.active || !get_turf(host.master))
atom.moveToNullspace()
src.angle = null
src.dist = null
src.rotation = null
return
var/reset_angle = rand(0, 360)
AnimationLock(MoveTo(host.master, null, reset_angle, 30, 90, reset_speed))
addtimer(CALLBACK(src, .proc/Orbit, host.master, reset_angle, 30, 3 SECONDS), reset_speed, TIMER_CLIENT_TIME)
if(STATE_RECOVER)
state = STATE_ATTACK
AnimationLock(Rotate(rand(-angle_vary, angle_vary), attack_speed, null))
/datum/summon_weapon/proc/Hit(atom/victim)
if(!isobj(victim) && !isliving(victim))
return FALSE
if(isliving(victim))
var/mob/living/L = victim
L.apply_damage(attack_damage, attack_type)
playsound(victim, pick(attack_sound), 75)
else if(isobj(victim))
var/obj/O = victim
O.take_damage(attack_damage, attack_type)
return TRUE
/**
* relative to defaults to current location
*/
/datum/summon_weapon/proc/MoveTo(atom/destination, atom/relative_to, angle = 0, dist = 64, rotation = 180, time)
. = time
// construct final transform
var/matrix/dest = ConstructMatrix(angle, dist, rotation)
// move to
atom.Lock(destination)
// get relative first positions
relative_to = get_turf(relative_to || atom.locked)
destination = get_turf(destination)
// if none, move to immediately and end
if(!relative_to)
atom.transform = dest
src.angle = angle
src.dist = dist
src.rotation = rotation
// end animations
animate(atom, time = 0, flags = ANIMATION_END_NOW)
return 0
// grab source
var/rel_x = (destination.x - relative_to.x) * world.icon_size + src.dist * sin(src.angle)
var/rel_y = (destination.y - relative_to.y) * world.icon_size + src.dist * cos(src.angle)
// construct source matrix
var/matrix/source = new
source.Turn((relative_to == get_turf(atom.locked))? src.rotation : Get_Angle(relative_to, destination))
source.Translate(rel_x, rel_y)
// set vars
src.angle = angle
src.dist = dist
src.rotation = rotation
// animate
atom.transform = source
animate(atom, transform = dest, time, FALSE, LINEAR_EASING, ANIMATION_LINEAR_TRANSFORM | ANIMATION_END_NOW)
/**
* rotation defaults to facing towards locked atom
*/
/datum/summon_weapon/proc/Rotate(degrees, time, rotation)
. = time
if(!dist)
return 0
var/matrix/M = ConstructMatrix(angle + degrees, dist, rotation || src.rotation)
if(rotation)
src.rotation = rotation
angle += degrees
animate(atom, transform = M, time, FALSE, LINEAR_EASING, ANIMATION_END_NOW | ANIMATION_LINEAR_TRANSFORM)
/datum/summon_weapon/proc/Orbit(atom/destination, initial_degrees = rand(0, 360), dist, speed)
. = 0
atom.Lock(destination)
animate(atom, 0, FALSE, flags = ANIMATION_END_NOW)
atom.transform = ConstructMatrix(initial_degrees, dist, 90)
atom.SpinAnimation(speed, parallel = FALSE, segments = 10)
// we can't predict dist/angle anymre because clienttime vs servertime.
// well, we can, but, let's not be bothered with timeofday math eh.
dist = 0
angle = 0
/datum/summon_weapon/proc/ConstructMatrix(angle = 0, dist = 64, rotation = 0)
var/matrix/M = new
M.Turn(rotation)
M.Translate(0, dist)
M.Turn(angle)
return M
/datum/summon_weapon/proc/HardReset(atom/snap_to)
if(animation_timerid)
deltimer(animation_timerid)
atom.Release()
atom.forceMove(snap_to)
atom.transform = null
/datum/summon_weapon/sword
name = "spectral blade"
icon_state = "sword"
attack_damage = 5
attack_speed = 1.5
attack_length = 1.5
/atom/movable/summon_weapon_effect
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
plane = GAME_PLANE
layer = ABOVE_MOB_LAYER
opacity = FALSE
density = FALSE
/// locked atom
var/atom/locked
/atom/movable/summon_weapon_effect/Destroy()
Release()
return ..()
/atom/movable/summon_weapon_effect/proc/Lock(atom/target)
if(locked == target)
return
if(locked)
Release()
if(!target)
return
locked = target
forceMove(locked.loc)
if(ismovable(locked))
RegisterSignal(locked, COMSIG_MOVABLE_MOVED, .proc/Update)
/atom/movable/summon_weapon_effect/proc/Release()
if(ismovable(locked))
UnregisterSignal(locked, COMSIG_MOVABLE_MOVED)
locked = null
/atom/movable/summon_weapon_effect/proc/Update()
if(!locked)
return
if(loc != locked.loc)
forceMove(locked.loc)
#undef STATE_IDLE
#undef STATE_ATTACK
#undef STATE_RECOVER
#undef STATE_RESET
+2 -2
View File
@@ -291,8 +291,8 @@
message_admins("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].")
log_game("Explosive tank rupture! Last key to touch the tank was [src.fingerprintslast].")
//Give the gas a chance to build up more pressure through reacting
air_contents.react(src)
air_contents.react(src)
for(var/i in 1 to TANK_POST_FRAGMENT_REACTIONS)
air_contents.react(src)
pressure = air_contents.return_pressure()
var/range = (pressure-TANK_FRAGMENT_PRESSURE)/TANK_FRAGMENT_SCALE
+19 -15
View File
@@ -58,7 +58,7 @@
/obj/item/watertank/equipped(mob/user, slot)
..()
if(slot != SLOT_BACK)
if(slot != ITEM_SLOT_BACK)
remove_noz()
/obj/item/watertank/proc/remove_noz()
@@ -271,18 +271,7 @@
if(resin_cooldown)
to_chat(user, "<span class='warning'>Resin launcher is still recharging...</span>")
return
resin_cooldown = TRUE
R.remove_any(100)
var/obj/effect/resin_container/A = new (get_turf(src))
log_game("[key_name(user)] used Resin Launcher at [AREACOORD(user)].")
playsound(src,'sound/items/syringeproj.ogg',40,1)
for(var/a=0, a<5, a++)
step_towards(A, target)
sleep(2)
A.Smoke()
spawn(100)
if(src)
resin_cooldown = FALSE
launch_resin(target, user)
return
if(nozzle_mode == RESIN_FOAM)
if(!Adj|| !isturf(target))
@@ -301,6 +290,21 @@
to_chat(user, "<span class='warning'>Resin foam mix is still being synthesized...</span>")
return
/obj/item/extinguisher/mini/nozzle/proc/launch_resin(atom/target, mob/user)
set waitfor = FALSE
resin_cooldown = TRUE
reagents.remove_any(100)
var/obj/effect/resin_container/A = new (get_turf(src))
log_game("[key_name(user)] used Resin Launcher at [AREACOORD(user)].")
playsound(src,'sound/items/syringeproj.ogg',40,1)
for(var/a=0, a<5, a++)
step_towards(A, target)
sleep(2)
A.Smoke()
spawn(100)
if(src)
resin_cooldown = FALSE
/obj/effect/resin_container
name = "resin container"
desc = "A compacted ball of expansive resin, used to repair the atmosphere in a room, or seal off breaches."
@@ -344,14 +348,14 @@
toggle_injection()
/obj/item/reagent_containers/chemtank/item_action_slot_check(slot, mob/user, datum/action/A)
if(slot == SLOT_BACK)
if(slot == ITEM_SLOT_BACK)
return 1
/obj/item/reagent_containers/chemtank/proc/toggle_injection()
var/mob/living/carbon/human/user = usr
if(!istype(user))
return
if (user.get_item_by_slot(SLOT_BACK) != src)
if (user.get_item_by_slot(ITEM_SLOT_BACK) != src)
to_chat(user, "<span class='warning'>The chemtank needs to be on your back before you can activate it!</span>")
return
if(on)
@@ -46,6 +46,8 @@
create_reagents(max_fuel)
reagents.add_reagent(/datum/reagent/fuel, max_fuel)
update_icon()
if(can_off_process)
START_PROCESSING(SSobj, src)
/obj/item/weldingtool/ComponentInitialize()
. = ..()
+117 -3
View File
@@ -1105,7 +1105,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
icon_state = "madeyoulook"
force = 0
throwforce = 0
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
item_flags = DROPDEL | ABSTRACT | HAND_ITEM
attack_verb = list("bopped")
/obj/item/circlegame/Initialize()
@@ -1207,7 +1207,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
item_state = "nothing"
force = 0
throwforce = 0
item_flags = DROPDEL | ABSTRACT // | HAND_ITEM
item_flags = DROPDEL | ABSTRACT | HAND_ITEM
attack_verb = list("slapped")
hitsound = 'sound/effects/snap.ogg'
@@ -1227,6 +1227,120 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
return FALSE
return TRUE
/obj/item/slapper/on_offered(mob/living/carbon/offerer)
. = TRUE
if(!(locate(/mob/living/carbon) in orange(1, offerer)))
visible_message(span_danger("[offerer] raises [offerer.p_their()] arm, looking around for a high-five, but there's no one around!"), \
span_warning("You post up, looking for a high-five, but finding no one within range!"), null, 2)
return
offerer.visible_message(span_notice("[offerer] raises [offerer.p_their()] arm, looking for a high-five!"), \
span_notice("You post up, looking for a high-five!"), null, 2)
offerer.apply_status_effect(STATUS_EFFECT_OFFERING, src, /atom/movable/screen/alert/give/highfive)
/// Yeah broh! This is where we do the high-fiving (or high-tenning :o)
/obj/item/slapper/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
. = TRUE
var/open_hands_taker
var/slappers_giver
for(var/i in taker.held_items) // see how many hands the taker has open for high'ing
if(isnull(i))
open_hands_taker++
if(!open_hands_taker)
to_chat(taker, span_warning("You can't high-five [offerer] with no open hands!"))
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five_full_hand) // not so successful now!
return
for(var/i in offerer.held_items)
var/obj/item/slapper/slap_check = i
if(istype(slap_check))
slappers_giver++
if(slappers_giver >= 2) // we only check this if it's already established the taker has 2+ hands free
offerer.visible_message(span_notice("[taker] enthusiastically high-tens [offerer]!"), span_nicegreen("Wow! You're high-tenned [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
to_chat(taker, span_nicegreen("You give high-tenning [offerer] your all!"))
playsound(offerer, 'sound/weapons/slap.ogg', 100, TRUE, 1)
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_ten)
else
offerer.visible_message(span_notice("[taker] high-fives [offerer]!"), span_nicegreen("All right! You're high-fived by [taker]!"), span_hear("You hear a sickening sound of flesh hitting flesh!"), ignored_mobs=taker)
to_chat(taker, span_nicegreen("You high-five [offerer]!"))
playsound(offerer, 'sound/weapons/slap.ogg', 50, TRUE, -1)
SEND_SIGNAL(offerer, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
SEND_SIGNAL(taker, COMSIG_ADD_MOOD_EVENT, "high_five", /datum/mood_event/high_five)
qdel(src)
/// Gangster secret handshakes.
/obj/item/slapper/secret_handshake
name = "Secret Handshake"
icon_state = "recruit"
icon = 'icons/obj/gang/actions.dmi'
/// References the active families gamemode handler (if one exists), for adding new family members to.
var/datum/gang_handler/handler
/// The typepath of the gang antagonist datum that the person who uses the package should have added to them -- remember that the distinction between e.g. Ballas and Grove Street is on the antag datum level, not the team datum level.
var/gang_to_use
/// The team datum that the person who uses this package should be added to.
var/datum/team/gang/team_to_use
/// Adds the user to the family that this package corresponds to, dispenses the free_clothes of that family, and adds them to the handler if it exists.
/obj/item/slapper/secret_handshake/proc/add_to_gang(mob/living/user, original_name)
var/datum/antagonist/gang/swappin_sides = new gang_to_use()
swappin_sides.original_name = original_name
swappin_sides.handler = handler
user.mind.add_antag_datum(swappin_sides, team_to_use)
var/policy = get_policy(ROLE_FAMILIES)
if(policy)
to_chat(user, policy)
team_to_use.add_member(user.mind)
swappin_sides.equip_gangster_in_inventory()
if (!isnull(handler) && !handler.gangbangers.Find(user.mind)) // if we have a handler and they're not tracked by it
handler.gangbangers += user.mind
/// Checks if the user is trying to use the package of the family they are in, and if not, adds them to the family, with some differing processing depending on whether the user is already a family member.
/obj/item/slapper/secret_handshake/proc/attempt_join_gang(mob/living/user)
if(!user?.mind)
return
var/datum/antagonist/gang/is_gangster = user.mind.has_antag_datum(/datum/antagonist/gang)
var/real_name_backup = user.real_name
if(is_gangster)
if(is_gangster.my_gang == team_to_use)
return
real_name_backup = is_gangster.original_name
is_gangster.my_gang.remove_member(user.mind)
user.mind.remove_antag_datum(/datum/antagonist/gang)
add_to_gang(user, real_name_backup)
/obj/item/slapper/secret_handshake/on_offer_taken(mob/living/carbon/offerer, mob/living/carbon/taker)
. = TRUE
if (!(null in taker.held_items))
to_chat(taker, span_warning("You can't get taught the secret handshake if [offerer] has no free hands!"))
return
if(HAS_TRAIT(taker, TRAIT_MINDSHIELD))
to_chat(taker, "You attended a seminar on not signing up for a gang and are not interested.")
return
var/datum/antagonist/gang/is_gangster = taker.mind.has_antag_datum(/datum/antagonist/gang)
if(is_gangster?.starter_gangster)
if(is_gangster.my_gang == team_to_use)
to_chat(taker, "You started your family. You don't need to join it.")
return
to_chat(taker, "You started your family. You can't turn your back on it now.")
return
offerer.visible_message(span_notice("[taker] is taught the secret handshake by [offerer]!"), span_nicegreen("All right! You've taught the secret handshake to [taker]!"), span_hear("You hear a bunch of weird shuffling and flesh slapping sounds!"), ignored_mobs=taker)
to_chat(taker, span_nicegreen("You get taught the secret handshake by [offerer]!"))
var/datum/antagonist/gang/owner_gang_datum = offerer.mind.has_antag_datum(/datum/antagonist/gang)
handler = owner_gang_datum.handler
gang_to_use = owner_gang_datum.type
team_to_use = owner_gang_datum.my_gang
attempt_join_gang(taker)
qdel(src)
/obj/item/extendohand
name = "extendo-hand"
desc = "Futuristic tech has allowed these classic spring-boxing toys to essentially act as a fully functional hand-operated hand prosthetic."
@@ -1261,7 +1375,7 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
// attack_verb_simple = list("whack", "thwack", "wallop", "sock")
// icon = 'icons/obj/items_and_weapons.dmi'
// icon_state = "gohei"
// inhand_icon_state = "gohei"
// item_state = "gohei"
// lefthand_file = 'icons/mob/inhands/weapons/staves_lefthand.dmi'
// righthand_file = 'icons/mob/inhands/weapons/staves_righthand.dmi'
+12 -1
View File
@@ -238,7 +238,18 @@
/obj/get_dumping_location(datum/component/storage/source,mob/user)
return get_turf(src)
/obj/proc/CanAStarPass()
/**
* This proc is used for telling whether something can pass by this object in a given direction, for use by the pathfinding system.
*
* Trying to generate one long path across the station will call this proc on every single object on every single tile that we're seeing if we can move through, likely
* multiple times per tile since we're likely checking if we can access said tile from multiple directions, so keep these as lightweight as possible.
*
* Arguments:
* * ID- An ID card representing what access we have (and thus if we can open things like airlocks or windows to pass through them). The ID card's physical location does not matter, just the reference
* * to_dir- What direction we're trying to move in, relevant for things like directional windows that only block movement in certain directions
* * caller- The movable we're checking pass flags for, if we're making any such checks
**/
/obj/proc/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
. = !density
/obj/proc/check_uplink_validity()
+3 -1
View File
@@ -52,6 +52,8 @@
var/author_ckey
var/icon_generated = FALSE
var/icon/generated_icon
///boolean that blocks persistence from saving it. enabled from printing copies, because we do not want to save copies.
var/no_save = FALSE
// Painting overlay offset when framed
var/framed_offset_x = 11
@@ -370,7 +372,7 @@
update_icon()
/obj/structure/sign/painting/proc/save_persistent()
if(!persistence_id || !current_canvas)
if(!persistence_id || !current_canvas || current_canvas.no_save)
return
if(sanitize_filename(persistence_id) != persistence_id)
stack_trace("Invalid persistence_id - [persistence_id]")
@@ -531,7 +531,7 @@
/obj/structure/closet/get_remote_view_fullscreens(mob/user)
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1)
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 1)
/obj/structure/closet/emp_act(severity)
. = ..()
+1 -1
View File
@@ -397,7 +397,7 @@
data["owner_name"] = payments_acc.account_holder
if(showpiece)
data["product_name"] = capitalize(showpiece.name)
var/base64 = icon2base64(icon(showpiece.icon, showpiece.icon_state))
var/base64 = icon2base64(icon(showpiece.icon, showpiece.icon_state, SOUTH, 1))
data["product_icon"] = base64
data["registered"] = register
data["product_cost"] = sale_price
@@ -1,5 +1,5 @@
//Objects that spawn ghosts in as a certain role when they click on it, i.e. away mission bartenders.
#define spawnOverride TRUE
//Preserved terrarium/seed vault: Spawns in seed vault structures in lavaland. Ghosts become plantpeople and are advised to begin growing plants in the room near them.
/obj/effect/mob_spawn/human/seed_vault
name = "preserved terrarium"
@@ -36,6 +36,44 @@
//Ash walker eggs: Spawns in ash walker dens in lavaland. Ghosts become unbreathing lizards that worship the Necropolis and are advised to retrieve corpses to create more ash walkers.
/obj/structure/ash_walker_eggshell
name = "ash walker egg"
desc = "A man-sized yellow egg, spawned from some unfathomable creature. A humanoid silhouette lurks within. The egg shell looks resistant to temperature but otherwise rather brittle."
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
icon_state = "large_egg"
resistance_flags = LAVA_PROOF | FIRE_PROOF | FREEZE_PROOF
max_integrity = 80
var/obj/effect/mob_spawn/human/ash_walker/egg
/obj/structure/ash_walker_eggshell/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) //lifted from xeno eggs
switch(damage_type)
if(BRUTE)
if(damage_amount)
playsound(loc, 'sound/effects/attackblob.ogg', 100, TRUE)
else
playsound(src, 'sound/weapons/tap.ogg', 50, TRUE)
if(BURN)
if(damage_amount)
playsound(loc, 'sound/items/welder.ogg', 100, TRUE)
/obj/structure/ash_walker_eggshell/attack_ghost(mob/user) //Pass on ghost clicks to the mob spawner
if(egg)
egg.attack_ghost(user)
. = ..()
/obj/structure/ash_walker_eggshell/Destroy()
if(!egg)
return ..()
var/mob/living/carbon/human/yolk = new /mob/living/carbon/human/(get_turf(src))
yolk.fully_replace_character_name(null,random_unique_lizard_name(gender))
yolk.set_species(/datum/species/lizard/ashwalker)
yolk.underwear = "Nude"
yolk.equipOutfit(/datum/outfit/ashwalker)//this is an authentic mess we're making
yolk.update_body()
yolk.gib()
QDEL_NULL(egg)
return ..()
/obj/effect/mob_spawn/human/ash_walker
name = "ash walker egg"
desc = "A man-sized yellow egg, spawned from some unfathomable creature. A humanoid silhouette lurks within."
@@ -55,12 +93,25 @@
You have seen lights in the distance... they foreshadow the arrival of outsiders to your domain. \
Ensure your nest remains protected at all costs."
assignedrole = "Ash Walker"
var/datum/team/ashwalkers/team
var/obj/structure/ash_walker_eggshell/eggshell
/obj/effect/mob_spawn/human/ash_walker/Destroy()
eggshell = null
return ..()
/obj/effect/mob_spawn/human/ash_walker/allow_spawn(mob/user, silent = FALSE)
if(!(user.key in team.players_spawned) || spawnOverride)//one per person unless you get a bonus spawn
return TRUE
to_chat(user, span_warning("<b>You have exhausted your usefulness to the Necropolis</b>."))
return FALSE
/obj/effect/mob_spawn/human/ash_walker/special(mob/living/new_spawn)
new_spawn.real_name = random_unique_lizard_name(gender)
if(is_mining_level(z))
to_chat(new_spawn, "<b>Drag the corpses of men and beasts to your nest. It will absorb them to create more of your kind. Glory to the Necropolis!</b>")
to_chat(new_spawn, "<b>You can expand the weather proof area provided by your shelters by using the 'New Area' key near the bottom right of your HUD.</b>")
to_chat(new_spawn, "<b>Dragging injured ashwalkers to the tentacle or using the sleep verb next to it youself causes the body to remade whole after a short delay!</b>")
else
to_chat(new_spawn, "<span class='userdanger'>You have been born outside of your natural home! Whether you decide to return home, or make due with your new home is your own decision.</span>")
@@ -72,10 +123,18 @@
H.undershirt = "Nude"
H.socks = "Nude"
H.update_body()
new_spawn.mind.add_antag_datum(/datum/antagonist/ashwalker, team)
team.players_spawned += (new_spawn.key)
eggshell.egg = null
QDEL_NULL(eggshell)
/obj/effect/mob_spawn/human/ash_walker/Initialize(mapload)
/obj/effect/mob_spawn/human/ash_walker/Initialize(mapload, datum/team/ashwalkers/ashteam)
. = ..()
var/area/A = get_area(src)
team = ashteam
eggshell = new /obj/structure/ash_walker_eggshell(get_turf(loc))
eggshell.egg = src
src.forceMove(eggshell)
if(A)
notify_ghosts("An ash walker egg is ready to hatch in \the [A.name].", source = src, action=NOTIFY_ATTACK, flashwindow = FALSE, ignore_key = POLL_IGNORE_ASHWALKER, ignore_dnr_observers = TRUE)
+3 -4
View File
@@ -305,11 +305,10 @@
else
return 0
/obj/structure/girder/CanAStarPass(ID, dir, caller)
/obj/structure/girder/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
. = !density
if(ismovable(caller))
var/atom/movable/mover = caller
. = . || (mover.pass_flags & PASSGRILLE)
if(istype(caller))
. = . || (caller.pass_flags & PASSGRILLE)
/obj/structure/girder/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
+3 -4
View File
@@ -133,11 +133,10 @@
else
return !density
/obj/structure/grille/CanAStarPass(ID, dir, caller)
/obj/structure/grille/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
. = !density
if(ismovable(caller))
var/atom/movable/mover = caller
. = . || (mover.pass_flags & PASSGRILLE)
if(istype(caller))
. = . || (caller.pass_flags & PASSGRILLE)
/obj/structure/grille/attackby(obj/item/W, mob/user, params)
user.DelayNextAction(CLICK_CD_MELEE)
@@ -25,7 +25,7 @@
/obj/machinery/manned_turret/Destroy()
target = null
target_turf = null
..()
return ..()
//BUCKLE HOOKS
+4 -5
View File
@@ -139,7 +139,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
/obj/structure/bodycontainer/get_remote_view_fullscreens(mob/user)
if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2)
user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/scaled/impaired, 2)
/*
* Morgue
*/
@@ -371,8 +371,7 @@ GLOBAL_LIST_EMPTY(crematoriums)
else
return 0
/obj/structure/tray/m_tray/CanAStarPass(ID, dir, caller)
/obj/structure/tray/m_tray/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
. = !density
if(ismovable(caller))
var/atom/movable/mover = caller
. = . || (mover.pass_flags & PASSTABLE)
if(istype(caller))
. = . || (caller.pass_flags & PASSTABLE)
+81 -43
View File
@@ -1,3 +1,5 @@
#define MAX_NOTICES 5
/obj/structure/noticeboard
name = "notice board"
desc = "A board for pinning important notices upon."
@@ -7,8 +9,25 @@
density = FALSE
anchored = TRUE
max_integrity = 150
/// Current number of a pinned notices
var/notices = 0
/obj/structure/noticeboard/directional/north
dir = SOUTH
pixel_y = 32
/obj/structure/noticeboard/directional/south
dir = NORTH
pixel_y = -32
/obj/structure/noticeboard/directional/east
dir = WEST
pixel_x = 32
/obj/structure/noticeboard/directional/west
dir = EAST
pixel_x = -32
/obj/structure/noticeboard/Initialize(mapload)
. = ..()
@@ -16,7 +35,7 @@
return
for(var/obj/item/I in loc)
if(notices > 4)
if(notices >= MAX_NOTICES)
break
if(istype(I, /obj/item/paper))
I.forceMove(src)
@@ -27,67 +46,84 @@
/obj/structure/noticeboard/attackby(obj/item/O, mob/user, params)
if(istype(O, /obj/item/paper) || istype(O, /obj/item/photo))
if(!allowed(user))
to_chat(user, "<span class='info'>You are not authorized to add notices</span>")
to_chat(user, span_warning("You are not authorized to add notices!"))
return
if(notices < 5)
if(notices < MAX_NOTICES)
if(!user.transferItemToLoc(O, src))
return
notices++
icon_state = "nboard0[notices]"
to_chat(user, "<span class='notice'>You pin the [O] to the noticeboard.</span>")
to_chat(user, span_notice("You pin the [O] to the noticeboard."))
else
to_chat(user, "<span class='notice'>The notice board is full</span>")
to_chat(user, span_warning("The notice board is full!"))
else
return ..()
/obj/structure/noticeboard/interact(mob/user)
ui_interact(user)
/obj/structure/noticeboard/ui_state(mob/user)
return GLOB.physical_state
/obj/structure/noticeboard/ui_interact(mob/user)
/obj/structure/noticeboard/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "NoticeBoard", name)
ui.open()
/obj/structure/noticeboard/ui_data(mob/user)
var/list/data = list()
data["allowed"] = allowed(user)
data["items"] = list()
for(var/obj/item/content in contents)
var/list/content_data = list(
name = content.name,
ref = REF(content)
)
data["items"] += list(content_data)
return data
/obj/structure/noticeboard/ui_act(action, params)
. = ..()
var/auth = allowed(user)
var/dat = "<B>[name]</B><BR>"
for(var/obj/item/P in src)
if(istype(P, /obj/item/paper))
dat += "<A href='?src=[REF(src)];read=[REF(P)]'>[P.name]</A> [auth ? "<A href='?src=[REF(src)];write=[REF(P)]'>Write</A> <A href='?src=[REF(src)];remove=[REF(P)]'>Remove</A>" : ""]<BR>"
else
dat += "<A href='?src=[REF(src)];read=[REF(P)]'>[P.name]</A> [auth ? "<A href='?src=[REF(src)];remove=[REF(P)]'>Remove</A>" : ""]<BR>"
user << browse("<HEAD><TITLE>Notices</TITLE></HEAD>[dat]","window=noticeboard")
onclose(user, "noticeboard")
if(.)
return
/obj/structure/noticeboard/Topic(href, href_list)
..()
usr.set_machine(src)
if(href_list["remove"])
if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
return
var/obj/item/I = locate(href_list["remove"]) in contents
if(istype(I) && I.loc == src)
I.forceMove(usr.loc)
usr.put_in_hands(I)
notices--
icon_state = "nboard0[notices]"
var/obj/item/item = locate(params["ref"]) in contents
if(!istype(item) || item.loc != src)
return
if(href_list["write"])
if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
return
var/obj/item/P = locate(href_list["write"]) in contents
if(istype(P) && P.loc == src)
var/obj/item/I = usr.is_holding_item_of_type(/obj/item/pen)
if(I)
add_fingerprint(usr)
P.attackby(I, usr)
var/mob/user = usr
switch(action)
if("examine")
if(istype(item, /obj/item/paper))
item.ui_interact(user)
else
to_chat(usr, "<span class='notice'>You'll need something to write with!</span>")
user.examinate(item)
return TRUE
if("remove")
if(!allowed(user))
return
remove_item(item, user)
return TRUE
if(href_list["read"])
var/obj/item/I = locate(href_list["read"]) in contents
if(istype(I) && I.loc == src)
usr.examinate(I)
/**
* Removes an item from the notice board
*
* Arguments:
* * item - The item that is to be removed
* * user - The mob that is trying to get the item removed, if there is one
*/
/obj/structure/noticeboard/proc/remove_item(obj/item/item, mob/user)
item.forceMove(drop_location())
if(user)
user.put_in_hands(item)
balloon_alert(user, "removed from board")
notices--
icon_state = "nboard0[notices]"
/obj/structure/noticeboard/deconstruct(disassembled = TRUE)
if(!(flags_1 & NODECONSTRUCT_1))
new /obj/item/stack/sheet/metal (loc, 1)
for(var/obj/item/content in contents)
remove_item(content)
qdel(src)
// Notice boards for the heads of staff (plus the qm)
@@ -131,3 +167,5 @@
name = "Staff Notice Board"
desc = "Important notices from the heads of staff."
req_access = list(ACCESS_HEADS)
#undef MAX_NOTICES
+9 -9
View File
@@ -56,18 +56,18 @@
return FALSE
return TRUE
/obj/structure/plasticflaps/CanAStarPass(ID, to_dir, caller)
/obj/structure/plasticflaps/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
if(isliving(caller))
if(isbot(caller))
return 1
return TRUE
var/mob/living/M = caller
if(!(SEND_SIGNAL(M, COMSIG_CHECK_VENTCRAWL)) && M.mob_size != MOB_SIZE_TINY)
return 0
var/atom/movable/M = caller
if(M && M.pulling)
return CanAStarPass(ID, to_dir, M.pulling)
return 1 //diseases, stings, etc can pass
var/mob/living/living_caller = caller
if(!(SEND_SIGNAL(living_caller, COMSIG_CHECK_VENTCRAWL)) && living_caller.mob_size != MOB_SIZE_TINY)
return FALSE
if(caller?.pulling)
return CanAStarPass(ID, to_dir, caller.pulling)
return TRUE //diseases, stings, etc can pass
/obj/structure/plasticflaps/CanPass(atom/movable/A, turf/T)
if(istype(A) && (A.pass_flags & PASSGLASS))
+6 -8
View File
@@ -109,11 +109,10 @@
else
return !density
/obj/structure/table/CanAStarPass(ID, dir, caller)
/obj/structure/table/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
. = !density
if(ismovable(caller))
var/atom/movable/mover = caller
. = . || (mover.pass_flags & PASSTABLE)
if(istype(caller))
. = . || (caller.pass_flags & PASSTABLE)
/obj/structure/table/proc/tableplace(mob/living/user, mob/living/pushed_mob)
pushed_mob.forceMove(src.loc)
@@ -702,11 +701,10 @@
else
return 0
/obj/structure/rack/CanAStarPass(ID, dir, caller)
/obj/structure/rack/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
. = !density
if(ismovable(caller))
var/atom/movable/mover = caller
. = . || (mover.pass_flags & PASSTABLE)
if(istype(caller))
. = . || (caller.pass_flags & PASSTABLE)
/obj/structure/rack/MouseDrop_T(obj/O, mob/user)
. = ..()
+4 -7
View File
@@ -58,13 +58,10 @@
pinned_target.nullPinnedLoc()
nullPinnedTarget()
handle_density()
if(ishuman(user))
if(!user.get_active_held_item())
user.put_in_hands(pinned_target)
to_chat(user, "<span class='notice'>You take the target out of the stake.</span>")
else
pinned_target.forceMove(user.drop_location())
to_chat(user, "<span class='notice'>You take the target out of the stake.</span>")
pinned_target.forceMove(get_turf(src))
to_chat(user, "<span class='notice'>You take the target out of the stake.</span>")
if(user.can_hold_items())
user.put_in_hands(pinned_target)
/obj/structure/target_stake/bullet_act(obj/item/projectile/P)
if(pinned_target)
+2 -2
View File
@@ -257,7 +257,7 @@
/obj/machinery/shower/Initialize()
. = ..()
soundloop = new(list(src), FALSE)
soundloop = new(src, FALSE)
/obj/machinery/shower/Destroy()
QDEL_NULL(soundloop)
@@ -669,7 +669,7 @@
icon_state = "well_3"
return TRUE
else
to_chat(user, "<span class='warning'>You need at least tweenty-five pieces of sandstone!</span>")
to_chat(user, "<span class='warning'>You need at least twenty-five pieces of sandstone!</span>")
return
if(steps == 3 && S.tool_behaviour == TOOL_SHOVEL)
S.use_tool(src, user, 80, volume=100)
+6 -7
View File
@@ -90,13 +90,13 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
. = ..()
if(direct)
setDir(direct)
if(extra_reinforced && anchored)
state = PRWINDOW_SECURE
else if(reinf && anchored)
state = WINDOW_SCREWED_TO_FRAME
if(mapload && electrochromatic_id && electrochromatic_id[1] == "!")
electrochromatic_id = SSmapping.get_obfuscated_id(electrochromatic_id)
@@ -571,13 +571,13 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
/obj/structure/window/get_dumping_location(obj/item/storage/source,mob/user)
return null
/obj/structure/window/CanAStarPass(ID, to_dir)
/obj/structure/window/CanAStarPass(obj/item/card/id/ID, to_dir, atom/movable/caller)
if(!density)
return 1
return TRUE
if((dir == FULLTILE_WINDOW_DIR) || (dir == to_dir))
return 0
return FALSE
return 1
return TRUE
/obj/structure/window/GetExplosionBlock()
return reinf && fulltile ? real_explosion_block : 0
@@ -719,7 +719,6 @@ GLOBAL_LIST_EMPTY(electrochromatic_window_lookup)
icon = 'icons/obj/smooth_structures/rplasma_window.dmi'
icon_state = "rplasmawindow"
dir = FULLTILE_WINDOW_DIR
state = PRWINDOW_SECURE
max_integrity = 1000
fulltile = TRUE
flags_1 = PREVENT_CLICK_UNDER_1