| 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/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/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm
index 691394eb1b4..88638abde76 100644
--- a/code/modules/clothing/head/misc.dm
+++ b/code/modules/clothing/head/misc.dm
@@ -361,6 +361,11 @@
desc = "A hat with bells, to add some merryness to the suit."
icon_state = "jester_hat"
+/obj/item/clothing/head/rice_hat
+ name = "rice hat"
+ desc = "Welcome to the rice fields, motherfucker."
+ icon_state = "rice_hat"
+
/obj/item/clothing/head/griffin
name = "griffon head"
desc = "Why not 'eagle head'? Who knows."
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index 886a98e4678..0ccdf7027d3 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -38,7 +38,7 @@
item_state = "apron"
blood_overlay_type = "armor"
body_parts_covered = UPPER_TORSO|LOWER_TORSO
- allowed = list (/obj/item/weapon/reagent_containers/spray/plantbgone,/obj/item/device/analyzer/plant_analyzer,/obj/item/seeds,/obj/item/weapon/reagent_containers/glass/bottle/fertilizer,/obj/item/weapon/minihoe)
+ allowed = list(/obj/item/weapon/reagent_containers/spray/plantbgone,/obj/item/device/plant_analyzer,/obj/item/seeds,/obj/item/weapon/reagent_containers/glass/bottle, /obj/item/weapon/reagent_containers/glass/beaker, /obj/item/weapon/cultivator,/obj/item/weapon/reagent_containers/spray/pestspray,/obj/item/weapon/hatchet,/obj/item/weapon/storage/bag/plants)
species_fit = list("Vox")
sprite_sheets = list(
"Vox" = 'icons/mob/species/vox/suit.dmi'
diff --git a/code/modules/clothing/suits/miscellaneous.dm b/code/modules/clothing/suits/miscellaneous.dm
index 37e8daad42b..ef20bbd0362 100644
--- a/code/modules/clothing/suits/miscellaneous.dm
+++ b/code/modules/clothing/suits/miscellaneous.dm
@@ -421,7 +421,7 @@
/obj/item/clothing/suit/hooded/wintercoat/hydro
name = "hydroponics winter coat"
icon_state = "wintercoat_hydro"
- allowed = list(/obj/item/weapon/reagent_containers/spray, /obj/item/device/analyzer/plant_analyzer, /obj/item/seeds, /obj/item/weapon/reagent_containers/glass/bottle, /obj/item/weapon/hatchet, /obj/item/weapon/storage/bag/plants)
+ allowed = list(/obj/item/weapon/reagent_containers/spray, /obj/item/device/plant_analyzer, /obj/item/seeds, /obj/item/weapon/reagent_containers/glass/bottle, /obj/item/weapon/hatchet, /obj/item/weapon/storage/bag/plants)
hoodtype = /obj/item/clothing/head/winterhood/hydro
/obj/item/clothing/head/winterhood/hydro
@@ -773,6 +773,13 @@
adjust_flavour = null
burn_state = FIRE_PROOF
+/obj/item/clothing/suit/jacket/leather/overcoat
+ name = "leather overcoat"
+ desc = "That's a damn fine coat."
+ icon_state = "leathercoat"
+ body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS
+ cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS
+
/obj/item/clothing/suit/officercoat
name = "Clown Officer's Coat"
desc = "A classy clown officer's overcoat, also designed by Hugo Boss."
diff --git a/code/modules/computer3/program_disks.dm b/code/modules/computer3/program_disks.dm
index 201fff39392..64b592b06be 100644
--- a/code/modules/computer3/program_disks.dm
+++ b/code/modules/computer3/program_disks.dm
@@ -1,44 +1,30 @@
-
-
/obj/item/weapon/disk/file/arcade
name = "Arcade game grab pack"
desc = "A program install disk."
- icon = 'icons/obj/stock_parts.dmi'
- icon_state = "datadisk_arcade"
spawn_files = list(/datum/file/program/arcade,/datum/file/program/arcade,/datum/file/program/arcade,/datum/file/program/arcade)
/*/obj/item/weapon/disk/file/aifixer
name = "AI System Integrity Restorer"
desc = "A program install disk."
- icon = 'icons/obj/stock_parts.dmi'
- icon_state = "datadisk_arcade"
spawn_files = list(/datum/file/program/aifixer)*/
/obj/item/weapon/disk/file/atmos_alert
name = "Atmospheric Alert Notifier"
desc = "A program install disk."
- icon = 'icons/obj/stock_parts.dmi'
- icon_state = "datadisk_arcade"
spawn_files = list(/datum/file/program/atmos_alert)
/obj/item/weapon/disk/file/cameras
name = "Camera Viewer"
desc = "A program install disk."
- icon = 'icons/obj/stock_parts.dmi'
- icon_state = "datadisk_arcade"
spawn_files = list(/datum/file/program/security)
/obj/item/weapon/disk/file/card
name = "ID Card Modifier"
desc = "A program install disk."
- icon = 'icons/obj/stock_parts.dmi'
- icon_state = "datadisk_arcade"
spawn_files = list(/datum/file/program/card_comp)
/*
/obj/item/weapon/disk/file/genetics
name = "Genetics & Cloning"
desc = "A program install disk."
- icon = 'icons/obj/stock_parts.dmi'
- icon_state = "datadisk_arcade"
spawn_files = list(/datum/file/program/cloning,/datum/file/program/dnascanner)
*/
diff --git a/code/modules/computer3/storage.dm b/code/modules/computer3/storage.dm
index 9ff8cab497b..acb569d3072 100644
--- a/code/modules/computer3/storage.dm
+++ b/code/modules/computer3/storage.dm
@@ -163,6 +163,7 @@
//parent_type = /obj/item/part/computer/storage // todon't: do this
name = "Data Disk"
desc = "A device that can be inserted and removed into computers easily as a form of portable data storage. This one stores 1 Megabyte"
+ icon_state = "datadisk_arcade"
var/list/files
var/list/spawn_files = list()
var/writeprotect = 0
diff --git a/code/modules/crafting/recipes.dm b/code/modules/crafting/recipes.dm
index 288dcfcd9c5..c75efd74929 100644
--- a/code/modules/crafting/recipes.dm
+++ b/code/modules/crafting/recipes.dm
@@ -6,7 +6,6 @@
var/time = 30 //time in deciseconds
var/parts[] = list() //type paths of items that will be placed in the result
var/chem_catalists[] = list() //like tools but for reagents
- var/fruit[] = list() //grown products required by the recipe
var/category = CAT_MISC // Recipe category
/datum/crafting_recipe/proc/AdjustChems(var/obj/resultobj as obj)
@@ -308,4 +307,11 @@
time = 20
reqs = list(/obj/item/stack/sheet/mineral/bananium = 5,
/obj/item/weapon/bikehorn)
- category = CAT_MISC
\ No newline at end of file
+ category = CAT_MISC
+
+/datum/crafting_recipe/bonfire
+ name = "Bonfire"
+ time = 60
+ reqs = list(/obj/item/weapon/grown/log = 5)
+ result = /obj/structure/bonfire
+ category = CAT_PRIMAL
\ No newline at end of file
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/events/spacevine.dm b/code/modules/events/spacevine.dm
index aab52b8974b..61ba1b5350d 100644
--- a/code/modules/events/spacevine.dm
+++ b/code/modules/events/spacevine.dm
@@ -1,11 +1,570 @@
-/var/global/spacevines_spawned = 0
-
-/datum/event/spacevine
- announceWhen = 10
+//Types of usual mutations
+#define POSITIVE 1
+#define NEGATIVE 2
+#define MINOR_NEGATIVE 3
/datum/event/spacevine/start()
- spacevine_infestation()
- spacevines_spawned = 1
+ var/list/turfs = list() //list of all the empty floor turfs in the hallway areas
-/datum/event/spacevine/announce()
- event_announcement.Announce("Confirmed outbreak of level 7 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg')
+ var/obj/effect/spacevine/SV = new()
+
+ for(var/area/hallway/A in world)
+ for(var/turf/F in A)
+ if(F.Enter(SV))
+ turfs += F
+
+ qdel(SV)
+
+ if(turfs.len) //Pick a turf to spawn at if we can
+ var/turf/T = pick(turfs)
+ new/obj/effect/spacevine_controller(T) //spawn a controller at turf
+
+
+/datum/spacevine_mutation
+ var/name = ""
+ var/severity = 1
+ var/hue
+ var/quality
+
+/datum/spacevine_mutation/proc/add_mutation_to_vinepiece(obj/effect/spacevine/holder)
+ holder.mutations |= src
+ holder.color = hue
+
+/datum/spacevine_mutation/proc/process_mutation(obj/effect/spacevine/holder)
+ return
+
+/datum/spacevine_mutation/proc/process_temperature(obj/effect/spacevine/holder, temp, volume)
+ return
+
+/datum/spacevine_mutation/proc/on_birth(obj/effect/spacevine/holder)
+ return
+
+/datum/spacevine_mutation/proc/on_grow(obj/effect/spacevine/holder)
+ return
+
+/datum/spacevine_mutation/proc/on_death(obj/effect/spacevine/holder)
+ return
+
+/datum/spacevine_mutation/proc/on_hit(obj/effect/spacevine/holder, mob/hitter, obj/item/I, expected_damage)
+ . = expected_damage
+
+/datum/spacevine_mutation/proc/on_cross(obj/effect/spacevine/holder, mob/crosser)
+ return
+
+/datum/spacevine_mutation/proc/on_chem(obj/effect/spacevine/holder, datum/reagent/R)
+ return
+
+/datum/spacevine_mutation/proc/on_eat(obj/effect/spacevine/holder, mob/living/eater)
+ return
+
+/datum/spacevine_mutation/proc/on_spread(obj/effect/spacevine/holder, turf/target)
+ return
+
+/datum/spacevine_mutation/proc/on_buckle(obj/effect/spacevine/holder, mob/living/buckled)
+ return
+
+/datum/spacevine_mutation/proc/on_explosion(severity, obj/effect/spacevine/holder)
+ return
+
+
+/datum/spacevine_mutation/space_covering
+ name = "space protective"
+ hue = "#aa77aa"
+ quality = POSITIVE
+
+/turf/simulated/floor/vines
+ color = "#aa77aa"
+ icon_state = "vinefloor"
+ broken_states = list()
+
+
+//All of this shit is useless for vines
+
+/turf/simulated/floor/vines/attackby()
+ return
+
+/turf/simulated/floor/vines/burn_tile()
+ return
+
+/turf/simulated/floor/vines/break_tile()
+ return
+
+/turf/simulated/floor/vines/make_plating()
+ return
+
+/turf/simulated/floor/vines/break_tile_to_plating()
+ return
+
+/turf/simulated/floor/vines/ex_act(severity)
+ if(severity < 3)
+ ChangeTurf(baseturf)
+
+/turf/simulated/floor/vines/narsie_act()
+ if(prob(20))
+ ChangeTurf(baseturf) //nar sie eats this shit
+
+/turf/simulated/floor/vines/singularity_pull(S, current_size)
+ if(current_size >= STAGE_FIVE)
+ if(prob(50))
+ ChangeTurf(baseturf)
+
+/turf/simulated/floor/vines/ChangeTurf(turf/open/floor/T)
+ . = ..()
+ //Do this *after* the turf has changed as qdel in spacevines will call changeturf again if it hasn't
+ for(var/obj/effect/spacevine/SV in src)
+ qdel(SV)
+
+/datum/spacevine_mutation/space_covering
+ var/static/list/coverable_turfs
+
+/datum/spacevine_mutation/space_covering/New()
+ . = ..()
+ if(!coverable_turfs)
+ coverable_turfs = typecacheof(list(
+ /turf/space
+ ))
+ coverable_turfs -= typecacheof(list(
+ /turf/space/transit
+ ))
+
+/datum/spacevine_mutation/space_covering/on_grow(obj/effect/spacevine/holder)
+ process_mutation(holder)
+
+/datum/spacevine_mutation/space_covering/process_mutation(obj/effect/spacevine/holder)
+ var/turf/T = get_turf(holder)
+ if(is_type_in_typecache(T, coverable_turfs))
+ var/currtype = T.type
+ T.ChangeTurf(/turf/simulated/floor/vines)
+ T.baseturf = currtype
+
+/datum/spacevine_mutation/space_covering/on_death(obj/effect/spacevine/holder)
+ var/turf/T = get_turf(holder)
+ if(istype(T, /turf/simulated/floor/vines))
+ T.ChangeTurf(T.baseturf)
+
+/datum/spacevine_mutation/bluespace
+ name = "bluespace"
+ hue = "#3333ff"
+ quality = MINOR_NEGATIVE
+
+/datum/spacevine_mutation/bluespace/on_spread(obj/effect/spacevine/holder, turf/target)
+ if(holder.energy > 1 && !locate(/obj/effect/spacevine) in target)
+ holder.master.spawn_spacevine_piece(target, holder)
+
+/datum/spacevine_mutation/light
+ name = "light"
+ hue = "#ffff00"
+ quality = POSITIVE
+ severity = 4
+
+/datum/spacevine_mutation/light/on_grow(obj/effect/spacevine/holder)
+ if(holder.energy)
+ holder.set_light(severity)
+
+/datum/spacevine_mutation/toxicity
+ name = "toxic"
+ hue = "#ff00ff"
+ severity = 10
+ quality = NEGATIVE
+
+/datum/spacevine_mutation/toxicity/on_cross(obj/effect/spacevine/holder, mob/living/crosser)
+ if(issilicon(crosser))
+ return
+ if(prob(severity) && istype(crosser) && !isvineimmune(crosser))
+ to_chat(crosser, "You accidently touch the vine and feel a strange sensation.")
+ crosser.adjustToxLoss(5)
+
+/datum/spacevine_mutation/toxicity/on_eat(obj/effect/spacevine/holder, mob/living/eater)
+ if(!isvineimmune(eater))
+ eater.adjustToxLoss(5)
+
+/datum/spacevine_mutation/explosive //OH SHIT IT CAN CHAINREACT RUN!!!
+ name = "explosive"
+ hue = "#ff0000"
+ quality = NEGATIVE
+ severity = 2
+
+/datum/spacevine_mutation/explosive/on_explosion(explosion_severity, obj/effect/spacevine/holder)
+ if(explosion_severity < 3)
+ qdel(holder)
+ else
+ . = 1
+ spawn(5)
+ qdel(holder)
+
+/datum/spacevine_mutation/explosive/on_death(obj/effect/spacevine/holder, mob/hitter, obj/item/I)
+ explosion(holder.loc, 0, 0, severity, 0, 0)
+
+/datum/spacevine_mutation/fire_proof
+ name = "fire proof"
+ hue = "#ff8888"
+ quality = MINOR_NEGATIVE
+
+/datum/spacevine_mutation/fire_proof/process_temperature(obj/effect/spacevine/holder, temp, volume)
+ return 1
+
+/datum/spacevine_mutation/fire_proof/on_hit(obj/effect/spacevine/holder, mob/hitter, obj/item/I, expected_damage)
+ if(I && I.damtype == "fire")
+ . = 0
+ else
+ . = expected_damage
+
+/datum/spacevine_mutation/vine_eating
+ name = "vine eating"
+ hue = "#ff7700"
+ quality = MINOR_NEGATIVE
+
+/datum/spacevine_mutation/vine_eating/on_spread(obj/effect/spacevine/holder, turf/target)
+ var/obj/effect/spacevine/prey = locate() in target
+ if(prey && !prey.mutations.Find(src)) //Eat all vines that are not of the same origin
+ qdel(prey)
+
+/datum/spacevine_mutation/aggressive_spread //very OP, but im out of other ideas currently
+ name = "aggressive spreading"
+ hue = "#333333"
+ severity = 3
+ quality = NEGATIVE
+
+/datum/spacevine_mutation/aggressive_spread/on_spread(obj/effect/spacevine/holder, turf/target)
+ target.ex_act(severity) // vine immunity handled at /mob/ex_act
+
+/datum/spacevine_mutation/aggressive_spread/on_buckle(obj/effect/spacevine/holder, mob/living/buckled)
+ buckled.ex_act(severity)
+
+/datum/spacevine_mutation/transparency
+ name = "transparent"
+ hue = ""
+ quality = POSITIVE
+
+/datum/spacevine_mutation/transparency/on_grow(obj/effect/spacevine/holder)
+ holder.set_opacity(0)
+ holder.alpha = 125
+
+/datum/spacevine_mutation/thorns
+ name = "thorny"
+ hue = "#666666"
+ severity = 10
+ quality = NEGATIVE
+
+/datum/spacevine_mutation/thorns/on_cross(obj/effect/spacevine/holder, mob/living/crosser)
+ if(prob(severity) && istype(crosser) && !isvineimmune(holder))
+ var/mob/living/M = crosser
+ M.adjustBruteLoss(5)
+ to_chat(M, "You cut yourself on the thorny vines.")
+
+/datum/spacevine_mutation/thorns/on_hit(obj/effect/spacevine/holder, mob/living/hitter, obj/item/I, expected_damage)
+ if(prob(severity) && istype(hitter) && !isvineimmune(holder))
+ var/mob/living/M = hitter
+ M.adjustBruteLoss(5)
+ to_chat(M, "You cut yourself on the thorny vines.")
+ . = expected_damage
+
+/datum/spacevine_mutation/woodening
+ name = "hardened"
+ hue = "#997700"
+ quality = NEGATIVE
+
+/datum/spacevine_mutation/woodening/on_grow(obj/effect/spacevine/holder)
+ if(holder.energy)
+ holder.density = 1
+ holder.maxhealth = 100
+ holder.health = holder.maxhealth
+
+/datum/spacevine_mutation/woodening/on_hit(obj/effect/spacevine/holder, mob/living/hitter, obj/item/I, expected_damage)
+ if(!is_sharp(I))
+ . = expected_damage * 0.5
+ else
+ . = expected_damage
+
+/datum/spacevine_mutation/flowering
+ name = "flowering"
+ hue = "#0A480D"
+ quality = NEGATIVE
+ severity = 10
+
+/datum/spacevine_mutation/flowering/on_grow(obj/effect/spacevine/holder)
+ if(holder.energy == 2 && prob(severity) && !locate(/obj/structure/alien/resin/flower_bud_enemy) in range(5,holder))
+ new /obj/structure/alien/resin/flower_bud_enemy(get_turf(holder))
+
+/datum/spacevine_mutation/flowering/on_cross(obj/effect/spacevine/holder, mob/living/crosser)
+ if(prob(25))
+ holder.entangle(crosser)
+
+
+// SPACE VINES (Note that this code is very similar to Biomass code)
+/obj/effect/spacevine
+ name = "space vines"
+ desc = "An extremely expansionistic species of vine."
+ icon = 'icons/effects/spacevines.dmi'
+ icon_state = "Light1"
+ anchored = 1
+ density = 0
+ layer = MOB_LAYER + 0.8
+ mouse_opacity = 2 //Clicking anywhere on the turf is good enough
+ pass_flags = PASSTABLE | PASSGRILLE
+ var/health = 50
+ var/maxhealth = 50
+ var/energy = 0
+ var/obj/effect/spacevine_controller/master = null
+ var/list/mutations = list()
+
+/obj/effect/spacevine/New()
+ ..()
+ color = "#ffffff"
+
+/obj/effect/spacevine/examine(mob/user)
+ ..()
+ var/text = "This one is a"
+ if(mutations.len)
+ for(var/A in mutations)
+ var/datum/spacevine_mutation/SM = A
+ text += " [SM.name]"
+ else
+ text += " normal"
+ text += " vine."
+ to_chat(user, text)
+
+/obj/effect/spacevine/Destroy()
+ for(var/datum/spacevine_mutation/SM in mutations)
+ SM.on_death(src)
+ if(master)
+ master.vines -= src
+ master.growth_queue -= src
+ if(!master.vines.len)
+ var/obj/item/seeds/kudzu/KZ = new(loc)
+ KZ.mutations |= mutations
+ KZ.set_potency(master.mutativeness * 10)
+ KZ.set_production((master.spread_cap / initial(master.spread_cap)) * 5)
+ master = null
+ mutations.Cut()
+ set_opacity(0)
+ if(buckled_mob)
+ unbuckle_mob()
+ return ..()
+
+/obj/effect/spacevine/proc/add_mutation(datum/spacevine_mutation/mutation)
+ mutations |= mutation
+ color = mutation.hue
+
+/obj/effect/spacevine/proc/on_chem_effect(datum/reagent/R)
+ var/override = 0
+ for(var/datum/spacevine_mutation/SM in mutations)
+ override += SM.on_chem(src, R)
+ if(!override && istype(R, /datum/reagent/glyphosate))
+ if(prob(50))
+ qdel(src)
+
+/obj/effect/spacevine/proc/eat(mob/eater)
+ var/override = 0
+ for(var/datum/spacevine_mutation/SM in mutations)
+ override += SM.on_eat(src, eater)
+ if(!override)
+ if(prob(10))
+ eater.say("Nom")
+ qdel(src)
+
+/obj/effect/spacevine/attackby(obj/item/weapon/W, mob/user, params)
+ if (!W || !user || !W.type)
+ return
+ user.changeNext_move(CLICK_CD_MELEE)
+ var/force = W.force
+
+ if(istype(W, /obj/item/weapon/scythe))
+ force = force * 4
+ for(var/obj/effect/spacevine/B in orange(1,src))
+ B.health = health - force
+ if(B.health < 1)
+ qdel(B)
+
+ health = health - force
+
+ if(health < 1)
+ qdel(src)
+
+ return
+
+ if(is_sharp(W))
+ force = force * 4
+
+ if(W && W.damtype == "fire")
+ force = force * 4
+
+ for(var/datum/spacevine_mutation/SM in mutations)
+ force = SM.on_hit(src, user, W, force) //on_hit now takes override damage as arg and returns new value for other mutations to permutate further
+
+ health = health - force
+ if(health < 1)
+ qdel(src)
+
+ ..()
+
+/obj/effect/spacevine/Crossed(mob/crosser)
+ if(isliving(crosser))
+ for(var/datum/spacevine_mutation/SM in mutations)
+ SM.on_cross(src, crosser)
+
+/obj/effect/spacevine/attack_hand(mob/user)
+ for(var/datum/spacevine_mutation/SM in mutations)
+ SM.on_hit(src, user)
+ user_unbuckle_mob(user, user)
+
+/obj/effect/spacevine/attack_alien(mob/living/user)
+ eat(user)
+
+/obj/effect/spacevine_controller
+ invisibility = 101
+ var/list/obj/effect/spacevine/vines = list()
+ var/list/growth_queue = list()
+ var/spread_multiplier = 5
+ var/spread_cap = 30
+ var/list/mutations_list = list()
+ var/mutativeness = 1
+
+/obj/effect/spacevine_controller/New(loc, list/muts, potency, production)
+ color = "#ffffff"
+ spawn_spacevine_piece(loc, , muts)
+ processing_objects.Add(src)
+ init_subtypes(/datum/spacevine_mutation/, mutations_list)
+ if(potency != null)
+ mutativeness = potency / 10
+ if(production != null)
+ spread_cap *= production / 5
+ spread_multiplier /= production / 5
+ ..()
+
+
+/obj/effect/spacevine_controller/ex_act() //only killing all vines will end this suffering
+ return
+
+/obj/effect/spacevine_controller/singularity_act()
+ return
+
+/obj/effect/spacevine_controller/singularity_pull()
+ return
+
+/obj/effect/spacevine_controller/Destroy()
+ processing_objects.Remove(src)
+ return ..()
+
+/obj/effect/spacevine_controller/proc/spawn_spacevine_piece(turf/location, obj/effect/spacevine/parent, list/muts)
+ var/obj/effect/spacevine/SV = new(location)
+ growth_queue += SV
+ vines += SV
+ SV.master = src
+ if(muts && muts.len)
+ for(var/datum/spacevine_mutation/M in muts)
+ M.add_mutation_to_vinepiece(SV)
+ return
+ if(parent)
+ SV.mutations |= parent.mutations
+ SV.color = parent.color
+ if(prob(mutativeness))
+ var/list/random_mutations_picked = mutations_list - SV.mutations
+ if(random_mutations_picked.len)
+ var/datum/spacevine_mutation/randmut = pick(random_mutations_picked)
+ randmut.add_mutation_to_vinepiece(SV)
+
+ for(var/datum/spacevine_mutation/SM in SV.mutations)
+ SM.on_birth(SV)
+
+/obj/effect/spacevine_controller/process()
+ if(!vines)
+ qdel(src) //space vines exterminated. Remove the controller
+ return
+ if(!growth_queue)
+ qdel(src) //Sanity check
+ return
+
+ var/length = 0
+
+ length = min( spread_cap , max( 1 , vines.len / spread_multiplier ) )
+ var/i = 0
+ var/list/obj/effect/spacevine/queue_end = list()
+
+ for(var/obj/effect/spacevine/SV in growth_queue)
+ if(qdeleted(SV))
+ continue
+ i++
+ queue_end += SV
+ growth_queue -= SV
+ for(var/datum/spacevine_mutation/SM in SV.mutations)
+ SM.process_mutation(SV)
+ if(SV.energy < 2) //If tile isn't fully grown
+ if(prob(20))
+ SV.grow()
+ else //If tile is fully grown
+ SV.entangle_mob()
+
+ //if(prob(25))
+ SV.spread()
+ if(i >= length)
+ break
+
+ growth_queue = growth_queue + queue_end
+
+/obj/effect/spacevine/proc/grow()
+ if(!energy)
+ icon_state = pick("Med1", "Med2", "Med3")
+ energy = 1
+ set_opacity(1)
+ else
+ icon_state = pick("Hvy1", "Hvy2", "Hvy3")
+ energy = 2
+
+ for(var/datum/spacevine_mutation/SM in mutations)
+ SM.on_grow(src)
+
+/obj/effect/spacevine/proc/entangle_mob()
+ if(!buckled_mob && prob(25))
+ for(var/mob/living/V in loc)
+ entangle(V)
+ if(has_buckled_mobs())
+ break //only capture one mob at a time
+
+
+/obj/effect/spacevine/proc/entangle(mob/living/V)
+ if(!V || isvineimmune(V))
+ return
+ for(var/datum/spacevine_mutation/SM in mutations)
+ SM.on_buckle(src, V)
+ if((V.stat != DEAD) && (V.buckled != src)) //not dead or captured
+ to_chat(V, "The vines [pick("wind", "tangle", "tighten")] around you!")
+ buckle_mob(V, 1)
+
+/obj/effect/spacevine/proc/spread()
+ var/direction = pick(cardinal)
+ var/turf/stepturf = get_step(src,direction)
+ for(var/datum/spacevine_mutation/SM in mutations)
+ SM.on_spread(src, stepturf)
+ stepturf = get_step(src,direction) //in case turf changes, to make sure no runtimes happen
+ if(!locate(/obj/effect/spacevine, stepturf))
+ if(stepturf.Enter(src))
+ if(master)
+ master.spawn_spacevine_piece(stepturf, src)
+
+/obj/effect/spacevine/ex_act(severity)
+ var/i
+ for(var/datum/spacevine_mutation/SM in mutations)
+ i += SM.on_explosion(severity, src)
+ if(!i && prob(100/severity))
+ qdel(src)
+
+/obj/effect/spacevine/temperature_expose(null, temp, volume)
+ var/override = 0
+ for(var/datum/spacevine_mutation/SM in mutations)
+ override += SM.process_temperature(src, temp, volume)
+ if(!override)
+ qdel(src)
+
+/obj/effect/spacevine/CanPass(atom/movable/mover, turf/target, height=0)
+ if(isvineimmune(mover))
+ . = TRUE
+ else
+ . = ..()
+
+/proc/isvineimmune(atom/A)
+ . = FALSE
+ if(isliving(A))
+ var/mob/living/M = A
+ if(("vines" in M.faction) || ("plants" in M.faction))
+ . = TRUE
\ No newline at end of file
diff --git a/code/modules/fish/fish_items.dm b/code/modules/fish/fish_items.dm
index 0266f06df90..7d5d3bf20ce 100644
--- a/code/modules/fish/fish_items.dm
+++ b/code/modules/fish/fish_items.dm
@@ -1,6 +1,6 @@
var/global/list/fish_items_list = list("goldfish" = /obj/item/weapon/fish/goldfish,
- "clownfish" = /obj/item/weapon/bananapeel/clownfish,
+ "clownfish" = /obj/item/weapon/grown/bananapeel/clownfish,
"shark" = /obj/item/weapon/fish/shark,
"baby space carp" = /obj/item/weapon/fish/babycarp,
"catfish" = /obj/item/weapon/fish/catfish,
@@ -208,7 +208,7 @@ var/global/list/fish_items_list = list("goldfish" = /obj/item/weapon/fish/goldfi
..()
-/obj/item/weapon/bananapeel/clownfish
+/obj/item/weapon/grown/bananapeel/clownfish
name = "clown fish"
desc = "Even underwater, you cannot escape HONKing."
icon = 'icons/obj/fish_items.dmi'
diff --git a/code/modules/food_and_drinks/drinks/bottler/bottler.dm b/code/modules/food_and_drinks/drinks/bottler/bottler.dm
index c6b88b78c6c..81974b39c0e 100644
--- a/code/modules/food_and_drinks/drinks/bottler/bottler.dm
+++ b/code/modules/food_and_drinks/drinks/bottler/bottler.dm
@@ -213,12 +213,7 @@
for(var/i = 1, i <= slots.len, i++)
var/obj/item/O = slots[i]
if(istype(O, recipe.ingredients[i]))
- if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown))
- var/obj/item/weapon/reagent_containers/food/snacks/grown/G = O
- if(G.seed && G.seed.kitchen_tag == recipe.tags[i])
- number_matches++
- else
- number_matches++
+ number_matches++
if(number_matches == 3)
return recipe
return null
diff --git a/code/modules/food_and_drinks/drinks/bottler/bottler_recipes.dm b/code/modules/food_and_drinks/drinks/bottler/bottler_recipes.dm
index be7504afade..85b3452ebfc 100644
--- a/code/modules/food_and_drinks/drinks/bottler/bottler_recipes.dm
+++ b/code/modules/food_and_drinks/drinks/bottler/bottler_recipes.dm
@@ -3,7 +3,6 @@
var/name = ""
var/description = ""
var/list/ingredients[3]
- var/list/tags[3]
var/datum/reagent/result = null
@@ -12,12 +11,10 @@
name = "Example"
description = "This is an example."
ingredients = list(thing_1, thing_2, thing_3)
- tags = list(null, "tag_2", null)
result = "water"
The ingredients list must have 3 non-null entries.
-The tags list must have 3 entries, using null where a tag is unused.
-Failing to ensure both lists have EXACTLY 3 entries (unless the system is updated in the future to use a different number) will result in runtimes.
+Failing to ensure the list has EXACTLY 3 entries (unless the system is updated in the future to use a different number) will result in runtimes.
There is no excuse to do this wrong now that there is an example for you. --FalseIncarnate
*/
@@ -25,62 +22,55 @@ There is no excuse to do this wrong now that there is an example for you. --Fals
/datum/bottler_recipe/Paradise_Punch
name = "Paradise Punch"
description = "Tastes just how you'd think Paradise would if you could bottle it."
- ingredients = list(/obj/item/weapon/reagent_containers/food/snacks/grown,
- /obj/item/weapon/reagent_containers/food/snacks/grown,
- /obj/item/weapon/reagent_containers/food/snacks/grown)
- tags = list("grapes", "banana", "cherries")
+ ingredients = list(/obj/item/weapon/reagent_containers/food/snacks/grown/grapes,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/cherries)
result = "paradise_punch"
/datum/bottler_recipe/Applepocalypse
name = "Apple-pocalypse"
description = "If doomsday came in fruit form, it'd probably be apples."
- ingredients = list(/obj/item/weapon/reagent_containers/food/snacks/grown,
- /obj/item/weapon/reagent_containers/food/snacks/grown,
- /obj/item/weapon/reagent_containers/food/snacks/grown)
- tags = list("apple", "apple", "apple")
+ ingredients = list(/obj/item/weapon/reagent_containers/food/snacks/grown/apple,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/apple,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/apple)
result = "apple-pocalypse"
/datum/bottler_recipe/Berry_Banned
name = "Berry Banned"
description = "Reason for ban: Excessive Flavor."
- ingredients = list(/obj/item/weapon/reagent_containers/food/snacks/grown,
- /obj/item/weapon/reagent_containers/food/snacks/grown,
- /obj/item/weapon/reagent_containers/food/snacks/grown)
- tags = list("berries", "berries", "berries")
+ ingredients = list(/obj/item/weapon/reagent_containers/food/snacks/grown/berries,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries)
result = "berry_banned"
/datum/bottler_recipe/Berry_Banned2
name = "Berry Banned"
description = "Reason for ban: Excessive Flavor."
- ingredients = list(/obj/item/weapon/reagent_containers/food/snacks/grown,
- /obj/item/weapon/reagent_containers/food/snacks/grown,
- /obj/item/weapon/reagent_containers/food/snacks/grown)
- tags = list("poisonberries", "poisonberries", "poisonberries")
+ ingredients = list(/obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison)
result = "berry_banned2"
/datum/bottler_recipe/Blackeye_Brew
name = "Blackeye Brew"
description = "Creamy, smooth flavor, just like the bald heads of the masses. Supposedly aged for 30 years."
ingredients = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola,
- /obj/item/weapon/reagent_containers/food/snacks/grown,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/sugarcane,
/obj/item/weapon/reagent_containers/food/snacks/icecream)
- tags = list(null, "sugarcane", null)
result = "blackeye_brew"
/datum/bottler_recipe/Grape_Granade
name = "Grape Granade"
description = "Exploding with grape flavor and a favorite among ERT members system-wide."
ingredients = list(/obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice,
- /obj/item/weapon/reagent_containers/food/snacks/grown,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/grapes,
/obj/item/device/flash)
- tags = list(null, "grapes", null)
result = "grape_granade"
/datum/bottler_recipe/Meteor_Malt
name = "Meteor Malt"
description = "Soft drinks have been detected on collision course with your tastebuds."
ingredients = list(/obj/item/weapon/ore,
- /obj/item/weapon/reagent_containers/food/snacks/grown,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/wheat,
/obj/item/weapon/ore)
- tags = list(null, "wheat", null)
result = "meteor_malt"
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/food/meat.dm b/code/modules/food_and_drinks/food/meat.dm
index 4635f6eb9b1..19d8307c906 100644
--- a/code/modules/food_and_drinks/food/meat.dm
+++ b/code/modules/food_and_drinks/food/meat.dm
@@ -44,4 +44,12 @@
/obj/item/weapon/reagent_containers/food/snacks/meat/ham
name = "Ham"
desc = "Taste like bacon."
- list_reagents = list("protein" = 3, "porktonium" = 10)
\ No newline at end of file
+ list_reagents = list("protein" = 3, "porktonium" = 10)
+
+/obj/item/weapon/reagent_containers/food/snacks/meat/meatwheat
+ name = "meatwheat clump"
+ desc = "This doesn't look like meat, but your standards aren't that high to begin with."
+ list_reagents = list("nutriment" = 3, "vitamin" = 2, "blood" = 5)
+ filling_color = rgb(150, 0, 0)
+ icon_state = "meatwheat_clump"
+ bitesize = 4
\ No newline at end of file
diff --git a/code/modules/food_and_drinks/food/snacks.dm b/code/modules/food_and_drinks/food/snacks.dm
index dd5884606d0..aafc4fd3648 100644
--- a/code/modules/food_and_drinks/food/snacks.dm
+++ b/code/modules/food_and_drinks/food/snacks.dm
@@ -11,7 +11,9 @@
var/eatverb
var/wrapped = 0
var/dried_type = null
+ var/dry = 0
var/cooktype[0]
+ var/cooked_type = null //for microwave cooking. path of the resulting item after microwaving
//Placeholder for effect that trigger on eating that aren't tied to reagents.
@@ -25,12 +27,8 @@
user.visible_message("[user] finishes eating \the [src].")
user.unEquip(src) //so icons update :[
Post_Consume(M)
- if(trash)
- if(ispath(trash,/obj/item))
- var/obj/item/TrashItem = new trash(user)
- user.put_in_hands(TrashItem)
- else if(istype(trash,/obj/item))
- user.put_in_hands(trash)
+ var/obj/item/trash_item = generate_trash(usr)
+ usr.put_in_hands(trash_item)
qdel(src)
return
@@ -173,6 +171,19 @@
return
+/obj/item/weapon/reagent_containers/food/snacks/proc/generate_trash(atom/location)
+ if(trash)
+ if(ispath(trash, /obj/item))
+ . = new trash(location)
+ trash = null
+ return
+ else if(istype(trash, /obj/item))
+ var/obj/item/trash_item = trash
+ trash_item.forceMove(location)
+ . = trash
+ trash = null
+ return
+
/obj/item/weapon/reagent_containers/food/snacks/Destroy()
if(contents)
for(var/atom/movable/something in contents)
@@ -887,6 +898,12 @@
junkiness = 25
list_reagents = list("protein" = 1, "sugar" = 3)
+/obj/item/weapon/reagent_containers/food/snacks/sosjerky/healthy
+ name = "homemade beef jerky"
+ desc = "Homemade beef jerky made from the finest space cows."
+ list_reagents = list("nutriment" = 3, "vitamin" = 1)
+ junkiness = 0
+
/obj/item/weapon/reagent_containers/food/snacks/pistachios
name = "Pistachios"
icon_state = "pistachios"
@@ -905,6 +922,12 @@
junkiness = 25
list_reagents = list("plantmatter" = 2, "sugar" = 4)
+/obj/item/weapon/reagent_containers/food/snacks/no_raisin/healthy
+ name = "homemade raisins"
+ desc = "homemade raisins, the best in all of spess."
+ list_reagents = list("nutriment" = 3, "vitamin" = 2)
+ junkiness = 0
+
/obj/item/weapon/reagent_containers/food/snacks/spacetwinkie
name = "Space Twinkie"
icon_state = "space_twinkie"
@@ -2472,4 +2495,27 @@
trash = /obj/item/trash/tastybread
filling_color = "#A66829"
junkiness = 20
- list_reagents = list("nutriment" = 2, "sugar" = 4)
\ No newline at end of file
+ list_reagents = list("nutriment" = 2, "sugar" = 4)
+
+/obj/item/weapon/reagent_containers/food/snacks/yakiimo
+ name = "yaki imo"
+ desc = "Made with roasted sweet potatoes!"
+ icon_state = "yakiimo"
+ trash = /obj/item/trash/plate
+ list_reagents = list("nutriment" = 5, "vitamin" = 4)
+ filling_color = "#8B1105"
+
+/obj/item/weapon/reagent_containers/food/snacks/roastparsnip
+ name = "roast parsnip"
+ desc = "Sweet and crunchy."
+ icon_state = "roastparsnip"
+ trash = /obj/item/trash/plate
+ list_reagents = list("nutriment" = 3, "vitamin" = 4)
+ filling_color = "#FF5500"
+
+/obj/item/weapon/reagent_containers/food/snacks/tatortot
+ name = "tator tot"
+ desc = "A large fried potato nugget that may or may not try to valid you."
+ icon_state = "tatortot"
+ list_reagents = list("nutriment" = 4)
+ filling_color = "FFD700"
\ No newline at end of file
diff --git a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
index 73607d4e3ab..d9b98c82724 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/deep_fryer.dm
@@ -48,11 +48,7 @@
return 0
for(var/Type in subtypesof(/datum/deepfryer_special))
var/datum/deepfryer_special/P = new Type()
- if(istype(I, /obj/item/weapon/reagent_containers/food/snacks/grown))
- var/obj/item/weapon/reagent_containers/food/snacks/grown/G = I
- if(G.seed.kitchen_tag != P.input)
- continue
- else if(!istype(I, P.input))
+ if(!istype(I, P.input))
continue
return P
return 0
diff --git a/code/modules/food_and_drinks/kitchen_machinery/juicer.dm b/code/modules/food_and_drinks/kitchen_machinery/juicer.dm
index 03ceb5c0034..edf714d0f21 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/juicer.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/juicer.dm
@@ -4,27 +4,29 @@
icon = 'icons/obj/kitchen.dmi'
icon_state = "juicer1"
layer = 2.9
- density = 0
+ density = 1
anchored = 0
use_power = 1
idle_power_usage = 5
active_power_usage = 100
+ pass_flags = PASSTABLE
var/obj/item/weapon/reagent_containers/beaker = null
- var/global/list/allowed_items = list(
+ var/global/list/allowed_items = list (
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato = "tomatojuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/carrot = "carrotjuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/grapes = "grapejuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/grapes/green = "grapejuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana = "banana",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/potato = "potato",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon = "lemonjuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange = "orangejuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime = "limejuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/watermelon = "watermelonjuice",
/obj/item/weapon/reagent_containers/food/snacks/watermelonslice = "watermelonjuice",
- /obj/item/weapon/reagent_containers/food/snacks/grown = "water",
- )
-
- var/global/list/allowed_tags = list (
- "tomato" = "tomatojuice",
- "carrot" = "carrotjuice",
- "berries" = "berryjuice",
- "banana" = "banana",
- "potato" = "potato",
- "lemon" = "lemonjuice",
- "orange" = "orangejuice",
- "lime" = "limejuice",
- "poisonberries" = "poisonberryjuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison = "poisonberryjuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries = "berryjuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin = "pumpkinjuice",
+ /obj/item/weapon/reagent_containers/food/snacks/grown/blumpkin = "blumpkinjuice",
)
/obj/machinery/juicer/New()
@@ -135,23 +137,18 @@
beaker = null
update_icon()
-/obj/machinery/juicer/proc/get_juice_id(var/obj/item/weapon/reagent_containers/food/snacks/O)
- if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/watermelonslice))
- return "watermelonjuice"
- else if(istype(O, /obj.item/weapon/reagent_containers/food/snacks/grown))
- var/obj/item/weapon/reagent_containers/food/snacks/grown/G = O
- for(var/i in allowed_tags)
- if(G.seed.kitchen_tag == allowed_tags[i])
- return allowed_tags[i]
- return "water"
+/obj/machinery/juicer/proc/get_juice_id(obj/item/weapon/reagent_containers/food/snacks/grown/O)
+ for (var/i in allowed_items)
+ if (istype(O, i))
+ return allowed_items[i]
-/obj/machinery/juicer/proc/get_juice_amount(var/obj/item/weapon/reagent_containers/food/snacks/grown/O)
- if(!istype(O))
+/obj/machinery/juicer/proc/get_juice_amount(obj/item/weapon/reagent_containers/food/snacks/grown/O)
+ if(!istype(O) || !O.seed)
return 5
- else if(O.potency == -1)
+ else if (O.seed.potency == -1)
return 5
else
- return round(5*sqrt(O.potency))
+ return round(5*sqrt(O.seed.potency))
/obj/machinery/juicer/proc/juice()
power_change() //it is a portable machine
@@ -167,19 +164,21 @@
if(beaker.reagents.total_volume >= beaker.reagents.maximum_volume)
break
-/obj/structure/closet/crate/juice
- New()
- ..()
- new/obj/machinery/juicer(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/tomato(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/carrot(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/berries(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/banana(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/tomato(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/carrot(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/berries(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/banana(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/tomato(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/carrot(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/berries(src)
- new/obj/item/weapon/reagent_containers/food/snacks/grown/banana(src)
\ No newline at end of file
+/obj/structure/closet/crate/juice/New()
+ ..()
+ new/obj/machinery/juicer(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/tomato(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/carrot(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/berries(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/banana(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/grapes(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/tomato(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/carrot(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/berries(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/banana(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/grapes(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/tomato(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/carrot(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/berries(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/banana(src)
+ new/obj/item/weapon/reagent_containers/food/snacks/grown/grapes(src)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
index 54f3bc739ab..baf5e8e98ee 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/kitchen_machine.dm
@@ -46,7 +46,7 @@
acceptable_items |= item
for(var/reagent in recipe.reagents)
acceptable_reagents |= reagent
- if(recipe.items || recipe.fruit)
+ if(recipe.items)
max_n_of_items = max(max_n_of_items,recipe.count_n_items())
else
qdel(recipe)
diff --git a/code/modules/food_and_drinks/kitchen_machinery/processor.dm b/code/modules/food_and_drinks/kitchen_machinery/processor.dm
index 5183fe6a4fa..df9d1dddf4d 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/processor.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/processor.dm
@@ -71,21 +71,34 @@
input = /obj/item/weapon/reagent_containers/food/snacks/meat
output = /obj/item/weapon/reagent_containers/food/snacks/meatball
-/datum/food_processor_process/potato
- input = "potato"
+/datum/food_processor_process/potatowedges
+ input = /obj/item/weapon/reagent_containers/food/snacks/grown/potato/wedges
output = /obj/item/weapon/reagent_containers/food/snacks/fries
+/datum/food_processor_process/sweetpotato
+ input = /obj/item/weapon/reagent_containers/food/snacks/grown/potato/sweet
+ output = /obj/item/weapon/reagent_containers/food/snacks/yakiimo
+
+/datum/food_processor_process/potato
+ input = /obj/item/weapon/reagent_containers/food/snacks/grown/potato
+ output = /obj/item/weapon/reagent_containers/food/snacks/tatortot
+
/datum/food_processor_process/carrot
- input = "carrot"
+ input = /obj/item/weapon/reagent_containers/food/snacks/grown/carrot
output = /obj/item/weapon/reagent_containers/food/snacks/carrotfries
/datum/food_processor_process/soybeans
- input = "soybeans"
+ input = /obj/item/weapon/reagent_containers/food/snacks/grown/soybeans
output = /obj/item/weapon/reagent_containers/food/snacks/soydope
/datum/food_processor_process/spaghetti
input = /obj/item/weapon/reagent_containers/food/snacks/doughslice
output = /obj/item/weapon/reagent_containers/food/snacks/spagetti
+
+/datum/food_processor_process/parsnip
+ input = /obj/item/weapon/reagent_containers/food/snacks/grown/parsnip
+ output = /obj/item/weapon/reagent_containers/food/snacks/roastparsnip
+
/////////////////////////
///END OBJECT RECIPIES///
/////////////////////////
@@ -142,14 +155,10 @@
//END RECIPE DATUMS
-/obj/machinery/processor/proc/select_recipe(var/X)
+/obj/machinery/processor/proc/select_recipe(X)
for(var/Type in subtypesof(/datum/food_processor_process) - /datum/food_processor_process/mob)
var/datum/food_processor_process/P = new Type()
- if(istype(X, /obj/item/weapon/reagent_containers/food/snacks/grown))
- var/obj/item/weapon/reagent_containers/food/snacks/grown/G = X
- if(G.seed.kitchen_tag != P.input)
- continue
- else if(!istype(X, P.input))
+ if(!istype(X, P.input))
continue
return P
return 0
diff --git a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
index f31bdbd95fe..3c694f993cf 100644
--- a/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
+++ b/code/modules/food_and_drinks/kitchen_machinery/smartfridge.dm
@@ -55,7 +55,7 @@
return ..()
/obj/machinery/smartfridge/proc/accept_check(obj/item/O)
- if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/))
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/grown/) || istype(O,/obj/item/seeds/) || istype(O,/obj/item/weapon/grown/))
return 1
return 0
@@ -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].")
@@ -402,6 +403,127 @@
visible_message("[src] launches [throw_item.name] at [target.name]!")
return 1
+// ----------------------------
+// Drying Rack 'smartfridge'
+// ----------------------------
+/obj/machinery/smartfridge/drying_rack
+ name = "drying rack"
+ desc = "A wooden contraption, used to dry plant products, food and leather."
+ icon = 'icons/obj/hydroponics/equipment.dmi'
+ icon_state = "drying_rack_on"
+ use_power = 1
+ idle_power_usage = 5
+ active_power_usage = 200
+ icon_on = "drying_rack_on"
+ icon_off = "drying_rack"
+ var/drying = FALSE
+
+/obj/machinery/smartfridge/drying_rack/New()
+ ..()
+ if(component_parts && component_parts.len)
+ component_parts.Cut()
+ component_parts = null
+
+/obj/machinery/smartfridge/drying_rack/Destroy()
+ new /obj/item/stack/sheet/wood(loc, 10)
+ return ..()
+
+/obj/machinery/smartfridge/drying_rack/RefreshParts()
+ return
+
+/obj/machinery/smartfridge/drying_rack/default_deconstruction_screwdriver()
+ return
+
+/obj/machinery/smartfridge/drying_rack/exchange_parts()
+ return
+
+/obj/machinery/smartfridge/drying_rack/spawn_frame()
+ return
+
+/obj/machinery/smartfridge/drying_rack/default_deconstruction_crowbar(obj/item/weapon/crowbar/C, ignore_panel = 1)
+ ..()
+
+/obj/machinery/smartfridge/drying_rack/power_change()
+ if(powered() && anchored)
+ stat &= ~NOPOWER
+ else
+ stat |= NOPOWER
+ toggle_drying(TRUE)
+ update_icon()
+
+/obj/machinery/smartfridge/drying_rack/load() //For updating the filled overlay
+ ..()
+ update_icon()
+
+/obj/machinery/smartfridge/drying_rack/update_icon()
+ ..()
+ overlays.Cut()
+ if(drying)
+ overlays += "drying_rack_drying"
+ if(contents.len)
+ overlays += "drying_rack_filled"
+
+/obj/machinery/smartfridge/drying_rack/process()
+ ..()
+ if(drying)
+ if(rack_dry())//no need to update unless something got dried
+ update_icon()
+
+/obj/machinery/smartfridge/drying_rack/accept_check(obj/item/O)
+ if(istype(O,/obj/item/weapon/reagent_containers/food/snacks/))
+ var/obj/item/weapon/reagent_containers/food/snacks/S = O
+ if(S.dried_type)
+ return TRUE
+ if(istype(O,/obj/item/stack/sheet/wetleather/))
+ return TRUE
+ return FALSE
+
+/obj/machinery/smartfridge/drying_rack/proc/toggle_drying(forceoff)
+ if(drying || forceoff)
+ drying = FALSE
+ use_power = 1
+ else
+ drying = TRUE
+ use_power = 2
+ update_icon()
+
+/obj/machinery/smartfridge/drying_rack/proc/rack_dry()
+ for(var/obj/item/weapon/reagent_containers/food/snacks/S in contents)
+ if(S.dried_type == S.type)//if the dried type is the same as the object's type, don't bother creating a whole new item...
+ S.color = "#ad7257"
+ S.dry = TRUE
+ S.forceMove(get_turf(src))
+ else
+ var/dried = S.dried_type
+ new dried(loc)
+ qdel(S)
+ return TRUE
+ for(var/obj/item/stack/sheet/wetleather/WL in contents)
+ var/obj/item/stack/sheet/leather/L = new(loc)
+ L.amount = WL.amount
+ qdel(WL)
+ return TRUE
+ return FALSE
+
+/obj/machinery/smartfridge/drying_rack/emp_act(severity)
+ ..()
+ atmos_spawn_air(SPAWN_HEAT)
+
+/obj/machinery/smartfridge/drying_rack/verb/adjust_drying()
+ set category = "Object"
+ set name = "Toggle Drying"
+ set src in view(1)
+
+ if(!iscarbon(usr))
+ to_chat(usr, "You can't do that!")
+ return
+
+ if(usr.incapacitated())
+ return
+
+ toggle_drying(FALSE)
+ update_icon()
+
/************************
* Secure SmartFridges
*************************/
@@ -414,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/food_and_drinks/recipes/recipes_candy.dm b/code/modules/food_and_drinks/recipes/recipes_candy.dm
index 414eee38354..4dcecb657b8 100644
--- a/code/modules/food_and_drinks/recipes/recipes_candy.dm
+++ b/code/modules/food_and_drinks/recipes/recipes_candy.dm
@@ -27,14 +27,14 @@
/datum/recipe/candy/fudge_peanut
reagents = list("sugar" = 5, "milk" = 5)
- items = list(/obj/item/weapon/reagent_containers/food/snacks/chocolatebar)
- fruit = list("peanuts" = 3)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/chocolatebar, /obj/item/weapon/reagent_containers/food/snacks/grown/peanuts,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/peanuts, /obj/item/weapon/reagent_containers/food/snacks/grown/peanuts)
result = /obj/item/weapon/reagent_containers/food/snacks/candy/fudge/peanut
/datum/recipe/candy/fudge_cherry
reagents = list("sugar" = 5, "milk" = 5)
- items = list(/obj/item/weapon/reagent_containers/food/snacks/chocolatebar)
- fruit = list("cherries" = 3)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/chocolatebar, /obj/item/weapon/reagent_containers/food/snacks/grown/cherries,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/cherries, /obj/item/weapon/reagent_containers/food/snacks/grown/cherries)
result = /obj/item/weapon/reagent_containers/food/snacks/candy/fudge/cherry
/datum/recipe/candy/fudge_cookies_n_cream
@@ -50,8 +50,8 @@
items = list(
/obj/item/weapon/reagent_containers/food/snacks/chocolatebar,
/obj/item/weapon/reagent_containers/food/snacks/candy/caramel,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/peanuts
)
- fruit = list("peanuts" = 1)
result = /obj/item/weapon/reagent_containers/food/snacks/candy/fudge/turtle
/datum/recipe/candy/fudge
@@ -319,10 +319,7 @@
/datum/recipe/candy/gummybear_wtf2
reagents = list()
- fruit = list("ambrosia" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/candy/gummybear
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/candy/gummybear, /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris)
result = /obj/item/weapon/reagent_containers/food/snacks/candy/gummybear/wtf
// ***********************************************************
@@ -386,10 +383,7 @@
result = /obj/item/weapon/reagent_containers/food/snacks/candy/gummyworm/wtf
/datum/recipe/candy/gummyworm_wtf2
- fruit = list("ambrosia" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/candy/gummyworm
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/candy/gummyworm, /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris)
result = /obj/item/weapon/reagent_containers/food/snacks/candy/gummyworm/wtf
// ***********************************************************
@@ -489,10 +483,7 @@
result = /obj/item/weapon/reagent_containers/food/snacks/candy/jellybean/wtf
/datum/recipe/candy/jellybean_wtf2
- fruit = list("ambrosia" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/candy/jellybean
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/candy/jellybean, /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris)
result = /obj/item/weapon/reagent_containers/food/snacks/candy/jellybean/wtf
// ***********************************************************
diff --git a/code/modules/food_and_drinks/recipes/recipes_microwave.dm b/code/modules/food_and_drinks/recipes/recipes_microwave.dm
index 2840c099ea6..962aa5e92a4 100644
--- a/code/modules/food_and_drinks/recipes/recipes_microwave.dm
+++ b/code/modules/food_and_drinks/recipes/recipes_microwave.dm
@@ -10,11 +10,8 @@
result = /obj/item/weapon/reagent_containers/food/snacks/boiledegg
/datum/recipe/microwave/dionaroast
- fruit = list("apple" = 1)
reagents = list("facid" = 5) //It dissolves the carapace. Still poisonous, though.
- items = list(
- /obj/item/weapon/holder/diona,
- )
+ items = list(/obj/item/weapon/holder/diona, /obj/item/weapon/reagent_containers/food/snacks/grown/apple)
result = /obj/item/weapon/reagent_containers/food/snacks/dionaroast
/datum/recipe/microwave/jellydonut
@@ -150,16 +147,16 @@
result = /obj/item/weapon/reagent_containers/food/snacks/warmdonkpocket
/datum/recipe/microwave/eggplantparm
- fruit = list("eggplant" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/eggplant
)
result = /obj/item/weapon/reagent_containers/food/snacks/eggplantparm
/datum/recipe/microwave/soylenviridians
- fruit = list("soybeans" = 1)
reagents = list("flour" = 10)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/soybeans)
result = /obj/item/weapon/reagent_containers/food/snacks/soylenviridians
/datum/recipe/microwave/soylentgreen
@@ -185,25 +182,27 @@
result = /obj/item/weapon/reagent_containers/food/snacks/cheesyfries
/datum/recipe/microwave/cubancarp
- fruit = list("chili" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/carpmeat,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/chili
)
result = /obj/item/weapon/reagent_containers/food/snacks/cubancarp
/datum/recipe/microwave/popcorn
- fruit = list("corn" = 1)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/corn)
result = /obj/item/weapon/reagent_containers/food/snacks/popcorn
/datum/recipe/microwave/spacylibertyduff
reagents = list("water" = 5, "vodka" = 5)
- fruit = list("libertycap" = 3)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/libertycap, /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/libertycap,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/libertycap)
result = /obj/item/weapon/reagent_containers/food/snacks/spacylibertyduff
/datum/recipe/microwave/amanitajelly
reagents = list("water" = 5, "vodka" = 5)
- fruit = list("amanita" = 3)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/amanita, /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/amanita,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/amanita)
result = /obj/item/weapon/reagent_containers/food/snacks/amanitajelly
/datum/recipe/microwave/amanitajelly/make_food(obj/container)
@@ -213,23 +212,18 @@
/datum/recipe/microwave/meatballsoup
reagents = list("water" = 10)
- fruit = list("potato" = 1, "carrot" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/meatball ,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/meatball, /obj/item/weapon/reagent_containers/food/snacks/grown/potato, /obj/item/weapon/reagent_containers/food/snacks/grown/carrot)
result = /obj/item/weapon/reagent_containers/food/snacks/meatballsoup
/datum/recipe/microwave/vegetablesoup
reagents = list("water" = 10)
- fruit = list("carrot" = 1, "corn" = 1, "eggplant" = 1, "potato" = 1)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/potato, /obj/item/weapon/reagent_containers/food/snacks/grown/carrot,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/corn, /obj/item/weapon/reagent_containers/food/snacks/grown/eggplant)
result = /obj/item/weapon/reagent_containers/food/snacks/vegetablesoup
/datum/recipe/microwave/nettlesoup
reagents = list("water" = 10)
- fruit = list("nettle" = 1, "potato" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/egg,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/egg, /obj/item/weapon/grown/nettle/basic, /obj/item/weapon/reagent_containers/food/snacks/grown/potato)
result = /obj/item/weapon/reagent_containers/food/snacks/nettlesoup
/datum/recipe/microwave/wishsoup
@@ -237,17 +231,11 @@
result= /obj/item/weapon/reagent_containers/food/snacks/wishsoup
/datum/recipe/microwave/hotchili
- fruit = list("chili" = 1, "tomato" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/meat,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/meat, /obj/item/weapon/reagent_containers/food/snacks/grown/chili, /obj/item/weapon/reagent_containers/food/snacks/grown/tomato)
result = /obj/item/weapon/reagent_containers/food/snacks/hotchili
/datum/recipe/microwave/coldchili
- fruit = list("icechili" = 1, "tomato" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/meat,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/meat, /obj/item/weapon/reagent_containers/food/snacks/grown/icepepper, /obj/item/weapon/reagent_containers/food/snacks/grown/tomato)
result = /obj/item/weapon/reagent_containers/food/snacks/coldchili
/datum/recipe/microwave/spellburger
@@ -275,10 +263,7 @@
result = /obj/item/weapon/reagent_containers/food/snacks/bigbiteburger
/datum/recipe/microwave/enchiladas
- fruit = list("chili" = 2, "corn" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/cutlet,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/cutlet, /obj/item/weapon/reagent_containers/food/snacks/grown/chili, /obj/item/weapon/reagent_containers/food/snacks/grown/corn)
result = /obj/item/weapon/reagent_containers/food/snacks/enchiladas
/datum/recipe/microwave/burrito
@@ -292,11 +277,8 @@
result = /obj/item/weapon/reagent_containers/food/snacks/burrito
/datum/recipe/microwave/monkeysdelight
- fruit = list("banana" = 1)
reagents = list("sodiumchloride" = 1, "blackpepper" = 1, "flour" = 10)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/monkeycube
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/monkeycube, /obj/item/weapon/reagent_containers/food/snacks/grown/banana)
result = /obj/item/weapon/reagent_containers/food/snacks/monkeysdelight
/datum/recipe/microwave/fishandchips
@@ -323,15 +305,14 @@
/datum/recipe/microwave/tomatosoup
reagents = list("water" = 10)
- fruit = list("tomato" = 2)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/tomato, /obj/item/weapon/reagent_containers/food/snacks/grown/tomato)
result = /obj/item/weapon/reagent_containers/food/snacks/tomatosoup
/datum/recipe/microwave/stew
reagents = list("water" = 10)
- fruit = list("tomato" = 1, "potato" = 1, "carrot" = 1, "eggplant" = 1, "mushroom" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/meat,
- )
+ items= list(/obj/item/weapon/reagent_containers/food/snacks/grown/tomato, /obj/item/weapon/reagent_containers/food/snacks/grown/potato,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/carrot, /obj/item/weapon/reagent_containers/food/snacks/grown/eggplant,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom, /obj/item/weapon/reagent_containers/food/snacks/meat)
result = /obj/item/weapon/reagent_containers/food/snacks/stew
/datum/recipe/microwave/slimetoast
@@ -359,11 +340,8 @@
result = /obj/item/weapon/reagent_containers/food/snacks/milosoup
/datum/recipe/microwave/stewedsoymeat
- fruit = list("carrot" = 1, "tomato" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/soydope,
- /obj/item/weapon/reagent_containers/food/snacks/soydope,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/soydope, /obj/item/weapon/reagent_containers/food/snacks/soydope,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/carrot, /obj/item/weapon/reagent_containers/food/snacks/grown/tomato)
result = /obj/item/weapon/reagent_containers/food/snacks/stewedsoymeat
/datum/recipe/microwave/boiledspagetti
@@ -383,17 +361,12 @@
/datum/recipe/microwave/pastatomato
reagents = list("water" = 5)
- fruit = list("tomato" = 2)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/spagetti,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/spagetti, /obj/item/weapon/reagent_containers/food/snacks/grown/tomato,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato)
result = /obj/item/weapon/reagent_containers/food/snacks/pastatomato
/datum/recipe/microwave/poppypretzel
- items = list(
- /obj/item/seeds/poppyseed,
- /obj/item/weapon/reagent_containers/food/snacks/dough,
- )
+ items = list(/obj/item/seeds/poppy, /obj/item/weapon/reagent_containers/food/snacks/dough)
result = /obj/item/weapon/reagent_containers/food/snacks/poppypretzel
/datum/recipe/microwave/meatballspagetti
@@ -418,19 +391,19 @@
/datum/recipe/microwave/superbiteburger
reagents = list("sodiumchloride" = 5, "blackpepper" = 5)
- fruit = list("tomato" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/bigbiteburger,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/meat,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
/obj/item/weapon/reagent_containers/food/snacks/boiledegg,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato
)
result = /obj/item/weapon/reagent_containers/food/snacks/superbiteburger
/datum/recipe/microwave/candiedapple
reagents = list("water" = 5, "sugar" = 5)
- fruit = list("apple" = 1)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/apple)
result = /obj/item/weapon/reagent_containers/food/snacks/candiedapple
/datum/recipe/microwave/slimeburger
@@ -473,19 +446,18 @@ datum/recipe/microwave/slimesandwich
/datum/recipe/microwave/bloodsoup
reagents = list("blood" = 10)
- fruit = list("bloodtomato" = 2)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/tomato/blood, /obj/item/weapon/reagent_containers/food/snacks/grown/tomato/blood)
result = /obj/item/weapon/reagent_containers/food/snacks/bloodsoup
/datum/recipe/microwave/slimesoup
reagents = list("water" = 10, "slimejelly" = 5)
- items = list()
result = /obj/item/weapon/reagent_containers/food/snacks/slimesoup
/datum/recipe/microwave/clownstears
reagents = list("water" = 10)
- fruit = list("banana" = 1)
items = list(
/obj/item/weapon/ore/bananium,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana
)
result = /obj/item/weapon/reagent_containers/food/snacks/clownstears
@@ -519,25 +491,26 @@ datum/recipe/microwave/slimesandwich
/datum/recipe/microwave/mushroomsoup
reagents = list("water" = 5, "milk" = 5)
- fruit = list("mushroom" = 1)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom)
result = /obj/item/weapon/reagent_containers/food/snacks/mushroomsoup
/datum/recipe/microwave/chawanmushi
reagents = list("water" = 5, "soysauce" = 5)
- fruit = list("mushroom" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/egg,
/obj/item/weapon/reagent_containers/food/snacks/egg,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom
)
result = /obj/item/weapon/reagent_containers/food/snacks/chawanmushi
/datum/recipe/microwave/beetsoup
reagents = list("water" = 10)
- fruit = list("whitebeet" = 1, "cabbage" = 1)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/whitebeet, /obj/item/weapon/reagent_containers/food/snacks/grown/cabbage)
result = /obj/item/weapon/reagent_containers/food/snacks/beetsoup
/datum/recipe/microwave/herbsalad
- fruit = list("ambrosia" = 3, "apple" = 1)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris, /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris, /obj/item/weapon/reagent_containers/food/snacks/grown/apple)
result = /obj/item/weapon/reagent_containers/food/snacks/herbsalad
/datum/recipe/microwave/herbsalad/make_food(obj/container)
@@ -546,14 +519,14 @@ datum/recipe/microwave/slimesandwich
return being_cooked
/datum/recipe/microwave/aesirsalad
- fruit = list("ambrosiadeus" = 3, "goldapple" = 1)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/deus, /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/deus,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/deus, /obj/item/weapon/reagent_containers/food/snacks/grown/apple/gold)
result = /obj/item/weapon/reagent_containers/food/snacks/aesirsalad
/datum/recipe/microwave/validsalad
- fruit = list("ambrosia" = 3, "potato" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/meatball,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris, /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris, /obj/item/weapon/reagent_containers/food/snacks/grown/potato,
+ /obj/item/weapon/reagent_containers/food/snacks/meatball)
result = /obj/item/weapon/reagent_containers/food/snacks/validsalad
/datum/recipe/microwave/validsalad/make_food(obj/container)
@@ -565,15 +538,12 @@ datum/recipe/microwave/slimesandwich
/datum/recipe/microwave/wrap
reagents = list("soysauce" = 10)
- fruit = list("cabbage" = 1)
- items = list(
- /obj/item/weapon/reagent_containers/food/snacks/friedegg,
- )
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/friedegg, /obj/item/weapon/reagent_containers/food/snacks/grown/cabbage )
result = /obj/item/weapon/reagent_containers/food/snacks/wrap
/datum/recipe/microwave/beans
reagents = list("ketchup" = 5)
- fruit = list("soybeans" = 2)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/soybeans, /obj/item/weapon/reagent_containers/food/snacks/grown/soybeans)
result = /obj/item/weapon/reagent_containers/food/snacks/beans
/datum/recipe/microwave/benedict
@@ -586,10 +556,10 @@ datum/recipe/microwave/slimesandwich
/datum/recipe/microwave/meatbun
reagents = list("soysauce" = 5)
- fruit = list("cabbage" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/meatball,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/cabbage
)
result = /obj/item/weapon/reagent_containers/food/snacks/meatbun
@@ -610,9 +580,9 @@ datum/recipe/microwave/slimesandwich
/datum/recipe/microwave/friedbanana
reagents = list("sugar" = 10, "cornoil" = 5)
- fruit = list("banana" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana
)
result = /obj/item/weapon/reagent_containers/food/snacks/friedbanana
@@ -655,8 +625,8 @@ datum/recipe/microwave/slimesandwich
result = /obj/item/weapon/reagent_containers/food/snacks/sashimi
/datum/recipe/microwave/mashedtaters
- fruit = list("potato" = 1)
reagents = list("gravy" = 5)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/potato)
result = /obj/item/weapon/reagent_containers/food/snacks/mashed_potatoes
//////////////////////////////////////////
diff --git a/code/modules/food_and_drinks/recipes/recipes_oven.dm b/code/modules/food_and_drinks/recipes/recipes_oven.dm
index d20c06e260f..473641c4cc8 100644
--- a/code/modules/food_and_drinks/recipes/recipes_oven.dm
+++ b/code/modules/food_and_drinks/recipes/recipes_oven.dm
@@ -51,11 +51,11 @@
/datum/recipe/oven/bananabread
reagents = list("milk" = 5, "sugar" = 15)
- fruit = list("banana" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/bananabread
@@ -68,11 +68,13 @@
/datum/recipe/oven/carrotcake
reagents = list("milk" = 5, "sugar" = 15)
- fruit = list("carrot" = 3)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/carrot,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/carrot,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/carrot
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/carrotcake
@@ -119,24 +121,24 @@
/datum/recipe/oven/pie
reagents = list("sugar" = 5)
- fruit = list("banana" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/banana
)
result = /obj/item/weapon/reagent_containers/food/snacks/pie
/datum/recipe/oven/cherrypie
reagents = list("sugar" = 10)
- fruit = list("cherries" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/cherries
)
result = /obj/item/weapon/reagent_containers/food/snacks/cherrypie
/datum/recipe/oven/berryclafoutis
- fruit = list("berries" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/berries
)
result = /obj/item/weapon/reagent_containers/food/snacks/berryclafoutis
@@ -155,9 +157,9 @@
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/tofubread
/datum/recipe/oven/loadedbakedpotato
- fruit = list("potato" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/potato
)
result = /obj/item/weapon/reagent_containers/food/snacks/loadedbakedpotato
@@ -194,71 +196,80 @@
return .
/datum/recipe/oven/pizzamargherita
- fruit = list("tomato" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/margherita
/datum/recipe/oven/meatpizza
- fruit = list("tomato" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
/obj/item/weapon/reagent_containers/food/snacks/meat,
/obj/item/weapon/reagent_containers/food/snacks/meat,
/obj/item/weapon/reagent_containers/food/snacks/meat,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza
/datum/recipe/oven/syntipizza
- fruit = list("tomato" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
/obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh,
/obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh,
/obj/item/weapon/reagent_containers/food/snacks/meat/syntiflesh,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/meatpizza
/datum/recipe/oven/mushroompizza
- fruit = list("mushroom" = 5, "tomato" = 1)
+
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/mushroompizza
/datum/recipe/oven/vegetablepizza
- fruit = list("eggplant" = 1, "carrot" = 1, "corn" = 1, "tomato" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
/obj/item/weapon/reagent_containers/food/snacks/cheesewedge,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/eggplant,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/carrot,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/corn,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/tomato
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pizza/vegetablepizza
/datum/recipe/oven/amanita_pie
- fruit = list("amanita" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/amanita
)
result = /obj/item/weapon/reagent_containers/food/snacks/amanita_pie
/datum/recipe/oven/plump_pie
- fruit = list("plumphelmet" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/plumphelmet
)
result = /obj/item/weapon/reagent_containers/food/snacks/plump_pie
/datum/recipe/oven/plumphelmetbiscuit
- fruit = list("plumphelmet" = 1)
reagents = list("water" = 5, "flour" = 5)
+ items = list(/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/plumphelmet)
result = /obj/item/weapon/reagent_containers/food/snacks/plumphelmetbiscuit
/datum/recipe/oven/creamcheesebread
@@ -301,49 +312,53 @@
return being_cooked
/datum/recipe/oven/applepie
- fruit = list("apple" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/apple
)
result = /obj/item/weapon/reagent_containers/food/snacks/applepie
/datum/recipe/oven/applecake
reagents = list("milk" = 5, "sugar" = 5)
- fruit = list("apple" = 2)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/apple,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/apple
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/applecake
/datum/recipe/oven/orangecake
reagents = list("milk" = 5)
- fruit = list("orange" = 2)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/orangecake
/datum/recipe/oven/limecake
reagents = list("milk" = 5)
- fruit = list("lime" = 2)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/limecake
/datum/recipe/oven/lemoncake
reagents = list("milk" = 5)
- fruit = list("lemon" = 2)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
/obj/item/weapon/reagent_containers/food/snacks/dough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/lemoncake
@@ -370,17 +385,17 @@
/datum/recipe/oven/pumpkinpie
reagents = list("milk" = 5, "sugar" = 5)
- fruit = list("pumpkin" = 1)
items = list(
/obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin
)
result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/pumpkinpie
/datum/recipe/oven/appletart
- fruit = list("goldapple" = 1)
reagents = list("sugar" = 5, "milk" = 5, "flour" = 10)
items = list(
- /obj/item/weapon/reagent_containers/food/snacks/egg
+ /obj/item/weapon/reagent_containers/food/snacks/egg,
+ /obj/item/weapon/reagent_containers/food/snacks/grown/apple/gold
)
result = /obj/item/weapon/reagent_containers/food/snacks/appletart
diff --git a/code/modules/food_and_drinks/recipes/tablecraft/recipes_table.dm b/code/modules/food_and_drinks/recipes/tablecraft/recipes_table.dm
index b6c943dbdab..f7eb2fa4a68 100644
--- a/code/modules/food_and_drinks/recipes/tablecraft/recipes_table.dm
+++ b/code/modules/food_and_drinks/recipes/tablecraft/recipes_table.dm
@@ -6,7 +6,6 @@
tools[] = list() //type paths of items needed but not consumed
time = 30 //time in deciseconds
parts[] = list() //type paths of items that will be placed in the result
- fruit[] = list() //grown products required by the recipe
*/
/datum/crafting_recipe/sandwich
@@ -55,45 +54,6 @@
result = /obj/item/weapon/reagent_containers/food/snacks/jellyburger/cherry
category = CAT_FOOD
-/* Temporarily disabled until we can find a workaround for this fruit shit.
-/datum/crafting_recipe/herbsalad
- name = "herb salad"
- fruit = list("ambrosia" = 3, "apple" = 1)
- result = /obj/item/weapon/reagent_containers/food/snacks/herbsalad
-
-/datum/crafting_recipe/herbsalad/AdjustChems(var/obj/resultobj as obj)
- if(istype(resultobj, /obj/item/weapon/reagent_containers))
- var/obj/item/weapon/reagent_containers/RC = resultobj
- RC.reagents.del_reagent("toxin")
-
-/datum/crafting_recipe/aesirsalad
- name = "Aesir salad"
- fruit = list("ambrosiadeus" = 3, "goldapple" = 1)
- result = /obj/item/weapon/reagent_containers/food/snacks/aesirsalad
-
-/datum/crafting_recipe/validsalad
- name = "valid salad"
- fruit = list("ambrosia" = 3, "potato" = 1)
- reqs = list(
- /obj/item/weapon/reagent_containers/food/snacks/meatball = 1,
- )
- result = /obj/item/weapon/reagent_containers/food/snacks/validsalad
-
-/datum/crafting_recipe/validsalad/AdjustChems(var/obj/resultobj as obj)
- if(istype(resultobj, /obj/item/weapon/reagent_containers))
- var/obj/item/weapon/reagent_containers/RC = resultobj
- RC.reagents.del_reagent("toxin")
-
-/datum/crafting_recipe/wrap
- name = "egg wrap"
- fruit = list("cabbage" = 1)
- reqs = list(
- /datum/reagent/soysauce = 10,
- /obj/item/weapon/reagent_containers/food/snacks/friedegg = 1,
- )
- result = /obj/item/weapon/reagent_containers/food/snacks/wrap
-*/
-
/datum/crafting_recipe/notasandwich
name = "not-a-sandwich"
reqs = list(
diff --git a/code/modules/hydroponics/beekeeping/beebox.dm b/code/modules/hydroponics/beekeeping/beebox.dm
index 817f002f014..18f87a8a45a 100644
--- a/code/modules/hydroponics/beekeeping/beebox.dm
+++ b/code/modules/hydroponics/beekeeping/beebox.dm
@@ -33,7 +33,7 @@
/obj/structure/beebox
name = "apiary"
desc = "Dr Miles Manners is just your average Wasp themed super hero by day, but by night he becomes DR BEES!"
- icon = 'icons/obj/apiary_bees.dmi'
+ icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "beebox"
anchored = 1
density = 1
@@ -251,4 +251,4 @@
if(!user.put_in_active_hand(QB))
QB.forceMove(get_turf(src))
visible_message("[user] removes the queen from the apiary.")
- queen_bee = null
\ No newline at end of file
+ queen_bee = null
diff --git a/code/modules/hydroponics/beekeeping/beekeeper_suit.dm b/code/modules/hydroponics/beekeeping/beekeeper_suit.dm
index 93b5b0c53a8..640d022c9e3 100644
--- a/code/modules/hydroponics/beekeeping/beekeeper_suit.dm
+++ b/code/modules/hydroponics/beekeeping/beekeeper_suit.dm
@@ -13,4 +13,5 @@
icon_state = "beekeeper"
item_state = "beekeeper"
flags = THICKMATERIAL
- allowed = list (/obj/item/weapon/reagent_containers/spray/plantbgone,/obj/item/device/analyzer/plant_analyzer,/obj/item/seeds,/obj/item/weapon/reagent_containers/glass/bottle/fertilizer,/obj/item/weapon/minihoe)
+ allowed = list (/obj/item/weapon/reagent_containers/spray,/obj/item/device/plant_analyzer,/obj/item/seeds,/obj/item/weapon/reagent_containers/glass/bottle,/obj/item/weapon/cultivator)
+
diff --git a/code/modules/hydroponics/beekeeping/honey_frame.dm b/code/modules/hydroponics/beekeeping/honey_frame.dm
index a58ab7041c7..74eec401d6d 100644
--- a/code/modules/hydroponics/beekeeping/honey_frame.dm
+++ b/code/modules/hydroponics/beekeeping/honey_frame.dm
@@ -1,8 +1,8 @@
/obj/item/honey_frame
name = "honey frame"
- desc = "a scaffold for bees to build honeycomb on"
- icon = 'icons/obj/apiary_bees.dmi'
+ desc = "A scaffold for bees to build honeycomb on."
+ icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "honey_frame"
var/honeycomb_capacity = 10 //10 Honeycomb per frame by default, researchable frames perhaps?
diff --git a/code/modules/hydroponics/beekeeping/honeycomb.dm b/code/modules/hydroponics/beekeeping/honeycomb.dm
index 23588cc5c48..713e46804e7 100644
--- a/code/modules/hydroponics/beekeeping/honeycomb.dm
+++ b/code/modules/hydroponics/beekeeping/honeycomb.dm
@@ -1,8 +1,8 @@
/obj/item/weapon/reagent_containers/honeycomb
name = "honeycomb"
- desc = "a hexagonal mesh of honeycomb"
- icon = 'icons/obj/apiary_bees.dmi'
+ desc = "A hexagonal mesh of honeycomb."
+ icon = 'icons/obj/hydroponics/harvest.dmi'
icon_state = "honeycomb"
possible_transfer_amounts = list()
disease_amount = 0
@@ -22,10 +22,10 @@
overlays.Cut()
var/image/honey
if(honey_color)
- honey = image(icon = 'icons/obj/apiary_bees.dmi', icon_state = "greyscale_honey")
+ honey = image(icon = 'icons/obj/hydroponics/harvest.dmi', icon_state = "greyscale_honey")
honey.color = honey_color
else
- honey = image(icon = 'icons/obj/apiary_bees.dmi', icon_state = "honey")
+ honey = image(icon = 'icons/obj/hydroponics/harvest.dmi', icon_state = "honey")
overlays += honey
@@ -37,4 +37,4 @@
reagents.add_reagent(R.id,5)
else
honey_color = ""
- update_icon()
+ update_icon()
\ No newline at end of file
diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm
new file mode 100644
index 00000000000..6635329c4cb
--- /dev/null
+++ b/code/modules/hydroponics/biogenerator.dm
@@ -0,0 +1,325 @@
+/obj/machinery/biogenerator
+ name = "Biogenerator"
+ desc = "Converts plants into biomass, which can be used to construct useful items."
+ icon = 'icons/obj/biogenerator.dmi'
+ icon_state = "biogen-empty"
+ density = 1
+ anchored = 1
+ use_power = 1
+ idle_power_usage = 40
+ var/processing = 0
+ var/obj/item/weapon/reagent_containers/glass/beaker = null
+ var/points = 0
+ var/menustat = "menu"
+ var/efficiency = 0
+ var/productivity = 0
+ var/max_items = 40
+ var/datum/research/files
+ var/list/show_categories = list("Food", "Botany Chemicals", "Leather and Cloth")
+ var/list/timesFiveCategories = list("Food", "Botany Chemicals")
+
+/obj/machinery/biogenerator/New()
+ ..()
+ files = new /datum/research/biogenerator(src)
+ create_reagents(1000)
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/biogenerator(null)
+ component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
+ component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
+ component_parts += new /obj/item/stack/cable_coil(null, 1)
+ RefreshParts()
+
+/obj/machinery/biogenerator/Destroy()
+ if(beaker)
+ qdel(beaker)
+ beaker = null
+ return ..()
+
+/obj/machinery/biogenerator/ex_act(severity)
+ if(beaker)
+ beaker.ex_act(severity)
+ ..()
+
+/obj/machinery/biogenerator/RefreshParts()
+ var/E = 0
+ var/P = 0
+ var/max_storage = 40
+ for(var/obj/item/weapon/stock_parts/matter_bin/B in component_parts)
+ P += B.rating
+ max_storage = 40 * B.rating
+ for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
+ E += M.rating
+ efficiency = E
+ productivity = P
+ max_items = max_storage
+
+/obj/machinery/biogenerator/on_reagent_change() //When the reagents change, change the icon as well.
+ update_icon()
+
+/obj/machinery/biogenerator/update_icon()
+ if(panel_open)
+ icon_state = "biogen-empty-o"
+ else if(!beaker)
+ icon_state = "biogen-empty"
+ else if(!processing)
+ icon_state = "biogen-stand"
+ else
+ icon_state = "biogen-work"
+ return
+
+/obj/machinery/biogenerator/attackby(obj/item/O, mob/user, params)
+ if(user.a_intent == I_HARM)
+ return ..()
+
+ if(processing)
+ to_chat(user, "The biogenerator is currently processing.")
+ return
+
+ if(default_deconstruction_screwdriver(user, "biogen-empty-o", "biogen-empty", O))
+ if(beaker)
+ var/obj/item/weapon/reagent_containers/glass/B = beaker
+ B.forceMove(loc)
+ beaker = null
+ update_icon()
+ return
+
+ if(exchange_parts(user, O))
+ return
+
+ if(default_deconstruction_crowbar(O))
+ return
+
+ if(istype(O, /obj/item/weapon/reagent_containers/glass))
+ . = 1 //no afterattack
+ if(!panel_open)
+ if(beaker)
+ to_chat(user, "A container is already loaded into the machine.")
+ else
+ if(!user.drop_item())
+ return
+ O.forceMove(src)
+ beaker = O
+ to_chat(user, "You add the container to the machine.")
+ update_icon()
+ updateUsrDialog()
+ else
+ to_chat(user, "Close the maintenance panel first.")
+ return
+
+ else if(istype(O, /obj/item/weapon/storage/bag/plants))
+ var/obj/item/weapon/storage/bag/plants/PB = O
+ var/i = 0
+ for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in contents)
+ i++
+ if(i >= max_items)
+ to_chat(user, "The biogenerator is already full! Activate it.")
+ else
+ for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in PB.contents)
+ if(i >= max_items)
+ break
+ PB.remove_from_storage(G, src)
+ i++
+ if(i Biogenerator is processing! Please wait... " + else + switch(menustat) + if("nopoints") + dat += " You do not have enough biomass to create products. "
+ menustat = "menu"
+ if("complete")
+ dat += "Please, put growns into reactor and activate it. Operation complete. "
+ menustat = "menu"
+ if("void")
+ dat += "Error: No growns inside. "
+ menustat = "menu"
+ if("nobeakerspace")
+ dat += "Please, put growns into reactor. Not enough space left in container. Unable to create product. "
+ menustat = "menu"
+ if(beaker)
+ var/categories = show_categories.Copy()
+ for(var/V in categories)
+ categories[V] = list()
+ for(var/V in files.known_designs)
+ var/datum/design/D = files.known_designs[V]
+ for(var/C in categories)
+ if(C in D.category)
+ categories[C] += D
+
+ dat += "Biomass: [points] units. " + dat += "ActivateDetach Container" + for(var/cat in categories) + dat += " [cat]:" + dat += ""
+ for(var/V in categories[cat])
+ var/datum/design/D = V
+ dat += "[D.name]: Make"
+ if(cat in timesFiveCategories)
+ dat += "x5"
+ if(ispath(D.build_path, /obj/item/stack))
+ dat += "x10"
+ dat += "([D.materials[MAT_BIOMASS]/efficiency]) "
+ else
+ dat += "" + dat += " No container inside, please insert container. "
+
+ var/datum/browser/popup = new(user, "biogen", name, 350, 520)
+ popup.set_content(dat)
+ popup.open()
+ return
+
+/obj/machinery/biogenerator/attack_hand(mob/user)
+ interact(user)
+
+/obj/machinery/biogenerator/proc/activate()
+ if(usr.stat != 0)
+ return
+ if(stat != 0) //NOPOWER etc
+ return
+ if(processing)
+ to_chat(usr, "The biogenerator is in the process of working.")
+ return
+ var/S = 0
+ for(var/obj/item/weapon/reagent_containers/food/snacks/grown/I in contents)
+ S += 5
+ if(I.reagents.get_reagent_amount("nutriment") < 0.1)
+ points += 1*productivity
+ else
+ points += I.reagents.get_reagent_amount("nutriment")*10*productivity
+ qdel(I)
+ if(S)
+ processing = 1
+ update_icon()
+ updateUsrDialog()
+ playsound(loc, 'sound/machines/blender.ogg', 50, 1)
+ use_power(S*30)
+ sleep(S+15/productivity)
+ processing = 0
+ update_icon()
+ else
+ menustat = "void"
+ return
+
+/obj/machinery/biogenerator/proc/check_cost(list/materials, multiplier = 1, remove_points = 1)
+ if(materials.len != 1 || materials[1] != MAT_BIOMASS)
+ return 0
+ if (materials[MAT_BIOMASS]*multiplier/efficiency > points)
+ menustat = "nopoints"
+ return 0
+ else
+ if(remove_points)
+ points -= materials[MAT_BIOMASS]*multiplier/efficiency
+ update_icon()
+ updateUsrDialog()
+ return 1
+
+/obj/machinery/biogenerator/proc/check_container_volume(list/reagents, multiplier = 1)
+ var/sum_reagents = 0
+ for(var/R in reagents)
+ sum_reagents += reagents[R]
+ sum_reagents *= multiplier
+
+ if(beaker.reagents.total_volume + sum_reagents > beaker.reagents.maximum_volume)
+ menustat = "nobeakerspace"
+ return 0
+
+ return 1
+
+/obj/machinery/biogenerator/proc/create_product(datum/design/D, amount)
+ if(!beaker || !loc)
+ return 0
+
+ if(ispath(D.build_path, /obj/item/stack))
+ if(!check_container_volume(D.make_reagents, amount))
+ return 0
+ if(!check_cost(D.materials, amount))
+ return 0
+
+ var/obj/item/stack/product = new D.build_path(loc)
+ product.amount = amount
+ for(var/R in D.make_reagents)
+ beaker.reagents.add_reagent(R, D.make_reagents[R]*amount)
+ else
+ var/i = amount
+ while(i > 0)
+ if(!check_container_volume(D.make_reagents))
+ return .
+ if(!check_cost(D.materials))
+ return .
+ if(D.build_path)
+ new D.build_path(loc)
+ for(var/R in D.make_reagents)
+ beaker.reagents.add_reagent(R, D.make_reagents[R])
+ . = 1
+ --i
+
+ menustat = "complete"
+ update_icon()
+ return .
+
+/obj/machinery/biogenerator/proc/detach()
+ if(beaker)
+ beaker.forceMove(loc)
+ beaker = null
+ update_icon()
+
+/obj/machinery/biogenerator/Topic(href, href_list)
+ if(..() || panel_open)
+ return
+
+ usr.set_machine(src)
+
+ if(href_list["activate"])
+ activate()
+ updateUsrDialog()
+
+ else if(href_list["detach"])
+ detach()
+ updateUsrDialog()
+
+ else if(href_list["create"])
+ var/amount = (text2num(href_list["amount"]))
+ var/datum/design/D = locate(href_list["create"])
+ create_product(D, amount)
+ updateUsrDialog()
+
+ else if(href_list["menu"])
+ menustat = "menu"
+ updateUsrDialog()
diff --git a/code/modules/hydroponics/gene_modder.dm b/code/modules/hydroponics/gene_modder.dm
new file mode 100644
index 00000000000..dcfd2601813
--- /dev/null
+++ b/code/modules/hydroponics/gene_modder.dm
@@ -0,0 +1,410 @@
+/obj/machinery/plantgenes
+ name = "plant DNA manipulator"
+ desc = "An advanced device designed to manipulate plant genetic makeup."
+ icon = 'icons/obj/hydroponics/equipment.dmi'
+ icon_state = "dnamod"
+ density = 1
+ anchored = 1
+
+ var/obj/item/seeds/seed
+ var/obj/item/weapon/disk/plantgene/disk
+
+ var/list/core_genes = list()
+ var/list/reagent_genes = list()
+ var/list/trait_genes = list()
+
+ var/datum/plant_gene/target
+ var/operation = ""
+ var/rating = 0
+ var/max_extract_pot = 50
+ // The cap on potency gene extraction.
+ // This number is for T1, each upgraded part adds 5% for a tech level above T1.
+ // At T4, it reaches 95%.
+
+/obj/machinery/plantgenes/New()
+ ..()
+ component_parts = list()
+ component_parts += new /obj/item/weapon/circuitboard/plantgenes(src)
+ component_parts += new /obj/item/weapon/stock_parts/console_screen(src)
+ component_parts += new /obj/item/weapon/stock_parts/scanning_module(src)
+ component_parts += new /obj/item/weapon/stock_parts/micro_laser(src)
+ component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
+ RefreshParts()
+
+/obj/machinery/plantgenes/RefreshParts()
+ rating = 0
+ for(var/I in component_parts)
+ if(istype(I, /obj/item/weapon/stock_parts))
+ var/obj/item/weapon/stock_parts/S = I
+ rating += S.rating-1
+ max_extract_pot = initial(max_extract_pot) + rating*5
+
+/obj/machinery/plantgenes/update_icon()
+ ..()
+ overlays.Cut()
+ if((stat & (BROKEN|NOPOWER)))
+ icon_state = "dnamod-off"
+ else
+ icon_state = "dnamod"
+ if(seed)
+ overlays += "dnamod-dna"
+ if(panel_open)
+ overlays += "dnamod-open"
+
+/obj/machinery/plantgenes/attackby(obj/item/I, mob/user, params)
+ if(default_deconstruction_screwdriver(user, "dnamod", "dnamod", I))
+ update_icon()
+ return
+ if(exchange_parts(user, I))
+ return
+ if(default_deconstruction_crowbar(I))
+ return
+ if(isrobot(user))
+ return
+
+ if(istype(I, /obj/item/seeds))
+ if(seed)
+ to_chat(user, "A sample is already loaded into the machine!")
+ else
+ if(!user.drop_item())
+ return
+ insert_seed(I)
+ to_chat(user, "You add [I] to the machine.")
+ interact(user)
+ return
+ else if(istype(I, /obj/item/weapon/disk/plantgene))
+ if(disk)
+ to_chat(user, "A data disk is already loaded into the machine!")
+ else
+ if(!user.drop_item())
+ return
+ disk = I
+ disk.forceMove(src)
+ to_chat(user, "You add [I] to the machine.")
+ interact(user)
+ else
+ ..()
+
+
+/obj/machinery/plantgenes/attack_hand(mob/user)
+ if(..())
+ return
+ interact(user)
+
+/obj/machinery/plantgenes/interact(mob/user)
+ user.set_machine(src)
+ if(!user)
+ return
+
+ var/datum/browser/popup = new(user, "plantdna", "Plant DNA Manipulator", 450, 600)
+ if(!(in_range(src, user) || issilicon(user)))
+ popup.close()
+ return
+
+ var/dat = ""
+
+ if(operation)
+ if(!seed || (!target && operation != "insert"))
+ operation = ""
+ target = null
+ interact(user)
+ return
+ if((operation == "replace" || operation == "insert") && (!disk || !disk.gene))
+ operation = ""
+ target = null
+ interact(user)
+ return
+
+ dat += "Confirm OperationAre you sure you want to [operation] "
+ switch(operation)
+ if("remove")
+ dat += "[target.get_name()] gene from \the [seed]? "
+ popup.set_content(dat)
+ popup.open()
+ return
+
+ dat+= "" + if("extract") + dat += "[target.get_name()] gene from \the [seed]? " + dat += "The sample will be destroyed in process!" + if(istype(target, /datum/plant_gene/core/potency)) + var/datum/plant_gene/core/gene = target + if(gene.value > max_extract_pot) + dat += " This device's extraction capabilities are currently limited to [max_extract_pot] potency. " + dat += "Target gene will be degraded to [max_extract_pot] potency on extraction." + if("replace") + dat += "[target.get_name()] gene with [disk.gene.get_name()]? " + if("insert") + dat += "[disk.gene.get_name()] gene into \the [seed]? " + dat += " "
+
+ dat += " "
+
+ if(seed)
+ var/can_insert = disk && disk.gene && disk.gene.can_add(seed)
+ var/can_extract = disk && !disk.read_only
+
+ dat += "Plant Sample: Core Genes
Content Genes"
+ if(reagent_genes.len)
+ dat += " "
+ if(can_insert && istype(disk.gene, /datum/plant_gene/reagent))
+ dat += "Insert: [disk.gene.get_name()]"
+
+ dat += "
" + dat += " Trait Genes"
+ if(trait_genes.len)
+ dat += " "
+ else
+ dat += "
" + if(can_insert && istype(disk.gene, /datum/plant_gene/trait)) + dat += "Insert: [disk.gene.get_name()]" + dat += " No sample found. Please, insert a plant sample to use this device." + popup.set_content(dat) + popup.open() + + +/obj/machinery/plantgenes/Topic(var/href, var/list/href_list) + if(..()) + return + usr.set_machine(src) + + if(href_list["eject_seed"] && !operation) + if(seed) + seed.forceMove(loc) + seed.verb_pickup() + seed = null + update_genes() + update_icon() + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/seeds)) + if(!usr.drop_item()) + return + insert_seed(I) + to_chat(usr, "You add [I] to the machine.") + update_icon() + else if(href_list["eject_disk"] && !operation) + if(disk) + disk.forceMove(loc) + disk.verb_pickup() + disk = null + update_genes() + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/disk/plantgene)) + if(!usr.drop_item()) + return + disk = I + disk.forceMove(src) + to_chat(usr, "You add [I] to the machine.") + else if(href_list["op"] == "insert" && disk && disk.gene && seed) + if(!operation) // Wait for confirmation + operation = "insert" + else + if(!istype(disk.gene, /datum/plant_gene/core) && disk.gene.can_add(seed)) + seed.genes += disk.gene.Copy() + if(istype(disk.gene, /datum/plant_gene/reagent)) + seed.reagents_from_genes() + update_genes() + repaint_seed() + operation = "" + target = null + + else if(href_list["gene"] && seed) + var/datum/plant_gene/G = seed.get_gene(href_list["gene"]) + if(!G || !href_list["op"] || !(href_list["op"] in list("remove", "extract", "replace"))) + interact(usr) + return + + if(!operation || target != G) // Wait for confirmation + target = G + operation = href_list["op"] + + else if(operation == href_list["op"] && target == G) + switch(href_list["op"]) + if("remove") + if(!istype(G, /datum/plant_gene/core)) + seed.genes -= G + if(istype(G, /datum/plant_gene/reagent)) + seed.reagents_from_genes() + repaint_seed() + if("extract") + if(disk && !disk.read_only) + disk.gene = G + if(istype(G, /datum/plant_gene/core/potency)) + var/datum/plant_gene/core/gene = G + gene.value = min(gene.value, max_extract_pot) + disk.update_name() + qdel(seed) + seed = null + update_icon() + if("replace") + if(disk && disk.gene && istype(disk.gene, G.type) && istype(G, /datum/plant_gene/core)) + seed.genes -= G + var/datum/plant_gene/core/C = disk.gene.Copy() + seed.genes += C + C.apply_stat(seed) + repaint_seed() + if("insert") + if(disk && disk.gene && !istype(disk.gene, /datum/plant_gene/core) && disk.gene.can_add(seed)) + seed.genes += disk.gene.Copy() + if(istype(disk.gene, /datum/plant_gene/reagent)) + seed.reagents_from_genes() + repaint_seed() + + update_genes() + operation = "" + target = null + else if(href_list["abort"]) + operation = "" + target = null + + interact(usr) + +/obj/machinery/plantgenes/proc/insert_seed(obj/item/seeds/S) + if(!istype(S) || seed) + return + S.forceMove(src) + seed = S + update_genes() + update_icon() + +/obj/machinery/plantgenes/proc/update_genes() + core_genes = list() + reagent_genes = list() + trait_genes = list() + + if(seed) + var/gene_paths = list( + /datum/plant_gene/core/potency, + /datum/plant_gene/core/yield, + /datum/plant_gene/core/production, + /datum/plant_gene/core/endurance, + /datum/plant_gene/core/lifespan, + /datum/plant_gene/core/weed_rate, + /datum/plant_gene/core/weed_chance + ) + for(var/a in gene_paths) + core_genes += seed.get_gene(a) + + for(var/datum/plant_gene/reagent/G in seed.genes) + reagent_genes += G + for(var/datum/plant_gene/trait/G in seed.genes) + trait_genes += G + +/obj/machinery/plantgenes/proc/repaint_seed() + if(!seed) + return + if(copytext(seed.name, 1, 13) == "experimental") + return // Already modded name and icon + seed.name = "experimental " + seed.name + seed.icon_state = "seed-x" + +/* + * Plant DNA disk + */ + +/obj/item/weapon/disk/plantgene + name = "plant data disk" + desc = "A disk for storing plant genetic data." + icon_state = "datadisk_hydro" + materials = list(MAT_METAL=30, MAT_GLASS=10) + var/datum/plant_gene/gene + var/read_only = 0 //Well, it's still a floppy disk + +/obj/item/weapon/disk/plantgene/New() + ..() + overlays += "datadisk_gene" + pixel_x = rand(-5, 5) + pixel_y = rand(-5, 5) + +/obj/item/weapon/disk/plantgene/attackby(obj/item/weapon/W, mob/user, params) + ..() + if(istype(W, /obj/item/weapon/pen)) + var/t = stripped_input(user, "What would you like the label to be?", name, null) + if(user.get_active_hand() != W) + return + if(!in_range(src, user) && loc != user) + return + if(t) + name = "plant data disk - '[t]'" + else + name = "plant data disk" + +/obj/item/weapon/disk/plantgene/proc/update_name() + if(gene) + name = "plant data disk - '[gene.get_name()]'" + else + name = "plant data disk" + +/obj/item/weapon/disk/plantgene/attack_self(mob/user) + read_only = !read_only + to_chat(user, "You flip the write-protect tab to [read_only ? "protected" : "unprotected"].") + +/obj/item/weapon/disk/plantgene/examine(mob/user) + ..() + to_chat(user, "The write-protect tab is set to [read_only ? "protected" : "unprotected"].") + + +/* + * Plant DNA Disks Box + */ +/obj/item/weapon/storage/box/disks_plantgene + name = "plant data disks box" + icon_state = "disk_kit" + +/obj/item/weapon/storage/box/disks_plantgene/New() + ..() + for(var/i in 1 to 7) + new /obj/item/weapon/disk/plantgene(src) \ No newline at end of file diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 70091039e6d..2322bbad5ca 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -1,535 +1,148 @@ -//Grown foods. +// *********************************************************** +// Foods that are produced from hydroponics ~~~~~~~~~~ +// Data from the seeds carry over to these grown foods +// *********************************************************** + +// Base type. Subtypes are found in /grown dir. /obj/item/weapon/reagent_containers/food/snacks/grown - - name = "fruit" - icon = 'icons/obj/hydroponics_products.dmi' - icon_state = "blank" - desc = "Nutritious! Probably." - - var/plantname - var/datum/seed/seed - var/potency = -1 - var/awakening = 0 + icon = 'icons/obj/hydroponics/harvest.dmi' + var/obj/item/seeds/seed = null // type path, gets converted to item on New(). It's safe to assume it's always a seed item. + var/plantname = "" + var/bitesize_mod = 0 + var/splat_type = /obj/effect/decal/cleanable/plant_smudge + // If set, bitesize = 1 + round(reagents.total_volume / bitesize_mod) + dried_type = -1 + // Saves us from having to define each stupid grown's dried_type as itself. + // If you don't want a plant to be driable (watermelons) set this to null in the time definition. burn_state = FLAMMABLE + origin_tech = "biotech=1" -/obj/item/weapon/reagent_containers/food/snacks/grown/New(newloc,planttype) - +/obj/item/weapon/reagent_containers/food/snacks/grown/New(newloc, var/obj/item/seeds/new_seed = null) ..() - if(!dried_type) + if(new_seed) + seed = new_seed.Copy() + else if(ispath(seed)) + // This is for adminspawn or map-placed growns. They get the default stats of their seed type. + seed = new seed() + seed.adjust_potency(50-seed.potency) + + pixel_x = rand(-5, 5) + pixel_y = rand(-5, 5) + + if(dried_type == -1) dried_type = type - src.pixel_x = rand(-5.0, 5) - src.pixel_y = rand(-5.0, 5) - - // Fill the object up with the appropriate reagents. - if(planttype) - plantname = planttype - - if(!plantname) - return - - if(!plant_controller) - sleep(250) // ugly hack, should mean roundstart plants are fine. - if(!plant_controller) - to_chat(world, "Plant controller does not exist and [src] requires it. Aborting.") - qdel(src) - return - - seed = plant_controller.seeds[plantname] - - if(!seed) - return - - name = "[seed.seed_name]" - - if(seed.modular_icon == 1) - update_icon() - else - icon = 'icons/obj/harvest.dmi' - icon_state = seed.preset_icon - - if(!seed.chems) - return - - potency = seed.get_trait(TRAIT_POTENCY) - - for(var/rid in seed.chems) - var/list/reagent_data = seed.chems[rid] - if(reagent_data && reagent_data.len) - var/rtotal = reagent_data[1] - if(reagent_data.len > 1 && potency > 0) - rtotal += round(potency/reagent_data[2]) - reagents.add_reagent(rid,max(1,rtotal)) - update_desc() - update_trash() - if(reagents.total_volume > 0) - bitesize = 1+round(reagents.total_volume / 2, 1) - -/obj/item/weapon/reagent_containers/food/snacks/grown/proc/update_trash() - if(!seed) - return - trash = seed.trash_type - if(seed.kitchen_tag) - if(seed.kitchen_tag == "watermelon") // 15% chance to leave behind a pack of watermelon seeds - if(prob(15)) - var/obj/item/seeds/seeds = new() - seeds.seed = seed - seeds.update_seed() - trash = seeds - else - trash = null - -/obj/item/weapon/reagent_containers/food/snacks/grown/proc/update_desc() - - if(!seed) - return - if(!plant_controller) - sleep(250) // ugly hack, should mean roundstart plants are fine. - if(!plant_controller) - to_chat(world, "Plant controller does not exist and [src] requires it. Aborting.") - qdel(src) - return - - if(plant_controller.product_descs["[seed.uid]"]) - desc = plant_controller.product_descs["[seed.uid]"] - else - var/list/descriptors = list() - if(reagents.has_reagent("sugar") || reagents.has_reagent("cherryjelly") || reagents.has_reagent("honey") || reagents.has_reagent("berryjuice")) - descriptors |= "sweet" - if(reagents.has_reagent("charcoal")) - descriptors |= "astringent" - if(reagents.has_reagent("frostoil")) - descriptors |= "numbing" - if(reagents.has_reagent("nutriment")) - descriptors |= "nutritious" - if(reagents.has_reagent("condensedcapsaicin") || reagents.has_reagent("capsaicin")) - descriptors |= "spicy" - if(reagents.has_reagent("cocoa")) - descriptors |= "bitter" - if(reagents.has_reagent("orangejuice") || reagents.has_reagent("lemonjuice") || reagents.has_reagent("limejuice")) - descriptors |= "sweet-sour" - if(reagents.has_reagent("radium") || reagents.has_reagent("uranium")) - descriptors |= "radioactive" - if(reagents.has_reagent("amanitin") || reagents.has_reagent("toxin") || reagents.has_reagent("carpotoxin")) - descriptors |= "poisonous" - if(reagents.has_reagent("lsd") || reagents.has_reagent("space_drugs") || reagents.has_reagent("psilocybin")) - descriptors |= "hallucinogenic" - if(reagents.has_reagent("styptic_powder")) - descriptors |= "medicinal" - if(reagents.has_reagent("gold") || reagents.has_reagent("silver")) - descriptors |= "shiny" - if(reagents.has_reagent("lube")) - descriptors |= "slippery" - if(reagents.has_reagent("facid") || reagents.has_reagent("sacid")) - descriptors |= "acidic" - if(reagents.has_reagent("fuel")) - descriptors |= "flammable" - if(reagents.has_reagent("moonshine")) - descriptors |= "intoxicating" - if(seed.get_trait(TRAIT_JUICY)) - descriptors |= "juicy" - if(seed.get_trait(TRAIT_STINGS)) - descriptors |= "stinging" - if(seed.get_trait(TRAIT_TELEPORTING)) - descriptors |= "glowing" - if(seed.get_trait(TRAIT_EXPLOSIVE)) - descriptors |= "bulbous" - - var/descriptor_num = rand(2,4) - var/descriptor_count = descriptor_num - desc = "A" - while(descriptors.len && descriptor_num > 0) - var/chosen = pick(descriptors) - descriptors -= chosen - desc += "[(descriptor_count>1 && descriptor_count!=descriptor_num) ? "," : "" ] [chosen]" - descriptor_num-- - if(seed.seed_noun == "spores") - desc += " mushroom" - else - desc += " fruit" - plant_controller.product_descs["[seed.uid]"] = desc - desc += ". Delicious! Probably." - -/obj/item/weapon/reagent_containers/food/snacks/grown/update_icon() - if(!seed || !plant_controller || !plant_controller.plant_icon_cache) - return - if(seed.modular_icon != 1) - return - overlays.Cut() - var/image/plant_icon - var/icon_key = "fruit-[seed.get_trait(TRAIT_PRODUCT_ICON)]-[seed.get_trait(TRAIT_PRODUCT_COLOUR)]-[seed.get_trait(TRAIT_PLANT_COLOUR)]" - if(plant_controller.plant_icon_cache[icon_key]) - plant_icon = plant_controller.plant_icon_cache[icon_key] - else - plant_icon = image('icons/obj/hydroponics_products.dmi',"blank") - var/image/fruit_base = image('icons/obj/hydroponics_products.dmi',"[seed.get_trait(TRAIT_PRODUCT_ICON)]-product") - fruit_base.color = "[seed.get_trait(TRAIT_PRODUCT_COLOUR)]" - plant_icon.overlays |= fruit_base - if("[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf" in icon_states('icons/obj/hydroponics_products.dmi')) - var/image/fruit_leaves = image('icons/obj/hydroponics_products.dmi',"[seed.get_trait(TRAIT_PRODUCT_ICON)]-leaf") - fruit_leaves.color = "[seed.get_trait(TRAIT_PLANT_COLOUR)]" - plant_icon.overlays |= fruit_leaves - plant_controller.plant_icon_cache[icon_key] = plant_icon - overlays |= plant_icon - -/obj/item/weapon/reagent_containers/food/snacks/grown/Crossed(var/mob/living/M) - if(seed && seed.get_trait(TRAIT_JUICY) == 2) - if(istype(M)) - - if(M.buckled) - return - - if(istype(M,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - if(H.shoes && H.shoes.flags & NOSLIP) - return - - M.stop_pulling() - to_chat(M, "You slipped on the [name]!") - playsound(src.loc, 'sound/misc/slip.ogg', 50, 1, -3) - M.Stun(8) - M.Weaken(5) - seed.thrown_at(src,M) - sleep(-1) - if(src) qdel(src) - return - -/obj/item/weapon/reagent_containers/food/snacks/grown/throw_impact(atom/hit_atom) - ..() - if(seed) seed.thrown_at(src,hit_atom) - -/obj/item/weapon/reagent_containers/food/snacks/grown/attackby(var/obj/item/weapon/W, var/mob/user) if(seed) - if(seed.get_trait(TRAIT_PRODUCES_POWER) && istype(W, /obj/item/stack/cable_coil)) - var/obj/item/stack/cable_coil/C = W - if(C.use(5)) - //TODO: generalize this. - to_chat(user, "You add some cable to the [src.name] and slide it inside the battery casing.") - var/obj/item/weapon/stock_parts/cell/potato/pocell = new /obj/item/weapon/stock_parts/cell/potato(get_turf(user)) - if(src.loc == user && !(user.l_hand && user.r_hand) && istype(user,/mob/living/carbon/human)) - user.put_in_hands(pocell) - pocell.maxcharge = src.potency * 10 - pocell.charge = pocell.maxcharge - qdel(src) - return - else if(W.sharp) - var/reagents_per_slice - var/obj/slice - if(seed.kitchen_tag == "pumpkin") // Ugggh these checks are awful. - user.show_message("You carve a face into [src]!", 1) - new /obj/item/clothing/head/hardhat/pumpkinhead (user.loc) - qdel(src) - return - else if(seed.kitchen_tag == "potato") - to_chat(user, "You slice \the [src] into sticks.") - reagents_per_slice = reagents.total_volume - slice = new /obj/item/weapon/reagent_containers/food/snacks/rawsticks(get_turf(src)) - reagents.trans_to(slice, reagents_per_slice) - qdel(src) - return - else if(seed.kitchen_tag == "carrot") - to_chat(user, "You slice \the [src] into sticks.") - reagents_per_slice = reagents.total_volume - slice = new /obj/item/weapon/reagent_containers/food/snacks/carrotfries(get_turf(src)) - reagents.trans_to(slice, reagents_per_slice) - qdel(src) - return - else if(seed.kitchen_tag == "watermelon") - to_chat(user, "You slice \the [src] into large slices.") - reagents_per_slice = reagents.total_volume/5 - for(var/i=0,i<5,i++) - slice = new /obj/item/weapon/reagent_containers/food/snacks/watermelonslice(get_turf(src)) - reagents.trans_to(slice, reagents_per_slice) - qdel(src) - return - else if(seed.kitchen_tag == "soybeans") - to_chat(user, "You roughly chop up \the [src].") - reagents_per_slice = reagents.total_volume - slice = new /obj/item/weapon/reagent_containers/food/snacks/soydope(get_turf(src)) - reagents.trans_to(slice, reagents_per_slice) - qdel(src) - return - else if(seed.chems) - if(istype(W,/obj/item/weapon/hatchet) && !isnull(seed.chems["woodpulp"])) - user.show_message("You make planks out of \the [src]!", 1) - for(var/i=0,i<2,i++) - var/obj/item/stack/sheet/wood/NG = new (user.loc) - NG.color = seed.get_trait(TRAIT_PRODUCT_COLOUR) - for(var/obj/item/stack/sheet/wood/G in user.loc) - if(G==NG) - continue - if(G.amount>=G.max_amount) - continue - G.attackby(NG, user) - to_chat(user, "You add the newly-formed wood to the stack. It now contains [NG.amount] planks.") - qdel(src) - return - else if(istype(W, /obj/item/weapon/rollingpaper)) - if(seed.kitchen_tag == "ambrosia" || seed.kitchen_tag == "ambrosiadeus" || seed.kitchen_tag == "tobacco" || seed.kitchen_tag == "stobacco") - user.unEquip(W) - if(seed.kitchen_tag == "ambrosia") - var/obj/item/clothing/mask/cigarette/joint/J = new /obj/item/clothing/mask/cigarette/joint(user.loc) - J.chem_volume = src.reagents.total_volume - src.reagents.trans_to(J, J.chem_volume) - qdel(W) - user.put_in_active_hand(J) - else if(seed.kitchen_tag == "ambrosiadeus") - var/obj/item/clothing/mask/cigarette/joint/deus/J = new /obj/item/clothing/mask/cigarette/joint/deus(user.loc) - J.chem_volume = src.reagents.total_volume - src.reagents.trans_to(J, J.chem_volume) - qdel(W) - user.put_in_active_hand(J) - else if(seed.kitchen_tag == "tobacco" || seed.kitchen_tag == "stobacco") - var/obj/item/clothing/mask/cigarette/handroll/J = new /obj/item/clothing/mask/cigarette/handroll(user.loc) - J.chem_volume = src.reagents.total_volume - src.reagents.trans_to(J, J.chem_volume) - qdel(W) - user.put_in_active_hand(J) - to_chat(user, "\blue You roll the [src] into a rolling paper.") - qdel(src) - else - to_chat(user, "\red You can't roll a smokable from the [src].") + for(var/datum/plant_gene/trait/T in seed.genes) + T.on_new(src, newloc) + seed.prepare_result(src) + transform *= TransformUsingVariable(seed.potency, 100, 0.5) //Makes the resulting produce's sprite larger or smaller based on potency! + add_juice() - ..() -/obj/item/weapon/reagent_containers/food/snacks/grown/attack(var/mob/living/carbon/M, var/mob/user, var/def_zone) - if(awakening) - to_chat(user, "The [src] is twitching and shaking, preventing you from eating it.") - return - if(user == M) - return ..() - if(user.a_intent == I_HARM) - - // This is being copypasted here because reagent_containers (WHY DOES FOOD DESCEND FROM THAT) overrides it completely. - // TODO: refactor all food paths to be less horrible and difficult to work with in this respect. ~Z - if(!istype(M)) return 0 - - if(!def_zone) - def_zone = check_zone(user.zone_sel.selecting) - - user.lastattacked = M - M.lastattacker = user - user.create_attack_log(" Attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])") - M.create_attack_log(" Attacked by [key_name(user)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])") - msg_admin_attack("[key_name_admin(user)] attacked [key_name_admin(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" ) - - if(istype(M, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = M - var/hit = H.attacked_by(src, user, def_zone) - if(hit && hitsound) - playsound(loc, hitsound, 50, 1, -1) - //return hit - else - if(attack_verb.len) - user.visible_message("[M] has been [pick(attack_verb)] with [src] by [user]!") - else - user.visible_message("[M] has been attacked with [src] by [user]!") - - if(hitsound) - playsound(loc, hitsound, 50, 1, -1) - switch(damtype) - if("brute") - M.take_organ_damage(force) - if(prob(33)) - var/turf/simulated/location = get_turf(M) - if(istype(location)) location.add_blood_floor(M) - if("fire") - M.take_organ_damage(0, force) - M.updatehealth() - - if(seed && seed.get_trait(TRAIT_CARNIVOROUS)) - seed.do_thorns(M, src, def_zone) - - if(ishuman(M) && seed && seed.get_trait(TRAIT_STINGS)) - if(!reagents || reagents.total_volume <= 0) - return - seed.do_sting(M, src, def_zone) - reagents.remove_any(rand(1,3)) //use up some of the reagents at random - sleep(-1) - if(!src) - return - if(reagents && reagents.total_volume <= 0) //used-up fruit will be destroyed - if(user) - to_chat(user, "\The [src] has dried out and crumbles to dust.") - //user.drop_from_inventory(src) - qdel(src) - else if(prob(35)) //fruit that still has reagents has a chance of breaking each time it stings on hit - if(user) - to_chat(user, "\The [src] has fallen to bits.") - //user.drop_from_inventory(src) - qdel(src) - - add_fingerprint(user) +/obj/item/weapon/reagent_containers/food/snacks/grown/proc/add_juice() + if(reagents) + if(bitesize_mod) + bitesize = 1 + round(reagents.total_volume / bitesize_mod) return 1 + return 0 +/obj/item/weapon/reagent_containers/food/snacks/grown/examine(user) + ..() + if(seed) + for(var/datum/plant_gene/trait/T in seed.genes) + if(T.examine_line) + to_chat(user, T.examine_line) + +/obj/item/weapon/reagent_containers/food/snacks/grown/attackby(obj/item/O, mob/user, params) + ..() + if (istype(O, /obj/item/device/plant_analyzer)) + var/msg = "*---------*\n This is \a [src].\n" + if(seed) + msg += seed.get_analyzer_text() + var/reag_txt = "" + if(seed) + for(var/reagent_id in seed.reagents_add) + var/datum/reagent/R = chemical_reagents_list[reagent_id] + var/amt = reagents.get_reagent_amount(reagent_id) + reag_txt += "\n- [R.name]: [amt]" + + if(reag_txt) + msg += reag_txt + msg += " *---------*" + to_chat(user, msg) else - ..() - -/obj/item/weapon/reagent_containers/food/snacks/grown/attack_self(mob/user as mob) - - if(!seed) - return - - if(istype(user.loc,/turf/space)) - return - - if(user.a_intent == I_HARM) - user.visible_message("\The [user] squashes \the [src]!") - seed.thrown_at(src,user) - sleep(-1) - if(src) qdel(src) - return - - if(user.a_intent == I_DISARM && seed.get_trait(TRAIT_SPREAD) > 0) //Using disarm so we can tell if you want to plant or convert non-final plants - to_chat(user, "You plant the [src.name].") - new /obj/machinery/portable_atmospherics/hydroponics/soil/invisible(get_turf(user),src.seed) - new /obj/effect/plant(get_turf(user), src.seed) - qdel(src) - return + if(seed) + for(var/datum/plant_gene/trait/T in seed.genes) + T.on_attackby(src, O, user) - if(!seed.final_form) //This isn't even my final form! (sorry, it had to be done) - switch(seed.kitchen_tag) - if("comfrey") - var/obj/item/stack/medical/bruise_pack/comfrey/poultice = new /obj/item/stack/medical/bruise_pack/comfrey(user.loc) - poultice.heal_brute = potency - to_chat(user, "You mash the leaves into a poultice.") - qdel(src) - return - if("aloe") - var/obj/item/stack/medical/ointment/aloe/poultice = new /obj/item/stack/medical/ointment/aloe(user.loc) - poultice.heal_burn = potency - to_chat(user, "You mash the petals into a poultice.") - qdel(src) - return - if("grass") - user.show_message("You make a grass tile out of \the [src]!", 1) - for(var/i=0,i<2,i++) - var/obj/item/stack/tile/grass/G = new (user.loc) - G.color = seed.get_trait(TRAIT_PRODUCT_COLOUR) - for(var/obj/item/stack/tile/grass/NG in user.loc) - if(G==NG) - continue - if(NG.amount>=NG.max_amount) - continue - NG.attackby(G, user) - to_chat(user, "You add the newly-formed grass to the stack. It now contains [G.amount] tiles.") - qdel(src) - return - if("sunflower") - var/obj/item/weapon/grown/sunflower/SF = new /obj/item/weapon/grown/sunflower(user.loc) - user.unEquip(src) - user.put_in_hands(SF) - qdel(src) - return - if("novaflower") - var/obj/item/weapon/grown/novaflower/NF = new /obj/item/weapon/grown/novaflower(user.loc) - if(prob(10)) - user.say("PRAISE THE SUN!") - else - to_chat(user, "PRAISE THE SUN!") - user.unEquip(src) - user.put_in_hands(NF) - qdel(src) - return - if("nettle") - var/obj/item/weapon/grown/nettle/nettle = new /obj/item/weapon/grown/nettle(user.loc) - nettle.force = round((5 + potency / 5), 1) - to_chat(user, "You straighten up the plant.") - user.unEquip(src) - user.put_in_hands(nettle) - qdel(src) - return - if("deathnettle") - var/obj/item/weapon/grown/nettle/death/DN = new /obj/item/weapon/grown/nettle/death(user.loc) - DN.force = round((5 + potency / 2.5), 1) - to_chat(user, "You straighten up the plant.") - user.unEquip(src) - user.put_in_hands(DN) - qdel(src) - return - if("killertomato") - if(awakening || istype(user.loc,/turf/space)) - return - to_chat(user, "You begin to awaken the Killer Tomato...") - awakening = 1 - - spawn(30) - if(!qdeleted(src)) - var/mob/living/simple_animal/hostile/killertomato/K = new /mob/living/simple_animal/hostile/killertomato(get_turf(src.loc)) - var/endurance_value = seed.get_trait(TRAIT_ENDURANCE) - K.maxHealth += round(endurance_value / 3) - K.melee_damage_lower += round(potency / 10) - K.melee_damage_upper += round(potency / 10) - var/production_value = seed.get_trait(TRAIT_PRODUCTION) - K.move_to_delay -= round(production_value / 50) - K.health = K.maxHealth - K.visible_message("The Killer Tomato growls as it suddenly awakens.") - if(user) - user.unEquip(src) - qdel(src) - if("cashpod") - to_chat(user, "You crack open the cash pod...") - var/value = round(seed.get_trait(TRAIT_POTENCY)) - user.unEquip(src) - switch(value) - if(0) - to_chat(user, "It's empty! What a waste...") - if(1 to 10) - to_chat(user, "It has a space dollar inside. Woo.") - new /obj/item/weapon/spacecash(get_turf(user)) - if(11 to 20) - to_chat(user, "It has 10 space dollars inside!") - new /obj/item/weapon/spacecash/c10(get_turf(user)) - if(21 to 30) - to_chat(user, "It has 20 space dollars inside! Cool!") - new /obj/item/weapon/spacecash/c20(get_turf(user)) - if(31 to 40) - to_chat(user, "It has 50 space dollars inside! Nice!") - new /obj/item/weapon/spacecash/c50(get_turf(user)) - if(41 to 50) - to_chat(user, "It has 100 space dollars inside! Sweet!") - new /obj/item/weapon/spacecash/c100(get_turf(user)) - if(51 to 60) - to_chat(user, "It has 200 space dollars inside! Awesome!") - new /obj/item/weapon/spacecash/c200(get_turf(user)) - if(61 to 80) - to_chat(user, "It has 500 space dollars inside! CHA-CHING!") - new /obj/item/weapon/spacecash/c500(get_turf(user)) - else - to_chat(user, "It has 1000 space dollars inside! JACKPOT!") - new /obj/item/weapon/spacecash/c1000(get_turf(user)) - qdel(src) - return - -/obj/item/weapon/reagent_containers/food/snacks/grown/pickup(mob/user) +// Various gene procs +/obj/item/weapon/reagent_containers/food/snacks/grown/attack_self(mob/user) + if(seed && seed.get_gene(/datum/plant_gene/trait/squash)) + squash(user) ..() - if(!seed) - return - if(seed.get_trait(TRAIT_STINGS)) - var/mob/living/carbon/human/H = user - if(istype(H) && H.gloves) - return - if(!reagents || reagents.total_volume <= 0) - return - reagents.remove_any(rand(1,3)) //Todo, make it actually remove the reagents the seed uses. - seed.do_thorns(H,src) - seed.do_sting(H,src,pick("r_hand","l_hand")) +/obj/item/weapon/reagent_containers/food/snacks/grown/throw_impact(atom/hit_atom) + if(!..()) //was it caught by a mob? + if(seed) + for(var/datum/plant_gene/trait/T in seed.genes) + T.on_throw_impact(src, hit_atom) + if(seed.get_gene(/datum/plant_gene/trait/squash)) + squash(hit_atom) -/obj/item/weapon/reagent_containers/food/snacks/grown/On_Consume(mob/M, mob/user) - if(seed && seed.get_trait(TRAIT_BATTERY_RECHARGE)) - if(!reagents.total_volume) - var/batteries_recharged = 0 - for(var/obj/item/weapon/stock_parts/cell/C in user.GetAllContents()) - var/newcharge = (potency*0.01)*C.maxcharge - if(C.charge < newcharge) - C.charge = newcharge - if(isobj(C.loc)) - var/obj/O = C.loc - O.update_icon() //update power meters and such - batteries_recharged = 1 - if(batteries_recharged) - to_chat(user, "Battery has recovered.") +/obj/item/weapon/reagent_containers/food/snacks/grown/proc/squash(atom/target) + var/turf/T = get_turf(target) + if(ispath(splat_type, /obj/effect/decal/cleanable/plant_smudge)) + if(filling_color) + var/obj/O = new splat_type(T) + O.color = filling_color + O.name = "[name] smudge" + else if(splat_type) + new splat_type(T) + + if(trash) + generate_trash(T) + + visible_message("[src] has been squashed.","You hear a smack.") + if(seed) + for(var/datum/plant_gene/trait/trait in seed.genes) + trait.on_squash(src, target) + + for(var/A in T) + if(reagents) + reagents.reaction(A) + + qdel(src) + +/obj/item/weapon/reagent_containers/food/snacks/grown/On_Consume() + if(iscarbon(usr)) + if(seed) + for(var/datum/plant_gene/trait/T in seed.genes) + T.on_consume(src, usr) ..() + +/obj/item/weapon/reagent_containers/food/snacks/grown/Crossed(atom/movable/AM) + if(seed) + for(var/datum/plant_gene/trait/T in seed.genes) + T.on_cross(src, AM) + ..() + + +// Glow gene procs +/obj/item/weapon/reagent_containers/food/snacks/grown/generate_trash(atom/location) + if(trash && ispath(trash, /obj/item/weapon/grown)) + . = new trash(location, seed) + trash = null + return + return ..() + +// For item-containing growns such as eggy or gatfruit +/obj/item/weapon/reagent_containers/food/snacks/grown/shell/attack_self(mob/user) + user.unEquip(src) + if(trash) + var/obj/item/T = generate_trash() + user.put_in_hands(T) + to_chat(user, "You open [src]\'s shell, revealing \a [T].") + qdel(src) diff --git a/code/modules/hydroponics/grown/ambrosia.dm b/code/modules/hydroponics/grown/ambrosia.dm new file mode 100644 index 00000000000..9700b8f3d3b --- /dev/null +++ b/code/modules/hydroponics/grown/ambrosia.dm @@ -0,0 +1,86 @@ +// Ambrosia - base type +/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia + seed = /obj/item/seeds/ambrosia + name = "ambrosia branch" + desc = "This is a plant." + icon_state = "ambrosiavulgaris" + slot_flags = SLOT_HEAD + filling_color = "#008000" + bitesize_mod = 2 + +// Ambrosia Vulgaris +/obj/item/seeds/ambrosia + name = "pack of ambrosia vulgaris seeds" + desc = "These seeds grow into common ambrosia, a plant grown by and from medicine." + icon_state = "seed-ambrosiavulgaris" + species = "ambrosiavulgaris" + plantname = "Ambrosia Vulgaris" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris + lifespan = 60 + endurance = 25 + yield = 6 + potency = 5 + icon_dead = "ambrosia-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/ambrosia/deus) + reagents_add = list("space_drugs" = 0.15, "bicaridine" = 0.1, "kelotane" = 0.1, "vitamin" = 0.04, "nutriment" = 0.05, "toxin" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/vulgaris + seed = /obj/item/seeds/ambrosia + name = "ambrosia vulgaris branch" + desc = "This is a plant containing various healing chemicals." + origin_tech = "biotech=2" + +// Ambrosia Deus +/obj/item/seeds/ambrosia/deus + name = "pack of ambrosia deus seeds" + desc = "These seeds grow into ambrosia deus. Could it be the food of the gods..?" + icon_state = "seed-ambrosiadeus" + species = "ambrosiadeus" + plantname = "Ambrosia Deus" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/deus + mutatelist = list(/obj/item/seeds/ambrosia/gaia) + reagents_add = list("weak_omnizine" = 0.15, "synaptizine" = 0.15, "space_drugs" = 0.1, "vitamin" = 0.04, "nutriment" = 0.05) + rarity = 40 + +/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/deus + seed = /obj/item/seeds/ambrosia/deus + name = "ambrosia deus branch" + desc = "Eating this makes you feel immortal!" + icon_state = "ambrosiadeus" + filling_color = "#008B8B" + origin_tech = "biotech=4;materials=3" + +//Ambrosia Gaia +/obj/item/seeds/ambrosia/gaia + name = "pack of ambrosia gaia seeds" + desc = "These seeds grow into ambrosia gaia, filled with infinite potential." + icon_state = "seed-ambrosia_gaia" + species = "ambrosia_gaia" + plantname = "Ambrosia Gaia" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/gaia + mutatelist = list() + reagents_add = list("earthsblood" = 0.05, "nutriment" = 0.06, "vitamin" = 0.05) + rarity = 30 //These are some pretty good plants right here + genes = list() + weed_rate = 4 + weed_chance = 100 + +/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/gaia + name = "ambrosia gaia branch" + desc = "Eating this makes you immortal." + icon_state = "ambrosia_gaia" + filling_color = rgb(255, 175, 0) + origin_tech = "biotech=6;materials=5" + light_range = 3 + seed = /obj/item/seeds/ambrosia/gaia + +// Ambrosia Cruciatus +/obj/item/seeds/ambrosia/cruciatus + product = /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/cruciatus + potency = 10 + mutatelist = list() + reagents_add = list("thc" = 0.15, "kelotane" = 0.15, "bicaridine" = 0.1, "bath_salts" = 0.20, "nutriment" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/cruciatus + seed = /obj/item/seeds/ambrosia/cruciatus \ No newline at end of file diff --git a/code/modules/hydroponics/grown/apple.dm b/code/modules/hydroponics/grown/apple.dm new file mode 100644 index 00000000000..8acfac34f74 --- /dev/null +++ b/code/modules/hydroponics/grown/apple.dm @@ -0,0 +1,57 @@ +// Apple +/obj/item/seeds/apple + name = "pack of apple seeds" + desc = "These seeds grow into apple trees." + icon_state = "seed-apple" + species = "apple" + plantname = "Apple Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/apple + lifespan = 55 + endurance = 35 + yield = 5 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "apple-grow" + icon_dead = "apple-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/apple/gold) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/apple + seed = /obj/item/seeds/apple + name = "apple" + desc = "It's a little piece of Eden." + icon_state = "apple" + filling_color = "#FF4500" + bitesize = 100 // Always eat the apple in one bite + +// Posioned Apple +/obj/item/seeds/apple/poisoned + product = /obj/item/weapon/reagent_containers/food/snacks/grown/apple/poisoned + mutatelist = list() + reagents_add = list("cyanide" = 0.5, "vitamin" = 0.04, "nutriment" = 0.1) + rarity = 50 // Source of cyanide, and hard (almost impossible) to obtain normally. + +/obj/item/weapon/reagent_containers/food/snacks/grown/apple/poisoned + seed = /obj/item/seeds/apple/poisoned + +// Gold Apple +/obj/item/seeds/apple/gold + name = "pack of golden apple seeds" + desc = "These seeds grow into golden apple trees. Good thing there are no firebirds in space." + icon_state = "seed-goldapple" + species = "goldapple" + plantname = "Golden Apple Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/apple/gold + maturation = 10 + production = 10 + mutatelist = list() + reagents_add = list("gold" = 0.2, "vitamin" = 0.04, "nutriment" = 0.1) + rarity = 40 // Alchemy! + +/obj/item/weapon/reagent_containers/food/snacks/grown/apple/gold + seed = /obj/item/seeds/apple/gold + name = "golden apple" + desc = "Emblazoned upon the apple is the word 'Kallisti'." + icon_state = "goldapple" + filling_color = "#FFD700" + origin_tech = "biotech=4;materials=5" diff --git a/code/modules/hydroponics/grown/banana.dm b/code/modules/hydroponics/grown/banana.dm new file mode 100644 index 00000000000..b59eeb42e53 --- /dev/null +++ b/code/modules/hydroponics/grown/banana.dm @@ -0,0 +1,121 @@ +// Banana +/obj/item/seeds/banana + name = "pack of banana seeds" + desc = "They're seeds that grow into banana trees. When grown, keep away from clown." + icon_state = "seed-banana" + species = "banana" + plantname = "Banana Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/banana + lifespan = 50 + endurance = 30 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_dead = "banana-dead" + genes = list(/datum/plant_gene/trait/slip, /datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/banana/mime, /obj/item/seeds/banana/bluespace) + reagents_add = list("banana" = 0.1, "potassium" = 0.1, "vitamin" = 0.04, "nutriment" = 0.02) + +/obj/item/weapon/reagent_containers/food/snacks/grown/banana + seed = /obj/item/seeds/banana + name = "banana" + desc = "It's an excellent prop for a clown." + icon_state = "banana" + item_state = "banana" + trash = /obj/item/weapon/grown/bananapeel + filling_color = "#FFFF00" + bitesize = 5 + +/obj/item/weapon/reagent_containers/food/snacks/grown/banana/suicide_act(mob/user) + user.visible_message("[user] is aiming the [src.name] at themself! It looks like \he's trying to commit suicide.") + playsound(loc, 'sound/items/bikehorn.ogg', 50, 1, -1) + sleep(25) + if(!user) + return (OXYLOSS) + user.say("BANG!") + sleep(25) + if(!user) + return (OXYLOSS) + user.visible_message("[user] laughs so hard they begin to suffocate!") + return (OXYLOSS) + +/obj/item/weapon/grown/bananapeel + seed = /obj/item/seeds/banana + name = "banana peel" + desc = "A peel from a banana." + icon_state = "banana_peel" + item_state = "banana_peel" + w_class = 1 + throwforce = 0 + throw_speed = 3 + throw_range = 7 + +/obj/item/weapon/grown/bananapeel/suicide_act(mob/user) + user.visible_message("[user] is deliberately slipping on the [src.name]! It looks like \he's trying to commit suicide.") + playsound(loc, 'sound/misc/slip.ogg', 50, 1, -1) + return (BRUTELOSS) + + +// Mimana - invisible sprites are totally a feature! +/obj/item/seeds/banana/mime + name = "pack of mimana seeds" + desc = "They're seeds that grow into mimana trees. When grown, keep away from mime." + icon_state = "seed-mimana" + species = "mimana" + plantname = "Mimana Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/banana/mime + growthstages = 4 + mutatelist = list() + reagents_add = list("nothing" = 0.1, "capulettium_plus" = 0.1, "nutriment" = 0.02) + rarity = 15 + +/obj/item/weapon/reagent_containers/food/snacks/grown/banana/mime + seed = /obj/item/seeds/banana/mime + name = "mimana" + desc = "It's an excellent prop for a mime." + icon_state = "mimana" + trash = /obj/item/weapon/grown/bananapeel/mimanapeel + filling_color = "#FFFFEE" + +/obj/item/weapon/grown/bananapeel/mimanapeel + seed = /obj/item/seeds/banana/mime + name = "mimana peel" + desc = "A mimana peel." + icon_state = "mimana_peel" + +// Bluespace Banana +/obj/item/seeds/banana/bluespace + name = "pack of bluespace banana seeds" + desc = "They're seeds that grow into bluespace banana trees. When grown, keep away from bluespace clown." + icon_state = "seed-banana-blue" + species = "bluespacebanana" + icon_grow = "banana-grow" + plantname = "Bluespace Banana Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/banana/bluespace + mutatelist = list() + genes = list(/datum/plant_gene/trait/slip, /datum/plant_gene/trait/teleport, /datum/plant_gene/trait/repeated_harvest) + reagents_add = list("singulo" = 0.2, "banana" = 0.1, "vitamin" = 0.04, "nutriment" = 0.02) + rarity = 30 + +/obj/item/weapon/reagent_containers/food/snacks/grown/banana/bluespace + seed = /obj/item/seeds/banana/bluespace + name = "bluespace banana" + icon_state = "banana_blue" + trash = /obj/item/weapon/grown/bananapeel/bluespace + filling_color = "#0000FF" + origin_tech = "biotech=3;bluespace=5" + +/obj/item/weapon/grown/bananapeel/bluespace + seed = /obj/item/seeds/banana/bluespace + name = "bluespace banana peel" + desc = "A peel from a bluespace banana." + icon_state = "banana_peel_blue" + +// Other +/obj/item/weapon/grown/bananapeel/specialpeel //used by /obj/item/clothing/shoes/clown_shoes/banana_shoes + name = "synthesized banana peel" + desc = "A synthetic banana peel." + +/obj/item/weapon/grown/bananapeel/specialpeel/Crossed(AM) + if(iscarbon(AM)) + var/mob/living/carbon/carbon = AM + if(carbon.slip("[src]", 2, 2)) + qdel(src) diff --git a/code/modules/hydroponics/grown/beans.dm b/code/modules/hydroponics/grown/beans.dm new file mode 100644 index 00000000000..277dfd06298 --- /dev/null +++ b/code/modules/hydroponics/grown/beans.dm @@ -0,0 +1,48 @@ +// Soybeans +/obj/item/seeds/soya + name = "pack of soybean seeds" + desc = "These seeds grow into soybean plants." + icon_state = "seed-soybean" + species = "soybean" + plantname = "Soybean Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/soybeans + maturation = 4 + production = 4 + potency = 15 + growthstages = 4 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + icon_grow = "soybean-grow" + icon_dead = "soybean-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/soya/koi) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/soybeans + seed = /obj/item/seeds/soya + name = "soybeans" + desc = "It's pretty bland, but oh the possibilities..." + gender = PLURAL + icon_state = "soybeans" + filling_color = "#F0E68C" + bitesize_mod = 2 + +// Koibean +/obj/item/seeds/soya/koi + name = "pack of koibean seeds" + desc = "These seeds grow into koibean plants." + icon_state = "seed-koibean" + species = "koibean" + plantname = "Koibean Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/koibeans + potency = 10 + mutatelist = list() + reagents_add = list("carpotoxin" = 0.1, "vitamin" = 0.04, "nutriment" = 0.05) + rarity = 20 + +/obj/item/weapon/reagent_containers/food/snacks/grown/koibeans + seed = /obj/item/seeds/soya/koi + name = "koibean" + desc = "Something about these seems fishy." + icon_state = "koibeans" + filling_color = "#F0E68C" + bitesize_mod = 2 \ No newline at end of file diff --git a/code/modules/hydroponics/grown/berries.dm b/code/modules/hydroponics/grown/berries.dm new file mode 100644 index 00000000000..165853c63f7 --- /dev/null +++ b/code/modules/hydroponics/grown/berries.dm @@ -0,0 +1,188 @@ +// Berries +/obj/item/seeds/berry + name = "pack of berry seeds" + desc = "These seeds grow into berry bushes." + icon_state = "seed-berry" + species = "berry" + plantname = "Berry Bush" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/berries + lifespan = 20 + maturation = 5 + production = 5 + yield = 2 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "berry-grow" // Uses one growth icons set for all the subtypes + icon_dead = "berry-dead" // Same for the dead icon + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/berry/glow, /obj/item/seeds/berry/poison) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/berries + seed = /obj/item/seeds/berry + name = "bunch of berries" + desc = "Nutritious!" + icon_state = "berrypile" + gender = PLURAL + filling_color = "#FF00FF" + bitesize_mod = 2 + +// Poison Berries +/obj/item/seeds/berry/poison + name = "pack of poison-berry seeds" + desc = "These seeds grow into poison-berry bushes." + icon_state = "seed-poisonberry" + species = "poisonberry" + plantname = "Poison-Berry Bush" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison + mutatelist = list(/obj/item/seeds/berry/death) + reagents_add = list("cyanide" = 0.15, "tirizene" = 0.2, "vitamin" = 0.04, "nutriment" = 0.1) + rarity = 10 // Mildly poisonous berries are common in reality + +/obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison + seed = /obj/item/seeds/berry/poison + name = "bunch of poison-berries" + desc = "Taste so good, you could die!" + icon_state = "poisonberrypile" + filling_color = "#C71585" + +// Death Berries +/obj/item/seeds/berry/death + name = "pack of death-berry seeds" + desc = "These seeds grow into death berries." + icon_state = "seed-deathberry" + species = "deathberry" + plantname = "Death Berry Bush" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/berries/death + lifespan = 30 + potency = 50 + mutatelist = list() + reagents_add = list("coniine" = 0.08, "tirizene" = 0.1, "vitamin" = 0.04, "nutriment" = 0.1) + rarity = 30 + +/obj/item/weapon/reagent_containers/food/snacks/grown/berries/death + seed = /obj/item/seeds/berry/death + name = "bunch of death-berries" + desc = "Taste so good, you could die!" + icon_state = "deathberrypile" + filling_color = "#708090" + +// Glow Berries +/obj/item/seeds/berry/glow + name = "pack of glow-berry seeds" + desc = "These seeds grow into glow-berry bushes." + icon_state = "seed-glowberry" + species = "glowberry" + plantname = "Glow-Berry Bush" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/berries/glow + lifespan = 30 + endurance = 25 + mutatelist = list() + genes = list(/datum/plant_gene/trait/glow/berry , /datum/plant_gene/trait/noreact, /datum/plant_gene/trait/repeated_harvest) + reagents_add = list("uranium" = 0.25, "iodine" = 0.2, "vitamin" = 0.04, "nutriment" = 0.1) + rarity = 20 + +/obj/item/weapon/reagent_containers/food/snacks/grown/berries/glow + seed = /obj/item/seeds/berry/glow + name = "bunch of glow-berries" + desc = "Nutritious!" + icon_state = "glowberrypile" + filling_color = "#7CFC00" + origin_tech = "plasmatech=6" + light_color = "#006622" + +// Cherries +/obj/item/seeds/cherry + name = "pack of cherry pits" + desc = "Careful not to crack a tooth on one... That'd be the pits." + icon_state = "seed-cherry" + species = "cherry" + plantname = "Cherry Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cherries + lifespan = 35 + endurance = 35 + maturation = 5 + production = 5 + growthstages = 5 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "cherry-grow" + icon_dead = "cherry-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/cherry/blue) + reagents_add = list("nutriment" = 0.07, "sugar" = 0.07) + +/obj/item/weapon/reagent_containers/food/snacks/grown/cherries + seed = /obj/item/seeds/cherry + name = "cherries" + desc = "Great for toppings!" + icon_state = "cherry" + gender = PLURAL + filling_color = "#FF0000" + bitesize_mod = 2 + +// Blue Cherries +/obj/item/seeds/cherry/blue + name = "pack of blue cherry pits" + desc = "The blue kind of cherries" + icon_state = "seed-bluecherry" + species = "bluecherry" + plantname = "Blue Cherry Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/bluecherries + mutatelist = list() + reagents_add = list("nutriment" = 0.07, "sugar" = 0.07) + rarity = 10 + +/obj/item/weapon/reagent_containers/food/snacks/grown/bluecherries + seed = /obj/item/seeds/cherry/blue + name = "blue cherries" + desc = "They're cherries that are blue." + icon_state = "bluecherry" + filling_color = "#6495ED" + bitesize_mod = 2 + +// Grapes +/obj/item/seeds/grape + name = "pack of grape seeds" + desc = "These seeds grow into grape vines." + icon_state = "seed-grapes" + species = "grape" + plantname = "Grape Vine" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/grapes + lifespan = 50 + endurance = 25 + maturation = 3 + production = 5 + yield = 4 + growthstages = 2 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "grape-grow" + icon_dead = "grape-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/grape/green) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.1, "sugar" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/grapes + seed = /obj/item/seeds/grape + name = "bunch of grapes" + desc = "Nutritious!" + icon_state = "grapes" + dried_type = /obj/item/weapon/reagent_containers/food/snacks/no_raisin + filling_color = "#FF1493" + bitesize_mod = 2 + +// Green Grapes +/obj/item/seeds/grape/green + name = "pack of green grape seeds" + desc = "These seeds grow into green-grape vines." + icon_state = "seed-greengrapes" + species = "greengrape" + plantname = "Green-Grape Vine" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/grapes/green + reagents_add = list("kelotane" = 0.2, "vitamin" = 0.04, "nutriment" = 0.1, "sugar" = 0.1) + // No rarity: technically it's a beneficial mutant, but it's not exactly "new"... + mutatelist = list() + +/obj/item/weapon/reagent_containers/food/snacks/grown/grapes/green + seed = /obj/item/seeds/grape/green + name = "bunch of green grapes" + icon_state = "greengrapes" + filling_color = "#7FFF00" diff --git a/code/modules/hydroponics/grown/cannabis.dm b/code/modules/hydroponics/grown/cannabis.dm new file mode 100644 index 00000000000..ab1dff988e8 --- /dev/null +++ b/code/modules/hydroponics/grown/cannabis.dm @@ -0,0 +1,121 @@ +// Cannabis +/obj/item/seeds/cannabis + name = "pack of cannabis seeds" + desc = "Taxable." + icon_state = "seed-cannabis" + species = "cannabis" + plantname = "Cannabis Plant" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cannabis + maturation = 8 + potency = 20 + growthstages = 1 + growing_icon = 'icons/goonstation/objects/hydroponics.dmi' + icon_grow = "cannabis-grow" // Uses one growth icons set for all the subtypes + icon_dead = "cannabis-dead" // Same for the dead icon + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/cannabis/rainbow, + /obj/item/seeds/cannabis/death, + /obj/item/seeds/cannabis/white, + /obj/item/seeds/cannabis/ultimate) + reagents_add = list("thc" = 0.15) + + +/obj/item/seeds/cannabis/rainbow + name = "pack of rainbow weed seeds" + desc = "These seeds grow into rainbow weed. Groovy." + icon_state = "seed-megacannabis" + species = "megacannabis" + plantname = "Rainbow Weed" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cannabis/rainbow + mutatelist = list() + reagents_add = list("lsd" = 0.15, "thc" = 0.15) + rarity = 40 + +/obj/item/seeds/cannabis/death + name = "pack of deathweed seeds" + desc = "These seeds grow into deathweed. Not groovy." + icon_state = "seed-blackcannabis" + species = "blackcannabis" + plantname = "Deathweed" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cannabis/death + mutatelist = list() + reagents_add = list("cyanide" = 0.35, "thc" = 0.15) + rarity = 40 + +/obj/item/seeds/cannabis/white + name = "pack of lifeweed seeds" + desc = "I will give unto him that is munchies of the fountain of the cravings of life, freely." + icon_state = "seed-whitecannabis" + species = "whitecannabis" + plantname = "Lifeweed" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cannabis/white + mutatelist = list() + reagents_add = list("omnizine" = 0.35, "thc" = 0.15) + rarity = 40 + + +/obj/item/seeds/cannabis/ultimate + name = "pack of omega weed seeds" + desc = "These seeds grow into omega weed." + icon_state = "seed-ocannabis" + species = "ocannabis" + plantname = "Omega Weed" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cannabis/ultimate + mutatelist = list() + reagents_add = list("lsd" = 0.15, + "suicider" = 0.15, + "space_drugs" = 0.15, + "mercury" = 0.15, + "lithium" = 0.15, + "atropine" = 0.15, + "ephedrine" = 0.15, + "haloperidol" = 0.15, + "methamphetamine" = 0.15, + "thc" = 0.15, + "psilocybin" = 0.15, + "hairgrownium" = 0.15, + "ectoplasm" = 0.15, + "bath_salts" = 0.15, + "itching_powder" = 0.15, + "crank" = 0.15, + "krokodil" = 0.15, + "histamine" = 0.15) + rarity = 69 + + +// --------------------------------------------------------------- + +/obj/item/weapon/reagent_containers/food/snacks/grown/cannabis + seed = /obj/item/seeds/cannabis + icon = 'icons/goonstation/objects/hydroponics.dmi' + name = "cannabis leaf" + desc = "Recently legalized in most galaxies." + icon_state = "cannabis" + filling_color = "#00FF00" + bitesize_mod = 2 + + +/obj/item/weapon/reagent_containers/food/snacks/grown/cannabis/rainbow + seed = /obj/item/seeds/cannabis/rainbow + name = "rainbow cannabis leaf" + desc = "Is it supposed to be glowing like that...?" + icon_state = "megacannabis" + +/obj/item/weapon/reagent_containers/food/snacks/grown/cannabis/death + seed = /obj/item/seeds/cannabis/death + name = "death cannabis leaf" + desc = "Looks a bit dark. Oh well." + icon_state = "blackcannabis" + +/obj/item/weapon/reagent_containers/food/snacks/grown/cannabis/white + seed = /obj/item/seeds/cannabis/white + name = "white cannabis leaf" + desc = "It feels smooth and nice to the touch." + icon_state = "whitecannabis" + +/obj/item/weapon/reagent_containers/food/snacks/grown/cannabis/ultimate + seed = /obj/item/seeds/cannabis/ultimate + name = "omega cannibas leaf" + desc = "You feel dizzy looking at it. What the fuck?" + icon_state = "ocannabis" + volume = 420 diff --git a/code/modules/hydroponics/grown/cereals.dm b/code/modules/hydroponics/grown/cereals.dm new file mode 100644 index 00000000000..fd5ea080ca3 --- /dev/null +++ b/code/modules/hydroponics/grown/cereals.dm @@ -0,0 +1,90 @@ +// Wheat +/obj/item/seeds/wheat + name = "pack of wheat seeds" + desc = "These may, or may not, grow into wheat." + icon_state = "seed-wheat" + species = "wheat" + plantname = "Wheat Stalks" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/wheat + production = 1 + yield = 4 + potency = 15 + icon_dead = "wheat-dead" + mutatelist = list(/obj/item/seeds/wheat/oat, /obj/item/seeds/wheat/meat) + reagents_add = list("nutriment" = 0.04) + +/obj/item/weapon/reagent_containers/food/snacks/grown/wheat + seed = /obj/item/seeds/wheat + name = "wheat" + desc = "Sigh... wheat... a-grain?" + gender = PLURAL + icon_state = "wheat" + filling_color = "#F0E68C" + bitesize_mod = 2 + +// Oat +/obj/item/seeds/wheat/oat + name = "pack of oat seeds" + desc = "These may, or may not, grow into oat." + icon_state = "seed-oat" + species = "oat" + plantname = "Oat Stalks" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/oat + mutatelist = list() + +/obj/item/weapon/reagent_containers/food/snacks/grown/oat + seed = /obj/item/seeds/wheat/oat + name = "oat" + desc = "Eat oats, do squats." + gender = PLURAL + icon_state = "oat" + filling_color = "#556B2F" + bitesize_mod = 2 + +// Rice +/obj/item/seeds/wheat/rice + name = "pack of rice seeds" + desc = "These may, or may not, grow into rice." + icon_state = "seed-rice" + species = "rice" + plantname = "Rice Stalks" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/rice + mutatelist = list() + growthstages = 3 + +/obj/item/weapon/reagent_containers/food/snacks/grown/rice + seed = /obj/item/seeds/wheat/rice + name = "rice" + desc = "Rice to meet you." + gender = PLURAL + icon_state = "rice" + filling_color = "#FAFAD2" + bitesize_mod = 2 + +//Meatwheat - grows into synthetic meat +/obj/item/seeds/wheat/meat + name = "pack of meatwheat seeds" + desc = "If you ever wanted to drive a vegetarian to insanity, here's how." + icon_state = "seed-meatwheat" + species = "meatwheat" + plantname = "Meatwheat" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/meatwheat + mutatelist = list() + +/obj/item/weapon/reagent_containers/food/snacks/grown/meatwheat + name = "meatwheat" + desc = "Some blood-drenched wheat stalks. You can crush them into what passes for meat if you squint hard enough." + icon_state = "meatwheat" + gender = PLURAL + filling_color = rgb(150, 0, 0) + bitesize_mod = 2 + seed = /obj/item/seeds/wheat/meat + +/obj/item/weapon/reagent_containers/food/snacks/grown/meatwheat/attack_self(mob/living/user) + user.visible_message("[user] crushes [src] into meat.", "You crush [src] into something that resembles meat.") + playsound(user, 'sound/effects/blobattack.ogg', 50, 1) + var/obj/item/weapon/reagent_containers/food/snacks/meat/meatwheat/M = new(get_turf(user)) + user.drop_item() + qdel(src) + user.put_in_hands(M) + return 1 diff --git a/code/modules/hydroponics/grown/chili.dm b/code/modules/hydroponics/grown/chili.dm new file mode 100644 index 00000000000..4d07f17bb4c --- /dev/null +++ b/code/modules/hydroponics/grown/chili.dm @@ -0,0 +1,76 @@ +// Chili +/obj/item/seeds/chili + name = "pack of chili seeds" + desc = "These seeds grow into chili plants. HOT! HOT! HOT!" + icon_state = "seed-chili" + species = "chili" + plantname = "Chili Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/chili + lifespan = 20 + maturation = 5 + production = 5 + yield = 4 + potency = 20 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + icon_grow = "chili-grow" // Uses one growth icons set for all the subtypes + icon_dead = "chili-dead" // Same for the dead icon + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/chili/ice, /obj/item/seeds/chili/ghost) + reagents_add = list("capsaicin" = 0.25, "vitamin" = 0.04, "nutriment" = 0.04) + +/obj/item/weapon/reagent_containers/food/snacks/grown/chili + seed = /obj/item/seeds/chili + name = "chili" + desc = "It's spicy! Wait... IT'S BURNING ME!!" + icon_state = "chilipepper" + filling_color = "#FF0000" + bitesize_mod = 2 + +// Ice Chili +/obj/item/seeds/chili/ice + name = "pack of ice pepper seeds" + desc = "These seeds grow into ice pepper plants." + icon_state = "seed-icepepper" + species = "chiliice" + plantname = "Ice Pepper Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/icepepper + lifespan = 25 + maturation = 4 + production = 4 + rarity = 20 + mutatelist = list() + reagents_add = list("frostoil" = 0.25, "vitamin" = 0.02, "nutriment" = 0.02) + +/obj/item/weapon/reagent_containers/food/snacks/grown/icepepper + seed = /obj/item/seeds/chili/ice + name = "ice pepper" + desc = "It's a mutant strain of chili" + icon_state = "icepepper" + filling_color = "#0000CD" + bitesize_mod = 2 + origin_tech = "biotech=4" + +// Ghost Chili +/obj/item/seeds/chili/ghost + name = "pack of ghost chili seeds" + desc = "These seeds grow into a chili said to be the hottest in the galaxy." + icon_state = "seed-chilighost" + species = "chilighost" + plantname = "Ghost Chili Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/ghost_chili + endurance = 10 + maturation = 10 + production = 10 + yield = 3 + rarity = 20 + mutatelist = list() + reagents_add = list("condensedcapsaicin" = 0.3, "capsaicin" = 0.55, "nutriment" = 0.04) + +/obj/item/weapon/reagent_containers/food/snacks/grown/ghost_chili + seed = /obj/item/seeds/chili/ghost + name = "ghost chili" + desc = "It seems to be vibrating gently." + icon_state = "ghostchilipepper" + filling_color = "#F8F8FF" + bitesize_mod = 4 + origin_tech = "biotech=4;magnets=5" \ No newline at end of file diff --git a/code/modules/hydroponics/grown/citrus.dm b/code/modules/hydroponics/grown/citrus.dm new file mode 100644 index 00000000000..7562e013b50 --- /dev/null +++ b/code/modules/hydroponics/grown/citrus.dm @@ -0,0 +1,155 @@ +// Citrus - base type +/obj/item/weapon/reagent_containers/food/snacks/grown/citrus + seed = /obj/item/seeds/lime + name = "citrus" + desc = "It's so sour, your face will twist." + icon_state = "lime" + bitesize_mod = 2 + +// Lime +/obj/item/seeds/lime + name = "pack of lime seeds" + desc = "These are very sour seeds." + icon_state = "seed-lime" + species = "lime" + plantname = "Lime Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime + lifespan = 55 + endurance = 50 + yield = 4 + potency = 15 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/orange) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime + seed = /obj/item/seeds/lime + name = "lime" + desc = "It's so sour, your face will twist." + icon_state = "lime" + filling_color = "#00FF00" + +// Orange +/obj/item/seeds/orange + name = "pack of orange seeds" + desc = "Sour seeds." + icon_state = "seed-orange" + species = "orange" + plantname = "Orange Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange + lifespan = 60 + endurance = 50 + yield = 5 + potency = 20 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "lime-grow" + icon_dead = "lime-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/lime) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange + seed = /obj/item/seeds/orange + name = "orange" + desc = "It's an tangy fruit." + icon_state = "orange" + filling_color = "#FFA500" + +// Lemon +/obj/item/seeds/lemon + name = "pack of lemon seeds" + desc = "These are sour seeds." + icon_state = "seed-lemon" + species = "lemon" + plantname = "Lemon Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon + lifespan = 55 + endurance = 45 + yield = 4 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "lime-grow" + icon_dead = "lime-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/firelemon) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon + seed = /obj/item/seeds/lemon + name = "lemon" + desc = "When life gives you lemons, make lemonade." + icon_state = "lemon" + filling_color = "#FFD700" + +// Combustible lemon +/obj/item/seeds/firelemon //combustible lemon is too long so firelemon + name = "pack of combustible lemon seeds" + desc = "When life gives you lemons, don't make lemonade. Make life take the lemons back! Get mad! I don't want your damn lemons!" + icon_state = "seed-firelemon" + species = "firelemon" + plantname = "Combustible Lemon Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/firelemon + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "lime-grow" + icon_dead = "lime-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + lifespan = 55 + endurance = 45 + yield = 4 + reagents_add = list("nutriment" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/firelemon + seed = /obj/item/seeds/firelemon + name = "Combustible Lemon" + desc = "Made for burning houses down." + icon_state = "firelemon" + bitesize_mod = 2 + +/obj/item/weapon/reagent_containers/food/snacks/grown/firelemon/attack_self(mob/living/user) + var/area/A = get_area(user) + user.visible_message("[user] primes the [src]!", "You prime the [src]!") + var/message = "[ADMIN_LOOKUPFLW(user)] primed a combustible lemon for detonation at [A] [ADMIN_COORDJMP(user)]" + bombers += message + message_admins(message) + log_game("[key_name(user)] primed a combustible lemon for detonation at [A] [COORD(user)].") + if(iscarbon(user)) + var/mob/living/carbon/C = user + C.throw_mode_on() + icon_state = "firelemon_active" + playsound(loc, 'sound/weapons/armbomb.ogg', 75, 1, -3) + addtimer(src, "prime", rand(10, 60)) + +/obj/item/weapon/reagent_containers/food/snacks/grown/firelemon/burn() + prime() + ..() + +/obj/item/weapon/reagent_containers/food/snacks/grown/firelemon/proc/update_mob() + if(ismob(loc)) + var/mob/M = loc + M.unEquip(src) + +/obj/item/weapon/reagent_containers/food/snacks/grown/firelemon/ex_act(severity) + qdel(src) //Ensuring that it's deleted by its own explosion + +/obj/item/weapon/reagent_containers/food/snacks/grown/firelemon/proc/prime() + switch(seed.potency) //Combustible lemons are alot like IEDs, lots of flame, very little bang. + if(0 to 30) + update_mob() + explosion(loc,-1,-1,2, flame_range = 1) + qdel(src) + if(31 to 50) + update_mob() + explosion(loc,-1,-1,2, flame_range = 2) + qdel(src) + if(51 to 70) + update_mob() + explosion(loc,-1,-1,2, flame_range = 3) + qdel(src) + if(71 to 90) + update_mob() + explosion(loc,-1,-1,2, flame_range = 4) + qdel(src) + else + update_mob() + explosion(loc,-1,-1,2, flame_range = 5) + qdel(src) diff --git a/code/modules/hydroponics/grown/cocoa_vanilla.dm b/code/modules/hydroponics/grown/cocoa_vanilla.dm new file mode 100644 index 00000000000..6e04bdaf97d --- /dev/null +++ b/code/modules/hydroponics/grown/cocoa_vanilla.dm @@ -0,0 +1,46 @@ +// Cocoa Pod +/obj/item/seeds/cocoapod + name = "pack of cocoa pod seeds" + desc = "These seeds grow into cacao trees. They look fattening." //SIC: cocoa is the seeds. The trees are spelled cacao. + icon_state = "seed-cocoapod" + species = "cocoapod" + plantname = "Cocao Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cocoapod + lifespan = 20 + maturation = 5 + production = 5 + yield = 2 + growthstages = 5 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "cocoapod-grow" + icon_dead = "cocoapod-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/cocoapod/vanillapod) + reagents_add = list("cocoa" = 0.25, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/cocoapod + seed = /obj/item/seeds/cocoapod + name = "cocoa pod" + desc = "Fattening... Mmmmm... chucklate." + icon_state = "cocoapod" + filling_color = "#FFD700" + bitesize_mod = 2 + +// Vanilla Pod +/obj/item/seeds/cocoapod/vanillapod + name = "pack of vanilla pod seeds" + desc = "These seeds grow into vanilla trees. They look fattening." + icon_state = "seed-vanillapod" + species = "vanillapod" + plantname = "Vanilla Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/vanillapod + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list() + reagents_add = list("vanilla" = 0.25, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/vanillapod + seed = /obj/item/seeds/cocoapod/vanillapod + name = "vanilla pod" + desc = "Fattening... Mmmmm... vanilla." + icon_state = "vanillapod" + filling_color = "#FFD700" \ No newline at end of file diff --git a/code/modules/hydroponics/grown/corn.dm b/code/modules/hydroponics/grown/corn.dm new file mode 100644 index 00000000000..835b2574731 --- /dev/null +++ b/code/modules/hydroponics/grown/corn.dm @@ -0,0 +1,84 @@ +// Corn +/obj/item/seeds/corn + name = "pack of corn seeds" + desc = "I don't mean to sound corny..." + icon_state = "seed-corn" + species = "corn" + plantname = "Corn Stalks" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/corn + maturation = 8 + potency = 20 + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + icon_grow = "corn-grow" // Uses one growth icons set for all the subtypes + icon_dead = "corn-dead" // Same for the dead icon + mutatelist = list(/obj/item/seeds/corn/snapcorn) + reagents_add = list("cornoil" = 0.2, "vitamin" = 0.04, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/corn + seed = /obj/item/seeds/corn + name = "ear of corn" + desc = "Needs some butter!" + icon_state = "corn" + cooked_type = /obj/item/weapon/reagent_containers/food/snacks/popcorn + filling_color = "#FFFF00" + trash = /obj/item/weapon/grown/corncob + bitesize_mod = 2 + +/obj/item/weapon/grown/corncob + name = "corn cob" + desc = "A reminder of meals gone by." + icon_state = "corncob" + item_state = "corncob" + w_class = 1 + throwforce = 0 + throw_speed = 3 + throw_range = 7 + +/obj/item/weapon/grown/corncob/attackby(obj/item/weapon/grown/W, mob/user, params) + if(is_sharp(W)) + to_chat(user, "You use [W] to fashion a pipe out of the corn cob!") + new /obj/item/clothing/mask/cigarette/pipe/cobpipe (user.loc) + user.unEquip(src) + qdel(src) + return + else + return ..() + +// Snapcorn +/obj/item/seeds/corn/snapcorn + name = "pack of snapcorn seeds" + desc = "Oh snap!" + icon_state = "seed-snapcorn" + species = "snapcorn" + plantname = "Snapcorn Stalks" + product = /obj/item/weapon/grown/snapcorn + mutatelist = list() + rarity = 10 + +/obj/item/weapon/grown/snapcorn + seed = /obj/item/seeds/corn/snapcorn + name = "snap corn" + desc = "A cob with snap pops" + icon_state = "snapcorn" + item_state = "corncob" + w_class = 1 + throwforce = 0 + throw_speed = 3 + throw_range = 7 + var/snap_pops = 1 + +/obj/item/weapon/grown/snapcorn/add_juice() + ..() + snap_pops = max(round(seed.potency/8), 1) + +/obj/item/weapon/grown/snapcorn/attack_self(mob/user) + ..() + to_chat(user, "You pick a snap pop from the cob.") + var/obj/item/toy/snappop/S = new /obj/item/toy/snappop(user.loc) + if(ishuman(user)) + user.put_in_hands(S) + snap_pops -= 1 + if(!snap_pops) + new /obj/item/weapon/grown/corncob(user.loc) + qdel(src) \ No newline at end of file diff --git a/code/modules/hydroponics/grown/eggplant.dm b/code/modules/hydroponics/grown/eggplant.dm new file mode 100644 index 00000000000..8ec46ff77de --- /dev/null +++ b/code/modules/hydroponics/grown/eggplant.dm @@ -0,0 +1,44 @@ +// Eggplant +/obj/item/seeds/eggplant + name = "pack of eggplant seeds" + desc = "These seeds grow to produce berries that look nothing like eggs." + icon_state = "seed-eggplant" + species = "eggplant" + plantname = "Eggplants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/eggplant + yield = 2 + potency = 20 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + icon_grow = "eggplant-grow" + icon_dead = "eggplant-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/eggplant/eggy) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/eggplant + seed = /obj/item/seeds/eggplant + name = "eggplant" + desc = "Maybe there's a chicken inside?" + icon_state = "eggplant" + filling_color = "#800080" + bitesize_mod = 2 + +// Egg-Plant +/obj/item/seeds/eggplant/eggy + desc = "These seeds grow to produce berries that look a lot like eggs." + icon_state = "seed-eggy" + species = "eggy" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/shell/eggy + lifespan = 75 + production = 12 + mutatelist = list() + reagents_add = list("nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/shell/eggy + seed = /obj/item/seeds/eggplant/eggy + name = "Egg-plant" + desc = "There MUST be a chicken inside." + icon_state = "eggyplant" + trash = /obj/item/weapon/reagent_containers/food/snacks/egg + filling_color = "#F8F8FF" + bitesize_mod = 2 \ No newline at end of file diff --git a/code/modules/hydroponics/grown/flowers.dm b/code/modules/hydroponics/grown/flowers.dm new file mode 100644 index 00000000000..74fc57be99c --- /dev/null +++ b/code/modules/hydroponics/grown/flowers.dm @@ -0,0 +1,202 @@ +// Poppy +/obj/item/seeds/poppy + name = "pack of poppy seeds" + desc = "These seeds grow into poppies." + icon_state = "seed-poppy" + species = "poppy" + plantname = "Poppy Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/poppy + endurance = 10 + maturation = 8 + yield = 6 + potency = 20 + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' + icon_grow = "poppy-grow" + icon_dead = "poppy-dead" + mutatelist = list(/obj/item/seeds/poppy/geranium, /obj/item/seeds/poppy/lily) + reagents_add = list("bicaridine" = 0.2, "nutriment" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/poppy + seed = /obj/item/seeds/poppy + name = "poppy" + desc = "Long-used as a symbol of rest, peace, and death." + icon_state = "poppy" + slot_flags = SLOT_HEAD + filling_color = "#FF6347" + bitesize_mod = 3 + +// Lily +/obj/item/seeds/poppy/lily + name = "pack of lily seeds" + desc = "These seeds grow into lilies." + icon_state = "seed-lily" + species = "lily" + plantname = "Lily Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/poppy/lily + mutatelist = list() + +/obj/item/weapon/reagent_containers/food/snacks/grown/poppy/lily + seed = /obj/item/seeds/poppy/lily + name = "lily" + desc = "A beautiful orange flower" + icon_state = "lily" + filling_color = "#FFA500" + +// Geranium +/obj/item/seeds/poppy/geranium + name = "pack of geranium seeds" + desc = "These seeds grow into geranium." + icon_state = "seed-geranium" + species = "geranium" + plantname = "Geranium Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/poppy/geranium + mutatelist = list() + +/obj/item/weapon/reagent_containers/food/snacks/grown/poppy/geranium + seed = /obj/item/seeds/poppy/geranium + name = "geranium" + desc = "A beautiful blue flower" + icon_state = "geranium" + filling_color = "#008B8B" + + +// Harebell +/obj/item/seeds/harebell + name = "pack of harebell seeds" + desc = "These seeds grow into pretty little flowers." + icon_state = "seed-harebell" + species = "harebell" + plantname = "Harebells" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/harebell + lifespan = 100 + endurance = 20 + maturation = 7 + production = 1 + yield = 2 + potency = 30 + growthstages = 4 + genes = list(/datum/plant_gene/trait/plant_type/weed_hardy) + growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' + reagents_add = list("nutriment" = 0.04) + +/obj/item/weapon/reagent_containers/food/snacks/grown/harebell + seed = /obj/item/seeds/harebell + name = "harebell" + desc = "\"I'll sweeten thy sad grave: thou shalt not lack the flower that's like thy face, pale primrose, nor the azured hare-bell, like thy veins; no, nor the leaf of eglantine, whom not to slander, out-sweeten'd not thy breath.\"" + icon_state = "harebell" + slot_flags = SLOT_HEAD + filling_color = "#E6E6FA" + bitesize_mod = 3 + + +// Sunflower +/obj/item/seeds/sunflower + name = "pack of sunflower seeds" + desc = "These seeds grow into sunflowers." + icon_state = "seed-sunflower" + species = "sunflower" + plantname = "Sunflowers" + product = /obj/item/weapon/grown/sunflower + endurance = 20 + production = 2 + yield = 2 + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_flowers.dmi' + icon_grow = "sunflower-grow" + icon_dead = "sunflower-dead" + mutatelist = list(/obj/item/seeds/sunflower/moonflower, /obj/item/seeds/sunflower/novaflower) + reagents_add = list("cornoil" = 0.08, "nutriment" = 0.04) + +/obj/item/weapon/grown/sunflower // FLOWER POWER! + seed = /obj/item/seeds/sunflower + name = "sunflower" + desc = "It's beautiful! A certain person might beat you to death if you trample these." + icon_state = "sunflower" + damtype = "fire" + force = 0 + slot_flags = SLOT_HEAD + throwforce = 0 + w_class = 1 + throw_speed = 1 + throw_range = 3 + +/obj/item/weapon/grown/sunflower/attack(mob/M, mob/user) + to_chat(M, " [user] smacks you with a sunflower!FLOWER POWER") + to_chat(user, "Your sunflower's FLOWER POWERstrikes [M]") + +// Moonflower +/obj/item/seeds/sunflower/moonflower + name = "pack of moonflower seeds" + desc = "These seeds grow into moonflowers." + icon_state = "seed-moonflower" + species = "moonflower" + plantname = "Moonflowers" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/moonflower + mutatelist = list() + reagents_add = list("moonshine" = 0.2, "vitamin" = 0.02, "nutriment" = 0.02) + rarity = 15 + +/obj/item/weapon/reagent_containers/food/snacks/grown/moonflower + seed = /obj/item/seeds/sunflower/moonflower + name = "moonflower" + desc = "Store in a location at least 50 yards away from werewolves." + icon_state = "moonflower" + slot_flags = SLOT_HEAD + filling_color = "#E6E6FA" + bitesize_mod = 2 + +// Novaflower +/obj/item/seeds/sunflower/novaflower + name = "pack of novaflower seeds" + desc = "These seeds grow into novaflowers." + icon_state = "seed-novaflower" + species = "novaflower" + plantname = "Novaflowers" + product = /obj/item/weapon/grown/novaflower + mutatelist = list() + reagents_add = list("condensedcapsaicin" = 0.25, "capsaicin" = 0.3, "nutriment" = 0) + rarity = 20 + +/obj/item/weapon/grown/novaflower + seed = /obj/item/seeds/sunflower/novaflower + name = "novaflower" + desc = "These beautiful flowers have a crisp smokey scent, like a summer bonfire." + icon_state = "novaflower" + damtype = "fire" + force = 0 + slot_flags = SLOT_HEAD + throwforce = 0 + w_class = 1 + throw_speed = 1 + throw_range = 3 + attack_verb = list("roasted", "scorched", "burned") + +/obj/item/weapon/grown/novaflower/add_juice() + ..() + force = round((5 + seed.potency / 5), 1) + +/obj/item/weapon/grown/novaflower/attack(mob/living/carbon/M, mob/user) + ..() + if(isliving(M)) + to_chat(M, "You are lit on fire from the intense heat of the [name]!") + M.adjust_fire_stacks(seed.potency / 20) + if(M.IgniteMob()) + message_admins("[key_name_admin(user)] set [key_name_admin(M)] on fire") + log_game("[key_name(user)] set [key_name(M)] on fire") + +/obj/item/weapon/grown/novaflower/afterattack(atom/A as mob|obj, mob/user,proximity) + if(!proximity) + return + if(force > 0) + force -= rand(1, (force / 3) + 1) + else + to_chat(usr, "All the petals have fallen off the [name] from violent whacking!") + usr.unEquip(src) + qdel(src) + +/obj/item/weapon/grown/novaflower/pickup(mob/living/carbon/human/user) + ..() + if(!user.gloves) + to_chat(user, "The [name] burns your bare hand!") + user.adjustFireLoss(rand(1, 5)) diff --git a/code/modules/hydroponics/grown/grass_carpet.dm b/code/modules/hydroponics/grown/grass_carpet.dm new file mode 100644 index 00000000000..f113074911c --- /dev/null +++ b/code/modules/hydroponics/grown/grass_carpet.dm @@ -0,0 +1,67 @@ +// Grass +/obj/item/seeds/grass + name = "pack of grass seeds" + desc = "These seeds grow into grass. Yummy!" + icon_state = "seed-grass" + species = "grass" + plantname = "Grass" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/grass + lifespan = 40 + endurance = 40 + maturation = 2 + production = 5 + yield = 5 + growthstages = 2 + icon_grow = "grass-grow" + icon_dead = "grass-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/grass/carpet) + reagents_add = list("nutriment" = 0.02, "hydrogen" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/grass + seed = /obj/item/seeds/grass + name = "grass" + desc = "Green and lush." + icon_state = "grassclump" + filling_color = "#32CD32" + bitesize_mod = 2 + var/stacktype = /obj/item/stack/tile/grass + var/tile_coefficient = 0.02 // 1/50 + +/obj/item/weapon/reagent_containers/food/snacks/grown/grass/attack_self(mob/user) + to_chat(user, "You prepare the astroturf.") + var/grassAmt = 1 + round(seed.potency * tile_coefficient) // The grass we're holding + for(var/obj/item/weapon/reagent_containers/food/snacks/grown/grass/G in user.loc) // The grass on the floor + if(G.type != type) + continue + grassAmt += 1 + round(G.seed.potency * tile_coefficient) + qdel(G) + var/obj/item/stack/tile/GT = new stacktype(user.loc) + while(grassAmt > GT.max_amount) + GT.amount = GT.max_amount + grassAmt -= GT.max_amount + GT = new stacktype(user.loc) + GT.amount = grassAmt + for(var/obj/item/stack/tile/T in user.loc) + if((T.type == stacktype) && (T.amount < T.max_amount) && (T != GT)) + T.attackby(GT, user) + qdel(src) + return + +// Carpet +/obj/item/seeds/grass/carpet + name = "pack of carpet seeds" + desc = "These seeds grow into stylish carpet samples." + icon_state = "seed-carpet" + species = "carpet" + plantname = "Carpet" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/grass/carpet + mutatelist = list() + rarity = 10 + +/obj/item/weapon/reagent_containers/food/snacks/grown/grass/carpet + seed = /obj/item/seeds/grass/carpet + name = "carpet" + desc = "The textile industry's dark secret." + icon_state = "carpetclump" + stacktype = /obj/item/stack/tile/carpet diff --git a/code/modules/hydroponics/grown/herbals.dm b/code/modules/hydroponics/grown/herbals.dm new file mode 100644 index 00000000000..05efe26bd99 --- /dev/null +++ b/code/modules/hydroponics/grown/herbals.dm @@ -0,0 +1,53 @@ +/obj/item/seeds/comfrey + name = "pack of comfrey seeds" + desc = "These seeds grow into comfrey." + icon_state = "seed-cabbage" + species = "cabbage" + plantname = "comfrey" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/comfrey + yield = 2 + maturation = 3 + growthstages = 1 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + reagents_add = list("nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/comfrey + seed = /obj/item/seeds/comfrey + name = "comfrey leaf" + desc = "Mash to turn into a poultice." + icon_state = "tea_astra_leaves" + color = "#378C61" + bitesize_mod = 2 + +/obj/item/weapon/reagent_containers/food/snacks/grown/comfrey/attack_self(mob/user) + var/obj/item/stack/medical/bruise_pack/comfrey/C = new(get_turf(user)) + C.heal_brute = seed.potency + to_chat(user, "You mash [src] into a poultice.") + user.drop_item() + qdel(src) + +/obj/item/seeds/aloe + name = "pack of aloe seeds" + desc = "These seeds grow into aloe vera plant." + icon_state = "seed-ambrosiavulgaris" + species = "ambrosiavulgaris" + plantname = "Aloe Vera Plant" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/aloe + yield = 2 + icon_dead = "ambrosia-dead" + reagents_add = list("nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/aloe + seed = /obj/item/seeds/aloe + name = "aloe leaf" + desc = "Mash to turn into a poultice." + icon_state = "ambrosiavulgaris" + color = "#4CC5C7" + bitesize_mod = 2 + +/obj/item/weapon/reagent_containers/food/snacks/grown/aloe/attack_self(mob/user) + var/obj/item/stack/medical/ointment/aloe/A = new(get_turf(user)) + A.heal_burn = seed.potency + to_chat(user, "You mash [src] into a poultice.") + user.drop_item() + qdel(src) \ No newline at end of file diff --git a/code/modules/hydroponics/grown/kudzu.dm b/code/modules/hydroponics/grown/kudzu.dm new file mode 100644 index 00000000000..84ea01235e9 --- /dev/null +++ b/code/modules/hydroponics/grown/kudzu.dm @@ -0,0 +1,97 @@ +// A very special plant, deserving it's own file. + +/obj/item/seeds/kudzu + name = "pack of kudzu seeds" + desc = "These seeds grow into a weed that grows incredibly fast." + icon_state = "seed-kudzu" + species = "kudzu" + plantname = "Kudzu" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/kudzupod + genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/plant_type/weed_hardy) + lifespan = 20 + endurance = 10 + yield = 4 + growthstages = 4 + rarity = 30 + var/list/mutations = list() + reagents_add = list("charcoal" = 0.04, "nutriment" = 0.02) + +/obj/item/seeds/kudzu/Copy() + var/obj/item/seeds/kudzu/S = ..() + S.mutations = mutations.Copy() + return S + +/obj/item/seeds/kudzu/suicide_act(mob/user) + user.visible_message("[user] swallows the pack of kudzu seeds! It looks like \he's trying to commit suicide..") + plant(user) + return (BRUTELOSS) + +/obj/item/seeds/kudzu/proc/plant(mob/user) + if(istype(user.loc, /turf/space)) + return + var/turf/T = get_turf(src) + message_admins("Kudzu planted by [key_name_admin(user)](?) (FLW) at ([T.x],[T.y],[T.z] - (JMP))",0,1) + investigate_log("was planted by [key_name(user)] at ([T.x],[T.y],[T.z])","kudzu") + new /obj/effect/spacevine_controller(user.loc, mutations, potency, production) + user.drop_item() + qdel(src) + +/obj/item/seeds/kudzu/attack_self(mob/user) + plant(user) + to_chat(user, "You plant the kudzu. You monster.") + +/obj/item/seeds/kudzu/get_analyzer_text() + var/text = ..() + var/text_string = "" + for(var/datum/spacevine_mutation/SM in mutations) + text_string += "[(text_string == "") ? "" : ", "][SM.name]" + text += "\n- Plant Mutations: [(text_string == "") ? "None" : text_string]" + return text + +/obj/item/seeds/kudzu/on_chem_reaction(datum/reagents/S) + var/list/temp_mut_list = list() + + if(S.has_reagent("sterilizine", 5)) + for(var/datum/spacevine_mutation/SM in mutations) + if(SM.quality == NEGATIVE) + temp_mut_list += SM + if(prob(20) && temp_mut_list.len) + mutations.Remove(pick(temp_mut_list)) + temp_mut_list.Cut() + + if(S.has_reagent("welding_fuel", 5)) + for(var/datum/spacevine_mutation/SM in mutations) + if(SM.quality == POSITIVE) + temp_mut_list += SM + if(prob(20) && temp_mut_list.len) + mutations.Remove(pick(temp_mut_list)) + temp_mut_list.Cut() + + if(S.has_reagent("phenol", 5)) + for(var/datum/spacevine_mutation/SM in mutations) + if(SM.quality == MINOR_NEGATIVE) + temp_mut_list += SM + if(prob(20) && temp_mut_list.len) + mutations.Remove(pick(temp_mut_list)) + temp_mut_list.Cut() + + if(S.has_reagent("blood", 15)) + adjust_production(rand(15, -5)) + + if(S.has_reagent("amanitin", 5)) + adjust_production(rand(5, -15)) + + if(S.has_reagent("plasma", 5)) + adjust_potency(rand(5, -15)) + + if(S.has_reagent("holywater", 10)) + adjust_potency(rand(15, -5)) + + +/obj/item/weapon/reagent_containers/food/snacks/grown/kudzupod + seed = /obj/item/seeds/kudzu + name = "kudzu pod" + desc = "Pueraria Virallis: An invasive species with vines that rapidly creep and wrap around whatever they contact." + icon_state = "kudzupod" + filling_color = "#6B8E23" + bitesize_mod = 2 \ No newline at end of file diff --git a/code/modules/hydroponics/grown/melon.dm b/code/modules/hydroponics/grown/melon.dm new file mode 100644 index 00000000000..633c39d37f6 --- /dev/null +++ b/code/modules/hydroponics/grown/melon.dm @@ -0,0 +1,47 @@ +// Watermelon +/obj/item/seeds/watermelon + name = "pack of watermelon seeds" + desc = "These seeds grow into watermelon plants." + icon_state = "seed-watermelon" + species = "watermelon" + plantname = "Watermelon Vines" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/watermelon + lifespan = 50 + endurance = 40 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_dead = "watermelon-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/watermelon/holy) + reagents_add = list("water" = 0.2, "vitamin" = 0.04, "nutriment" = 0.2) + +/obj/item/weapon/reagent_containers/food/snacks/grown/watermelon + seed = /obj/item/seeds/watermelon + name = "watermelon" + desc = "It's full of watery goodness." + icon_state = "watermelon" + slice_path = /obj/item/weapon/reagent_containers/food/snacks/watermelonslice + slices_num = 5 + dried_type = null + w_class = 3 + filling_color = "#008000" + bitesize_mod = 3 + +// Holymelon +/obj/item/seeds/watermelon/holy + name = "pack of holymelon seeds" + desc = "These seeds grow into holymelon plants." + icon_state = "seed-holymelon" + species = "holymelon" + plantname = "Holy Melon Vines" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/holymelon + mutatelist = list() + reagents_add = list("holywater" = 0.2, "vitamin" = 0.04, "nutriment" = 0.1) + rarity = 20 + +/obj/item/weapon/reagent_containers/food/snacks/grown/holymelon + seed = /obj/item/seeds/watermelon/holy + name = "holymelon" + desc = "The water within this melon has been blessed by some deity that's particularly fond of watermelon." + icon_state = "holymelon" + filling_color = "#FFD700" + dried_type = null diff --git a/code/modules/hydroponics/grown/misc.dm b/code/modules/hydroponics/grown/misc.dm new file mode 100644 index 00000000000..0d0d786a077 --- /dev/null +++ b/code/modules/hydroponics/grown/misc.dm @@ -0,0 +1,139 @@ +// Weeds +/obj/item/seeds/weeds + name = "pack of weed seeds" + desc = "Yo mang, want some weeds?" + icon_state = "seed" + species = "weeds" + plantname = "Starthistle" + lifespan = 100 + endurance = 50 // damm pesky weeds + maturation = 5 + production = 1 + yield = -1 + potency = -1 + growthstages = 4 + genes = list(/datum/plant_gene/trait/plant_type/weed_hardy) + + +// Cabbage +/obj/item/seeds/cabbage + name = "pack of cabbage seeds" + desc = "These seeds grow into cabbages." + icon_state = "seed-cabbage" + species = "cabbage" + plantname = "Cabbages" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cabbage + lifespan = 50 + endurance = 25 + maturation = 3 + production = 5 + yield = 4 + growthstages = 1 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/replicapod) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/cabbage + seed = /obj/item/seeds/cabbage + name = "cabbage" + desc = "Ewwwwwwwwww. Cabbage." + icon_state = "cabbage" + filling_color = "#90EE90" + bitesize_mod = 2 + + +// Sugarcane +/obj/item/seeds/sugarcane + name = "pack of sugarcane seeds" + desc = "These seeds grow into sugarcane." + icon_state = "seed-sugarcane" + species = "sugarcane" + plantname = "Sugarcane" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/sugarcane + genes = list(/datum/plant_gene/trait/repeated_harvest) + lifespan = 60 + endurance = 50 + maturation = 3 + yield = 4 + growthstages = 3 + reagents_add = list("sugar" = 0.25) + +/obj/item/weapon/reagent_containers/food/snacks/grown/sugarcane + seed = /obj/item/seeds/sugarcane + name = "sugarcane" + desc = "Sickly sweet." + icon_state = "sugarcane" + filling_color = "#FFD700" + bitesize_mod = 2 + + +// Gatfruit +/obj/item/seeds/gatfruit + name = "pack of gatfruit seeds" + desc = "These seeds grow into .357 revolvers." + icon_state = "seed-gatfruit" + species = "gatfruit" + plantname = "Gatfruit Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/shell/gatfruit + genes = list(/datum/plant_gene/trait/repeated_harvest) + lifespan = 20 + endurance = 20 + maturation = 40 + production = 10 + yield = 2 + potency = 60 + growthstages = 2 + rarity = 60 // Obtainable only with xenobio+superluck. + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + reagents_add = list("sulfur" = 0.1, "carbon" = 0.1, "nitrogen" = 0.07, "potassium" = 0.05) + +/obj/item/weapon/reagent_containers/food/snacks/grown/shell/gatfruit + seed = /obj/item/seeds/gatfruit + name = "gatfruit" + desc = "It smells like burning." + icon_state = "gatfruit" + origin_tech = "combat=6" + trash = /obj/item/weapon/gun/projectile/revolver + bitesize_mod = 2 + +//Cherry Bombs +/obj/item/seeds/cherry/bomb + name = "pack of cherry bomb pits" + desc = "They give you vibes of dread and frustration." + icon_state = "seed-cherry_bomb" + species = "cherry_bomb" + plantname = "Cherry Bomb Tree" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/cherry_bomb + mutatelist = list() + reagents_add = list("nutriment" = 0.1, "sugar" = 0.1, "blackpowder" = 0.7) + rarity = 60 //See above + +/obj/item/weapon/reagent_containers/food/snacks/grown/cherry_bomb + name = "cherry bombs" + desc = "You think you can hear the hissing of a tiny fuse." + icon_state = "cherry_bomb" + filling_color = rgb(20, 20, 20) + seed = /obj/item/seeds/cherry/bomb + bitesize_mod = 2 + volume = 125 //Gives enough room for the black powder at max potency + +/obj/item/weapon/reagent_containers/food/snacks/grown/cherry_bomb/attack_self(mob/living/user) + var/area/A = get_area(user) + user.visible_message("[user] plucks the stem from [src]!", "You pluck the stem from [src], which begins to hiss loudly!") + message_admins("[user] ([user.key ? user.key : "no key"]) primed a cherry bomb for detonation at [A] ([user.x], [user.y], [user.z]) (JMP)") + log_game("[user] ([user.key ? user.key : "no key"]) primed a cherry bomb for detonation at [A] ([user.x],[user.y],[user.z]).") + prime() + +/obj/item/weapon/reagent_containers/food/snacks/grown/cherry_bomb/burn() + prime() + ..() + +/obj/item/weapon/reagent_containers/food/snacks/grown/cherry_bomb/ex_act(severity) + qdel(src) //Ensuring that it's deleted by its own explosion. Also prevents mass chain reaction with piles of cherry bombs + +/obj/item/weapon/reagent_containers/food/snacks/grown/cherry_bomb/proc/prime() + icon_state = "cherry_bomb_lit" + playsound(src, 'sound/goonstation/misc/fuse.ogg', seed.potency, 0) + reagents.chem_temp = 1000 //Sets off the black powder + reagents.handle_reactions() diff --git a/code/modules/hydroponics/grown/mushrooms.dm b/code/modules/hydroponics/grown/mushrooms.dm new file mode 100644 index 00000000000..0588bdef455 --- /dev/null +++ b/code/modules/hydroponics/grown/mushrooms.dm @@ -0,0 +1,294 @@ +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom + name = "mushroom" + bitesize_mod = 2 + + +// Reishi +/obj/item/seeds/reishi + name = "pack of reishi mycelium" + desc = "This mycelium grows into something medicinal and relaxing." + icon_state = "mycelium-reishi" + species = "reishi" + plantname = "Reishi" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/reishi + lifespan = 35 + endurance = 35 + maturation = 10 + production = 5 + yield = 4 + potency = 15 + growthstages = 4 + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + reagents_add = list("morphine" = 0.35, "charcoal" = 0.35, "nutriment" = 0) + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/reishi + seed = /obj/item/seeds/reishi + name = "reishi" + desc = "Ganoderma lucidum: A special fungus known for its medicinal and stress relieving properties." + icon_state = "reishi" + filling_color = "#FF4500" + + +// Fly Amanita +/obj/item/seeds/amanita + name = "pack of fly amanita mycelium" + desc = "This mycelium grows into something horrible." + icon_state = "mycelium-amanita" + species = "amanita" + plantname = "Fly Amanitas" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/amanita + lifespan = 50 + endurance = 35 + maturation = 10 + production = 5 + yield = 4 + growthstages = 3 + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + mutatelist = list(/obj/item/seeds/angel) + reagents_add = list("psilocybin" = 0.04, "amanitin" = 0.35, "nutriment" = 0, "growthserum" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/amanita + seed = /obj/item/seeds/amanita + name = "fly amanita" + desc = "Amanita Muscaria: Learn poisonous mushrooms by heart. Only pick mushrooms you know." + icon_state = "amanita" + filling_color = "#FF0000" + + +// Destroying Angel +/obj/item/seeds/angel + name = "pack of destroying angel mycelium" + desc = "This mycelium grows into something devastating." + icon_state = "mycelium-angel" + species = "angel" + plantname = "Destroying Angels" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/angel + lifespan = 50 + endurance = 35 + maturation = 12 + production = 5 + yield = 2 + potency = 35 + growthstages = 3 + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + reagents_add = list("psilocybin" = 0.04, "amanitin" = 0.1, "nutriment" = 0, "amanitin" = 0.2) + rarity = 30 + origin_tech = "biotech=5" + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/angel + seed = /obj/item/seeds/angel + name = "destroying angel" + desc = "Amanita Virosa: Deadly poisonous basidiomycete fungus filled with alpha amatoxins." + icon_state = "angel" + filling_color = "#C0C0C0" + + +// Liberty Cap +/obj/item/seeds/liberty + name = "pack of liberty-cap mycelium" + desc = "This mycelium grows into liberty-cap mushrooms." + icon_state = "mycelium-liberty" + species = "liberty" + plantname = "Liberty-Caps" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/libertycap + maturation = 7 + production = 1 + yield = 5 + potency = 15 + growthstages = 3 + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + reagents_add = list("psilocybin" = 0.25, "nutriment" = 0.02) + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/libertycap + seed = /obj/item/seeds/liberty + name = "liberty-cap" + desc = "Psilocybe Semilanceata: Liberate yourself!" + icon_state = "libertycap" + filling_color = "#DAA520" + + +// Plump Helmet +/obj/item/seeds/plump + name = "pack of plump-helmet mycelium" + desc = "This mycelium grows into helmets... maybe." + icon_state = "mycelium-plump" + species = "plump" + plantname = "Plump-Helmet Mushrooms" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/plumphelmet + maturation = 8 + production = 1 + yield = 4 + potency = 15 + growthstages = 3 + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + mutatelist = list(/obj/item/seeds/plump/walkingmushroom) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/plumphelmet + seed = /obj/item/seeds/plump + name = "plump-helmet" + desc = "Plumus Hellmus: Plump, soft and s-so inviting~" + icon_state = "plumphelmet" + filling_color = "#9370DB" + + +// Walking Mushroom +/obj/item/seeds/plump/walkingmushroom + name = "pack of walking mushroom mycelium" + desc = "This mycelium will grow into huge stuff!" + icon_state = "mycelium-walkingmushroom" + species = "walkingmushroom" + plantname = "Walking Mushrooms" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/walkingmushroom + lifespan = 30 + endurance = 30 + maturation = 5 + yield = 1 + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + mutatelist = list() + reagents_add = list("vitamin" = 0.05, "nutriment" = 0.15) + rarity = 30 + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/walkingmushroom + seed = /obj/item/seeds/plump/walkingmushroom + name = "walking mushroom" + desc = "Plumus Locomotus: The beginning of the great walk." + icon_state = "walkingmushroom" + filling_color = "#9370DB" + origin_tech = "biotech=4;programming=5" + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/walkingmushroom/attack_self(mob/user) + if(istype(user.loc, /turf/space)) + return + var/mob/living/simple_animal/hostile/mushroom/M = new /mob/living/simple_animal/hostile/mushroom(user.loc) + M.maxHealth += round(seed.endurance / 4) + M.melee_damage_lower += round(seed.potency / 20) + M.melee_damage_upper += round(seed.potency / 20) + M.move_to_delay -= round(seed.production / 50) + M.health = M.maxHealth + qdel(src) + to_chat(user, "You plant the walking mushroom.") + + +// Chanterelle +/obj/item/seeds/chanter + name = "pack of chanterelle mycelium" + desc = "This mycelium grows into chanterelle mushrooms." + icon_state = "mycelium-chanter" + species = "chanter" + plantname = "Chanterelle Mushrooms" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/chanterelle + lifespan = 35 + endurance = 20 + maturation = 7 + production = 1 + yield = 5 + potency = 15 + growthstages = 3 + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + reagents_add = list("nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/chanterelle + seed = /obj/item/seeds/chanter + name = "chanterelle cluster" + desc = "Cantharellus Cibarius: These jolly yellow little shrooms sure look tasty!" + icon_state = "chanterelle" + filling_color = "#FFA500" + + +// Glowshroom +/obj/item/seeds/glowshroom + name = "pack of glowshroom mycelium" + desc = "This mycelium -glows- into mushrooms!" + icon_state = "mycelium-glowshroom" + species = "glowshroom" + plantname = "Glowshrooms" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/glowshroom + lifespan = 100 //ten times that is the delay + endurance = 30 + maturation = 15 + production = 1 + yield = 3 //-> spread + potency = 30 //-> brightness + growthstages = 4 + rarity = 20 + genes = list(/datum/plant_gene/trait/glow, /datum/plant_gene/trait/plant_type/fungal_metabolism) + growing_icon = 'icons/obj/hydroponics/growing_mushrooms.dmi' + mutatelist = list(/obj/item/seeds/glowshroom/glowcap) + reagents_add = list("radium" = 0.1, "phosphorus" = 0.1, "nutriment" = 0.04) + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/glowshroom + seed = /obj/item/seeds/glowshroom + name = "glowshroom cluster" + desc = "Mycena Bregprox: This species of mushroom glows in the dark." + icon_state = "glowshroom" + filling_color = "#00FA9A" + var/effect_path = /obj/effect/glowshroom + origin_tech = "biotech=4;plasmatech=6" + light_color = "#006622" + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/glowshroom/attack_self(mob/user) + if(istype(user.loc, /turf/space)) + return + var/obj/effect/glowshroom/planted = new effect_path(user.loc) + planted.delay = planted.delay - seed.production * 100 //So the delay goes DOWN with better stats instead of up. :I + planted.endurance = seed.endurance + planted.yield = seed.yield + planted.potency = seed.potency + to_chat(user, "You plant [src].") + qdel(src) + + +// Glowcap +/obj/item/seeds/glowshroom/glowcap + name = "pack of glowcap mycelium" + desc = "This mycelium -powers- into mushrooms!" + icon_state = "mycelium-glowcap" + species = "glowcap" + icon_grow = "glowshroom-grow" + icon_dead = "glowshroom-dead" + plantname = "Glowcaps" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/glowshroom/glowcap + genes = list(/datum/plant_gene/trait/glow, /datum/plant_gene/trait/cell_charge, /datum/plant_gene/trait/plant_type/fungal_metabolism) + mutatelist = list() + reagents_add = list("teslium" = 0.1, "nutriment" = 0.04) + rarity = 30 + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/glowshroom/glowcap + seed = /obj/item/seeds/glowshroom/glowcap + name = "glowcap cluster" + desc = "Mycena Ruthenia: This species of mushroom glows in the dark, but aren't bioluminescent. They're warm to the touch..." + icon_state = "glowcap" + filling_color = "#00FA9A" + effect_path = /obj/effect/glowshroom/glowcap + origin_tech = "biotech=4;powerstorage=6;plasmatech=4" + light_color = "#8E0300" + +// Fungus/Mold +/obj/item/seeds/fungus + name = "pack of fungus spores" + desc = "These spores grow into fungus." + icon_state = "mycelium-tower" + species = "mold" + plantname = "Fungus" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/fungus + yield = 4 + icon_grow = "mold-grow" + icon_dead = "mold-dead" + icon_harvest = "mold-harvest" + growthstages = 3 + genes = list(/datum/plant_gene/trait/plant_type/fungal_metabolism) + reagents_add = list("fungus" = 0.35) + +/obj/item/weapon/reagent_containers/food/snacks/grown/mushroom/fungus + seed = /obj/item/seeds/fungus + name = "fungus" + desc = "A fungus ideal for making antibacterials." + icon_state = "angel" + color = "#4f4331" \ No newline at end of file diff --git a/code/modules/hydroponics/grown/nettle.dm b/code/modules/hydroponics/grown/nettle.dm new file mode 100644 index 00000000000..f6d9be51b4a --- /dev/null +++ b/code/modules/hydroponics/grown/nettle.dm @@ -0,0 +1,113 @@ +/obj/item/seeds/nettle + name = "pack of nettle seeds" + desc = "These seeds grow into nettles." + icon_state = "seed-nettle" + species = "nettle" + plantname = "Nettles" + product = /obj/item/weapon/grown/nettle/basic + lifespan = 30 + endurance = 40 // tuff like a toiger + yield = 4 + growthstages = 5 + genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/plant_type/weed_hardy) + mutatelist = list(/obj/item/seeds/nettle/death) + reagents_add = list("sacid" = 0.5) + +/obj/item/seeds/nettle/death + name = "pack of death-nettle seeds" + desc = "These seeds grow into death-nettles." + icon_state = "seed-deathnettle" + species = "deathnettle" + plantname = "Death Nettles" + product = /obj/item/weapon/grown/nettle/death + endurance = 25 + maturation = 8 + yield = 2 + genes = list(/datum/plant_gene/trait/repeated_harvest, /datum/plant_gene/trait/plant_type/weed_hardy, /datum/plant_gene/trait/stinging) + mutatelist = list() + reagents_add = list("facid" = 0.5, "sacid" = 0.5) + rarity = 20 + +/obj/item/weapon/grown/nettle //abstract type + name = "nettle" + desc = "It's probably not wise to touch it with bare hands..." + icon = 'icons/obj/weapons.dmi' + icon_state = "nettle" + damtype = "fire" + force = 15 + hitsound = 'sound/weapons/bladeslice.ogg' + throwforce = 5 + w_class = 1 + throw_speed = 1 + throw_range = 3 + origin_tech = "combat=3" + attack_verb = list("stung") + +/obj/item/weapon/grown/nettle/suicide_act(mob/user) + user.visible_message("[user] is eating some of the [src.name]! It looks like \he's trying to commit suicide.") + return (BRUTELOSS|TOXLOSS) + +/obj/item/weapon/grown/nettle/pickup(mob/living/user) + ..() + if(!ishuman(user)) + return 0 + var/mob/living/carbon/human/H = user + if(H.gloves) + return 0 + var/organ = ((H.hand ? "l_":"r_") + "arm") + var/obj/item/organ/external/affecting = H.get_organ(organ) + if(affecting) + if(affecting.take_damage(0, force)) + H.UpdateDamageIcon() + to_chat(H, "The nettle burns your bare hand!") + return 1 + + + +/obj/item/weapon/grown/nettle/afterattack(atom/A as mob|obj, mob/user,proximity) + if(!proximity) + return + if(force > 0) + force -= rand(1, (force / 3) + 1) // When you whack someone with it, leaves fall off + else + to_chat(usr, "All the leaves have fallen off the nettle from violent whacking.") + usr.unEquip(src) + qdel(src) + +/obj/item/weapon/grown/nettle/basic + seed = /obj/item/seeds/nettle + +/obj/item/weapon/grown/nettle/basic/add_juice() + ..() + force = round((5 + seed.potency / 5), 1) + +/obj/item/weapon/grown/nettle/death + seed = /obj/item/seeds/nettle/death + name = "deathnettle" + desc = "The glowing nettle incites rage in you just from looking at it!" + icon_state = "deathnettle" + force = 30 + throwforce = 15 + origin_tech = "combat=5" + +/obj/item/weapon/grown/nettle/death/add_juice() + ..() + force = round((5 + seed.potency / 2.5), 1) + +/obj/item/weapon/grown/nettle/death/pickup(mob/living/carbon/user) + if(..()) + if(prob(50)) + user.Paralyse(5) + to_chat(user, "You are stunned by the Deathnettle when you try picking it up!") + +/obj/item/weapon/grown/nettle/death/attack(mob/living/carbon/M, mob/user) + ..() + if(isliving(M)) + to_chat(M, "You are stunned by the powerful acid of the Deathnettle!") + add_logs(user, M, "attacked", src) + + M.AdjustEyeBlurry(force/7) + if(prob(20)) + M.Paralyse(force / 6) + M.Weaken(force / 15) + M.drop_item() diff --git a/code/modules/hydroponics/grown/nymph.dm b/code/modules/hydroponics/grown/nymph.dm new file mode 100644 index 00000000000..b1868f10a11 --- /dev/null +++ b/code/modules/hydroponics/grown/nymph.dm @@ -0,0 +1,26 @@ +/obj/item/seeds/nymph + name = "pack of diona nymph seeds" + desc = "These seeds grow into diona nymphs." + icon_state = "seed-replicapod" + species = "replicapod" + plantname = "Nymph Pod" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/nymph_pod + lifespan = 50 + endurance = 8 + maturation = 10 + production = 1 + yield = 1 + reagents_add = list("nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/nymph_pod + seed = /obj/item/seeds/nymph + name = "nymph pod" + desc = "A peculiar wriggling pod with a grown nymph inside. Crack it open to let the nymph out." + icon_state = "mushy" + bitesize_mod = 2 + +/obj/item/weapon/reagent_containers/food/snacks/grown/nymph_pod/attack_self(mob/user) + new /mob/living/simple_animal/diona(get_turf(user)) + to_chat(user, "You crack open [src] letting the nymph out.") + user.drop_item() + qdel(src) \ No newline at end of file diff --git a/code/modules/hydroponics/grown/peanut.dm b/code/modules/hydroponics/grown/peanut.dm new file mode 100644 index 00000000000..922b64a8a20 --- /dev/null +++ b/code/modules/hydroponics/grown/peanut.dm @@ -0,0 +1,25 @@ +// Peanuts +/obj/item/seeds/peanuts + name = "pack of peanut seeds" + desc = "These seeds grow into peanuts." + icon_state = "seed-potato" + species = "potato" + plantname = "Peanut Vines" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/peanuts + lifespan = 30 + maturation = 10 + production = 1 + yield = 4 + growthstages = 4 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + icon_grow = "potato-grow" + icon_dead = "potato-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + reagents_add = list("nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/peanuts + seed = /obj/item/seeds/peanuts + name = "patch of peanuts" + desc = "Best avoided if you have spess allergies." + icon_state = "peanuts" + gender = PLURAL \ No newline at end of file diff --git a/code/modules/hydroponics/grown/potato.dm b/code/modules/hydroponics/grown/potato.dm new file mode 100644 index 00000000000..2b144679698 --- /dev/null +++ b/code/modules/hydroponics/grown/potato.dm @@ -0,0 +1,65 @@ +// Potato +/obj/item/seeds/potato + name = "pack of potato seeds" + desc = "Boil 'em! Mash 'em! Stick 'em in a stew!" + icon_state = "seed-potato" + species = "potato" + plantname = "Potato Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/potato + lifespan = 30 + maturation = 10 + production = 1 + yield = 4 + growthstages = 4 + growing_icon = 'icons/obj/hydroponics/growing_vegetables.dmi' + icon_grow = "potato-grow" + icon_dead = "potato-dead" + genes = list(/datum/plant_gene/trait/battery) + mutatelist = list(/obj/item/seeds/potato/sweet) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/potato + seed = /obj/item/seeds/potato + name = "potato" + desc = "Boil 'em! Mash 'em! Stick 'em in a stew!" + icon_state = "potato" + filling_color = "#E9967A" + bitesize = 100 + + +/obj/item/weapon/reagent_containers/food/snacks/grown/potato/wedges + name = "potato wedges" + desc = "Slices of neatly cut potato." + icon_state = "potato_wedges" + filling_color = "#E9967A" + bitesize = 100 + + +/obj/item/weapon/reagent_containers/food/snacks/grown/potato/attackby(obj/item/weapon/W, mob/user, params) + if(is_sharp(W)) + to_chat(user, "You cut the potato into wedges with [W].") + var/obj/item/weapon/reagent_containers/food/snacks/grown/potato/wedges/Wedges = new /obj/item/weapon/reagent_containers/food/snacks/grown/potato/wedges + if(!remove_item_from_storage(user)) + user.unEquip(src) + user.put_in_hands(Wedges) + qdel(src) + else + return ..() + + +// Sweet Potato +/obj/item/seeds/potato/sweet + name = "pack of sweet potato seeds" + desc = "These seeds grow into sweet potato plants." + icon_state = "seed-sweetpotato" + species = "sweetpotato" + plantname = "Sweet Potato Plants" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/potato/sweet + mutatelist = list() + reagents_add = list("vitamin" = 0.1, "sugar" = 0.1, "nutriment" = 0.1) + +/obj/item/weapon/reagent_containers/food/snacks/grown/potato/sweet + seed = /obj/item/seeds/potato/sweet + name = "sweet potato" + desc = "It's sweet." + icon_state = "sweetpotato" \ No newline at end of file diff --git a/code/modules/hydroponics/grown/pumpkin.dm b/code/modules/hydroponics/grown/pumpkin.dm new file mode 100644 index 00000000000..ed8e64742ce --- /dev/null +++ b/code/modules/hydroponics/grown/pumpkin.dm @@ -0,0 +1,54 @@ +// Pumpkin +/obj/item/seeds/pumpkin + name = "pack of pumpkin seeds" + desc = "These seeds grow into pumpkin vines." + icon_state = "seed-pumpkin" + species = "pumpkin" + plantname = "Pumpkin Vines" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin + lifespan = 50 + endurance = 40 + growthstages = 3 + growing_icon = 'icons/obj/hydroponics/growing_fruits.dmi' + icon_grow = "pumpkin-grow" + icon_dead = "pumpkin-dead" + genes = list(/datum/plant_gene/trait/repeated_harvest) + mutatelist = list(/obj/item/seeds/pumpkin/blumpkin) + reagents_add = list("vitamin" = 0.04, "nutriment" = 0.2) + +/obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin + seed = /obj/item/seeds/pumpkin + name = "pumpkin" + desc = "It's large and scary." + icon_state = "pumpkin" + filling_color = "#FFA500" + bitesize_mod = 2 + +/obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin/attackby(obj/item/weapon/W as obj, mob/user as mob, params) + if(is_sharp(W)) + user.show_message("You carve a face into [src]!", 1) + new /obj/item/clothing/head/hardhat/pumpkinhead(user.loc) + qdel(src) + return + else + return ..() + +// Blumpkin +/obj/item/seeds/pumpkin/blumpkin + name = "pack of blumpkin seeds" + desc = "These seeds grow into blumpkin vines." + icon_state = "seed-blumpkin" + species = "blumpkin" + plantname = "Blumpkin Vines" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/blumpkin + mutatelist = list() + reagents_add = list("ammonia" = 0.2, "chlorine" = 0.1, "nutriment" = 0.2) + rarity = 20 + +/obj/item/weapon/reagent_containers/food/snacks/grown/blumpkin + seed = /obj/item/seeds/pumpkin/blumpkin + name = "blumpkin" + desc = "The pumpkin's toxic sibling." + icon_state = "blumpkin" + filling_color = "#87CEFA" + bitesize_mod = 2 diff --git a/code/modules/hydroponics/grown/random.dm b/code/modules/hydroponics/grown/random.dm new file mode 100644 index 00000000000..1f48f83c9a5 --- /dev/null +++ b/code/modules/hydroponics/grown/random.dm @@ -0,0 +1,29 @@ +//Random seeds; stats, traits, and plant type are randomized for each seed. + +/obj/item/seeds/random + name = "pack of strange seeds" + desc = "Mysterious seeds as strange as their name implies. Spooky" + icon_state = "seed-x" + species = "?????" + plantname = "strange plant" + product = /obj/item/weapon/reagent_containers/food/snacks/grown/random + icon_grow = "xpod-grow" + icon_dead = "xpod-dead" + icon_harvest = "xpod-harvest" + growthstages = 4 + +/obj/item/seeds/random/New() + randomize_stats() + ..() + if(prob(60)) + add_random_reagents() + if(prob(50)) + add_random_traits() + add_random_plant_type(35) + +/obj/item/weapon/reagent_containers/food/snacks/grown/random + seed = /obj/item/seeds/random + name = "strange plant" + desc = "What could this even be?" + icon_state = "crunchy" + bitesize_mod = 2 \ No newline at end of file diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm new file mode 100644 index 00000000000..f3cfa967363 --- /dev/null +++ b/code/modules/hydroponics/grown/replicapod.dm @@ -0,0 +1,104 @@ +// A very special plant, deserving it's own file. + +/obj/item/seeds/replicapod + name = "pack of replica pod seeds" + desc = "These seeds grow into replica pods. They say these are used to harvest humans." + icon_state = "seed-replicapod" + species = "replicapod" + plantname = "Replica Pod" + product = /mob/living/carbon/human/diona //verrry special -- Urist + lifespan = 50 + endurance = 8 + maturation = 10 + production = 1 + yield = 1 //seeds if there isn't a dna inside + potency = 30 + var/ckey = null + var/realName = null + var/datum/mind/mind = null + var/blood_gender = null + var/blood_type = null + var/factions = null + var/contains_sample = 0 + +/obj/item/seeds/replicapod/Destroy() + mind = null + return ..() + +/obj/item/seeds/replicapod/attackby(obj/item/weapon/W, mob/user, params) + if(istype(W,/obj/item/weapon/reagent_containers/syringe)) + if(!contains_sample) + for(var/datum/reagent/blood/bloodSample in W.reagents.reagent_list) + if(bloodSample.data["mind"] && bloodSample.data["cloneable"] == 1) + mind = bloodSample.data["mind"] + ckey = bloodSample.data["ckey"] + realName = bloodSample.data["real_name"] + blood_gender = bloodSample.data["gender"] + blood_type = bloodSample.data["blood_type"] + factions = bloodSample.data["factions"] + W.reagents.clear_reagents() + to_chat(user, "You inject the contents of the syringe into the seeds.") + contains_sample = 1 + else + to_chat(user, "The seeds reject the sample!") + else + to_chat(user, "The seeds already contain a genetic sample!") + else + return ..() + +/obj/item/seeds/replicapod/get_analyzer_text() + var/text = ..() + if(contains_sample) + text += "\n It contains a blood sample!" + return text + + +/obj/item/seeds/replicapod/harvest(mob/user = usr) //now that one is fun -- Urist + var/obj/machinery/hydroponics/parent = loc + var/make_podman = 0 + var/ckey_holder = null + if(config.revival_pod_plants) + if(ckey) + for(var/mob/M in player_list) + if(isobserver(M)) + var/mob/dead/observer/O = M + if(O.ckey == ckey && O.can_reenter_corpse) + make_podman = 1 + break + else + if(M.ckey == ckey && M.stat == DEAD && !M.suiciding) + make_podman = 1 + break + else //If the player has ghosted from his corpse before blood was drawn, his ckey is no longer attached to the mob, so we need to match up the cloned player through the mind key + for(var/mob/M in player_list) + if(mind && M.mind && ckey(M.mind.key) == ckey(mind.key) && M.ckey && M.client && M.stat == DEAD && !M.suiciding) + if(isobserver(M)) + var/mob/dead/observer/O = M + if(!O.can_reenter_corpse) + break + make_podman = 1 + ckey_holder = M.ckey + break + + if(make_podman) //all conditions met! + var/mob/living/carbon/human/diona/podman = new /mob/living/carbon/human/diona(parent.loc) + if(realName) + podman.real_name = realName + mind.transfer_to(podman) + if(ckey) + podman.ckey = ckey + else + podman.ckey = ckey_holder + podman.gender = blood_gender + podman.faction |= factions + + else //else, one packet of seeds. maybe two + var/seed_count = 1 + if(prob(getYield() * 20)) + seed_count++ + var/output_loc = parent.Adjacent(user) ? user.loc : parent.loc //needed for TK + for(var/i=0,i " + + if (contents.len == 0) + dat += "No seeds" + else + dat += "
Seed storage contents
Plant data for [form_title]" - user.visible_message("[user] runs the scanner over \the [target].") - - dat += "General Data" - - dat += "
Reagent Data" - - dat += "This sample contains: " - for(var/datum/reagent/R in grown_reagents.reagent_list) - dat += " - [R.id], [grown_reagents.get_reagent_amount(R.id)] unit(s)" - - dat += " Other Data" - - if(grown_seed.get_trait(TRAIT_HARVEST_REPEAT)) - dat += "This plant can be harvested repeatedly." - - if(grown_seed.get_trait(TRAIT_IMMUTABLE) == -1) - dat += "This plant is highly mutable. " - else if(grown_seed.get_trait(TRAIT_IMMUTABLE) > 0) - dat += "This plant does not possess genetics that are alterable. " - - if(grown_seed.get_trait(TRAIT_REQUIRES_NUTRIENTS)) - if(grown_seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) < 0.05) - dat += "It consumes a small amount of nutrient fluid. " - else if(grown_seed.get_trait(TRAIT_NUTRIENT_CONSUMPTION) > 0.2) - dat += "It requires a heavy supply of nutrient fluid. " - else - dat += "It requires a supply of nutrient fluid. " - - if(grown_seed.get_trait(TRAIT_REQUIRES_WATER)) - if(grown_seed.get_trait(TRAIT_WATER_CONSUMPTION) < 1) - dat += "It requires very little water. " - else if(grown_seed.get_trait(TRAIT_WATER_CONSUMPTION) > 5) - dat += "It requires a large amount of water. " - else - dat += "It requires a stable supply of water. " - - if(grown_seed.mutants && grown_seed.mutants.len) - dat += "It exhibits a high degree of potential subspecies shift. " - - dat += "It thrives in a temperature of [grown_seed.get_trait(TRAIT_IDEAL_HEAT)] Kelvin." - - if(grown_seed.consume_gasses && grown_seed.consume_gasses.len) - for(var/gas in grown_seed.consume_gasses) - if(gas == "carbon_dioxide") gas = "carbon dioxide" - if(gas == "toxins") gas = "plasma" - dat += " It requires an environment rich in [gas] gas to thrive." - - if(grown_seed.exude_gasses && grown_seed.exude_gasses.len) - for(var/gas in grown_seed.exude_gasses) - if(gas == "carbon_dioxide") gas = "carbon dioxide" - if(gas == "toxins") gas = "plasma" - dat += " It releases [gas] gas as a byproduct of it's growth." - - if(grown_seed.get_trait(TRAIT_LOWKPA_TOLERANCE) < 20) - dat += " It is well adapted to low pressure levels." - if(grown_seed.get_trait(TRAIT_HIGHKPA_TOLERANCE) > 220) - dat += " It is well adapted to high pressure levels." - - if(grown_seed.get_trait(TRAIT_HEAT_TOLERANCE) > 30) - dat += " It is well adapted to a range of temperatures." - else if(grown_seed.get_trait(TRAIT_HEAT_TOLERANCE) < 10) - dat += " It is very sensitive to temperature shifts." - - dat += " It thrives in a light level of [grown_seed.get_trait(TRAIT_IDEAL_LIGHT)] lumen[grown_seed.get_trait(TRAIT_IDEAL_LIGHT) == 1 ? "" : "s"]." - - if(grown_seed.get_trait(TRAIT_LIGHT_TOLERANCE) > 10) - dat += " It is well adapted to a range of light levels." - else if(grown_seed.get_trait(TRAIT_LIGHT_TOLERANCE) < 3) - dat += " It is very sensitive to light level shifts." - - if(grown_seed.get_trait(TRAIT_TOXINS_TOLERANCE) < 3) - dat += " It is highly sensitive to toxins." - else if(grown_seed.get_trait(TRAIT_TOXINS_TOLERANCE) > 6) - dat += " It is remarkably resistant to toxins." - - if(grown_seed.get_trait(TRAIT_PEST_TOLERANCE) < 3) - dat += " It is highly sensitive to pests." - else if(grown_seed.get_trait(TRAIT_PEST_TOLERANCE) > 6) - dat += " It is remarkably resistant to pests." - - if(grown_seed.get_trait(TRAIT_WEED_TOLERANCE) < 3) - dat += " It is highly sensitive to weeds." - else if(grown_seed.get_trait(TRAIT_WEED_TOLERANCE) > 6) - dat += " It is remarkably resistant to weeds." - - switch(grown_seed.get_trait(TRAIT_SPREAD)) - if(1) - dat += " It is able to be planted outside of a tray." - if(2) - dat += " It is a robust and vigorous vine that will spread rapidly." - - switch(grown_seed.get_trait(TRAIT_CARNIVOROUS)) - if(1) - dat += " It is carniovorous and will eat tray pests for sustenance." - if(2) - dat += " It is carnivorous and poses a significant threat to living things around it." - - if(grown_seed.get_trait(TRAIT_PARASITE)) - dat += " It is capable of parisitizing and gaining sustenance from tray weeds." - if(grown_seed.get_trait(TRAIT_ALTER_TEMP)) - dat += " It will periodically alter the local temperature by [grown_seed.get_trait(TRAIT_ALTER_TEMP)] Kelvin." - - if(grown_seed.get_trait(TRAIT_BIOLUM)) - dat += " It is [grown_seed.get_trait(TRAIT_BIOLUM_COLOUR) ? "bio-luminescent" : "bio-luminescent"]." - - if(grown_seed.get_trait(TRAIT_PRODUCES_POWER)) - dat += " The fruit will function as a battery if prepared appropriately." - - if(grown_seed.get_trait(TRAIT_BATTERY_RECHARGE)) - dat += " The fruit hums with an odd electrical energy." - - if(grown_seed.get_trait(TRAIT_STINGS)) - dat += " The fruit is covered in stinging spines." - - if(grown_seed.get_trait(TRAIT_JUICY) == 1) - dat += " The fruit is soft-skinned and juicy." - else if(grown_seed.get_trait(TRAIT_JUICY) == 2) - dat += " The fruit is excessively juicy." - - if(grown_seed.get_trait(TRAIT_EXPLOSIVE)) - dat += " The fruit is internally unstable." - - if(grown_seed.get_trait(TRAIT_TELEPORTING)) - dat += " The fruit is temporal/spatially unstable." - - if(dat) - last_data = dat - dat += " \[print report\]" - user << browse(dat,"window=plant_analyzer") - - return - -/obj/item/weapon/minihoe // -- Numbers - name = "mini hoe" - desc = "It's used for removing weeds or scratching your back." - icon = 'icons/obj/weapons.dmi' - icon_state = "hoe" - item_state = "hoe" - flags = CONDUCT | NOBLUDGEON - force = 5 - throwforce = 7 - w_class = 2 - materials = list(MAT_METAL=50) - attack_verb = list("slashed", "sliced", "cut", "clawed") - hitsound = 'sound/weapons/bladeslice.ogg' - -//Hatchets and things to kill kudzu -/obj/item/weapon/hatchet - name = "hatchet" - desc = "A very sharp axe blade upon a short fibremetal handle. It has a long history of chopping things, but now it is used for chopping wood." - icon = 'icons/obj/weapons.dmi' - icon_state = "hatchet" - flags = CONDUCT - force = 12 - sharp = 1 - edge = 1 - w_class = 2 - throwforce = 15 - throw_speed = 4 - throw_range = 4 - materials = list(MAT_METAL=15000) - origin_tech = "materials=2;combat=1" - attack_verb = list("chopped", "torn", "cut") - hitsound = 'sound/weapons/bladeslice.ogg' - -/obj/item/weapon/hatchet/unathiknife - name = "duelling knife" - desc = "A length of leather-bound wood studded with razor-sharp teeth. How crude." - icon = 'icons/obj/weapons.dmi' - icon_state = "unathiknife" - attack_verb = list("ripped", "torn", "cut") - -/obj/item/weapon/scythe - icon_state = "scythe0" - name = "scythe" - desc = "A sharp and curved blade on a long fibremetal handle, this tool makes it easy to reap what you sow." - force = 13 - throwforce = 5 - sharp = 1 - edge = 1 - throw_speed = 2 - throw_range = 3 - w_class = 4 - var/extend = 1 - flags = CONDUCT - armour_penetration = 20 - slot_flags = SLOT_BACK - origin_tech = "materials=2;combat=2" - attack_verb = list("chopped", "sliced", "cut", "reaped") - hitsound = 'sound/weapons/bladeslice.ogg' - -/obj/item/weapon/scythe/afterattack(atom/A, mob/user as mob, proximity) - if(!proximity) return - if(istype(A, /obj/effect/plant) && extend == 1) - for(var/obj/effect/plant/B in orange(A,1)) - if(prob(80)) - B.die_off(1) - qdel(A) - -/obj/item/weapon/scythe/tele - icon_state = "tscythe0" - name = "telescopic scythe" - desc = "A sharp and curved blade on a collapsable fibremetal handle, this tool is the pinnacle of covert reaping technology." - force = 3.0 - sharp = 0 - edge = 0 - throw_speed = 2 - throw_range = 3 - w_class = 2 - extend = 0 - armour_penetration = 20 - slot_flags = SLOT_BELT - origin_tech = "materials=3;combat=3" - attack_verb = list("chopped", "sliced", "cut", "reaped") - hitsound = "swing_hit" - -/obj/item/weapon/scythe/tele/attack_self(mob/user as mob) - extend = !extend - if(extend) - to_chat(user, "With a flick of the wrist, you extend the scythe. It's reaping time!") - icon_state = "tscythe1" - item_state = "scythe0" - slot_flags &= ~SLOT_BELT - w_class = 4 //doesnt fit in backpack when its on for balance - force = 13 //normal scythe damage - attack_verb = list("chopped", "sliced", "cut", "reaped") - hitsound = 'sound/weapons/bladeslice.ogg' - //Extend sound (blade unsheath) - playsound(src.loc, 'sound/weapons/blade_unsheath.ogg', 50, 1) //Sound credit to Qat of Freesound.org - else - to_chat(user, "You collapse the scythe, folding it for easy storage.") - icon_state = "tscythe0" - item_state = "tscythe0" //no sprite in other words - slot_flags |= SLOT_BELT - w_class = 2 - force = 3 //not so robust now - attack_verb = list("hit", "poked") - hitsound = "swing_hit" - //Collapse sound (blade sheath) - playsound(src.loc, 'sound/weapons/blade_sheath.ogg', 50, 1) //Sound credit to Q.K. of Freesound.org - if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - H.update_inv_l_hand() - H.update_inv_r_hand() - add_fingerprint(user) - if(!blood_DNA) return - if(blood_overlay && (blood_DNA.len >= 1)) //updates blood overlay, if any - overlays.Cut()//this might delete other item overlays as well but eeeeeeeh - - var/icon/I = new /icon(src.icon, src.icon_state) - I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD) - I.Blend(new /icon('icons/effects/blood.dmi', "itemblood"),ICON_MULTIPLY) - blood_overlay = I - - overlays += blood_overlay - - return - -/obj/item/weapon/rsp - name = "\improper Rapid-Seed-Producer (RSP)" - desc = "A device used to rapidly deploy seeds." - icon = 'icons/obj/items.dmi' - icon_state = "rcd" - opacity = 0 - density = 0 - anchored = 0.0 - var/matter = 0 - var/mode = 1 - w_class = 3 - -/obj/item/weapon/bananapeel - name = "banana peel" - desc = "A peel from a banana." - icon = 'icons/obj/items.dmi' - icon_state = "banana_peel" - item_state = "banana_peel" - w_class = 1 - throwforce = 0 - throw_speed = 4 - throw_range = 20 - -/obj/item/weapon/corncob - name = "corn cob" - desc = "A reminder of meals gone by." - icon = 'icons/obj/harvest.dmi' - icon_state = "corncob" - item_state = "corncob" - w_class = 1 - throwforce = 0 - throw_speed = 4 - throw_range = 20 diff --git a/code/modules/hydroponics/trays/tray_update_icons.dm b/code/modules/hydroponics/trays/tray_update_icons.dm deleted file mode 100644 index aac0822a283..00000000000 --- a/code/modules/hydroponics/trays/tray_update_icons.dm +++ /dev/null @@ -1,86 +0,0 @@ -//Refreshes the icon and sets the luminosity -/obj/machinery/portable_atmospherics/hydroponics/update_icon() - // Update name. - if(seed) - if(mechanical) - name = "[base_name] (#[seed.uid])" - else - name = "[seed.seed_name]" - else - name = initial(name) - - if(labelled) - name += " ([labelled])" - - overlays.Cut() - // Updates the plant overlay. - if(!isnull(seed)) - - if(mechanical && health <= (seed.get_trait(TRAIT_ENDURANCE) / 2)) - overlays += "over_lowhealth3" - - if(dead) - var/ikey = "[seed.get_trait(TRAIT_PLANT_ICON)]-dead" - var/image/dead_overlay = plant_controller.plant_icon_cache["[ikey]"] - if(!dead_overlay) - dead_overlay = image('icons/obj/hydroponics_growing.dmi', "[ikey]") - dead_overlay.color = DEAD_PLANT_COLOUR - overlays |= dead_overlay - else - if(!seed.growth_stages) - seed.update_growth_stages() - if(!seed.growth_stages) - to_chat(world, "Seed type [seed.get_trait(TRAIT_PLANT_ICON)] cannot find a growth stage value.") - return - var/overlay_stage = 1 - if(age >= seed.get_trait(TRAIT_MATURATION)) - overlay_stage = seed.growth_stages - else - var/maturation = seed.get_trait(TRAIT_MATURATION)/seed.growth_stages - if(maturation < 1) - maturation = 1 - overlay_stage = maturation ? max(1,round(age/maturation)) : 1 - var/ikey = "[seed.get_trait(TRAIT_PLANT_ICON)]-[overlay_stage]" - var/image/plant_overlay = plant_controller.plant_icon_cache["[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"] - if(!plant_overlay) - plant_overlay = image('icons/obj/hydroponics_growing.dmi', "[ikey]") - plant_overlay.color = seed.get_trait(TRAIT_PLANT_COLOUR) - plant_controller.plant_icon_cache["[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"] = plant_overlay - overlays |= plant_overlay - last_plant_ikey = ikey - - if(harvest && overlay_stage == seed.growth_stages) - ikey = "[seed.get_trait(TRAIT_PRODUCT_ICON)]" - var/image/harvest_overlay = plant_controller.plant_icon_cache["product-[ikey]-[seed.get_trait(TRAIT_PLANT_COLOUR)]"] - if(!harvest_overlay) - harvest_overlay = image('icons/obj/hydroponics_products.dmi', "[ikey]") - harvest_overlay.color = seed.get_trait(TRAIT_PRODUCT_COLOUR) - plant_controller.plant_icon_cache["product-[ikey]-[seed.get_trait(TRAIT_PRODUCT_COLOUR)]"] = harvest_overlay - overlays |= harvest_overlay - - //Draw the cover. - if(closed_system) - overlays += "hydrocover" - - //Updated the various alert icons. - if(mechanical) - if(waterlevel <= 10) - overlays += "over_lowwater3" - if(nutrilevel <= 2) - overlays += "over_lownutri3" - if(weedlevel >= 5 || pestlevel >= 5 || toxins >= 40) - overlays += "over_alert3" - if(harvest) - overlays += "over_harvest3" - - // Update bioluminescence. - if(seed) - if(seed.get_trait(TRAIT_BIOLUM)) - var/clr - if(seed.get_trait(TRAIT_BIOLUM_COLOUR)) - clr = seed.get_trait(TRAIT_BIOLUM_COLOUR) - set_light(round(seed.get_trait(TRAIT_POTENCY)/10), l_color = clr) - return - - set_light(0) - return \ No newline at end of file diff --git a/code/modules/mining/abandonedcrates.dm b/code/modules/mining/abandonedcrates.dm index cd229550880..db641754240 100644 --- a/code/modules/mining/abandonedcrates.dm +++ b/code/modules/mining/abandonedcrates.dm @@ -22,7 +22,7 @@ switch(loot) if(1 to 5) //5% chance new /obj/item/weapon/reagent_containers/food/drinks/bottle/rum(src) - new /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosiadeus(src) + new /obj/item/weapon/reagent_containers/food/snacks/grown/ambrosia/deus(src) new /obj/item/weapon/reagent_containers/food/drinks/bottle/whiskey(src) new /obj/item/weapon/lighter(src) if(6 to 10) @@ -41,11 +41,9 @@ if(21 to 25) for(var/i in 1 to 5) new /obj/item/weapon/contraband/poster(src) - if(26 to 30) + if(26 to 35) for(var/i in 1 to 3) new /obj/item/weapon/reagent_containers/glass/beaker/noreact(src) - if(31 to 35) - new /obj/item/seeds/cash(src) if(36 to 40) new /obj/item/weapon/melee/baton(src) if(41 to 45) 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/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index ebe6445121c..07ba42b69d7 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -914,14 +914,14 @@ var/list/ventcrawl_machinery = list(/obj/machinery/atmospherics/unary/vent_pump, /mob/living/carbon/proc/slip(var/description, var/stun, var/weaken, var/tilesSlipped, var/walkSafely, var/slipAny) if(flying || buckled || (walkSafely && m_intent == "walk")) - return + return 0 if((lying) && (!(tilesSlipped))) - return + return 0 if(!(slipAny)) if(istype(src, /mob/living/carbon/human)) var/mob/living/carbon/human/H = src if((isobj(H.shoes) && H.shoes.flags & NOSLIP) || H.species.bodyflags & FEET_NOSLIP) - return + return 0 if(tilesSlipped) for(var/t = 0, t<=tilesSlipped, t++) spawn (t) step(src, src.dir) 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/interactive/functions.dm b/code/modules/mob/living/carbon/human/interactive/functions.dm index 080736ddfd7..272b95d3577 100644 --- a/code/modules/mob/living/carbon/human/interactive/functions.dm +++ b/code/modules/mob/living/carbon/human/interactive/functions.dm @@ -119,15 +119,15 @@ internalBeaker.name = "Grow-U-All Super Spray" if(internalBeaker && internalBag) - var/obj/machinery/portable_atmospherics/hydroponics/HP + var/obj/machinery/hydroponics/HP //consider the appropriate target var/list/considered = list() - for(var/obj/machinery/portable_atmospherics/hydroponics/tester in view(12,src)) + for(var/obj/machinery/hydroponics/tester in view(12,src)) considered[tester] = 1 - if(!tester.seed) + if(!tester.myseed) considered[tester] += 50 if(tester.weedlevel > 0) considered[tester] += 5 @@ -154,7 +154,7 @@ else if(HP.harvest || HP.dead) HP.attack_hand(src) - else if(!HP.seed) + else if(!HP.myseed) var/obj/item/seeds/SEED = new /obj/item/seeds/random(src) custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")] towards [TARGET], producing a [SEED]!") HP.attackby(SEED, src) @@ -170,8 +170,8 @@ internalBeaker.reagents.add_reagent("diethylamine", 10) if(HP.nutrilevel < HP.maxnutri) change = 1 - if(!internalBeaker.reagents.has_reagent("eznutrient", 15)) - internalBeaker.reagents.add_reagent("eznutrient", 15) + if(!internalBeaker.reagents.has_reagent("eznutriment", 15)) + internalBeaker.reagents.add_reagent("eznutriment", 15) if(!internalBeaker.reagents.has_reagent("diethylamine", 15)) internalBeaker.reagents.add_reagent("diethylamine", 15) if(HP.waterlevel < HP.maxwater) @@ -261,7 +261,7 @@ var/pranksNearby = 100 for(var/turf/simulated/T in orange(1, C)) for(var/obj/item/A in T) - if(istype(A,/obj/item/weapon/soap) || istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana) || istype(A,/obj/item/weapon/bananapeel)) + if(istype(A,/obj/item/weapon/soap) || istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana) || istype(A,/obj/item/weapon/grown/bananapeel)) pranksNearby-- if(T.wet) pranksNearby -= 10 @@ -287,7 +287,7 @@ if(istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana)) var/obj/item/weapon/reagent_containers/food/snacks/B = A B.attack(src, src) - if(istype(A,/obj/item/weapon/bananapeel)) + if(istype(A,/obj/item/weapon/grown/bananapeel)) npcDrop(A) hasPranked = 1 if(!hasPranked) @@ -403,26 +403,6 @@ ingredients += I I.forceMove(null) - for(var/P in R.fruit) - for(var/i = 1 to R.fruit[P]) - var/obj/item/I = locate(P) in allContents - if(I) - ingredients += I - I.forceMove(null) - continue - - I = locate(P) in rangeCheck - TARGET = I - if(I && !Adjacent(I)) - tryWalk(get_turf(I)) - sleep(get_dist(src, I)) - if(!I || !(I in rangeCheck)) - refundrecipe(ingredients) - return 0 - custom_emote(1, "[pick("gibbers","drools","slobbers","claps wildly","spits")], picking up [I].") - ingredients += I - I.forceMove(null) - // cheaply cook the ingredients into result sleep(R.time) for(var/obj/I in ingredients) @@ -579,8 +559,8 @@ var/highest_count = 0 var/datum/recipe/winner = null for(var/datum/recipe/R in available_recipes) - if(R.check_items(ingredientZone) >= 0 && R.check_fruit(ingredientZone) >= 0) - var/count = (R.items ? R.items.len : 0) + (R.fruit ? R.fruit.len : 0) + if(R.check_items(ingredientZone) >= 0) + var/count = (R.items ? R.items.len : 0) if(count > highest_count) highest_count = count winner = R diff --git a/code/modules/mob/living/carbon/human/interactive/interactive.dm b/code/modules/mob/living/carbon/human/interactive/interactive.dm index 2dfc4f8d24e..fa8de8e2b87 100644 --- a/code/modules/mob/living/carbon/human/interactive/interactive.dm +++ b/code/modules/mob/living/carbon/human/interactive/interactive.dm @@ -349,10 +349,10 @@ favoured_types = list(/obj/item/weapon/mop, /obj/item/weapon/reagent_containers/glass/bucket, /obj/item/weapon/reagent_containers/spray/cleaner, /obj/effect/decal/cleanable) functions += "dojanitor" if("Clown") - favoured_types = list(/obj/item/weapon/soap, /obj/item/weapon/reagent_containers/food/snacks/grown/banana, /obj/item/weapon/bananapeel) + favoured_types = list(/obj/item/weapon/soap, /obj/item/weapon/reagent_containers/food/snacks/grown/banana, /obj/item/weapon/grown/bananapeel) functions += "clowning" if("Botanist") - favoured_types = list(/obj/machinery/portable_atmospherics/hydroponics, /obj/item/weapon/reagent_containers, /obj/item/weapon) + favoured_types = list(/obj/machinery/hydroponics, /obj/item/weapon/reagent_containers, /obj/item/weapon) functions += "botany" restrictedJob = 1 else 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/simple_animal/bot/cleanbot.dm b/code/modules/mob/living/simple_animal/bot/cleanbot.dm index fb128acdbfe..3f60793de02 100644 --- a/code/modules/mob/living/simple_animal/bot/cleanbot.dm +++ b/code/modules/mob/living/simple_animal/bot/cleanbot.dm @@ -143,7 +143,7 @@ target_types += /obj/effect/decal/cleanable/blood/gibs/robot target_types += /obj/effect/decal/cleanable/crayon target_types += /obj/effect/decal/cleanable/liquid_fuel - target_types += /obj/effect/decal/cleanable/molten_item + target_types += /obj/effect/decal/cleanable/molten_object target_types += /obj/effect/decal/cleanable/tomato_smudge target_types += /obj/effect/decal/cleanable/egg_smudge target_types += /obj/effect/decal/cleanable/pie_smudge 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/diona.dm b/code/modules/mob/living/simple_animal/friendly/diona.dm index 3161399a66e..925aba51fc9 100644 --- a/code/modules/mob/living/simple_animal/friendly/diona.dm +++ b/code/modules/mob/living/simple_animal/friendly/diona.dm @@ -127,16 +127,16 @@ set desc = "Turn your food into nutrients for plants." var/list/trays = list() - for(var/obj/machinery/portable_atmospherics/hydroponics/tray in range(1)) + for(var/obj/machinery/hydroponics/tray in range(1)) if(tray.nutrilevel < 10) trays += tray - var/obj/machinery/portable_atmospherics/hydroponics/target = input("Select a tray:") as null|anything in trays + var/obj/machinery/hydroponics/target = input("Select a tray:") as null|anything in trays if(!src || !target || target.nutrilevel == 10) return //Sanity check. src.nutrition -= ((10-target.nutrilevel)*5) - target.nutrilevel = 10 + target.adjustNutri(10) src.visible_message("[src] secretes a trickle of green liquid from its tail, refilling [target]'s nutrient tray.","\red You secrete a trickle of green liquid from your tail, refilling [target]'s nutrient tray.") /mob/living/simple_animal/diona/verb/eat_weeds() @@ -145,16 +145,16 @@ set desc = "Clean the weeds out of soil or a hydroponics tray." var/list/trays = list() - for(var/obj/machinery/portable_atmospherics/hydroponics/tray in range(1)) + for(var/obj/machinery/hydroponics/tray in range(1)) if(tray.weedlevel > 0) trays += tray - var/obj/machinery/portable_atmospherics/hydroponics/target = input("Select a tray:") as null|anything in trays + var/obj/machinery/hydroponics/target = input("Select a tray:") as null|anything in trays if(!src || !target || target.weedlevel == 0) return //Sanity check. src.nutrition += target.weedlevel * 15 - target.weedlevel = 0 + target.adjustWeeds(-10) src.visible_message("[src] begins rooting through [target], ripping out weeds and eating them noisily.","You begin rooting through [target], ripping out weeds and eating them noisily.") /mob/living/simple_animal/diona/verb/evolve() diff --git a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm index 04506c819c8..b1b31461b77 100644 --- a/code/modules/mob/living/simple_animal/friendly/farm_animals.dm +++ b/code/modules/mob/living/simple_animal/friendly/farm_animals.dm @@ -34,7 +34,7 @@ for(var/direction in shuffle(list(1,2,4,8,5,6,9,10))) var/step = get_step(src, direction) if(step) - if(locate(/obj/effect/plant) in step) + if(locate(/obj/effect/spacevine) in step) Move(step, get_dir(src, step)) /mob/living/simple_animal/hostile/retaliate/goat/handle_automated_action() @@ -47,11 +47,9 @@ LoseTarget() src.visible_message("\blue [src] calms down.") - if(locate(/obj/effect/plant) in loc) - var/obj/effect/plant/SV = locate(/obj/effect/plant) in loc - qdel(SV) - if(prob(10)) - say("Nom") + var/obj/effect/spacevine/SV = locate(/obj/effect/spacevine) in loc + if(SV) + SV.eat(src) /mob/living/simple_animal/hostile/retaliate/goat/Life() . = ..() @@ -66,11 +64,9 @@ /mob/living/simple_animal/hostile/retaliate/goat/Move() ..() if(!stat) - if(locate(/obj/effect/plant) in loc) - var/obj/effect/plant/SV = locate(/obj/effect/plant) in loc - qdel(SV) - if(prob(10)) - say("Nom") + var/obj/effect/spacevine/SV = locate(/obj/effect/spacevine) in loc + if(SV) + SV.eat(src) /mob/living/simple_animal/hostile/retaliate/goat/attackby(var/obj/item/O as obj, var/mob/user as mob, params) if(stat == CONSCIOUS && istype(O, /obj/item/weapon/reagent_containers/glass)) @@ -162,6 +158,7 @@ icon_living = "chick" icon_dead = "chick_dead" icon_gib = "chick_gib" + gender = FEMALE speak = list("Cherp.","Cherp?","Chirrup.","Cheep!") speak_emote = list("cheeps") emote_hear = list("cheeps") @@ -205,9 +202,10 @@ var/global/chicken_count = 0 /mob/living/simple_animal/chicken name = "\improper chicken" desc = "Hopefully the eggs are good this season." - icon_state = "chicken" - icon_living = "chicken" - icon_dead = "chicken_dead" + gender = FEMALE + icon_state = "chicken_brown" + icon_living = "chicken_brown" + icon_dead = "chicken_brown_dead" speak = list("Cluck!","BWAAAAARK BWAK BWAK BWAK!","Bwaak bwak.") speak_emote = list("clucks","croons") emote_hear = list("clucks") @@ -216,62 +214,68 @@ var/global/chicken_count = 0 speak_chance = 2 turns_per_move = 3 butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat = 2) + var/egg_type = /obj/item/weapon/reagent_containers/food/snacks/egg + var/food_type = /obj/item/weapon/reagent_containers/food/snacks/grown/wheat response_help = "pets the" response_disarm = "gently pushes aside the" response_harm = "kicks the" attacktext = "kicks" - health = 10 - maxHealth = 10 + health = 15 + maxHealth = 15 + ventcrawler = 2 var/eggsleft = 0 - var/chicken_color + var/eggsFertile = TRUE + var/body_color + var/icon_prefix = "chicken" pass_flags = PASSTABLE | PASSMOB mob_size = MOB_SIZE_SMALL can_hide = 1 can_collar = 1 + var/list/feedMessages = list("It clucks happily.","It clucks happily.") + var/list/layMessage = list("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.") + var/list/validColors = list("brown","black","white") gold_core_spawnable = CHEM_MOB_SPAWN_FRIENDLY /mob/living/simple_animal/chicken/New() ..() - if(!chicken_color) - chicken_color = pick( list("brown","black","white") ) - icon_state = "chicken_[chicken_color]" - icon_living = "chicken_[chicken_color]" - icon_dead = "chicken_[chicken_color]_dead" + if(!body_color) + body_color = pick(validColors) + icon_state = "[icon_prefix]_[body_color]" + icon_living = "[icon_prefix]_[body_color]" + icon_dead = "[icon_prefix]_[body_color]_dead" pixel_x = rand(-6, 6) pixel_y = rand(0, 10) chicken_count += 1 /mob/living/simple_animal/chicken/death(gibbed) - ..() + ..(gibbed) chicken_count -= 1 -/mob/living/simple_animal/chicken/attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/grown)) //feedin' dem chickens - var/obj/item/weapon/reagent_containers/food/snacks/grown/G = O - if(G.seed.kitchen_tag == "wheat") - if(!stat && eggsleft < 8) - user.visible_message("\blue [user] feeds [O] to [name]! It clucks happily.","\blue You feed [O] to [name]! It clucks happily.") - user.drop_item() - qdel(O) - eggsleft += rand(1, 4) -// to_chat(world, eggsleft) - else - to_chat(user, "\blue [name] doesn't seem hungry!") +/mob/living/simple_animal/chicken/attackby(obj/item/O, mob/user, params) + if(istype(O, food_type)) //feedin' dem chickens + if(!stat && eggsleft < 8) + var/feedmsg = "[user] feeds [O] to [name]! [pick(feedMessages)]" + user.visible_message(feedmsg) + user.drop_item() + qdel(O) + eggsleft += rand(1, 4) + //world << eggsleft else - to_chat(user, "\blue [name] doesn't seem interested in [O]!") + to_chat(user, "[name] doesn't seem hungry!") else ..() /mob/living/simple_animal/chicken/Life() . = ..() - if(. && prob(3) && eggsleft > 0) - visible_message("[src] [pick("lays an egg.","squats down and croons.","begins making a huge racket.","begins clucking raucously.")]") + if((. && prob(3) && eggsleft > 0) && egg_type) + visible_message("[src] [pick(layMessage)]") eggsleft-- - var/obj/item/weapon/reagent_containers/food/snacks/egg/E = new(get_turf(src)) + var/obj/item/E = new egg_type(get_turf(src)) E.pixel_x = rand(-6,6) E.pixel_y = rand(-6,6) - if(chicken_count < MAX_CHICKENS && prob(10)) - processing_objects.Add(E) + if(eggsFertile) + if(chicken_count < MAX_CHICKENS && prob(25)) + processing_objects.Add(E) /obj/item/weapon/reagent_containers/food/snacks/egg/var/amount_grown = 0 /obj/item/weapon/reagent_containers/food/snacks/egg/process() diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index 8c927eaefbd..538ce8450c7 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -5,9 +5,9 @@ #define BEE_TRAY_RECENT_VISIT 200 //How long in deciseconds until a tray can be visited by a bee again #define BEE_DEFAULT_COLOUR "#e5e500" //the colour we make the stripes of the bee if our reagent has no colour (or we have no reagent) -#define BEE_POLLINATE_YIELD_CHANCE 10 +#define BEE_POLLINATE_YIELD_CHANCE 33 #define BEE_POLLINATE_PEST_CHANCE 33 -#define BEE_POLLINATE_POTENTCY_CHANCE 50 +#define BEE_POLLINATE_POTENCY_CHANCE 50 /mob/living/simple_animal/hostile/poison/bees name = "bee" @@ -15,6 +15,7 @@ icon_state = "" icon_living = "" icon = 'icons/mob/bees.dmi' + gender = FEMALE speak_emote = list("buzzes") emote_hear = list("buzzes") turns_per_move = 0 @@ -142,7 +143,7 @@ if(beehome.bees) beehome.bees.Remove(src) beehome = null - ..() + return ..() /mob/living/simple_animal/hostile/poison/bees/worker/death(gibbed) if(beehome) @@ -158,10 +159,10 @@ to_chat(user, "This bee is homeless!") /mob/living/simple_animal/hostile/poison/bees/worker/Found(atom/A) - if(istype(A, /obj/machinery/portable_atmospherics/hydroponics)) - var/obj/machinery/portable_atmospherics/hydroponics/Hydro = A - if(Hydro.seed && !Hydro.dead && !Hydro.recent_bee_visit && !Hydro.closed_system) - wanted_objects |= /obj/machinery/portable_atmospherics/hydroponics //so we only hunt them while they're alive/seeded/not visisted and uncovered + if(istype(A, /obj/machinery/hydroponics)) + var/obj/machinery/hydroponics/Hydro = A + if(Hydro.myseed && !Hydro.dead && !Hydro.recent_bee_visit) + wanted_objects |= /obj/machinery/hydroponics //so we only hunt them while they're alive/seeded/not visisted return 1 ..() @@ -175,24 +176,24 @@ /mob/living/simple_animal/hostile/poison/bees/worker/AttackingTarget() //Pollinate - if(istype(target, /obj/machinery/portable_atmospherics/hydroponics)) - var/obj/machinery/portable_atmospherics/hydroponics/Hydro = target + if(istype(target, /obj/machinery/hydroponics)) + var/obj/machinery/hydroponics/Hydro = target pollinate(Hydro) else if(target == beehome) var/obj/structure/beebox/BB = target forceMove(BB) target = null - wanted_objects.Remove(/obj/structure/beebox) //so we don't attack beeboxes when not going home + wanted_objects -= /obj/structure/beebox //so we don't attack beeboxes when not going home else ..() -/mob/living/simple_animal/hostile/poison/bees/worker/proc/pollinate(obj/machinery/portable_atmospherics/hydroponics/Hydro) - if(!istype(Hydro) || !Hydro.seed || Hydro.dead || Hydro.recent_bee_visit || Hydro.closed_system) +/mob/living/simple_animal/hostile/poison/bees/worker/proc/pollinate(obj/machinery/hydroponics/Hydro) + if(!istype(Hydro) || !Hydro.myseed || Hydro.dead || Hydro.recent_bee_visit) target = null return target = null //so we pick a new hydro tray next FindTarget(), instead of loving the same plant for eternity - wanted_objects.Remove(/obj/machinery/portable_atmospherics/hydroponics) //so we only hunt them while they're alive/seeded/not visisted + wanted_objects -= /obj/machinery/hydroponics //so we only hunt them while they're alive/seeded/not visisted Hydro.recent_bee_visit = TRUE spawn(BEE_TRAY_RECENT_VISIT) if(Hydro) @@ -200,23 +201,14 @@ var/growth = health //Health also means how many bees are in the swarm, roughly. //better healthier plants! - Hydro.health += round(growth*0.5) + Hydro.adjustHealth(growth*0.5) if(prob(BEE_POLLINATE_PEST_CHANCE)) - Hydro.pestlevel = max(0, --Hydro.pestlevel) - if(prob(BEE_POLLINATE_YIELD_CHANCE)) //Yield mod is HELLA powerful, but quite rare - if(!isnull(plant_controller.seeds[Hydro.seed.name])) - Hydro.seed = Hydro.seed.diverge() - else - Hydro.seed.update_name_prefixes() - var/seed_yield = Hydro.seed.get_trait(TRAIT_YIELD) - Hydro.seed.set_trait(TRAIT_YIELD, seed_yield + 1, 10, 0) - if(prob(BEE_POLLINATE_POTENTCY_CHANCE)) - if(!isnull(plant_controller.seeds[Hydro.seed.name])) - Hydro.seed = Hydro.seed.diverge() - else - Hydro.seed.update_name_prefixes() - var/seed_potency = Hydro.seed.get_trait(TRAIT_POTENCY) - Hydro.seed.set_trait(TRAIT_POTENCY, seed_potency + 1, 200, 0) + Hydro.adjustPests(-10) + if(prob(BEE_POLLINATE_YIELD_CHANCE)) + Hydro.myseed.adjust_yield(1) + Hydro.yieldmod = 2 + if(prob(BEE_POLLINATE_POTENCY_CHANCE)) + Hydro.myseed.adjust_potency(1) if(beehome) beehome.bee_resources = min(beehome.bee_resources + growth, 100) @@ -234,7 +226,7 @@ idle = max(0, --idle) if(idle <= BEE_IDLE_GOHOME && prob(BEE_PROB_GOHOME)) if(!FindTarget()) - wanted_objects.Add(/obj/structure/beebox) //so we don't attack beeboxes when not going home + wanted_objects += /obj/structure/beebox //so we don't attack beeboxes when not going home target = beehome if(!beehome) //add outselves to a beebox (of the same reagent) if we have no home for(var/obj/structure/beebox/BB in view(vision_range, src)) @@ -304,6 +296,7 @@ /obj/item/queen_bee/Destroy() qdel(queen) + queen = null return ..() diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index a3739dfe088..1df7348a839 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -1,6 +1,5 @@ /mob/living/simple_animal/hostile faction = list("hostile") - mouse_opacity = 2 //This makes it easier to hit hostile mobs, you only need to click on their tile, and is set back to 1 when they die stop_automated_movement_when_pulled = 0 environment_smash = 1 //Set to 1 to break closets,tables,racks, etc; 2 for walls; 3 for rwalls var/atom/target @@ -13,7 +12,8 @@ var/list/friends = list() var/ranged_message = "fires" //Fluff text for ranged mobs var/ranged_cooldown = 0 //What the starting cooldown is on ranged attacks - var/ranged_cooldown_cap = 3 //What ranged attacks, after being used are set to, to go back on cooldown, defaults to 3 life() ticks + var/ranged_cooldown_time = 30 //How long, in deciseconds, the cooldown of ranged attacks is + var/ranged_ignores_vision = FALSE //if it'll fire ranged attacks even if it lacks vision on its target, only works with environment smash var/check_friendly_fire = 0 // Should the ranged mob check for friendlies when shooting var/retreat_distance = null //If our mob runs from players when they're too close, set in tile distance. By default, mobs do not retreat. var/minimum_distance = 1 //Minimum approach distance, so ranged mobs chase targets down, but still keep their distance set in tiles to the target, set higher to make mobs keep distance @@ -30,11 +30,33 @@ var/attack_same = 0 //Set us to 1 to allow us to attack our own faction, or 2, to only ever attack our own faction var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_IDLE (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever) + + //typecache of things this mob will attack in DestroySurroundings() if it has environment_smash + var/list/environment_target_typecache = list( + /obj/machinery/door/window, + /obj/structure/window, + /obj/structure/closet, + /obj/structure/table, + /obj/structure/grille, + /obj/structure/girder, + /obj/structure/rack, + /obj/structure/barricade) //turned into a typecache in New() + var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such + var/list/emote_taunt = list() + var/taunt_chance = 0 + +/mob/living/simple_animal/hostile/New() + ..() + if(!targets_from) + targets_from = src + environment_target_typecache = typecacheof(environment_target_typecache) + +/mob/living/simple_animal/hostile/Destroy() + targets_from = null + return ..() /mob/living/simple_animal/hostile/Life() . = ..() - if(ranged) - ranged_cooldown-- if(!.) walk(src, 0) return 0 @@ -58,76 +80,89 @@ if(AIShouldSleep(possible_targets)) // we try to acquire a new one AIStatus = AI_IDLE // otherwise we go idle return 1 + +/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user) + if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user) + FindTarget(list(user), 1) + return ..() + +/mob/living/simple_animal/hostile/bullet_act(obj/item/projectile/P) + if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client) + if(P.firer && get_dist(src, P.firer) <= aggro_vision_range) + FindTarget(list(P.firer), 1) + Goto(P.starting, move_to_delay, 3) + return ..() + //////////////HOSTILE MOB TARGETTING AND AGGRESSION//////////// /mob/living/simple_animal/hostile/proc/ListTargets()//Step 1, find out what we can see - var/list/L = list() + . = list() if(!search_objects) - var/list/Mobs = hearers(vision_range, src) - src //Remove self, so we don't suicide - L += Mobs - for(var/obj/mecha/M in mechas_list) - if(get_dist(M, src) <= vision_range && can_see(src, M, vision_range)) - L += M - for(var/obj/spacepod/S in spacepods_list) - if(get_dist(S, src) <= vision_range && can_see(src, S, vision_range)) - L += S + var/list/Mobs = hearers(vision_range, targets_from) - src //Remove self, so we don't suicide + . += Mobs + + var/static/hostile_machines = typecacheof(list(/obj/machinery/porta_turret, /obj/mecha, /obj/spacepod)) + + for(var/HM in typecache_filter_list(range(vision_range, targets_from), hostile_machines)) + if(can_see(targets_from, HM, vision_range)) + . += HM else - var/list/Objects = oview(vision_range, src) - L += Objects - return L + var/list/Objects = oview(vision_range, targets_from) + . += Objects /mob/living/simple_animal/hostile/proc/FindTarget(var/list/possible_targets, var/HasTargetsList = 0)//Step 2, filter down possible targets to things we actually care about - var/list/Targets = list() + . = list() if(!HasTargetsList) possible_targets = ListTargets() - for(var/atom/A in possible_targets) + for(var/pos_targ in possible_targets) + var/atom/A = pos_targ if(Found(A))//Just in case people want to override targetting - Targets = list(A) + . = list(A) break if(CanAttack(A))//Can we attack it? - Targets += A + . += A continue - var/Target = PickTarget(Targets) + var/Target = PickTarget(.) GiveTarget(Target) return Target //We now have a target + +/mob/living/simple_animal/hostile/proc/PossibleThreats() + . = list() + for(var/pos_targ in ListTargets()) + var/atom/A = pos_targ + if(Found(A)) + . = list(A) + break + if(CanAttack(A)) + . += A + continue /mob/living/simple_animal/hostile/proc/Found(var/atom/A)//This is here as a potential override to pick a specific target if available return -/mob/living/simple_animal/hostile/proc/PickTarget(var/list/Targets)//Step 3, pick amongst the possible, attackable targets - if(!Targets.len)//We didnt find nothin! - return +/mob/living/simple_animal/hostile/proc/PickTarget(list/Targets)//Step 3, pick amongst the possible, attackable targets if(target != null)//If we already have a target, but are told to pick again, calculate the lowest distance between all possible, and pick from the lowest distance targets - for(var/atom/A in Targets) - var/target_dist = get_dist(src, target) - var/possible_target_distance = get_dist(src, A) + for(var/pos_targ in Targets) + var/atom/A = pos_targ + var/target_dist = get_dist(targets_from, target) + var/possible_target_distance = get_dist(targets_from, A) if(target_dist < possible_target_distance) Targets -= A - var/chosen_target = safepick(Targets)//Pick the remaining targets (if any) at random + if(!Targets.len)//We didnt find nothin! + return + var/chosen_target = pick(Targets)//Pick the remaining targets (if any) at random return chosen_target /mob/living/simple_animal/hostile/CanAttack(var/atom/the_target)//Can we actually attack a possible target? + if(!the_target) + return 0 if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it return 0 if(search_objects < 2) - if(istype(the_target, /obj/mecha)) - var/obj/mecha/M = the_target - if(M.occupant)//Just so we don't attack empty mechs - if(CanAttack(M.occupant)) - return 1 - if(istype(the_target, /obj/spacepod)) - var/obj/spacepod/S = the_target - if(S.pilot) //Just so we don't attack empty pods - if(CanAttack(S.pilot)) - return 1 if(isliving(the_target)) var/mob/living/L = the_target - var/faction_check = 0 - for(var/F in faction) - if(F in L.faction) - faction_check = 1 - break + var/faction_check = faction_check(L) if(robust_searching) if(L.stat > stat_attack || L.stat != stat_attack && stat_exclusive == 1) return 0 @@ -141,14 +176,42 @@ if(faction_check && !attack_same) return 0 return 1 + + if(ishuman(the_target)) + var/mob/living/carbon/human/H = the_target + if(is_type_in_list(src, H.species.ignored_by)) + return 0 + + if(istype(the_target, /obj/mecha)) + var/obj/mecha/M = the_target + if(M.occupant)//Just so we don't attack empty mechs + if(CanAttack(M.occupant)) + return 1 + + if(istype(the_target, /obj/spacepod)) + var/obj/spacepod/S = the_target + if(S.pilot) //Just so we don't attack empty pods + if(CanAttack(S.pilot)) + return 1 + + if(istype(the_target, /obj/machinery/porta_turret)) + var/obj/machinery/porta_turret/P = the_target + if(P.faction in faction) + return 0 + if(!P.raised) //Don't attack invincible turrets + return 0 + if(P.stat & BROKEN) //Or turrets that are already broken + return 0 + return 1 if(isobj(the_target)) if(is_type_in_list(the_target, wanted_objects)) return 1 return 0 -/mob/living/simple_animal/hostile/proc/GiveTarget(var/new_target)//Step 4, give us our selected target +/mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target target = new_target if(target != null) + Aggro() return 1 /mob/living/simple_animal/hostile/proc/MoveToTarget(var/list/possible_targets)//Step 5, handle movement between us and our target @@ -157,38 +220,45 @@ LoseTarget() return 0 if(target in possible_targets) - var/target_distance = get_dist(src,target) - if(ranged)//We ranged? Shoot at em - if(target_distance >= 2 && ranged_cooldown <= 0)//But make sure they're a tile away at least, and our range attack is off cooldown + if(target.z != z) + LoseTarget() + return 0 + var/target_distance = get_dist(targets_from,target) + if(ranged) //We ranged? Shoot at em + if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown OpenFire(target) - if(retreat_distance != null)//If we have a retreat distance, check if we need to run from our target - if(target_distance <= retreat_distance)//If target's closer than our retreat distance, run + if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target + if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run walk_away(src,target,retreat_distance,move_to_delay) else - Goto(target,move_to_delay,minimum_distance)//Otherwise, get to our minimum distance so we chase them + Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them else Goto(target,move_to_delay,minimum_distance) - if(isturf(loc) && target.Adjacent(src)) //If they're next to us, attack - AttackingTarget() - return 1 + if(target) + if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack + AttackingTarget() + return 1 + return 0 if(environment_smash) - if(target.loc != null && get_dist(src, target.loc) <= vision_range)//We can't see our target, but he's in our vision range still - if(environment_smash >= 2)//If we're capable of smashing through walls, forget about vision completely after finding our target + if(target.loc != null && get_dist(targets_from, target.loc) <= vision_range) //We can't see our target, but he's in our vision range still + if(ranged_ignores_vision && ranged_cooldown <= world.time) //we can't see our target... but we can fire at them! + OpenFire(target) + if(environment_smash >= 2) //If we're capable of smashing through walls, forget about vision completely after finding our target Goto(target,move_to_delay,minimum_distance) FindHidden() return 1 else if(FindHidden()) return 1 - LostTarget() + LoseTarget() return 0 -/mob/living/simple_animal/hostile/proc/Goto(var/target, var/delay, var/minimum_distance) +/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance) walk_to(src, target, minimum_distance, delay) /mob/living/simple_animal/hostile/adjustHealth(damage) - ..(damage) - if(!stat && search_objects < 3)//Not unconscious, and we don't ignore mobs + . = ..() + if(!ckey && !stat && search_objects < 3 && damage > 0)//Not unconscious, and we don't ignore mobs if(search_objects)//Turn off item searching and ignore whatever item we were looking at, we're more concerned with fight or flight search_objects = 0 target = null @@ -203,101 +273,102 @@ /mob/living/simple_animal/hostile/proc/Aggro() vision_range = aggro_vision_range + if(target && emote_taunt.len && prob(taunt_chance)) + emote("me", 1, "[pick(emote_taunt)] at [target].") + taunt_chance = max(taunt_chance-7,2) /mob/living/simple_animal/hostile/proc/LoseAggro() stop_automated_movement = 0 vision_range = idle_vision_range + taunt_chance = initial(taunt_chance) /mob/living/simple_animal/hostile/proc/LoseTarget() target = null walk(src, 0) LoseAggro() -/mob/living/simple_animal/hostile/proc/LostTarget() - walk(src, 0) - LoseAggro() - //////////////END HOSTILE MOB TARGETTING AND AGGRESSION//////////// /mob/living/simple_animal/hostile/death(gibbed) - LoseAggro() - mouse_opacity = 1 - ..() - walk(src, 0) + LoseTarget() + ..(gibbed) + +/mob/living/simple_animal/hostile/proc/summon_backup(distance) + do_alert_animation(src) + playsound(loc, 'sound/machines/chime.ogg', 50, 1, -1) + for(var/mob/living/simple_animal/hostile/M in oview(distance, targets_from)) + var/list/L = M.faction&faction + if(L.len) + if(M.AIStatus == AI_OFF) + return + else + M.Goto(src,M.move_to_delay,M.minimum_distance) -/mob/living/simple_animal/hostile/proc/OpenFire(var/the_target) - var/target = the_target +/mob/living/simple_animal/hostile/proc/OpenFire(atom/A) if(check_friendly_fire) - for(var/turf/T in getline(src, target)) // Not 100% reliable but this is faster than simulating actual trajectory + for(var/turf/T in getline(src,A)) // Not 100% reliable but this is faster than simulating actual trajectory for(var/mob/living/L in T) - if(L == src || L == target) + if(L == src || L == A) continue if(faction_check(L) && !attack_same) return - visible_message("[src] [ranged_message] at [target]!") + visible_message("[src] [ranged_message] at [A]!") if(rapid) spawn(1) - Shoot(target, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) + Shoot(A) spawn(4) - Shoot(target, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) + Shoot(A) spawn(6) - Shoot(target, src.loc, src) - if(casingtype) - new casingtype(get_turf(src)) + Shoot(A) else - Shoot(target, src.loc, src) - if(casingtype) - new casingtype - ranged_cooldown = ranged_cooldown_cap - return + Shoot(A) + ranged_cooldown = world.time + ranged_cooldown_time -/mob/living/simple_animal/hostile/proc/Shoot(var/target, var/start, var/user, var/bullet = 0) - if(target == start) +/mob/living/simple_animal/hostile/proc/Shoot(atom/targeted_atom) + if(targeted_atom == targets_from.loc || targeted_atom == targets_from) return + var/turf/startloc = get_turf(targets_from) if(casingtype) - var/obj/item/ammo_casing/casing = new casingtype + var/obj/item/ammo_casing/casing = new casingtype(startloc) playsound(src, projectilesound, 100, 1) - casing.fire(target, src, zone_override = ran_zone()) - casing.loc = loc - else - var/obj/item/projectile/A = new projectiletype(loc) - playsound(user, projectilesound, 100, 1) - A.current = target - A.firer = src - A.yo = target:y - start:y - A.xo = target:x - start:x - if(AIStatus == AI_OFF)//Don't want mindless mobs to have their movement screwed up firing in space - newtonian_move(get_dir(target, user)) - A.original = target - A.fire() - return A + casing.fire(targeted_atom, src, zone_override = ran_zone()) + else if(projectiletype) + var/obj/item/projectile/P = new projectiletype(startloc) + playsound(src, projectilesound, 100, 1) + P.current = startloc + P.starting = startloc + P.firer = src + P.yo = targeted_atom.y - startloc.y + P.xo = targeted_atom.x - startloc.x + if(AIStatus != AI_ON)//Don't want mindless mobs to have their movement screwed up firing in space + newtonian_move(get_dir(targeted_atom, targets_from)) + P.original = targeted_atom + P.fire() + return P /mob/living/simple_animal/hostile/proc/DestroySurroundings() if(environment_smash) EscapeConfinement() for(var/dir in cardinal) - var/turf/T = get_step(src, dir) - if(istype(T, /turf/simulated/wall) || istype(T, /turf/simulated/mineral)) - if(T.Adjacent(src)) + var/turf/T = get_step(targets_from, dir) + if(iswallturf(T) || ismineralturf(T)) + if(T.Adjacent(targets_from)) T.attack_animal(src) - for(var/atom/A in T) - if(!A.Adjacent(src)) + for(var/a in T) + var/atom/A = a + if(!A.Adjacent(targets_from)) continue - if(istype(A, /obj/structure/window) || istype(A, /obj/structure/closet) || istype(A, /obj/structure/table) || istype(A, /obj/structure/grille) || istype(A, /obj/structure/rack)) + if(is_type_in_typecache(A, environment_target_typecache)) A.attack_animal(src) return /mob/living/simple_animal/hostile/proc/EscapeConfinement() if(buckled) buckled.attack_animal(src) - if(!isturf(src.loc) && src.loc != null)//Did someone put us in something? - var/atom/A = src.loc + if(!isturf(targets_from.loc) && targets_from.loc != null)//Did someone put us in something? + var/atom/A = get_turf(targets_from) A.attack_animal(src)//Bang on it till we get out return @@ -305,12 +376,12 @@ if(istype(target.loc, /obj/structure/closet) || istype(target.loc, /obj/machinery/disposal) || istype(target.loc, /obj/machinery/sleeper) || istype(target.loc, /obj/machinery/bodyscanner) || istype(target.loc, /obj/machinery/recharge_station)) var/atom/A = target.loc Goto(A,move_to_delay,minimum_distance) - if(A.Adjacent(src)) + if(A.Adjacent(targets_from)) A.attack_animal(src) return 1 -/mob/living/simple_animal/hostile/RangedAttack(var/atom/A, var/params) //Player firing - if(ranged && ranged_cooldown <= 0) +/mob/living/simple_animal/hostile/RangedAttack(atom/A, params) //Player firing + if(ranged && ranged_cooldown <= world.time) target = A OpenFire(A) ..() diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm new file mode 100644 index 00000000000..85a351606f6 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -0,0 +1,393 @@ +#define MEDAL_PREFIX "Bubblegum" + +/* + +BUBBLEGUM + +Bubblegum spawns randomly wherever a lavaland creature is able to spawn. It is the most powerful slaughter demon in existence. +Bubblegum's footsteps are heralded by shaking booms, proving its tremendous size. + +It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage. + +It tries to strike at its target through any bloodpools under them; if it fails to do that, it will spray blood and then attempt to warp to a bloodpool near the target. +If it fails to warp to a target, it may summon up to 6 slaughterlings from the blood around it. +If it does not summon all 6 slaughterlings, it will instead charge at its target, dealing massive damage to anything it hits and spraying a stream of blood. +At half health, it will either charge three times or warp, then charge, instead of doing a single charge. + +When Bubblegum dies, it leaves behind a chest that can contain three things: + 1. A bottle that, when activated, drives everyone nearby into a frenzy + 2. A contract that marks for death the chosen target + 3. A spellblade that can slice off limbs at range + +Difficulty: Hard + +*/ + +/mob/living/simple_animal/hostile/megafauna/bubblegum + name = "bubblegum" + desc = "In what passes for a heirarchy among slaughter demons, this one is king." + health = 2500 + maxHealth = 2500 + attacktext = "rends" + attack_sound = 'sound/misc/demon_attack1.ogg' + icon_state = "bubblegum" + icon_living = "bubblegum" + icon_dead = "" + friendly = "stares down" + icon = 'icons/mob/lavaland/96x96megafauna.dmi' + speak_emote = list("gurgles") + armour_penetration = 40 + melee_damage_lower = 40 + melee_damage_upper = 40 + speed = 1 + move_to_delay = 10 + ranged = 1 + pixel_x = -32 + del_on_death = 1 + loot = list(/obj/structure/closet/crate/necropolis/bubblegum) + var/charging = 0 + medal_type = MEDAL_PREFIX + score_type = BUBBLEGUM_SCORE + deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... " + death_sound = 'sound/misc/enter_blood.ogg' + +/obj/item/device/gps/internal/bubblegum + icon_state = null + gpstag = "Bloody Signal" + desc = "You're not quite sure how a signal can be bloody." + invisibility = 100 + +/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount) + if(amount > 0 && prob(25)) + var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc) + if(prob(40)) + step(B, pick(cardinal)) + else + B.dir = pick(cardinal) + . = ..() + +/obj/effect/decal/cleanable/blood/gibs/bubblegum + name = "thick blood" + desc = "Thick, splattered blood." + random_icon_states = list("gib3", "gib5", "gib6") + amount = 20 + +/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in() + return TRUE + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Life() + ..() + move_to_delay = Clamp((health/maxHealth) * 10, 5, 10) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire() + anger_modifier = Clamp(((maxHealth - health)/60),0,20) + if(charging) + return + ranged_cooldown = world.time + ranged_cooldown_time + + var/warped = FALSE + if(!try_bloodattack()) + spawn(0) + blood_spray() + warped = blood_warp() + if(warped && prob(100 - anger_modifier)) + return + + if(prob(90 - anger_modifier) || slaughterlings()) + if(health > maxHealth * 0.5) + spawn(0) + charge() + else + if(prob(70) || warped) + spawn(0) + triple_charge() + else + spawn(0) + warp_charge() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/New() + ..() + for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in mob_list) + if(B != src) + qdel(src) //There can be only one + break + var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new + AddSpell(bloodspell) + if(istype(loc, /obj/effect/dummy/slaughter)) + bloodspell.phased = 1 + internal_gps = new/obj/item/device/gps/internal/bubblegum(src) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon) + if(!charging) + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget() + if(!charging) + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance) + if(!charging) + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Move() + if(charging) + new /obj/effect/overlay/temp/decoy/fading(loc, src) + DestroySurroundings() + . = ..() + if(!stat && .) + playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1) + if(charging) + DestroySurroundings() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/warp_charge() + blood_warp() + charge() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge() + charge() + charge() + charge() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge() + var/turf/T = get_turf(target) + if(!T || T == loc) + return + new /obj/effect/overlay/temp/dragon_swoop(T) + charging = 1 + DestroySurroundings() + walk(src, 0) + dir = get_dir(src, T) + var/obj/effect/overlay/temp/decoy/D = new /obj/effect/overlay/temp/decoy(loc, src) + animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 5) + sleep(5) + throw_at(T, get_dist(src, T), 1, src, 0) + charging = 0 + try_bloodattack() + if(target) + Goto(target, move_to_delay, minimum_distance) + + +/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A) + if(charging) + if(isturf(A) || isobj(A) && A.density) + A.ex_act(2) + DestroySurroundings() + ..() + +/mob/living/simple_animal/hostile/megafauna/bubblegum/throw_impact(atom/A) + if(!charging) + return ..() + + else if(isliving(A)) + var/mob/living/L = A + L.visible_message("[src] slams into [L]!", "[src] slams into you!") + L.apply_damage(40, BRUTE) + playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1) + shake_camera(L, 4, 3) + shake_camera(src, 2, 3) + var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src))) + L.throw_at_fast(throwtarget, 3) + + charging = 0 + + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood() + var/list/targets = ListTargets() + . = list() + for(var/mob/living/L in targets) + var/list/bloodpool = get_pools(get_turf(L), 0) + if(bloodpool.len && (!faction_check(L) || L.stat == DEAD)) + . += L + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack() + var/list/targets = get_mobs_on_blood() + if(targets.len) + spawn(0) + bloodattack(targets, prob(50)) + + return TRUE + return FALSE + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodattack(list/targets, handedness) + var/mob/living/target_one = pick_n_take(targets) + var/turf/target_one_turf = get_turf(target_one) + var/mob/living/target_two + if(targets.len) + target_two = pick_n_take(targets) + var/turf/target_two_turf = get_turf(target_two) + if(target_two.stat != CONSCIOUS || prob(10)) + bloodgrab(target_two_turf, handedness) + else + bloodsmack(target_two_turf, handedness) + + if(target_one) + var/list/pools = get_pools(get_turf(target_one), 0) + if(pools.len) + target_one_turf = get_turf(target_one) + if(target_one_turf) + if(target_one.stat != CONSCIOUS || prob(10)) + bloodgrab(target_one_turf, !handedness) + else + bloodsmack(target_one_turf, !handedness) + + if(!target_two && target_one) + var/list/poolstwo = get_pools(get_turf(target_one), 0) + if(poolstwo.len) + target_one_turf = get_turf(target_one) + if(target_one_turf) + if(target_one.stat != CONSCIOUS || prob(10)) + bloodgrab(target_one_turf, handedness) + else + bloodsmack(target_one_turf, handedness) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodsmack(turf/T, handedness) + if(handedness) + new /obj/effect/overlay/temp/bubblegum_hands/rightsmack(T) + else + new /obj/effect/overlay/temp/bubblegum_hands/leftsmack(T) + sleep(2.5) + for(var/mob/living/L in T) + if(!faction_check(L)) + to_chat(L, "[src] rends you!") + playsound(T, attack_sound, 100, 1, -1) + var/limb_to_hit = pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg") + L.apply_damage(25, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration)) + sleep(3) + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodgrab(turf/T, handedness) + if(handedness) + new /obj/effect/overlay/temp/bubblegum_hands/rightpaw(T) + new /obj/effect/overlay/temp/bubblegum_hands/rightthumb(T) + else + new /obj/effect/overlay/temp/bubblegum_hands/leftpaw(T) + new /obj/effect/overlay/temp/bubblegum_hands/leftthumb(T) + sleep(6) + for(var/mob/living/L in T) + if(!faction_check(L)) + to_chat(L, "[src] drags you through the blood!") + playsound(T, 'sound/misc/enter_blood.ogg', 100, 1, -1) + var/turf/targetturf = get_step(src, dir) + L.forceMove(targetturf) + playsound(targetturf, 'sound/misc/exit_blood.ogg', 100, 1, -1) + if(L.stat != CONSCIOUS) + spawn(2) + devour(L) + sleep(1) + +/obj/effect/overlay/temp/bubblegum_hands + icon = 'icons/effects/bubblegum.dmi' + duration = 9 + +/obj/effect/overlay/temp/bubblegum_hands/rightthumb + icon_state = "rightthumbgrab" + +/obj/effect/overlay/temp/bubblegum_hands/leftthumb + icon_state = "leftthumbgrab" + +/obj/effect/overlay/temp/bubblegum_hands/rightpaw + icon_state = "rightpawgrab" + layer = MOB_LAYER - 0.1 + +/obj/effect/overlay/temp/bubblegum_hands/leftpaw + icon_state = "leftpawgrab" + layer = MOB_LAYER - 0.1 + +/obj/effect/overlay/temp/bubblegum_hands/rightsmack + icon_state = "rightsmack" + +/obj/effect/overlay/temp/bubblegum_hands/leftsmack + icon_state = "leftsmack" + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp() + if(Adjacent(target)) + return FALSE + var/list/can_jaunt = get_pools(get_turf(src), 1) + if(!can_jaunt.len) + return FALSE + + var/list/pools = get_pools(get_turf(target), 2) + var/list/pools_to_remove = get_pools(get_turf(target), 1) + pools -= pools_to_remove + if(!pools.len) + return FALSE + + var/obj/effect/overlay/temp/decoy/DA = new /obj/effect/overlay/temp/decoy(loc, src) + DA.color = "#FF0000" + var/oldtransform = DA.transform + DA.transform = matrix()*2 + animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 5) + sleep(5) + qdel(DA) + + var/obj/effect/decal/cleanable/blood/found_bloodpool + pools = get_pools(get_turf(target), 2) + pools_to_remove = get_pools(get_turf(target), 1) + pools -= pools_to_remove + if(pools.len) + shuffle(pools) + found_bloodpool = pick(pools) + if(found_bloodpool) + visible_message("[src] sinks into the blood...") + playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, 1, -1) + forceMove(get_turf(found_bloodpool)) + playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, 1, -1) + visible_message("And springs back out!") + return TRUE + return FALSE + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range) + . = list() + for(var/obj/effect/decal/cleanable/nearby in view(T, range)) + if(nearby.can_bloodcrawl_in()) + . += nearby + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_spray() + visible_message("[src] sprays a stream of gore!") + var/range = 6 + round(anger_modifier * 0.4) + var/turf/previousturf = get_turf(src) + var/turf/J = previousturf + var/targetdir = get_dir(src, target) + if(target.loc == loc) + targetdir = dir + face_atom(target) + new /obj/effect/decal/cleanable/blood/bubblegum(J) + for(var/i in 1 to range) + J = get_step(previousturf, targetdir) + new /obj/effect/overlay/temp/dir_setting/bloodsplatter(previousturf, get_dir(previousturf, J)) + playsound(previousturf,'sound/effects/splat.ogg', 100, 1, -1) + if(!J || !previousturf.CanAtmosPass(J)) + break + new /obj/effect/decal/cleanable/blood/bubblegum(J) + previousturf = J + sleep(1) + +/obj/effect/decal/cleanable/blood/bubblegum + amount = 0 + +/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in() + return TRUE + +/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/slaughterlings() + visible_message("[src] summons a shoal of slaughterlings!") + var/max_amount = 6 + for(var/H in get_pools(get_turf(src), 1)) + if(!max_amount) + break + max_amount-- + var/obj/effect/decal/cleanable/blood/B = H + new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter(B.loc) + return max_amount + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter + name = "slaughterling" + desc = "Though not yet strong enough to create a true physical form, it's nonetheless determined to murder you." + density = 0 + faction = list("mining", "boss") + weather_immunities = list("lava","ash") + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter/CanPass(atom/movable/mover, turf/target, height = 0) + if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) + return 1 + return 0 + +#undef MEDAL_PREFIX \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm new file mode 100644 index 00000000000..b40e64c6a31 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -0,0 +1,266 @@ + +#define MEDAL_PREFIX "Colossus" +/* + +COLOSSUS + +The colossus spawns randomly wherever a lavaland creature is able to spawn. It is powerful, ancient, and extremely deadly. +The colossus has a degree of sentience, proving this in speech during its attacks. + +It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage. + +The colossus' true danger lies in its ranged capabilities. It fires immensely damaging death bolts that penetrate all armor in a variety of ways: + 1. The colossus fires death bolts in alternating patterns: the cardinal directions and the diagonal directions. + 2. The colossus fires death bolts in a shotgun-like pattern, instantly downing anything unfortunate enough to be hit by all of them. + 3. The colossus fires a spiral of death bolts. +At 33% health, the colossus gains an additional attack: + 4. The colossus fires two spirals of death bolts, spinning in opposite directions. + +When a colossus dies, it leaves behind a chunk of glowing crystal known as a black box. Anything placed inside will carry over into future rounds. +For instance, you could place a bag of holding into the black box, and then kill another colossus next round and retrieve the bag of holding from inside. + +Difficulty: Very Hard + +*/ + +/mob/living/simple_animal/hostile/megafauna/colossus + name = "colossus" + desc = "A monstrous creature protected by heavy shielding." + health = 2500 + maxHealth = 2500 + attacktext = "judges" + attack_sound = 'sound/magic/ratvar_attack.ogg' + icon_state = "eva" + icon_living = "eva" + icon_dead = "dragon_dead" + friendly = "stares down" + icon = 'icons/mob/lavaland/96x96megafauna.dmi' + speak_emote = list("roars") + armour_penetration = 40 + melee_damage_lower = 40 + melee_damage_upper = 40 + speed = 1 + move_to_delay = 10 + ranged = 1 + pixel_x = -32 + del_on_death = 1 + medal_type = MEDAL_PREFIX + score_type = COLOSSUS_SCORE + loot = list(/obj/machinery/anomalous_crystal/random, /obj/item/organ/internal/vocal_cords/colossus) + butcher_results = list(/obj/item/weapon/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30) + deathmessage = "disintegrates, leaving a glowing core in its wake." + death_sound = 'sound/misc/demon_dies.ogg' + +/mob/living/simple_animal/hostile/megafauna/colossus/devour(mob/living/L) + visible_message("[src] disintegrates [L]!") + L.dust() + +/mob/living/simple_animal/hostile/megafauna/colossus/OpenFire() + anger_modifier = Clamp(((maxHealth - health)/50),0,20) + ranged_cooldown = world.time + 120 + + if(enrage(target)) + if(move_to_delay == initial(move_to_delay)) + visible_message("\"You can't dodge.\"") + ranged_cooldown = world.time + 30 + telegraph() + dir_shots(alldirs) + move_to_delay = 3 + return + else + move_to_delay = initial(move_to_delay) + + if(prob(20+anger_modifier)) //Major attack + telegraph() + + if(health < maxHealth/3) + double_spiral() + else + visible_message("\"Judgement.\"") + spawn(0) + spiral_shoot(rand(0, 1)) + + else if(prob(20)) + ranged_cooldown = world.time + 30 + random_shots() + else + if(prob(70)) + ranged_cooldown = world.time + 20 + blast() + else + ranged_cooldown = world.time + 40 + spawn(0) + alternating_dir_shots() + + +/mob/living/simple_animal/hostile/megafauna/colossus/New() + ..() + internal_gps = new/obj/item/device/gps/internal/colossus(src) + +/obj/effect/overlay/temp/at_shield + name = "anti-toolbox field" + desc = "A shimmering forcefield protecting the colossus." + icon = 'icons/effects/effects.dmi' + icon_state = "at_shield2" + layer = FLY_LAYER + luminosity = 2 + duration = 8 + var/target + +/obj/effect/overlay/temp/at_shield/New(new_loc, new_target) + ..() + target = new_target + spawn(0) + orbit(target, 0, FALSE, 0, 0, FALSE, TRUE) + +/mob/living/simple_animal/hostile/megafauna/colossus/bullet_act(obj/item/projectile/P) + if(!stat) + var/obj/effect/overlay/temp/at_shield/AT = new /obj/effect/overlay/temp/at_shield(loc, src) + var/random_x = rand(-32, 32) + AT.pixel_x += random_x + + var/random_y = rand(0, 72) + AT.pixel_y += random_y + ..() + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.martial_art && prob(H.martial_art.deflection_chance)) + . = TRUE + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots() + dir_shots(diagonals) + sleep(10) + dir_shots(cardinal) + sleep(10) + dir_shots(diagonals) + sleep(10) + dir_shots(cardinal) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/double_spiral() + visible_message("\"Die.\"") + + sleep(10) + spawn(0) + spiral_shoot() + spawn(0) + spiral_shoot(1) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = 0, counter_start = 1) + var/counter = counter_start + var/turf/marker + for(var/i in 1 to 80) + switch(counter) + if(1) + marker = locate(x, y - 2, z) + if(2) + marker = locate(x - 1, y - 2, z) + if(3) + marker = locate(x - 2, y - 2, z) + if(4) + marker = locate(x - 2, y - 1, z) + if(5) + marker = locate(x - 2, y, z) + if(6) + marker = locate(x - 2, y + 1, z) + if(7) + marker = locate(x - 2, y + 2, z) + if(8) + marker = locate(x - 1, y + 2, z) + if(9) + marker = locate(x, y + 2, z) + if(10) + marker = locate(x + 1, y + 2, z) + if(11) + marker = locate(x + 2, y + 2, z) + if(12) + marker = locate(x + 2, y + 1, z) + if(13) + marker = locate(x + 2, y, z) + if(14) + marker = locate(x + 2, y - 1, z) + if(15) + marker = locate(x + 2, y - 2, z) + if(16) + marker = locate(x + 1, y - 2, z) + + if(negative) + counter-- + else + counter++ + if(counter > 16) + counter = 1 + if(counter < 1) + counter = 16 + shoot_projectile(marker) + playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 20, 1) + sleep(1) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/shoot_projectile(turf/marker) + if(!marker || marker == loc) + return + var/turf/startloc = get_turf(src) + var/obj/item/projectile/P = new /obj/item/projectile/colossus(startloc) + P.current = startloc + P.starting = startloc + P.firer = src + P.yo = marker.y - startloc.y + P.xo = marker.x - startloc.x + if(target) + P.original = target + else + P.original = marker + P.fire() + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/random_shots() + var/turf/U = get_turf(src) + playsound(U, 'sound/magic/invoke_general.ogg', 300, 1, 5) + for(var/T in RANGE_TURFS(12, U) - U) + if(prob(5)) + shoot_projectile(T) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/blast() + playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 200, 1, 2) + var/turf/T = get_turf(target) + newtonian_move(get_dir(T, targets_from)) + for(var/turf/turf in range(1, T)) + shoot_projectile(turf) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs) + if(!islist(dirs)) + dirs = alldirs.Copy() + playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 200, 1, 2) + for(var/d in dirs) + var/turf/E = get_step(src, d) + shoot_projectile(E) + +/mob/living/simple_animal/hostile/megafauna/colossus/proc/telegraph() + for(var/mob/M in range(10,src)) + if(M.client) + flash_color(M.client, rgb(200, 0, 0), 1) + shake_camera(M, 4, 3) + playsound(get_turf(src),'sound/magic/narsie_attack.ogg', 200, 1) + +/obj/item/projectile/colossus + name ="death bolt" + icon_state= "chronobolt" + damage = 25 + armour_penetration = 100 + speed = 2 + eyeblur = 0 + damage_type = BRUTE + pass_flags = PASSTABLE + +/obj/item/projectile/colossus/on_hit(atom/target, blocked = 0) + . = ..() + if(isturf(target) || isobj(target)) + target.ex_act(2) + +/obj/item/device/gps/internal/colossus + icon_state = null + gpstag = "Angelic Signal" + desc = "Get in the fucking robot." + invisibility = 100 + +#undef MEDAL_PREFIX diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm new file mode 100644 index 00000000000..5afa7613c78 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm @@ -0,0 +1,278 @@ +#define MEDAL_PREFIX "Drake" +/* + +ASH DRAKE + +Ash drakes spawn randomly wherever a lavaland creature is able to spawn. They are the draconic guardians of the Necropolis. + +It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage. + +Whenever possible, the drake will breathe fire in the four cardinal directions, igniting and heavily damaging anything caught in the blast. +It also often causes fire to rain from the sky - many nearby turfs will flash red as a fireball crashes into them, dealing damage to anything on the turfs. +The drake also utilizes its wings to fly into the sky and crash down onto a specified point. Anything on this point takes tremendous damage. + - Sometimes it will chain these swooping attacks over and over, making swiftness a necessity. + +When an ash drake dies, it leaves behind a chest that can contain four things: + 1. A spectral blade that allows its wielder to call ghosts to it, enhancing its power + 2. A lava staff that allows its wielder to create lava + 3. A spellbook and wand of fireballs + 4. A bottle of dragon's blood with several effects, including turning its imbiber into a drake themselves. + +When butchered, they leave behind diamonds, sinew, bone, and ash drake hide. Ash drake hide can be used to create a hooded cloak that protects its wearer from ash storms. + +Difficulty: Medium + +*/ + +/mob/living/simple_animal/hostile/megafauna/dragon + name = "ash drake" + desc = "Guardians of the necropolis." + health = 2500 + maxHealth = 2500 + attacktext = "chomps" + attack_sound = 'sound/misc/demon_attack1.ogg' + icon_state = "dragon" + icon_living = "dragon" + icon_dead = "dragon_dead" + friendly = "stares down" + icon = 'icons/mob/lavaland/dragon.dmi' + speak_emote = list("roars") + armour_penetration = 40 + melee_damage_lower = 40 + melee_damage_upper = 40 + speed = 1 + move_to_delay = 10 + ranged = 1 + pixel_x = -16 + loot = list(/obj/structure/closet/crate/necropolis/dragon) + butcher_results = list(/obj/item/weapon/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30) + var/swooping = 0 + var/swoop_cooldown = 0 + medal_type = MEDAL_PREFIX + score_type = DRAKE_SCORE + deathmessage = "collapses into a pile of bones, its flesh sloughing away." + death_sound = 'sound/misc/demon_dies.ogg' + +/mob/living/simple_animal/hostile/megafauna/dragon/New() + ..() + internal_gps = new/obj/item/device/gps/internal/dragon(src) + +/mob/living/simple_animal/hostile/megafauna/dragon/ex_act(severity, target) + if(severity == 3) + return + ..() + +/mob/living/simple_animal/hostile/megafauna/dragon/adjustHealth(amount) + if(swooping) + return 0 + return ..() + +/mob/living/simple_animal/hostile/megafauna/dragon/AttackingTarget() + if(!swooping) + ..() + +/mob/living/simple_animal/hostile/megafauna/dragon/DestroySurroundings() + if(!swooping) + ..() + +/mob/living/simple_animal/hostile/megafauna/dragon/Move() + if(!swooping) + ..() + +/mob/living/simple_animal/hostile/megafauna/dragon/Goto(target, delay, minimum_distance) + if(!swooping) + ..() + +/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = 0) + return 1 + +/obj/effect/overlay/temp/fireball + icon = 'icons/obj/wizard.dmi' + icon_state = "fireball" + name = "fireball" + desc = "Get out of the way!" + layer = 6 + randomdir = 0 + duration = 12 + pixel_z = 500 + +/obj/effect/overlay/temp/fireball/New(loc) + ..() + animate(src, pixel_z = 0, time = 12) + +/obj/effect/overlay/temp/target + icon = 'icons/mob/actions.dmi' + icon_state = "sniper_zoom" + layer = MOB_LAYER - 0.1 + luminosity = 2 + duration = 12 + +/obj/effect/overlay/temp/dragon_swoop + name = "certain death" + desc = "Don't just stand there, move!" + icon = 'icons/effects/96x96.dmi' + icon_state = "landing" + layer = MOB_LAYER - 0.1 + pixel_x = -32 + pixel_y = -32 + color = "#FF0000" + duration = 10 + +/obj/effect/overlay/temp/target/ex_act() + return + +/obj/effect/overlay/temp/target/New(loc) + ..() + spawn(0) + fall() + +/obj/effect/overlay/temp/target/proc/fall() + var/turf/T = get_turf(src) + playsound(T,'sound/magic/Fireball.ogg', 200, 1) + new /obj/effect/overlay/temp/fireball(T) + sleep(12) + explosion(T, 0, 0, 1, 0, 0, 0, 1) + +/mob/living/simple_animal/hostile/megafauna/dragon/OpenFire() + anger_modifier = Clamp(((maxHealth - health)/50),0,20) + ranged_cooldown = world.time + ranged_cooldown_time + if(swooping) + fire_rain() + return + + if(prob(15 + anger_modifier) && !client) + if(health < maxHealth/2) + spawn(0) + swoop_attack(1) + else + fire_rain() + + else if(prob(10+anger_modifier) && !client) + if(health > maxHealth/2) + spawn(0) + swoop_attack(0) + else + spawn(0) + triple_swoop(0) + else + fire_walls() + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_rain() + visible_message("Fire rains from the sky!") + for(var/turf/turf in range(12,get_turf(src))) + if(prob(10)) + new /obj/effect/overlay/temp/target(turf) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_walls() + playsound(get_turf(src),'sound/magic/Fireball.ogg', 200, 1) + + for(var/d in cardinal) + spawn(0) + fire_wall(d) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_wall(dir) + var/list/hit_things = list(src) + var/turf/E = get_edge_target_turf(src, dir) + var/range = 10 + var/turf/previousturf = get_turf(src) + for(var/turf/J in getline(src,E)) + if(!range || !previousturf.CanAtmosPass(J)) + break + range-- + new /obj/effect/hotspot(J) + J.hotspot_expose(700,50,1) + for(var/mob/living/L in J.contents - hit_things) + L.adjustFireLoss(20) + to_chat(L, "You're hit by the drake's fire breath!") + hit_things += L + previousturf = J + sleep(1) + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/triple_swoop() + swoop_attack() + swoop_attack() + swoop_attack() + +/mob/living/simple_animal/hostile/megafauna/dragon/proc/swoop_attack(fire_rain = 0, atom/movable/manual_target) + if(stat || swooping) + return + swoop_cooldown = world.time + 200 + var/atom/swoop_target + if(manual_target) + swoop_target = manual_target + else + swoop_target = target + stop_automated_movement = TRUE + swooping = 1 + density = 0 + icon_state = "swoop" + visible_message("[src] swoops up high!") + if(prob(50)) + animate(src, pixel_x = 500, pixel_z = 500, time = 10) + else + animate(src, pixel_x = -500, pixel_z = 500, time = 10) + sleep(30) + + var/turf/tturf + if(fire_rain) + fire_rain() + + icon_state = "dragon" + if(swoop_target && !qdeleted(swoop_target) && swoop_target.z == src.z) + tturf = get_turf(swoop_target) + else + tturf = get_turf(src) + forceMove(tturf) + new /obj/effect/overlay/temp/dragon_swoop(tturf) + animate(src, pixel_x = initial(pixel_x), pixel_z = 0, time = 10) + sleep(10) + playsound(src.loc, 'sound/effects/meteorimpact.ogg', 200, 1) + for(var/mob/living/L in orange(1, src)) + if(L.stat) + visible_message("[src] slams down on [L], crushing them!") + L.gib() + else + L.adjustBruteLoss(75) + if(L && !qdeleted(L)) // Some mobs are deleted on death + var/throw_dir = get_dir(src, L) + if(L.loc == loc) + throw_dir = pick(alldirs) + var/throwtarget = get_edge_target_turf(src, throw_dir) + L.throw_at_fast(throwtarget, 3) + visible_message("[L] is thrown clear of [src]!") + + for(var/mob/M in range(7, src)) + shake_camera(M, 15, 1) + + stop_automated_movement = FALSE + swooping = 0 + density = 1 + +/mob/living/simple_animal/hostile/megafauna/dragon/AltClickOn(atom/movable/A) + if(!istype(A)) + return + if(swoop_cooldown >= world.time) + to_chat(src, "You need to wait 20 seconds between swoop attacks!") + return + swoop_attack(1, A) + +/obj/item/device/gps/internal/dragon + icon_state = null + gpstag = "Fiery Signal" + desc = "Here there be dragons." + invisibility = 100 + +/mob/living/simple_animal/hostile/megafauna/dragon/lesser + name = "lesser ash drake" + maxHealth = 200 + health = 200 + faction = list("neutral") + melee_damage_upper = 30 + melee_damage_lower = 30 + damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) + loot = list() + +/mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype) + return + +#undef MEDAL_PREFIX \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm new file mode 100644 index 00000000000..dfb9948a1e3 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -0,0 +1,611 @@ +#define MEDAL_PREFIX "Hierophant" +/* + +The Hierophant + +The Hierophant spawns in its arena, an area designed to make it harder to fight than it would otherwise be. + +The text this boss speaks is ROT4, use ROT22 to decode + +The Hierophant's attacks are as follows, and INTENSIFY at a random chance based on Hierophant's health; +- Creates a cardinal or diagonal blast(Cross Blast) under its target, exploding after a short time. + INTENSITY EFFECT: Creates one of the cross blast types under itself instead of under the target. + INTENSITY EFFECT: The created Cross Blast fires in all directions if below half health. +- If no chasers exist, creates a chaser that will seek its target, leaving a trail of blasts. + INTENSITY EFFECT: Creates a second, slower chaser. +- Creates an expanding AoE burst. +- INTENSE ATTACKS: + If target is at least 2 tiles away; Blinks to the target after a very brief delay, damaging everything near the start and end points. + As above, but does so multiple times if below half health. + Rapidly creates Cross Blasts under a target. + If chasers are off cooldown, creates four high-speed chasers. +- IF TARGET WAS STRUCK IN MELEE: Creates a 3x3 square of blasts under the target. + +Cross Blasts and the AoE burst gain additional range as the Hierophant loses health, while Chasers gain additional speed. + +When The Hierophant dies, it leaves behind its staff, which, while much weaker than when wielded by The Hierophant itself, is still quite effective. +- The staff can place a teleport rune, allowing the user to teleport themself and their allies to the rune. + +Difficulty: Hard + +*/ + +/mob/living/simple_animal/hostile/megafauna/hierophant + name = "Hierophant" + desc = "An ancient, powerful priest-like being wielding a mighty staff." + health = 2500 + maxHealth = 2500 + attacktext = "clubs" + //attack_sound = 'sound/weapons/sonic_jackhammer.ogg' + attack_sound = "swing_hit" + icon_state = "hierophant" + icon_living = "hierophant" + friendly = "stares down" + icon = 'icons/mob/lavaland/hierophant.dmi' + faction = list("boss") //asteroid mobs? get that shit out of my beautiful square house + speak_emote = list("preaches") + armour_penetration = 50 + melee_damage_lower = 10 + melee_damage_upper = 10 + speed = 1 + move_to_delay = 10 + ranged = 1 + pixel_x = -16 + ranged_cooldown_time = 40 + aggro_vision_range = 23 + loot = list(/obj/item/weapon/hierophant_staff) + wander = FALSE + var/burst_range = 3 //range on burst aoe + var/beam_range = 5 //range on cross blast beams + var/chaser_speed = 3 //how fast chasers are currently + var/chaser_cooldown = 101 //base cooldown/cooldown var between spawning chasers + var/major_attack_cooldown = 60 //base cooldown for major attacks + var/blinking = FALSE //if we're doing something that requires us to stand still and not attack + var/obj/effect/hierophant/spawned_rune //the rune we teleport back to + var/timeout_time = 15 //after this many Life() ticks with no target, we return to our rune + var/did_reset = TRUE //if we timed out, returned to our rune, and healed some + //var/list/kill_phrases = list("Wsyvgi sj irivkc xettih. Vitemvmrk...", "Irivkc wsyvgi jsyrh. Vitemvmrk...", "Jyip jsyrh. Egxmzexmrk vitemv gcgpiw...") + //var/list/target_phrases = list("Xevkix psgexih.", "Iriqc jsyrh.", "Eguymvih xevkix.") + medal_type = MEDAL_PREFIX + score_type = BIRD_SCORE + del_on_death = TRUE + death_sound = 'sound/magic/Repulse.ogg' + +/mob/living/simple_animal/hostile/megafauna/hierophant/New() + ..() + internal_gps = new/obj/item/device/gps/internal/hierophant(src) + spawned_rune = new(loc) + +/mob/living/simple_animal/hostile/megafauna/hierophant/Life() + . = ..() + if(. && spawned_rune && !client) + if(target || loc == spawned_rune.loc) + timeout_time = initial(timeout_time) + else + timeout_time-- + if(timeout_time <= 0 && !did_reset) + did_reset = TRUE + //visible_message("\"Vixyvrmrk xs fewi...\"") + blink(spawned_rune) + adjustHealth(min((health - maxHealth) * 0.5, -50)) //heal for 50% of our missing health + wander = FALSE + /*if(health > maxHealth * 0.9) + visible_message("\"Vitemvw gsqtpixi. Stivexmrk ex qebmqyq ijjmgmirgc.\"") + else + visible_message("\"Vitemvw gsqtpixi. Stivexmsrep ijjmgmirgc gsqtvsqmwih.\"")*/ + +/mob/living/simple_animal/hostile/megafauna/hierophant/death() + if(health > 0 || stat == DEAD) + return + else + stat = DEAD + blinking = TRUE //we do a fancy animation, release a huge burst(), and leave our staff. + animate(src, alpha = 0, color = "660099", time = 20, easing = EASE_OUT) + burst_range = 10 + //visible_message("\"Mrmxmexmrk wipj-hiwxvygx wiuyirgi...\"") + visible_message("[src] disappears in a massive burst of magic, leaving only its staff.") + burst(get_turf(src)) + ..() + +/mob/living/simple_animal/hostile/megafauna/hierophant/Destroy() + qdel(spawned_rune) + . = ..() + +/mob/living/simple_animal/hostile/megafauna/hierophant/devour(mob/living/L) + for(var/obj/item/W in L) + if(!L.unEquip(W)) + qdel(W) + /*visible_message( + "\"[pick(kill_phrases)]\"\n[src] annihilates [L]!", + "You annihilate [L], restoring your health!")*/ + visible_message( + "\"Caw.\"\n[src] annihilates [L]!", + "You annihilate [L], restoring your health!") + adjustHealth(-L.maxHealth*0.5) + L.dust() + +/*/mob/living/simple_animal/hostile/megafauna/hierophant/GiveTarget(new_target) + var/targets_the_same = (new_target == target) + . = ..() + if(. && target && !targets_the_same) + visible_message("\"[pick(target_phrases)]\"")*/ + +/mob/living/simple_animal/hostile/megafauna/hierophant/adjustHealth(amount) + . = ..() + if(src && amount > 0 && !blinking) + wander = TRUE + did_reset = FALSE + +/mob/living/simple_animal/hostile/megafauna/hierophant/AttackingTarget() + if(!blinking) + if(target && isliving(target)) + spawn(0) + melee_blast(get_turf(target)) //melee attacks on living mobs produce a 3x3 blast + ..() + +/mob/living/simple_animal/hostile/megafauna/hierophant/DestroySurroundings() + if(!blinking) + ..() + +/mob/living/simple_animal/hostile/megafauna/hierophant/Move() + if(!blinking) + /*if(!stat) + playsound(loc, 'sound/mecha/mechmove04.ogg', 150, 1, -4)*/ + ..() + +/mob/living/simple_animal/hostile/megafauna/hierophant/Goto(target, delay, minimum_distance) + wander = TRUE + if(!blinking) + ..() + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/calculate_rage() //how angry we are overall + did_reset = FALSE //oh hey we're doing SOMETHING, clearly we might need to heal if we recall + anger_modifier = Clamp(((maxHealth - health) / 42),0,50) + burst_range = initial(burst_range) + round(anger_modifier * 0.08) + beam_range = initial(beam_range) + round(anger_modifier * 0.12) + +/mob/living/simple_animal/hostile/megafauna/hierophant/OpenFire() + calculate_rage() + var/target_is_slow = FALSE + if(isliving(target)) + var/mob/living/L = target + if(!blinking && L.stat == DEAD && get_dist(src, L) > 2) + blink(L) + return + if(L.movement_delay() > 1.5) + target_is_slow = TRUE + chaser_speed = max(1, (3 - anger_modifier * 0.04) + target_is_slow * 0.5) + if(blinking) + return + ranged_cooldown = world.time + max(5, ranged_cooldown_time - anger_modifier * 0.75) //scale cooldown lower with high anger. + + if(prob(anger_modifier * 0.75)) //major ranged attack + var/list/possibilities = list() + var/cross_counter = 1 + round(anger_modifier * 0.12) + if(cross_counter > 1) + possibilities += "cross_blast_spam" + if(get_dist(src, target) > 2) + possibilities += "blink_spam" + if(chaser_cooldown < world.time) + if(prob(anger_modifier * 2)) + possibilities = list("chaser_swarm") + else + possibilities += "chaser_swarm" + if(possibilities.len) + ranged_cooldown = world.time + max(5, major_attack_cooldown - anger_modifier * 0.75) //we didn't cancel out of an attack, use the higher cooldown + var/blink_counter = 1 + round(anger_modifier * 0.08) + switch(pick(possibilities)) + if("blink_spam") //blink either once or multiple times. + if(health < maxHealth * 0.5 && !target_is_slow && blink_counter > 1) + //visible_message("\"Mx ampp rsx iwgeti.\"") + animate(src, color = "#660099", time = 6) + while(health && target && blink_counter) + if(loc == target.loc || loc == target) //we're on the same tile as them after about a second we can stop now + break + blink_counter-- + blinking = FALSE + blink(target) + blinking = TRUE + sleep(5) + animate(src, color = initial(color), time = 8) + sleep(8) + blinking = FALSE + else + blink(target) + if("cross_blast_spam") //fire a lot of cross blasts at a target. + //visible_message("\"Piezi mx rsalivi xs vyr.\"") + blinking = TRUE + animate(src, color = "#660099", time = 6) + while(health && target && cross_counter) + cross_counter-- + var/delay = 6 + if(prob(60)) + spawn(0) + cardinal_blasts(target) + else + spawn(0) + diagonal_blasts(target) + delay = 5 //this one isn't so mean, so do the next one faster(if there is one) + sleep(delay) + animate(src, color = initial(color), time = 8) + sleep(8) + blinking = FALSE + if("chaser_swarm") //fire four fucking chasers at a target and their friends. + //visible_message("\"Mx gerrsx lmhi.\"") + blinking = TRUE + animate(src, color = "#660099", time = 10) + var/list/targets = ListTargets() + var/list/cardinal_copy = cardinal.Copy() + while(health && targets.len && cardinal_copy.len) + var/mob/living/pickedtarget = pick(targets) + if(targets.len > 4) + pickedtarget = pick_n_take(targets) + if(pickedtarget.stat == DEAD) + pickedtarget = target + var/obj/effect/overlay/temp/hierophant/chaser/C = new /obj/effect/overlay/temp/hierophant/chaser(loc, src, pickedtarget, chaser_speed, FALSE) + C.moving = 3 + C.moving_dir = pick_n_take(cardinal_copy) + sleep(10) + chaser_cooldown = world.time + initial(chaser_cooldown) + animate(src, color = initial(color), time = 8) + sleep(8) + blinking = FALSE + return + + if(prob(10 + (anger_modifier * 0.5)) && get_dist(src, target) > 2) + blink(target) + + else if(prob(70 - anger_modifier)) //a cross blast of some type + if(prob(anger_modifier)) //at us? + if(prob(anger_modifier * 2) && health < maxHealth * 0.5) //we're super angry do it at all dirs + spawn(0) + alldir_blasts(src) + else if(prob(60)) + spawn(0) + cardinal_blasts(src) + else + spawn(0) + diagonal_blasts(src) + else //at them? + if(prob(anger_modifier * 2) && health < maxHealth * 0.5 && !target_is_slow) //we're super angry do it at all dirs + spawn(0) + alldir_blasts(target) + else if(prob(60)) + spawn(0) + cardinal_blasts(target) + else + spawn(0) + diagonal_blasts(target) + else if(chaser_cooldown < world.time) //if chasers are off cooldown, fire some! + var/obj/effect/overlay/temp/hierophant/chaser/C = new /obj/effect/overlay/temp/hierophant/chaser(loc, src, target, chaser_speed, FALSE) + chaser_cooldown = world.time + initial(chaser_cooldown) + if((prob(anger_modifier) || target.Adjacent(src)) && target != src) + var/obj/effect/overlay/temp/hierophant/chaser/OC = new /obj/effect/overlay/temp/hierophant/chaser(loc, src, target, max(1.5, 5 - anger_modifier * 0.07), FALSE) + OC.moving = 4 + OC.moving_dir = pick(cardinal - C.moving_dir) + else //just release a burst of power + spawn(0) + burst(get_turf(src)) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/diagonal_blasts(mob/victim) //fire diagonal cross blasts with a delay + var/turf/T = get_turf(victim) + if(!T) + return + new /obj/effect/overlay/temp/hierophant/telegraph/diagonal(T, src) + 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, src, FALSE) + for(var/d in diagonals) + spawn(0) + blast_wall(T, d) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/cardinal_blasts(mob/victim) //fire cardinal cross blasts with a delay + var/turf/T = get_turf(victim) + if(!T) + return + new /obj/effect/overlay/temp/hierophant/telegraph/cardinal(T, src) + 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, src, FALSE) + for(var/d in cardinal) + spawn(0) + blast_wall(T, d) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/alldir_blasts(mob/victim) //fire alldir cross blasts with a delay + var/turf/T = get_turf(victim) + if(!T) + return + new /obj/effect/overlay/temp/hierophant/telegraph(T, src) + 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, src, FALSE) + for(var/d in alldirs) + spawn(0) + blast_wall(T, d) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blast_wall(turf/T, dir) //make a wall of blasts beam_range tiles long + var/range = beam_range + var/turf/previousturf = T + var/turf/J = get_step(previousturf, dir) + for(var/i in 1 to range) + new /obj/effect/overlay/temp/hierophant/blast(J, src, FALSE) + previousturf = J + J = get_step(previousturf, dir) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blink(mob/victim) //blink to a target + if(blinking || !victim) + return + var/turf/T = get_turf(victim) + var/turf/source = get_turf(src) + new /obj/effect/overlay/temp/hierophant/telegraph(T, src) + new /obj/effect/overlay/temp/hierophant/telegraph(source, src) + 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) + blinking = TRUE + sleep(2) //short delay before we start... + new /obj/effect/overlay/temp/hierophant/telegraph/teleport(T, src) + new /obj/effect/overlay/temp/hierophant/telegraph/teleport(source, src) + for(var/t in RANGE_TURFS(1, T)) + var/obj/effect/overlay/temp/hierophant/blast/B = new /obj/effect/overlay/temp/hierophant/blast(t, src, FALSE) + 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, src, FALSE) + B.damage = 30 + animate(src, alpha = 0, time = 2, easing = EASE_OUT) //fade out + sleep(1) + visible_message("[src] fades out!") + density = FALSE + sleep(2) + forceMove(T) + sleep(1) + animate(src, alpha = 255, time = 2, easing = EASE_IN) //fade IN + sleep(1) + density = TRUE + visible_message("[src] fades in!") + sleep(1) //at this point the blasts we made detonate + blinking = FALSE + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/melee_blast(mob/victim) //make a 3x3 blast around a target + if(!victim) + return + var/turf/T = get_turf(victim) + if(!T) + return + new /obj/effect/overlay/temp/hierophant/telegraph(T, src) + 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, src, FALSE) + +/mob/living/simple_animal/hostile/megafauna/hierophant/proc/burst(turf/original) //release a wave of blasts + playsound(original,'sound/magic/blink.ogg', 200, 1) + //playsound(original,'sound/machines/AirlockOpen.ogg', 200, 1) + var/last_dist = 0 + for(var/t in spiral_range_turfs(burst_range, original)) + var/turf/T = t + if(!T) + continue + var/dist = get_dist(original, T) + if(dist > last_dist) + last_dist = dist + sleep(1 + (burst_range - last_dist) * 0.5) //gets faster as it gets further out + new /obj/effect/overlay/temp/hierophant/blast(T, src, FALSE) + +/mob/living/simple_animal/hostile/megafauna/hierophant/AltClickOn(atom/A) //player control handler(don't give this to a player holy fuck) + if(!istype(A) || get_dist(A, src) <= 2) + return + blink(A) + +//Hierophant overlays +/obj/effect/overlay/temp/hierophant + layer = MOB_LAYER - 0.1 + var/mob/living/caster //who made this, anyway + +/obj/effect/overlay/temp/hierophant/New(loc, new_caster) + ..() + if(new_caster) + caster = new_caster + +/obj/effect/overlay/temp/hierophant/chaser //a hierophant's chaser. follows target around, moving and producing a blast every speed deciseconds. + duration = 98 + var/mob/living/target //what it's following + var/turf/targetturf //what turf the target is actually on + var/moving_dir //what dir it's moving in + var/previous_moving_dir //what dir it was moving in before that + var/more_previouser_moving_dir //what dir it was moving in before THAT + var/moving = 0 //how many steps to move before recalculating + var/standard_moving_before_recalc = 4 //how many times we step before recalculating normally + var/tiles_per_step = 1 //how many tiles we move each step + var/speed = 3 //how many deciseconds between each step + var/currently_seeking = FALSE + var/friendly_fire_check = FALSE //if blasts produced apply friendly fire + +/obj/effect/overlay/temp/hierophant/chaser/New(loc, new_caster, new_target, new_speed, is_friendly_fire) + ..() + target = new_target + friendly_fire_check = is_friendly_fire + if(new_speed) + speed = new_speed + spawn(0) + seek_target() + +/obj/effect/overlay/temp/hierophant/chaser/proc/get_target_dir() + . = get_cardinal_dir(src, targetturf) + if((. != previous_moving_dir && . == more_previouser_moving_dir) || . == 0) //we're alternating, recalculate + var/list/cardinal_copy = cardinal.Copy() + cardinal_copy -= more_previouser_moving_dir + . = pick(cardinal_copy) + +/obj/effect/overlay/temp/hierophant/chaser/proc/seek_target() + if(!currently_seeking) + currently_seeking = TRUE + targetturf = get_turf(target) + while(target && src && !qdeleted(src) && currently_seeking && x && y && targetturf) //can this target actually be sook out + if(!moving) //we're out of tiles to move, find more and where the target is! + more_previouser_moving_dir = previous_moving_dir + previous_moving_dir = moving_dir + moving_dir = get_target_dir() + var/standard_target_dir = get_cardinal_dir(src, targetturf) + if((standard_target_dir != previous_moving_dir && standard_target_dir == more_previouser_moving_dir) || standard_target_dir == 0) + moving = 1 //we would be repeating, only move a tile before checking + else + moving = standard_moving_before_recalc + if(moving) //move in the dir we're moving in right now + var/turf/T = get_turf(src) + for(var/i in 1 to tiles_per_step) + var/maybe_new_turf = get_step(T, moving_dir) + if(maybe_new_turf) + T = maybe_new_turf + else + break + forceMove(T) + make_blast() //make a blast, too + moving-- + sleep(speed) + targetturf = get_turf(target) + +/obj/effect/overlay/temp/hierophant/chaser/proc/make_blast() + new /obj/effect/overlay/temp/hierophant/blast(loc, caster, friendly_fire_check) + +/obj/effect/overlay/temp/hierophant/telegraph + icon = 'icons/effects/96x96.dmi' + icon_state = "hierophant_telegraph" + pixel_x = -32 + pixel_y = -32 + duration = 3 + +/obj/effect/overlay/temp/hierophant/telegraph/diagonal + icon_state = "hierophant_telegraph_diagonal" + +/obj/effect/overlay/temp/hierophant/telegraph/cardinal + icon_state = "hierophant_telegraph_cardinal" + +/obj/effect/overlay/temp/hierophant/telegraph/teleport + icon_state = "hierophant_telegraph_teleport" + duration = 9 + +/obj/effect/overlay/temp/hierophant/blast + icon = 'icons/effects/effects.dmi' + icon_state = "hierophant_blast" + name = "vortex blast" + luminosity = 1 + desc = "Get out of the way!" + duration = 9 + var/damage = 10 //how much damage do we do? + var/list/hit_things = list() //we hit these already, ignore them + var/friendly_fire_check = FALSE + var/bursting = FALSE //if we're bursting and need to hit anyone crossing us + +/obj/effect/overlay/temp/hierophant/blast/New(loc, new_caster, friendly_fire) + ..() + friendly_fire_check = friendly_fire + if(new_caster) + hit_things += new_caster + if(ismineralturf(loc)) //drill mineral turfs + var/turf/simulated/mineral/M = loc + M.gets_drilled(caster) + spawn(0) + blast() + +/obj/effect/overlay/temp/hierophant/blast/proc/blast() + var/turf/T = get_turf(src) + if(!T) + return + playsound(T,'sound/magic/Blind.ogg', 125, 1, -5) //make a sound + sleep(6) //wait a little + bursting = TRUE + do_damage(T) //do damage and mark us as bursting + sleep(1.3) //slightly forgiving; the burst animation is 1.5 deciseconds + bursting = FALSE //we no longer damage crossers + +/obj/effect/overlay/temp/hierophant/blast/Crossed(atom/movable/AM) + ..() + if(bursting) + do_damage(get_turf(src)) + +/obj/effect/overlay/temp/hierophant/blast/proc/do_damage(turf/T) + for(var/mob/living/L in T.contents - hit_things) //find and damage mobs... + hit_things += L + if((friendly_fire_check && caster && caster.faction_check(L)) || L.stat == DEAD) + continue + if(L.client) + flash_color(L.client, "#660099", 1) + playsound(L,'sound/weapons/sear.ogg', 50, 1, -4) + to_chat(L, "You're struck by a [name]!") + var/limb_to_hit = pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg") + var/armor = L.run_armor_check(limb_to_hit, "melee", "Your armor absorbs [src]!", "Your armor blocks part of [src]!", 50, "Your armor was penetrated by [src]!") + L.apply_damage(damage, BURN, limb_to_hit, armor) + if(ismegafauna(L) || istype(L, /mob/living/simple_animal/hostile/asteroid)) + L.adjustBruteLoss(damage) + add_logs(caster, L, "struck with a [name]") + for(var/obj/mecha/M in T.contents - hit_things) //and mechs. + hit_things += M + if(M.occupant) + if(friendly_fire_check && caster && caster.faction_check(M.occupant)) + continue + to_chat(M.occupant, "Your [M.name] is struck by a [name]!") + playsound(M,'sound/weapons/sear.ogg', 50, 1, -4) + M.take_damage(damage, BURN, 0, 0) + +/obj/effect/hierophant + name = "hierophant rune" + desc = "A powerful magic mark allowing whomever attunes themself to it to return to it at will." + icon = 'icons/obj/rune.dmi' + icon_state = "hierophant" + layer = 2.5 + anchored = TRUE + color = "#CC00FF" + +/obj/effect/hierophant/attackby(obj/item/I, mob/user, params) + if(istype(I, /obj/item/weapon/hierophant_staff)) + var/obj/item/weapon/hierophant_staff/H = I + if(H.rune == src) + to_chat(user, "You start removing your hierophant rune...") + H.timer = world.time + 51 + if(do_after(user, 50, target = src)) + playsound(src,'sound/magic/Blind.ogg', 200, 1, -4) + new /obj/effect/overlay/temp/hierophant/telegraph/teleport(get_turf(src), user) + to_chat(user, "You touch the rune with the staff, dispelling it!") + H.rune = null + user.update_action_buttons_icon() + qdel(src) + else + H.timer = world.time + else + to_chat(user, "You touch the rune with the staff, but nothing happens.") + + else + ..() + +/obj/item/device/gps/internal/hierophant + icon_state = null + gpstag = "Zealous Signal" + desc = "Heed its words." + invisibility = 100 + +/turf/simulated/indestructible/hierophant + icon_state = "hierophant1" + oxygen = 14 + nitrogen = 23 + temperature = 300 + desc = "A floor with a square pattern. It's faintly cool to the touch." + +/turf/simulated/indestructible/hierophant/New() + ..() + if(prob(50)) + icon_state = "hierophant2" + +/turf/simulated/indestructible/riveted/hierophant + name = "wall" + desc = "A wall made out of smooth, cold stone." + icon = 'icons/turf/walls/hierophant_wall.dmi' + icon_state = "hierophant" + smooth = SMOOTH_TRUE + +#undef MEDAL_PREFIX \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm new file mode 100644 index 00000000000..89119952f60 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -0,0 +1,142 @@ +#define MEDAL_PREFIX "Legion" +/* + +LEGION + +Legion spawns from the necropolis gate in the far north of lavaland. It is the guardian of the Necropolis and emerges from within whenever an intruder tries to enter through its gate. +Whenever Legion emerges, everything in lavaland will receive a notice via color, audio, and text. This is because Legion is powerful enough to slaughter the entirety of lavaland with little effort. + +It has two attack modes that it constantly rotates between. + +In ranged mode, it will behave like a normal legion - retreating when possible and firing legion skulls at the target. +In charge mode, it will spin and rush its target, attacking with melee whenever possible. + +When Legion dies, it drops a staff of storms, which allows its wielder to call and disperse ash storms at will and functions as a powerful melee weapon. + +Difficulty: Medium + +*/ + +/mob/living/simple_animal/hostile/megafauna/legion + name = "Legion" + health = 800 + maxHealth = 800 + icon_state = "legion" + icon_living = "legion" + desc = "One of many." + icon = 'icons/mob/lavaland/legion.dmi' + attacktext = "chomps" + attack_sound = 'sound/misc/demon_attack1.ogg' + speak_emote = list("echoes") + armour_penetration = 50 + melee_damage_lower = 25 + melee_damage_upper = 25 + speed = 2 + ranged = 1 + del_on_death = 1 + retreat_distance = 5 + minimum_distance = 5 + ranged_cooldown_time = 20 + var/size = 5 + var/charging = 0 + medal_type = MEDAL_PREFIX + score_type = LEGION_SCORE + pixel_y = -90 + pixel_x = -75 + loot = list(/obj/item/stack/sheet/bone = 3) + vision_range = 13 + elimination = 1 + idle_vision_range = 13 + appearance_flags = 0 + mouse_opacity = 1 + stat_attack = 1 // Overriden from /tg/ - otherwise Legion starts chasing its minions + +/mob/living/simple_animal/hostile/megafauna/legion/New() + ..() + internal_gps = new/obj/item/device/gps/internal/legion(src) + +/mob/living/simple_animal/hostile/megafauna/legion/AttackingTarget() + ..() + if(ishuman(target)) + var/mob/living/L = target + if(L.stat == UNCONSCIOUS) + var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc) + A.infest(L) + +/mob/living/simple_animal/hostile/megafauna/legion/OpenFire(the_target) + if(world.time >= ranged_cooldown && !charging) + if(prob(75)) + var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc) + A.GiveTarget(target) + A.friends = friends + A.faction = faction + ranged_cooldown = world.time + ranged_cooldown_time + else + visible_message("[src] charges!") + SpinAnimation(speed = 20, loops = 5) + ranged = 0 + retreat_distance = 0 + minimum_distance = 0 + speed = 0 + charging = 1 + spawn(50) + reset_charge() + +/mob/living/simple_animal/hostile/megafauna/legion/proc/reset_charge() + ranged = 1 + retreat_distance = 5 + minimum_distance = 5 + speed = 2 + charging = 0 + +/mob/living/simple_animal/hostile/megafauna/legion/death() + if(health > 0) + return + if(size > 1) + adjustHealth(-maxHealth) //heal ourself to full in prep for splitting + var/mob/living/simple_animal/hostile/megafauna/legion/L = new(loc) + + L.maxHealth = maxHealth * 0.6 + maxHealth = L.maxHealth + + L.health = L.maxHealth + health = maxHealth + + size-- + L.size = size + + L.resize = L.size * 0.2 + transform = initial(transform) + resize = size * 0.2 + + L.update_transform() + update_transform() + + L.faction = faction.Copy() + + L.GiveTarget(target) + + visible_message("[src] splits in twain!") + else + var/last_legion = TRUE + for(var/mob/living/simple_animal/hostile/megafauna/legion/other in mob_list) + if(other != src) + last_legion = FALSE + break + if(last_legion) + loot = list(/obj/item/weapon/staff/storm) + elimination = 0 + else if(prob(5)) + loot = list(/obj/structure/closet/crate/necropolis/tendril) + ..() + +/mob/living/simple_animal/hostile/megafauna/legion/Process_Spacemove(movement_dir = 0) + return 1 + +/obj/item/device/gps/internal/legion + icon_state = null + gpstag = "Echoing Signal" + desc = "The message repeats." + invisibility = 100 + +#undef MEDAL_PREFIX \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm new file mode 100644 index 00000000000..11d6b68aeec --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -0,0 +1,230 @@ +#define MEDAL_PREFIX "Boss" + +/mob/living/simple_animal/hostile/megafauna + name = "megafauna" + desc = "Attack the weak point for massive damage." + health = 1000 + maxHealth = 1000 + a_intent = "harm" + sentience_type = SENTIENCE_BOSS + environment_smash = 3 + luminosity = 3 + faction = list("mining", "boss") + weather_immunities = list("lava","ash") + flying = 1 + robust_searching = 1 + ranged_ignores_vision = TRUE + stat_attack = 2 + 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) + damage_coeff = list(BRUTE = 1, BURN = 0.5, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) + minbodytemp = 0 + maxbodytemp = INFINITY + aggro_vision_range = 18 + idle_vision_range = 5 + environment_target_typecache = list( + /obj/machinery/door/window, + /obj/structure/window, + /obj/structure/closet, + /obj/structure/table, + /obj/structure/grille, + /obj/structure/girder, + /obj/structure/rack, + /obj/structure/barricade, + /obj/machinery/field, + /obj/machinery/power/emitter) + var/medal_type = MEDAL_PREFIX + var/score_type = BOSS_SCORE + var/elimination = 0 + var/anger_modifier = 0 + var/obj/item/device/gps/internal_gps + anchored = TRUE + mob_size = MOB_SIZE_LARGE + layer = MOB_LAYER + 0.5 //Looks weird with them slipping under mineral walls and cameras and shit otherwise + mouse_opacity = 2 // Easier to click on in melee, they're giant targets anyway + +/mob/living/simple_animal/hostile/megafauna/Destroy() + qdel(internal_gps) + . = ..() + +/mob/living/simple_animal/hostile/megafauna/death(gibbed) + if(health > 0) + return + else + if(!admin_spawned) + feedback_set_details("megafauna_kills","[initial(name)]") + if(!elimination) //used so the achievment only occurs for the last legion to die. + grant_achievement(medal_type,score_type) + ..() + +/mob/living/simple_animal/hostile/megafauna/gib() + if(health > 0) + return + else + ..() + +/mob/living/simple_animal/hostile/megafauna/dust() + if(health > 0) + return + else + ..() + +/mob/living/simple_animal/hostile/megafauna/AttackingTarget() + ..() + if(isliving(target)) + var/mob/living/L = target + if(L.stat != DEAD) + if(!client && ranged && ranged_cooldown <= world.time) + OpenFire() + else + devour(L) + +/mob/living/simple_animal/hostile/megafauna/onShuttleMove() + var/turf/oldloc = loc + . = ..() + if(!.) + return + var/turf/newloc = loc + message_admins("Megafauna [src] \ + (FLW) \ + moved via shuttle from ([oldloc.x], [oldloc.y], [oldloc.z]) to \ + ([newloc.x], [newloc.y], [newloc.z])") + +/mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L) + if(!L) + return + visible_message( + "[src] devours [L]!", + "You feast on [L], restoring your health!") + adjustBruteLoss(-L.maxHealth/2) + L.gib() + +/mob/living/simple_animal/hostile/megafauna/ex_act(severity, target) + switch(severity) + if(1) + adjustBruteLoss(250) + + if(2) + adjustBruteLoss(100) + + if(3) + adjustBruteLoss(50) + +/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype,scoretype) + + if(medal_type == "Boss") //Don't award medals if the medal type isn't set + return + + if(admin_spawned) + return + + if(global.medal_hub && global.medal_pass && global.medals_enabled) + for(var/mob/living/L in view(7,src)) + if(L.stat) + continue + if(L.client) + var/client/C = L.client + var/suffixm = BOSS_KILL_MEDAL + UnlockMedal("Boss [suffixm]",C) + UnlockMedal("[medaltype] [suffixm]",C) + SetScore(BOSS_SCORE,C,1) + SetScore(score_type,C,1) + +/proc/UnlockMedal(medal,client/player) + + if(!player || !medal) + return + if(global.medal_hub && global.medal_pass && global.medals_enabled) + spawn() + var/result = world.SetMedal(medal, player, global.medal_hub, global.medal_pass) + if(isnull(result)) + global.medals_enabled = FALSE + log_game("MEDAL ERROR: Could not contact hub to award medal:[medal] player:[player.ckey]") + message_admins("Error! Failed to contact hub to award [medal] medal to [player.ckey]!") + else if(result) + to_chat(player.mob, "Achievement unlocked: [medal]!") + + +/proc/SetScore(score,client/player,increment,force) + + if(!score || !player) + return + if(global.medal_hub && global.medal_pass && global.medals_enabled) + spawn() + var/list/oldscore = GetScore(score,player,1) + + if(increment) + if(!oldscore[score]) + oldscore[score] = 1 + else + oldscore[score] = (text2num(oldscore[score]) + 1) + else + oldscore[score] = force + + var/newscoreparam = list2params(oldscore) + + var/result = world.SetScores(player.ckey, newscoreparam, global.medal_hub, global.medal_pass) + + if(isnull(result)) + global.medals_enabled = FALSE + log_game("SCORE ERROR: Could not contact hub to set score. Score:[score] player:[player.ckey]") + message_admins("Error! Failed to contact hub to set [score] score for [player.ckey]!") + + +/proc/GetScore(score,client/player,returnlist) + + if(!score || !player) + return + if(global.medal_hub && global.medal_pass && global.medals_enabled) + + var/scoreget = world.GetScores(player.ckey, score, global.medal_hub, global.medal_pass) + if(isnull(scoreget)) + global.medals_enabled = FALSE + log_game("SCORE ERROR: Could not contact hub to get score. Score:[score] player:[player.ckey]") + message_admins("Error! Failed to contact hub to get score: [score] for [player.ckey]!") + return + + var/list/scoregetlist = params2list(scoreget) + + if(returnlist) + return scoregetlist + else + return scoregetlist[score] + + +/proc/CheckMedal(medal,client/player) + + if(!player || !medal) + return + if(global.medal_hub && global.medal_pass && global.medals_enabled) + + var/result = world.GetMedal(medal, player, global.medal_hub, global.medal_pass) + + if(isnull(result)) + global.medals_enabled = FALSE + log_game("MEDAL ERROR: Could not contact hub to get medal:[medal] player:[player.ckey]") + message_admins("Error! Failed to contact hub to get [medal] medal for [player.ckey]!") + else if(result) + to_chat(player.mob, "[medal] is unlocked") + +/proc/LockMedal(medal,client/player) + + if(!player || !medal) + return + if(global.medal_hub && global.medal_pass && global.medals_enabled) + + var/result = world.ClearMedal(medal, player, global.medal_hub, global.medal_pass) + + if(isnull(result)) + global.medals_enabled = FALSE + log_game("MEDAL ERROR: Could not contact hub to clear medal:[medal] player:[player.ckey]") + message_admins("Error! Failed to contact hub to clear [medal] medal for [player.ckey]!") + else if(result) + message_admins("Medal: [medal] removed for [player.ckey]") + else + message_admins("Medal: [medal] was not found for [player.ckey]. Unable to clear.") + + +/proc/ClearScore(client/player) + world.SetScores(player.ckey, "", global.medal_hub, global.medal_pass) + +#undef MEDAL_PREFIX \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/mimic.dm b/code/modules/mob/living/simple_animal/hostile/mimic.dm index ac9b8562681..87754e1428a 100644 --- a/code/modules/mob/living/simple_animal/hostile/mimic.dm +++ b/code/modules/mob/living/simple_animal/hostile/mimic.dm @@ -91,10 +91,6 @@ ..() icon_state = initial(icon_state) -/mob/living/simple_animal/hostile/mimic/crate/LostTarget() - ..() - icon_state = initial(icon_state) - /mob/living/simple_animal/hostile/mimic/crate/death(gibbed) var/obj/structure/closet/crate/C = new(get_turf(src)) // Put loot in crate diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm b/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm new file mode 100644 index 00000000000..3e6d98afd95 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm @@ -0,0 +1,78 @@ +/mob/living/simple_animal/hostile/asteroid/goldgrub + name = "goldgrub" + desc = "A worm that grows fat from eating everything in its sight. Seems to enjoy precious metals and other shiny things, hence the name." + icon = 'icons/mob/animal.dmi' + icon_state = "Goldgrub" + icon_living = "Goldgrub" + icon_aggro = "Goldgrub_alert" + icon_dead = "Goldgrub_dead" + icon_gib = "syndicate_gib" + vision_range = 2 + aggro_vision_range = 9 + idle_vision_range = 2 + move_to_delay = 5 + friendly = "harmlessly rolls into" + maxHealth = 45 + health = 45 + harm_intent_damage = 5 + melee_damage_lower = 0 + melee_damage_upper = 0 + attacktext = "barrels into" + attack_sound = 'sound/weapons/punch1.ogg' + a_intent = I_HELP + speak_emote = list("screeches") + throw_message = "sinks in slowly, before being pushed out of " + status_flags = CANPUSH + search_objects = 1 + wanted_objects = list(/obj/item/weapon/ore/diamond, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/silver, + /obj/item/weapon/ore/uranium) + + var/chase_time = 100 + var/will_burrow = TRUE + +/mob/living/simple_animal/hostile/asteroid/goldgrub/New() + ..() + var/i = rand(1,3) + while(i) + loot += pick(/obj/item/weapon/ore/silver, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/uranium, /obj/item/weapon/ore/diamond) + i-- + +/mob/living/simple_animal/hostile/asteroid/goldgrub/GiveTarget(new_target) + target = new_target + if(target != null) + if(istype(target, /obj/item/weapon/ore) && loot.len < 10) + visible_message("The [src.name] looks at [target.name] with hungry eyes.") + else if(isliving(target)) + Aggro() + visible_message("The [src.name] tries to flee from [target.name]!") + retreat_distance = 10 + minimum_distance = 10 + if(will_burrow) + spawn(chase_time) + Burrow() + +/mob/living/simple_animal/hostile/asteroid/goldgrub/AttackingTarget() + if(istype(target, /obj/item/weapon/ore)) + EatOre(target) + return + ..() + +/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/EatOre(var/atom/targeted_ore) + for(var/obj/item/weapon/ore/O in targeted_ore.loc) + if(loot.len < 10) + loot += O.type + qdel(O) + visible_message("The ore was swallowed whole!") + +/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Burrow()//Begin the chase to kill the goldgrub in time + if(!stat) + visible_message("The [src.name] buries into the ground, vanishing from sight!") + qdel(src) + +/mob/living/simple_animal/hostile/asteroid/goldgrub/bullet_act(obj/item/projectile/P) + visible_message("The [P.name] was repelled by [src.name]'s girth!") + return + +/mob/living/simple_animal/hostile/asteroid/goldgrub/adjustHealth(damage) + idle_vision_range = 9 + . = ..() \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm new file mode 100644 index 00000000000..c50e2dd2944 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm @@ -0,0 +1,319 @@ +/mob/living/simple_animal/hostile/asteroid/hivelord + name = "hivelord" + desc = "A truly alien creature, it is a mass of unknown organic material, constantly fluctuating. When attacking, pieces of it split off and attack in tandem with the original." + icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + icon_state = "Hivelord" + icon_living = "Hivelord" + icon_aggro = "Hivelord_alert" + icon_dead = "Hivelord_dead" + icon_gib = "syndicate_gib" + mouse_opacity = 2 + move_to_delay = 14 + ranged = 1 + vision_range = 5 + aggro_vision_range = 9 + idle_vision_range = 5 + speed = 3 + maxHealth = 75 + health = 75 + harm_intent_damage = 5 + melee_damage_lower = 0 + melee_damage_upper = 0 + attacktext = "lashes out at" + speak_emote = list("telepathically cries") + attack_sound = 'sound/weapons/pierce.ogg' + throw_message = "falls right through the strange body of the" + ranged_cooldown = 0 + ranged_cooldown_time = 20 + environment_smash = 0 + retreat_distance = 3 + minimum_distance = 3 + pass_flags = PASSTABLE + loot = list(/obj/item/organ/internal/hivelord_core) + var/brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood + +/mob/living/simple_animal/hostile/asteroid/hivelord/OpenFire(the_target) + if(world.time >= ranged_cooldown) + var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/A = new brood_type(src.loc) + A.admin_spawned = admin_spawned + A.GiveTarget(target) + A.friends = friends + A.faction = faction.Copy() + ranged_cooldown = world.time + ranged_cooldown_time + +/mob/living/simple_animal/hostile/asteroid/hivelord/AttackingTarget() + OpenFire() + +/mob/living/simple_animal/hostile/asteroid/hivelord/death(gibbed) + mouse_opacity = 1 + ..(gibbed) + +/obj/item/organ/internal/hivelord_core + name = "hivelord remains" + desc = "All that remains of a hivelord, it seems to be what allows it to break pieces of itself off without being hurt... its healing properties will soon become inert if not used quickly." + icon_state = "roro core 2" + flags = NOBLUDGEON + slot = "hivecore" + force = 0 + actions_types = list(/datum/action/item_action/organ_action/use) + var/inert = 0 + var/preserved = 0 + +/obj/item/organ/internal/hivelord_core/New() + ..() + addtimer(src, "inert_check", 2400) + +/obj/item/organ/internal/hivelord_core/proc/inert_check() + if(!owner && !preserved) + go_inert() + else + preserved(implanted = 1) + +/obj/item/organ/internal/hivelord_core/proc/preserved(implanted = 0) + inert = FALSE + preserved = TRUE + update_icon() + + if(implanted) + feedback_add_details("hivelord_core", "[type]|implanted") + else + feedback_add_details("hivelord_core", "[type]|stabilizer") + + +/obj/item/organ/internal/hivelord_core/proc/go_inert() + inert = TRUE + desc = "The remains of a hivelord that have become useless, having been left alone too long after being harvested." + feedback_add_details("hivelord_core", "[src.type]|inert") + update_icon() + +/obj/item/organ/internal/hivelord_core/ui_action_click() + owner.revive() + qdel(src) + +/obj/item/organ/internal/hivelord_core/on_life() + ..() + if(owner.health < config.health_threshold_crit) + ui_action_click() + +/obj/item/organ/internal/hivelord_core/afterattack(atom/target, mob/user, proximity_flag) + if(proximity_flag && ishuman(target)) + var/mob/living/carbon/human/H = target + if(inert) + to_chat(user, "[src] has become inert, its healing properties are no more.") + return + else + if(H.stat == DEAD) + to_chat(user, "[src] are useless on the dead.") + return + if(H != user) + H.visible_message("[user] forces [H] to apply [src]... They quickly regenerate all injuries!") + feedback_add_details("hivelord_core","[src.type]|used|other") + else + to_chat(user, "You start to smear [src] on yourself. It feels and smells disgusting, but you feel amazingly refreshed in mere moments.") + feedback_add_details("hivelord_core","[src.type]|used|self") + playsound(src.loc,'sound/items/eatfood.ogg', rand(10,50), 1) + H.revive() + qdel(src) + ..() + +/obj/item/organ/internal/hivelord_core/prepare_eat() + return null + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood + name = "hivelord brood" + desc = "A fragment of the original Hivelord, rallying behind its original. One isn't much of a threat, but..." + icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + icon_state = "Hivelordbrood" + icon_living = "Hivelordbrood" + icon_aggro = "Hivelordbrood" + icon_dead = "Hivelordbrood" + icon_gib = "syndicate_gib" + mouse_opacity = 2 + move_to_delay = 1 + friendly = "buzzes near" + vision_range = 10 + speed = 3 + maxHealth = 1 + health = 1 + flying = 1 + harm_intent_damage = 5 + melee_damage_lower = 2 + melee_damage_upper = 2 + attacktext = "slashes" + speak_emote = list("telepathically cries") + attack_sound = 'sound/weapons/pierce.ogg' + throw_message = "falls right through the strange body of the" + environment_smash = 0 + pass_flags = PASSTABLE + del_on_death = 1 + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/New() + ..() + addtimer(src, "death", 100) + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood + name = "blood brood" + desc = "A living string of blood and alien materials." + icon_state = "bloodbrood" + icon_living = "bloodbrood" + icon_aggro = "bloodbrood" + attacktext = "pierces" + color = "#C80000" + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/death() + if(loc) // Splash the turf we are on with blood + reagents.reaction(get_turf(src)) + ..() + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/New() + create_reagents(30) + ..() + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/AttackingTarget() + ..() + if(iscarbon(target)) + transfer_reagents(target, 1) + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/attack_hand(mob/living/carbon/human/M) + if("\ref[M]" in faction) + reabsorb_host(M) + else + return ..() + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/attack_alien(mob/living/carbon/alien/humanoid/M) + if("\ref[M]" in faction) + reabsorb_host(M) + else + return ..() + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/reabsorb_host(mob/living/carbon/C) + C.visible_message("[src] is reabsorbed by [C]'s body.", \ + "[src] is reabsorbed by your body.") + transfer_reagents(C) + death() + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/transfer_reagents(mob/living/carbon/C, volume = 30) + if(!reagents.total_volume) + return + + volume = min(volume, reagents.total_volume) + + var/fraction = min(volume/reagents.total_volume, 1) + reagents.reaction(C, INGEST, fraction) + reagents.trans_to(C, volume) + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/proc/link_host(mob/living/carbon/human/H) + faction = list("\ref[src]", "\ref[H]") // Hostile to everyone except the host. + H.vessel.trans_to(src, 30) + color = mix_color_from_reagents(reagents.reagent_list) + +// Legion +/mob/living/simple_animal/hostile/asteroid/hivelord/legion + name = "legion" + desc = "You can still see what was once a human under the shifting mass of corruption." + icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + icon_state = "legion" + icon_living = "legion" + icon_aggro = "legion" + icon_dead = "legion" + icon_gib = "syndicate_gib" + melee_damage_lower = 15 + melee_damage_upper = 15 + attacktext = "lashes out at" + speak_emote = list("echoes") + attack_sound = 'sound/weapons/pierce.ogg' + throw_message = "bounces harmlessly off of" + loot = list(/obj/item/organ/internal/hivelord_core/legion) + brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion + del_on_death = 1 + stat_attack = 1 + robust_searching = 1 + var/mob/living/carbon/human/stored_mob + +/mob/living/simple_animal/hostile/asteroid/hivelord/legion/death(gibbed) + visible_message("The skulls on [src] wail in anger as they flee from their dying host!") + var/turf/T = get_turf(src) + if(T) + if(stored_mob) + stored_mob.forceMove(get_turf(src)) + stored_mob = null + else + new /obj/effect/landmark/corpse/damaged(T) + ..(gibbed) + + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion + name = "legion" + desc = "One of many." + icon = 'icons/mob/lavaland/lavaland_monsters.dmi' + icon_state = "legion_head" + icon_living = "legion_head" + icon_aggro = "legion_head" + icon_dead = "legion_head" + icon_gib = "syndicate_gib" + friendly = "buzzes near" + vision_range = 10 + maxHealth = 1 + health = 5 + harm_intent_damage = 5 + melee_damage_lower = 12 + melee_damage_upper = 12 + attacktext = "bites" + speak_emote = list("echoes") + attack_sound = 'sound/weapons/pierce.ogg' + throw_message = "is shrugged off by" + pass_flags = PASSTABLE + del_on_death = 1 + stat_attack = 1 + robust_searching = 1 + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/Life() + if(isturf(loc)) + for(var/mob/living/carbon/human/H in view(src,1)) //Only for corpse right next to/on same tile + if(H.stat == UNCONSCIOUS) + infest(H) + ..() + +/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/proc/infest(mob/living/carbon/human/H) + visible_message("[name] burrows into the flesh of [H]!") + var/mob/living/simple_animal/hostile/asteroid/hivelord/legion/L = new(H.loc) + visible_message("[L] staggers to their feet!") + H.death() + H.adjustBruteLoss(1000) + L.stored_mob = H + H.forceMove(L) + qdel(src) + +/obj/item/organ/internal/hivelord_core/legion + name = "legion's soul" + desc = "A strange rock that still crackles with power... its \ + healing properties will soon become inert if not used quickly." + icon_state = "legion_soul" + +/obj/item/organ/internal/hivelord_core/legion/New() + ..() + update_icon() + +/obj/item/organ/internal/hivelord_core/legion/update_icon() + icon_state = inert ? "legion_soul_inert" : "legion_soul" + overlays.Cut() + if(!inert && !preserved) + overlays += image(icon, "legion_soul_crackle") + for(var/X in actions) + var/datum/action/A = X + A.UpdateButtonIcon() + +/obj/item/organ/internal/hivelord_core/legion/go_inert() + . = ..() + desc = "[src] has become inert, it crackles no more and is useless for \ + healing injuries." + +/obj/item/organ/internal/hivelord_core/legion/preserved(implanted = 0) + ..() + desc = "[src] has been stabilized. It no longer crackles with power, but it's healing properties are preserved indefinitely." + +/obj/item/weapon/legion_skull + name = "legion's head" + desc = "The once living, now empty eyes of the former human's skull cut deep into your soul." + icon = 'icons/obj/mining.dmi' + icon_state = "skull" \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm index 47c243e970e..f3ed8a34f51 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs.dm @@ -58,7 +58,7 @@ projectilesound = 'sound/weapons/pierce.ogg' ranged = 1 ranged_message = "stares" - ranged_cooldown_cap = 20 + ranged_cooldown_time = 30 throw_message = "does nothing against the hard shell of" vision_range = 2 speed = 3 @@ -71,7 +71,6 @@ a_intent = I_HARM speak_emote = list("chitters") attack_sound = 'sound/weapons/bladeslice.ogg' - ranged_cooldown_cap = 4 aggro_vision_range = 9 idle_vision_range = 2 turns_per_move = 5 @@ -104,232 +103,6 @@ if(3.0) adjustBruteLoss(110) -/mob/living/simple_animal/hostile/asteroid/goldgrub - name = "goldgrub" - desc = "A worm that grows fat from eating everything in its sight. Seems to enjoy precious metals and other shiny things, hence the name." - icon = 'icons/mob/animal.dmi' - icon_state = "Goldgrub" - icon_living = "Goldgrub" - icon_aggro = "Goldgrub_alert" - icon_dead = "Goldgrub_dead" - icon_gib = "syndicate_gib" - vision_range = 2 - aggro_vision_range = 9 - idle_vision_range = 2 - move_to_delay = 5 - friendly = "harmlessly rolls into" - maxHealth = 45 - health = 45 - harm_intent_damage = 5 - melee_damage_lower = 0 - melee_damage_upper = 0 - attacktext = "barrels into" - attack_sound = 'sound/weapons/punch1.ogg' - a_intent = I_HELP - speak_emote = list("screeches") - throw_message = "sinks in slowly, before being pushed out of " - status_flags = CANPUSH - search_objects = 1 - wanted_objects = list(/obj/item/weapon/ore/diamond, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/silver, - /obj/item/weapon/ore/uranium) - - var/list/ore_types_eaten = list() - var/alerted = 0 - var/ore_eaten = 1 - var/chase_time = 100 - var/will_burrow = 1 - -/mob/living/simple_animal/hostile/asteroid/goldgrub/New() - ..() - ore_types_eaten += pick(/obj/item/weapon/ore/silver, /obj/item/weapon/ore/gold, /obj/item/weapon/ore/uranium, /obj/item/weapon/ore/diamond) - ore_eaten = rand(1,3) - -/mob/living/simple_animal/hostile/asteroid/goldgrub/GiveTarget(var/new_target) - target = new_target - if(target != null) - if(istype(target, /obj/item/weapon/ore)) - visible_message("The [src.name] looks at [target.name] with hungry eyes.") - else if(isliving(target)) - Aggro() - visible_message("The [src.name] tries to flee from [target.name]!") - retreat_distance = 10 - minimum_distance = 10 - Burrow() - -/mob/living/simple_animal/hostile/asteroid/goldgrub/AttackingTarget() - if(istype(target, /obj/item/weapon/ore)) - EatOre(target) - return - ..() - -/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/EatOre(var/atom/targeted_ore) - for(var/obj/item/weapon/ore/O in targeted_ore.loc) - ore_eaten++ - if(!(O.type in ore_types_eaten)) - ore_types_eaten += O.type - qdel(O) - if(ore_eaten > 10)//Limit the scope of the reward you can get, or else things might get silly - ore_eaten = 10 - visible_message("The ore was swallowed whole!") - -/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Burrow()//Begin the chase to kill the goldgrub in time - if(!alerted && will_burrow) - alerted = 1 - spawn(chase_time) - if(alerted) - visible_message("The [src.name] buries into the ground, vanishing from sight!") - qdel(src) - -/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Reward() - if(!ore_eaten || ore_types_eaten.len == 0) - return - visible_message("[src] spits up the contents of its stomach before dying!") - var/counter - for(var/R in ore_types_eaten) - for(counter=0, counter < ore_eaten, counter++) - new R(src.loc) - ore_types_eaten.Cut() - ore_eaten = 0 - - -/mob/living/simple_animal/hostile/asteroid/goldgrub/bullet_act(var/obj/item/projectile/P) - visible_message("The [P.name] was repelled by [src.name]'s girth!") - return - -/mob/living/simple_animal/hostile/asteroid/goldgrub/death(gibbed) - alerted = 0 - Reward() - ..() - -/mob/living/simple_animal/hostile/asteroid/goldgrub/adjustHealth(damage) - idle_vision_range = 9 - ..() - -/mob/living/simple_animal/hostile/asteroid/hivelord - name = "hivelord" - desc = "A truly alien creature, it is a mass of unknown organic material, constantly fluctuating. When attacking, pieces of it split off and attack in tandem with the original." - icon = 'icons/mob/animal.dmi' - icon_state = "Hivelord" - icon_living = "Hivelord" - icon_aggro = "Hivelord_alert" - icon_dead = "Hivelord_dead" - icon_gib = "syndicate_gib" - mouse_opacity = 2 - move_to_delay = 14 - ranged = 1 - vision_range = 5 - aggro_vision_range = 9 - idle_vision_range = 5 - speed = 3 - maxHealth = 75 - health = 75 - harm_intent_damage = 5 - melee_damage_lower = 0 - melee_damage_upper = 0 - attacktext = "lashes out at" - speak_emote = list("telepathically cries") - attack_sound = 'sound/weapons/pierce.ogg' - throw_message = "falls right through the strange body of the" - ranged_cooldown = 0 - ranged_cooldown_cap = 0 - environment_smash = 0 - retreat_distance = 3 - minimum_distance = 3 - pass_flags = PASSTABLE - loot = list(/obj/item/organ/internal/hivelord_core) - -/mob/living/simple_animal/hostile/asteroid/hivelord/OpenFire(var/the_target) - var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/A = new /mob/living/simple_animal/hostile/asteroid/hivelordbrood(src.loc) - A.GiveTarget(target) - A.friends = friends - A.faction = faction - return - -/mob/living/simple_animal/hostile/asteroid/hivelord/AttackingTarget() - OpenFire() - -/obj/item/organ/internal/hivelord_core - name = "hivelord remains" - desc = "All that remains of a hivelord, it seems to be what allows it to break pieces of itself off without being hurt... its healing properties will soon become inert if not used quickly. Try not to think about what you're eating." - icon = 'icons/obj/food/food.dmi' - icon_state = "boiledrorocore" - slot = "hivecore" - var/inert = 0 - var/preserved = 0 - -/obj/item/organ/internal/hivelord_core/New() - spawn(2400) - if(!preserved) - inert = 1 - desc = "The remains of a hivelord that have become useless, having been left alone too long after being harvested." - -/obj/item/organ/internal/hivelord_core/on_life() - ..() - if(owner) - owner.adjustBruteLoss(-1) - owner.adjustFireLoss(-1) - owner.adjustOxyLoss(-2) - if(ishuman(owner)) - var/mob/living/carbon/human/H = owner - var/datum/reagent/blood/B = locate() in H.vessel.reagent_list //Grab some blood - var/blood_volume = round(H.vessel.get_reagent_amount("blood")) - if(B && blood_volume < H.max_blood && blood_volume) - B.volume += 2 // Fast blood regen - -/obj/item/organ/internal/hivelord_core/attack(mob/living/M as mob, mob/living/user as mob) - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(inert) - to_chat(user, "[src] have become inert, its healing properties are no more.") - return - else - if(H.stat == DEAD) - to_chat(user, "[src] are useless on the dead.") - return - if(H != user) - H.visible_message("[user] forces [H] to eat [src]... they quickly regenerate all injuries!") - else - to_chat(user, "You chomp into [src], barely managing to hold it down, but feel amazingly refreshed in mere moments.") - playsound(src.loc,'sound/items/eatfood.ogg', rand(10,50), 1) - H.revive() - qdel(src) - ..() - -/obj/item/organ/internal/hivelord_core/prepare_eat() - return null - -/mob/living/simple_animal/hostile/asteroid/hivelordbrood - name = "hivelord brood" - desc = "A fragment of the original Hivelord, rallying behind its original. One isn't much of a threat, but..." - icon = 'icons/mob/animal.dmi' - icon_state = "Hivelordbrood" - icon_living = "Hivelordbrood" - icon_aggro = "Hivelordbrood" - icon_dead = "Hivelordbrood" - icon_gib = "syndicate_gib" - mouse_opacity = 2 - move_to_delay = 1 - friendly = "buzzes near" - vision_range = 10 - speed = 3 - maxHealth = 1 - health = 1 - harm_intent_damage = 5 - melee_damage_lower = 2 - melee_damage_upper = 2 - attacktext = "slashes" - speak_emote = list("telepathically cries") - attack_sound = 'sound/weapons/pierce.ogg' - throw_message = "falls right through the strange body of the" - environment_smash = 0 - pass_flags = PASSTABLE - del_on_death = 1 - -/mob/living/simple_animal/hostile/asteroid/hivelordbrood/New() - ..() - spawn(100) - death() - /mob/living/simple_animal/hostile/asteroid/goliath name = "goliath" desc = "A massive beast that uses long tentacles to ensare its prey, threatening them is not advised under any conditions." @@ -344,7 +117,7 @@ move_to_delay = 40 ranged = 1 ranged_cooldown = 2 //By default, start the Goliath with his cooldown off so that people can run away quickly on first sight - ranged_cooldown_cap = 8 + ranged_cooldown_time = 120 friendly = "wails at" speak_emote = list("bellows") vision_range = 4 @@ -368,7 +141,7 @@ handle_preattack() /mob/living/simple_animal/hostile/asteroid/goliath/proc/handle_preattack() - if(ranged_cooldown <= 2 && !pre_attack) + if(ranged_cooldown <= world.time + ranged_cooldown_time * 0.25 && !pre_attack) pre_attack++ if(!pre_attack || stat || AIStatus == AI_IDLE) return @@ -383,7 +156,7 @@ if(get_dist(src, target) <= 7)//Screen range check, so you can't get tentacle'd offscreen visible_message("The [src.name] digs its tentacles under [target.name]!") new /obj/effect/goliath_tentacle/original(tturf) - ranged_cooldown = ranged_cooldown_cap + ranged_cooldown = world.time + ranged_cooldown_time icon_state = icon_aggro pre_attack = 0 return diff --git a/code/modules/mob/living/simple_animal/hostile/mushroom.dm b/code/modules/mob/living/simple_animal/hostile/mushroom.dm index 99e6f61431c..d7984031052 100644 --- a/code/modules/mob/living/simple_animal/hostile/mushroom.dm +++ b/code/modules/mob/living/simple_animal/hostile/mushroom.dm @@ -156,7 +156,7 @@ var/counter for(counter=0, counter<=powerlevel, counter++) var/obj/item/weapon/reagent_containers/food/snacks/hugemushroomslice/S = new /obj/item/weapon/reagent_containers/food/snacks/hugemushroomslice(src.loc) - S.reagents.add_reagent("mushroomhallucinogen", powerlevel) + S.reagents.add_reagent("psilocybin", powerlevel) S.reagents.add_reagent("omnizine", powerlevel) S.reagents.add_reagent("synaptizine", powerlevel) qdel(src) \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm index d749db16bf5..41ca671bc56 100644 --- a/code/modules/mob/living/simple_animal/hostile/statue.dm +++ b/code/modules/mob/living/simple_animal/hostile/statue.dm @@ -163,7 +163,7 @@ clothes_req = 0 range = 14 -/obj/effect/proc_holder/spell/aoe_turf/flicker_lights/cast(list/targets) +/obj/effect/proc_holder/spell/aoe_turf/flicker_lights/cast(list/targets, mob/user = usr) for(var/turf/T in targets) for(var/obj/machinery/light/L in T) L.flicker() @@ -179,9 +179,9 @@ clothes_req = 0 range = 10 -/obj/effect/proc_holder/spell/aoe_turf/blindness/cast(list/targets) +/obj/effect/proc_holder/spell/aoe_turf/blindness/cast(list/targets, mob/user = usr) for(var/mob/living/L in living_mob_list) - if(L == usr) + if(L == user) continue var/turf/T = get_turf(L.loc) if(T && T in targets) @@ -200,7 +200,7 @@ range = -1 include_user = 1 -/obj/effect/proc_holder/spell/targeted/night_vision/cast(list/targets) +/obj/effect/proc_holder/spell/targeted/night_vision/cast(list/targets, mob/user = usr) for(var/mob/living/target in targets) if(target.see_invisible == SEE_INVISIBLE_LIVING) diff --git a/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm new file mode 100644 index 00000000000..b4ac6212220 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/venus_human_trap.dm @@ -0,0 +1,116 @@ + + +/obj/structure/alien/resin/flower_bud_enemy //inheriting basic attack/damage stuff from alien structures + name = "flower bud" + desc = "A large pulsating plant..." + icon = 'icons/effects/spacevines.dmi' + icon_state = "flower_bud" + layer = MOB_LAYER + 0.9 + opacity = 0 + canSmoothWith = list() + smooth = SMOOTH_FALSE + var/growth_time = 1200 + +/obj/structure/alien/resin/flower_bud_enemy/New() + ..() + var/list/anchors = list() + anchors += locate(x-2,y+2,z) + anchors += locate(x+2,y+2,z) + anchors += locate(x-2,y-2,z) + anchors += locate(x+2,y-2,z) + + for(var/turf/T in anchors) + var/datum/beam/B = Beam(T, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine) + B.sleep_time = 10 //these shouldn't move, so let's slow down updates to 1 second (any slower and the deletion of the vines would be too slow) + addtimer(src, "bear_fruit", growth_time) + +/obj/structure/alien/resin/flower_bud_enemy/proc/bear_fruit() + visible_message("the plant has borne fruit!") + new /mob/living/simple_animal/hostile/venus_human_trap(get_turf(src)) + qdel(src) + + +/obj/effect/ebeam/vine + name = "thick vine" + mouse_opacity = 1 + desc = "A thick vine, painful to the touch." + + +/obj/effect/ebeam/vine/Crossed(atom/movable/AM) + if(isliving(AM)) + var/mob/living/L = AM + if(!("vines" in L.faction)) + L.adjustBruteLoss(5) + to_chat(L, "You cut yourself on the thorny vines.") + + + +/mob/living/simple_animal/hostile/venus_human_trap + name = "venus human trap" + desc = "Now you know how the fly feels." + icon_state = "venus_human_trap" + layer = MOB_LAYER + 0.9 + health = 50 + maxHealth = 50 + ranged = 1 + harm_intent_damage = 5 + melee_damage_lower = 25 + melee_damage_upper = 25 + a_intent = I_HARM + attack_sound = 'sound/weapons/bladeslice.ogg' + 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) + unsuitable_atmos_damage = 0 + faction = list("hostile","vines","plants") + var/list/grasping = list() + var/max_grasps = 4 + var/grasp_chance = 20 + var/grasp_pull_chance = 85 + var/grasp_range = 4 + del_on_death = 1 + +/mob/living/simple_animal/hostile/venus_human_trap/handle_automated_action() + if(..()) + for(var/mob/living/L in grasping) + if(L.stat == DEAD) + var/datum/beam/B = grasping[L] + if(B) + B.End() + grasping -= L + + //Can attack+pull multiple times per cycle + if(L.Adjacent(src)) + L.attack_animal(src) + else + if(prob(grasp_pull_chance)) + dir = get_dir(src,L) //staaaare + step(L,get_dir(L,src)) //reel them in + L.Weaken(3) //you can't get away now~ + + if(grasping.len < max_grasps) + grasping: + for(var/mob/living/L in view(grasp_range, src)) + if(L == src || faction_check(L) || (L in grasping) || L == target) + continue + for(var/t in getline(src,L)) + for(var/a in t) + var/atom/A = a + if(A.density && A != L) + continue grasping + if(prob(grasp_chance)) + to_chat(L, "\The [src] has you entangled!") + grasping[L] = Beam(L, "vine", time=INFINITY, maxdistance=5, beam_type=/obj/effect/ebeam/vine) + + break //only take 1 new victim per cycle + + +/mob/living/simple_animal/hostile/venus_human_trap/OpenFire(atom/the_target) + var/dist = get_dist(src,the_target) + Beam(the_target, "vine", time=dist*2, maxdistance=dist+2, beam_type=/obj/effect/ebeam/vine) + the_target.attack_animal(src) + + +/mob/living/simple_animal/hostile/venus_human_trap/CanAttack(atom/the_target) + . = ..() + if(.) + if(the_target in grasping) + return 0 diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index d2b03557479..9669c90b3ec 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -301,6 +301,9 @@ var/damage = rand(M.melee_damage_lower, M.melee_damage_upper) attack_threshold_check(damage,M.melee_damage_type) +/mob/living/simple_animal/proc/attacked_by(obj/item/I, mob/living/user) // Handled in _onclick/click.dm + return + /mob/living/simple_animal/bullet_act(var/obj/item/projectile/Proj) if(!Proj) return diff --git a/code/modules/mob/living/simple_animal/spawner.dm b/code/modules/mob/living/simple_animal/spawner.dm index 0cbf24cf6e2..46a05daba98 100644 --- a/code/modules/mob/living/simple_animal/spawner.dm +++ b/code/modules/mob/living/simple_animal/spawner.dm @@ -40,6 +40,7 @@ return 0 spawn_delay = world.time + spawn_time var/mob/living/simple_animal/L = new mob_type(src.loc) + L.admin_spawned = admin_spawned //If we were admin spawned, lets have our children count as that as well. spawned_mobs += L L.nest = src L.faction = src.faction diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index a35c51aa0cf..216bb896e7e 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1265,7 +1265,7 @@ mob/proc/yank_out_object() return // Only living mobs can ventcrawl //You can buckle on mobs if you're next to them since most are dense -/mob/buckle_mob(mob/living/M) +/mob/buckle_mob(mob/living/M, force = 0) if(M.buckled) return 0 var/turf/T = get_turf(src) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 483b765b65e..4307ec02655 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -434,7 +434,7 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HARM) lname = "[lname] " to_chat(M, "[lname][follow][message]") -/proc/notify_ghosts(message, ghost_sound = null, enter_link = null, atom/source = null, image/alert_overlay = null, attack_not_jump = 0, flashwindow = TRUE) //Easy notification of ghosts. +/proc/notify_ghosts(message, ghost_sound = null, enter_link = null, atom/source = null, image/alert_overlay = null, attack_not_jump = 0, flashwindow = TRUE, var/action = NOTIFY_JUMP) //Easy notification of ghosts. for(var/mob/dead/observer/O in player_list) if(O.client) to_chat(O, "[message][(enter_link) ? " [enter_link]" : ""]") @@ -443,13 +443,13 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HARM) if(flashwindow) window_flash(O.client) if(source) - var/obj/screen/alert/notify_jump/A = O.throw_alert("\ref[source]_notify_jump", /obj/screen/alert/notify_jump) + var/obj/screen/alert/notify_action/A = O.throw_alert("\ref[source]_notify_action", /obj/screen/alert/notify_action) if(A) if(O.client.prefs && O.client.prefs.UI_style) A.icon = ui_style2icon(O.client.prefs.UI_style) A.desc = message - A.attack_not_jump = attack_not_jump - A.jump_target = source + A.action = action + A.target = source if(!alert_overlay) var/old_layer = source.layer var/old_plane = source.plane diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index 7acb4e7356c..f88dce25b51 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -1,34 +1,6 @@ // the power cell // charge from 0 to 100% // fits in APC to provide backup power -/obj/item/weapon/stock_parts/cell/var/image/overlay_image - -/obj/item/weapon/stock_parts/cell/New() - ..() - processing_objects.Add(src) - charge = maxcharge - - spawn(5) - updateicon() - -/obj/item/weapon/stock_parts/cell/Destroy() - processing_objects.Remove(src) - return ..() - -/obj/item/weapon/stock_parts/cell/proc/updateicon() - if(isnull(src.overlay_image)) - src.overlay_image = image('icons/obj/power.dmi') - overlays.Cut() - - if(charge < 0.01) - return - else if(charge/maxcharge >=0.995) - src.overlay_image.icon_state = "cell-o2" - overlays += src.overlay_image - else - src.overlay_image.icon_state = "cell-o1" - overlays += src.overlay_image - /obj/item/weapon/stock_parts/cell/proc/percent() // return % charge of cell return 100.0*charge/maxcharge diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 00028e58afd..03b1d07b85b 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -32,7 +32,7 @@ var/area/A = get_area(src) if(A) var/image/alert_overlay = image('icons/effects/effects.dmi', "ghostalertsie") - notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, attack_not_jump = 1) + notify_ghosts("Nar-Sie has risen in \the [A.name]. Reach out to the Geometer to be given a new shell for your soul.", source = src, alert_overlay = alert_overlay, action=NOTIFY_ATTACK) narsie_spawn_animation() diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index 2fa6facdab0..c0e61cc8236 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -37,6 +37,7 @@ src.energy = starting_energy ..() processing_objects.Add(src) + poi_list |= src for(var/obj/machinery/power/singularity_beacon/singubeacon in world) if(singubeacon.active) target = singubeacon @@ -45,6 +46,7 @@ /obj/singularity/Destroy() processing_objects.Remove(src) + poi_list.Remove(src) singularities -= src target = null return ..() diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm index 323c240d239..c1a202fa134 100644 --- a/code/modules/power/supermatter/supermatter.dm +++ b/code/modules/power/supermatter/supermatter.dm @@ -64,6 +64,7 @@ /obj/machinery/power/supermatter_shard/New() . = ..() + poi_list |= src radio = new(src) radio.listening = 0 investigate_log("has been created.", "supermatter") @@ -71,8 +72,10 @@ /obj/machinery/power/supermatter_shard/Destroy() investigate_log("has been destroyed.", "supermatter") - qdel(radio) - radio = null + if(radio) + qdel(radio) + radio = null + poi_list.Remove(src) return ..() /obj/machinery/power/supermatter_shard/proc/explode() diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm index 14407ef9202..67bad46f4b2 100644 --- a/code/modules/power/tesla/energy_ball.dm +++ b/code/modules/power/tesla/energy_ball.dm @@ -34,6 +34,8 @@ var/list/blacklisted_tesla_types = typecacheof(list(/obj/machinery/atmospherics, var/produced_power var/energy_to_raise = 32 var/energy_to_lower = -20 + + /obj/singularity/energy_ball/Destroy() if(orbiting && istype(orbiting, /obj/singularity/energy_ball)) @@ -44,6 +46,8 @@ var/list/blacklisted_tesla_types = typecacheof(list(/obj/machinery/atmospherics, for(var/ball in orbiting_balls) var/obj/singularity/energy_ball/EB = ball qdel(EB) + + poi_list.Remove(src) return ..() @@ -125,6 +129,7 @@ var/list/blacklisted_tesla_types = typecacheof(list(/obj/machinery/atmospherics, /obj/singularity/energy_ball/orbit(obj/singularity/energy_ball/target) if(istype(target)) target.orbiting_balls += src + poi_list.Remove(src) . = ..() diff --git a/code/modules/projectiles/ammunition.dm b/code/modules/projectiles/ammunition.dm index b5a990dec69..cbbe43cd32c 100644 --- a/code/modules/projectiles/ammunition.dm +++ b/code/modules/projectiles/ammunition.dm @@ -188,3 +188,9 @@ //Behavior for magazines /obj/item/ammo_box/magazine/proc/ammo_count() return stored_ammo.len + +/obj/item/ammo_box/magazine/proc/empty_magazine() + var/turf_mag = get_turf(src) + for(var/obj/item/ammo in stored_ammo) + ammo.forceMove(turf_mag) + stored_ammo -= ammo \ No newline at end of file diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm index ed068c46868..3b808c64d53 100644 --- a/code/modules/projectiles/ammunition/ammo_casings.dm +++ b/code/modules/projectiles/ammunition/ammo_casings.dm @@ -260,7 +260,7 @@ ..() reagents.add_reagent("neurotoxin", 6) reagents.add_reagent("spore", 6) - reagents.add_reagent("mutetoxin", 6) //;HELP OPS IN MAINT + reagents.add_reagent("capulettium_plus", 6) //;HELP OPS IN MAINT reagents.add_reagent("coniine", 6) reagents.add_reagent("sodium_thiopental", 6) diff --git a/code/modules/projectiles/ammunition/special.dm b/code/modules/projectiles/ammunition/special.dm index d17d65e9fc8..d5c7df48200 100644 --- a/code/modules/projectiles/ammunition/special.dm +++ b/code/modules/projectiles/ammunition/special.dm @@ -26,6 +26,9 @@ /obj/item/ammo_casing/magic/chaos projectile_type = /obj/item/projectile/magic + +/obj/item/ammo_casing/magic/spellblade + projectile_type = /obj/item/projectile/magic/spellblade /obj/item/ammo_casing/magic/chaos/newshot() projectile_type = pick(typesof(/obj/item/projectile/magic)) diff --git a/code/modules/projectiles/guns/magic/staff.dm b/code/modules/projectiles/guns/magic/staff.dm index 9ef7de7f369..6479854f854 100644 --- a/code/modules/projectiles/guns/magic/staff.dm +++ b/code/modules/projectiles/guns/magic/staff.dm @@ -8,6 +8,7 @@ ammo_type = /obj/item/ammo_casing/magic/change icon_state = "staffofchange" item_state = "staffofchange" + fire_sound = "sound/magic/Staff_Change.ogg" /obj/item/weapon/gun/magic/staff/animate name = "staff of animation" @@ -15,6 +16,7 @@ ammo_type = /obj/item/ammo_casing/magic/animate icon_state = "staffofanimation" item_state = "staffofanimation" + fire_sound = "sound/magic/Staff_animation.ogg" /obj/item/weapon/gun/magic/staff/healing name = "staff of healing" @@ -22,6 +24,7 @@ ammo_type = /obj/item/ammo_casing/magic/heal icon_state = "staffofhealing" item_state = "staffofhealing" + fire_sound = "sound/magic/Staff_Healing.ogg" /obj/item/weapon/gun/magic/staff/healing/handle_suicide() //Stops people trying to commit suicide to heal themselves return @@ -35,6 +38,7 @@ max_charges = 10 recharge_rate = 2 no_den_usage = 1 + fire_sound = "sound/magic/Staff_Chaos.ogg" /obj/item/weapon/gun/magic/staff/door name = "staff of door creation" @@ -45,16 +49,17 @@ max_charges = 10 recharge_rate = 2 no_den_usage = 1 + fire_sound = "sound/magic/Staff_Door.ogg" /obj/item/weapon/gun/magic/staff/honk name = "staff of the honkmother" desc = "Honk" - fire_sound = "sound/items/airhorn.ogg" ammo_type = /obj/item/ammo_casing/magic/honk icon_state = "honker" item_state = "honker" max_charges = 4 recharge_rate = 8 + fire_sound = "sound/items/airhorn.ogg" /obj/item/weapon/gun/magic/staff/focus name = "mental focus" @@ -63,3 +68,23 @@ icon_state = "focus" item_state = "focus" ammo_type = list(/obj/item/ammo_casing/forcebolt) + +/obj/item/weapon/gun/magic/staff/spellblade + name = "spellblade" + desc = "A deadly combination of laziness and boodlust, this blade allows the user to dismember their enemies without all the hard work of actually swinging the sword." + fire_sound = "sound/magic/Fireball.ogg" + ammo_type = /obj/item/ammo_casing/magic/spellblade + icon_state = "spellblade" + item_state = "spellblade" + hitsound = 'sound/weapons/rapierhit.ogg' + force = 20 + armour_penetration = 75 + block_chance = 50 + sharp = 1 + edge = 1 + max_charges = 4 + +/obj/item/weapon/gun/magic/staff/spellblade/hit_reaction(mob/living/carbon/human/owner, attack_text, final_block_chance, damage, attack_type) + if(attack_type == PROJECTILE_ATTACK) + final_block_chance = 0 + return ..() diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm index a1c1d828288..572412dee8b 100644 --- a/code/modules/projectiles/guns/magic/wand.dm +++ b/code/modules/projectiles/guns/magic/wand.dm @@ -59,6 +59,7 @@ /obj/item/weapon/gun/magic/wand/death name = "wand of death" desc = "This deadly wand overwhelms the victim's body with pure energy, slaying them without fail." + fire_sound = "sound/magic/WandoDeath.ogg" ammo_type = /obj/item/ammo_casing/magic/death icon_state = "deathwand" max_charges = 3 //3, 2, 2, 1 @@ -79,6 +80,7 @@ name = "wand of resurrection" desc = "This wand uses healing magics to heal and revive. They are rarely utilized within the Wizard Federation for some reason." ammo_type = /obj/item/ammo_casing/magic/heal + fire_sound = "sound/magic/Staff_Healing.ogg" icon_state = "revivewand" max_charges = 3 //3, 2, 2, 1 @@ -96,6 +98,7 @@ name = "wand of polymorph" desc = "This wand is attuned to chaos and will radically alter the victim's form." ammo_type = /obj/item/ammo_casing/magic/change + fire_sound = "sound/magic/Staff_Change.ogg" icon_state = "polywand" max_charges = 10 //10, 5, 5, 4 @@ -115,6 +118,7 @@ icon_state = "telewand" max_charges = 10 //10, 5, 5, 4 no_den_usage = 1 + fire_sound = "sound/magic/Wand_Teleport.ogg" /obj/item/weapon/gun/magic/wand/teleport/zap_self(mob/living/user) do_teleport(user, user, 10) @@ -132,6 +136,7 @@ name = "wand of door creation" desc = "This particular wand can create doors in any wall for the unscrupulous wizard who shuns teleportation magics." ammo_type = /obj/item/ammo_casing/magic/door + fire_sound = "sound/magic/Staff_Door.ogg" icon_state = "doorwand" max_charges = 20 //20, 10, 10, 7 @@ -147,6 +152,7 @@ /obj/item/weapon/gun/magic/wand/fireball name = "wand of fireball" desc = "This wand shoots scorching balls of fire that explode into destructive flames." + fire_sound = "sound/magic/Fireball.ogg" ammo_type = /obj/item/ammo_casing/magic/fireball icon_state = "firewand" max_charges = 8 //8, 4, 4, 3 diff --git a/code/modules/projectiles/guns/projectile/bow.dm b/code/modules/projectiles/guns/projectile/bow.dm new file mode 100644 index 00000000000..2512ebfa35f --- /dev/null +++ b/code/modules/projectiles/guns/projectile/bow.dm @@ -0,0 +1,98 @@ +/obj/item/weapon/gun/projectile/bow + name = "bow" + desc = "A sturdy bow made out of wood and reinforced with iron." + icon_state = "bow_unloaded" + item_state = "bow" + fire_sound = 'sound/weapons/grenadelaunch.ogg' + mag_type = /obj/item/ammo_box/magazine/internal/bow + flags = HANDSLOW + weapon_weight = WEAPON_HEAVY + var/draw_sound = 'sound/weapons/draw_bow.ogg' + var/ready_to_fire = 0 + var/slowdown_when_ready = 2 + +/obj/item/weapon/gun/projectile/bow/update_icon() + if(magazine.ammo_count() && !ready_to_fire) + icon_state = "bow_loaded" + else if(ready_to_fire) + icon_state = "bow_firing" + slowdown = slowdown_when_ready + else + icon_state = initial(icon_state) + slowdown = initial(slowdown) + +/obj/item/weapon/gun/projectile/bow/dropped(mob/user) + if(magazine && magazine.ammo_count()) + magazine.empty_magazine() + ready_to_fire = FALSE + update_icon() + +/obj/item/weapon/gun/projectile/bow/attack_self(mob/living/user) + if(!ready_to_fire && magazine.ammo_count()) + ready_to_fire = TRUE + playsound(user, draw_sound, 100, 1) + update_icon() + else + ready_to_fire = FALSE + update_icon() + +/obj/item/weapon/gun/projectile/bow/attackby(obj/item/A, mob/user, params) + var/num_loaded = magazine.attackby(A, user, params, 1) + if(num_loaded) + to_chat(user, "You ready \the [A] into \the [src].") + update_icon() + chamber_round() + +/obj/item/weapon/gun/projectile/bow/can_shoot() + . = ..() + if(!ready_to_fire) + return FALSE + +/obj/item/weapon/gun/projectile/bow/shoot_with_empty_chamber(mob/living/user as mob|obj) + return + +/obj/item/weapon/gun/projectile/bow/process_chamber(eject_casing = 0, empty_chamber = 1) + . = ..() + ready_to_fire = FALSE + update_icon() + +// ammo +/obj/item/ammo_box/magazine/internal/bow + name = "bow internal magazine" + ammo_type = /obj/item/ammo_casing/caseless/arrow + caliber = "arrow" + max_ammo = 1 + + +/obj/item/projectile/bullet/reusable/arrow + name = "arrow" + icon_state = "arrow" + ammo_type = /obj/item/ammo_casing/caseless/arrow + range = 10 + damage = 25 + damage_type = BRUTE + +/obj/item/ammo_casing/caseless/arrow + name = "arrow" + desc = "Stab, stab, stab." + icon_state = "arrow" + force = 10 + sharp = 1 + projectile_type = /obj/item/projectile/bullet/reusable/arrow + caliber = "arrow" + +//quiver +/obj/item/weapon/storage/backpack/quiver + name = "quiver" + desc = "A quiver for holding arrows." + icon_state = "quiver" + item_state = "quiver" + storage_slots = 20 + can_hold = list( + /obj/item/ammo_casing/caseless/arrow + ) + +/obj/item/weapon/storage/backpack/quiver/full/New() + ..() + for(var/i in 1 to storage_slots) + new /obj/item/ammo_casing/caseless/arrow(src) diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm index cf7c2a4bb35..bf26dad78b3 100644 --- a/code/modules/projectiles/guns/projectile/sniper.dm +++ b/code/modules/projectiles/guns/projectile/sniper.dm @@ -53,6 +53,7 @@ damage = 70 stun = 5 weaken = 5 + dismemberment = 50 armour_penetration = 50 var/breakthings = TRUE @@ -82,6 +83,7 @@ armour_penetration = 0 nodamage = 1 stun = 0 + dismemberment = 0 weaken = 0 breakthings = FALSE @@ -113,6 +115,7 @@ armour_penetration = 15 damage = 15 stun = 0 + dismemberment = 0 weaken = 0 breakthings = FALSE @@ -142,5 +145,6 @@ damage = 60 forcedodge = 1 stun = 0 + dismemberment = 0 weaken = 0 breakthings = FALSE diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 7d6f629aa4c..7b0bcda17d0 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -50,6 +50,7 @@ var/jitter = 0 var/embed = 0 // whether or not the projectile can embed itself in the mob var/forcedodge = 0 //to pass through everything + var/dismemberment = 0 //The higher the number, the greater the bonus to dismembering. 0 will not dismember at all. var/log = 1 //whether print to admin attack logs or just keep it in the diary diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm index 9a9d5df1845..ccab76f6e92 100644 --- a/code/modules/projectiles/projectile/bullets.dm +++ b/code/modules/projectiles/projectile/bullets.dm @@ -206,7 +206,7 @@ nodamage = 1 pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE hitsound = 'sound/items/bikehorn.ogg' - icon = 'icons/obj/harvest.dmi' + icon = 'icons/obj/hydroponics/harvest.dmi' icon_state = "banana" range = 200 diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index 19bfd4b02fa..b0d2a53c0d9 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -17,6 +17,12 @@ damage = 10 damage_type = BRUTE nodamage = 0 + + //explosion values + var/exp_heavy = 0 + var/exp_light = 2 + var/exp_flash = 3 + var/exp_fire = 2 /obj/item/projectile/magic/death/on_hit(var/mob/living/carbon/G) . = ..() @@ -27,11 +33,16 @@ /obj/item/projectile/magic/fireball/Range() var/turf/T1 = get_step(src,turn(dir, -45)) var/turf/T2 = get_step(src,turn(dir, 45)) + var/turf/T3 = get_step(src,dir) var/mob/living/L = locate(/mob/living) in T1 //if there's a mob alive in our front right diagonal, we hit it. if(L && L.stat != DEAD) Bump(L) //Magic Bullet #teachthecontroversy return L = locate(/mob/living) in T2 + if(L && L.stat != DEAD) + Bump(L) + return + L = locate(/mob/living) in T3 if(L && L.stat != DEAD) Bump(L) return @@ -44,6 +55,14 @@ if(ismob(target)) //multiple flavors of pain var/mob/living/M = target M.take_overall_damage(0,10) //between this 10 burn, the 10 brute, the explosion brute, and the onfire burn, your at about 65 damage if you stop drop and roll immediately + + +/obj/item/projectile/magic/fireball/infernal + name = "infernal fireball" + exp_heavy = -1 + exp_light = -1 + exp_flash = 4 + exp_fire= 5 /obj/item/projectile/magic/resurrection name = "bolt of resurrection" @@ -266,3 +285,12 @@ proc/wabbajack(mob/living/M) // Change our allegiance! var/mob/living/simple_animal/hostile/mimic/copy/C = change C.ChangeOwner(firer) + +/obj/item/projectile/magic/spellblade + name = "blade energy" + icon_state = "lavastaff" + damage = 15 + damage_type = BURN + flag = "magic" + dismemberment = 50 + nodamage = 0 \ No newline at end of file diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index ab65418b6f1..b1cdef1f73c 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -299,6 +299,7 @@ obj/item/projectile/kinetic/New() damage_type = BRUTE damage = 5 range = 3 + dismemberment = 20 /obj/item/projectile/plasma/New() var/turf/proj_turf = get_turf(src) diff --git a/code/modules/reagents/chemistry/holder.dm b/code/modules/reagents/chemistry/holder.dm index c9a05f263a2..2b1a8706123 100644 --- a/code/modules/reagents/chemistry/holder.dm +++ b/code/modules/reagents/chemistry/holder.dm @@ -726,6 +726,16 @@ atom/proc/create_reagents(max_vol) reagents = new/datum/reagents(max_vol) reagents.my_atom = src +/proc/get_random_reagent_id() // Returns a random reagent ID minus blacklisted reagents + var/static/list/random_reagents = list() + if(!random_reagents.len) + for(var/thing in subtypesof(/datum/reagent)) + var/datum/reagent/R = thing + if(initial(R.can_synth)) + random_reagents += initial(R.id) + var/picked_reagent = pick(random_reagents) + return picked_reagent + /datum/reagents/proc/get_reagent_from_id(id) var/datum/reagent/result = null for(var/datum/reagent/R in reagent_list) diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm index 38cfdf4b0ae..8685056d9d3 100644 --- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm +++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm @@ -3,62 +3,85 @@ icon = 'icons/obj/kitchen.dmi' icon_state = "juicer1" layer = 2.9 - density = 1 anchored = 1 use_power = 1 idle_power_usage = 5 active_power_usage = 100 - var/inuse = 0 + pass_flags = PASSTABLE + var/operating = 0 var/obj/item/weapon/reagent_containers/beaker = null var/limit = 10 //IMPORTANT NOTE! A negative number is a multiplier, a positive number is a flat amount to add. 0 means equal to the amount of the original reagent var/list/blend_items = list ( - //Sheets - /obj/item/stack/sheet/mineral/plasma = list("plasma_dust" = 20), - /obj/item/stack/sheet/mineral/uranium = list("uranium" = 20), - /obj/item/stack/sheet/mineral/bananium = list("banana" = 20), - /obj/item/stack/sheet/mineral/tranquillite = list("nothing" = 20), - /obj/item/stack/sheet/mineral/silver = list("silver" = 20), - /obj/item/stack/sheet/mineral/gold = list("gold" = 20), - /obj/item/weapon/grown/novaflower = list("capsaicin" = 0), + //Sheets + /obj/item/stack/sheet/mineral/plasma = list("plasma_dust" = 20), + /obj/item/stack/sheet/metal = list("iron" = 20), + /obj/item/stack/rods = list("iron" = 10), + /obj/item/stack/sheet/plasteel = list("iron" = 20, "plasma_dust" = 20), + /obj/item/stack/sheet/wood = list("carbon" = 20), + /obj/item/stack/sheet/glass = list("silicon" = 20), + /obj/item/stack/sheet/rglass = list("silicon" = 20, "iron" = 20), + /obj/item/stack/sheet/mineral/uranium = list("uranium" = 20), + /obj/item/stack/sheet/mineral/bananium = list("banana" = 20), + /obj/item/stack/sheet/mineral/tranquillite = list("nothing" = 20), + /obj/item/stack/sheet/mineral/silver = list("silver" = 20), + /obj/item/stack/sheet/mineral/gold = list("gold" = 20), + /obj/item/weapon/grown/nettle/basic = list("sacid" = 0), + /obj/item/weapon/grown/nettle/death = list("facid" = 0, "sacid" = 0), + /obj/item/weapon/grown/novaflower = list("capsaicin" = 0, "condensedcapsaicin" = 0), - //archaeology! - ///obj/item/weapon/rocksliver = list("ground_rock" = 50), + //Blender Stuff + /obj/item/weapon/reagent_containers/food/snacks/grown/soybeans = list("soymilk" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/tomato = list("ketchup" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/wheat = list("flour" = -5), + /obj/item/weapon/reagent_containers/food/snacks/grown/oat = list("flour" = -5), + /obj/item/weapon/reagent_containers/food/snacks/grown/cherries = list("cherryjelly" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/bluecherries = list("bluecherryjelly" = 0), + /obj/item/weapon/reagent_containers/food/snacks/egg = list("egg" = -5), + + //Grinder stuff, but only if dry + /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/coffee = list("coffeepowder" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/tea = list("teapowder" = 0), - //All types that you can put into the grinder to transfer the reagents to the beaker. !Put all recipes above this.! - /obj/item/weapon/reagent_containers/food = list(), - /obj/item/weapon/reagent_containers/honeycomb = list() - ) - - var/list/blend_tags = list ( - "nettle" = list("sacid" = 0), - "deathnettle" = list("facid" = 0), - "soybeans" = list("soymilk" = 0), - "tomato" = list("ketchup" = 0), - "wheat" = list("flour" = -5), - "rice" = list("rice" = -5), - "cherries" = list("cherryjelly" = 0), + //All types that you can put into the grinder to transfer the reagents to the beaker. !Put all recipes above this.! + /obj/item/slime_extract = list(), + /obj/item/weapon/reagent_containers/food = list(), + /obj/item/weapon/reagent_containers/honeycomb = list() ) var/list/juice_items = list ( - /obj/item/weapon/reagent_containers/food/snacks/watermelonslice = list("watermelonjuice" = 0), + + //Juicer Stuff + /obj/item/weapon/reagent_containers/food/snacks/grown/corn = list("corn_starch" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/tomato = list("tomatojuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/carrot = list("carrotjuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/berries = list("berryjuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/banana = list("banana" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/potato = list("potato" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lemon = list("lemonjuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/orange = list("orangejuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/citrus/lime = list("limejuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/watermelon = list("watermelonjuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/watermelonslice = list("watermelonjuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/berries/poison = list("poisonberryjuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/pumpkin = list("pumpkinjuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/blumpkin = list("blumpkinjuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/apple = list("applejuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/grapes = list("grapejuice" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/grapes/green = list("grapejuice" = 0), ) - var/list/juice_tags = list ( - "tomato" = list("tomatojuice" = 0), - "carrot" = list("carrotjuice" = 0), - "berries" = list("berryjuice" = 0), - "banana" = list("banana" = 0), - "potato" = list("potato" = 0), - "lemon" = list("lemonjuice" = 0), - "orange" = list("orangejuice" = 0), - "lime" = list("limejuice" = 0), - "poisonberries" = list("poisonberryjuice" = 0), - "grapes" = list("grapejuice" = 0), - "corn" = list("cornoil" = 0), + var/list/dried_items = list( + //Grinder stuff, but only if dry, + /obj/item/weapon/reagent_containers/food/snacks/grown/coffee/robusta = list("coffeepowder" = 0, "morphine" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/coffee = list("coffeepowder" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/tea/astra = list("teapowder" = 0, "salglu_solution" = 0), + /obj/item/weapon/reagent_containers/food/snacks/grown/tea = list("teapowder" = 0) ) var/list/holdingitems = list() @@ -68,348 +91,352 @@ beaker = new /obj/item/weapon/reagent_containers/glass/beaker/large(src) return +/obj/machinery/reagentgrinder/Destroy() + if(beaker) + qdel(beaker) + beaker = null + return ..() + +/obj/machinery/reagentgrinder/ex_act(severity) + if(beaker) + beaker.ex_act(severity) + ..() + /obj/machinery/reagentgrinder/update_icon() - icon_state = "juicer"+num2text(!isnull(beaker)) - return + if(beaker) + icon_state = "juicer1" + else + icon_state = "juicer0" -/obj/machinery/reagentgrinder/attackby(obj/item/O, mob/user, params) - - if(istype(O,/obj/item/weapon/reagent_containers/glass) || \ - istype(O,/obj/item/weapon/reagent_containers/food/drinks/drinkingglass) || \ - istype(O,/obj/item/weapon/reagent_containers/food/drinks/shaker)) - - if(beaker) - return 1 - else - if(!user.drop_item()) - to_chat(user, "[O] is stuck to you!") +/obj/machinery/reagentgrinder/attackby(obj/item/I, mob/user, params) + if(default_unfasten_wrench(user, I)) return - beaker = O - O.forceMove(src) - update_icon() - updateUsrDialog() - return 0 - if(holdingitems && holdingitems.len >= limit) - to_chat(usr, "The machine cannot hold anymore items.") - return 1 + if (istype(I, /obj/item/weapon/reagent_containers) && (I.flags & OPENCONTAINER) ) + if (!beaker) + if(!user.drop_item()) + return 1 + beaker = I + beaker.loc = src + update_icon() + src.updateUsrDialog() + else + to_chat(user, "There's already a container inside.") + return 1 //no afterattack - //Fill machine with a plantbag! - if(istype(O, /obj/item/weapon/storage/bag/plants)) - var/obj/item/weapon/storage/bag/plants/PB = O - for(var/obj/item/weapon/reagent_containers/food/snacks/grown/G in PB.contents) - PB.remove_from_storage(G, src) - holdingitems += G - if(holdingitems && holdingitems.len >= limit) //Sanity checking so the blender doesn't overfill - to_chat(user, "You must dry that first!") + return 1 - if(!PB.contents.len) - to_chat(user, "You empty [PB] into the All-In-One grinder.") + if(holdingitems && holdingitems.len >= limit) + to_chat(usr, "The machine cannot hold anymore items.") + return 1 - updateUsrDialog() - return 0 + //Fill machine with a bag! + if(istype(I, /obj/item/weapon/storage/bag)) + var/obj/item/weapon/storage/bag/B = I + for (var/obj/item/weapon/reagent_containers/food/snacks/grown/G in B.contents) + B.remove_from_storage(G, src) + holdingitems += G + if(holdingitems && holdingitems.len >= limit) //Sanity checking so the blender doesn't overfill + to_chat(user, "You fill the All-In-One grinder to the brim.") + break + if(!I.contents.len) + to_chat(user, "You empty the plant bag into the All-In-One grinder.") - if(!is_type_in_list(O, blend_items) && !is_type_in_list(O, juice_items)) - to_chat(user, "Cannot refine into a reagent.") - return 1 + src.updateUsrDialog() + return 1 - user.unEquip(O) - O.forceMove(src) - holdingitems += O - updateUsrDialog() - return 0 + if (!is_type_in_list(I, blend_items) && !is_type_in_list(I, juice_items)) + if(user.a_intent == I_HARM) + return ..() + else + to_chat(user, "Cannot refine into a reagent!") + return 1 + + if(user.drop_item()) + I.loc = src + holdingitems += I + src.updateUsrDialog() + return 0 /obj/machinery/reagentgrinder/attack_ai(mob/user) - return 0 + return 0 /obj/machinery/reagentgrinder/attack_hand(mob/user) - user.set_machine(src) - interact(user) + user.set_machine(src) + interact(user) /obj/machinery/reagentgrinder/interact(mob/user) // The microwave Menu - var/is_chamber_empty = 0 - var/is_beaker_ready = 0 - var/processing_chamber = "" - var/beaker_contents = "" - var/dat = "" + var/is_chamber_empty = 0 + var/is_beaker_ready = 0 + var/processing_chamber = "" + var/beaker_contents = "" + var/dat = "" - if(!inuse) - for(var/obj/item/O in holdingitems) - processing_chamber += "\A [O.name] " + if(!operating) + for (var/obj/item/O in holdingitems) + processing_chamber += "\A [O.name] " - if(!processing_chamber) - is_chamber_empty = 1 - processing_chamber = "Nothing." - if(!beaker) - beaker_contents = "No beaker attached. " + if (!processing_chamber) + is_chamber_empty = 1 + processing_chamber = "Nothing." + if (!beaker) + beaker_contents = "No beaker attached. " + else + is_beaker_ready = 1 + beaker_contents = "The beaker contains: " + var/anything = 0 + for(var/datum/reagent/R in beaker.reagents.reagent_list) + anything = 1 + beaker_contents += "[R.volume] - [R.name] " + if(!anything) + beaker_contents += "Nothing " + + + dat = {" + Processing chamber contains: + [processing_chamber] + [beaker_contents] + "} + if (is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN))) + dat += "Grind the reagents " + dat += "Juice the reagents " + if(holdingitems && holdingitems.len > 0) + dat += "Eject the reagents " + if (beaker) + dat += "Detach the beaker " else - is_beaker_ready = 1 - beaker_contents = "The beaker contains: " - var/anything = 0 - for(var/datum/reagent/R in beaker.reagents.reagent_list) - anything = 1 - beaker_contents += "[R.volume] - [R.name] " - if(!anything) - beaker_contents += "Nothing " + dat += "Please wait..." - - dat = {" - Processing chamber contains: - [processing_chamber] - [beaker_contents] - "} - if(is_beaker_ready && !is_chamber_empty && !(stat & (NOPOWER|BROKEN))) - dat += "Grind the reagents " - dat += "Juice the reagents " - if(holdingitems && holdingitems.len > 0) - dat += "Eject the reagents " - if(beaker) - dat += "Detach the beaker " - else - dat += "Please wait..." - user << browse(" " + if(S.rarity == 0) // Mundane species + msg += "+0: We don't need samples of mundane species \"[capitalize(S.species)]\". " else if(shuttle_master.discoveredPlants[S.type]) // This species has already been sent to CentComm - var/potDiff = S.seed.get_trait(TRAIT_POTENCY) - shuttle_master.discoveredPlants[S.type] // Compare it to the previous best + var/potDiff = S.potency - shuttle_master.discoveredPlants[S.type] // Compare it to the previous best if(potDiff > 0) // This sample is better - shuttle_master.discoveredPlants[S.type] = S.seed.get_trait(TRAIT_POTENCY) - msg += "+[potDiff]: New sample of \"[capitalize(S.seed.seed_name)]\" is superior. Good work. " + shuttle_master.discoveredPlants[S.type] = S.potency + msg += "+[potDiff]: New sample of \"[capitalize(S.species)]\" is superior. Good work. " shuttle_master.points += potDiff else // This sample is worthless - msg += "+0: New sample of \"[capitalize(S.seed.seed_name)]\" is not more potent than existing sample ([shuttle_master.discoveredPlants[S.type]] potency). " + msg += "+0: New sample of \"[capitalize(S.species)]\" is not more potent than existing sample ([shuttle_master.discoveredPlants[S.type]] potency). " else // This is a new discovery! - shuttle_master.discoveredPlants[S.type] = S.seed.get_trait(TRAIT_POTENCY) - msg += "+[S.seed.get_trait(TRAIT_RARITY)]: New species discovered: \"[capitalize(S.seed.seed_name)]\". Excellent work. " - shuttle_master.points += S.seed.get_trait(TRAIT_RARITY) // That's right, no bonus for potency. Send a crappy sample first to "show improvement" later + shuttle_master.discoveredPlants[S.type] = S.potency + msg += "[S.rarity]: New species discovered: \"[capitalize(S.species)]\". Excellent work. " + shuttle_master.points += S.rarity // That's right, no bonus for potency. Send a crappy sample first to "show improvement" later qdel(MA) shuttle_master.sold_atoms += "." diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index 1bdf29dd191..d1a36f2c4a3 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -186,6 +186,16 @@ temp_chem += R.id temp_chem[R.id] = R.volume B.data["trace_chem"] = list2params(temp_chem) + if(mind) + B.data["mind"] = mind + if(ckey) + B.data["ckey"] = ckey + + if(!suiciding) + B.data["cloneable"] = 1 + B.data["gender"] = gender + B.data["real_name"] = real_name + B.data["factions"] = faction return B //For humans, blood does not appear from blue, it comes from vessels. diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 5dd4d570ee2..8a5a6a6b158 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -526,7 +526,7 @@ /obj/item/organ/internal/honktumor/process() if(isturf(loc)) visible_message("[src] honks in on itself!") - new /obj/item/weapon/bananapeel(get_turf(loc)) + new /obj/item/weapon/grown/bananapeel(get_turf(loc)) qdel(src) diff --git a/code/modules/surgery/organs/subtypes/diona.dm b/code/modules/surgery/organs/subtypes/diona.dm index a4b0d8fb92b..48ad2fb3cea 100644 --- a/code/modules/surgery/organs/subtypes/diona.dm +++ b/code/modules/surgery/organs/subtypes/diona.dm @@ -1,22 +1,3 @@ -/*/proc/spawn_diona_nymph_from_organ(var/obj/item/organ/organ) - if(!istype(organ)) - return 0 - - //This is a terrible hack and I should be ashamed. - var/datum/seed/diona = plant_controller.seeds["diona"] - if(!diona) - return 0 - - spawn(1) // So it has time to be thrown about by the gib() proc. - var/mob/living/simple_animal/diona/D = new(get_turf(organ)) - var/datum/ghosttrap/plant/P = get_ghost_trap("living plant") - P.request_player(D, "A diona nymph has split off from its gestalt. ") - spawn(60) - if(D) - if(!D.ckey || !D.client) - D.death() - return 1 */ - /obj/item/organ/external/chest/diona name = "core trunk" max_damage = 200 diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm new file mode 100644 index 00000000000..63fe8fc81bc --- /dev/null +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -0,0 +1,455 @@ +var/static/regex/stun_words = regex("stop|wait|stand still|hold on|halt") +var/static/regex/weaken_words = regex("drop|fall|trip") +var/static/regex/sleep_words = regex("sleep|slumber") +var/static/regex/vomit_words = regex("vomit|throw up") +var/static/regex/silence_words = regex("shut up|silence|ssh|quiet|hush") +var/static/regex/hallucinate_words = regex("see the truth|hallucinate") +var/static/regex/wakeup_words = regex("wake up|awaken") +var/static/regex/heal_words = regex("live|heal|survive|mend|heroes never die") +var/static/regex/hurt_words = regex("die|suffer") +var/static/regex/bleed_words = regex("bleed") +var/static/regex/burn_words = regex("burn|ignite") +var/static/regex/repulse_words = regex("shoo|go away|leave me alone|begone|flee|fus ro dah") +var/static/regex/whoareyou_words = regex("who are you|say your name|state your name|identify") +var/static/regex/saymyname_words = regex("say my name") +var/static/regex/knockknock_words = regex("knock knock") +var/static/regex/statelaws_words = regex("state laws|state your laws") +var/static/regex/move_words = regex("move") +var/static/regex/walk_words = regex("walk|slow down") +var/static/regex/run_words = regex("run") +var/static/regex/helpintent_words = regex("help") +var/static/regex/disarmintent_words = regex("disarm") +var/static/regex/grabintent_words = regex("grab") +var/static/regex/harmintent_words = regex("harm|fight") +var/static/regex/throwmode_words = regex("throw|catch") +var/static/regex/flip_words = regex("flip|rotate|revolve|roll|somersault") +var/static/regex/rest_words = regex("rest") +var/static/regex/getup_words = regex("get up") +var/static/regex/sit_words = regex("sit") +var/static/regex/stand_words = regex("stand") +var/static/regex/dance_words = regex("dance") +var/static/regex/jump_words = regex("jump") +var/static/regex/salute_words = regex("salute") +var/static/regex/deathgasp_words = regex("play dead") +var/static/regex/clap_words = regex("clap|applaud") +var/static/regex/honk_words = regex("ho+nk") //hooooooonk +var/static/regex/multispin_words = regex("like a record baby") + +/obj/item/organ/internal/vocal_cords //organs that are activated through speech with the :x channel + name = "vocal cords" + icon_state = "appendix" + zone = "mouth" + slot = "vocal_cords" + parent_organ = "mouth" + var/spans = null + +/obj/item/organ/internal/vocal_cords/proc/can_speak_with() //if there is any limitation to speaking with these cords + return TRUE + +/obj/item/organ/internal/vocal_cords/proc/speak_with(message) //do what the organ does + return + +/obj/item/organ/internal/vocal_cords/proc/handle_speech(message) //change the message + return message + +//Colossus drop, forces the listeners to obey certain commands +/obj/item/organ/internal/vocal_cords/colossus + name = "divine vocal cords" + desc = "They carry the voice of an ancient god." + icon_state = "voice_of_god" + actions_types = list(/datum/action/item_action/organ_action/colossus) + var/next_command = 0 + var/cooldown_stun = 1200 + var/cooldown_damage = 600 + var/cooldown_meme = 300 + var/cooldown_none = 150 + var/base_multiplier = 1 + spans = "colossus yell" + +/datum/action/item_action/organ_action/colossus + name = "Voice of God" + var/obj/item/organ/internal/vocal_cords/colossus/cords = null + +/datum/action/item_action/organ_action/colossus/New() + ..() + cords = target + +/datum/action/item_action/organ_action/colossus/IsAvailable() + if(world.time < cords.next_command) + return FALSE + if(!owner) + return FALSE + if(!owner.can_speak()) + return FALSE + if(check_flags & AB_CHECK_CONSCIOUS) + if(owner.stat) + return FALSE + return TRUE + +/datum/action/item_action/organ_action/colossus/Trigger() + . = ..() + if(!IsAvailable()) + if(world.time < cords.next_command) + to_chat(owner, "You must wait [(cords.next_command - world.time)/10] seconds before Speaking again.") + return + var/command = input(owner, "Speak with the Voice of God", "Command") + if(!command) + return + owner.say(".x[command]") + +/obj/item/organ/internal/vocal_cords/colossus/can_speak_with() + if(world.time < next_command) + to_chat(owner, "You must wait [(next_command - world.time)/10] seconds before Speaking again.") + return FALSE + if(!owner) + return FALSE + if(!owner.can_speak()) + to_chat(owner, "You are unable to speak!") + return FALSE + if(owner.stat) + return FALSE + return TRUE + +/obj/item/organ/internal/vocal_cords/colossus/handle_speech(message) + spans = "colossus yell" //reset spans, just in case someone gets deculted or the cords change owner + if(iscultist(owner)) + spans += " narsiesmall" + return "[uppertext(message)]" + +/obj/item/organ/internal/vocal_cords/colossus/speak_with(message) + var/log_message = uppertext(message) + message = lowertext(message) + playsound(get_turf(owner), 'sound/magic/invoke_general.ogg', 300, 1, 5) + + var/mob/living/list/listeners = list() + for(var/mob/living/L in get_mobs_in_view(8, owner, TRUE)) + if(!L.ear_deaf && !L.null_rod_check() && L != owner && L.stat != DEAD) + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(istype(H.l_ear, /obj/item/clothing/ears/earmuffs) || istype(H.r_ear, /obj/item/clothing/ears/earmuffs)) + continue + listeners += L + + if(!listeners.len) + next_command = world.time + cooldown_none + return + + var/power_multiplier = base_multiplier + + if(owner.mind) + //Chaplains are very good at speaking with the voice of god + if(owner.mind.assigned_role == "Chaplain") + power_multiplier *= 2 + //Command staff has authority + if(owner.mind.assigned_role in command_positions) + power_multiplier *= 1.4 + //Why are you speaking + if(owner.mind.assigned_role == "Mime") + power_multiplier *= 0.5 + + //Cultists are closer to their gods and are more powerful, but they'll give themselves away + if(iscultist(owner)) + power_multiplier *= 2 + + //Try to check if the speaker specified a name or a job to focus on + var/list/specific_listeners = list() + var/found_string = null + + for(var/V in listeners) + var/mob/living/L = V + /*if(L.mind && L.mind.devilinfo && findtext(message, L.mind.devilinfo.truename)) + var/start = findtext(message, L.mind.devilinfo.truename) + listeners = list(L) //let's be honest you're never going to find two devils with the same name + power_multiplier *= 5 //if you're a devil and god himself addressed you, you fucked up + //Cut out the name so it doesn't trigger commands + message = copytext(message, 0, start)+copytext(message, start + length(L.mind.devilinfo.truename), length(message) + 1) + break*/ + if(findtext(message, L.real_name) == 1) + specific_listeners += L //focus on those with the specified name + //Cut out the name so it doesn't trigger commands + found_string = L.real_name + + else if(L.mind && findtext(message, L.mind.assigned_role) == 1) + specific_listeners += L //focus on those with the specified job + //Cut out the job so it doesn't trigger commands + found_string = L.mind.assigned_role + + if(specific_listeners.len) + listeners = specific_listeners + power_multiplier *= (1 + (1/specific_listeners.len)) //2x on a single guy, 1.5x on two and so on + message = copytext(message, 0, 1)+copytext(message, 1 + length(found_string), length(message) + 1) + + //STUN + if(findtext(message, stun_words)) + for(var/V in listeners) + var/mob/living/L = V + L.Stun(3 * power_multiplier) + next_command = world.time + cooldown_stun + + //WEAKEN + else if(findtext(message, weaken_words)) + for(var/V in listeners) + var/mob/living/L = V + L.Weaken(3 * power_multiplier) + next_command = world.time + cooldown_stun + + //SLEEP + else if((findtext(message, sleep_words))) + for(var/V in listeners) + var/mob/living/L = V + L.Sleeping(2 * power_multiplier) + next_command = world.time + cooldown_stun + + //VOMIT + else if((findtext(message, vomit_words))) + for(var/mob/living/carbon/C in listeners) + C.vomit(10 * power_multiplier) + next_command = world.time + cooldown_stun + + //SILENCE + else if((findtext(message, silence_words))) + for(var/mob/living/carbon/C in listeners) + if(owner.mind && (owner.mind.assigned_role == "Librarian" || owner.mind.assigned_role == "Mime")) + power_multiplier *= 3 + C.silent += (10 * power_multiplier) + next_command = world.time + cooldown_stun + + //HALLUCINATE + else if((findtext(message, hallucinate_words))) + for(var/V in listeners) + var/mob/living/L = V + new /obj/effect/hallucination/delusion(get_turf(L),L,duration=150 * power_multiplier,skip_nearby=0) + next_command = world.time + cooldown_meme + + //WAKE UP + else if((findtext(message, wakeup_words))) + for(var/V in listeners) + var/mob/living/L = V + L.SetSleeping(0) + next_command = world.time + cooldown_damage + + //HEAL + else if((findtext(message, heal_words))) + for(var/V in listeners) + var/mob/living/L = V + L.heal_overall_damage(10 * power_multiplier, 10 * power_multiplier, 0, 0) + next_command = world.time + cooldown_damage + + //BRUTE DAMAGE + else if((findtext(message, hurt_words))) + for(var/V in listeners) + var/mob/living/L = V + L.apply_damage(15 * power_multiplier, def_zone = "chest") + next_command = world.time + cooldown_damage + + //BLEED + else if((findtext(message, bleed_words))) + for(var/mob/living/carbon/human/H in listeners) + H.drip(100 * power_multiplier) + next_command = world.time + cooldown_damage + + //FIRE + else if((findtext(message, burn_words))) + for(var/V in listeners) + var/mob/living/L = V + L.adjust_fire_stacks(1 * power_multiplier) + L.IgniteMob() + next_command = world.time + cooldown_damage + + //REPULSE + else if((findtext(message, repulse_words))) + for(var/V in listeners) + var/mob/living/L = V + var/throwtarget = get_edge_target_turf(owner, get_dir(owner, get_step_away(L, owner))) + L.throw_at(throwtarget, 3 * power_multiplier, 1) + next_command = world.time + cooldown_damage + + //WHO ARE YOU? + else if((findtext(message, whoareyou_words))) + for(var/V in listeners) + var/mob/living/L = V + /*if(L.mind && L.mind.devilinfo) + L.say("[L.mind.devilinfo.truename]") + else*/ + L.say("[L.real_name]") + next_command = world.time + cooldown_meme + + //SAY MY NAME + else if((findtext(message, saymyname_words))) + for(var/V in listeners) + var/mob/living/L = V + L.say("[owner.name]!") //"Unknown!" + next_command = world.time + cooldown_meme + + //KNOCK KNOCK + else if((findtext(message, knockknock_words))) + for(var/V in listeners) + var/mob/living/L = V + L.say("Who's there?") + next_command = world.time + cooldown_meme + + //STATE LAWS + else if((findtext(message, statelaws_words))) + for(var/mob/living/silicon/S in listeners) + S.statelaws(S.laws) + next_command = world.time + cooldown_stun + + //MOVE + else if((findtext(message, move_words))) + for(var/V in listeners) + var/mob/living/L = V + step(L, pick(cardinal)) + next_command = world.time + cooldown_meme + + //WALK + else if((findtext(message, walk_words))) + for(var/V in listeners) + var/mob/living/L = V + if(L.m_intent != "walk") + L.m_intent = "walk" + if(L.hud_used) + L.hud_used.move_intent.icon_state = "walking" + next_command = world.time + cooldown_meme + + //RUN + else if((findtext(message, run_words))) + for(var/V in listeners) + var/mob/living/L = V + if(L.m_intent != "run") + L.m_intent = "run" + if(L.hud_used) + L.hud_used.move_intent.icon_state = "running" + next_command = world.time + cooldown_meme + + //HELP INTENT + else if((findtext(message, helpintent_words))) + for(var/mob/living/carbon/human/H in listeners) + H.a_intent_change(I_HELP) + next_command = world.time + cooldown_meme + + //DISARM INTENT + else if((findtext(message, disarmintent_words))) + for(var/mob/living/carbon/human/H in listeners) + H.a_intent_change(I_DISARM) + next_command = world.time + cooldown_meme + + //GRAB INTENT + else if((findtext(message, grabintent_words))) + for(var/mob/living/carbon/human/H in listeners) + H.a_intent_change(I_GRAB) + next_command = world.time + cooldown_meme + + //HARM INTENT + else if((findtext(message, harmintent_words))) + for(var/mob/living/carbon/human/H in listeners) + H.a_intent_change(I_HARM) + next_command = world.time + cooldown_meme + + //THROW/CATCH + else if((findtext(message, throwmode_words))) + for(var/mob/living/carbon/C in listeners) + C.throw_mode_on() + next_command = world.time + cooldown_meme + + //FLIP + else if((findtext(message, flip_words))) + for(var/V in listeners) + var/mob/living/L = V + L.emote("flip") + next_command = world.time + cooldown_meme + + //REST + else if((findtext(message, rest_words))) + for(var/V in listeners) + var/mob/living/L = V + if(!L.resting) + L.lay_down() + next_command = world.time + cooldown_meme + + //GET UP + else if((findtext(message, getup_words))) + for(var/V in listeners) + var/mob/living/L = V + if(L.resting) + L.lay_down() //aka get up + L.SetStunned(0) + L.SetWeakened(0) + L.SetParalysis(0) //i said get up i don't care if you're being tazed + next_command = world.time + cooldown_damage + + //SIT + else if((findtext(message, sit_words))) + for(var/V in listeners) + var/mob/living/L = V + for(var/obj/structure/stool/bed/chair/chair in get_turf(L)) + chair.buckle_mob(L) + break + next_command = world.time + cooldown_meme + + //STAND UP + else if((findtext(message, stand_words))) + for(var/V in listeners) + var/mob/living/L = V + if(L.buckled && istype(L.buckled, /obj/structure/stool/bed/chair)) + L.buckled.unbuckle_mob(L) + next_command = world.time + cooldown_meme + + //DANCE + else if((findtext(message, dance_words))) + for(var/V in listeners) + var/mob/living/L = V + L.emote("dance") + next_command = world.time + cooldown_meme + + //JUMP + else if((findtext(message, jump_words))) + for(var/V in listeners) + var/mob/living/L = V + L.say("HOW HIGH?!!") + L.emote("jump") + next_command = world.time + cooldown_meme + + //SALUTE + else if((findtext(message, salute_words))) + for(var/V in listeners) + var/mob/living/L = V + L.emote("salute") + next_command = world.time + cooldown_meme + + //PLAY DEAD + else if((findtext(message, deathgasp_words))) + for(var/V in listeners) + var/mob/living/L = V + L.emote("deathgasp") + next_command = world.time + cooldown_meme + + //PLEASE CLAP + else if((findtext(message, clap_words))) + for(var/V in listeners) + var/mob/living/L = V + L.emote("clap") + next_command = world.time + cooldown_meme + + //HONK + else if((findtext(message, honk_words))) + spawn(25) + playsound(get_turf(owner), "sound/items/bikehorn.ogg", 300, 1) + if(owner.mind && owner.mind.assigned_role == "Clown") + for(var/mob/living/carbon/C in listeners) + C.slip(0,7 * power_multiplier) + next_command = world.time + cooldown_stun + else + next_command = world.time + cooldown_meme + + //RIGHT ROUND + else if((findtext(message, multispin_words))) + for(var/V in listeners) + var/mob/living/L = V + L.SpinAnimation(speed = 10, loops = 5) + next_command = world.time + cooldown_meme + + else + next_command = world.time + cooldown_none + + message_admins("[key_name_admin(owner)] has said '[log_message]' with a Voice of God, affecting [english_list(listeners)], with a power multiplier of [power_multiplier].") + log_game("[key_name(owner)] has said '[log_message]' with a Voice of God, affecting [english_list(listeners)], with a power multiplier of [power_multiplier].") \ No newline at end of file diff --git a/code/modules/telesci/gps.dm b/code/modules/telesci/gps.dm index 2d99a538244..7558fd602ee 100644 --- a/code/modules/telesci/gps.dm +++ b/code/modules/telesci/gps.dm @@ -10,6 +10,7 @@ var/list/GPS_list = list() var/gpstag = "COM0" var/emped = 0 var/turf/locked_location + var/tracking = TRUE /obj/item/device/gps/New() ..() @@ -25,13 +26,32 @@ var/list/GPS_list = list() emped = 1 overlays -= "working" overlays += "emp" - spawn(300) - emped = 0 - overlays -= "emp" + addtimer(src, "reboot", 300) + +/obj/item/device/gps/proc/reboot() + emped = FALSE + overlays -= "emp" + overlays += "working" + +/obj/item/device/gps/AltClick(mob/user) + if(CanUseTopic(user, inventory_state) != STATUS_INTERACTIVE) + return 1 //user not valid to use gps + if(emped) + to_chat(user, "It's busted!") + if(tracking) + overlays -= "working" + to_chat(user, "[src] is no longer tracking, or visible to other GPS devices.") + tracking = FALSE + else overlays += "working" + to_chat(user, "[src] is now tracking, and visible to other GPS devices.") + tracking = TRUE /obj/item/device/gps/attack_self(mob/user as mob) - + if(!tracking) + to_chat(user, "[src] is turned off. Use alt+click to toggle it back on.") + return + var/obj/item/device/gps/t = "" var/gps_window_height = 110 + GPS_list.len * 20 // Variable window height, depending on how many GPS units there are to show if(emped) @@ -49,8 +69,10 @@ var/list/GPS_list = list() var/tracked_gpstag = G.gpstag if(G.emped == 1) t += " [tracked_gpstag]: ERROR" - else + else if(G.tracking) t += " [tracked_gpstag]: [format_text(gps_area.name)] ([pos.x], [pos.y], [pos.z])" + else + continue var/datum/browser/popup = new(user, "GPS", name, 360, min(gps_window_height, 800)) popup.set_content(t) @@ -58,9 +80,14 @@ var/list/GPS_list = list() popup.open() /obj/item/device/gps/Topic(href, href_list) - ..() + if(..(state = inventory_state)) + return 1 + if(href_list["tag"] ) - var/a = input("Please enter desired tag.", name, gpstag) as text + var/a = input("Please enter desired tag.", name, gpstag) as text|null + if(!a || ..(state = inventory_state)) + return 1 + a = uppertext(sanitize(copytext(a, 1, 5))) if(src.loc == usr) gpstag = a @@ -84,4 +111,55 @@ var/list/GPS_list = list() icon_state = "gps-b" gpstag = "BORG0" desc = "A mining cyborg internal positioning system. Used as a recovery beacon for damaged cyborg assets, or a collaboration tool for mining teams." - flags = NODROP \ No newline at end of file + flags = NODROP + +/obj/item/device/gps/internal + icon_state = null + flags = ABSTRACT + gpstag = "Eerie Signal" + desc = "Report to a coder immediately." + invisibility = INVISIBILITY_MAXIMUM + +/obj/item/device/gps/internal/mining + icon_state = "gps-m" + gpstag = "MINER" + desc = "A positioning system helpful for rescuing trapped or injured miners, keeping one on you at all times while mining might just save your life." + +/obj/item/device/gps/internal/base + gpstag = "NT_AUX" + desc = "A homing signal from Nanotrasen's mining base." + +/obj/item/device/gps/visible_debug + name = "visible GPS" + gpstag = "ADMIN" + desc = "This admin-spawn GPS unit leaves the coordinates visible \ + on any turf that it passes over, for debugging. Especially useful \ + for marking the area around the transition edges." + var/list/turf/tagged + +/obj/item/device/gps/visible_debug/New() + . = ..() + tagged = list() + fast_processing.Add(src) + +/obj/item/device/gps/visible_debug/process() + var/turf/T = get_turf(src) + if(T) + // I assume it's faster to color,tag and OR the turf in, rather + // then checking if its there + T.color = RANDOM_COLOUR + T.maptext = "[T.x],[T.y],[T.z]" + tagged |= T + +/obj/item/device/gps/visible_debug/proc/clear() + while(tagged.len) + var/turf/T = pop(tagged) + T.color = initial(T.color) + T.maptext = initial(T.maptext) + +/obj/item/device/gps/visible_debug/Destroy() + if(tagged) + clear() + tagged = null + fast_processing.Remove(src) + . = ..() \ No newline at end of file diff --git a/code/modules/vehicle/atv.dm b/code/modules/vehicle/atv.dm index d4a843b80bf..a67310c8874 100644 --- a/code/modules/vehicle/atv.dm +++ b/code/modules/vehicle/atv.dm @@ -9,45 +9,39 @@ vehicle_move_delay = 1 var/static/image/atvcover = null - /obj/vehicle/atv/New() ..() if(!atvcover) atvcover = image("icons/vehicles/4wheeler.dmi", "4wheeler_north") atvcover.layer = MOB_LAYER + 0.1 - -obj/vehicle/atv/post_buckle_mob(mob/living/M) +/obj/vehicle/atv/post_buckle_mob(mob/living/M) if(buckled_mob) overlays += atvcover else overlays -= atvcover - /obj/vehicle/atv/handle_vehicle_layer() if(dir == SOUTH) layer = MOB_LAYER+0.1 else layer = OBJ_LAYER - //TURRETS! /obj/vehicle/atv/turret - var/obj/machinery/gun_turret/vehicle_turret/turret = null + var/obj/machinery/porta_turret/syndicate/vehicle_turret/turret = null - -/obj/machinery/gun_turret/vehicle_turret +/obj/machinery/porta_turret/syndicate/vehicle_turret name = "mounted turret" scan_range = 7 + emp_vulnerable = 1 density = 0 - /obj/vehicle/atv/turret/New() ..() turret = new(loc) //turret.base = src - /obj/vehicle/atv/turret/handle_vehicle_layer() if(dir == SOUTH) layer = MOB_LAYER+0.1 @@ -60,7 +54,6 @@ obj/vehicle/atv/post_buckle_mob(mob/living/M) else turret.layer = OBJ_LAYER - /obj/vehicle/atv/turret/handle_vehicle_offsets() ..() if(turret) diff --git a/code/world.dm b/code/world.dm index 08430bc0475..c33b41388f4 100644 --- a/code/world.dm +++ b/code/world.dm @@ -49,7 +49,6 @@ var/global/list/map_transition_config = MAP_TRANSITION_CONFIG . = ..() - plant_controller = new() // Create robolimbs for chargen. populate_robolimb_list() diff --git a/config/example/config.txt b/config/example/config.txt index 8f7e0e009ef..5bcd469be87 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -326,5 +326,12 @@ DISABLE_SPACE_RUINS # Setting this to 0 will disable the penalty period #ROUND_ABANDON_PENALTY_PERIOD 30 +## Hub address for tracking stats +## example: Hubmakerckey.Hubname +#MEDAL_HUB_ADDRESS + +## Password for the hub page +#MEDAL_HUB_PASSWORD + ## Uncomment this if you want to disable usage of emoji in OOC #DISABLE_OOC_EMOJI \ No newline at end of file diff --git a/goon/browserassets/css/browserOutput.css b/goon/browserassets/css/browserOutput.css index c7204d36577..970e94bb3b4 100644 --- a/goon/browserassets/css/browserOutput.css +++ b/goon/browserassets/css/browserOutput.css @@ -239,6 +239,7 @@ em {font-style: normal; font-weight: bold;} .playerreply {color: #8800bb; font-weight: bold;} .name { font-weight: bold;} .say {} +.yell { font-weight: bold;} .siliconsay {font-family: 'Courier New', Courier, monospace;} .deadsay {color: #5c00e6;} .radio {color: #008000;} @@ -323,8 +324,9 @@ h1.alert, h2.alert {color: #000000;} .cult {color: #800080; font-weight: bold; font-style: italic;} .cultspeech {color: #7F0000; font-style: italic;} .cultitalic {color: #960000; font-style: italic;} -.cultlarge {color: #960000; font-weight: bold; font-size: 3;} -.narsie {color: #960000; font-weight: bold; font-size: 15;} +.cultlarge {color: #960000; font-weight: bold; font-size: 120%;} +.narsie {color: #960000; font-weight: bold; font-size: 300%;} +.narsiesmall {color: #960000; font-weight: bold; font-size: 200%;} .interface {color: #330033;} .big {font-size: 150%;} .reallybig {font-size: 175%;} @@ -341,10 +343,10 @@ h1.alert, h2.alert {color: #000000;} .revennotice {color: #1d2953;} .revenboldnotice {color: #1d2953; font-weight: bold;} -.revenbignotice {color: #1d2953; font-weight: bold; font-size: 3;} +.revenbignotice {color: #1d2953; font-weight: bold; font-size: 120%;} .revenminor {color: #823abb} .revenwarning {color: #760fbb; font-style: italic;} -.revendanger {color: #760fbb; font-weight: bold; font-size: 3;} +.revendanger {color: #760fbb; font-weight: bold; font-size: 120%;} /* /vg/ */ .good {color: green;} @@ -358,7 +360,7 @@ h1.alert, h2.alert {color: #000000;} "} .memo {color: #638500; text-align: center;} -.memoedit {text-align: center; font-size: 2;} +.memoedit {text-align: center; font-size: 75%;} .connectionClosed, .fatalError {background: red; color: white; padding: 5px;} .connectionClosed.restored {background: green;} @@ -368,5 +370,10 @@ h1.alert, h2.alert {color: #000000;} .text-normal {font-weight: normal; font-style: normal;} .hidden {display: none; visibility: hidden;} +/* MEGAFAUNA */ +.colossus {color: #7F282A; font-size: 175%;} +.hierophant {color: #660099; font-weight: bold; font-size: 150%;} +.hierophant_warning {color: #660099; font-weight: bold; font-style: italic;} + /* EMOJI STUFF */ .emoji {max-height: 16px; max-width: 16px} \ No newline at end of file diff --git a/html/changelog.html b/html/changelog.html index d6d77e1998b..332bc3e6ddf 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -55,6 +55,49 @@ -->
+
{{/if}}
- {{if data.is_lethal}}
+ {{if data.lethal_control}}
05 February 2017+Fethas updated:+
Fox McCloud and Core0verload updated:+
Ported by Markolie, developed by KorPhaeron and others from /tg/. The Hierophant was developed by ChangelingRain. updated:+
uraniummeltdown updated:+
03 February 2017DrunkDwarf updated:
Lethal Mode:
diff --git a/paradise.dme b/paradise.dme
index 0f9fa26434c..9776de13d69 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -304,6 +304,7 @@
#include "code\datums\spells\mind_transfer.dm"
#include "code\datums\spells\projectile.dm"
#include "code\datums\spells\rathens.dm"
+#include "code\datums\spells\shapeshift.dm"
#include "code\datums\spells\summonitem.dm"
#include "code\datums\spells\touch_attacks.dm"
#include "code\datums\spells\trigger.dm"
@@ -490,7 +491,6 @@
#include "code\game\machinery\atmo_control.dm"
#include "code\game\machinery\autolathe.dm"
#include "code\game\machinery\Beacon.dm"
-#include "code\game\machinery\biogenerator.dm"
#include "code\game\machinery\buttons.dm"
#include "code\game\machinery\cell_charger.dm"
#include "code\game\machinery\chiller.dm"
@@ -530,7 +530,6 @@
#include "code\game\machinery\recycler.dm"
#include "code\game\machinery\requests_console.dm"
#include "code\game\machinery\robot_fabricator.dm"
-#include "code\game\machinery\seed_extractor.dm"
#include "code\game\machinery\shieldgen.dm"
#include "code\game\machinery\Sleeper.dm"
#include "code\game\machinery\slotmachine.dm"
@@ -544,7 +543,6 @@
#include "code\game\machinery\transformer.dm"
#include "code\game\machinery\turntable.dm"
#include "code\game\machinery\turret_control.dm"
-#include "code\game\machinery\turrets.dm"
#include "code\game\machinery\vending.dm"
#include "code\game\machinery\washing_machine.dm"
#include "code\game\machinery\wishgranter.dm"
@@ -1362,30 +1360,51 @@
#include "code\modules\garbage_collection\gc_testing.dm"
#include "code\modules\holiday\christmas.dm"
#include "code\modules\holiday\holiday.dm"
+#include "code\modules\hydroponics\biogenerator.dm"
+#include "code\modules\hydroponics\gene_modder.dm"
#include "code\modules\hydroponics\grown.dm"
-#include "code\modules\hydroponics\grown_inedible.dm"
-#include "code\modules\hydroponics\grown_predefined.dm"
-#include "code\modules\hydroponics\hydro_reagents.dm"
-#include "code\modules\hydroponics\seed.dm"
-#include "code\modules\hydroponics\seed_controller.dm"
-#include "code\modules\hydroponics\seed_datums.dm"
-#include "code\modules\hydroponics\seed_machines.dm"
-#include "code\modules\hydroponics\seed_mobs.dm"
-#include "code\modules\hydroponics\seed_packets.dm"
-#include "code\modules\hydroponics\seed_storage.dm"
+#include "code\modules\hydroponics\growninedible.dm"
+#include "code\modules\hydroponics\hydroitemdefines.dm"
+#include "code\modules\hydroponics\hydroponics.dm"
+#include "code\modules\hydroponics\plant_genes.dm"
+#include "code\modules\hydroponics\sample.dm"
+#include "code\modules\hydroponics\seed_extractor.dm"
+#include "code\modules\hydroponics\seeds.dm"
#include "code\modules\hydroponics\beekeeping\beebox.dm"
#include "code\modules\hydroponics\beekeeping\beekeeper_suit.dm"
#include "code\modules\hydroponics\beekeeping\honey_frame.dm"
#include "code\modules\hydroponics\beekeeping\honeycomb.dm"
-#include "code\modules\hydroponics\spreading\spreading.dm"
-#include "code\modules\hydroponics\spreading\spreading_growth.dm"
-#include "code\modules\hydroponics\spreading\spreading_response.dm"
-#include "code\modules\hydroponics\trays\tray.dm"
-#include "code\modules\hydroponics\trays\tray_process.dm"
-#include "code\modules\hydroponics\trays\tray_reagents.dm"
-#include "code\modules\hydroponics\trays\tray_soil.dm"
-#include "code\modules\hydroponics\trays\tray_tools.dm"
-#include "code\modules\hydroponics\trays\tray_update_icons.dm"
+#include "code\modules\hydroponics\grown\ambrosia.dm"
+#include "code\modules\hydroponics\grown\apple.dm"
+#include "code\modules\hydroponics\grown\banana.dm"
+#include "code\modules\hydroponics\grown\beans.dm"
+#include "code\modules\hydroponics\grown\berries.dm"
+#include "code\modules\hydroponics\grown\cannabis.dm"
+#include "code\modules\hydroponics\grown\cereals.dm"
+#include "code\modules\hydroponics\grown\chili.dm"
+#include "code\modules\hydroponics\grown\citrus.dm"
+#include "code\modules\hydroponics\grown\cocoa_vanilla.dm"
+#include "code\modules\hydroponics\grown\corn.dm"
+#include "code\modules\hydroponics\grown\eggplant.dm"
+#include "code\modules\hydroponics\grown\flowers.dm"
+#include "code\modules\hydroponics\grown\grass_carpet.dm"
+#include "code\modules\hydroponics\grown\herbals.dm"
+#include "code\modules\hydroponics\grown\kudzu.dm"
+#include "code\modules\hydroponics\grown\melon.dm"
+#include "code\modules\hydroponics\grown\misc.dm"
+#include "code\modules\hydroponics\grown\mushrooms.dm"
+#include "code\modules\hydroponics\grown\nettle.dm"
+#include "code\modules\hydroponics\grown\nymph.dm"
+#include "code\modules\hydroponics\grown\peanut.dm"
+#include "code\modules\hydroponics\grown\potato.dm"
+#include "code\modules\hydroponics\grown\pumpkin.dm"
+#include "code\modules\hydroponics\grown\random.dm"
+#include "code\modules\hydroponics\grown\replicapod.dm"
+#include "code\modules\hydroponics\grown\root.dm"
+#include "code\modules\hydroponics\grown\tea_coffee.dm"
+#include "code\modules\hydroponics\grown\tobacco.dm"
+#include "code\modules\hydroponics\grown\tomato.dm"
+#include "code\modules\hydroponics\grown\towercap.dm"
#include "code\modules\karma\karma.dm"
#include "code\modules\library\admin.dm"
#include "code\modules\library\lib_items.dm"
@@ -1431,6 +1450,9 @@
#include "code\modules\mining\laborcamp\laborminerals.dm"
#include "code\modules\mining\laborcamp\laborshuttle.dm"
#include "code\modules\mining\laborcamp\laborstacker.dm"
+#include "code\modules\mining\lavaland\ash_flora.dm"
+#include "code\modules\mining\lavaland\colossus_loot.dm"
+#include "code\modules\mining\lavaland\necropolis_chests.dm"
#include "code\modules\mob\abilities.dm"
#include "code\modules\mob\death.dm"
#include "code\modules\mob\emote.dm"
@@ -1642,6 +1664,7 @@
#include "code\modules\mob\living\simple_animal\bot\secbot.dm"
#include "code\modules\mob\living\simple_animal\friendly\butterfly.dm"
#include "code\modules\mob\living\simple_animal\friendly\cat.dm"
+#include "code\modules\mob\living\simple_animal\friendly\cockroach.dm"
#include "code\modules\mob\living\simple_animal\friendly\corgi.dm"
#include "code\modules\mob\living\simple_animal\friendly\corgi_powers.dm"
#include "code\modules\mob\living\simple_animal\friendly\crab.dm"
@@ -1679,7 +1702,16 @@
#include "code\modules\mob\living\simple_animal\hostile\statue.dm"
#include "code\modules\mob\living\simple_animal\hostile\syndicate.dm"
#include "code\modules\mob\living\simple_animal\hostile\tree.dm"
+#include "code\modules\mob\living\simple_animal\hostile\venus_human_trap.dm"
#include "code\modules\mob\living\simple_animal\hostile\winter_mobs.dm"
+#include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm"
+#include "code\modules\mob\living\simple_animal\hostile\megafauna\colossus.dm"
+#include "code\modules\mob\living\simple_animal\hostile\megafauna\dragon.dm"
+#include "code\modules\mob\living\simple_animal\hostile\megafauna\hierophant.dm"
+#include "code\modules\mob\living\simple_animal\hostile\megafauna\legion.dm"
+#include "code\modules\mob\living\simple_animal\hostile\megafauna\megafauna.dm"
+#include "code\modules\mob\living\simple_animal\hostile\mining\goldgrub.dm"
+#include "code\modules\mob\living\simple_animal\hostile\mining\hivelord.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\fish.dm"
@@ -1833,6 +1865,7 @@
#include "code\modules\projectiles\guns\magic\staff.dm"
#include "code\modules\projectiles\guns\magic\wand.dm"
#include "code\modules\projectiles\guns\projectile\automatic.dm"
+#include "code\modules\projectiles\guns\projectile\bow.dm"
#include "code\modules\projectiles\guns\projectile\launchers.dm"
#include "code\modules\projectiles\guns\projectile\pistol.dm"
#include "code\modules\projectiles\guns\projectile\revolver.dm"
@@ -1912,6 +1945,7 @@
#include "code\modules\research\server.dm"
#include "code\modules\research\designs\AI_module_designs.dm"
#include "code\modules\research\designs\autolathe_designs.dm"
+#include "code\modules\research\designs\biogenerator_designs.dm"
#include "code\modules\research\designs\bluespace_designs.dm"
#include "code\modules\research\designs\comp_board_designs.dm"
#include "code\modules\research\designs\equipment_designs.dm"
@@ -2056,6 +2090,7 @@
#include "code\modules\surgery\organs\parasites.dm"
#include "code\modules\surgery\organs\robolimbs.dm"
#include "code\modules\surgery\organs\skeleton.dm"
+#include "code\modules\surgery\organs\vocal_cords.dm"
#include "code\modules\surgery\organs\wound.dm"
#include "code\modules\surgery\organs\subtypes\diona.dm"
#include "code\modules\surgery\organs\subtypes\drask.dm"
diff --git a/sound/magic/Blind.ogg b/sound/magic/Blind.ogg
new file mode 100644
index 00000000000..ded32a594e4
Binary files /dev/null and b/sound/magic/Blind.ogg differ
diff --git a/sound/magic/CastSummon.ogg b/sound/magic/CastSummon.ogg
new file mode 100644
index 00000000000..493fb239764
Binary files /dev/null and b/sound/magic/CastSummon.ogg differ
diff --git a/sound/magic/Disable_Tech.ogg b/sound/magic/Disable_Tech.ogg
new file mode 100644
index 00000000000..478dd042b53
Binary files /dev/null and b/sound/magic/Disable_Tech.ogg differ
diff --git a/sound/magic/Ethereal_Enter.ogg b/sound/magic/Ethereal_Enter.ogg
new file mode 100644
index 00000000000..a0c7222c167
Binary files /dev/null and b/sound/magic/Ethereal_Enter.ogg differ
diff --git a/sound/magic/Ethereal_Exit.ogg b/sound/magic/Ethereal_Exit.ogg
new file mode 100644
index 00000000000..bd60ca8c695
Binary files /dev/null and b/sound/magic/Ethereal_Exit.ogg differ
diff --git a/sound/magic/Fireball.ogg b/sound/magic/Fireball.ogg
new file mode 100644
index 00000000000..b9f6937f06b
Binary files /dev/null and b/sound/magic/Fireball.ogg differ
diff --git a/sound/magic/ForceWall.ogg b/sound/magic/ForceWall.ogg
new file mode 100644
index 00000000000..4fbca3f42b7
Binary files /dev/null and b/sound/magic/ForceWall.ogg differ
diff --git a/sound/magic/HorseHead_curse.ogg b/sound/magic/HorseHead_curse.ogg
new file mode 100644
index 00000000000..598d096036b
Binary files /dev/null and b/sound/magic/HorseHead_curse.ogg differ
diff --git a/sound/magic/Knock.ogg b/sound/magic/Knock.ogg
new file mode 100644
index 00000000000..fe2f54f97ac
Binary files /dev/null and b/sound/magic/Knock.ogg differ
diff --git a/sound/magic/MAGIC_MISSILE.ogg b/sound/magic/MAGIC_MISSILE.ogg
new file mode 100644
index 00000000000..9d0799d82e9
Binary files /dev/null and b/sound/magic/MAGIC_MISSILE.ogg differ
diff --git a/sound/magic/MM_Hit.ogg b/sound/magic/MM_Hit.ogg
new file mode 100644
index 00000000000..4f09ef46d51
Binary files /dev/null and b/sound/magic/MM_Hit.ogg differ
diff --git a/sound/magic/MindSwap.ogg b/sound/magic/MindSwap.ogg
new file mode 100644
index 00000000000..2f4848becb0
Binary files /dev/null and b/sound/magic/MindSwap.ogg differ
diff --git a/sound/magic/Repulse.ogg b/sound/magic/Repulse.ogg
new file mode 100644
index 00000000000..ed010370754
Binary files /dev/null and b/sound/magic/Repulse.ogg differ
diff --git a/sound/magic/Smoke.ogg b/sound/magic/Smoke.ogg
new file mode 100644
index 00000000000..ca50a349676
Binary files /dev/null and b/sound/magic/Smoke.ogg differ
diff --git a/sound/magic/Staff_Change.ogg b/sound/magic/Staff_Change.ogg
new file mode 100644
index 00000000000..9e6eb72ae06
Binary files /dev/null and b/sound/magic/Staff_Change.ogg differ
diff --git a/sound/magic/Staff_Door.ogg b/sound/magic/Staff_Door.ogg
new file mode 100644
index 00000000000..220f8124825
Binary files /dev/null and b/sound/magic/Staff_Door.ogg differ
diff --git a/sound/magic/Staff_Healing.ogg b/sound/magic/Staff_Healing.ogg
new file mode 100644
index 00000000000..af2a56049c8
Binary files /dev/null and b/sound/magic/Staff_Healing.ogg differ
diff --git a/sound/magic/Staff_animation.ogg b/sound/magic/Staff_animation.ogg
new file mode 100644
index 00000000000..8abd7faa60f
Binary files /dev/null and b/sound/magic/Staff_animation.ogg differ
diff --git a/sound/magic/SummonItems_generic.ogg b/sound/magic/SummonItems_generic.ogg
new file mode 100644
index 00000000000..80cc412a4a8
Binary files /dev/null and b/sound/magic/SummonItems_generic.ogg differ
diff --git a/sound/magic/Summon_Karp.ogg b/sound/magic/Summon_Karp.ogg
new file mode 100644
index 00000000000..d3890776f6d
Binary files /dev/null and b/sound/magic/Summon_Karp.ogg differ
diff --git a/sound/magic/Summon_Magic.ogg b/sound/magic/Summon_Magic.ogg
new file mode 100644
index 00000000000..86b33032826
Binary files /dev/null and b/sound/magic/Summon_Magic.ogg differ
diff --git a/sound/magic/Summon_guns.ogg b/sound/magic/Summon_guns.ogg
new file mode 100644
index 00000000000..b9b5394a4ed
Binary files /dev/null and b/sound/magic/Summon_guns.ogg differ
diff --git a/sound/magic/Tail_swing.ogg b/sound/magic/Tail_swing.ogg
new file mode 100644
index 00000000000..d39462fae30
Binary files /dev/null and b/sound/magic/Tail_swing.ogg differ
diff --git a/sound/magic/Teleport_app.ogg b/sound/magic/Teleport_app.ogg
new file mode 100644
index 00000000000..dd9222ae0c3
Binary files /dev/null and b/sound/magic/Teleport_app.ogg differ
diff --git a/sound/magic/Teleport_diss.ogg b/sound/magic/Teleport_diss.ogg
new file mode 100644
index 00000000000..edc0c55ed00
Binary files /dev/null and b/sound/magic/Teleport_diss.ogg differ
diff --git a/sound/magic/WandODeath.ogg b/sound/magic/WandODeath.ogg
new file mode 100644
index 00000000000..4412609657c
Binary files /dev/null and b/sound/magic/WandODeath.ogg differ
diff --git a/sound/magic/Wand_Teleport.ogg b/sound/magic/Wand_Teleport.ogg
new file mode 100644
index 00000000000..8a300d7763f
Binary files /dev/null and b/sound/magic/Wand_Teleport.ogg differ
diff --git a/sound/magic/blink.ogg b/sound/magic/blink.ogg
new file mode 100644
index 00000000000..58b7cc8562c
Binary files /dev/null and b/sound/magic/blink.ogg differ
diff --git a/sound/magic/invoke_general.ogg b/sound/magic/invoke_general.ogg
new file mode 100644
index 00000000000..6f0c0dd097a
Binary files /dev/null and b/sound/magic/invoke_general.ogg differ
diff --git a/sound/magic/narsie_attack.ogg b/sound/magic/narsie_attack.ogg
new file mode 100644
index 00000000000..89fac778058
Binary files /dev/null and b/sound/magic/narsie_attack.ogg differ
diff --git a/sound/magic/ratvar_attack.ogg b/sound/magic/ratvar_attack.ogg
new file mode 100644
index 00000000000..1c17fe10510
Binary files /dev/null and b/sound/magic/ratvar_attack.ogg differ
diff --git a/sound/misc/desceration-01.ogg b/sound/misc/desceration-01.ogg
new file mode 100644
index 00000000000..29bc568c575
Binary files /dev/null and b/sound/misc/desceration-01.ogg differ
diff --git a/sound/misc/desceration-02.ogg b/sound/misc/desceration-02.ogg
new file mode 100644
index 00000000000..761a73a7b9f
Binary files /dev/null and b/sound/misc/desceration-02.ogg differ
diff --git a/sound/misc/desceration-03.ogg b/sound/misc/desceration-03.ogg
new file mode 100644
index 00000000000..876de18de50
Binary files /dev/null and b/sound/misc/desceration-03.ogg differ
diff --git a/sound/misc/e1m1.ogg b/sound/misc/e1m1.ogg
new file mode 100644
index 00000000000..e611e154ae6
Binary files /dev/null and b/sound/misc/e1m1.ogg differ
diff --git a/sound/weapons/ZapBang.ogg b/sound/weapons/ZapBang.ogg
new file mode 100644
index 00000000000..4e14e30a11b
Binary files /dev/null and b/sound/weapons/ZapBang.ogg differ
diff --git a/sound/weapons/bulletflyby.ogg b/sound/weapons/bulletflyby.ogg
new file mode 100644
index 00000000000..ce9405a5776
Binary files /dev/null and b/sound/weapons/bulletflyby.ogg differ
diff --git a/sound/weapons/bulletflyby2.ogg b/sound/weapons/bulletflyby2.ogg
new file mode 100644
index 00000000000..63956534ba3
Binary files /dev/null and b/sound/weapons/bulletflyby2.ogg differ
diff --git a/sound/weapons/bulletflyby3.ogg b/sound/weapons/bulletflyby3.ogg
new file mode 100644
index 00000000000..e8afd28b06d
Binary files /dev/null and b/sound/weapons/bulletflyby3.ogg differ
diff --git a/sound/weapons/draw_bow.ogg b/sound/weapons/draw_bow.ogg
new file mode 100644
index 00000000000..01f1393bc84
Binary files /dev/null and b/sound/weapons/draw_bow.ogg differ
diff --git a/sound/weapons/rapierhit.ogg b/sound/weapons/rapierhit.ogg
new file mode 100644
index 00000000000..6e214d2821f
Binary files /dev/null and b/sound/weapons/rapierhit.ogg differ
|