[MIRROR] Adds Ants, Decomposition, and some extras. (#6944)

* Adds Ants, Decomposition, and some extras. (#59634)

Co-authored-by: Kylerace <kylerlumpkin1@ gmail.com>

* Adds Ants, Decomposition, and some extras.

Co-authored-by: Wallemations <66052067+Wallemations@users.noreply.github.com>
Co-authored-by: Kylerace <kylerlumpkin1@ gmail.com>
This commit is contained in:
SkyratBot
2021-07-16 08:05:37 +01:00
committed by GitHub
co-authored by Kylerace Wallemations
parent 88b3b30c2d
commit 114347972a
26 changed files with 524 additions and 25 deletions
+2
View File
@@ -43,6 +43,8 @@
#define CALTROP_BYPASS_SHOES (1 << 0)
#define CALTROP_IGNORE_WALKERS (1 << 1)
#define CALTROP_SILENT (1 << 2)
#define CALTROP_NOSTUN (1 << 3)
#define CALTROP_NOCRAWL (1 << 4)
//Ingredient type in datum/component/customizable_reagent_holder
#define CUSTOM_INGREDIENT_TYPE_EDIBLE 1
+3
View File
@@ -114,6 +114,9 @@
/// Read the documentation for /datum/status_effect/confusion for more information.
#define STATUS_EFFECT_CONFUSION /datum/status_effect/confusion
//Deals with covering the target in ants.
#define STATUS_EFFECT_ANTS /datum/status_effect/ants
/////////////
// NEUTRAL //
/////////////
@@ -0,0 +1,147 @@
//"Don't leave food on the floor, that's how we get ants"
#define DECOMPOSITION_TIME 10 MINUTES
#define DECOMPOSITION_TIME_RAW 5 MINUTES
#define DECOMPOSITION_TIME_GROSS 7 MINUTES
#define DECOMP_EXAM_NORMAL 0
#define DECOMP_EXAM_GROSS 1
#define DECOMP_EXAM_RAW 2
/datum/component/decomposition
dupe_mode = COMPONENT_DUPE_UNIQUE
/// Makes sure food only starts decomposing if a player's EVER picked it up before
var/handled = FALSE
/// Used to stop food in someone's hand & in storage slots from decomposing.
var/protected = FALSE
/// Used to stop the timer & check for the examine proc
var/timerid
/// Used so the timer won't reset.
var/time_remaining = DECOMPOSITION_TIME
/// Used to give raw/gross food lower timers
var/decomp_flags
/// Used for examining
var/examine_type = DECOMP_EXAM_NORMAL
/datum/component/decomposition/Initialize(decomp_flags = NONE)
if(!isobj(parent))
return COMPONENT_INCOMPATIBLE
src.decomp_flags = decomp_flags
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/handle_movement)
RegisterSignal(parent, list(
COMSIG_ITEM_PICKUP, //person picks up an item
COMSIG_STORAGE_ENTERED), //Object enters a storage object (boxes, etc.)
.proc/picked_up)
RegisterSignal(parent, list(
COMSIG_ITEM_DROPPED, //Object is dropped anywhere
COMSIG_STORAGE_EXITED), //Object exits a storage object (boxes, etc)
.proc/dropped)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
if(decomp_flags & RAW) // Raw food overrides gross
time_remaining = DECOMPOSITION_TIME_RAW
examine_type = DECOMP_EXAM_RAW
else if(decomp_flags & GROSS)
time_remaining = DECOMPOSITION_TIME_GROSS
examine_type = DECOMP_EXAM_GROSS
/datum/component/decomposition/UnregisterFromParent()
UnregisterSignal(parent, list(
COMSIG_ITEM_PICKUP,
COMSIG_STORAGE_ENTERED,
COMSIG_MOVABLE_MOVED,
COMSIG_ITEM_DROPPED,
COMSIG_STORAGE_EXITED,
COMSIG_PARENT_EXAMINE))
/datum/component/decomposition/proc/handle_movement()
if(!handled) // Has someone touched this previously?
return
var/obj/food = parent // Doesn't HAVE to be food, that's just what it's intended for
if(!(istype(food.loc, /turf/open))) // Is this currently in an open turf?
remove_timer() // If not, remove any active timers and return
return
if(locate(/obj/structure/table) in get_turf(food)) // Is this currently over a table?
remove_timer()
return
if(locate(/obj/structure/rack) in get_turf(food)) // Is it on a rack?
remove_timer()
return
if(locate(/obj/machinery/conveyor) in get_turf(food)) // Makes sure no decals spawn on disposals conveyors
remove_timer()
return
// If all other checks fail, then begin decomposition.
timerid = addtimer(CALLBACK(src, .proc/decompose), time_remaining, TIMER_STOPPABLE | TIMER_UNIQUE)
/datum/component/decomposition/Destroy()
remove_timer()
return ..()
/datum/component/decomposition/proc/remove_timer()
if(active_timers) // Makes sure there's an active timer to delete.
time_remaining = timeleft(timerid)
deltimer(timerid)
/datum/component/decomposition/proc/dropped()
SIGNAL_HANDLER
protected = FALSE
handle_movement()
/datum/component/decomposition/proc/picked_up()
SIGNAL_HANDLER
remove_timer()
protected = TRUE
if(!handled)
handled = TRUE
/datum/component/decomposition/proc/decompose()
var/obj/decomp = parent //Lets us spawn things at decomp
new /obj/effect/decal/cleanable/ants(decomp.loc)
new /obj/item/food/badrecipe/moldy(decomp.loc)
decomp.visible_message("<span class='notice'>[decomp] gets overtaken by mold and ants! Gross!</span>")
qdel(decomp)
return
/datum/component/decomposition/proc/examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
var/time_d = 0
if(active_timers) // Is the timer currently applied to this?
time_d = timeleft(timerid)
else
time_d = time_remaining
switch(examine_type)
if(DECOMP_EXAM_NORMAL)// All other types
switch(time_d) // Deciseconds used so there's no gaps between examine times.
if(3001 to 4500) // 7.5 to 5 Minutes left
examine_list += "[parent] looks kinda stale."
if(1501 to 3000) // 5 to 2.5 Minutes left
examine_list += "[parent] is starting to look pretty gross."
if(1 to 1500) // 2.5 Minutes to 1 Decisecond left
examine_list += "[parent] looks barely edible."
if(DECOMP_EXAM_GROSS) // Gross food
switch(time_d)
if(2101 to 3150) // 5.25 to 3.5 Minutes
examine_list += "[parent] looks kinda stale."
if(1050 to 2100) // 3.5 to 1.75 Minutes left
examine_list += "[parent] is starting to look pretty gross."
if(1 to 1051) // 1.75 Minutes to 1 Decisecond left
examine_list += "[parent] looks barely edible."
if(DECOMP_EXAM_RAW) // Raw food
switch(time_d)
if(1501 to 2250) // 3.75 to 2.5 Minutes left
examine_list += "[parent] looks kinda stale."
if(751 to 1500) // 2.5 to 1.25 Minutes left
examine_list += "[parent] is starting to look pretty gross."
if(1 to 750) // 1.25 Minutes to 1 Decisecond left
examine_list += "[parent] looks barely edible."
#undef DECOMPOSITION_TIME
#undef DECOMPOSITION_TIME_GROSS
#undef DECOMPOSITION_TIME_RAW
#undef DECOMP_EXAM_NORMAL
#undef DECOMP_EXAM_GROSS
#undef DECOMP_EXAM_RAW
+13 -3
View File
@@ -19,12 +19,15 @@
///Miscelanous caltrop flags; shoe bypassing, walking interaction, silence
var/flags
///The sound that plays when a caltrop is triggered.
var/soundfile
///given to connect_loc to listen for something moving over target
var/static/list/crossed_connections = list(
COMSIG_ATOM_ENTERED = .proc/on_entered,
)
/datum/element/caltrop/Attach(datum/target, min_damage = 0, max_damage = 0, probability = 100, flags = NONE)
/datum/element/caltrop/Attach(datum/target, min_damage = 0, max_damage = 0, probability = 100, flags = NONE, soundfile = null)
. = ..()
if(!isatom(target))
return ELEMENT_INCOMPATIBLE
@@ -33,6 +36,7 @@
src.max_damage = max(min_damage, max_damage)
src.probability = probability
src.flags = flags
src.soundfile = soundfile
if(ismovable(target))
AddElement(/datum/element/connect_loc_behalf, target, crossed_connections)
@@ -62,7 +66,7 @@
if(H.buckled) //if they're buckled to something, that something should be checked instead.
return
if(H.body_position == LYING_DOWN) //if we're not standing we cant step on the caltrop
if(H.body_position == LYING_DOWN && !(flags & CALTROP_NOCRAWL)) //if we're not standing we cant step on the caltrop
return
var/picked_def_zone = pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)
@@ -94,7 +98,13 @@
)
H.apply_damage(damage, BRUTE, picked_def_zone, wound_bonus = CANT_WOUND)
H.Paralyze(60)
if(!(flags & CALTROP_NOSTUN)) // Won't set off the paralysis.
H.Paralyze(60)
if(!soundfile)
return
playsound(H, soundfile, 15, TRUE, -3)
/datum/element/caltrop/Detach(datum/target)
. = ..()
+86 -1
View File
@@ -969,6 +969,91 @@
. = ..()
QDEL_NULL(mob_overlay)
//Deals with ants covering someone.
/datum/status_effect/ants
id = "ants"
status_type = STATUS_EFFECT_REFRESH
alert_type = /atom/movable/screen/alert/status_effect/ants
duration = 2 MINUTES //Keeping the normal timer makes sure people can't somehow dump 300+ ants on someone at once so they stay there for like 30 minutes. Max w/ 1 dump is 57.6 brute.
examine_text = "<span class='warning'>SUBJECTPRONOUN is covered in ants!</span>"
processing_speed = STATUS_EFFECT_NORMAL_PROCESS
/// Will act as the main timer as well as changing how much damage the ants do.
var/ants_remaining = 0
/datum/status_effect/ants/on_creation(mob/living/new_owner, amount_left)
if(isnum(amount_left))
to_chat(new_owner, "<span class='userdanger'>You're covered in ants!</span>")
ants_remaining += amount_left
. = ..()
/datum/status_effect/ants/refresh(effect, amount_left)
var/mob/living/carbon/human/victim = owner
if(isnum(amount_left) && ants_remaining >= 1)
if(!prob(1)) // 99%
to_chat(victim, "<span class='userdanger'>You're covered in MORE ants!</span>")
else // 1%
victim.say("AAHH! THIS SITUATION HAS ONLY BEEN MADE WORSE WITH THE ADDITION OF YET MORE ANTS!!", forced = /datum/status_effect/ants)
ants_remaining += amount_left
. = ..()
/datum/status_effect/ants/on_remove()
ants_remaining = 0
to_chat(owner, "<span class='notice'>All of the ants are off of your body!</span>")
UnregisterSignal(owner, COMSIG_COMPONENT_CLEAN_ACT, .proc/ants_washed)
. = ..()
/datum/status_effect/ants/proc/ants_washed()
SIGNAL_HANDLER
owner.remove_status_effect(STATUS_EFFECT_ANTS)
return COMPONENT_CLEANED
/datum/status_effect/ants/tick()
var/mob/living/carbon/human/victim = owner
victim.adjustBruteLoss(max(0.1, round((ants_remaining * 0.004),0.1))) //Scales with # of ants (lowers with time). Roughly 10 brute over 50 seconds.
if(victim.stat <= SOFT_CRIT) //Makes sure people don't scratch at themselves while they're unconcious
if(prob(15))
switch(rand(1,2))
if(1)
victim.say(pick("GET THEM OFF ME!!", "OH GOD THE ANTS!!", "MAKE IT END!!", "THEY'RE EVERYWHERE!!", "GET THEM OFF!!", "SOMEBODY HELP ME!!"), forced = /datum/status_effect/ants)
if(2)
victim.emote("scream")
if(prob(50))
switch(rand(1,50))
if (1 to 8) //16% Chance
var/obj/item/bodypart/head/hed = victim.get_bodypart(BODY_ZONE_HEAD)
to_chat(victim, "<span class='danger'>You scratch at the ants on your scalp!.</span>")
hed.receive_damage(0.1,0)
if (9 to 29) //40% chance
var/obj/item/bodypart/arm = victim.get_bodypart(pick(BODY_ZONE_L_ARM,BODY_ZONE_R_ARM))
to_chat(victim, "<span class='danger'>You scratch at the ants on your arms!</span>")
arm.receive_damage(0.1,0)
if (30 to 49) //38% chance
var/obj/item/bodypart/leg = victim.get_bodypart(pick(BODY_ZONE_L_LEG,BODY_ZONE_R_LEG))
to_chat(victim, "<span class='danger'>You scratch at the ants on your leg!</span>")
leg.receive_damage(0.1,0)
if(50) // 2% chance
to_chat(victim, "<span class='danger'>You rub some ants away from your eyes!</span>")
victim.blur_eyes(3)
ants_remaining -= 5 // To balance out the blindness, it'll be a little shorter.
ants_remaining--
if(ants_remaining <= 0 || victim.stat >= HARD_CRIT)
victim.remove_status_effect(STATUS_EFFECT_ANTS) //If this person has no more ants on them or are dead, they are no longer affected.
/atom/movable/screen/alert/status_effect/ants
name = "Ants!"
desc = "<span class='warning'>JESUS FUCKING CHRIST! CLICK TO GET THOSE THINGS OFF!</span>"
icon_state = "antalert"
/atom/movable/screen/alert/status_effect/ants/Click()
var/mob/living/living = owner
if(!istype(living) || !living.can_resist() || living != owner)
return
to_chat(living, "<span class='notice'>You start to shake the ants off!</span>")
if(!do_after(living, 2 SECONDS, target = living))
return
for (var/datum/status_effect/ants/ant_covered in living.status_effects)
to_chat(living, "<span class='notice'>You manage to get some of the ants off!</span>")
ant_covered.ants_remaining -= 10 // 5 Times more ants removed per second than just waiting in place
/datum/status_effect/ghoul
id = "ghoul"
@@ -980,4 +1065,4 @@
name = "Flesh Servant"
desc = "You are a Ghoul! A eldritch monster reanimated to serve its master."
icon_state = "mind_control"
+4 -4
View File
@@ -78,7 +78,7 @@
/datum/status_effect/proc/before_remove() //! Called before being removed; returning FALSE will cancel removal
return TRUE
/datum/status_effect/proc/refresh()
/datum/status_effect/proc/refresh(effect, ...)
var/original_duration = initial(duration)
if(original_duration == -1)
return
@@ -112,17 +112,17 @@
. = FALSE
var/datum/status_effect/S1 = effect
LAZYINITLIST(status_effects)
var/list/arguments = args.Copy()
arguments[1] = src
for(var/datum/status_effect/S in status_effects)
if(S.id == initial(S1.id) && S.status_type)
if(S.status_type == STATUS_EFFECT_REPLACE)
S.be_replaced()
else if(S.status_type == STATUS_EFFECT_REFRESH)
S.refresh()
S.refresh(arglist(arguments))
return
else
return
var/list/arguments = args.Copy()
arguments[1] = src
S1 = new effect(arguments)
. = S1
@@ -257,3 +257,20 @@
/obj/effect/decal/cleanable/garbage/Initialize()
. = ..()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_SLUDGE, CELL_VIRUS_TABLE_GENERIC, rand(2,4), 15)
/obj/effect/decal/cleanable/ants
name = "space ants"
desc = "A small colony of space ants. They're normally used to the vacuum of space, so they can't climb too well."
icon = 'icons/obj/objects.dmi'
icon_state = "spaceants"
beauty = -150
/obj/effect/decal/cleanable/ants/Initialize(mapload)
. = ..()
var/scale = (rand(6, 8) / 10) + (rand(2, 5) / 50)
transform = matrix(transform, scale, scale, MATRIX_SCALE)
setDir(pick(GLOB.cardinals))
reagents.add_reagent(/datum/reagent/ants, rand(2, 5))
pixel_x = rand(-5, 5)
pixel_y = rand(-5, 5)
AddElement(/datum/element/caltrop, min_damage = 0.2, max_damage = 1, flags = (CALTROP_NOCRAWL | CALTROP_NOSTUN | CALTROP_BYPASS_SHOES), soundfile = 'sound/weapons/bite.ogg')
+2 -1
View File
@@ -11,5 +11,6 @@
/obj/effect/decal/cleanable/wrapping = 5,
/obj/effect/decal/cleanable/plastic = 5,
/obj/effect/decal/cleanable/glass = 5,
/obj/effect/decal/cleanable/dirt = 30
/obj/effect/decal/cleanable/dirt = 30,
/obj/effect/decal/cleanable/ants = 5,
)
+8
View File
@@ -36,6 +36,8 @@
var/burns_on_grill = FALSE
///Price of this food if sold in a venue
var/venue_value
///Food that's immune to decomposition.
var/preserved_food = FALSE
/obj/item/food/Initialize()
. = ..()
@@ -51,6 +53,7 @@
MakeProcessable()
MakeLeaveTrash()
MakeGrillable()
MakeDecompose()
///This proc adds the edible component, overwrite this if you for some reason want to change some specific args like callbacks.
/obj/item/food/proc/MakeEdible()
@@ -82,3 +85,8 @@
if(trash_type)
AddElement(/datum/element/food_trash, trash_type)
return
///This proc makes things decompose. Set preserved_food to TRUE to make it never decompose.
/obj/item/food/proc/MakeDecompose()
if(!preserved_food)
AddComponent(/datum/component/decomposition, decomp_flags = foodtypes)
+15 -1
View File
@@ -190,6 +190,7 @@
food_reagents = list(/datum/reagent/toxin/bad_food = 30)
foodtypes = GROSS
w_class = WEIGHT_CLASS_SMALL
preserved_food = TRUE //Can't decompose any more than this
/obj/item/food/badrecipe/Initialize()
. = ..()
@@ -197,7 +198,7 @@
/obj/item/food/badrecipe/moldy
name = "moldy mess"
desc = "A rancid, living culture of mold. Somewhere, under there, at SOME POINT... there was food."
desc = "A rancid, disgusting culture of mold and ants. Somewhere under there, at <i>some point,</i> there was food."
food_reagents = list(/datum/reagent/consumable/mold = 30)
/obj/item/food/badrecipe/moldy/Initialize()
@@ -592,12 +593,15 @@
w_class = WEIGHT_CLASS_NORMAL
max_volume = 30
w_class = WEIGHT_CLASS_SMALL
preserved_food = TRUE
/obj/item/food/canned/proc/open_can(mob/user)
to_chat(user, span_notice("You pull back the tab of \the [src]."))
playsound(user.loc, 'sound/items/foodcanopen.ogg', 50)
reagents.flags |= OPENCONTAINER
preserved_food = FALSE
MakeDecompose()
/obj/item/food/canned/attack_self(mob/user)
if(!is_drainable())
@@ -691,3 +695,13 @@
/obj/item/food/rationpack/proc/check_liked(fraction, mob/M) //Nobody likes rationpacks. Nobody.
return FOOD_DISLIKED
/obj/item/food/ant_candy
name = "ant candy"
desc = "A colony of ants suspended in hardened sugar. Those things are dead, right?"
icon_state = "ant_pop"
food_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 1, /datum/reagent/consumable/sugar = 5, /datum/reagent/ants = 3)
tastes = list("candy" = 1, "insects" = 1)
foodtypes = JUNKFOOD | SUGAR | GROSS
food_flags = FOOD_FINGER_FOOD
w_class = WEIGHT_CLASS_TINY
+18
View File
@@ -252,3 +252,21 @@
/obj/item/food/pizzaslice/arnold/attackby(obj/item/I, mob/user)
i_kill_you(I, user)
. = ..()
// Ant Pizza, now with more ants.
/obj/item/food/pizza/ants
name = "\improper Ant Party pizza"
desc = "/// Filled with bugs, remember to fix"
icon_state = "antpizza"
food_reagents = list(/datum/reagent/consumable/nutriment = 20, /datum/reagent/ants = 25, /datum/reagent/consumable/tomatojuice = 10, /datum/reagent/consumable/nutriment/vitamin = 4, /datum/reagent/consumable/nutriment/protein = 2)
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "insects" = 1)
foodtypes = GRAIN | VEGETABLES | DAIRY | GROSS
slice_type = /obj/item/food/pizzaslice/ants
boxtag = "Anthill Deluxe"
/obj/item/food/pizzaslice/ants
name = "\improper Ant Party pizza slice"
desc = "The key to a perfect slice of pizza is not to overdo it with the ants."
icon_state = "antpizzaslice"
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "insects" = 1)
foodtypes = GRAIN | VEGETABLES | DAIRY | GROSS
+16 -9
View File
@@ -37,7 +37,7 @@
if(prob(80)) //mid dirt - 1/15
return
//Construction zones. Blood, sweat, and oil. Oh, and dirt.
//Construction zones. Blood, sweat, and oil. Oh, and dirt. A small colony of space-ants or two will pop up
var/static/list/engine_dirt_areas = typecacheof(list(/area/engineering,
/area/command/heads_quarters/ce,
/area/science/robotics,
@@ -49,13 +49,16 @@
if(prob(3))
new /obj/effect/decal/cleanable/blood/old(src)
else
if(prob(35))
if(prob(4))
new /obj/effect/decal/cleanable/robot_debris/old(src)
else
new /obj/effect/decal/cleanable/oil(src)
else
new /obj/effect/decal/cleanable/dirt(src)
switch (rand(1, 100))
if(1 to 35)//35%
if(prob(4))
new /obj/effect/decal/cleanable/robot_debris/old(src)
else
new /obj/effect/decal/cleanable/oil(src)
if(35 to 95)//60%
new /obj/effect/decal/cleanable/dirt(src)
if(95 to 100)//5%
new /obj/effect/decal/cleanable/ants(src)
return
//Bathrooms. Blood, vomit, and shavings in the sinks.
@@ -67,6 +70,8 @@
new /obj/effect/decal/cleanable/vomit/old(src)
else
new /obj/effect/decal/cleanable/blood/old(src)
else if(prob(40)) //16% effective chance
new /obj/effect/decal/cleanable/ants(src)
return
// Cargo bays covered in oil.
@@ -95,7 +100,7 @@
new /obj/effect/decal/cleanable/blood/old(src)
return
//Kitchen areas. Broken eggs, flour, spilled milk (no crying allowed.)
//Kitchen areas. Broken eggs, flour, spilled milk (no crying allowed.), ants.
var/static/list/kitchen_dirt_areas = typecacheof(list(/area/service/kitchen,
/area/service/cafeteria))
if(is_type_in_typecache(A, kitchen_dirt_areas))
@@ -104,6 +109,8 @@
new /obj/effect/decal/cleanable/food/egg_smudge(src)
else
new /obj/effect/decal/cleanable/food/flour(src)
else if(prob(20)) //12% effective chance
new /obj/effect/decal/cleanable/ants(src)
return
//Medical areas. Mostly clean by space-OSHA standards, but has some blood and oil spread about.
@@ -193,3 +193,13 @@
)
result = /obj/item/food/cheese/royal
subcategory = CAT_MISCFOOD
/datum/crafting_recipe/food/ant_candy
name = "Ant Candy"
reqs = list(/obj/item/stack/rods = 1,
/datum/reagent/consumable/sugar = 5,
/datum/reagent/water = 5,
/datum/reagent/ants = 10
)
result = /obj/item/food/ant_candy
subcategory = CAT_MISCFOOD
@@ -101,3 +101,14 @@
)
result = /obj/item/food/pizza/pineapple
subcategory = CAT_PIZZA
/datum/crafting_recipe/food/antspizza
name = "Ant Party pizza"
reqs = list(
/obj/item/food/pizzabread = 1,
/obj/item/food/cheese = 2,
/obj/item/food/grown/tomato = 1,
/datum/reagent/ants = 20
)
result = /obj/item/food/pizza/ants
subcategory = CAT_PIZZA
@@ -0,0 +1,50 @@
/mob/living/simple_animal/ant
name = "giant ant"
desc = "A writhing mass of ants, glued together to make an adorable pet!"
icon = 'icons/mob/pets.dmi'
icon_state = "ant"
icon_living = "ant"
icon_dead = "ant_dead"
speak = list("BZZZZT!", "CHTCHTCHT!", "Bzzz", "ChtChtCht")
speak_emote = list("buzzes", "chitters")
emote_hear = list("buzzes.", "clacks.")
emote_see = list("shakes its head.", "twitches its antennae.")
speak_chance = 1
turns_per_move = 5
see_in_dark = 6
pass_flags = PASSTABLE
mob_size = MOB_SIZE_SMALL
mob_biotypes = MOB_ORGANIC|MOB_BUG
minbodytemp = 200
maxbodytemp = 400
unsuitable_atmos_damage = 1
butcher_results = list(/obj/item/food/meat/slab = 2, /obj/effect/decal/cleanable/ants = 1) //It's just a bunch of ants glued together into a larger ant
response_help_continuous = "pets"
response_help_simple = "pet"
response_disarm_continuous = "gently pushes aside"
response_disarm_simple = "gently push aside"
response_harm_continuous = "kicks"
response_harm_simple = "kick"
gold_core_spawnable = FRIENDLY_SPAWN
can_be_held = FALSE
footstep_type = FOOTSTEP_MOB_CLAW
health = 75
maxHealth = 75
// randomizes hunting intervals, minimum 5 turns
var/time_to_hunt = 5
/mob/living/simple_animal/ant/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
time_to_hunt = rand(5,10)
/mob/living/simple_animal/ant/Life(delta_time = SSMOBS_DT, times_fired) // In this larger state, the ants have become the predators.
. = ..()
turns_since_scan++
if(turns_since_scan > time_to_hunt)
turns_since_scan = 0
var/list/target_types = list(/mob/living/simple_animal/hostile/cockroach)
for(var/mob/living/simple_animal/hostile/potential_target in view(2, get_turf(src)))
if(potential_target.type in target_types)
hunt(potential_target)
return
@@ -29,12 +29,7 @@
response_harm_continuous = "kicks"
response_harm_simple = "kick"
mobility_flags = MOBILITY_FLAGS_REST_CAPABLE_DEFAULT
var/turns_since_scan = 0
var/mob/living/simple_animal/mouse/movement_target
///Limits how often cats can spam chasing mice.
COOLDOWN_DECLARE(emote_cooldown)
///Can this cat catch special mice?
var/inept_hunter = FALSE
gold_core_spawnable = FRIENDLY_SPAWN
collar_type = "cat"
can_be_held = TRUE
@@ -31,13 +31,16 @@
environment_smash = ENVIRONMENT_SMASH_NONE
faction = list("neutral")
var/squish_chance = 50
// Randomizes hunting intervals, minumum 5 turns
var/time_to_hunt = 5
/mob/living/simple_animal/hostile/cockroach/Initialize()
. = ..()
add_cell_sample()
make_squashable()
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
time_to_hunt = rand(5,10)
/mob/living/simple_animal/hostile/cockroach/proc/make_squashable()
AddComponent(/datum/component/squashable, squash_chance = 50, squash_damage = 1)
@@ -45,6 +48,18 @@
/mob/living/simple_animal/hostile/cockroach/add_cell_sample()
AddElement(/datum/element/swabable, CELL_LINE_TABLE_COCKROACH, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 7)
/mob/living/simple_animal/hostile/cockroach/Life(delta_time = SSMOBS_DT, times_fired) // Cockroaches are predators to space ants
. = ..()
turns_since_scan++
if(turns_since_scan > time_to_hunt)
turns_since_scan = 0
var/list/target_types = list(/obj/effect/decal/cleanable/ants)
for(var/obj/effect/decal/cleanable/ants/potential_target in view(2, get_turf(src)))
if(potential_target.type in target_types)
hunt(potential_target)
return
/obj/projectile/glockroachbullet
damage = 10 //same damage as a hivebot
damage_type = BRUTE
@@ -156,6 +156,13 @@
///Generic flags
var/simple_mob_flags = NONE
///Limits how often mobs can hunt other mobs
COOLDOWN_DECLARE(emote_cooldown)
var/turns_since_scan = 0
///Is this animal horrible at hunting?
var/inept_hunter = FALSE
/mob/living/simple_animal/Initialize(mapload)
. = ..()
@@ -684,3 +691,41 @@
/mob/living/simple_animal/proc/stop_deadchat_plays()
stop_automated_movement = FALSE
//Makes this mob hunt the prey, be it living or an object. Will kill living creatures, and delete objects.
/mob/living/simple_animal/proc/hunt(hunted)
if(src == hunted) //Make sure it doesn't eat itself. While not likely to ever happen, might as well check just in case.
return
stop_automated_movement = FALSE
if(!isturf(src.loc)) // Are we on a proper turf?
return
if(stat || resting || buckled) // Are we concious, upright, and not buckled?
return
if(!COOLDOWN_FINISHED(src, emote_cooldown)) // Has the cooldown on this ended?
return
if(!Adjacent(hunted))
stop_automated_movement = TRUE
walk_to(src,hunted,0,3)
if(Adjacent(hunted))
hunt(hunted) // In case it gets next to the target immediately, skip the scan timer and kill it.
return
if(isliving(hunted)) // Are we hunting a living mob?
var/mob/living/prey = hunted
if(inept_hunter) // Make your hunter inept to have them unable to catch their prey.
visible_message("<span class='warning'>[src] chases [prey] around, to no avail!</span>")
step(prey, pick(GLOB.cardinals))
COOLDOWN_START(src, emote_cooldown, 1 MINUTES)
return
if(!(prey.stat))
manual_emote("chomps [prey]!")
prey.death()
prey = null
COOLDOWN_START(src, emote_cooldown, 1 MINUTES)
return
else // We're hunting an object, and should delete it instead of killing it. Mostly useful for decal bugs like ants or spider webs.
manual_emote("chomps [hunted]!")
qdel(hunted)
hunted = null
COOLDOWN_START(src, emote_cooldown, 1 MINUTES)
return
@@ -2715,3 +2715,46 @@
var/color
CONVERT_PH_TO_COLOR(exposed_atom.reagents.ph, color)
exposed_atom.add_atom_colour(color, WASHABLE_COLOUR_PRIORITY)
/datum/reagent/ants
name = "Ants"
description = "A sample of a lost breed of Space Ants (formicidae bastardium tyrannus), they are well-known for ravaging the living shit out of pretty much anything."
reagent_state = SOLID
color = "#993333"
taste_mult = 1.3
taste_description = "tiny legs scuttling down the back of your throat."
metabolization_rate = 5 * REAGENTS_METABOLISM //1u per second
glass_name = "glass of ants"
glass_desc = "Bottoms up...?"
ph = 4.6 // Ants contain Formic Acid
/// How much damage the ants are going to be doing (rises with each tick the ants are in someone's body)
var/ant_damage = 0
/// Tells the debuff how many ants we are being covered with.
var/amount_left = 0
/datum/reagent/ants/on_mob_life(mob/living/carbon/victim, delta_time)
victim.adjustBruteLoss(max(0.1, round((ant_damage * 0.005),0.1))) //Scales with time. Around 12.5 brute for 50 seconds.
if(DT_PROB(5, delta_time))
if(DT_PROB(5, delta_time)) //Super rare statement
victim.say("AUGH NO NOT THE ANTS! NOT THE ANTS! AAAAUUGH THEY'RE IN MY EYES! MY EYES! AUUGH!!", forced = /datum/reagent/ants)
else
victim.say(pick("THEY'RE UNDER MY SKIN!!", "GET THEM OUT OF ME!!", "HOLY HELL THEY BURN!!", "MY GOD THEY'RE INSIDE ME!!", "GET THEM OUT!!"), forced = /datum/reagent/ants)
if(DT_PROB(15, delta_time))
victim.emote("scream")
if(DT_PROB(2, delta_time))
victim.vomit(rand(1, 2), stun = FALSE)
ant_damage += 1
return ..()
/datum/reagent/ants/on_mob_end_metabolize(mob/living/living_anthill)
ant_damage = 0
to_chat(living_anthill, "<span class='notice'>You feel like the last of the ants are out of your system.</span>")
return ..()
/datum/reagent/ants/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume)
. = ..()
if(!iscarbon(exposed_mob) || (methods & (INGEST|INJECT)))
return
if(methods & (PATCH|TOUCH|VAPOR))
amount_left = round(reac_volume,0.1)
exposed_mob.apply_status_effect(STATUS_EFFECT_ANTS, amount_left)
@@ -908,3 +908,19 @@
return
clear_products(holder, step_volume_added)
holder.my_atom.audible_message(span_notice("[icon2html(holder.my_atom, viewers(DEFAULT_MESSAGE_RANGE, src))] The reaction gives out a fizz, teleporting items everywhere!"))
/datum/chemical_reaction/ants
results = list(/datum/reagent/ants = 3)
required_reagents = list(/datum/reagent/ants = 2, /datum/reagent/consumable/sugar = 6)
/datum/chemical_reaction/ant_slurry // We're basically glueing ants together with synthflesh & maint sludge to make a bigger ant.
required_reagents = list(/datum/reagent/ants = 40, /datum/reagent/medicine/c2/synthflesh = 20, /datum/reagent/drug/maint/sludge = 5)
required_temp = 480
reaction_flags = REACTION_INSTANT
reaction_tags = REACTION_TAG_EASY | REACTION_TAG_UNIQUE
/datum/chemical_reaction/ant_slurry/on_reaction(datum/reagents/holder, datum/equilibrium/reaction, created_volume)
var/location = get_turf(holder.my_atom)
for(var/i in rand(1, created_volume) to created_volume)
new /mob/living/simple_animal/ant(location)
..()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 103 KiB

+2
View File
@@ -641,6 +641,7 @@
#include "code\datums\components\fantasy\affix.dm"
#include "code\datums\components\fantasy\prefixes.dm"
#include "code\datums\components\fantasy\suffixes.dm"
#include "code\datums\components\food\decomposition.dm"
#include "code\datums\components\food\edible.dm"
#include "code\datums\components\food\ice_cream_holder.dm"
#include "code\datums\components\plumbing\_plumbing.dm"
@@ -2743,6 +2744,7 @@
#include "code\modules\mob\living\simple_animal\bot\secbot.dm"
#include "code\modules\mob\living\simple_animal\bot\SuperBeepsky.dm"
#include "code\modules\mob\living\simple_animal\bot\vibebot.dm"
#include "code\modules\mob\living\simple_animal\friendly\ant.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\crab.dm"