Greimorian Egg Cluster Implantation Refactor (#21554)

Fixes https://github.com/Aurorastation/Aurora.3/issues/21509

**Summary:**
Cleans up a lot of old Greimorian egg implantation code- rewrote the
actual worker injection behavior to use the organ/internal/parasite code
we have, instead of creating effects with their loc set to the organ
itself.

This largely means that not only is Greimorian implantation able to be
treated pharmaceutically (as you'd expect, with them being parasitic and
medicine including antiparasitics), but it also means that 'greimorian
eggs' also get the same treatment as nerve flukes, etc. That is to say,
antags can now purchase Greimorian Clade Kits from the uplink and inject
people or monkeys with them to cause Unhappy Tidings.

This code is also fairly easy to modify, if one wanted to create more
implantable parasites that burst out of people, unrelated to
Greimorians.

**Changes:**
- refactor: "Refactors the egg injection of Greimorian Workers to use
parasite code; it can now be treated with anti-parasitic medicines in
addition to amputation of the affected limb. Works on all playable
species, and monkeys."
- rscadd: "New reagent 'Greimorian Eggs' creates an egg cluster parasite
in a random organ when metabolized in the bloodstream. It also has very
mild soporific effects."
- balance: "Greimorian Workers and playable Greimorian Servants/Queen
now inject Greimorian Eggs instead of Soporific."
- rscadd: "Greimorian Clade Kit now available in Uplink under Bioweapons
section (Greimorians now able to be introduced to ship by egg cluster or
injectable reagent)."
- balance: "All parasite kit bioweapons have had their vial reagent
count increased from 2 -> 5."
- balance: "Helmizole no longer treats K'ois Mycosis, Black K'ois
Mycosis, Zombification, or Tumors (Benign or Malignant)."
  - code_imp: "Updates a lot of code documentation to dmdocs."
This commit is contained in:
Batrachophreno
2025-12-02 05:53:54 -05:00
committed by GitHub
parent a2237973f2
commit db452842bf
20 changed files with 376 additions and 176 deletions
+1
View File
@@ -3129,6 +3129,7 @@
#include "code\modules\organs\subtypes\augment\augments\zengu_plate.dm"
#include "code\modules\organs\subtypes\parasite\_parasite.dm"
#include "code\modules\organs\subtypes\parasite\black_kois.dm"
#include "code\modules\organs\subtypes\parasite\greim_eggs.dm"
#include "code\modules\organs\subtypes\parasite\heart_fluke.dm"
#include "code\modules\organs\subtypes\parasite\kois.dm"
#include "code\modules\organs\subtypes\parasite\tumours.dm"
+1
View File
@@ -148,6 +148,7 @@
#define BP_WORM_NERVE "nerve fluke"
#define BP_TUMOUR_NONSPREADING "benign tumour"
#define BP_TUMOUR_SPREADING "malignant tumour"
#define BP_GREIMORIAN_EGGCLUSTER "greimorian egg cluster"
//Augment organs
#define BP_AUG_ACC_CORDS "modified synthetic vocal cords"
+54 -42
View File
@@ -5,8 +5,8 @@
//Checks if all high bits in req_mask are set in bitfield
#define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0)
//Inverts the colour of an HTML string
/proc/invertHTML(HTMLstring)
/// Inverts the colour of an HTML string
/proc/htmlInvertColor(HTMLstring)
if (!( istext(HTMLstring) ))
CRASH("Given non-text argument!")
@@ -30,7 +30,7 @@
textr = "0[textb]"
return "#[textr][textg][textb]"
//Returns the middle-most value
/// Returns the middle-most value
/proc/dd_range(var/low, var/high, var/num)
return max(low,min(high,num))
@@ -95,7 +95,7 @@
ty += AM.step_y
return SIMPLIFY_DEGREES(arctan(ty - sy, tx - sx))
//Returns location. Returns null if no location was found.
/// Returns location. Returns null if no location was found.
/proc/get_teleport_loc(turf/location,mob/target,distance = 1, density = 0, errorx = 0, errory = 0, eoffsetx = 0, eoffsety = 0)
/*
Location where the teleport begins, target that will teleport, distance to go, density checking 0/1(yes/no).
@@ -227,19 +227,27 @@ Turf and target are seperate in case you want to teleport some distance from a t
return 1
return 0
/proc/getline(atom/M,atom/N)//Ultra-Fast Bresenham Line-Drawing Algorithm
var/px=M.x //starting x
/// Ultra-Fast Bresenham Line-Drawing Algorithm
/proc/getline(atom/M,atom/N)
/// starting x
var/px=M.x
var/py=M.y
var/line[] = list(locate(px,py,M.z))
var/dx=N.x-px //x distance
/// x distance
var/dx=N.x-px
var/dy=N.y-py
var/dxabs=abs(dx)//Absolute value of x distance
/// Absolute value of x distance
var/dxabs=abs(dx)
var/dyabs=abs(dy)
var/sdx=SIGN(dx) //Sign of x distance (+ or -)
/// Sign of x distance (+ or -)
var/sdx=SIGN(dx)
var/sdy=SIGN(dy)
var/x=dxabs>>1 //Counters for steps taken, setting to distance/2
var/y=dyabs>>1 //Bit-shifting makes me l33t. It also makes getline() unnessecarrily fast.
var/j //Generic integer for counting
/// Counters for steps taken, setting to distance/2
var/x=dxabs>>1
var/y=dyabs>>1
/// Generic integer for counting
var/j
if(dxabs>=dyabs) //x distance is greater than y
for(j=0;j<dxabs;j++)//It'll take dxabs steps to get there
y+=dyabs
@@ -259,7 +267,9 @@ Turf and target are seperate in case you want to teleport some distance from a t
return line
#define LOCATE_COORDS(X, Y, Z) locate(between(1, X, world.maxx), between(1, Y, world.maxy), Z)
/proc/getcircle(turf/center, var/radius) //Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle.
/// Uses a fast Bresenham rasterization algorithm to return the turfs in a thin circle.
/proc/getcircle(turf/center, var/radius)
if(!radius) return list(center)
var/x = 0
@@ -285,7 +295,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
#undef LOCATE_COORDS
///Returns whether or not a player is a guest using their ckey as an input
/// Returns whether or not a player is a guest using their ckey as an input
/proc/IsGuestKey(key)
if (findtext(key, "Guest-", 1, 7) != 1) //was findtextEx
return 0
@@ -301,7 +311,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
return 0
return 1
///Ensure the frequency is within bounds of what it should be sending/recieving at
/// Ensure the frequency is within bounds of what it should be sending/recieving at
/proc/sanitize_frequency(var/f, var/low = PUBLIC_LOW_FREQ, var/high = PUBLIC_HIGH_FREQ)
f = round(f)
f = max(low, f)
@@ -310,15 +320,15 @@ Turf and target are seperate in case you want to teleport some distance from a t
f += 1
return f
///Turns 1479 into 147.9
/// Turns 1479 into 147.9
/proc/format_frequency(var/f)
return "[round(f / 10)].[f % 10]"
///Picks a string of symbols to display as the law number for hacked or ion laws
/// Picks a string of symbols to display as the law number for hacked or ion laws
/proc/ionnum()
return "[pick("1","2","3","4","5","6","7","8","9","0")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]"
///When an AI is activated, it can choose from a list of non-slaved borgs to have as a slave.
/// When an AI is activated, it can choose from a list of non-slaved borgs to have as a slave.
/proc/freeborg()
var/select = null
var/list/borgs = list()
@@ -332,7 +342,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
select = input("Unshackled borg signals detected:", "Borg selection", null, null) as null|anything in borgs
return borgs[select]
///When a borg is activated, it can choose which AI it wants to be slaved to
/// When a borg is activated, it can choose which AI it wants to be slaved to
/proc/active_ais()
. = list()
for(var/mob/living/silicon/ai/A in GLOB.living_mob_list)
@@ -343,7 +353,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
. += A
return .
///Find an active ai with the least borgs. VERBOSE PROCNAME HUH!
/// Find an active ai with the least borgs. VERBOSE PROCNAME HUH!
/proc/select_active_ai_with_fewest_borgs()
var/mob/living/silicon/ai/selected
var/list/active = active_ais()
@@ -388,7 +398,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
new_list += Dead_list
return new_list
///Returns a list of all mobs with their name
/// Returns a list of all mobs with their name
/proc/getmobs()
var/list/mobs = sortmobs()
@@ -414,7 +424,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
return creatures
///Orders mobs by type then by name
/// Orders mobs by type then by name
/proc/sortmobs()
var/list/moblist = list()
var/list/sortmob = sortAtom(GLOB.mob_list)
@@ -494,18 +504,17 @@ Turf and target are seperate in case you want to teleport some distance from a t
return locate(x,y,A.z)
// returns turf relative to A offset in dx and dy tiles
// bound to map limits
/// Returns turf relative to A offset in dx and dy tiles. Bound to map limits.
/proc/get_offset_target_turf(var/atom/A, var/dx, var/dy)
var/x = min(world.maxx, max(1, A.x + dx))
var/y = min(world.maxy, max(1, A.y + dy))
return locate(x,y,A.z)
///Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value.
/// Makes sure MIDDLE is between LOW and HIGH. If not, it adjusts it. Returns the adjusted value.
/proc/between(var/low, var/middle, var/high)
return max(min(middle, high), low)
///Returns random gauss number
/// Returns random gauss number
/proc/GaussRand(var/sigma)
var/x,y,rsq
do
@@ -515,7 +524,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
while(rsq>1 || !rsq)
return sigma*y*sqrt(-2*log(rsq)/rsq)
///Step-towards method of determining whether one atom can see another. Similar to viewers()
/// Step-towards method of determining whether one atom can see another. Similar to viewers()
/proc/can_see(var/atom/source, var/atom/target, var/length=5) // I couldn't be arsed to do actual raycasting :I This is horribly inaccurate.
var/turf/current = get_turf(source)
var/turf/target_turf = get_turf(target)
@@ -769,8 +778,10 @@ Turf and target are seperate in case you want to teleport some distance from a t
return FALSE
return TRUE
//Takes: Anything that could possibly have variables and a varname to check.
//Returns: 1 if found, 0 if not.
/**
* Takes: Anything that could possibly have variables and a varname to check.
* Returns: 1 if found, 0 if not.
*/
/proc/hasvar(var/datum/A, var/varname)
if(A.vars.Find(lowertext(varname))) return 1
else return 0
@@ -798,7 +809,8 @@ Turf and target are seperate in case you want to teleport some distance from a t
var/dy = abs(B.y - A.y)
return get_dir(A, B) & (rand() * (dx+dy) < dy ? 3 : 12)
/proc/get_compass_dir(atom/start, atom/end) //get_dir() only considers an object to be north/south/east/west if there is zero deviation. This uses rounding instead. // Ported from CM-SS13
/// get_dir() only considers an object to be north/south/east/west if there is zero deviation. This uses rounding instead.
/proc/get_compass_dir(atom/start, atom/end)
if(!start || !end)
return 0
if(!start.z || !end.z)
@@ -892,7 +904,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
return get_turf(location)
//Quick type checks for some tools ~ BRAH wtf is this shit that's not how one should do this
/// Quick type checks for some tools ~ BRAH wtf is this shit that's not how one should do this
GLOBAL_LIST_INIT(common_tools, list(
/obj/item/stack/cable_coil,
/obj/item/wrench,
@@ -942,7 +954,7 @@ GLOBAL_LIST_INIT(common_tools, list(
if(istype(W, /obj/item/melee/energy))
return 3500
//Whether or not the given item counts as sharp in terms of dealing damage
/// Whether or not the given item counts as sharp in terms of dealing damage
/proc/is_sharp(obj/O)
if (!O)
return 0
@@ -952,7 +964,7 @@ GLOBAL_LIST_INIT(common_tools, list(
return 1
return 0
//Whether or not the given item counts as cutting with an edge in terms of removing limbs
/// Whether or not the given item counts as cutting with an edge in terms of removing limbs
/proc/has_edge(obj/O)
if (!O)
return 0
@@ -966,7 +978,7 @@ GLOBAL_LIST_INIT(common_tools, list(
/proc/is_borg_item(obj/item/W)
return W && W.loc && isrobot(W.loc)
//check if mob is lying down on something we can operate him on.
/// Check if mob is lying down on something we can operate him on.
/proc/can_operate(mob/living/carbon/M) //If it's 2, commence surgery, if it's 1, fail surgery, if it's 0, attack
var/surgery_attempt = SURGERY_IGNORE
var/located = FALSE
@@ -989,9 +1001,7 @@ GLOBAL_LIST_INIT(common_tools, list(
surgery_attempt = SURGERY_IGNORE //hit yourself if you're not lying
return surgery_attempt
/*
Checks if that loc and dir has a item on the wall
*/
/// Checks if that loc and dir has a item on the wall
GLOBAL_LIST_INIT(wall_items, typecacheof(list(
/obj/machinery/power/apc,
/obj/machinery/alarm,
@@ -1044,9 +1054,11 @@ GLOBAL_LIST_INIT(wall_items, typecacheof(list(
return 1
return 0
// Returns a variable type as string, optionally with some details:
// Objects (datums) get their type, paths get the type name, scalars show length (text) and value (numbers), lists show length.
// Also attempts some detection of otherwise undetectable types using ref IDs
/**
* Returns a variable type as string, optionally with some details:
* Objects (datums) get their type, paths get the type name, scalars show length (text) and value (numbers), lists show length.
* Also attempts some detection of otherwise undetectable types using ref IDs
*/
/proc/get_debug_type(var/V, var/details = TRUE, var/print_numbers = TRUE, var/path_names = TRUE, var/text_lengths = TRUE, var/list_lengths = TRUE, var/show_useless_subtypes = TRUE)
// scalars / basic types
if(isnull(V))
@@ -1150,11 +1162,11 @@ GLOBAL_LIST_INIT(wall_items, typecacheof(list(
colour += temp_col
return "#[colour]"
// call to generate a stack trace and print to runtime logs
/// Call to generate a stack trace and print to runtime logs
/proc/crash_with(msg)
CRASH(msg)
//similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs)
/// Similar function to RANGE_TURFS(), but will search spiraling outwards from the center (like the above, but only turfs)
/proc/spiral_range_turfs(dist=0, center=usr, orange=0)
if(!dist)
if(!orange)
+8 -2
View File
@@ -20,8 +20,14 @@
telecrystal_cost = 5
path = /obj/item/storage/box/syndie_kit/heartworms
/datum/uplink_item/item/bioweapons/greimorian_eggs
name = "Greimorian Eggs"
/datum/uplink_item/item/bioweapons/greimorians_kit
name = "Parasitic Eggs Kit - Greimorian Clade"
desc = "Contains the eggs of a Greimorian clade. Semi-lethal and incapacitating. Compatible with most sapient bipedal species (including Earth monkeys)."
telecrystal_cost = 4
path = /obj/item/storage/box/syndie_kit/greimorians
/datum/uplink_item/item/bioweapons/greimorian_eggcluster
name = "Greimorian Egg Cluster"
desc = "A cluster of greimorian eggs. (They will be planted at your feet on-purchase and CANNOT be moved, so make sure you're where you want them to be)"
telecrystal_cost = 4
path = /obj/effect/spider/eggcluster
+26 -80
View File
@@ -84,13 +84,13 @@
return prob(30)
return TRUE
/// The effect/spider/eggcluster is only for egg clusters spawned in-world, not implanted by a worker.
/obj/effect/spider/eggcluster
name = "egg cluster"
desc = "They seem to pulse slightly with an inner life."
icon_state = "eggs"
health = 10
var/amount_grown = 0
var/last_itch = 0
/obj/effect/spider/eggcluster/Initialize(var/mapload, var/atom/parent)
. = ..(mapload)
@@ -112,29 +112,16 @@
/obj/effect/spider/eggcluster/process()
amount_grown += rand(0, 2)
var/obj/item/organ/external/O = null
if(isorgan(loc))
O = loc
if(amount_grown >= 100)
var/num = rand(6,24)
for(var/i = 0, i < num, i++)
var/spiderling = new /obj/effect/spider/spiderling(src.loc, src, 0.75)
if(O)
O.implants += spiderling
new /obj/effect/spider/spiderling(src.loc, src, 0.75)
qdel(src)
else if (O && O.owner && prob(1))
if(world.time > last_itch + 30 SECONDS)
last_itch = world.time
to_chat(O.owner, SPAN_NOTICE("Your [O.name] itches."))
/obj/effect/spider/eggcluster/proc/take_damage(var/damage)
health -= damage
if(health <= 0)
var/obj/item/organ/external/O = loc
if(istype(O) && O.owner)
to_chat(O.owner, SPAN_WARNING("You feel something dissolve in your [O.name]..."))
qdel(src)
/obj/effect/spider/spiderling
@@ -144,29 +131,41 @@
They originate from the Badlands planet Greima, once covered in crystalized phoron. A decaying orbit led to its combustion from proximity to its sun, and its dominant inhabitants \
managed to survive in orbit. Countless years later, they prove to be a menace across the galaxy, having carried themselves within the hulls of Human vessels to spread wildly."
icon_state = "spiderling"
anchored = 0
layer = 2.7
anchored = FALSE
layer = OPEN_DOOR_LAYER // 2.7
health = 3
var/last_itch = 0
var/amount_grown = -1
/// % chance that the spiderling will eventually turn into a fully-grown greimorian.
var/can_mature_chance = 50
var/can_mature = FALSE
/// Multiplier to growth gained per tick.
var/growth_rate = 1
/// Their current growth, from 0-100.
var/growth_level = 0
var/obj/machinery/atmospherics/unary/vent_pump/entry_vent
var/travelling_in_vent = 0
var/list/possible_offspring
/// Possible creatures the larva can mature into.
var/list/possible_offspring = list(
/mob/living/simple_animal/hostile/giant_spider,
/mob/living/simple_animal/hostile/giant_spider/nurse,
/mob/living/simple_animal/hostile/giant_spider/emp,
/mob/living/simple_animal/hostile/giant_spider/hunter,
/mob/living/simple_animal/hostile/giant_spider/bombardier
)
/obj/effect/spider/spiderling/Initialize(var/mapload, var/atom/parent, var/new_rate = 1, var/list/spawns = list(/mob/living/simple_animal/hostile/giant_spider, /mob/living/simple_animal/hostile/giant_spider/nurse, /mob/living/simple_animal/hostile/giant_spider/emp, /mob/living/simple_animal/hostile/giant_spider/hunter, /mob/living/simple_animal/hostile/giant_spider/bombardier))
/obj/effect/spider/spiderling/Initialize(var/mapload, var/atom/parent, var/new_growth_rate = 1, var/list/new_possible_offspring = possible_offspring)
. = ..(mapload)
pixel_x = rand(6,-6)
pixel_y = rand(6,-6)
START_PROCESSING(SSprocessing, src)
//50% chance to grow up
if(prob(50))
amount_grown = 1
if(prob(can_mature_chance))
can_mature = TRUE
growth_rate = new_rate
growth_rate = new_growth_rate
possible_offspring = spawns
possible_offspring = new_possible_offspring
get_light_and_color(parent)
@@ -247,32 +246,16 @@
GLOB.move_manager.move_to(src, entry_vent, 0, 5)
break
if(isturf(loc) && amount_grown >= 100)
if(isturf(loc) && growth_level >= 100)
var/spawn_type = pick(possible_offspring)
new spawn_type(src.loc, src)
qdel(src)
else if(isorgan(loc))
var/obj/item/organ/external/O = loc
if(amount_grown > 70)
burst_out(O)
if (O.owner)
if(amount_grown > 40 && prob(1))
O.owner.apply_damage(1, DAMAGE_TOXIN, O.limb_name)
if(world.time > last_itch + 30 SECONDS)
last_itch = world.time
O.owner.visible_message(
SPAN_WARNING("You think you see something moving around in \the [O.owner]'s [O.name]."),
SPAN_WARNING("You [prob(25) ? "see" : "feel"] something large move around in your [O.name]!"))
else if (prob(1))
if(world.time > last_itch + 30 SECONDS)
last_itch = world.time
to_chat(O.owner, SPAN_WARNING("You feel something large move around in your [O.name]!"))
else if(prob(1))
src.visible_message(SPAN_NOTICE("\The [src] skitters."))
if (amount_grown > -1)
amount_grown += (rand(0, 1) * growth_rate)
if(can_mature)
growth_level += (rand(0, 1) * growth_rate)
/obj/effect/spider/spiderling/attack_hand(mob/living/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
@@ -294,43 +277,6 @@
die()
return TRUE
/**
* Makes the organ spew out all of the spiderlings it has. It's triggered at the point
* of the first spiderling reaching 80% of more amount grown. This stops them from growing
* to full size inside a human.
*
* The proc also drops the limb if it's on a human, or gibs it if it's on the floor. For
* maximum drama, of course!
*
* @param O The organ/external limb the src is located inside of.
*/
/obj/effect/spider/spiderling/proc/burst_out(obj/item/organ/external/O = src.loc)
if (!istype(O))
return
if (O.owner)
O.owner.visible_message(
SPAN_DANGER("A group of [src] burst out of [O.owner]'s [O]!"),
SPAN_DANGER("A group of [src] burst out of your [O]!"))
O.owner.emote("scream")
else
O.visible_message(SPAN_DANGER("A group of [src] burst out of \the [O]!"))
var/target_loc = O.owner ? O.owner.loc : O.loc
// Swarm all of the spiders out so we can gib the limb.
for (var/obj/effect/spider/spiderling/S in O.implants)
O.implants -= S
S.forceMove(target_loc)
// if owner, dismember the shit out of it.
if (O.owner)
O.droplimb(0, DROPLIMB_BLUNT)
else
O.visible_message(SPAN_DANGER("\The [O] explodes into a pile of gore!"))
gibs(target_loc)
qdel(O)
/obj/effect/decal/cleanable/spiderling_remains
name = "greimorian larva remains"
desc = "Green squishy mess."
+13 -2
View File
@@ -98,7 +98,7 @@
. = ..()
if(is_open_container())
atom_flags ^= ATOM_FLAG_OPEN_CONTAINER
reagents.add_reagent(/singleton/reagent/toxin/nerveworm_eggs, 2)
reagents.add_reagent(/singleton/reagent/toxin/nerveworm_eggs, 5)
desc = "<b>BIOHAZARDOUS! - Nerve Fluke eggs.</b> Purchased from <i>SciSupply Eridani</i>, recently incorporated into <i>Zeng-Hu Pharmaceuticals' Keiretsu</i>!"
update_icon()
@@ -109,10 +109,21 @@
. = ..()
if(is_open_container())
atom_flags ^= ATOM_FLAG_OPEN_CONTAINER
reagents.add_reagent(/singleton/reagent/toxin/heartworm_eggs, 2)
reagents.add_reagent(/singleton/reagent/toxin/heartworm_eggs, 5)
desc = "<b>BIOHAZARDOUS! - Heart Fluke eggs.</b> Purchased from <i>SciSupply Eridani</i>, recently incorporated into <i>Zeng-Hu Pharmaceuticals' Keiretsu</i>!"
update_icon()
/obj/item/reagent_containers/glass/beaker/vial/greimorian_eggs
atom_flags = 0
/obj/item/reagent_containers/glass/beaker/vial/greimorian_eggs/Initialize()
. = ..()
if(is_open_container())
atom_flags ^= ATOM_FLAG_OPEN_CONTAINER
reagents.add_reagent(/singleton/reagent/toxin/greimorian_eggs, 5)
desc = "<b>BIOHAZARDOUS! - Greimorian eggs.</b> Purchased from <i>SciSupply Eridani</i>, recently incorporated into <i>Zeng-Hu Pharmaceuticals' Keiretsu</i>!"
update_icon()
/obj/item/reagent_containers/powder
name = "chemical powder"
desc = "A powdered form of... something."
@@ -220,6 +220,11 @@
desc = "Contains the eggs of a Heart Fluke (lethal)."
starts_with = list(/obj/item/reagent_containers/glass/beaker/vial/heartworm_eggs = 1, /obj/item/reagent_containers/syringe = 1, /obj/item/reagent_containers/pill/antiparasitic = 1, /obj/item/reagent_containers/pill/asinodryl = 1)
/obj/item/storage/box/syndie_kit/greimorians
name = "greimorian clade kit"
desc = "Contains the eggs of greimorian clade (semi-lethal, incapacitating)."
starts_with = list(/obj/item/reagent_containers/glass/beaker/vial/greimorian_eggs = 1, /obj/item/reagent_containers/syringe = 1, /obj/item/reagent_containers/pill/antiparasitic = 1, /obj/item/reagent_containers/pill/asinodryl = 1)
/obj/item/storage/box/syndie_kit/radsuit
name = "radiation suit kit"
desc = "Contains a radiation suit and geiger counter to protect you from radiation."
@@ -2,46 +2,55 @@
gender = MALE
accent = ACCENT_CETI
blocks_emissive = EMISSIVE_BLOCK_UNIQUE
var/datum/species/species //Contains icon generation and language information, set during New().
//stomach contents redefined at mob/living level, removed from here
var/analgesic = 0 // when this is set, the mob isn't affected by shock or pain
// life should decrease this by 1 every tick
// total amount of wounds on mob, used to spread out healing and the like over all wounds
/// Contains icon generation and language information, set during New().
var/datum/species/species
/// When this is set, the mob isn't affected by shock or pain.
var/analgesic = 0
/// life should decrease this by 1 every tick (??? -batra)
/// total amount of wounds on mob, used to spread out healing and the like over all wounds (??? -batra)
var/number_wounds = 0
var/obj/item/handcuffed = null //Whether or not the mob is handcuffed
var/obj/item/legcuffed = null //Same as handcuffs but for legs. Bear traps use this.
//Surgery info
/// Whether or not the mob is handcuffed.
var/obj/item/handcuffed = null
/// Same as handcuffs but for legs. Bear traps use this.
var/obj/item/legcuffed = null
/// Surgery info
var/datum/surgery_status/op_stage = new/datum/surgery_status
//Active emote/pose
/// Active emote/pose
var/pose = null
var/list/chem_effects = list()
var/list/chem_doses = list()
/// For keeping count of misc values (amount of damage, number of ticks, etc)
/// For keeping count of misc values (amount of damage, number of ticks, etc).
var/list/chem_tracking = list()
var/intoxication = 0//Units of alcohol in their system
var/datum/reagents/metabolism/bloodstr = null
var/datum/reagents/metabolism/touching = null
var/datum/reagents/metabolism/breathing = null
//these two help govern taste. The first is the last time a taste message was shown to the plaer.
//the second is the message in question.
/// These two help govern taste. The first is the last time a taste message was shown to the player.
/// The second is the message in question.
var/last_taste_time = 0
var/last_taste_text = ""
var/last_smell_time = 0
var/last_smell_text = ""
var/coughedtime = null // should only be useful for carbons as the only thing using it has a carbon arg.
/// Should only be useful for carbons as the only thing using it has a carbon arg.
var/coughedtime = null
var/willfully_sleeping = FALSE
var/consume_nutrition_from_air = FALSE // used by Diona
/// Used by Diona.
var/consume_nutrition_from_air = FALSE
var/help_up_offer = 0 //if they have their hand out to offer someone up from the ground.
/// If they have their hand out to offer someone up from the ground.
var/help_up_offer = 0
var/list/organs_by_name = list() // map organ names to organs
var/list/internal_organs_by_name = list() // so internal organs have less ickiness too
/// Map organ names to organs.
var/list/organs_by_name = list()
/// So internal organs have less ickiness too.
var/list/internal_organs_by_name = list()
var/list/stasis_sources = list()
var/stasis_value
var/pain_immune = FALSE //for special cases where something permanently removes a mob's ability to feel pain
/// For special cases where something permanently removes a mob's ability to feel pain.
var/pain_immune = FALSE
@@ -172,7 +172,7 @@
spark(src, 5)
//Handles chem traces
/// Handles chem traces
/mob/living/carbon/human/proc/handle_trace_chems()
//New are added for reagents to random organs.
for(var/_A in reagents.reagent_volumes)
@@ -64,9 +64,9 @@
melee_damage_lower = 5
melee_damage_upper = 10
armor_penetration = 20
venom_per_bite = 10
venom_per_bite = 4
var/atom/cocoon_target
venom_type = /singleton/reagent/soporific
venom_type = /singleton/reagent/toxin/greimorian_eggs
var/fed = 0
sample_data = list("Genetic markers identified as being linked with stem cell differentiaton", "Cellular structures indicative of high offspring production")
@@ -82,9 +82,9 @@
melee_damage_lower = 15
melee_damage_upper = 20
armor_penetration = 30
venom_per_bite = 10
venom_per_bite = 1
speed = -2
venom_type = /singleton/reagent/soporific
venom_type = /singleton/reagent/toxin/greimorian_eggs
fed = 1
var/playable = TRUE
sample_data = list("Genetic markers identified as being linked with stem cell differentiaton", "Cellular structures indicative of high offspring production", "Tissue sample contains high neural cell content")
@@ -190,16 +190,17 @@
for(var/armor in armors)
var/datum/component/armor/armor_datum = armor
inject_probability -= armor_datum.armor_values[MELEE] * 1.8
if(prob(inject_probability))
if(prob(inject_probability) && !BP_IS_ROBOTIC(limb))
to_chat(target, SPAN_WARNING("You feel a tiny prick."))
target.reagents.add_reagent(venom_type, venom_per_bite)
/mob/living/simple_animal/hostile/giant_spider/nurse/on_attack_mob(var/mob/hit_mob, var/obj/item/organ/external/limb)
/mob/living/simple_animal/hostile/giant_spider/nurse/on_attack_mob(var/mob/living/carbon/human/hit_mob, var/obj/item/organ/external/limb)
. = ..()
if(ishuman(hit_mob) && istype(limb) && !BP_IS_ROBOTIC(limb) && prob(venom_per_bite))
var/eggs = new /obj/effect/spider/eggcluster(limb, src)
limb.implants += eggs
to_chat(hit_mob, SPAN_WARNING("\The [src] injects something into your [limb.name]!"))
if(istype(limb))
if(BP_IS_ROBOTIC(limb))
to_chat(hit_mob, SPAN_WARNING("\The [src] tries to inject something into your [limb.name], but fortunately it finds no living flesh!"))
else
to_chat(hit_mob, SPAN_WARNING("\The [src] injects something into your [limb.name]!"))
/mob/living/simple_animal/hostile/giant_spider/emp/on_attack_mob(var/mob/hit_mob, var/obj/item/organ/external/limb)
. = ..()
+3 -3
View File
@@ -854,11 +854,11 @@
if(transforming) return 0
return 1
// Not sure what to call this. Used to check if humans are wearing an AI-controlled exosuit and hence don't need to fall over yet.
/// Not sure what to call this. Used to check if humans are wearing an AI-controlled exosuit and hence don't need to fall over yet.
/mob/proc/can_stand_overridden()
return 0
//Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it.
/// Updates canmove, lying and icons. Could perhaps do with a rename but I can't think of anything to describe it.
/mob/proc/update_canmove()
if(in_neck_grab())
lying = FALSE
@@ -976,7 +976,7 @@
return facedir(client.client_dir(SOUTH))
//This might need a rename but it should replace the can this mob use things check
/// This might need a rename but it should replace the can this mob use things check
/mob/proc/IsAdvancedToolUser()
return 0
+16 -9
View File
@@ -2,23 +2,29 @@
INTERNAL ORGANS DEFINES
****************************************************/
/obj/item/organ/internal
var/dead_icon // Icon to use when the organ has died.
var/damage_reduction = 0.5 //modifier for internal organ injury
var/unknown_pain_location = TRUE // if TRUE, pain messages will point to the parent organ, otherwise it will print the organ name
/// Icon to use when the organ has died.
var/dead_icon
/// Modifier for internal organ injury.
var/damage_reduction = 0.5
/// If TRUE, pain messages will point to the parent organ, otherwise it will print the organ name.
var/unknown_pain_location = TRUE
var/toxin_type = "undefined"
var/relative_size = 25 //Used for size calcs
/// The icon state to overlay on the mob
/// Used for size calcs.
var/relative_size = 25
/// The icon state to overlay on the mob.
var/on_mob_icon
/// If the icon state has an active overlay
/// If the icon state has an active overlay.
var/active_overlay = FALSE
/// If the icon state has an active emissive overlay
/// If the icon state has an active emissive overlay.
var/active_emissive = FALSE
var/list/possible_modifications = list("Normal","Assisted","Mechanical") //this is used in the character setup
/// Used in character setup.
var/list/possible_modifications = list("Normal","Assisted","Mechanical")
/// The amount all organs heal themselves by per second when not being affected by chems.
var/organ_self_heal_per_second = 0.2
min_broken_damage = 10 //Internal organs are frail, man.
/// Internal organs are frail, man.
min_broken_damage = 10
/obj/item/organ/internal/Destroy()
if(owner)
@@ -31,6 +37,7 @@
if(istype(E)) E.internal_organs -= src
return ..()
/// Sets the internal organ as belonging to the targeted external organ, and matches the target's species/robotness. Also updates all organ lists belonging to the owner.
/obj/item/organ/internal/replaced(var/mob/living/carbon/human/target, var/obj/item/organ/external/affected)
if(!istype(target))
return 0
+1
View File
@@ -459,6 +459,7 @@ INITIALIZE_IMMEDIATE(/obj/item/organ)
owner.update_action_buttons()
owner = null
/// Sets the organ's owner to the proc's target, and ensures its forceMoved into that target.
/obj/item/organ/proc/replaced(var/mob/living/carbon/human/target, var/obj/item/organ/external/affected)
owner = target
action_button_name = initial(action_button_name)
+5
View File
@@ -1135,10 +1135,15 @@ Note that amputating the affected organ does in fact remove the infection from t
if(victim.get_blood_color())
gore.basecolor = victim.get_blood_color()
gore.update_icon()
// Tiny amount of blood and not impacted by anything like coagulants, etc- we just want it for effect.
victim.blood_squirt(2, loc, rand(2,5))
add_blood(victim)
INVOKE_ASYNC(gore, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 4)
for(var/obj/item/organ/I in internal_organs)
I.removed()
victim.blood_squirt(2, loc, rand(2,5))
if(istype(loc,/turf))
INVOKE_ASYNC(I, TYPE_PROC_REF(/atom/movable, throw_at), get_edge_target_turf(src, pick(GLOB.alldirs)), rand(1,3), 4)
@@ -38,6 +38,7 @@
/obj/item/organ/internal/parasite/process()
..()
if(!owner)
qdel(src)
return
if(owner.chem_effects[CE_ANTIPARASITE] && !drug_resistance)
@@ -74,6 +75,13 @@
/obj/item/organ/internal/parasite/proc/stage_effect()
return
/// Removes the parasite on next process().
/obj/item/organ/internal/parasite/proc/remove_parasite()
recession = 1000
stage = 1
stage_ticker = 0
drug_resistance = FALSE
/mob/living/carbon/human/proc/infest_with_parasite(var/mob/living/carbon/victim, var/parasite_type, var/obj/item/organ/external/organ_to_infest, var/chance_to_infest = 100, var/parasite_limit = 3)
if(ishuman(victim))
var/mob/living/carbon/human/H = victim
@@ -0,0 +1,131 @@
/obj/item/organ/internal/parasite/greimorian_eggcluster
name = "cluster of greimorian eggs"
icon = 'icons/effects/effects.dmi'
icon_state = "eggs"
dead_icon = "eggs"
organ_tag = BP_GREIMORIAN_EGGCLUSTER
parent_organ = BP_CHEST
subtle = 1
/// ~2 minutes/stage (at two seconds/tick)
stage_interval = 60
max_stage = 5
origin_tech = list(TECH_BIO = 4)
egg = /singleton/reagent/toxin/greimorian_eggs
/// Everyone's favorite-named variable
var/gestating_spiderlings = 0
var/last_crawling_msg = 0
/// True when it has ruptured; end additional behavior.
var/ruptured = FALSE
/obj/item/organ/internal/parasite/greimorian_eggcluster/process()
..()
if(!owner && gestating_spiderlings)
return
if(ruptured)
return
var/obj/item/organ/external/affecting_organ = owner.organs_by_name[parent_organ]
if(prob(8))
gestating_spiderlings += 1
if(prob(5))
to_chat(affecting_organ.owner, SPAN_NOTICE("Your [affecting_organ.name] itches."))
if(prob(33))
owner.adjustNutritionLoss(3)
if(stage >= 2) //after ~2.5 minutes
if(prob(8))
gestating_spiderlings += rand(1,2)
if(prob(33))
owner.adjustNutritionLoss(4)
if(prob(5))
owner.emote("whimper")
if(prob(3))
to_chat(owner, SPAN_WARNING(pick("You feel nauseous and hungry at the same time.", "You feel a burning pain in your [parent_organ].", "Your sense of balance seems broken.")))
if(stage >= 3) //after ~5 minutes
if(prob(8))
gestating_spiderlings += rand(1,3)
if(prob(33))
owner.adjustNutritionLoss(5)
if(prob(10))
affecting_organ.take_damage(rand(1,3))
owner.adjustHalLoss(8)
if(world.time > last_crawling_msg + 15 SECONDS)
last_crawling_msg = world.time
owner.visible_message(
SPAN_WARNING("You think you see something moving around in \the [owner.name]'s [affecting_organ.name]."),
SPAN_WARNING("You [prob(25) ? "see" : "feel"] something large move around in your [affecting_organ.name]!"))
if(prob(1))
owner.seizure(0.4)
if(stage >= 4) //after ~7.5 minutes
if(prob(8))
gestating_spiderlings += rand(1,4)
if(prob(5))
owner.reagents.add_reagent(/singleton/reagent/toxin/greimorian_eggs, (gestating_spiderlings / 3))
owner.adjustHalLoss(12)
to_chat(owner, SPAN_WARNING(pick("A sharp, caustic pain boils out from your [affecting_organ.name]!", "You can feel the flesh of your [affecting_organ.name] beginning to bulge!", "It's going to burst! Your [affecting_organ.name] is going to burst!")))
if(prob(5))
owner.seizure(0.5)
if(stage >= 5) //after ~9 minutes
if(prob(8))
gestating_spiderlings += rand(1,5)
if(prob(10))
owner.seizure(0.6)
if(prob(5))
ruptured = TRUE
owner.adjustHalLoss(75)
owner.seizure()
src.owner.emote("scream")
owner.visible_message(
SPAN_DANGER("You can see the flesh of [owner.name]'s [affecting_organ.name] begin rippling violently!"),
SPAN_DANGER("An <b>extreme</b>, nauseating pain erupts from your [affecting_organ.name]; you feel something burst inside it. The flesh begins to ripple violently!"))
addtimer(CALLBACK(src, PROC_REF(rupture)), rand(30, 50))
/obj/item/organ/internal/parasite/greimorian_eggcluster/Destroy()
var/obj/item/organ/external/affecting_organ = owner.organs_by_name[parent_organ]
to_chat(src.owner, SPAN_WARNING("Mercifully, you feel something finally dissolve in your [affecting_organ.name]..."))
..()
/**
* Makes the victim spew out all of the spiderlings it has. It's triggered at the point
* of the parasite hitting stage 5 of its growth.
*
* The proc also drops the limb if it's on a human, or gibs it if it's on the floor. For
* maximum drama, of course!
*/
/obj/item/organ/internal/parasite/greimorian_eggcluster/proc/rupture()
var/obj/item/organ/external/victim_external_organ = owner.organs_by_name[parent_organ]
if(src.owner)
src.owner.visible_message(
SPAN_DANGER("A group of [src] burst out of [owner]'s [victim_external_organ.name]!"),
SPAN_DANGER("A group of [src] burst out of your [victim_external_organ.name]!"))
src.owner.emote("scream")
var/target_loc = src.owner ? src.owner.loc : src.loc
for(var/i = 0 to gestating_spiderlings)
// For details on the spiderlings, check out 'code\game\objects\effects\spiders.dm'
new /obj/effect/spider/spiderling(target_loc, src, 3)
if(victim_external_organ.owner)
victim_external_organ.droplimb(0, DROPLIMB_BLUNT)
remove_parasite()
@@ -2015,13 +2015,12 @@
M.add_chemical_effect(CE_EMETIC, M.chem_doses[type]/2)
/singleton/reagent/antiparasitic/overdose(mob/living/carbon/M, alien, removed, scale, datum/reagents/holder)
if(istype(M,/mob/living/carbon/human))
var/mob/living/carbon/human/H = M
for(var/obj/item/organ/internal/parasite/P in H.internal_organs)
if(P)
if(P.drug_resistance == 0)
P.drug_resistance = 1
M.dizziness = max(50, M.dizziness)
M.make_dizzy(5)
M.adjustHydrationLoss(2*removed)
M.adjustNutritionLoss(8*removed)
M.adjustHalLoss(5)
to_chat(M, SPAN_WARNING(pick("You feel flushed and woozy.", "Your guts feel like they're crawling.")))
/singleton/reagent/antibodies
name = "Hylemnomil-Zeta Antibodies"
@@ -1029,6 +1029,44 @@
var/obj/item/organ/internal/parasite/heartworm/infest = new()
infest.replaced(H, affected)
/// Functions as a weaker version of soporific and also infests the victim with eggs.
/singleton/reagent/toxin/greimorian_eggs
name = "Greimorian Eggs"
description = "The eggs of a greimorian clade. They are highly opportunistic, capable of infesting almost any organism, and are feared for the relatively swift speed with which they gestate. Immediate surgical removal or pharmaceutical intervention is a vital necessity."
reagent_state = SOLID
color = "#5f683f"
metabolism = REM*2
ingest_met = REM*2
touch_met = REM*5
taste_description = "something noxious"
taste_mult = 0.25
strength = 0
/singleton/reagent/toxin/greimorian_eggs/affect_blood(mob/living/carbon/mob, alien, removed, datum/reagents/holder)
..()
if(istype(mob,/mob/living/carbon/human))
var/mob/living/carbon/human/victim = mob
victim.add_chemical_effect(CE_PULSE, -2)
var/dose = victim.chem_doses[type]
if(dose > 2)
if(ishuman(victim) && (dose == metabolism * 2 || prob(3)))
victim.emote("yawn")
if(dose > 20)
victim.eye_blurry = max(victim.eye_blurry, 10)
if(dose > 40)
victim.Weaken(1)
victim.drowsiness = max(victim.drowsiness, 20)
if(victim.chem_effects[CE_ANTIPARASITE])
return
if(!victim.internal_organs_by_name[BP_GREIMORIAN_EGGCLUSTER])
var/obj/item/organ/external/affected = pick(victim.organs)
var/obj/item/organ/internal/parasite/greimorian_eggcluster/infest = new()
infest.parent_organ = affected.limb_name
infest.replaced(victim, affected)
/singleton/reagent/toxin/malignant_tumour_cells
name = "Malignant Tumour Cells"
description = "Cells of a malignant tumour which have broken off and entered the circulatory and/or lymphatic system to spread to other regions of the body."
+19
View File
@@ -0,0 +1,19 @@
# Your name.
author: Batrachophrenoboocosmomachia
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
delete-after: True
# Any changes you've made. See valid prefix list above.
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
# SCREW THIS UP AND IT WON'T WORK.
# Also, this gets changed to [] after reading. Just remove the brackets when you add new shit.
# Please surround your changes in double quotes ("). It works without them, but if you use certain characters it screws up compiling. The quotes will not show up in the changelog.
changes:
- refactor: "Refactors the egg injection of Greimorian Workers to use parasite code; it can now be treated with anti-parasitic medicines in addition to amputation of the affected limb. Works on all playable species, and monkeys."
- rscadd: "New reagent 'Greimorian Eggs' creates an egg cluster parasite in a random organ when metabolized in the bloodstream. It also has very mild soporific effects."
- balance: "Greimorian Workers and playable Greimorian Servants/Queen now inject Greimorian Eggs instead of Soporific."
- rscadd: "Greimorian Clade Kit now available in Uplink under Bioweapons section (Greimorians now able to be introduced to ship by egg cluster or injectable reagent)."
- balance: "All parasite kit bioweapons have had their vial reagent count increased from 2 -> 5."
- balance: "Helmizole no longer treats K'ois Mycosis, Black K'ois Mycosis, Zombification, or Tumors (Benign or Malignant)."
- code_imp: "Updates a lot of code documentation to dmdocs."
File diff suppressed because one or more lines are too long