Merge remote-tracking branch 'upstream/master' into cool-ipcs

This commit is contained in:
Timothy Teakettle
2020-09-16 05:12:20 +01:00
635 changed files with 23608 additions and 12861 deletions

View File

@@ -17,11 +17,15 @@
var/countdown_colour
var/obj/effect/countdown/anomaly/countdown
/obj/effect/anomaly/Initialize(mapload, new_lifespan)
/// chance we drop a core when neutralized
var/core_drop_chance = 100
/obj/effect/anomaly/Initialize(mapload, new_lifespan, core_drop_chance = 100)
. = ..()
GLOB.poi_list |= src
START_PROCESSING(SSobj, src)
impact_area = get_area(src)
src.core_drop_chance = core_drop_chance
if (!impact_area)
return INITIALIZE_HINT_QDEL
@@ -54,6 +58,8 @@
GLOB.poi_list.Remove(src)
STOP_PROCESSING(SSobj, src)
qdel(countdown)
if(aSignal)
QDEL_NULL(aSignal)
return ..()
/obj/effect/anomaly/proc/anomalyEffect()
@@ -70,12 +76,12 @@
/obj/effect/anomaly/proc/anomalyNeutralize()
new /obj/effect/particle_effect/smoke/bad(loc)
for(var/atom/movable/O in src)
O.forceMove(drop_location())
if(prob(core_drop_chance))
aSignal.forceMove(drop_location())
aSignal = null
qdel(src)
/obj/effect/anomaly/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_ANALYZER) //revert if runtimed
to_chat(user, "<span class='notice'>Analyzing... [src]'s unstable field is fluctuating along frequency [format_frequency(aSignal.frequency)], code [aSignal.code].</span>")
@@ -285,7 +291,7 @@
S.rabid = TRUE
S.amount_grown = SLIME_EVOLUTION_THRESHOLD
S.Evolve()
offer_control(S)
offer_control(S,POLL_IGNORE_SENTIENCE_POTION)
/////////////////////

View File

@@ -46,4 +46,5 @@
var/turf/T = loc
if(!istype(T)) //you know this will happen somehow
CRASH("Turf decal initialized in an object/nullspace")
T.AddElement(/datum/element/decal, icon, icon_state, turn(dir, -dir2angle(T.dir)), CLEAN_GOD, color, null, null, alpha)
var/turn_dir = 180 - dir2angle(T.dir) //Turning a dir by 0 results in a roulette of random dirs.
T.AddElement(/datum/element/decal, icon, icon_state, turn_dir ? turn(dir, turn_dir) : dir, CLEAN_GOD, color, null, null, alpha)

View File

@@ -242,7 +242,6 @@ RLD
var/datum/browser/popup = new(user, "rcd_access", "Access Control", 900, 500, src)
popup.set_content(t1)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.open()
/obj/item/construction/rcd/Topic(href, href_list)

View File

@@ -28,39 +28,37 @@
/// triggered on wield of two handed item
/obj/item/broom/proc/on_wield(obj/item/source, mob/user)
to_chat(user, "<span class='notice'>You brace the [src] against the ground in a firm sweeping stance.</span>")
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/sweep)
RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, .proc/sweep)
/// triggered on unwield of two handed item
/obj/item/broom/proc/on_unwield(obj/item/source, mob/user)
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE)
/obj/item/broom/afterattack(atom/A, mob/user, proximity)
. = ..()
if(!proximity)
return
sweep(user, A, FALSE)
sweep(user, A)
/obj/item/broom/proc/sweep(mob/user, atom/A, moving = TRUE)
var/turf/target
if (!moving)
if (isturf(A))
target = A
else
target = A.loc
else
target = user.loc
if (!isturf(target))
/obj/item/broom/proc/sweep(datum/source, atom/newLoc)
if(!ismob(source) || !isturf(newLoc) || (get_dist(source, newLoc) > 1))
return
if (locate(/obj/structure/table) in target.contents)
var/turf/target = newLoc
var/atom/movable/AM
var/sweep_dir = get_dir(source, target)
if(!sweep_dir)
return
for(var/i in target.contents)
AM = i
if(AM.density) // eh good enough heuristic check
return
var/i = 0
for(var/obj/item/garbage in target.contents)
if(!garbage.anchored)
garbage.Move(get_step(target, user.dir), user.dir)
i++
if(i >= 20)
step(garbage, sweep_dir)
if(++i > 20)
break
if(i >= 1)
if(i)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 30, TRUE, -1)
/obj/item/broom/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) //bless you whoever fixes this copypasta

View File

@@ -13,6 +13,7 @@
var/ignores_timeout = FALSE
var/response_timer_id = null
var/approval_time = 600
var/allow_unicode = FALSE
var/static/regex/standard_station_regex
@@ -48,6 +49,9 @@
if(!new_name)
return
if(!allow_unicode && (length(new_name) != length_char(new_name)))
to_chat(user, "Unicode is not allowed. Adminhelp if you want to use it so badly.")
return
log_game("[key_name(user)] has proposed to name the station as \
[new_name]")

View File

@@ -390,7 +390,7 @@
/obj/item/circuitboard/machine/thermomachine/examine()
. = ..()
. += "<span class='notice'>It is set to layer [pipe_layer].</span>"
. += "<span class='notice'>It is set to layer [pipe_layer]. Use a Multitool on the circuit to change this.</span>"
/obj/item/circuitboard/machine/thermomachine/heater
name = "Heater (Machine Board)"
@@ -1146,3 +1146,8 @@
build_path = /obj/machinery/atmospherics/components/unary/shuttle/heater
req_components = list(/obj/item/stock_parts/micro_laser = 2,
/obj/item/stock_parts/matter_bin = 1)
/obj/item/circuitboard/machine/explosive_compressor
name = "Explosive Compressor (Machine Board)"
build_path = /obj/machinery/research/explosive_compressor
req_components = list(/obj/item/stock_parts/matter_bin = 3)

View File

@@ -735,7 +735,11 @@
if(isobj(target))
if(actually_paints)
var/list/hsl = rgb2hsl(hex2num(copytext(paint_color,2,4)),hex2num(copytext(paint_color,4,6)),hex2num(copytext(paint_color,6,8)))
if(hsl[3] < 0.25 && !istype(target, /obj/structure/window) && !istype(target, /obj/effect/decal/cleanable/crayon)) //Colors too dark are rejected
var/static/whitelisted = typecacheof(list(/obj/structure/window,
/obj/effect/decal/cleanable/crayon,
/obj/machinery/door/window)
)
if(hsl[3] < 0.25 && !whitelisted[target]) //Colors too dark are rejected
to_chat(usr, "<span class='warning'>A color that dark on an object like this? Surely not...</span>")
return FALSE

View File

@@ -394,8 +394,6 @@
to_chat(user, "<span class='warning'>[src] are recharging!</span>")
return
user.stop_pulling() //User has hands full, and we don't care about anyone else pulling on it, their problem. CLEAR!!
if(user.a_intent == INTENT_DISARM)
do_disarm(M, user)
return
@@ -447,8 +445,7 @@
if(do_after(user, isnull(defib?.disarm_shock_time)? disarm_shock_time : defib.disarm_shock_time, target = M))
M.visible_message("<span class='danger'>[user] zaps [M] with [src]!</span>", \
"<span class='userdanger'>[user] zaps [M] with [src]!</span>")
M.adjustStaminaLoss(50)
M.DefaultCombatKnockdown(100)
M.DefaultCombatKnockdown(140)
M.updatehealth() //forces health update before next life tick
playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1)
M.emote("gasp")

View File

@@ -266,7 +266,7 @@ GLOBAL_LIST_EMPTY(PDAs)
var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/simple/pda)
assets.send(user)
var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/goonchat)
var/datum/asset/spritesheet/emoji_s = get_asset_datum(/datum/asset/spritesheet/chat)
emoji_s.send(user) //Already sent by chat but no harm doing this
user.set_machine(src)

View File

@@ -590,7 +590,7 @@ Code:
var/static/list/emoji_icon_states
var/static/emoji_table
if(!emoji_table)
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/chat)
var/list/collate = list("<br><table>")
for(var/emoji in sortList(icon_states(icon('icons/emoji.dmi'))))
var/tag = sheet.icon_tag("emoji-[emoji]")

View File

@@ -0,0 +1,207 @@
/obj/item/storage/portable_chem_mixer
name = "Portable Chemical Mixer"
desc = "A portable device that dispenses and mixes chemicals. All necessary reagents need to be supplied with beakers. A label indicates that a screwdriver is required to open it for refills. This device can be worn on a belt. The letters 'S&T' are imprinted on the side."
icon = 'icons/obj/chemical.dmi'
icon_state = "portablechemicalmixer_open"
w_class = WEIGHT_CLASS_HUGE
slot_flags = ITEM_SLOT_BELT
custom_price = 2000
custom_premium_price = 2000
var/obj/item/reagent_containers/beaker = null ///Creating an empty slot for a beaker that can be added to dispense into
var/amount = 30 ///The amount of reagent that is to be dispensed currently
var/list/dispensable_reagents = list() ///List in which all currently dispensable reagents go
/obj/item/storage/portable_chem_mixer/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_combined_w_class = 200
STR.max_items = 50
STR.insert_preposition = "in"
STR.can_hold = typecacheof(list(
/obj/item/reagent_containers/glass/beaker,
))
/obj/item/storage/portable_chem_mixer/Destroy()
QDEL_NULL(beaker)
return ..()
/obj/item/storage/portable_chem_mixer/ex_act(severity, target)
if(severity < 3)
..()
/obj/item/storage/portable_chem_mixer/attackby(obj/item/I, mob/user, params)
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if (I.tool_behaviour == TOOL_SCREWDRIVER)
SEND_SIGNAL(src, COMSIG_TRY_STORAGE_SET_LOCKSTATE, !locked)
if (!locked)
update_contents()
if (locked)
replace_beaker(user)
update_icon()
I.play_tool_sound(src, 50)
return
else if (istype(I, /obj/item/reagent_containers) && !(I.item_flags & ABSTRACT) && I.is_open_container() && locked)
var/obj/item/reagent_containers/B = I
. = TRUE //no afterattack
if(!user.transferItemToLoc(B, src))
return
replace_beaker(user, B)
update_icon()
updateUsrDialog()
return
return ..()
/**
* Updates the contents of the portable chemical mixer
*
* A list of dispensable reagents is created by iterating through each source beaker in the portable chemical beaker and reading its contents
*/
/obj/item/storage/portable_chem_mixer/proc/update_contents()
dispensable_reagents.Cut()
for (var/obj/item/reagent_containers/glass/beaker/B in contents)
var/key = B.reagents.get_master_reagent_id()
if (!(key in dispensable_reagents))
dispensable_reagents[key] = list()
dispensable_reagents[key]["reagents"] = list()
dispensable_reagents[key]["reagents"] += B.reagents
return
/obj/item/storage/portable_chem_mixer/update_icon_state()
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if (!locked)
icon_state = "portablechemicalmixer_open"
else if (beaker)
icon_state = "portablechemicalmixer_full"
else
icon_state = "portablechemicalmixer_empty"
/obj/item/storage/portable_chem_mixer/AltClick(mob/living/user)
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if (!locked)
return ..()
if(!can_interact(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
return
replace_beaker(user)
update_icon()
/**
* Replaces the beaker of the portable chemical mixer with another beaker, or simply adds the new beaker if none is in currently
*
* Checks if a valid user and a valid new beaker exist and attempts to replace the current beaker in the portable chemical mixer with the one in hand. Simply places the new beaker in if no beaker is currently loaded
* Arguments:
* * mob/living/user - The user who is trying to exchange beakers
* * obj/item/reagent_containers/new_beaker - The new beaker that the user wants to put into the device
*/
/obj/item/storage/portable_chem_mixer/proc/replace_beaker(mob/living/user, obj/item/reagent_containers/new_beaker)
if(!user)
return FALSE
if(beaker)
user.put_in_hands(beaker)
beaker = null
if(new_beaker)
beaker = new_beaker
return TRUE
/obj/item/storage/portable_chem_mixer/attack_hand(mob/user)
if (loc != user)
return ..()
if(SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED))
ui_interact(user)
return
/obj/item/storage/portable_chem_mixer/attack_self(mob/user)
if(loc == user)
var/locked = SEND_SIGNAL(src, COMSIG_IS_STORAGE_LOCKED)
if (locked)
ui_interact(user)
return
else
to_chat(user, "<span class='notice'>The portable chemical mixer is currently open and its contents can be accessed.</span>")
return
return
/obj/item/storage/portable_chem_mixer/MouseDrop(obj/over_object)
. = ..()
if(ismob(loc))
var/mob/M = loc
if(!M.incapacitated() && istype(over_object, /obj/screen/inventory/hand))
var/obj/screen/inventory/hand/H = over_object
M.putItemFromInventoryInHandIfPossible(src, H.held_index)
/obj/item/storage/portable_chem_mixer/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PortableChemMixer", name)
if(user.hallucinating())
// to not ruin the immersion by constantly changing the fake chemicals
ui.set_autoupdate(FALSE)
ui.open()
/obj/item/storage/portable_chem_mixer/ui_data(mob/user)
var/list/data = list()
data["amount"] = amount
data["isBeakerLoaded"] = beaker ? 1 : 0
data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
data["beakerMaxVolume"] = beaker ? beaker.volume : null
data["beakerTransferAmounts"] = beaker ? beaker.possible_transfer_amounts : null
var/chemicals[0]
var/is_hallucinating = user.hallucinating()
if(user.hallucinating())
is_hallucinating = TRUE
for(var/re in dispensable_reagents)
var/value = dispensable_reagents[re]
var/datum/reagent/temp = GLOB.chemical_reagents_list[re]
if(temp)
var/chemname = temp.name
var/total_volume = 0
for (var/datum/reagents/rs in value["reagents"])
total_volume += rs.total_volume
if(is_hallucinating && prob(5))
chemname = "[pick_list_replacements("hallucination.json", "chemicals")]"
chemicals.Add(list(list("title" = chemname, "id" = ckey(temp.name), "volume" = total_volume )))
data["chemicals"] = chemicals
var/beakerContents[0]
if(beaker)
for(var/datum/reagent/R in beaker.reagents.reagent_list)
beakerContents.Add(list(list("name" = R.name, "id" = ckey(R.name), "volume" = R.volume))) // list in a list because Byond merges the first list...
data["beakerContents"] = beakerContents
return data
/obj/item/storage/portable_chem_mixer/ui_act(action, params)
if(..())
return
switch(action)
if("amount")
var/target = text2num(params["target"])
amount = target
. = TRUE
if("dispense")
var/reagent_name = params["reagent"]
var/datum/reagent/reagent = GLOB.name2reagent[reagent_name]
var/entry = dispensable_reagents[reagent]
if(beaker)
var/datum/reagents/R = beaker.reagents
var/actual = min(amount, 1000, R.maximum_volume - R.total_volume)
// todo: add check if we have enough reagent left
for (var/datum/reagents/source in entry["reagents"])
var/to_transfer = min(source.total_volume, actual)
source.trans_to(beaker, to_transfer)
actual -= to_transfer
if (actual <= 0)
break
. = TRUE
if("remove")
var/amount = text2num(params["amount"])
beaker.reagents.remove_all(amount)
. = TRUE
if("eject")
replace_beaker(usr)
update_icon()
. = TRUE

View File

@@ -16,9 +16,6 @@
var/on = TRUE
var/shock_cooldown = FALSE
var/ui_x = 260
var/ui_y = 137
/obj/item/electropack/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] hooks [user.p_them()]self to the electropack and spams the trigger! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (FIRELOSS)
@@ -201,17 +198,7 @@
else
return ..()
/obj/item/electropack/shockcollar/ui_interact(mob/user) //note to src: use tgooey
var/dat = {"
<TT>
<B>Frequency/Code</B> for shock collar:<BR>
Frequency:
[format_frequency(src.frequency)]
<A href='byond://?src=[REF(src)];set=freq'>Set</A><BR>
Code:
[src.code]
<A href='byond://?src=[REF(src)];set=code'>Set</A><BR>
</TT>"}
user << browse(dat, "window=radio")
onclose(user, "radio")
return
/obj/item/electropack/ui_act(action, params)
if(action == "power") // DO. NOT.
return FALSE
return ..()

View File

@@ -729,10 +729,10 @@ GENETICS SCANNER
to_chat(user, "<span class='notice'>[target] is empty!</span>")
if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected
var/fusion_power = round(cached_scan_results["fusion"], 0.01)
var/tier = fusionpower2text(fusion_power)
var/instability = round(cached_scan_results["fusion"], 0.01)
var/tier = instability2text(instability)
to_chat(user, "<span class='boldnotice'>Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.</span>")
to_chat(user, "<span class='notice'>Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.</span>")
to_chat(user, "<span class='notice'>Instability of the last fusion reaction: [instability]\n This indicates it was [tier].</span>")
return
/obj/item/analyzer/proc/scan_turf(mob/user, turf/location)
@@ -783,10 +783,10 @@ GENETICS SCANNER
to_chat(user, "<span class='info'>Temperature: [round(environment.return_temperature()-T0C, 0.01)] &deg;C ([round(environment.return_temperature(), 0.01)] K)</span>")
if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected
var/fusion_power = round(cached_scan_results["fusion"], 0.01)
var/tier = fusionpower2text(fusion_power)
var/instability = round(cached_scan_results["fusion"], 0.01)
var/tier = instability2text(instability)
to_chat(user, "<span class='boldnotice'>Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.</span>")
to_chat(user, "<span class='notice'>Power of the last fusion reaction: [fusion_power]\n This power indicates it was a [tier]-tier fusion reaction.</span>")
to_chat(user, "<span class='notice'>Instability of the last fusion reaction: [instability]\n This indicates it was [tier].</span>")
/obj/item/analyzer/ranged
desc = "A hand-held scanner which uses advanced spectroscopy and infrared readings to analyze gases as a distance. Alt-Click to use the built in barometer function."
@@ -992,4 +992,4 @@ GENETICS SCANNER
#undef SCANMODE_CHEMICAL
#undef SCANMODE_WOUND
#undef SCANNER_CONDENSED
#undef SCANNER_VERBOSE
#undef SCANNER_VERBOSE

View File

@@ -289,3 +289,9 @@
. = TRUE
update_icon()
/**
* Returns if this is ready to be detonated. Checks if both tanks are in place.
*/
/obj/item/transfer_valve/proc/ready()
return tank_one && tank_two

View File

@@ -26,6 +26,7 @@
wound_bonus = -110
bare_wound_bonus = 20
block_parry_data = /datum/block_parry_data/dual_esword
block_chance = 60
var/hacked = FALSE
/// Can this reflect all energy projectiles?
var/can_reflect = TRUE
@@ -38,7 +39,8 @@
var/wielded = FALSE // track wielded status on item
var/slowdown_wielded = 0
/datum/block_parry_data/dual_esword
/datum/block_parry_data/dual_esword // please run at the man going apeshit with his funny doublesword
can_block_directions = BLOCK_DIR_NORTH | BLOCK_DIR_NORTHEAST | BLOCK_DIR_NORTHWEST | BLOCK_DIR_WEST | BLOCK_DIR_EAST
block_damage_absorption = 2
block_damage_multiplier = 0.15
block_damage_multiplier_override = list(
@@ -50,10 +52,10 @@
block_lock_sprinting = TRUE
// no attacking while blocking
block_lock_attacking = TRUE
block_projectile_mitigation = 75
block_projectile_mitigation = 85
// more efficient vs projectiles
block_stamina_efficiency_override = list(
TEXT_ATTACK_TYPE_PROJECTILE = 4
TEXT_ATTACK_TYPE_PROJECTILE = 6
)
parry_time_windup = 0

View File

@@ -234,6 +234,9 @@
/obj/item/melee/rapier/attack(mob/living/target, mob/living/user)
. = ..()
if(iscarbon(target))
if(HAS_TRAIT(user, TRAIT_PACIFISM))
visible_message("<span class='warning'>[user] gently taps [target] with [src].</span>",null,null,COMBAT_MESSAGE_RANGE)
log_combat(user, target, "slept", src)
var/mob/living/carbon/H = target
H.Dizzy(10)
H.adjustStaminaLoss(30)

View File

@@ -255,7 +255,7 @@
/obj/item/choice_beacon/box/plushie/generate_display_names()
var/list/plushie_list = list()
//plushie set 1: just subtypes of /obj/item/toy/plush
var/list/plushies_set_one = subtypesof(/obj/item/toy/plush) - list(/obj/item/toy/plush/narplush, /obj/item/toy/plush/awakenedplushie, /obj/item/toy/plush/random_snowflake, /obj/item/toy/plush/random) //don't allow these special ones (you can still get narplush/hugbox)
var/list/plushies_set_one = subtypesof(/obj/item/toy/plush) - list(/obj/item/toy/plush/narplush, /obj/item/toy/plush/awakenedplushie, /obj/item/toy/plush/random_snowflake, /obj/item/toy/plush/plushling, /obj/item/toy/plush/random) //don't allow these special ones (you can still get narplush/hugbox)
for(var/V in plushies_set_one)
var/atom/A = V
plushie_list[initial(A.name)] = A

View File

@@ -167,7 +167,7 @@
return
log_game("[key_name(user)] activated a hidden grenade in [src].")
grenade.preprime(user, msg = FALSE, volume = 10)
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plushpet", /datum/mood_event/plushpet)
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plushpet", /datum/mood_event/plushpet)
else
to_chat(user, "<span class='notice'>You try to pet [src], but it has no stuffing. Aww...</span>")
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plush_nostuffing", /datum/mood_event/plush_nostuffing)
@@ -688,18 +688,6 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
icon_state = "scrubpuppy"
item_state = "scrubpuppy"
/obj/item/toy/plush/borgplushie/meddrake
name = "MediDrake Plushie"
desc = "An adorable stuffed toy of a Medidrake."
icon_state = "meddrake"
item_state = "meddrake"
/obj/item/toy/plush/borgplushie/secdrake
name = "SecDrake Plushie"
desc = "An adorable stuffed toy of a Secdrake."
icon_state = "secdrake"
item_state = "secdrake"
/obj/item/toy/plush/aiplush
name = "AI plushie"
desc = "A little stuffed toy AI core... it appears to be malfunctioning."
@@ -766,8 +754,8 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
attack_verb = list("headbutt", "scritched", "bit")
squeak_override = list('modular_citadel/sound/voice/nya.ogg' = 1)
can_random_spawn = FALSE
/obj/item/toy/plush/hairball
name = "Hairball"
desc = "A bundle of undigested fibers and scales. Yuck."
@@ -777,3 +765,78 @@ GLOBAL_LIST_INIT(valid_plushie_paths, valid_plushie_paths())
squeak_override = list('sound/misc/splort.ogg'=1)
attack_verb = list("sploshed", "splorted", "slushed")
can_random_spawn = FALSE
/obj/item/toy/plush/plushling
name = "peculiar plushie"
desc = "An adorable stuffed toy- wait, did it just move?"
can_random_spawn = FALSE
var/absorb_cooldown = 100 //ticks cooldown between absorbs
var/next_absorb = 0 //When can it absorb another plushie
var/check_interval = 20
var/next_check = 0
//Overrides parent proc
/obj/item/toy/plush/plushling/attack_self(mob/user)
if(!user) //hmmmmm
return
to_chat(user, "<span class='warning'>You try to pet the plushie, but recoil as it bites your hand instead! OW!</span>")
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT,"plush_bite", /datum/mood_event/plush_bite)
var/mob/living/carbon/human/H = user
if(!H)
return //Type safety.
H.apply_damage(5, BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
addtimer(CALLBACK(H, /mob/living/carbon/human.proc/dropItemToGround, src, TRUE), 1)
/obj/item/toy/plush/plushling/New()
var/initial_state = pick("plushie_lizard", "plushie_snake", "plushie_slime", "fox")
icon_state = initial_state
item_state = initial_state
START_PROCESSING(SSobj, src)
. = ..()
/obj/item/toy/plush/plushling/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/item/toy/plush/plushling/process()
if(world.time < next_absorb || world.time < next_check)
return
next_check = world.time + check_interval
var/obj/item/toy/plush/target
for(var/obj/item/toy/plush/possible_target in loc) //First, it tries to get anything in its same location, be it a tile or a backpack
if(possible_target == src || istype(possible_target, /obj/item/toy/plush/plushling))
continue
target = possible_target
break
if(!target)
if(!isturf(loc))
return
for(var/obj/item/toy/plush/P in oview(1, src)) //If that doesn't work, it hunts for plushies adjacent to its own tile
if(istype(P, /obj/item/toy/plush/plushling)) //These do not hunt their own kind
continue
src.throw_at(P, 1, 2)
visible_message("<span class='danger'>[src] leaps at [P]!</span>")
break
return
if(istype(target, /obj/item/toy/plush/plushling)) //These do not consume their own.
return
next_absorb = world.time + absorb_cooldown
plushie_absorb(target)
/obj/item/toy/plush/plushling/proc/plushie_absorb(obj/item/toy/plush/victim)
if(!victim)
return
visible_message("<span class='warning'>[src] gruesomely mutilliates [victim], leaving nothing more than dust!</span>")
name = victim.name
desc = victim.desc + " Wait, did it just move..?"
icon_state = victim.icon_state
item_state = victim.item_state
squeak_override = victim.squeak_override
attack_verb = victim.attack_verb
new /obj/effect/decal/cleanable/ash(get_turf(victim))
qdel(victim)
/obj/item/toy/plush/plushling/love(obj/item/toy/plush/Kisser, mob/living/user) //You shouldn't have come here, poor plush.
if(!Kisser)
return
plushie_absorb(Kisser)

View File

@@ -117,6 +117,7 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \
new/datum/stack_recipe("Captain Statue", /obj/structure/statue/diamond/captain, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("AI Hologram Statue", /obj/structure/statue/diamond/ai1, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("AI Core Statue", /obj/structure/statue/diamond/ai2, 5, one_per_turf = 1, on_floor = 1), \
// new/datum/stack_recipe("diamond brick", /obj/item/ingot/diamond, 6, time = 100), \ not yet
))
/obj/item/stack/sheet/mineral/diamond/get_main_recipes()
@@ -145,6 +146,7 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \
new/datum/stack_recipe("uranium tile", /obj/item/stack/tile/mineral/uranium, 1, 4, 20), \
new/datum/stack_recipe("Nuke Statue", /obj/structure/statue/uranium/nuke, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Engineer Statue", /obj/structure/statue/uranium/eng, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("uranium ingot", /obj/item/ingot/uranium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/uranium/get_main_recipes()
@@ -177,6 +179,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \
new/datum/stack_recipe("plasma door", /obj/structure/mineral_door/transparent/plasma, 10, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("plasma tile", /obj/item/stack/tile/mineral/plasma, 1, 4, 20), \
new/datum/stack_recipe("Scientist Statue", /obj/structure/statue/plasma/scientist, 5, one_per_turf = 1, on_floor = 1), \
// new/datum/stack_recipe("plasma ingot", /obj/item/ingot/plasma, 6, time = 100), \ no
))
/obj/item/stack/sheet/mineral/plasma/get_main_recipes()
@@ -221,6 +224,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \
new/datum/stack_recipe("RD Statue", /obj/structure/statue/gold/rd, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Simple Crown", /obj/item/clothing/head/crown, 5), \
new/datum/stack_recipe("CMO Statue", /obj/structure/statue/gold/cmo, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("gold ingot", /obj/item/ingot/gold, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/gold/get_main_recipes()
@@ -252,6 +256,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
new/datum/stack_recipe("Sec Officer Statue", /obj/structure/statue/silver/sec, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Sec Borg Statue", /obj/structure/statue/silver/secborg, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("Med Borg Statue", /obj/structure/statue/silver/medborg, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("silver ingot", /obj/item/ingot/silver, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/silver/get_main_recipes()
@@ -278,6 +283,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \
GLOBAL_LIST_INIT(bananium_recipes, list ( \
new/datum/stack_recipe("bananium tile", /obj/item/stack/tile/mineral/bananium, 1, 4, 20), \
new/datum/stack_recipe("Clown Statue", /obj/structure/statue/bananium/clown, 5, one_per_turf = 1, on_floor = 1), \
new/datum/stack_recipe("hilarious ingot", /obj/item/ingot/bananium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/bananium/get_main_recipes()
@@ -306,6 +312,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \
GLOBAL_LIST_INIT(titanium_recipes, list ( \
new/datum/stack_recipe("titanium tile", /obj/item/stack/tile/mineral/titanium, 1, 4, 20), \
new/datum/stack_recipe("titanic ingot", /obj/item/ingot/titanium, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/titanium/get_main_recipes()
@@ -353,6 +360,7 @@ GLOBAL_LIST_INIT(plastitanium_recipes, list ( \
*/
GLOBAL_LIST_INIT(adamantine_recipes, list(
new /datum/stack_recipe("incomplete servant golem shell", /obj/item/golem_shell/servant, req_amount=1, res_amount=1),
new/datum/stack_recipe("adamant ingot", /obj/item/ingot/adamantine, 6, time = 100), \
))
/obj/item/stack/sheet/mineral/adamantine

View File

@@ -121,6 +121,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("iron door", /obj/structure/mineral_door/iron, 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("pestle", /obj/item/pestle, 1, time = 50), \
new/datum/stack_recipe("floodlight frame", /obj/structure/floodlight_frame, 5, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("iron ingot", /obj/item/ingot/iron, 6, time = 100), \
))
/obj/item/stack/sheet/metal
@@ -556,6 +557,9 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \
new/datum/stack_recipe("forge", /obj/structure/destructible/cult/forge, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("archives", /obj/structure/destructible/cult/tome, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("altar", /obj/structure/destructible/cult/talisman, 3, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("anvil", /obj/structure/anvil/obtainable/narsie, 4, time = 40, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("runic ingot", /obj/item/ingot/cult, 2, time = 100), \
new/datum/stack_recipe("rune smith's hammer", /obj/item/melee/smith/hammer/narsie, 6), \
))
/obj/item/stack/sheet/runed_metal
@@ -618,6 +622,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
new/datum/stack_recipe("brass bar stool", /obj/structure/chair/stool/bar/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass stool", /obj/structure/chair/stool/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass table frame", /obj/structure/table_frame/brass, 1, time = 5, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass anvil", /obj/structure/anvil/obtainable/ratvar, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("brass furnace", /obj/structure/furnace/infinite/ratvar, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
null, \
new/datum/stack_recipe("sender - pressure sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("sender - mech sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/mech, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
@@ -629,6 +635,8 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
new/datum/stack_recipe("receiver - power nullifier", /obj/structure/destructible/clockwork/trap/power_nullifier, 5, time = 20, one_per_turf = TRUE, on_floor = TRUE, placement_checks = STACK_CHECK_CARDINALS), \
null, \
new/datum/stack_recipe("brass flask", /obj/item/reagent_containers/food/drinks/bottle/holyoil/empty), \
new/datum/stack_recipe("brass smith's hammer", /obj/item/melee/smith/hammer/ratvar, 6), \
new/datum/stack_recipe("brass ingot", /obj/item/ingot/ratvar, 6, time = 100), \
))
/obj/item/stack/tile/brass
@@ -684,7 +692,10 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
new/datum/stack_recipe("bronze chair", /obj/structure/chair/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bronze bar stool", /obj/structure/chair/stool/bar/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("bronze stool", /obj/structure/chair/stool/bronze, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
new /datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \
new/datum/stack_recipe("bronze anvil",/obj/structure/anvil/obtainable/bronze, 20, time = 110, one_per_turf = TRUE, on_floor = TRUE), \
null,
new/datum/stack_recipe("bronze ingot", /obj/item/ingot/bronze, 6, time = 100), \
new/datum/stack_recipe("bronze floor tiles", /obj/item/stack/tile/bronze, 1, 4, 20), \
))
/obj/item/stack/sheet/bronze

View File

@@ -815,3 +815,18 @@
attack_verb = list("bashed", "slashes", "prods", "pokes")
fitting_swords = list(/obj/item/melee/rapier)
starting_sword = /obj/item/melee/rapier
/obj/item/storage/belt/sabre/twin
name = "twin sheath"
desc = "Two sheaths. One is capable of holding a katana (or bokken) and the other a wakizashi. You could put two wakizashis in if you really wanted to. Now you can really roleplay as a samurai."
icon_state = "twinsheath"
item_state = "quiver" //this'll do.
w_class = WEIGHT_CLASS_BULKY
fitting_swords = list(/obj/item/melee/smith/wakizashi, /obj/item/melee/smith/twohand/katana, /obj/item/melee/bokken)
starting_sword = null
/obj/item/storage/belt/sabre/twin/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_items = 2
STR.max_w_class = WEIGHT_CLASS_BULKY + WEIGHT_CLASS_NORMAL //katana and waki.

View File

@@ -237,6 +237,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
resistance_flags = FIRE_PROOF
total_mass = TOTAL_MASS_MEDIEVAL_WEAPON
/obj/item/katana/lavaland
desc = "Woefully underpowered in Lavaland."
block_chance = 30
force = 25 //Like a fireaxe but one handed and can block!
/obj/item/katana/cursed
slot_flags = null

View File

@@ -143,10 +143,18 @@
var/amt = max(0, ((force - (move_resist * MOVE_FORCE_CRUSH_RATIO)) / (move_resist * MOVE_FORCE_CRUSH_RATIO)) * 10)
take_damage(amt, BRUTE)
#define BLACKLISTED_OBJECTS list(/obj/machinery/power/apc, /obj/machinery/airalarm, /obj/machinery/power/smes, /obj/structure/cable)
/obj/attack_slime(mob/living/simple_animal/slime/user)
if(!user.is_adult)
return
attack_generic(user, rand(10, 15), "melee", 1)
if(src.type in BLACKLISTED_OBJECTS)
return
if(istype(src, /obj/machinery/atmospherics))
return
attack_generic(user, rand(10, 15), BRUTE, "melee", 1)
#undef BLACKLISTED_OBJECTS
/obj/mech_melee_attack(obj/mecha/M)
M.do_attack_animation(src)

View File

@@ -304,18 +304,20 @@
/obj/proc/reskin_obj(mob/M)
if(!LAZYLEN(unique_reskin))
return
var/dat = "<b>Reskin options for [name]:</b>\n"
for(var/V in unique_reskin)
var/output = icon2html(src, M, unique_reskin[V])
dat += "[V]: <span class='reallybig'>[output]</span>\n"
to_chat(M, dat)
var/choice = input(M, always_reskinnable ? "Choose the a reskin for [src]" : "Warning, you can only reskin [src] once!","Reskin Object") as null|anything in unique_reskin
if(QDELETED(src) || !choice || (current_skin && !always_reskinnable) || M.incapacitated() || !in_range(M,src) || !unique_reskin[choice] || unique_reskin[choice] == current_skin)
return
current_skin = choice
var/list/skins = list()
for(var/S in unique_reskin)
skins[S] = image(icon = icon, icon_state = unique_reskin[S])
var/choice = show_radial_menu(M, src, skins, custom_check = CALLBACK(src, .proc/check_skinnable, M), radius = 40, require_near = TRUE)
if(!choice)
return FALSE
icon_state = unique_reskin[choice]
to_chat(M, "[src] is now skinned as '[choice]'.")
current_skin = choice
return
/obj/proc/check_skinnable(/mob/M)
if(current_skin || !always_reskinnable)
return FALSE
return TRUE
/obj/update_overlays()
. = ..()

View File

@@ -4,25 +4,6 @@
req_access = list(ACCESS_ALL_PERSONAL_LOCKERS)
var/registered_name = null
/obj/structure/closet/secure_closet/personal/examine(mob/user)
. = ..()
if(registered_name)
. += "<span class='notice'>The display reads, \"Owned by [registered_name]\".</span>"
/obj/structure/closet/secure_closet/personal/check_access(obj/item/I)
. = ..()
if(!I || !istype(I))
return
if(istype(I,/obj/item/modular_computer/tablet))
var/obj/item/modular_computer/tablet/ourTablet = I
var/obj/item/computer_hardware/card_slot/card_slot = ourTablet.all_components[MC_CARD]
if(card_slot)
return registered_name == card_slot.stored_card.registered_name || registered_name == card_slot.stored_card2.registered_name
var/obj/item/card/id/ID = I.GetID()
if(ID && registered_name == ID.registered_name)
return TRUE
return FALSE
/obj/structure/closet/secure_closet/personal/PopulateContents()
..()
if(prob(50))
@@ -54,15 +35,24 @@
/obj/structure/closet/secure_closet/personal/attackby(obj/item/W, mob/user, params)
var/obj/item/card/id/I = W.GetID()
if(!I || !istype(I))
return ..()
if(!can_lock(user, FALSE)) //Can't do anything if there isn't a lock!
return
if(I.registered_name && !registered_name)
to_chat(user, "<span class='notice'>You claim [src].</span>")
registered_name = I.registered_name
if(istype(I))
if(broken)
to_chat(user, "<span class='danger'>It appears to be broken.</span>")
return
if(!I || !I.registered_name)
return
if(allowed(user) || !registered_name || (istype(I) && (registered_name == I.registered_name)))
//they can open all lockers, or nobody owns this, or they own this locker
locked = !locked
update_icon()
if(!registered_name)
registered_name = I.registered_name
desc = "Owned by [I.registered_name]."
else
to_chat(user, "<span class='danger'>Access Denied.</span>")
else
..()
return ..()
/obj/structure/closet/secure_closet/personal/handle_lock_addition() //If lock construction is successful we don't care what access the electronics had, so we override it
if(..())

View File

@@ -10,6 +10,7 @@
max_mobs = 3
max_integrity = 250
mob_types = list(/mob/living/simple_animal/hostile/asteroid/basilisk/watcher/tendril)
var/loot_type = /obj/structure/closet/crate/necropolis/tendril/all
move_resist=INFINITY // just killing it tears a massive hole in the ground, let's not move it
anchored = TRUE
@@ -41,7 +42,7 @@ GLOBAL_LIST_INIT(tendrils, list())
/obj/structure/spawner/lavaland/deconstruct(disassembled)
new /obj/effect/collapse(loc)
new /obj/structure/closet/crate/necropolis/tendril(loc)
new loot_type(loc)
return ..()