| Nuclear Disk(s) | ||||||||
[N.name], "
var/atom/disk_loc = N.loc
while(!istype(disk_loc, /turf))
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index bb8014037d0..4a01b1161e8 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -2173,6 +2173,7 @@
else
var/atom/O = new path(target)
if(O)
+ O.admin_spawned = TRUE
O.dir = obj_dir
if(obj_name)
O.name = obj_name
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index c17671995ff..73b481bbc6b 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -119,6 +119,7 @@ var/list/adminhelp_ignored_words = list("unknown","the","a","an","of","monkey","
for(var/client/X in modholders + adminholders)
if(X.prefs.sound & SOUND_ADMINHELP)
X << 'sound/effects/adminhelp.ogg'
+ window_flash(X)
to_chat(X, msg)
//show it to the person adminhelping too
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 989a7827e3d..5de3178e7ff 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -133,6 +133,7 @@
if(holder && !C.holder)
recieve_message = "-- Click the [recieve_pm_type]'s name to reply --\n"
if(C.adminhelped)
+ window_flash(C)
to_chat(C, recieve_message)
C.adminhelped = 0
diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm
index 862eed1b986..59ea8992889 100644
--- a/code/modules/admin/verbs/debug.dm
+++ b/code/modules/admin/verbs/debug.dm
@@ -1474,3 +1474,57 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that
return
error_cache.showTo(usr)
+
+/client/proc/jump_to_ruin()
+ set category = "Debug"
+ set name = "Jump to Ruin"
+ set desc = "Displays a list of all placed ruins to teleport to."
+
+ if(!check_rights(R_DEBUG))
+ return
+
+ var/list/names = list()
+ for(var/i in ruin_landmarks)
+ var/obj/effect/landmark/ruin/ruin_landmark = i
+ var/datum/map_template/ruin/template = ruin_landmark.ruin_template
+
+ var/count = 1
+ var/name = template.name
+ var/original_name = name
+
+ while(name in names)
+ count++
+ name = "[original_name] ([count])"
+
+ names[name] = ruin_landmark
+
+ var/ruinname = input("Select ruin", "Jump to Ruin") as null|anything in names
+
+ var/obj/effect/landmark/ruin/landmark = names[ruinname]
+
+ if(istype(landmark))
+ var/datum/map_template/ruin/template = landmark.ruin_template
+ admin_forcemove(usr, get_turf(landmark))
+
+ to_chat(usr, "[template.name]")
+ to_chat(usr, "[template.description]")
+
+ log_admin("[key_name(usr)] jumped to ruin [ruinname]")
+ if(!isobserver(usr))
+ message_admins("[key_name_admin(usr)] jumped to ruin [ruinname]", 1)
+
+ feedback_add_details("admin_verb","JT") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/proc/toggle_medal_disable()
+ set category = "Debug"
+ set name = "Toggle Medal Disable"
+ set desc = "Toggles the safety lock on trying to contact the medal hub."
+
+ if(!check_rights(R_DEBUG))
+ return
+
+ global.medals_enabled = !global.medals_enabled
+
+ message_admins("[key_name_admin(src)] [global.medals_enabled ? "disabled" : "enabled"] the medal hub lockout.")
+ feedback_add_details("admin_verb","TMH") // If...
+ log_admin("[key_name(src)] [global.medals_enabled ? "disabled" : "enabled"] the medal hub lockout.")
diff --git a/code/modules/admin/verbs/massmodvar.dm b/code/modules/admin/verbs/massmodvar.dm
index c416f224cf9..c7b2d6d537e 100644
--- a/code/modules/admin/verbs/massmodvar.dm
+++ b/code/modules/admin/verbs/massmodvar.dm
@@ -137,32 +137,38 @@
for(var/mob/M in mob_list)
if( istype(M , O.type) )
M.vars[variable] = O.vars[variable]
+ M.on_varedit(variable)
else if(istype(O, /obj))
for(var/obj/A in world)
if( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
+ A.on_varedit(variable)
else if(istype(O, /turf))
for(var/turf/A in world)
if( istype(A , O.type) )
A.vars[variable] = O.vars[variable]
+ A.on_varedit(variable)
else
if(istype(O, /mob))
for(var/mob/M in mob_list)
if(M.type == O.type)
M.vars[variable] = O.vars[variable]
+ M.on_varedit(variable)
else if(istype(O, /obj))
for(var/obj/A in world)
if(A.type == O.type)
A.vars[variable] = O.vars[variable]
+ A.on_varedit(variable)
else if(istype(O, /turf))
for(var/turf/A in world)
if(A.type == O.type)
A.vars[variable] = O.vars[variable]
+ A.on_varedit(variable)
if("edit referenced object")
return .(O.vars[variable])
diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm
index 0be55889bc2..8cce4c5a761 100644
--- a/code/modules/admin/verbs/modifyvariables.dm
+++ b/code/modules/admin/verbs/modifyvariables.dm
@@ -131,7 +131,7 @@ var/list/forbidden_varedit_object_types = list(
L[var_value] = mod_list_add_ass() //haha
if("No")
L += var_value
-
+
/client/proc/mod_list(var/list/L)
if(!check_rights(R_VAREDIT)) return
@@ -440,6 +440,7 @@ var/list/forbidden_varedit_object_types = list(
if(var_as_text == null)
var_as_text = "[O.vars[variable]]"
+ O.on_varedit(variable)
log_to_dd("### VarEdit by [src]: [O.type] [variable]=[html_encode("[var_as_text]")]")
log_admin("[key_name(src)] modified [original_name]'s [variable] to [var_as_text]")
message_admins("[key_name_admin(src)] modified [original_name]'s [variable] to [var_as_text]", 1)
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index b2864d1c7cc..4c050e0d4f3 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -26,6 +26,8 @@
var/corpseidicon = null //For setting it to be a gold, silver, centcomm etc ID
var/timeofdeath = null
var/coffin = 0
+ var/brute_damage = 0
+ var/oxy_damage = 0
/obj/effect/landmark/corpse/initialize()
if(istype(src,/obj/effect/landmark/corpse/clown))
@@ -37,34 +39,36 @@
var/mob/living/carbon/human/human/M = new /mob/living/carbon/human/human (src.loc)
M.real_name = src.name
M.death(1) //Kills the new mob
+ M.adjustOxyLoss(oxy_damage)
+ M.adjustBruteLoss(brute_damage)
M.timeofdeath = timeofdeath
- if(src.mob_species)
- M.set_species(src.mob_species)
- if(src.corpseuniform)
- M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform)
- if(src.corpsesuit)
- M.equip_to_slot_or_del(new src.corpsesuit(M), slot_wear_suit)
- if(src.corpseshoes)
- M.equip_to_slot_or_del(new src.corpseshoes(M), slot_shoes)
- if(src.corpsegloves)
- M.equip_to_slot_or_del(new src.corpsegloves(M), slot_gloves)
- if(src.corpseradio)
- M.equip_to_slot_or_del(new src.corpseradio(M), slot_l_ear)
- if(src.corpseglasses)
- M.equip_to_slot_or_del(new src.corpseglasses(M), slot_glasses)
- if(src.corpsemask)
- M.equip_to_slot_or_del(new src.corpsemask(M), slot_wear_mask)
- if(src.corpsehelmet)
- M.equip_to_slot_or_del(new src.corpsehelmet(M), slot_head)
- if(src.corpsebelt)
- M.equip_to_slot_or_del(new src.corpsebelt(M), slot_belt)
- if(src.corpsepocket1)
- M.equip_to_slot_or_del(new src.corpsepocket1(M), slot_r_store)
- if(src.corpsepocket2)
- M.equip_to_slot_or_del(new src.corpsepocket2(M), slot_l_store)
- if(src.corpseback)
- M.equip_to_slot_or_del(new src.corpseback(M), slot_back)
- if(src.corpseid == 1)
+ if(mob_species)
+ M.set_species(mob_species)
+ if(corpseuniform)
+ M.equip_to_slot_or_del(new corpseuniform(M), slot_w_uniform)
+ if(corpsesuit)
+ M.equip_to_slot_or_del(new corpsesuit(M), slot_wear_suit)
+ if(corpseshoes)
+ M.equip_to_slot_or_del(new corpseshoes(M), slot_shoes)
+ if(corpsegloves)
+ M.equip_to_slot_or_del(new corpsegloves(M), slot_gloves)
+ if(corpseradio)
+ M.equip_to_slot_or_del(new corpseradio(M), slot_l_ear)
+ if(corpseglasses)
+ M.equip_to_slot_or_del(new corpseglasses(M), slot_glasses)
+ if(corpsemask)
+ M.equip_to_slot_or_del(new corpsemask(M), slot_wear_mask)
+ if(corpsehelmet)
+ M.equip_to_slot_or_del(new corpsehelmet(M), slot_head)
+ if(corpsebelt)
+ M.equip_to_slot_or_del(new corpsebelt(M), slot_belt)
+ if(corpsepocket1)
+ M.equip_to_slot_or_del(new corpsepocket1(M), slot_r_store)
+ if(corpsepocket2)
+ M.equip_to_slot_or_del(new corpsepocket2(M), slot_l_store)
+ if(corpseback)
+ M.equip_to_slot_or_del(new corpseback(M), slot_back)
+ if(corpseid == 1)
var/obj/item/weapon/card/id/W = new(M)
W.name = "[M.real_name]'s ID Card"
var/datum/job/jobdatum
@@ -73,9 +77,9 @@
if(J.title == corpseidaccess)
jobdatum = J
break
- if(src.corpseidicon)
+ if(corpseidicon)
W.icon_state = corpseidicon
- if(src.corpseidaccess)
+ if(corpseidaccess)
if(jobdatum)
W.access = jobdatum.get_access()
else
@@ -84,18 +88,14 @@
W.assignment = corpseidjob
W.registered_name = M.real_name
M.equip_to_slot_or_del(W, slot_wear_id)
- if(src.coffin == 1)
+ if(coffin == 1)
var/obj/structure/closet/coffin/sarcophagus/sarc = locate(/obj/structure/closet/coffin/sarcophagus) in loc
if(sarc) M.loc = sarc
qdel(src)
-
-
// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example.
-
-
-
-
+/obj/effect/landmark/corpse/damaged
+ brute_damage = 1000
/obj/effect/landmark/corpse/syndicatesoldier
name = "Syndicate Operative"
diff --git a/code/modules/awaymissions/zlevel.dm b/code/modules/awaymissions/zlevel.dm
index b12ee063f2d..f5b951eb627 100644
--- a/code/modules/awaymissions/zlevel.dm
+++ b/code/modules/awaymissions/zlevel.dm
@@ -205,7 +205,17 @@ var/global/list/potentialRandomZlevels = generateMapList(filename = "config/away
template = safepick(possible_ruins)
if(!template)
return 0
+ var/turf/central_turf = get_turf(src)
+ for(var/i in template.get_affected_turfs(central_turf, 1))
+ var/turf/T = i
+ for(var/mob/living/simple_animal/monster in T)
+ qdel(monster)
+ for(var/obj/structure/flora/ash/plant in T)
+ qdel(plant)
template.load(get_turf(src),centered = 1)
template.loaded++
+ var/datum/map_template/ruin = template
+ if(istype(ruin))
+ new /obj/effect/landmark/ruin(central_turf, ruin)
qdel(src)
return 1
diff --git a/code/modules/client/preference/preferences.dm b/code/modules/client/preference/preferences.dm
index a558bad3dec..c9b3127094b 100644
--- a/code/modules/client/preference/preferences.dm
+++ b/code/modules/client/preference/preferences.dm
@@ -93,6 +93,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
var/show_ghostitem_attack = TRUE
var/UI_style_color = "#ffffff"
var/UI_style_alpha = 255
+ var/windowflashing = TRUE
//character preferences
@@ -418,6 +419,7 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts
dat += "General Settings" dat += "Fancy NanoUI: [(nanoui_fancy) ? "Yes" : "No"]" dat += "Ghost-Item Attack Animation: [(show_ghostitem_attack) ? "Yes" : "No"] " + dat += "Window Flashing: [(windowflashing) ? "Yes" : "No"] " dat += "Custom UI settings: " dat += " - UI Style: [UI_style] " dat += " - Color: [UI_style_color]
" @@ -1935,6 +1937,9 @@ var/global/list/special_role_times = list( //minimum age (in days) for accounts if("ghost_att_anim") show_ghostitem_attack = !show_ghostitem_attack + if("winflash") + windowflashing = !windowflashing + if("UIcolor") var/UI_style_color_new = input(user, "Choose your UI color, dark colors are not recommended!", UI_style_color) as color|null if(!UI_style_color_new) return diff --git a/code/modules/client/preference/preferences_mysql.dm b/code/modules/client/preference/preferences_mysql.dm index 2713ae2dd7c..8f1b11b5ca6 100644 --- a/code/modules/client/preference/preferences_mysql.dm +++ b/code/modules/client/preference/preferences_mysql.dm @@ -14,7 +14,8 @@ nanoui_fancy, show_ghostitem_attack, lastchangelog, - exp + exp, + windowflashing FROM [format_table_name("player")] WHERE ckey='[C.ckey]'"} ) @@ -42,6 +43,7 @@ show_ghostitem_attack = text2num(query.item[12]) lastchangelog = query.item[13] exp = query.item[14] + windowflashing = text2num(query.item[15]) //Sanitize ooccolor = sanitize_hexcolor(ooccolor, initial(ooccolor)) @@ -57,6 +59,7 @@ show_ghostitem_attack = sanitize_integer(show_ghostitem_attack, 0, 1, initial(show_ghostitem_attack)) lastchangelog = sanitize_text(lastchangelog, initial(lastchangelog)) exp = sanitize_text(exp, initial(exp)) + windowflashing = sanitize_integer(windowflashing, 0, 1, initial(windowflashing)) return 1 /datum/preferences/proc/save_preferences(client/C) diff --git a/code/modules/clothing/masks/miscellaneous.dm b/code/modules/clothing/masks/miscellaneous.dm index ee0c5ec0fb7..427cc071eda 100644 --- a/code/modules/clothing/masks/miscellaneous.dm +++ b/code/modules/clothing/masks/miscellaneous.dm @@ -340,7 +340,7 @@ icon_override = 'icons/goonstation/mob/clothing/mask.dmi' lefthand_file = 'icons/goonstation/mob/inhands/clothing_lefthand.dmi' righthand_file = 'icons/goonstation/mob/inhands/clothing_righthand.dmi' - flags = NODROP + flags = NODROP | AIRTIGHT | MASKCOVERSMOUTH /obj/item/clothing/mask/cursedclown/equipped(mob/user, slot) ..() diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/alien_infestation.dm index 5ce9d28be35..ed8b8d52ccd 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/alien_infestation.dm @@ -29,6 +29,8 @@ respawnable_list -= C.client var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc) new_xeno.key = C.key + if(ticker && ticker.mode) + ticker.mode.xenos += new_xeno.mind spawncount-- successSpawn = 1 diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index b1a5a0f054a..f7c48c88e23 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -34,8 +34,13 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 loc = start z_original = z destination = end + poi_list |= src if(end && end.z==z_original) walk_towards(src, destination, 1) + +/obj/effect/immovablerod/Destroy() + poi_list.Remove(src) + return ..() /obj/effect/immovablerod/Move() if(z != z_original || loc == destination) diff --git a/code/modules/food_and_drinks/drinks/drinks.dm b/code/modules/food_and_drinks/drinks/drinks.dm index 0814bf988f4..eedae606007 100644 --- a/code/modules/food_and_drinks/drinks/drinks.dm +++ b/code/modules/food_and_drinks/drinks/drinks.dm @@ -331,4 +331,11 @@ name = "cup" desc = "A cup with the british flag emblazoned on it." icon_state = "britcup" - volume = 30 \ No newline at end of file + volume = 30 + +/obj/item/weapon/reagent_containers/food/drinks/mushroom_bowl + name = "mushroom bowl" + desc = "A bowl made out of mushrooms. Not food, though it might have contained some at some point." + icon = 'icons/obj/lavaland/ash_flora.dmi' + icon_state = "mushroom_bowl" + w_class = 2 \ No newline at end of file diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm index 61ddaac84e3..3c694f993cf 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm @@ -193,14 +193,15 @@ * Item Adding ********************/ -/obj/machinery/smartfridge/attackby(obj/item/O, mob/user) - if(istype(O, /obj/item/weapon/screwdriver) && anchored) - playsound(loc, 'sound/items/Screwdriver.ogg', 50, 1) - panel_open = !panel_open - to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.") - overlays.Cut() - if(panel_open) - overlays += image(icon, "[initial(icon_state)]-panel") +/obj/machinery/smartfridge/default_deconstruction_screwdriver(mob/user, obj/item/weapon/screwdriver/S) + . = ..(user, icon_state, icon_state, S) + + overlays.Cut() + if(panel_open) + overlays += image(icon, "[initial(icon_state)]-panel") + +/obj/machinery/smartfridge/attackby(obj/item/O, var/mob/user) + if(default_deconstruction_screwdriver(user, O)) return if(exchange_parts(user, O)) @@ -306,7 +307,7 @@ to_chat(user, "Some items are refused.") nanomanager.update_uis(src) -/obj/machinery/smartfridge/secure/emag_act(user) +/obj/machinery/smartfridge/secure/emag_act(mob/user) emagged = 1 locked = -1 to_chat(user, "You short out the product lock on [src].") @@ -535,4 +536,4 @@ to_chat(usr, "Access denied.") nanomanager.update_uis(src) return 0 - return ..() + return ..() \ No newline at end of file diff --git a/code/modules/garbage_collection/__gc_info.dm b/code/modules/garbage_collection/__gc_info.dm index be72097a8c9..e36b5fd2b1b 100644 --- a/code/modules/garbage_collection/__gc_info.dm +++ b/code/modules/garbage_collection/__gc_info.dm @@ -1,7 +1,7 @@ /* =============================================================================== - How Garbage Collection Works + How Garbage Collection Works =============================================================================== In BYOND, there are exactly two ways anything gets deleted: @@ -31,8 +31,8 @@ that can't be. There are two main procs involved in this process: /proc/qdel(datumToDelete) This is, effectively, a replacement for del that tells an object to prepare itself to be soft deleted by calling its Destroy() proc. Depending on the qdel hint returned by Destroy(), qdel will queue the object in the garbage collector - (to be hard deleted if it isn't soft deleted), directly delete the object, pool the object, or ignore the object and - assume it will handle deleting itself. An object passed into qdel will have its gcDestroyed var set, so + (to be hard deleted if it isn't soft deleted), directly delete the object, or ignore the object and + assume it will handle deleting itself. An object passed into qdel will have its gcDestroyed var set, so isnull(gcDestroyed) will be true if an object is not being destroyed, and false if it is (which means you should get rid of the reference you have to it). @@ -50,7 +50,7 @@ that can't be. There are two main procs involved in this process: should use a Del proc for any necessary cleanup, as a Destroy proc on them will not automatically get called. When called by qdel, Destroy is expected to return a qdel hint, which determines whether the object should be directly - deleted, queued for deletion, pooled, or ignored entirely; which of these are appropriate will vary, though objects + deleted, queued for deletion, or ignored entirely; which of these are appropriate will vary, though objects should be pooled or queued whenever possible. The full list of qdel hints are in the code for the qdel proc. As mentioned above, gcDestroyed can be used to tell whether an object is being destroyed. This is important, because if @@ -62,7 +62,4 @@ reference immediately. The inner workings of the GC itself and the stuff related to testing it probably don't need a detailed description - if you intend to work with them, it would be best to read the code to understand what they do. -Pooling is related to GC, in that it's meant to reduce deletion overhead, but it does so by re-using objects instead of -deleting them at all. Explaining pooling is outside the scope of this file. - */ \ No newline at end of file diff --git a/code/modules/mining/equipment_locker.dm b/code/modules/mining/equipment_locker.dm index e573c20840e..712fca9592a 100644 --- a/code/modules/mining/equipment_locker.dm +++ b/code/modules/mining/equipment_locker.dm @@ -727,7 +727,7 @@ ranged = 1 sentience_type = SENTIENCE_MINEBOT ranged_message = "shoots" - ranged_cooldown_cap = 3 + ranged_cooldown_time = 30 projectiletype = /obj/item/projectile/kinetic projectilesound = 'sound/weapons/Gunshot4.ogg' speak_emote = list("states") @@ -978,10 +978,10 @@ name = "minebot cooldown upgrade" /obj/item/device/mine_bot_ugprade/cooldown/upgrade_bot(mob/living/simple_animal/hostile/mining_drone/M, mob/user) - if(M.ranged_cooldown_cap != initial(M.ranged_cooldown_cap)) + if(M.ranged_cooldown_time != initial(M.ranged_cooldown_time)) to_chat(user, "[M] already has a decreased weapon cooldown!") return - M.ranged_cooldown_cap = 1 + M.ranged_cooldown_time = 10 to_chat(user, "You upgrade [M]'s ranged weaponry, reducing its cooldown.") qdel(src) diff --git a/code/modules/mining/lavaland/ash_flora.dm b/code/modules/mining/lavaland/ash_flora.dm new file mode 100644 index 00000000000..922dd34a166 --- /dev/null +++ b/code/modules/mining/lavaland/ash_flora.dm @@ -0,0 +1,205 @@ +/obj/structure/flora/ash + gender = PLURAL + icon = 'icons/obj/lavaland/ash_flora.dmi' + icon_state = "l_mushroom" + name = "large mushrooms" + desc = "A number of large mushrooms, covered in a faint layer of ash and what can only be spores." + var/harvested_name = "shortened mushrooms" + var/harvested_desc = "Some quickly regrowing mushrooms, formerly known to be quite large." + var/needs_sharp_harvest = TRUE + var/harvest = /obj/item/weapon/reagent_containers/food/snacks/ash_flora/shavings + var/harvest_amount_low = 1 + var/harvest_amount_high = 3 + var/harvest_time = 60 + var/harvest_message_low = "You pick a mushroom, but fail to collect many shavings from its cap." + var/harvest_message_med = "You pick a mushroom, carefully collecting the shavings from its cap." + var/harvest_message_high = "You harvest and collect shavings from several mushroom caps." + var/harvested = FALSE + var/base_icon + var/regrowth_time_low = 4800 + var/regrowth_time_high = 8400 + +/obj/structure/flora/ash/New() + ..() + base_icon = "[icon_state][rand(1, 4)]" + icon_state = base_icon + if(prob(15)) + harvest(null, TRUE) + +/obj/structure/flora/ash/proc/harvest(user, no_drop) + if(harvested) + return 0 + if(!no_drop) + var/rand_harvested = rand(harvest_amount_low, harvest_amount_high) + if(rand_harvested) + if(user) + var/msg = harvest_message_med + if(rand_harvested == harvest_amount_low) + msg = harvest_message_low + else if(rand_harvested == harvest_amount_high) + msg = harvest_message_high + to_chat(user, "[msg]") + for(var/i in 1 to rand_harvested) + new harvest(get_turf(src)) + icon_state = "[base_icon]p" + name = harvested_name + desc = harvested_desc + harvested = TRUE + addtimer(src, "regrow", rand(regrowth_time_low, regrowth_time_high)) + return 1 + +/obj/structure/flora/ash/proc/regrow() + icon_state = base_icon + name = initial(name) + desc = initial(desc) + harvested = FALSE + +/obj/structure/flora/ash/attackby(obj/item/weapon/W, mob/user, params) + if(!harvested && needs_sharp_harvest && W.sharp) + user.visible_message("[user] starts to harvest from [src] with [W].","You begin to harvest from [src] with [W].") + if(do_after(user, harvest_time, target = src)) + harvest(user) + else + return ..() + +/obj/structure/flora/ash/attack_hand(mob/user) + if(!harvested && !needs_sharp_harvest) + user.visible_message("[user] starts to harvest from [src].","You begin to harvest from [src].") + if(do_after(user, harvest_time, target = src)) + harvest(user) + else + ..() + +/obj/structure/flora/ash/tall_shroom //exists only so that the spawning check doesn't allow these spawning near other things + regrowth_time_low = 4200 + +/obj/structure/flora/ash/leaf_shroom + icon_state = "s_mushroom" + name = "leafy mushrooms" + desc = "A number of mushrooms, each of which surrounds a greenish sporangium with a number of leaf-like structures." + harvested_name = "leafless mushrooms" + harvested_desc = "A bunch of formerly-leafed mushrooms, with their sporangiums exposed. Scandalous?" + harvest = /obj/item/weapon/reagent_containers/food/snacks/ash_flora/mushroom_leaf + needs_sharp_harvest = FALSE + harvest_amount_high = 4 + harvest_time = 20 + harvest_message_low = "You pluck a single, suitable leaf." + harvest_message_med = "You pluck a number of leaves, leaving a few unsuitable ones." + harvest_message_high = "You pluck quite a lot of suitable leaves." + regrowth_time_low = 2400 + regrowth_time_high = 6000 + +/obj/structure/flora/ash/cap_shroom + icon_state = "r_mushroom" + name = "tall mushrooms" + desc = "Several mushrooms, the larger of which have a ring of conks at the midpoint of their stems." + harvested_name = "small mushrooms" + harvested_desc = "Several small mushrooms near the stumps of what likely were larger mushrooms." + harvest = /obj/item/weapon/reagent_containers/food/snacks/ash_flora/mushroom_cap + harvest_amount_high = 4 + harvest_time = 50 + harvest_message_low = "You slice the cap off of a mushroom." + harvest_message_med = "You slice off a few conks from the larger mushrooms." + harvest_message_high = "You slice off a number of caps and conks from these mushrooms." + regrowth_time_low = 3000 + regrowth_time_high = 5400 + +/obj/structure/flora/ash/stem_shroom + icon_state = "t_mushroom" + name = "numerous mushrooms" + desc = "A large number of mushrooms, some of which have long, fleshy stems. They're radiating light!" + luminosity = 1 + harvested_name = "tiny mushrooms" + harvested_desc = "A few tiny mushrooms around larger stumps. You can already see them growing back." + harvest = /obj/item/weapon/reagent_containers/food/snacks/ash_flora/mushroom_stem + harvest_amount_high = 4 + harvest_time = 40 + harvest_message_low = "You pick and slice the cap off of a mushroom, leaving the stem." + harvest_message_med = "You pick and decapitate several mushrooms for their stems." + harvest_message_high = "You acquire a number of stems from these mushrooms." + regrowth_time_low = 3000 + regrowth_time_high = 6000 + +/obj/structure/flora/ash/cacti + icon_state = "cactus" + name = "fruiting cacti" + desc = "Several prickly cacti, brimming with ripe fruit and covered in a thin layer of ash." + harvested_name = "cacti" + harvested_desc = "A bunch of prickly cacti. You can see fruits slowly growing beneath the covering of ash." + harvest = /obj/item/weapon/reagent_containers/food/snacks/ash_flora/cactus_fruit + needs_sharp_harvest = FALSE + harvest_amount_high = 2 + harvest_time = 10 + harvest_message_low = "You pick a cactus fruit." + harvest_message_med = "You pick several cactus fruit." //shouldn't show up, because you can't get more than two + harvest_message_high = "You pick a pair of cactus fruit." + regrowth_time_low = 4800 + regrowth_time_high = 7200 + +/obj/structure/flora/ash/cacti/Crossed(mob/AM) + if(ishuman(AM) && has_gravity(loc) && prob(70)) + var/mob/living/carbon/human/H = AM + if(!H.shoes && !H.lying) //ouch, my feet. + var/picked_def_zone = pick("l_leg", "r_leg") + var/obj/item/organ/external/affected = H.get_organ(picked_def_zone) + if(!istype(affected)) + return + H.apply_damage(rand(3, 6), BRUTE, picked_def_zone) + H.Weaken(2) + H.visible_message("[H] steps on a cactus!", \ + "You step on a cactus!") + +/obj/item/weapon/reagent_containers/food/snacks/ash_flora + name = "mushroom shavings" + desc = "Some shavings from a tall mushroom. With enough, might serve as a bowl." + icon = 'icons/obj/lavaland/ash_flora.dmi' + icon_state = "mushroom_shavings" + list_reagents = list("sugar" = 3, "ethanol" = 2, "stabilizing_agent" = 3, "minttoxin" = 2) + w_class = 1 + +/obj/item/weapon/reagent_containers/food/snacks/ash_flora/New() + ..() + pixel_x = rand(-4, 4) + pixel_y = rand(-4, 4) + +/obj/item/weapon/reagent_containers/food/snacks/ash_flora/shavings //for actual crafting + +/obj/item/weapon/reagent_containers/food/snacks/ash_flora/mushroom_leaf + name = "mushroom leaf" + desc = "A leaf, from a mushroom." + list_reagents = list("nutriment" = 3, "vitfro" = 2, "nicotine" = 2) + icon_state = "mushroom_leaf" + +/obj/item/weapon/reagent_containers/food/snacks/ash_flora/mushroom_cap + name = "mushroom cap" + desc = "The cap of a large mushroom." + list_reagents = list("lsd" = 2, "entpoly" = 4, "psilocybin" = 2) + icon_state = "mushroom_cap" + +/obj/item/weapon/reagent_containers/food/snacks/ash_flora/mushroom_stem + name = "mushroom stem" + desc = "A long mushroom stem. It's slightly glowing." + list_reagents = list("tinlux" = 2, "vitamin" = 1, "space_drugs" = 1) + icon_state = "mushroom_stem" + luminosity = 1 + +/obj/item/weapon/reagent_containers/food/snacks/ash_flora/cactus_fruit + name = "cactus fruit" + list_reagents = list("vitamin" = 2, "nutriment" = 2, "vitfro" = 4) + desc = "A cactus fruit covered in a thick, reddish skin. And some ash." + icon_state = "cactus_fruit" + +/obj/item/mushroom_bowl + name = "mushroom bowl" + desc = "A bowl made out of mushrooms. Not food, though it might have contained some at some point." + icon = 'icons/obj/lavaland/ash_flora.dmi' + icon_state = "mushroom_bowl" + w_class = 2 + +//what you can craft with these things +/datum/crafting_recipe/mushroom_bowl + name = "Mushroom Bowl" + result = /obj/item/weapon/reagent_containers/food/drinks/mushroom_bowl + reqs = list(/obj/item/weapon/reagent_containers/food/snacks/ash_flora/shavings = 5) + time = 30 + category = CAT_PRIMAL diff --git a/code/modules/mining/lavaland/colossus_loot.dm b/code/modules/mining/lavaland/colossus_loot.dm new file mode 100644 index 00000000000..1928ff0f342 --- /dev/null +++ b/code/modules/mining/lavaland/colossus_loot.dm @@ -0,0 +1,493 @@ +//Black Box + +/obj/machinery/smartfridge/black_box + name = "black box" + desc = "A completely indestructible chunk of crystal, rumoured to predate the start of this universe. It looks like you could store things inside it." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "blackbox" + icon_on = "blackbox" + icon_off = "blackbox" + luminosity = 8 + max_n_of_items = INFINITY + unacidable = 1 + pixel_y = -4 + use_power = 0 + var/memory_saved = FALSE + var/list/stored_items = list() + var/static/list/blacklist = typecacheof(list(/obj/item/weapon/spellbook)) + +/obj/machinery/smartfridge/black_box/update_icon() + return + +/obj/machinery/smartfridge/black_box/accept_check(obj/item/O) + if(!istype(O)) + return FALSE + if(is_type_in_typecache(O, blacklist)) + return FALSE + return TRUE + +/obj/machinery/smartfridge/black_box/New() + var/static/obj/machinery/smartfridge/black_box/current + if(current && current != src) + qdel(src, force=TRUE) + return + current = src + ReadMemory() + . = ..() + +/obj/machinery/smartfridge/black_box/process() + ..() + if(!memory_saved && ticker.current_state == GAME_STATE_FINISHED) + WriteMemory() + +/obj/machinery/smartfridge/black_box/proc/WriteMemory() + var/savefile/S = new /savefile("data/npc_saves/Blackbox.sav") + stored_items = list() + + for(var/obj/O in (contents-component_parts)) + stored_items += O.type + + S["stored_items"] << stored_items + memory_saved = TRUE + +/obj/machinery/smartfridge/black_box/proc/ReadMemory() + var/savefile/S = new /savefile("data/npc_saves/Blackbox.sav") + S["stored_items"] >> stored_items + + if(isnull(stored_items)) + stored_items = list() + + for(var/item in stored_items) + create_item(item) + +//in it's own proc to avoid issues with items that nolonger exist in the code base. +//try catch doesn't always prevent byond runtimes from halting a proc, +/obj/machinery/smartfridge/black_box/proc/create_item(item_type) + new item_type(src) + +/obj/machinery/smartfridge/black_box/Destroy(force = FALSE) + if(force) + for(var/thing in src) + qdel(thing) + return ..() + else + return QDEL_HINT_LETMELIVE + + +//No taking it apart + +/obj/machinery/smartfridge/black_box/default_deconstruction_screwdriver() + return + +/obj/machinery/smartfridge/black_box/exchange_parts() + return + +/obj/machinery/smartfridge/black_box/default_unfasten_wrench() + return + +/obj/machinery/smartfridge/black_box/default_deconstruction_crowbar() + return + +///Anomolous Crystal/// + +/obj/machinery/anomalous_crystal + name = "anomalous crystal" + desc = "A strange chunk of crystal, being in the presence of it fills you with equal parts excitement and dread." + icon = 'icons/obj/lavaland/artefacts.dmi' + icon_state = "anomaly_crystal" + luminosity = 8 + use_power = 0 + density = 1 + unacidable = 1 + var/activation_method = "touch" + var/activation_damage_type = null + var/last_use_timer = 0 + var/cooldown_add = 30 + var/list/affected_targets = list() + var/activation_sound = 'sound/effects/break_stone.ogg' + +/obj/machinery/anomalous_crystal/New() + activation_method = pick("touch","laser","bullet","energy","bomb","mob_bump","weapon","speech") // "heat" removed due to lack of is_hot() + ..() + +/obj/machinery/anomalous_crystal/hear_talk(mob/speaker, message) + ..() + if(isliving(speaker) && message) + ActivationReaction(speaker, "speech") + +/obj/machinery/anomalous_crystal/attack_hand(mob/user) + ..() + ActivationReaction(user,"touch") + +/obj/machinery/anomalous_crystal/attackby(obj/item/I, mob/user, params) + ActivationReaction(user,"weapon") + ..() + +/obj/machinery/anomalous_crystal/bullet_act(obj/item/projectile/P, def_zone) + ..() + if(istype(P, /obj/item/projectile/magic)) + ActivationReaction(P.firer, "magic", P.damage_type) + return + ActivationReaction(P.firer, P.flag, P.damage_type) + +/obj/machinery/anomalous_crystal/proc/ActivationReaction(mob/user, method, damtype) + if(world.time < last_use_timer) + return 0 + if(activation_damage_type && activation_damage_type != damtype) + return 0 + if(method != activation_method) + return 0 + last_use_timer = (world.time + cooldown_add) + playsound(user, activation_sound, 100, 1) + return 1 + +/obj/machinery/anomalous_crystal/Bumped(atom/AM as mob|obj) + ..() + if(ismob(AM)) + ActivationReaction(AM,"mob_bump") + +/obj/machinery/anomalous_crystal/ex_act() + ActivationReaction(null,"bomb") + +/obj/machinery/anomalous_crystal/random/New()//Just a random crysal spawner for loot + var/random_crystal = pick(typesof(/obj/machinery/anomalous_crystal) - /obj/machinery/anomalous_crystal/random - /obj/machinery/anomalous_crystal) + new random_crystal(loc) + qdel(src) + +/obj/machinery/anomalous_crystal/honk //Strips and equips you as a clown. I apologize for nothing + activation_method = "mob_bump" + activation_sound = 'sound/items/bikehorn.ogg' + +/obj/machinery/anomalous_crystal/honk/ActivationReaction(mob/user) + if(..() && ishuman(user) && !(user in affected_targets)) + var/mob/living/carbon/human/H = user + for(var/obj/item/W in H) + H.unEquip(W) + var/datum/job/clown/C = job_master.GetJob("Clown") + C.equip(H) + affected_targets.Add(H) + +/obj/machinery/anomalous_crystal/honk/New() + ..() + activation_method = pick("mob_bump","speech") + +/obj/machinery/anomalous_crystal/theme_warp //Warps the area you're in to look like a new one + activation_method = "touch" + cooldown_add = 200 + var/terrain_theme = "winter" + var/NewTerrainFloors + var/NewTerrainWalls + var/NewTerrainChairs + var/NewTerrainTables + var/list/NewFlora = list() + var/florachance = 8 + +/obj/machinery/anomalous_crystal/theme_warp/New() + ..() + terrain_theme = pick("lavaland","winter","jungle","alien") + switch(terrain_theme) + if("lavaland")//Depressurizes the place... and free cult metal, I guess. + NewTerrainFloors = /turf/simulated/floor/basalt // Needs to be updated after turf update + NewTerrainWalls = /turf/simulated/wall/cult + NewFlora = list(/mob/living/simple_animal/hostile/asteroid/goldgrub) + florachance = 1 + if("winter") //Snow terrain is slow to move in and cold! Get the assistants to shovel your driveway. + NewTerrainFloors = /turf/simulated/floor/snow // Needs to be updated after turf update + NewTerrainWalls = /turf/simulated/wall/mineral/wood + NewTerrainChairs = /obj/structure/stool/bed/chair/wood/normal + NewTerrainTables = /obj/structure/table/glass + NewFlora = list(/obj/structure/flora/grass/green, /obj/structure/flora/grass/brown, /obj/structure/flora/grass/both) + if("jungle") //Beneficial due to actually having breathable air. Plus, monkeys and bows and arrows. + NewTerrainFloors = /turf/simulated/floor/grass + NewTerrainWalls = /turf/simulated/wall/mineral/sandstone + NewTerrainChairs = /obj/structure/stool/bed/chair/wood/normal + NewTerrainTables = /obj/structure/table/woodentable + NewFlora = list(/obj/structure/flora/ausbushes/sparsegrass, /obj/structure/flora/ausbushes/fernybush, /obj/structure/flora/ausbushes/leafybush, + /obj/structure/flora/ausbushes/grassybush, /obj/structure/flora/ausbushes/sunnybush, /obj/structure/flora/tree/palm, /mob/living/carbon/human/monkey, + /obj/item/weapon/gun/projectile/bow, /obj/item/weapon/storage/backpack/quiver/full) + florachance = 20 + if("alien") //Beneficial, turns stuff into alien alloy which is useful to cargo and research. Also repairs atmos. + NewTerrainFloors = /turf/simulated/floor/mineral/abductor + NewTerrainWalls = /turf/simulated/wall/mineral/abductor + NewTerrainChairs = /obj/structure/stool/bed/abductor //ayys apparently don't have chairs. An entire species of people who only recline. + NewTerrainTables = /obj/structure/table/abductor + +/obj/machinery/anomalous_crystal/theme_warp/ActivationReaction(mob/user, method) + if(..()) + var/area/A = get_area(src) + if(!A.outdoors && !(A in affected_targets)) + for(var/atom/Stuff in A) + if(isturf(Stuff)) + var/turf/T = Stuff + if((isspaceturf(T) || isfloorturf(T)) && NewTerrainFloors) + var/turf/simulated/O = T.ChangeTurf(NewTerrainFloors) + if(O.air) + var/datum/gas_mixture/G = O.air + G.copy_from(O.air) + if(prob(florachance) && NewFlora.len && !is_blocked_turf(O)) + var/atom/Picked = pick(NewFlora) + new Picked(O) + continue + if(iswallturf(T) && NewTerrainWalls) + T.ChangeTurf(NewTerrainWalls) + continue + if(istype(Stuff, /obj/structure/stool/bed/chair) && NewTerrainChairs) + var/obj/structure/stool/bed/chair/Original = Stuff + var/obj/structure/stool/bed/chair/C = new NewTerrainChairs(Original.loc) + C.dir = Original.dir + qdel(Stuff) + continue + if(istype(Stuff, /obj/structure/table) && NewTerrainTables) + var/obj/structure/table/Original = Stuff + var/obj/structure/table/T = new NewTerrainTables(Original.loc) + T.dir = Original.dir + qdel(Stuff) + continue + affected_targets += A + +/obj/machinery/anomalous_crystal/emitter //Generates a projectile when interacted with + activation_method = "touch" + cooldown_add = 50 + var/generated_projectile = /obj/item/projectile/beam/emitter + +/obj/machinery/anomalous_crystal/emitter/New() + ..() + generated_projectile = pick(/obj/item/projectile/magic/fireball/infernal,/obj/item/projectile/magic/spellblade, + /obj/item/projectile/bullet/meteorshot, /obj/item/projectile/beam/xray, /obj/item/projectile/colossus) + +/obj/machinery/anomalous_crystal/emitter/ActivationReaction(mob/user, method) + if(..()) + var/obj/item/projectile/P = new generated_projectile(get_turf(src)) + P.dir = dir + switch(dir) + if(NORTH) + P.yo = 20 + P.xo = 0 + if(EAST) + P.yo = 0 + P.xo = 20 + if(WEST) + P.yo = 0 + P.xo = -20 + else + P.yo = -20 + P.xo = 0 + P.fire() + +/obj/machinery/anomalous_crystal/dark_reprise //Revives anyone nearby, but turns them into shadowpeople and renders them uncloneable, so the crystal is your only hope of getting up again if you go down. + activation_method = "touch" + activation_sound = 'sound/hallucinations/growl1.ogg' + +/obj/machinery/anomalous_crystal/dark_reprise/ActivationReaction(mob/user, method) + if(..()) + for(var/i in range(1, src)) + if(isturf(i)) + new /obj/effect/overlay/temp/cult/sparks(i) + continue + if(ishuman(i)) + var/mob/living/carbon/human/H = i + if(H.stat == DEAD) + H.set_species("Shadow") + H.revive() + H.disabilities |= NOCLONE //Free revives, but significantly limits your options for reviving except via the crystal + H.grab_ghost(force = TRUE) + +/obj/machinery/anomalous_crystal/helpers //Lets ghost spawn as helpful creatures that can only heal people slightly. Incredibly fragile and they can't converse with humans + activation_method = "touch" + var/ready_to_deploy = 0 + +/obj/machinery/anomalous_crystal/helpers/ActivationReaction(mob/user, method) + if(..() && !ready_to_deploy) + ready_to_deploy = 1 + notify_ghosts("An anomalous crystal has been activated in [get_area(src)]! This crystal can always be used by ghosts hereafter.", enter_link = "(Click to enter)", source = src, action = NOTIFY_ATTACK) + +/obj/machinery/anomalous_crystal/helpers/attack_ghost(mob/dead/observer/user) + ..() + if(ready_to_deploy) + var/be_helper = alert("Become a Lightgeist? (Warning, You can no longer be cloned!)",,"Yes","No") + if(be_helper == "No") + return + var/mob/living/simple_animal/hostile/lightgeist/W = new /mob/living/simple_animal/hostile/lightgeist(get_turf(loc)) + W.key = user.key + +/obj/machinery/anomalous_crystal/helpers/Topic(href, href_list) + if(href_list["ghostjoin"]) + var/mob/dead/observer/ghost = usr + if(istype(ghost)) + attack_ghost(ghost) + +/mob/living/simple_animal/hostile/lightgeist + name = "lightgeist" + desc = "This small floating creature is a completely unknown form of life... being near it fills you with a sense of tranquility." + icon_state = "lightgeist" + icon_living = "lightgeist" + icon_dead = "butterfly_dead" + turns_per_move = 1 + response_help = "waves away" + response_disarm = "brushes aside" + response_harm = "disrupts" + speak_emote = list("oscillates") + maxHealth = 2 + health = 2 + harm_intent_damage = 1 + friendly = "mends" + density = 0 + flying = 1 + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + ventcrawler = 2 + mob_size = MOB_SIZE_TINY + gold_core_spawnable = 0 + speak_emote = list("warps") + damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) + luminosity = 4 + faction = list("neutral") + universal_understand = 1 + del_on_death = 1 + unsuitable_atmos_damage = 0 + flying = 1 + minbodytemp = 0 + maxbodytemp = 1500 + environment_smash = 0 + AIStatus = AI_OFF + stop_automated_movement = 1 + var/heal_power = 5 + +/mob/living/simple_animal/hostile/lightgeist/New() + ..() + verbs -= /mob/living/verb/pulled + verbs -= /mob/verb/me_verb + var/datum/atom_hud/medsensor = huds[DATA_HUD_MEDICAL_ADVANCED] + medsensor.add_hud_to(src) + +/mob/living/simple_animal/hostile/lightgeist/AttackingTarget() + ..() + if(isliving(target) && target != src) + var/mob/living/L = target + if(L.stat < DEAD) + L.heal_overall_damage(heal_power, heal_power) + new /obj/effect/overlay/temp/heal(get_turf(target), "#80F5FF") + +/mob/living/simple_animal/hostile/lightgeist/ghostize() + if(..()) + death() + +/obj/machinery/anomalous_crystal/refresher //Deletes and recreates a copy of the item, "refreshing" it. + activation_method = "touch" + cooldown_add = 50 + activation_sound = 'sound/magic/TIMEPARADOX2.ogg' + var/list/banned_items_typecache = list(/obj/item/weapon/storage, /obj/item/weapon/implant, /obj/item/weapon/implanter, /obj/item/weapon/disk/nuclear, /obj/item/projectile, /obj/item/weapon/spellbook) + +/obj/machinery/anomalous_crystal/refresher/New() + ..() + banned_items_typecache = typecacheof(banned_items_typecache) + + +/obj/machinery/anomalous_crystal/refresher/ActivationReaction(mob/user, method) + if(..()) + var/list/L = list() + var/turf/T = get_step(src, dir) + new /obj/effect/overlay/temp/emp/pulse(T) + for(var/i in T) + if(istype(i, /obj/item) && !is_type_in_typecache(i, banned_items_typecache)) + var/obj/item/W = i + if(!W.admin_spawned) + L += W + if(L.len) + var/obj/item/CHOSEN = pick(L) + new CHOSEN.type(T) + qdel(CHOSEN) + +/obj/machinery/anomalous_crystal/possessor //Allows you to bodyjack small animals, then exit them at your leisure, but you can only do this once per activation. Because they blow up. Also, if the bodyjacked animal dies, SO DO YOU. + activation_method = "touch" + +/obj/machinery/anomalous_crystal/possessor/ActivationReaction(mob/user, method) + if(..()) + if(ishuman(user)) + var/mobcheck = 0 + for(var/mob/living/simple_animal/A in range(1, src)) + if(A.melee_damage_upper > 5 || A.mob_size >= MOB_SIZE_LARGE || A.ckey || A.stat) + break + var/obj/structure/closet/stasis/S = new /obj/structure/closet/stasis(A) + user.forceMove(S) + mobcheck = 1 + break + if(!mobcheck) + new /mob/living/simple_animal/cockroach(get_step(src,dir)) //Just in case there aren't any animals on the station, this will leave you with a terrible option to possess if you feel like it + +/obj/structure/closet/stasis + name = "quantum entanglement stasis warp field" + desc = "You can hardly comprehend this thing... which is why you can't see it." + icon_state = null //This shouldn't even be visible, so if it DOES show up, at least nobody will notice + density = 1 + anchored = 1 + var/mob/living/simple_animal/holder_animal + +/obj/structure/closet/stasis/process() + if(holder_animal) + if(holder_animal.stat == DEAD && !qdeleted(holder_animal)) + dump_contents() + holder_animal.gib() + return + +/obj/structure/closet/stasis/New() + ..() + if(isanimal(loc)) + holder_animal = loc + processing_objects.Add(src) + +/obj/structure/closet/stasis/Entered(atom/A) + if(isliving(A) && holder_animal) + var/mob/living/L = A + L.notransform = 1 + L.disabilities |= MUTE + L.status_flags |= GODMODE + L.mind.transfer_to(holder_animal) + var/obj/effect/proc_holder/spell/targeted/exit_possession/P = new /obj/effect/proc_holder/spell/targeted/exit_possession + holder_animal.mind.AddSpell(P) + holder_animal.verbs -= /mob/living/verb/pulled + +/obj/structure/closet/stasis/dump_contents(var/kill = 1) + processing_objects.Remove(src) + for(var/mob/living/L in src) + L.disabilities &= ~MUTE + L.status_flags &= ~GODMODE + L.notransform = 0 + if(holder_animal && !qdeleted(holder_animal)) + holder_animal.mind.transfer_to(L) + L.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/exit_possession) + if(kill || !isanimal(loc)) + L.death(0) + ..() + +/obj/structure/closet/stasis/emp_act() + return + +/obj/structure/closet/stasis/ex_act() + return + +/obj/effect/proc_holder/spell/targeted/exit_possession + name = "Exit Possession" + desc = "Exits the body you are possessing" + charge_max = 60 + clothes_req = 0 + invocation_type = "none" + max_targets = 1 + range = -1 + include_user = 1 + selection_type = "view" + action_icon_state = "exit_possession" + sound = null + +/obj/effect/proc_holder/spell/targeted/exit_possession/cast(list/targets, mob/user = usr) + if(!isfloorturf(user.loc)) + return + var/datum/mind/target_mind = user.mind + var/mob/living/current = user // Saving the current mob here to gib as usr seems to get confused after the mind's been transferred, due to delay in transfer_to + for(var/i in user) + if(istype(i, /obj/structure/closet/stasis)) + var/obj/structure/closet/stasis/S = i + S.dump_contents(0) + qdel(S) + break + current.gib() + target_mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/exit_possession) diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm new file mode 100644 index 00000000000..649a868077d --- /dev/null +++ b/code/modules/mining/lavaland/necropolis_chests.dm @@ -0,0 +1,618 @@ +//The chests dropped by mob spawner tendrils. Also contains associated loot. + +/obj/structure/closet/crate/necropolis + name = "necropolis chest" + desc = "It's watching you closely." + icon_state = "necrocrate" + icon_opened = "necrocrateopen" + icon_closed = "necrocrate" + +/obj/structure/closet/crate/necropolis/tendril + desc = "It's watching you suspiciously." + +/obj/structure/closet/crate/necropolis/tendril/New() + ..() + // uncomment me once these items are being implemented + /*var/loot = rand(1,25) + switch(loot) + if(1) + new /obj/item/device/shared_storage/red(src) + if(2) + new /obj/item/clothing/suit/space/hardsuit/cult(src) + if(3) + new /obj/item/device/soulstone/anybody(src) + if(4) + new /obj/item/weapon/katana/cursed(src) + if(5) + new /obj/item/clothing/glasses/godeye(src) + if(6) + new /obj/item/weapon/reagent_containers/glass/bottle/potion/flight(src) + if(7) + new /obj/item/weapon/pickaxe/diamond(src) + if(8) + new /obj/item/clothing/head/culthood(src) + new /obj/item/clothing/suit/cultrobes(src) + new /obj/item/weapon/bedsheet/cult(src) + if(9) + new /obj/item/organ/brain/alien(src) + if(10) + new /obj/item/organ/heart/cursed(src) + if(11) + new /obj/item/ship_in_a_bottle(src) + if(12) + new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/beserker(src) + if(13) + new /obj/item/weapon/sord(src) + if(14) + new /obj/item/weapon/nullrod/scythe/talking(src) + if(15) + new /obj/item/weapon/nullrod/armblade(src) + if(16) + new /obj/item/weapon/guardiancreator(src) + if(17) + new /obj/item/borg/upgrade/modkit/aoe/turfs/andmobs(src) + if(18) + new /obj/item/device/warp_cube/red(src) + if(19) + new /obj/item/device/wisp_lantern(src) + if(20) + new /obj/item/device/immortality_talisman(src) + if(21) + new /obj/item/weapon/gun/magic/hook(src) + if(22) + new /obj/item/voodoo(src) + if(23) + new /obj/item/weapon/grenade/clusterbuster/inferno(src) + if(24) + new /obj/item/weapon/reagent_containers/food/drinks/bottle/holywater/hell(src) + new /obj/item/clothing/suit/space/hardsuit/ert/paranormal/inquisitor(src) + if(25) + new /obj/item/weapon/spellbook/oneuse/summonitem(src)*/ + +///Bosses + +//Dragon + +/obj/structure/closet/crate/necropolis/dragon + name = "dragon chest" + +/obj/structure/closet/crate/necropolis/dragon/New() + ..() + var/loot = rand(1,4) + switch(loot) + if(1) + new /obj/item/weapon/melee/ghost_sword(src) + if(2) + new /obj/item/weapon/lava_staff(src) + if(3) + new /obj/item/weapon/spellbook/oneuse/sacredflame(src) + new /obj/item/weapon/gun/magic/wand/fireball(src) + if(4) + new /obj/item/weapon/dragons_blood(src) + +/obj/item/weapon/melee/ghost_sword + name = "spectral blade" + desc = "A rusted and dulled blade. It doesn't look like it'd do much damage. It glows weakly." + icon_state = "spectral" + item_state = "spectral" + flags = CONDUCT + sharp = 1 + edge = 1 + w_class = 4 + force = 1 + throwforce = 1 + hitsound = 'sound/effects/ghost2.ogg' + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "rended") + var/summon_cooldown = 0 + var/list/mob/dead/observer/spirits + +/obj/item/weapon/melee/ghost_sword/New() + ..() + spirits = list() + processing_objects.Add(src) + poi_list |= src + +/obj/item/weapon/melee/ghost_sword/Destroy() + for(var/mob/dead/observer/G in spirits) + G.invisibility = initial(G.invisibility) + spirits.Cut() + processing_objects.Remove(src) + poi_list -= src + . = ..() + +/obj/item/weapon/melee/ghost_sword/attack_self(mob/user) + if(summon_cooldown > world.time) + to_chat(user, "You just recently called out for aid. You don't want to annoy the spirits.") + return + to_chat(user, "You call out for aid, attempting to summon spirits to your side.") + + notify_ghosts("[user] is raising their [src], calling for your help!", enter_link="(Click to help)", source = user, action = NOTIFY_FOLLOW) + + summon_cooldown = world.time + 600 + +/obj/item/weapon/melee/ghost_sword/Topic(href, href_list) + if(href_list["follow"]) + var/mob/dead/observer/ghost = usr + if(istype(ghost)) + ghost.ManualFollow(src) + +/obj/item/weapon/melee/ghost_sword/process() + ghost_check() + +/obj/item/weapon/melee/ghost_sword/proc/ghost_check() + var/ghost_counter = 0 + var/turf/T = get_turf(src) + var/list/contents = T.GetAllContents() + var/mob/dead/observer/current_spirits = list() + + for(var/mob/dead/observer/O in player_list) + if(is_type_in_list(O.following, contents)) + ghost_counter++ + O.invisibility = 0 + current_spirits |= O + + for(var/mob/dead/observer/G in spirits - current_spirits) + G.invisibility = initial(G.invisibility) + + spirits = current_spirits + + return ghost_counter + +/obj/item/weapon/melee/ghost_sword/attack(mob/living/target, mob/living/carbon/human/user) + force = 0 + var/ghost_counter = ghost_check() + + force = Clamp((ghost_counter * 4), 0, 75) + user.visible_message("[user] strikes with the force of [ghost_counter] vengeful spirits!") + ..() + +/obj/item/weapon/melee/ghost_sword/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance, damage, attack_type) + var/ghost_counter = ghost_check() + final_block_chance += Clamp((ghost_counter * 5), 0, 75) + owner.visible_message("[owner] is protected by a ring of [ghost_counter] ghosts!") + return ..() + +// Blood + +/obj/item/weapon/dragons_blood + name = "bottle of dragons blood" + desc = "You're not actually going to drink this, are you?" + icon = 'icons/obj/wizard.dmi' + icon_state = "vial" + +/obj/item/weapon/dragons_blood/attack_self(mob/living/carbon/human/user) + if(!istype(user)) + return + + var/mob/living/carbon/human/H = user + var/random = rand(1,3) + + switch(random) + if(1) + to_chat(user, "Your flesh begins to melt! Miraculously, you seem fine otherwise.") + H.set_species("Skeleton") + if(2) + to_chat(user, "Power courses through you! You can now shift your form at will.") + if(user.mind) + var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/D = new + user.mind.AddSpell(D) + if(3) + to_chat(user, "You feel like you could walk straight through lava now.") + H.weather_immunities |= "lava" + + playsound(user.loc,'sound/items/drink.ogg', rand(10,50), 1) + qdel(src) + +/datum/disease/transformation/dragon + name = "dragon transformation" + cure_text = "nothing" + cures = list("adminordrazine") + agent = "dragon's blood" + desc = "What do dragons have to do with Space Station 13?" + stage_prob = 20 + severity = BIOHAZARD + visibility_flags = 0 + stage1 = list("Your bones ache.") + stage2 = list("Your skin feels scaley.") + stage3 = list("You have an overwhelming urge to terrorize some peasants.", "Your teeth feel sharper.") + stage4 = list("Your blood burns.") + stage5 = list("You're a fucking dragon. However, any previous allegiances you held still apply. It'd be incredibly rude to eat your still human friends for no reason.") + new_form = /mob/living/simple_animal/hostile/megafauna/dragon/lesser + +//Lava Staff + +/obj/item/weapon/lava_staff + name = "staff of lava" + desc = "The ability to fill the emergency shuttle with lava. What more could you want out of life?" + icon_state = "staffofstorms" + item_state = "staffofstorms" + icon = 'icons/obj/guns/magic.dmi' + slot_flags = SLOT_BACK + item_state = "staffofstorms" + w_class = 4 + force = 25 + damtype = BURN + hitsound = 'sound/weapons/sear.ogg' + var/turf_type = /turf/unsimulated/floor/lava // /turf/simulated/floor/plating/lava/smooth once Lavaland turfs are added + var/transform_string = "lava" + var/reset_turf_type = /turf/simulated/floor/plating/airless/asteroid // /turf/simulated/floor/plating/asteroid/basalt once Lavaland turfs are added + var/reset_string = "basalt" + var/create_cooldown = 100 + var/create_delay = 30 + var/reset_cooldown = 50 + var/timer = 0 + var/banned_turfs + +/obj/item/weapon/lava_staff/New() + . = ..() + banned_turfs = typecacheof(list(/turf/space/transit, /turf/unsimulated)) + +/obj/item/weapon/lava_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + ..() + if(timer > world.time) + return + + if(is_type_in_typecache(target, banned_turfs)) + return + + if(target in view(user.client.view, get_turf(user))) + + var/turf/simulated/T = get_turf(target) + if(!istype(T)) + return + if(!istype(T, turf_type)) + var/obj/effect/overlay/temp/lavastaff/L = new /obj/effect/overlay/temp/lavastaff(T) + L.alpha = 0 + animate(L, alpha = 255, time = create_delay) + user.visible_message("[user] points [src] at [T]!") + timer = world.time + create_delay + 1 + if(do_after(user, create_delay, target = T)) + user.visible_message("[user] turns \the [T] into [transform_string]!") + message_admins("[key_name_admin(user)] fired the lava staff at [get_area(target)] (JMP).") + log_game("[key_name(user)] fired the lava staff at [get_area(target)] ([T.x], [T.y], [T.z]).") + T.ChangeTurf(turf_type) + timer = world.time + create_cooldown + qdel(L) + else + timer = world.time + qdel(L) + return + else + user.visible_message("[user] turns \the [T] into [reset_string]!") + T.ChangeTurf(reset_turf_type) + timer = world.time + reset_cooldown + playsound(T,'sound/magic/Fireball.ogg', 200, 1) + +/obj/effect/overlay/temp/lavastaff + icon_state = "lavastaff_warn" + duration = 50 + +// Bubblegum + +/obj/structure/closet/crate/necropolis/bubblegum + name = "bubblegum chest" + +/obj/structure/closet/crate/necropolis/bubblegum/New() + ..() + var/loot = rand(1,3) + switch(loot) + if(1) + new /obj/item/mayhem(src) + if(2) + new /obj/item/blood_contract(src) + if(3) + new /obj/item/weapon/gun/magic/staff/spellblade(src) + +// Mayhem + +/obj/item/mayhem + name = "mayhem in a bottle" + desc = "A magically infused bottle of blood, the scent of which will drive anyone nearby into a murderous frenzy." + icon = 'icons/obj/wizard.dmi' + icon_state = "vial" + +/obj/item/mayhem/attack_self(mob/user) + for(var/mob/living/carbon/human/H in range(7,user)) + spawn() + var/obj/effect/mine/pickup/bloodbath/B = new(H) + B.mineEffect(H) + to_chat(user, "You shatter the bottle!") + playsound(user.loc, 'sound/effects/Glassbr1.ogg', 100, 1) + qdel(src) + +// Blood Contract + +/obj/item/blood_contract + name = "blood contract" + icon = 'icons/obj/wizard.dmi' + icon_state = "scroll2" + color = "#FF0000" + desc = "Mark your target for death." + var/used = FALSE + +/obj/item/blood_contract/attack_self(mob/user) + if(used) + return + + used = TRUE + var/choice = input(user,"Who do you want dead?","Choose Your Victim") as null|anything in player_list + + if(!choice) + used = FALSE + return + else if(!isliving(choice)) + to_chat(user, "[choice] is already dead!") + used = FALSE + return + else + var/mob/living/L = choice + + message_admins("[key_name_admin(L)] has been marked for death by [key_name_admin(user)].") + log_admin("[key_name(L)] has been marked for death by [key_name(user)].") + + var/datum/objective/survive/survive = new + survive.owner = L.mind + L.mind.objectives += survive + to_chat(L, "You've been marked for death! Don't let the demons get you!") + L.color = "#FF0000" + spawn() + var/obj/effect/mine/pickup/bloodbath/B = new(L) + B.mineEffect(L) + + for(var/mob/living/carbon/human/H in player_list) + if(H == L) + continue + to_chat(H, "You have an overwhelming desire to kill [L]. They have been marked red! Go kill them!") + H.put_in_hands(new /obj/item/weapon/kitchen/knife/butcher(H)) + + qdel(src) + +// Legion + +// Staff of Storms + +/obj/item/weapon/staff/storm + name = "staff of storms" + desc = "An ancient staff retrieved from the remains of Legion. The wind stirs as you move it." + icon_state = "staffofstorms" + item_state = "staffofstorms" + icon = 'icons/obj/guns/magic.dmi' + slot_flags = SLOT_BACK + item_state = "staffofstorms" + w_class = 4 + force = 25 + damtype = BURN + hitsound = 'sound/weapons/sear.ogg' + var/storm_type = /datum/weather/ash_storm + var/storm_cooldown = 0 + +/obj/item/weapon/staff/storm/attack_self(mob/user) + if(storm_cooldown > world.time) + to_chat(user, "The staff is still recharging!") + return + + var/area/user_area = get_area(user) + var/datum/weather/A + var/z_level_name = space_manager.levels_by_name[user.z] + for(var/V in weather_master.existing_weather) + var/datum/weather/W = V + if(W.target_z == z_level_name && W.area_type == user_area.type) + A = W + break + if(A) + + if(A.stage != END_STAGE) + if(A.stage == WIND_DOWN_STAGE) + to_chat(user, "The storm is already ending! It would be a waste to use the staff now.") + return + user.visible_message("[user] holds [src] skywards as an orange beam travels into the sky!", \ + "You hold [src] skyward, dispelling the storm!") + playsound(user, 'sound/magic/Staff_Change.ogg', 200, 0) + A.wind_down() + return + else + A = new storm_type + A.name = "staff storm" + A.area_type = user_area.type + A.target_z = z_level_name + A.telegraph_duration = 100 + A.end_duration = 100 + + user.visible_message("[user] holds [src] skywards as red lightning crackles into the sky!", \ + "You hold [src] skyward, calling down a terrible storm!") + playsound(user, 'sound/magic/Staff_Change.ogg', 200, 0) + A.telegraph() + storm_cooldown = world.time + 200 + +// Hierophant + +//Hierophant + +/obj/item/weapon/hierophant_staff + name = "Hierophant's staff" + desc = "A large club with intense magic power infused into it." + icon_state = "hierophant_staff" + item_state = "hierophant_staff" + icon = 'icons/obj/guns/magic.dmi' + slot_flags = SLOT_BACK + w_class = 4 + force = 20 + hitsound = "swing_hit" + //hitsound = 'sound/weapons/sonic_jackhammer.ogg' + actions_types = list(/datum/action/item_action/vortex_recall, /datum/action/item_action/toggle_unfriendly_fire) + var/cooldown_time = 20 //how long the cooldown between non-melee ranged attacks is + var/chaser_cooldown = 101 //how long the cooldown between firing chasers at mobs is + var/chaser_timer = 0 //what our current chaser cooldown is + var/timer = 0 //what our current cooldown is + var/blast_range = 3 //how long the cardinal blast's walls are + var/obj/effect/hierophant/rune //the associated rune we teleport to + var/teleporting = FALSE //if we ARE teleporting + var/friendly_fire_check = FALSE //if the blasts we make will consider our faction against the faction of hit targets + +/obj/item/weapon/hierophant_staff/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + ..() + var/turf/T = get_turf(target) + if(!T || timer > world.time) + return + timer = world.time + CLICK_CD_MELEE //by default, melee attacks only cause melee blasts, and have an accordingly short cooldown + if(proximity_flag) + spawn(0) + aoe_burst(T, user) + add_logs(user, target, "fired 3x3 blast at", src) + else + if(ismineralturf(target) && get_dist(user, target) < 6) //target is minerals, we can hit it(even if we can't see it) + spawn(0) + cardinal_blasts(T, user) + timer = world.time + cooldown_time + else if(target in view(5, get_turf(user))) //if the target is in view, hit it + timer = world.time + cooldown_time + if(isliving(target) && chaser_timer <= world.time) //living and chasers off cooldown? fire one! + chaser_timer = world.time + chaser_cooldown + new /obj/effect/overlay/temp/hierophant/chaser(get_turf(user), user, target, 1.5, friendly_fire_check) + add_logs(user, target, "fired a chaser at", src) + else + spawn(0) + cardinal_blasts(T, user) //otherwise, just do cardinal blast + add_logs(user, target, "fired cardinal blast at", src) + else + to_chat(user, "That target is out of range!") //too far away + +/obj/item/weapon/hierophant_staff/ui_action_click(mob/user, actiontype) + if(actiontype == /datum/action/item_action/toggle_unfriendly_fire) //toggle friendly fire... + friendly_fire_check = !friendly_fire_check + to_chat(user, "You toggle friendly fire [friendly_fire_check ? "off":"on"]!") + return + if(user.get_active_hand() != src && user.get_inactive_hand() != src) //you need to hold the staff to teleport + to_chat(user, "You need to hold the staff in your hands to [rune ? "teleport with it" : "create a rune"]!") + return + if(!rune) + if(isturf(user.loc)) + user.visible_message("[user] holds [src] carefully in front of them, moving it in a strange pattern...", \ + "You start creating a hierophant rune to teleport to...") + timer = world.time + 51 + if(do_after(user, 50, target = user)) + var/turf/T = get_turf(user) + playsound(T,'sound/magic/Blind.ogg', 200, 1, -4) + new /obj/effect/overlay/temp/hierophant/telegraph/teleport(T, user) + var/obj/effect/hierophant/H = new/obj/effect/hierophant(T) + rune = H + user.update_action_buttons_icon() + user.visible_message("[user] creates a strange rune beneath them!", \ + "You create a hierophant rune, which you can teleport yourself and any allies to at any time!\n\ + You can remove the rune to place a new one by striking it with the staff.") + else + timer = world.time + else + to_chat(user, "You need to be on solid ground to produce a rune!") + return + if(get_dist(user, rune) <= 2) //rune too close abort + to_chat(user, "You are too close to the rune to teleport to it!") + return + if(is_blocked_turf(get_turf(rune))) + to_chat(user, "The rune is blocked by something, preventing teleportation!") + return + teleporting = TRUE //start channel + user.update_action_buttons_icon() + user.visible_message("[user] starts to glow faintly...") + timer = world.time + 50 + if(do_after(user, 40, target = user) && rune) + var/turf/T = get_turf(rune) + var/turf/source = get_turf(user) + if(is_blocked_turf(T)) + teleporting = FALSE + to_chat(user, "The rune is blocked by something, preventing teleportation!") + user.update_action_buttons_icon() + return + new /obj/effect/overlay/temp/hierophant/telegraph(T, user) + new /obj/effect/overlay/temp/hierophant/telegraph(source, user) + playsound(T,'sound/magic/blink.ogg', 200, 1) + //playsound(T,'sound/magic/Wand_Teleport.ogg', 200, 1) + playsound(source,'sound/magic/blink.ogg', 200, 1) + //playsound(source,'sound/machines/AirlockOpen.ogg', 200, 1) + if(!do_after(user, 3, target = user) || !rune) //no walking away shitlord + teleporting = FALSE + if(user) + user.update_action_buttons_icon() + return + if(is_blocked_turf(T)) + teleporting = FALSE + to_chat(user, "The rune is blocked by something, preventing teleportation!") + user.update_action_buttons_icon() + return + add_logs(user, rune, "teleported self from ([source.x],[source.y],[source.z]) to") + new /obj/effect/overlay/temp/hierophant/telegraph/teleport(T, user) + new /obj/effect/overlay/temp/hierophant/telegraph/teleport(source, user) + for(var/t in RANGE_TURFS(1, T)) + var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, user, TRUE) //blasts produced will not hurt allies + B.damage = 30 + for(var/t in RANGE_TURFS(1, source)) + var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, user, TRUE) //but absolutely will hurt enemies + B.damage = 30 + for(var/mob/living/L in range(1, source)) + spawn(0) + teleport_mob(source, L, T, user) //regardless, take all mobs near us along + sleep(6) //at this point the blasts detonate + else + timer = world.time + teleporting = FALSE + if(user) + user.update_action_buttons_icon() + +/obj/item/weapon/hierophant_staff/proc/teleport_mob(turf/source, mob/M, turf/target, mob/user) + var/turf/turf_to_teleport_to = get_step(target, get_dir(source, M)) //get position relative to caster + if(!turf_to_teleport_to || is_blocked_turf(turf_to_teleport_to)) + return + animate(M, alpha = 0, time = 2, easing = EASE_OUT) //fade out + sleep(1) + if(!M) + return + M.visible_message("[M] fades out!") + sleep(2) + if(!M) + return + M.forceMove(turf_to_teleport_to) + sleep(1) + if(!M) + return + animate(M, alpha = 255, time = 2, easing = EASE_IN) //fade IN + sleep(1) + if(!M) + return + M.visible_message("[M] fades in!") + if(user != M) + add_logs(user, M, "teleported", null, "from ([source.x],[source.y],[source.z])") + +/obj/item/weapon/hierophant_staff/proc/cardinal_blasts(turf/T, mob/living/user) //fire cardinal cross blasts with a delay + if(!T) + return + new /obj/effect/overlay/temp/hierophant/telegraph/cardinal(T, user) + playsound(T,'sound/magic/blink.ogg', 200, 1) + //playsound(T,'sound/effects/bin_close.ogg', 200, 1) + sleep(2) + new /obj/effect/overlay/temp/hierophant/blast(T, user, friendly_fire_check) + for(var/d in cardinal) + spawn(0) + blast_wall(T, d, user) + +/obj/item/weapon/hierophant_staff/proc/blast_wall(turf/T, dir, mob/living/user) //make a wall of blasts blast_range tiles long + if(!T) + return + var/range = blast_range + var/turf/previousturf = T + var/turf/J = get_step(previousturf, dir) + for(var/i in 1 to range) + if(!J) + return + new /obj/effect/overlay/temp/hierophant/blast(J, user, friendly_fire_check) + previousturf = J + J = get_step(previousturf, dir) + +/obj/item/weapon/hierophant_staff/proc/aoe_burst(turf/T, mob/living/user) //make a 3x3 blast around a target + if(!T) + return + new /obj/effect/overlay/temp/hierophant/telegraph(T, user) + playsound(T,'sound/magic/blink.ogg', 200, 1) + //playsound(T,'sound/effects/bin_close.ogg', 200, 1) + sleep(2) + for(var/t in RANGE_TURFS(1, T)) + new /obj/effect/overlay/temp/hierophant/blast(t, user, friendly_fire_check) \ No newline at end of file diff --git a/code/modules/mining/mine_areas.dm b/code/modules/mining/mine_areas.dm index 96ef9d3104b..e07d779c9a8 100644 --- a/code/modules/mining/mine_areas.dm +++ b/code/modules/mining/mine_areas.dm @@ -13,6 +13,7 @@ power_environ = 0 power_equip = 0 power_light = 0 + outdoors = 1 ambientsounds = list('sound/ambience/ambimine.ogg') /area/mine/dangerous/unexplored @@ -25,6 +26,7 @@ power_environ = 0 power_equip = 0 power_light = 0 + outdoors = 1 ambientsounds = list('sound/ambience/ambimine.ogg') /area/mine/lobby diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 83e794b9d20..6027a0272d8 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -2,10 +2,12 @@ //this item is intended to give the effect of entering the mine, so that light gradually fades /obj/effect/light_emitter - name = "Light-emtter" + name = "Light emtter" anchored = 1 + invisibility = 101 unacidable = 1 light_range = 8 + light_power = 0 /**********************Miner Lockers**************************/ @@ -89,7 +91,7 @@ var/excavation_amount = 100 /obj/item/weapon/pickaxe/proc/playDigSound() - playsound(src, pick(digsound),20,1) + playsound(src, pick(digsound),20,1) /obj/item/weapon/pickaxe/silver name = "silver-plated pickaxe" diff --git a/code/modules/mining/ore.dm b/code/modules/mining/ore.dm index ec36988cb20..9e4d268281c 100644 --- a/code/modules/mining/ore.dm +++ b/code/modules/mining/ore.dm @@ -62,6 +62,10 @@ points = 1 refined_type = /obj/item/stack/sheet/glass materials = list(MAT_GLASS=MINERAL_MATERIAL_AMOUNT) + +/obj/item/weapon/ore/glass/basalt + name = "volcanic ash" + icon_state = "volcanic_sand" /obj/item/weapon/ore/glass/attack_self(mob/living/user as mob) to_chat(user, "You use the sand to make sandstone.") diff --git a/code/modules/mob/dead/observer/spells.dm b/code/modules/mob/dead/observer/spells.dm index 39dae4c0746..620c1854e73 100644 --- a/code/modules/mob/dead/observer/spells.dm +++ b/code/modules/mob/dead/observer/spells.dm @@ -25,7 +25,7 @@ var/global/list/boo_phrases=list( invocation_type = "none" range = 1 // Or maybe 3? -/obj/effect/proc_holder/spell/aoe_turf/boo/cast(list/targets) +/obj/effect/proc_holder/spell/aoe_turf/boo/cast(list/targets, mob/user = usr) for(var/turf/T in targets) for(var/atom/A in T.contents) diff --git a/code/modules/mob/living/carbon/brain/brain.dm b/code/modules/mob/living/carbon/brain/brain.dm index 555fcff29da..e8afbb0360b 100644 --- a/code/modules/mob/living/carbon/brain/brain.dm +++ b/code/modules/mob/living/carbon/brain/brain.dm @@ -10,6 +10,7 @@ /mob/living/carbon/brain/New() create_reagents(330) + add_language("Galactic Common") ..() /mob/living/carbon/brain/Destroy() diff --git a/code/modules/mob/living/carbon/carbon_defenses.dm b/code/modules/mob/living/carbon/carbon_defenses.dm index efebc58ffc1..b2dcf20f1b2 100644 --- a/code/modules/mob/living/carbon/carbon_defenses.dm +++ b/code/modules/mob/living/carbon/carbon_defenses.dm @@ -36,4 +36,4 @@ for(var/datum/disease/D in user.viruses) if(D.IsSpreadByTouch()) ContractDisease(D) - return 0 + return 0 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 558a6f8e9de..f9315966d27 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -83,12 +83,12 @@ message = "[src] pings at [param]." else message = "[src] pings." - playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) + playsound(loc, 'sound/machines/ping.ogg', 50, 0) m_type = 2 if("buzz2") message = "[src] emits an irritated buzzing sound." - playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) + playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) m_type = 2 if("buzz", "buzzes") @@ -105,7 +105,7 @@ message = "[src] buzzes at [param]." else message = "[src] buzzes." - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) m_type = 2 if("beep", "beeps") @@ -122,7 +122,7 @@ message = "[src] beeps at [param]." else message = "[src] beeps." - playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0) + playsound(loc, 'sound/machines/twobeep.ogg', 50, 0) m_type = 2 if("drone", "drones", "hum", "hums", "rumble", "rumbles") @@ -139,7 +139,7 @@ message = "[src] drones at [param]." else message = "[src] rumbles." - playsound(src.loc, 'sound/voice/DraskTalk.ogg', 50, 0) + playsound(loc, 'sound/voice/DraskTalk.ogg', 50, 0) m_type = 2 if("squish", "squishes") @@ -156,7 +156,7 @@ message = "[src] squishes at [param]." else message = "[src] squishes." - playsound(src.loc, 'sound/effects/slime_squish.ogg', 50, 0) //Credit to DrMinky (freesound.org) for the sound. + playsound(loc, 'sound/effects/slime_squish.ogg', 50, 0) //Credit to DrMinky (freesound.org) for the sound. m_type = 2 if("yes") @@ -173,7 +173,7 @@ message = "[src] emits an affirmative blip at [param]." else message = "[src] emits an affirmative blip." - playsound(src.loc, 'sound/machines/synth_yes.ogg', 50, 0) + playsound(loc, 'sound/machines/synth_yes.ogg', 50, 0) m_type = 2 if("no") @@ -190,7 +190,7 @@ message = "[src] emits a negative blip at [param]." else message = "[src] emits a negative blip." - playsound(src.loc, 'sound/machines/synth_no.ogg', 50, 0) + playsound(loc, 'sound/machines/synth_no.ogg', 50, 0) m_type = 2 if("wag", "wags") @@ -202,7 +202,7 @@ else if(species.bodyflags & TAIL_WAGGING) if(!wear_suit || !(wear_suit.flags_inv & HIDETAIL) && !istype(wear_suit, /obj/item/clothing/suit/space)) message = "[src] starts wagging \his tail." - src.start_tail_wagging(1) + start_tail_wagging(1) else return else @@ -212,15 +212,25 @@ if("swag", "swags") if(species.bodyflags & TAIL_WAGGING || body_accessory) message = "[src] stops wagging \his tail." - src.stop_tail_wagging(1) + stop_tail_wagging(1) else return m_type = 1 if("airguitar") - if(!src.restrained()) + if(!restrained()) message = "[src] is strumming the air and headbanging like a safari chimp." m_type = 1 + + if("dance") + if(!restrained()) + message = "[src] dances around happily." + m_type = 1 + + if("jump") + if(!restrained()) + message = "[src] jumps!" + m_type = 1 if("blink", "blinks") message = "[src] blinks." @@ -231,7 +241,7 @@ m_type = 1 if("bow", "bows") - if(!src.buckled) + if(!buckled) var/M = null if(param) for(var/mob/A in view(null, null)) @@ -248,7 +258,7 @@ m_type = 1 if("salute", "salutes") - if(!src.buckled) + if(!buckled) var/M = null if(param) for(var/mob/A in view(null, null)) @@ -288,13 +298,13 @@ message = "[src] makes a peculiar noise." m_type = 2 if("clap", "claps") - if(!src.restrained()) + if(!restrained()) message = "[src] claps." m_type = 2 if(miming) m_type = 1 if("flap", "flaps") - if(!src.restrained()) + if(!restrained()) message = "[src] flaps \his wings." m_type = 2 if(miming) @@ -302,7 +312,7 @@ if("flip", "flips") m_type = 1 - if(!src.restrained()) + if(!restrained()) var/M = null if(param) for(var/mob/A in view(1, null)) @@ -340,7 +350,7 @@ SpinAnimation(5,1) if("aflap", "aflaps") - if(!src.restrained()) + if(!restrained()) message = "[src] flaps \his wings ANGRILY!" m_type = 2 if(miming) @@ -376,7 +386,7 @@ if("faint", "faints") message = "[src] faints." - if(src.sleeping) + if(sleeping) return //Can't faint while asleep AdjustSleeping(2) m_type = 1 @@ -578,11 +588,11 @@ message = "[src] takes a drag from a cigarette and blows \"[M]\" out in smoke." m_type = 1 else - message = "[src] says, \"[M], please. They had a family.\" [src.name] takes a drag from a cigarette and blows their name out in smoke." + message = "[src] says, \"[M], please. They had a family.\" [name] takes a drag from a cigarette and blows their name out in smoke." m_type = 2 if("point", "points") - if(!src.restrained()) + if(!restrained()) var/atom/M = null if(param) for(var/atom/A as mob|obj|turf in view()) @@ -597,7 +607,7 @@ m_type = 1 if("raise", "raises") - if(!src.restrained()) + if(!restrained()) message = "[src] raises a hand." m_type = 1 @@ -610,12 +620,12 @@ m_type = 1 if("signal", "signals") - if(!src.restrained()) + if(!restrained()) var/t1 = round(text2num(param)) if(isnum(t1)) - if(t1 <= 5 && (!src.r_hand || !src.l_hand)) + if(t1 <= 5 && (!r_hand || !l_hand)) message = "[src] raises [t1] finger\s." - else if(t1 <= 10 && (!src.r_hand && !src.l_hand)) + else if(t1 <= 10 && (!r_hand && !l_hand)) message = "[src] raises [t1] finger\s." m_type = 1 @@ -699,7 +709,7 @@ if("hug", "hugs") m_type = 1 - if(!src.restrained()) + if(!restrained()) var/M = null if(param) for(var/mob/A in view(1, null)) @@ -716,7 +726,7 @@ if("handshake") m_type = 1 - if(!src.restrained() && !src.r_hand) + if(!restrained() && !r_hand) var/mob/M = null if(param) for(var/mob/A in view(1, null)) @@ -734,7 +744,7 @@ if("dap", "daps") m_type = 1 - if(!src.restrained()) + if(!restrained()) var/M = null if(param) for(var/mob/A in view(1, null)) @@ -748,7 +758,7 @@ if("slap", "slaps") m_type = 1 - if(!src.restrained()) + if(!restrained()) var/M = null if(param) for(var/mob/A in view(1, null)) @@ -757,11 +767,11 @@ break if(M) message = "[src] slaps [M] across the face. Ouch!" - playsound(src.loc, 'sound/effects/snap.ogg', 50, 1) + playsound(loc, 'sound/effects/snap.ogg', 50, 1) else message = "[src] slaps \himself!" - playsound(src.loc, 'sound/effects/snap.ogg', 50, 1) - src.adjustFireLoss(4) + playsound(loc, 'sound/effects/snap.ogg', 50, 1) + adjustFireLoss(4) if("scream", "screams") if(miming) @@ -772,9 +782,9 @@ message = "[src] [species.scream_verb]!" m_type = 2 if(gender == FEMALE) - playsound(src.loc, "[species.female_scream_sound]", 80, 1, 0, pitch = get_age_pitch()) + playsound(loc, "[species.female_scream_sound]", 80, 1, 0, pitch = get_age_pitch()) else - playsound(src.loc, "[species.male_scream_sound]", 80, 1, 0, pitch = get_age_pitch()) //default to male screams if no gender is present. + playsound(loc, "[species.male_scream_sound]", 80, 1, 0, pitch = get_age_pitch()) //default to male screams if no gender is present. else message = "[src] makes a very loud noise." @@ -799,24 +809,24 @@ return message = "[src] snaps \his fingers." - playsound(src.loc, 'sound/effects/fingersnap.ogg', 50, 1, -3) + playsound(loc, 'sound/effects/fingersnap.ogg', 50, 1, -3) else message = "[src] snaps \his fingers right off!" - playsound(src.loc, 'sound/effects/snap.ogg', 50, 1) + playsound(loc, 'sound/effects/snap.ogg', 50, 1) // Needed for M_TOXIC_FART if("fart", "farts") if(reagents.has_reagent("simethicone")) return -// playsound(src.loc, 'sound/effects/fart.ogg', 50, 1, -3) //Admins still vote no to fun +// playsound(loc, 'sound/effects/fart.ogg', 50, 1, -3) //Admins still vote no to fun if(locate(/obj/item/weapon/storage/bible) in get_turf(src)) to_chat(viewers(src), "[src] farts on the Bible!") to_chat(viewers(src), "A mysterious force smites [src]!") var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread s.set_up(3, 1, src) s.start() - src.gib() + gib() else if(TOXIC_FARTS in mutations) message = "[src] unleashes a [pick("horrible","terrible","foul","disgusting","awful")] fart." else if(SUPER_FART in mutations) @@ -841,9 +851,9 @@ M.reagents.add_reagent("jenkem", 1) if("help") - var/emotelist = "aflap(s), airguitar, blink(s), blink(s)_r, blush(es), bow(s)-(none)/mob, burp(s), choke(s), chuckle(s), clap(s), collapse(s), cough(s),cry, cries, custom, dap(s)(none)/mob," \ - + " deathgasp(s), drool(s), eyebrow,fart(s), faint(s), flap(s), flip(s), frown(s), gasp(s), giggle(s), glare(s)-(none)/mob, grin(s), groan(s), grumble(s), handshake-mob, hug(s)-(none)/mob," \ - + " glare(s)-(none)/mob, grin(s), johnny, laugh(s), look(s)-(none)/mob, moan(s), mumble(s), nod(s), pale(s), point(s)-atom, quiver(s), raise(s), salute(s)-(none)/mob, scream(s), shake(s)," \ + var/emotelist = "aflap(s), airguitar, blink(s), blink(s)_r, blush(es), bow(s)-(none)/mob, burp(s), choke(s), chuckle(s), clap(s), collapse(s), cough(s),cry, cries, custom, dance, dap(s)(none)/mob," \ + + " deathgasp(s), drool(s), eyebrow, fart(s), faint(s), flap(s), flip(s), frown(s), gasp(s), giggle(s), glare(s)-(none)/mob, grin(s), groan(s), grumble(s), grin(s)," \ + + " handshake-mob, hug(s)-(none)/mob, johnny, jump, laugh(s), look(s)-(none)/mob, moan(s), mumble(s), nod(s), pale(s), point(s)-atom, quiver(s), raise(s), salute(s)-(none)/mob, scream(s), shake(s)," \ + " shiver(s), shrug(s), sigh(s), signal(s)-#1-10,slap(s)-(none)/mob, smile(s),snap(s), sneeze(s), sniff(s), snore(s), stare(s)-(none)/mob, swag(s), tremble(s), twitch(es), twitch(es)_s," \ + " wag(s), wave(s), whimper(s), wink(s), yawn(s)" if(species.name == "Machine") diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 48cc455c113..986a3ffb9c4 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -38,6 +38,7 @@ emp_act var/obj/item/organ/external/organ = get_organ(check_zone(def_zone)) if(isnull(organ)) + . = bullet_act(P, "chest") //act on chest instead return //Shrapnel @@ -56,6 +57,18 @@ emp_act organ.add_autopsy_data(P.name, P.damage) // Add the bullet's name to the autopsy data return (..(P , def_zone)) + +/mob/living/carbon/human/check_projectile_dismemberment(obj/item/projectile/P, def_zone) + var/obj/item/organ/external/affecting = get_organ(check_zone(def_zone)) + if(affecting && !affecting.cannot_amputate && affecting.get_damage() >= (affecting.max_damage - P.dismemberment)) + var/damtype = DROPLIMB_EDGE + switch(P.damage_type) + if(BRUTE) + damtype = DROPLIMB_BLUNT + if(BURN) + damtype = DROPLIMB_BURN + + affecting.droplimb(FALSE, damtype) /mob/living/carbon/human/getarmor(var/def_zone, var/type) var/armorval = 0 diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index 26ca4af83e8..7384ccc6796 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -1,10 +1,10 @@ -/mob/living/carbon/human/say(var/message) +/mob/living/carbon/human/say(var/message, var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE) var/alt_name = "" if(name != GetVoice()) alt_name = " (as [get_id_name("Unknown")])" - ..(message, alt_name = alt_name) //ohgod we should really be passing a datum here. + ..(message, alt_name = alt_name, sanitize = sanitize, ignore_speech_problems = ignore_speech_problems, ignore_atmospherics = ignore_atmospherics) //ohgod we should really be passing a datum here. /mob/living/carbon/human/proc/forcesay(list/append) if(stat == CONSCIOUS) diff --git a/code/modules/mob/living/carbon/human/species/shadow.dm b/code/modules/mob/living/carbon/human/species/shadow.dm index ca26348d188..66a62f1b10c 100644 --- a/code/modules/mob/living/carbon/human/species/shadow.dm +++ b/code/modules/mob/living/carbon/human/species/shadow.dm @@ -7,7 +7,10 @@ default_language = "Galactic Common" unarmed_type = /datum/unarmed_attack/claws + darksight = 8 + + ignored_by = list(/mob/living/simple_animal/hostile/faithless) blood_color = "#CCCCCC" flesh_color = "#AAAAAA" @@ -20,6 +23,7 @@ oxy_mod = 0 virus_immune = 1 + dietflags = DIET_OMNI //the mutation process allowed you to now digest all foods regardless of initial race reagent_tag = PROCESS_ORG suicide_messages = list( @@ -27,14 +31,46 @@ "is jamming their claws into their eye sockets!", "is twisting their own neck!", "is staring into the closest light source!") + + var/grant_vision_toggle = 1 + var/datum/action/innate/shadow/darkvision/vision_toggle + +/datum/action/innate/shadow/darkvision //Darkvision toggle so shadowpeople can actually see where darkness is + name = "Toggle Darkvision" + check_flags = AB_CHECK_CONSCIOUS + background_icon_state = "bg_default" + button_icon_state = "blind" + +/datum/action/innate/shadow/darkvision/Activate() + var/mob/living/carbon/human/H = owner + if(!H.vision_type) + H.vision_type = new /datum/vision_override/nightvision + to_chat(H, "You adjust your vision to pierce the darkness.") + else + H.vision_type = null + to_chat(H, "You adjust your vision to recognize the shadows.") + +/datum/species/shadow/grant_abilities(var/mob/living/carbon/human/H) + . = ..() + if(grant_vision_toggle) + vision_toggle = new + vision_toggle.Grant(H) + +/datum/species/shadow/remove_abilities(var/mob/living/carbon/human/H) + . = ..() + if(grant_vision_toggle && vision_toggle) + H.vision_type = null + vision_toggle.Remove(H) /datum/species/shadow/handle_life(var/mob/living/carbon/human/H) var/light_amount = 0 if(isturf(H.loc)) var/turf/T = H.loc - light_amount = T.get_lumcount()*10 + light_amount = T.get_lumcount() * 10 if(light_amount > 2) //if there's enough light, start dying H.take_overall_damage(1,1) + H.throw_alert("lightexposure", /obj/screen/alert/lightexposure) else if(light_amount < 2) //heal in the dark - H.heal_overall_damage(1,1) \ No newline at end of file + H.heal_overall_damage(1,1) + H.clear_alert("lightexposure") \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index e2238057533..7745f950aec 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -50,6 +50,7 @@ var/invis_sight = SEE_INVISIBLE_LIVING var/darksight = 2 + var/hazard_high_pressure = HAZARD_HIGH_PRESSURE // Dangerously high pressure. var/warning_high_pressure = WARNING_HIGH_PRESSURE // High pressure warning. var/warning_low_pressure = WARNING_LOW_PRESSURE // Low pressure warning. @@ -84,6 +85,8 @@ var/ventcrawler = 0 //Determines if the mob can go through the vents. var/has_fine_manipulation = 1 // Can use small items. + + var/mob/living/list/ignored_by = list() // list of mobs that will ignore this species var/list/allowed_consumed_mobs = list() //If a species can consume mobs, put the type of mobs it can consume here. @@ -162,7 +165,6 @@ "l_foot" = list("path" = /obj/item/organ/external/foot), "r_foot" = list("path" = /obj/item/organ/external/foot/right) ) - var/cyborg_type = "Cyborg" var/list/proc/species_abilities = list() /datum/species/New() diff --git a/code/modules/mob/living/carbon/human/species/station.dm b/code/modules/mob/living/carbon/human/species/station.dm index 5bde6ce8e5b..ec4fe892af9 100644 --- a/code/modules/mob/living/carbon/human/species/station.dm +++ b/code/modules/mob/living/carbon/human/species/station.dm @@ -808,8 +808,8 @@ return ..() /datum/species/diona/handle_life(var/mob/living/carbon/human/H) - var/rads = H.radiation/25 - H.radiation = Clamp(H.radiation - rads, 0, 100) + var/rads = H.radiation / 25 + H.apply_effect(-rads,IRRADIATE,0) H.nutrition += rads H.adjustBruteLoss(-(rads)) H.adjustOxyLoss(-(rads)) @@ -818,18 +818,23 @@ var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing if(isturf(H.loc)) //else, there's considered to be no light var/turf/T = H.loc - light_amount = min(T.get_lumcount()*10, 5) //hardcapped so it's not abused by having a ton of flashlights + light_amount = min(T.get_lumcount() * 10, 5) //hardcapped so it's not abused by having a ton of flashlights H.nutrition += light_amount H.traumatic_shock -= light_amount if(H.nutrition > NUTRITION_LEVEL_WELL_FED) H.nutrition = NUTRITION_LEVEL_WELL_FED + + if(light_amount > 0) + H.clear_alert("nolight") + else + H.throw_alert("nolight", /obj/screen/alert/nolight) if((light_amount >= 5) && !H.suiciding) //if there's enough light, heal + H.adjustBruteLoss(-(light_amount/2)) H.adjustFireLoss(-(light_amount/4)) H.adjustOxyLoss(-(light_amount)) - if(H.nutrition < 200) H.take_overall_damage(10,0) H.traumatic_shock++ diff --git a/code/modules/mob/living/carbon/superheroes.dm b/code/modules/mob/living/carbon/superheroes.dm index 60e1cd82654..fac41aa66f7 100644 --- a/code/modules/mob/living/carbon/superheroes.dm +++ b/code/modules/mob/living/carbon/superheroes.dm @@ -147,69 +147,69 @@ action_icon_state = "spell_greytide" var/recruiting = 0 -/obj/effect/proc_holder/spell/targeted/recruit/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/recruit/cast(list/targets,mob/living/user = usr) for(var/mob/living/carbon/human/target in targets) var/obj/item/organ/external/head/head_organ = target.get_organ("head") if(ticker.mode.greyshirts.len >= 3) - to_chat(usr, "You have already recruited the maximum number of henchmen.") - if(!in_range(usr, target)) - to_chat(usr, "You need to be closer to enthrall [target].") + to_chat(user, "You have already recruited the maximum number of henchmen.") + if(!in_range(user, target)) + to_chat(user, "You need to be closer to enthrall [target].") charge_counter = charge_max return if(!target.ckey) - to_chat(usr, "The target has no mind.") + to_chat(user, "The target has no mind.") charge_counter = charge_max return if(target.stat) - to_chat(usr, "The target must be conscious.") + to_chat(user, "The target must be conscious.") charge_counter = charge_max return if(!ishuman(target)) - to_chat(usr, "You can only recruit humans.") + to_chat(user, "You can only recruit humans.") charge_counter = charge_max return if(target.mind.assigned_role != "Civilian") - to_chat(usr, "You can only recruit Civilians.") + to_chat(user, "You can only recruit Civilians.") if(recruiting) - to_chat(usr, "You are already recruiting!") + to_chat(user, "You are already recruiting!") charge_counter = charge_max return recruiting = 1 - to_chat(usr, "This target is valid. You begin the recruiting process.") - to_chat(target, "[usr] focuses in concentration. Your head begins to ache.") + to_chat(user, "This target is valid. You begin the recruiting process.") + to_chat(target, "[user] focuses in concentration. Your head begins to ache.") for(var/progress = 0, progress <= 3, progress++) switch(progress) if(1) - to_chat(usr, "You begin by introducing yourself and explaining what you're about.") - usr.visible_message("[usr]'s introduces himself and explains his plans.") + to_chat(user, "You begin by introducing yourself and explaining what you're about.") + user.visible_message("[user]'s introduces himself and explains his plans.") if(2) - to_chat(usr, "You begin the recruitment of [target].") - usr.visible_message("[usr] leans over towards [target], whispering excitedly as he gives a speech.") - to_chat(target, "You feel yourself agreeing with [usr], and a surge of loyalty begins building.") + to_chat(user, "You begin the recruitment of [target].") + user.visible_message("[user] leans over towards [target], whispering excitedly as he gives a speech.") + to_chat(target, "You feel yourself agreeing with [user], and a surge of loyalty begins building.") target.Weaken(12) sleep(20) if(isloyal(target)) - to_chat(usr, "They are enslaved by Nanotrasen. You feel their interest in your cause wane and disappear.") - usr.visible_message("[usr] stops talking for a moment, then moves back away from [target].") + to_chat(user, "They are enslaved by Nanotrasen. You feel their interest in your cause wane and disappear.") + user.visible_message("[user] stops talking for a moment, then moves back away from [target].") to_chat(target, "Your mindshield implant activates, protecting you from conversion.") return if(3) - to_chat(usr, "You begin filling out the application form with [target].") - usr.visible_message("[usr] pulls out a pen and paper and begins filling an application form with [target].") - to_chat(target, "You are being convinced by [usr] to fill out an application form to become a henchman.")//Ow the edge + to_chat(user, "You begin filling out the application form with [target].") + user.visible_message("[user] pulls out a pen and paper and begins filling an application form with [target].") + to_chat(target, "You are being convinced by [user] to fill out an application form to become a henchman.")//Ow the edge - if(!do_mob(usr, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a mindshield implant - to_chat(usr, "The enrollment process has been interrupted - you have lost the attention of [target].") - to_chat(target, "You move away and are no longer under the charm of [usr]. The application form is null and void.") + if(!do_mob(user, target, 100)) //around 30 seconds total for enthralling, 45 for someone with a mindshield implant + to_chat(user, "The enrollment process has been interrupted - you have lost the attention of [target].") + to_chat(target, "You move away and are no longer under the charm of [user]. The application form is null and void.") recruiting = 0 return recruiting = 0 - to_chat(usr, "You have recruited [target] as your henchman!") - to_chat(target, "You have decided to enroll as a henchman for [usr]. You are now part of the feared 'Greyshirts'.") - to_chat(target, "You must follow the orders of [usr], and help him succeed in his dastardly schemes.") - to_chat(target, "You may not harm other Greyshirt or [usr]. However, you do not need to obey other Greyshirts.") + to_chat(user, "You have recruited [target] as your henchman!") + to_chat(target, "You have decided to enroll as a henchman for [user]. You are now part of the feared 'Greyshirts'.") + to_chat(target, "You must follow the orders of [user], and help him succeed in his dastardly schemes.") + to_chat(target, "You may not harm other Greyshirt or [user]. However, you do not need to obey other Greyshirts.") ticker.mode.greyshirts += target.mind target.set_species("Human") head_organ.h_style = "Bald" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index c1f3f338893..bc849f9674e 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1,7 +1,15 @@ /mob/living/Destroy() + if(ranged_ability) + ranged_ability.remove_ranged_ability(src) ..() return QDEL_HINT_HARDDEL_NOW + +/mob/living/ghostize(can_reenter_corpse = 1) + var/prev_client = client + if(..()) + if(ranged_ability && prev_client) + ranged_ability.remove_mousepointer(prev_client) /mob/living/proc/OpenCraftingMenu() return diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 03e8331bde2..609d166183d 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -49,7 +49,12 @@ if(!P.nodamage) apply_damage(P.damage, P.damage_type, def_zone, armor) + if(P.dismemberment) + check_projectile_dismemberment(P, def_zone) return P.on_hit(src, armor, def_zone) + +/mob/living/proc/check_projectile_dismemberment(obj/item/projectile/P, def_zone) + return 0 /mob/living/proc/electrocute_act(shock_damage, obj/source, siemens_coeff = 1, safety = 0, tesla_shock = 0) return 0 //only carbon liveforms have this proc diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm index 0ec5f16c787..66aa234e2ca 100644 --- a/code/modules/mob/living/living_defines.dm +++ b/code/modules/mob/living/living_defines.dm @@ -52,3 +52,5 @@ var/list/surgeries = list() //a list of surgery datums. generally empty, they're added when the player wants them. var/gene_stability = DEFAULT_GENE_STABILITY + + var/obj/effect/proc_holder/ranged_ability //Any ranged ability the mob has, as a click override diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index a64126db27a..a2fc8e934cb 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -9,6 +9,10 @@ //Vents if(ventcrawler) to_chat(src, "You can ventcrawl! Use alt+click on vents to quickly travel about the station.") + + if(ranged_ability) + ranged_ability.add_ranged_ability(src, "You currently have [ranged_ability] active!") + //Should update regardless of if we can ventcrawl, since we can end up in pipes in other ways. update_pipe_vision() diff --git a/code/modules/mob/living/logout.dm b/code/modules/mob/living/logout.dm index 10ba19b4302..6235aecf2c4 100644 --- a/code/modules/mob/living/logout.dm +++ b/code/modules/mob/living/logout.dm @@ -1,4 +1,6 @@ /mob/living/Logout() + if(ranged_ability && client) + ranged_ability.remove_mousepointer(client) ..() if(mind) if(!key) //key and mind have become seperated. I believe this is for when a staff member aghosts. diff --git a/code/modules/mob/living/say.dm b/code/modules/mob/living/say.dm index bbf238fd037..b0f42e9a8cb 100644 --- a/code/modules/mob/living/say.dm +++ b/code/modules/mob/living/say.dm @@ -14,6 +14,7 @@ var/list/department_radio_keys = list( ":u" = "Supply", "#u" = "Supply", ".u" = "Supply", ":z" = "Service", "#z" = "Service", ".z" = "Service", ":p" = "AI Private", "#p" = "AI Private", ".p" = "AI Private", + ":x" = "cords", "#x" = "cords", ".x" = "cords", ":R" = "right ear", "#R" = "right ear", ".R" = "right ear", ":L" = "left ear", "#L" = "left ear", ".L" = "left ear", @@ -30,7 +31,8 @@ var/list/department_radio_keys = list( ":Z" = "Service", "#Z" = "Service", ".Z" = "Service", ":P" = "AI Private", "#P" = "AI Private", ".P" = "AI Private", ":-" = "Special Ops", "#-" = "Special Ops", ".-" = "Special Ops", - ":_" = "SyndTeam", "#_" = "SyndTeam", "._" = "SyndTeam" + ":_" = "SyndTeam", "#_" = "SyndTeam", "._" = "SyndTeam", + ":X" = "cords", "#X" = "cords", ".X" = "cords" ) @@ -106,13 +108,14 @@ proc/get_radio_key_from_channel(var/channel) return returns -/mob/living/say(var/message, var/datum/language/speaking = null, var/verb = "says", var/alt_name="") +/mob/living/say(var/message, var/datum/language/speaking = null, var/verb = "says", var/alt_name = "", var/sanitize = TRUE, var/ignore_speech_problems = FALSE, var/ignore_atmospherics = FALSE) if(client) if(client.prefs.muted & MUTE_IC) to_chat(src, "You cannot speak in IC (Muted).") return - message = trim_strip_html_properly(message) + if(sanitize) + message = trim_strip_html_properly(message) if(stat) if(stat == DEAD) @@ -146,6 +149,15 @@ proc/get_radio_key_from_channel(var/channel) if(speaking && (speaking.flags & HIVEMIND)) speaking.broadcast(src,trim(message)) return 1 + + if(message_mode == "cords") + if(iscarbon(src)) + var/mob/living/carbon/C = src + var/obj/item/organ/internal/vocal_cords/V = C.get_int_organ(/obj/item/organ/internal/vocal_cords) + if(V && V.can_speak_with()) + C.say(V.handle_speech(message), sanitize = FALSE, ignore_speech_problems = TRUE, ignore_atmospherics = TRUE) + V.speak_with(message) //words come before actions + return 1 verb = say_quote(message, speaking) @@ -162,7 +174,7 @@ proc/get_radio_key_from_channel(var/channel) message = handle_autohiss(message, speaking) - if(speaking && !(speaking.flags & NO_STUTTER)) + if(!ignore_speech_problems && (speaking && !(speaking.flags & NO_STUTTER))) var/list/handle_s = handle_speech_problems(message, verb) message = handle_s[1] verb = handle_s[2] @@ -217,12 +229,13 @@ proc/get_radio_key_from_channel(var/channel) //make sure the air can transmit speech - speaker's side var/datum/gas_mixture/environment = T.return_air() var/pressure = environment ? environment.return_pressure() : 0 - if(pressure < SOUND_MINIMUM_PRESSURE) - message_range = 1 + if(!ignore_atmospherics) + if(pressure < SOUND_MINIMUM_PRESSURE) + message_range = 1 - if(pressure < ONE_ATMOSPHERE * 0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet - italics = 1 - sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact + if(pressure < ONE_ATMOSPHERE * 0.4) //sound distortion pressure, to help clue people in that the air is thin, even if it isn't a vacuum yet + italics = 1 + sound_vol *= 0.5 //muffle the sound a bit, so it's like we're actually talking through contact var/list/hear = hear(message_range, T) var/list/hearturfs = list() diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 39f73310566..152ed03fec8 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -204,7 +204,7 @@ modules += new /obj/item/weapon/gripper(src) modules += new /obj/item/weapon/matter_decompiler(src) modules += new /obj/item/device/floor_painter(src) - + modules += new /obj/item/areaeditor/blueprints/cyborg(src) emag = new /obj/item/borg/stun(src) for(var/G in stacktypes) //Attempt to unify Engi-Borg material stacks into fewer lines. See Line 492 for example. Variables changed out of paranoia. diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index ffd3f91cf5c..3ac858a8a6c 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -788,6 +788,7 @@ Pass a positive integer as an argument to override a bot's default speed. frustration = 0 /mob/living/simple_animal/bot/show_inv() + return /mob/living/simple_animal/bot/proc/show_controls(mob/M) users |= M diff --git a/code/modules/mob/living/simple_animal/friendly/cockroach.dm b/code/modules/mob/living/simple_animal/friendly/cockroach.dm new file mode 100644 index 00000000000..c0f7efca2c6 --- /dev/null +++ b/code/modules/mob/living/simple_animal/friendly/cockroach.dm @@ -0,0 +1,51 @@ +/mob/living/simple_animal/cockroach + name = "cockroach" + desc = "This station is just crawling with bugs." + icon_state = "cockroach" + icon_dead = "cockroach" + health = 1 + maxHealth = 1 + turns_per_move = 5 + atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0) + minbodytemp = 270 + maxbodytemp = INFINITY + pass_flags = PASSTABLE | PASSGRILLE | PASSMOB + mob_size = MOB_SIZE_TINY + response_help = "pokes" + response_disarm = "shoos" + response_harm = "splats" + density = 0 + ventcrawler = 2 + gold_core_spawnable = 2 + var/squish_chance = 50 + loot = list(/obj/effect/decal/cleanable/deadcockroach) + del_on_death = 1 + +/mob/living/simple_animal/cockroach/death(gibbed) + if(ticker.cinematic) //If the nuke is going off, then cockroaches are invincible. Keeps the nuke from killing them, cause cockroaches are immune to nukes. + return + ..() + +/mob/living/simple_animal/cockroach/Crossed(var/atom/movable/AM) + if(isliving(AM)) + var/mob/living/A = AM + if(A.mob_size > MOB_SIZE_SMALL) + if(prob(squish_chance)) + A.visible_message("\The [A] squashed \the [name].", "You squashed \the [name].") + death() + else + visible_message("\The [name] avoids getting crushed.") + else if(istype(AM, /obj/structure)) + visible_message("As \the [AM] moved over \the [name], it was crushed.") + death() + +/mob/living/simple_animal/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach. + return + +/obj/effect/decal/cleanable/deadcockroach + name = "cockroach guts" + desc = "One bug squashed. Four more will rise in its place." + icon = 'icons/effects/blood.dmi' + icon_state = "xfloor1" + random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7") + anchored = 1 \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/friendly/corgi.dm b/code/modules/mob/living/simple_animal/friendly/corgi.dm index 2f45d3e802e..dfc0bce32a3 100644 --- a/code/modules/mob/living/simple_animal/friendly/corgi.dm +++ b/code/modules/mob/living/simple_animal/friendly/corgi.dm @@ -58,7 +58,7 @@ dat += " Back: | [(inventory_back && !(inventory_back.flags&ABSTRACT)) ? inventory_back : "Empty"] | | Collar: | [(collar && !(collar.flags&ABSTRACT)) ? collar : "Empty"] | Collar: | [(collar && !(collar.flags&ABSTRACT)) ? collar : "Empty"] | |
| Left Hand: | [(l_hand && !(l_hand.flags&ABSTRACT)) ? l_hand : "Empty"] |
| Right Hand: | [(r_hand && !(r_hand.flags&ABSTRACT)) ? r_hand : "Empty"] |
| Collar: | [(collar && !(collar.flags&ABSTRACT)) ? collar : "Empty"] |
| Collar: | [(collar && !(collar.flags&ABSTRACT)) ? collar : "Empty"] |
{{:value}} - |
+ {{:helper.link('Select', null, { 'shipSelect' : value })}} |
+
There are no available shipping destinations. |