mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-27 23:27:34 +01:00
March into Mapness: Meateor (#74070)
This commit is contained in:
@@ -0,0 +1,981 @@
|
||||
//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
|
||||
"a" = (
|
||||
/turf/template_noop,
|
||||
/area/template_noop)
|
||||
"e" = (
|
||||
/obj/effect/mob_spawn/corpse/headcrab,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"f" = (
|
||||
/obj/effect/mob_spawn/corpse/human/monkey,
|
||||
/obj/effect/decal/cleanable/blood/tracks{
|
||||
dir = 8
|
||||
},
|
||||
/obj/effect/decal/cleanable/blood,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"g" = (
|
||||
/turf/closed/mineral/random,
|
||||
/area/ruin/space/meateor)
|
||||
"j" = (
|
||||
/obj/item/gun/energy/recharge/kinetic_accelerator,
|
||||
/obj/effect/decal/cleanable/blood,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"k" = (
|
||||
/obj/item/flashlight/flare,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"m" = (
|
||||
/obj/item/toy/plush/snakeplushie,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"p" = (
|
||||
/obj/structure/statue/bone/rib,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"q" = (
|
||||
/obj/effect/mob_spawn/corpse/headcrab,
|
||||
/obj/effect/decal/cleanable/xenoblood,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"r" = (
|
||||
/obj/effect/decal/cleanable/blood/tracks{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"s" = (
|
||||
/obj/effect/mob_spawn/corpse/headcrab,
|
||||
/obj/effect/decal/cleanable/xenoblood/xgibs,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"v" = (
|
||||
/obj/structure/meateor_fluff/eyeball,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"z" = (
|
||||
/obj/effect/mob_spawn/corpse/human/tigercultist/perforated,
|
||||
/obj/item/storage/organbox/preloaded{
|
||||
desc = "Often used, but not often cleaned.";
|
||||
name = "abandoned lunchbox"
|
||||
},
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"A" = (
|
||||
/turf/closed/wall/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"B" = (
|
||||
/obj/item/paper/fluff/ruins/meateor/letter,
|
||||
/obj/structure/bed/maint,
|
||||
/obj/item/reagent_containers/cup/glass/bottle/ritual_wine{
|
||||
pixel_x = -10
|
||||
},
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"C" = (
|
||||
/obj/effect/mob_spawn/corpse/human/miner,
|
||||
/obj/effect/decal/cleanable/blood,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"D" = (
|
||||
/obj/structure/statue/bone/rib{
|
||||
dir = 8
|
||||
},
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"G" = (
|
||||
/obj/structure/meateor_fluff/flesh_pod,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"L" = (
|
||||
/obj/effect/spawner/random/trash/food_packaging,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"O" = (
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"R" = (
|
||||
/obj/structure/meateor_fluff/abandoned_headcrab_egg,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"S" = (
|
||||
/obj/effect/decal/remains/human,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"T" = (
|
||||
/turf/open/misc/asteroid/airless,
|
||||
/area/ruin/space/meateor)
|
||||
"X" = (
|
||||
/mob/living/basic/meteor_heart,
|
||||
/turf/open/floor/material/meat/airless,
|
||||
/area/ruin/space/meateor)
|
||||
|
||||
(1,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(2,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(3,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(4,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(5,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
L
|
||||
G
|
||||
S
|
||||
G
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(6,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
B
|
||||
z
|
||||
O
|
||||
v
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
G
|
||||
A
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
A
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(7,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
e
|
||||
D
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
v
|
||||
D
|
||||
R
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
A
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(8,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
A
|
||||
A
|
||||
L
|
||||
G
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
R
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(9,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
A
|
||||
A
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
G
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(10,1,1) = {"
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
G
|
||||
O
|
||||
v
|
||||
O
|
||||
O
|
||||
X
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(11,1,1) = {"
|
||||
a
|
||||
a
|
||||
g
|
||||
A
|
||||
A
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
S
|
||||
O
|
||||
v
|
||||
O
|
||||
O
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
a
|
||||
a
|
||||
"}
|
||||
(12,1,1) = {"
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
G
|
||||
O
|
||||
S
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
R
|
||||
O
|
||||
O
|
||||
O
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
"}
|
||||
(13,1,1) = {"
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
R
|
||||
O
|
||||
p
|
||||
O
|
||||
O
|
||||
v
|
||||
O
|
||||
O
|
||||
p
|
||||
O
|
||||
G
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
A
|
||||
A
|
||||
g
|
||||
a
|
||||
"}
|
||||
(14,1,1) = {"
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
R
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
A
|
||||
g
|
||||
a
|
||||
"}
|
||||
(15,1,1) = {"
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
q
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
m
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(16,1,1) = {"
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
D
|
||||
k
|
||||
C
|
||||
D
|
||||
O
|
||||
R
|
||||
D
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(17,1,1) = {"
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
R
|
||||
O
|
||||
j
|
||||
O
|
||||
O
|
||||
q
|
||||
v
|
||||
O
|
||||
R
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(18,1,1) = {"
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
O
|
||||
O
|
||||
O
|
||||
s
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(19,1,1) = {"
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
R
|
||||
O
|
||||
v
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
O
|
||||
G
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(20,1,1) = {"
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
p
|
||||
R
|
||||
O
|
||||
p
|
||||
O
|
||||
r
|
||||
p
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
T
|
||||
T
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(21,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
O
|
||||
A
|
||||
A
|
||||
r
|
||||
A
|
||||
A
|
||||
O
|
||||
v
|
||||
T
|
||||
T
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(22,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
O
|
||||
O
|
||||
O
|
||||
f
|
||||
A
|
||||
A
|
||||
O
|
||||
A
|
||||
A
|
||||
T
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(23,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
O
|
||||
O
|
||||
A
|
||||
A
|
||||
O
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(24,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
v
|
||||
O
|
||||
O
|
||||
O
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
A
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(25,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(26,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
A
|
||||
A
|
||||
A
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
A
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(27,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
g
|
||||
g
|
||||
g
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
(28,1,1) = {"
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
a
|
||||
"}
|
||||
@@ -226,6 +226,9 @@
|
||||
#define BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION "BB_basic_flee_target_hiding_location"
|
||||
#define BB_FLEE_TARGETTING_DATUM "flee_targetting_datum"
|
||||
|
||||
///How long have we spent with no target?
|
||||
#define BB_TARGETLESS_TIME "BB_targetless_time"
|
||||
|
||||
///List of mobs who have damaged us
|
||||
#define BB_BASIC_MOB_RETALIATE_LIST "BB_basic_mob_shitlist"
|
||||
|
||||
@@ -249,6 +252,12 @@
|
||||
#define BB_BILEWORM_RESURFACE "BB_bileworm_resurface"
|
||||
#define BB_BILEWORM_DEVOUR "BB_bileworm_devour"
|
||||
|
||||
// Meteor Heart AI keys
|
||||
/// Key where we keep the spike trail ability
|
||||
#define BB_METEOR_HEART_GROUND_SPIKES "BB_meteor_ground_spikes"
|
||||
/// Key where we keep the spine traps ability
|
||||
#define BB_METEOR_HEART_SPINE_TRAPS "BB_meteor_spine_traps"
|
||||
|
||||
// Spider AI keys
|
||||
/// Key where we store a turf to put webs on
|
||||
#define BB_SPIDER_WEB_TARGET "BB_spider_web_target"
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/// Disables AI after a certain amount of time spent with no target, you will have to enable the AI again somewhere else
|
||||
/datum/ai_planning_subtree/sleep_with_no_target
|
||||
/// Behaviour to execute when sleeping
|
||||
var/sleep_behaviour = /datum/ai_behavior/sleep_after_targetless_time
|
||||
/// Target key to interrogate
|
||||
var/target_key = BB_BASIC_MOB_CURRENT_TARGET
|
||||
|
||||
/datum/ai_planning_subtree/sleep_with_no_target/SelectBehaviors(datum/ai_controller/controller, delta_time)
|
||||
. = ..()
|
||||
controller.queue_behavior(sleep_behaviour, BB_BASIC_MOB_CURRENT_TARGET)
|
||||
|
||||
/// Disables AI after a certain amount of time spent with no target, you will have to enable the AI again somewhere else
|
||||
/datum/ai_behavior/sleep_after_targetless_time
|
||||
/// Turn off AI if we spend this many seconds without a target, don't use the macro because delta_time is already in seconds
|
||||
var/time_to_wait = 10
|
||||
|
||||
/datum/ai_behavior/sleep_after_targetless_time/perform(delta_time, datum/ai_controller/controller, target_key)
|
||||
var/datum/weakref/weak_target = controller.blackboard[target_key]
|
||||
var/atom/target = weak_target?.resolve()
|
||||
finish_action(controller, succeeded = !target, delta_time = delta_time)
|
||||
|
||||
/datum/ai_behavior/sleep_after_targetless_time/finish_action(datum/ai_controller/controller, succeeded, delta_time)
|
||||
. = ..()
|
||||
if (!succeeded)
|
||||
controller.blackboard[BB_TARGETLESS_TIME] = 0
|
||||
return
|
||||
controller.blackboard[BB_TARGETLESS_TIME] += delta_time
|
||||
if (controller.blackboard[BB_TARGETLESS_TIME] > time_to_wait)
|
||||
enter_sleep(controller)
|
||||
|
||||
/// Disables AI, override to do additional things or something else
|
||||
/datum/ai_behavior/sleep_after_targetless_time/proc/enter_sleep(datum/ai_controller/controller)
|
||||
controller.set_ai_status(AI_STATUS_OFF)
|
||||
@@ -6,6 +6,8 @@
|
||||
var/target_key = BB_BASIC_MOB_CURRENT_TARGET
|
||||
/// Behaviour to perform using ability
|
||||
var/use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability
|
||||
/// If true we terminate planning after trying to use the ability.
|
||||
var/finish_planning = TRUE
|
||||
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/SelectBehaviors(datum/ai_controller/controller, delta_time)
|
||||
if (!ability_key)
|
||||
@@ -22,4 +24,5 @@
|
||||
return
|
||||
|
||||
controller.queue_behavior(use_ability_behaviour, ability_key, target_key)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
if (finish_planning)
|
||||
return SUBTREE_RETURN_FINISH_PLANNING
|
||||
|
||||
@@ -1,30 +1,30 @@
|
||||
/datum/component/spawner
|
||||
/// Time to wait between spawns
|
||||
var/spawn_time
|
||||
/// Maximum number of mobs we can have active at one time
|
||||
var/max_mobs
|
||||
/// Visible message to show when a mob spawns
|
||||
/// Maximum number of atoms we can have active at one time
|
||||
var/max_spawned
|
||||
/// Visible message to show when something spawns
|
||||
var/spawn_text
|
||||
/// List of mob types to spawn, picked randomly
|
||||
var/list/mob_types
|
||||
/// Faction to grant to mobs
|
||||
/// List of atom types to spawn, picked randomly
|
||||
var/list/spawn_types
|
||||
/// Faction to grant to mobs (only applies to mobs)
|
||||
var/list/faction
|
||||
/// List of weak references to mobs we have already created
|
||||
var/list/spawned_mobs = list()
|
||||
/// List of weak references to things we have already created
|
||||
var/list/spawned_things = list()
|
||||
/// Time until we next spawn
|
||||
COOLDOWN_DECLARE(spawn_delay)
|
||||
|
||||
/datum/component/spawner/Initialize(mob_types = list(), spawn_time = 30 SECONDS, max_mobs = 5, faction = list(FACTION_MINING), spawn_text = "emerges from")
|
||||
if (!islist(mob_types))
|
||||
CRASH("invalid mob_types to spawn specified for spawner component!")
|
||||
/datum/component/spawner/Initialize(spawn_types = list(), spawn_time = 30 SECONDS, max_spawned = 5, faction = list(FACTION_MINING), spawn_text = null)
|
||||
if (!islist(spawn_types))
|
||||
CRASH("invalid spawn_types to spawn specified for spawner component!")
|
||||
src.spawn_time = spawn_time
|
||||
src.mob_types = mob_types
|
||||
src.spawn_types = spawn_types
|
||||
src.faction = faction
|
||||
src.spawn_text = spawn_text
|
||||
src.max_mobs = max_mobs
|
||||
src.max_spawned = max_spawned
|
||||
|
||||
RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(stop_spawning))
|
||||
START_PROCESSING(SSprocessing, src)
|
||||
START_PROCESSING((spawn_time < 2 SECONDS ? SSfastprocess : SSprocessing), src)
|
||||
|
||||
/datum/component/spawner/process()
|
||||
try_spawn_mob()
|
||||
@@ -34,44 +34,54 @@
|
||||
SIGNAL_HANDLER
|
||||
|
||||
STOP_PROCESSING(SSprocessing, src)
|
||||
spawned_mobs = list()
|
||||
spawned_things = list()
|
||||
|
||||
/// Try to create a new mob
|
||||
/datum/component/spawner/proc/try_spawn_mob()
|
||||
if(!COOLDOWN_FINISHED(src, spawn_delay))
|
||||
return
|
||||
validate_references()
|
||||
if(length(spawned_mobs) >= max_mobs)
|
||||
if(length(spawned_things) >= max_spawned)
|
||||
return
|
||||
var/atom/spawner = parent
|
||||
COOLDOWN_START(src, spawn_delay, spawn_time)
|
||||
|
||||
var/chosen_mob_type = pick(mob_types)
|
||||
var/mob/living/created = new chosen_mob_type(spawner.loc)
|
||||
var/chosen_mob_type = pick(spawn_types)
|
||||
var/atom/created = new chosen_mob_type(spawner.loc)
|
||||
created.flags_1 |= (spawner.flags_1 & ADMIN_SPAWNED_1)
|
||||
spawned_mobs += WEAKREF(created)
|
||||
created.faction = src.faction
|
||||
spawner.visible_message(span_danger("[created] [spawn_text] [spawner]."))
|
||||
spawned_things += WEAKREF(created)
|
||||
if (isliving(created))
|
||||
var/mob/living/created_mob = created
|
||||
created_mob.faction = src.faction
|
||||
RegisterSignal(created, COMSIG_MOB_STATCHANGE, PROC_REF(mob_stat_changed))
|
||||
|
||||
RegisterSignal(created, COMSIG_PARENT_QDELETING, PROC_REF(mob_deleted))
|
||||
RegisterSignal(created, COMSIG_MOB_STATCHANGE, PROC_REF(mob_stat_changed))
|
||||
if (spawn_text)
|
||||
spawner.visible_message(span_danger("[created] [spawn_text] [spawner]."))
|
||||
|
||||
/// Remove weakrefs to mobs which have been killed or deleted without us picking it up somehow
|
||||
RegisterSignal(created, COMSIG_PARENT_QDELETING, PROC_REF(on_deleted))
|
||||
|
||||
/// Remove weakrefs to atoms which have been killed or deleted without us picking it up somehow
|
||||
/datum/component/spawner/proc/validate_references()
|
||||
for (var/datum/weakref/weak_mob as anything in spawned_mobs)
|
||||
var/mob/living/previously_spawned = weak_mob.resolve()
|
||||
if (previously_spawned && previously_spawned.stat != DEAD)
|
||||
for (var/datum/weakref/weak_thing as anything in spawned_things)
|
||||
var/atom/previously_spawned = weak_thing.resolve()
|
||||
if (!previously_spawned)
|
||||
spawned_things -= weak_thing
|
||||
continue
|
||||
spawned_mobs -= weak_mob
|
||||
if (!isliving(previously_spawned))
|
||||
continue
|
||||
var/mob/living/spawned_mob = previously_spawned
|
||||
if (spawned_mob.stat != DEAD)
|
||||
continue
|
||||
spawned_things -= weak_thing
|
||||
|
||||
/// Called when a mob we spawned is deleted, remove it from the list
|
||||
/datum/component/spawner/proc/mob_deleted(mob/living/source)
|
||||
/// Called when an atom we spawned is deleted, remove it from the list
|
||||
/datum/component/spawner/proc/on_deleted(atom/source)
|
||||
SIGNAL_HANDLER
|
||||
spawned_mobs -= WEAKREF(source)
|
||||
spawned_things -= WEAKREF(source)
|
||||
|
||||
/// Called when a mob we spawned dies, remove it from the list and unregister signals
|
||||
/datum/component/spawner/proc/mob_stat_changed(mob/living/source)
|
||||
if (source.stat != DEAD)
|
||||
return
|
||||
spawned_mobs -= WEAKREF(source)
|
||||
spawned_things -= WEAKREF(source)
|
||||
UnregisterSignal(source, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_STATCHANGE))
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/// Prevents a movable atom from moving to somewhere which isn't an open turf with floor on it
|
||||
/datum/element/floor_loving
|
||||
|
||||
/datum/element/floor_loving/Attach(datum/target)
|
||||
. = ..()
|
||||
if(!ismovable(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
RegisterSignal(target, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(attempting_move))
|
||||
|
||||
/datum/element/floor_loving/Detach(datum/target)
|
||||
UnregisterSignal(target, COMSIG_MOVABLE_PRE_MOVE)
|
||||
return ..()
|
||||
|
||||
/// Block movement to any non-floor location
|
||||
/datum/element/floor_loving/proc/attempting_move(atom/movable/parent, newloc)
|
||||
SIGNAL_HANDLER
|
||||
if (!isopenturf(newloc) || isspaceturf(newloc) || isopenspaceturf(newloc))
|
||||
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
|
||||
if (isliving(parent))
|
||||
var/mob/living/living_parent = parent
|
||||
living_parent.balloon_alert(living_parent, "can't move there!")
|
||||
@@ -20,6 +20,13 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
|
||||
target_obj.add_atom_colour(GLOB.freon_color_matrix, TEMPORARY_COLOUR_PRIORITY)
|
||||
target_obj.alpha -= 25
|
||||
|
||||
if (isinternalorgan(target))
|
||||
var/obj/item/organ/internal/organ = target
|
||||
organ.organ_flags |= ORGAN_FROZEN
|
||||
else if (isbodypart(target))
|
||||
for(var/obj/item/organ/internal/organ in target_obj.contents)
|
||||
organ.organ_flags |= ORGAN_FROZEN
|
||||
|
||||
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
|
||||
RegisterSignal(target, COMSIG_MOVABLE_THROW_LANDED, PROC_REF(shatter_on_throw))
|
||||
RegisterSignal(target, COMSIG_MOVABLE_IMPACT, PROC_REF(shatter_on_throw))
|
||||
@@ -32,7 +39,15 @@ GLOBAL_LIST_INIT(freon_color_matrix, list("#2E5E69", "#60A2A8", "#A1AFB1", rgb(0
|
||||
obj_source.name = replacetext(obj_source.name, "frozen ", "")
|
||||
obj_source.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, GLOB.freon_color_matrix)
|
||||
obj_source.alpha += 25
|
||||
. = ..()
|
||||
|
||||
if (isinternalorgan(source))
|
||||
var/obj/item/organ/internal/organ = source
|
||||
organ.organ_flags &= ~ORGAN_FROZEN
|
||||
else if (isbodypart(source))
|
||||
for(var/obj/item/organ/internal/organ in obj_source.contents)
|
||||
organ.organ_flags &= ~ORGAN_FROZEN
|
||||
|
||||
return ..()
|
||||
|
||||
///signal handler for COMSIG_OBJ_UNFREEZE that forces us to detach from the target
|
||||
/datum/element/frozen/proc/on_unfreeze(datum/source)
|
||||
|
||||
@@ -120,10 +120,19 @@
|
||||
return
|
||||
// If we have a timer, we're varying mid length, and this is happening while we're runnin mid_sounds
|
||||
if(timer_id && mid_length_vary && start_time)
|
||||
updatetimedelay(timer_id, mid_length + rand(-mid_length_vary, mid_length_vary))
|
||||
updatetimedelay(timer_id, mid_length + rand(-mid_length_vary, mid_length_vary), timer_subsystem = SSsound_loops)
|
||||
if(!chance || prob(chance))
|
||||
play(get_sound())
|
||||
|
||||
/**
|
||||
* Applies a new mid length to the sound
|
||||
*/
|
||||
/datum/looping_sound/proc/set_mid_length(new_mid)
|
||||
mid_length = new_mid
|
||||
if(!timer_id)
|
||||
return
|
||||
updatetimedelay(timer_id, mid_length + rand(-mid_length_vary, mid_length_vary), timer_subsystem = SSsound_loops)
|
||||
|
||||
/**
|
||||
* The proc that handles actually playing the sound.
|
||||
*
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
start_sound = list('sound/items/taperecorder/taperecorder_hiss_start.ogg' = 1)
|
||||
volume = 10
|
||||
|
||||
/datum/looping_sound/heartbeat
|
||||
mid_sounds = list('sound/effects/singlebeat.ogg' = 1)
|
||||
mid_length = 1 SECONDS
|
||||
|
||||
/datum/looping_sound/trapped_machine_beep
|
||||
mid_sounds = list('sound/machines/beep.ogg' = 1)
|
||||
mid_length = 10 SECONDS
|
||||
|
||||
@@ -358,6 +358,12 @@
|
||||
name = "Alternate Sciences Research Center"
|
||||
description = "When you're messing with the occult, who knows what you're going to get?"
|
||||
|
||||
/datum/map_template/ruin/space/meateor
|
||||
id = "meateor"
|
||||
suffix = "meateor.dmm"
|
||||
name = "Meateor"
|
||||
description = "A big chunk of meat floating in space. How did it get there?"
|
||||
|
||||
/datum/map_template/ruin/space/the_faceoff
|
||||
id = "the_faceoff"
|
||||
suffix = "the_faceoff.dmm"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/datum/storage/organ_box
|
||||
|
||||
/datum/storage/organ_box/handle_enter(datum/source, obj/item/arrived)
|
||||
/datum/storage/organ_box/handle_enter(obj/item/storage/organbox/source, obj/item/arrived)
|
||||
. = ..()
|
||||
|
||||
if(!istype(arrived))
|
||||
if(!istype(arrived) || !istype(source) || !source.coolant_to_spend())
|
||||
return
|
||||
|
||||
arrived.freeze()
|
||||
|
||||
@@ -550,6 +550,7 @@
|
||||
for(var/i in 1 to 5)
|
||||
new /obj/item/reagent_containers/pill/paxpsych(src)
|
||||
|
||||
/// A box which takes in coolant and uses it to preserve organs and body parts
|
||||
/obj/item/storage/organbox
|
||||
name = "organ transport box"
|
||||
desc = "An advanced box with an cooling mechanism that uses cryostylane or other cold reagents to keep the organs or bodyparts inside preserved."
|
||||
@@ -579,56 +580,39 @@
|
||||
|
||||
/obj/item/storage/organbox/process(delta_time)
|
||||
///if there is enough coolant var
|
||||
var/cool = FALSE
|
||||
var/amount = min(reagents.get_reagent_amount(/datum/reagent/cryostylane), 0.05 * delta_time)
|
||||
if(amount > 0)
|
||||
reagents.remove_reagent(/datum/reagent/cryostylane, amount)
|
||||
cool = TRUE
|
||||
else
|
||||
amount = min(reagents.get_reagent_amount(/datum/reagent/consumable/ice), 0.1 * delta_time)
|
||||
if(amount > 0)
|
||||
reagents.remove_reagent(/datum/reagent/consumable/ice, amount)
|
||||
cool = TRUE
|
||||
if(!cooling && cool)
|
||||
cooling = TRUE
|
||||
update_appearance()
|
||||
for(var/C in contents)
|
||||
freeze_contents(C)
|
||||
var/using_coolant = coolant_to_spend()
|
||||
if (isnull(using_coolant))
|
||||
if (cooling)
|
||||
cooling = FALSE
|
||||
update_appearance()
|
||||
for(var/obj/stored in contents)
|
||||
stored.unfreeze()
|
||||
return
|
||||
if(cooling && !cool)
|
||||
cooling = FALSE
|
||||
update_appearance()
|
||||
for(var/C in contents)
|
||||
unfreeze_contents(C)
|
||||
|
||||
var/amount_used = 0.05 * delta_time
|
||||
if (using_coolant != /datum/reagent/cryostylane)
|
||||
amount_used *= 2
|
||||
reagents.remove_reagent(using_coolant, amount_used)
|
||||
|
||||
if(cooling)
|
||||
return
|
||||
cooling = TRUE
|
||||
update_appearance()
|
||||
for(var/obj/stored in contents)
|
||||
stored.freeze()
|
||||
|
||||
/// Returns which coolant we are about to use, or null if there isn't any
|
||||
/obj/item/storage/organbox/proc/coolant_to_spend()
|
||||
if (reagents.get_reagent_amount(/datum/reagent/cryostylane))
|
||||
return /datum/reagent/cryostylane
|
||||
if (reagents.get_reagent_amount(/datum/reagent/consumable/ice))
|
||||
return /datum/reagent/consumable/ice
|
||||
return null
|
||||
|
||||
/obj/item/storage/organbox/update_icon_state()
|
||||
icon_state = "[base_icon_state][cooling ? "-working" : null]"
|
||||
return ..()
|
||||
|
||||
///freezes the organ and loops bodyparts like heads
|
||||
/obj/item/storage/organbox/proc/freeze_contents(datum/source, obj/item/I)
|
||||
SIGNAL_HANDLER
|
||||
if(isinternalorgan(I))
|
||||
var/obj/item/organ/internal/int_organ = I
|
||||
int_organ.organ_flags |= ORGAN_FROZEN
|
||||
return
|
||||
if(isbodypart(I))
|
||||
var/obj/item/bodypart/B = I
|
||||
for(var/obj/item/organ/internal/int_organ in B.contents)
|
||||
int_organ.organ_flags |= ORGAN_FROZEN
|
||||
|
||||
///unfreezes the organ and loops bodyparts like heads
|
||||
/obj/item/storage/organbox/proc/unfreeze_contents(datum/source, obj/item/I)
|
||||
SIGNAL_HANDLER
|
||||
if(isinternalorgan(I))
|
||||
var/obj/item/organ/internal/int_organ = I
|
||||
int_organ.organ_flags &= ~ORGAN_FROZEN
|
||||
return
|
||||
if(isbodypart(I))
|
||||
var/obj/item/bodypart/B = I
|
||||
for(var/obj/item/organ/internal/int_organ in B.contents)
|
||||
int_organ.organ_flags &= ~ORGAN_FROZEN
|
||||
|
||||
/obj/item/storage/organbox/attackby(obj/item/I, mob/user, params)
|
||||
if(is_reagent_container(I) && I.is_open_container())
|
||||
var/obj/item/reagent_containers/RC = I
|
||||
@@ -659,3 +643,10 @@
|
||||
user.adjust_bodytemperature(-300)
|
||||
user.apply_status_effect(/datum/status_effect/freon)
|
||||
return FIRELOSS
|
||||
|
||||
/// A subtype of organ storage box which starts with a full coolant tank
|
||||
/obj/item/storage/organbox/preloaded
|
||||
|
||||
/obj/item/storage/organbox/preloaded/Initialize(mapload)
|
||||
. = ..()
|
||||
reagents.add_reagent(/datum/reagent/cryostylane, reagents.maximum_volume)
|
||||
|
||||
@@ -135,3 +135,16 @@
|
||||
smoothing_flags = NONE
|
||||
canSmoothWith = null
|
||||
smoothing_groups = null
|
||||
|
||||
/turf/closed/wall/material/meat
|
||||
name = "living wall"
|
||||
baseturfs = /turf/open/floor/material/meat
|
||||
girder_type = null
|
||||
material_flags = MATERIAL_EFFECTS | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
|
||||
|
||||
/turf/closed/wall/material/meat/Initialize(mapload)
|
||||
. = ..()
|
||||
set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat) = MINERAL_MATERIAL_AMOUNT))
|
||||
|
||||
/turf/closed/wall/material/meat/airless
|
||||
baseturfs = /turf/open/floor/material/meat/airless
|
||||
|
||||
@@ -323,7 +323,19 @@
|
||||
/turf/open/floor/cult/airless
|
||||
initial_gas_mix = AIRLESS_ATMOS
|
||||
|
||||
/// TGMC EMBLEM ///
|
||||
/turf/open/floor/material/meat
|
||||
name = "living floor"
|
||||
icon_state = "grey"
|
||||
baseturfs = /turf/open/misc/asteroid
|
||||
material_flags = MATERIAL_EFFECTS | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
|
||||
|
||||
/turf/open/floor/material/meat/Initialize(mapload)
|
||||
. = ..()
|
||||
set_custom_materials(list(GET_MATERIAL_REF(/datum/material/meat) = MINERAL_MATERIAL_AMOUNT))
|
||||
|
||||
/turf/open/floor/material/meat/airless
|
||||
initial_gas_mix = AIRLESS_ATMOS
|
||||
baseturfs = /turf/open/misc/asteroid/airless
|
||||
|
||||
/turf/open/floor/iron/tgmcemblem
|
||||
name = "TGMC Emblem"
|
||||
|
||||
@@ -42,3 +42,11 @@
|
||||
name = "Piercing Eldritch Medallion"
|
||||
desc = "A strange medallion. Peering through the crystalline surface, the light refracts into new and terrifying spectrums of color. You see yourself, reflected off cascading mirrors, warped into impossible shapes."
|
||||
heretic_only_trait = TRAIT_XRAY_VISION
|
||||
|
||||
// Cosmetic-only version
|
||||
/obj/item/clothing/neck/fake_heretic_amulet
|
||||
name = "religious icon"
|
||||
desc = "A strange medallion, which makes its wearer look like they're part of some cult."
|
||||
icon = 'icons/obj/eldritch.dmi'
|
||||
icon_state = "eye_medalion"
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
/area/ruin/space/meateor
|
||||
name = "\improper Organic Asteroid"
|
||||
sound_environment = SOUND_AREA_SMALL_ENCLOSED
|
||||
|
||||
/obj/item/paper/fluff/ruins/meateor/letter
|
||||
name = "letter"
|
||||
default_raw_text = {"We offer our sincerest congratulations, to be chosen to take this journey is an honour and privilege afforded to few.
|
||||
<br> While you are walking in the footsteps of the divine, don't forget about the rest of us back at the farm!
|
||||
<br> <This letter has been signed by 15 people.>"}
|
||||
|
||||
/// Tiger cultist corpse but with an exit wound
|
||||
/obj/effect/mob_spawn/corpse/human/tigercultist/perforated
|
||||
|
||||
/obj/effect/mob_spawn/corpse/human/tigercultist/perforated/special(mob/living/carbon/human/spawned_human)
|
||||
. = ..()
|
||||
var/datum/wound/pierce/critical/exit_hole = new()
|
||||
exit_hole.apply_wound(spawned_human.get_bodypart(BODY_ZONE_CHEST))
|
||||
|
||||
/// A fun drink enjoyed by the tiger cooperative, might corrode your brain if you drink the whole bottle
|
||||
/obj/item/reagent_containers/cup/glass/bottle/ritual_wine
|
||||
name = "ritual wine"
|
||||
desc = "A bottle filled with liquids of a dubious nature, often enjoyed by members of the Tiger Cooperative."
|
||||
icon_state = "winebottle"
|
||||
list_reagents = list(
|
||||
/datum/reagent/drug/mushroomhallucinogen = 25,
|
||||
/datum/reagent/consumable/ethanol/ritual_wine = 25,
|
||||
/datum/reagent/medicine/changelinghaste = 20, // This metabolises very fast
|
||||
/datum/reagent/drug/bath_salts = 5,
|
||||
/datum/reagent/blood = 5,
|
||||
/datum/reagent/toxin/leadacetate = 5,
|
||||
/datum/reagent/medicine/omnizine = 5,
|
||||
/datum/reagent/medicine/c2/penthrite = 5,
|
||||
/datum/reagent/consumable/vinegar = 5,
|
||||
)
|
||||
drink_type = NONE
|
||||
age_restricted = FALSE
|
||||
|
||||
/// Abstract holder object for shared behaviour
|
||||
/obj/structure/meateor_fluff
|
||||
icon = 'icons/mob/simple/meteor_heart.dmi'
|
||||
anchored = TRUE
|
||||
|
||||
/obj/structure/meateor_fluff/play_attack_sound(damage_amount, damage_type, damage_flag)
|
||||
switch(damage_type)
|
||||
if(BRUTE)
|
||||
if(damage_amount)
|
||||
playsound(loc, 'sound/effects/attackblob.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
|
||||
else
|
||||
playsound(loc, 'sound/effects/meatslap.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
|
||||
if(BURN)
|
||||
playsound(loc, 'sound/effects/wounds/sizzle1.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
|
||||
|
||||
/// A sort of loot box for organs, cut it open and find a prize
|
||||
/obj/structure/meateor_fluff/flesh_pod
|
||||
name = "flesh pod"
|
||||
desc = "A quivering pod of living meat. Something is pulsing inside."
|
||||
icon_state = "flesh_pod"
|
||||
max_integrity = 120
|
||||
density = TRUE
|
||||
/// Typepath of the organ to spawn when this is destroyed
|
||||
var/stored_organ
|
||||
/// Types of organ we can spawn
|
||||
var/static/list/allowed_organs = list(
|
||||
/obj/item/organ/internal/heart/gland/egg = 7,
|
||||
/obj/item/organ/internal/heart/gland/plasma = 7,
|
||||
/obj/item/organ/internal/alien/plasmavessel = 5,
|
||||
/obj/item/organ/internal/heart/gland/chem = 5,
|
||||
/obj/item/organ/internal/heart/gland/mindshock = 5,
|
||||
/obj/item/organ/internal/heart/gland/spiderman = 5,
|
||||
/obj/item/organ/internal/heart/gland/transform = 5,
|
||||
/obj/item/organ/internal/heart/gland/slime = 4,
|
||||
/obj/item/organ/internal/heart/gland/trauma = 4,
|
||||
/obj/item/organ/internal/heart/carp = 3,
|
||||
/obj/item/organ/internal/heart/rat = 3,
|
||||
/obj/item/organ/internal/heart/gland/electric = 3,
|
||||
/obj/item/organ/internal/monster_core/brimdust_sac = 3,
|
||||
/obj/item/organ/internal/monster_core/regenerative_core = 3,
|
||||
/obj/item/organ/internal/monster_core/rush_gland = 3,
|
||||
/obj/item/organ/internal/tongue/carp = 3,
|
||||
/obj/item/organ/internal/alien/acid = 2,
|
||||
/obj/item/organ/internal/alien/resinspinner = 2,
|
||||
/obj/item/organ/internal/eyes/night_vision/goliath = 2,
|
||||
/obj/item/organ/internal/eyes/night_vision/rat = 2,
|
||||
/obj/item/organ/internal/heart/gland/ventcrawling = 1,
|
||||
)
|
||||
|
||||
/obj/structure/meateor_fluff/flesh_pod/Initialize(mapload)
|
||||
. = ..()
|
||||
stored_organ = pick_weight(allowed_organs)
|
||||
|
||||
/obj/structure/meateor_fluff/flesh_pod/attackby(obj/item/attacking_item, mob/user, params)
|
||||
if (attacking_item.sharpness & SHARP_EDGED)
|
||||
cut_open(user)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/// Cut the pod open and destroy it
|
||||
/obj/structure/meateor_fluff/flesh_pod/proc/cut_open(mob/user)
|
||||
balloon_alert(user, "slicing...")
|
||||
if (!do_after(user, 3 SECONDS, target = src))
|
||||
return
|
||||
take_damage(max_integrity)
|
||||
|
||||
/obj/structure/meateor_fluff/flesh_pod/atom_destruction(damage_flag)
|
||||
new stored_organ(loc)
|
||||
new /obj/effect/decal/cleanable/blood(loc)
|
||||
new /obj/structure/meateor_fluff/flesh_pod_open(loc)
|
||||
playsound(loc, 'sound/effects/wounds/blood3.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
|
||||
return ..()
|
||||
|
||||
/obj/structure/meateor_fluff/flesh_pod_open
|
||||
name = "flesh pod"
|
||||
desc = "A pod of living meat, this one has been hollowed out."
|
||||
icon_state = "flesh_pod_open"
|
||||
max_integrity = 60
|
||||
|
||||
/obj/structure/meateor_fluff/flesh_pod_open/atom_destruction(damage_flag)
|
||||
new /obj/effect/gibspawner/human(loc)
|
||||
return ..()
|
||||
|
||||
/// Decorative fluff egg object
|
||||
/obj/structure/meateor_fluff/abandoned_headcrab_egg
|
||||
name = "meaty eggs"
|
||||
desc = "A mass of fleshy, egg-shaped nodes."
|
||||
icon_state = "eggs"
|
||||
max_integrity = 15
|
||||
|
||||
/obj/structure/meateor_fluff/abandoned_headcrab_egg/atom_destruction(damage_flag)
|
||||
new /obj/effect/decal/cleanable/xenoblood(loc)
|
||||
playsound(loc, 'sound/effects/gib_step.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
|
||||
return ..()
|
||||
@@ -0,0 +1,112 @@
|
||||
/// An ability which makes spikes come out of the ground towards your target
|
||||
/datum/action/cooldown/chasing_spikes
|
||||
name = "impaling tendril"
|
||||
desc = "Send a spiked subterranean tendril chasing after your target."
|
||||
button_icon = 'icons/mob/simple/meteor_heart.dmi'
|
||||
button_icon_state = "spike"
|
||||
cooldown_time = 10 SECONDS
|
||||
click_to_activate = TRUE
|
||||
/// Lazy list of references to spike trails
|
||||
var/list/active_chasers
|
||||
|
||||
/datum/action/cooldown/chasing_spikes/Activate(atom/target)
|
||||
. = ..()
|
||||
playsound(owner, 'sound/magic/demon_attack1.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
|
||||
var/obj/effect/temp_visual/spike_chaser/chaser = new(get_turf(owner), target)
|
||||
LAZYADD(active_chasers, WEAKREF(chaser))
|
||||
RegisterSignal(chaser, COMSIG_PARENT_QDELETING, PROC_REF(on_chaser_destroyed))
|
||||
|
||||
/// Remove a spike trail from our list of active trails
|
||||
/datum/action/cooldown/chasing_spikes/proc/on_chaser_destroyed(atom/chaser)
|
||||
SIGNAL_HANDLER
|
||||
LAZYREMOVE(active_chasers, WEAKREF(chaser))
|
||||
|
||||
// Clean up after ourselves
|
||||
/datum/action/cooldown/chasing_spikes/Remove(mob/removed_from)
|
||||
QDEL_LIST(active_chasers)
|
||||
return ..()
|
||||
|
||||
/// An invisible effect which chases a target, spawning spikes every so often.
|
||||
/obj/effect/temp_visual/spike_chaser
|
||||
name = "spike chaser"
|
||||
desc = "An invisible effect, how did you examine this?"
|
||||
icon = 'icons/mob/silicon/cameramob.dmi'
|
||||
icon_state = "marker"
|
||||
duration = 15 SECONDS
|
||||
invisibility = INVISIBILITY_ABSTRACT
|
||||
/// Speed at which we chase target
|
||||
var/move_speed = 3
|
||||
/// What are we chasing?
|
||||
var/datum/weakref/target
|
||||
/// Handles chasing the target
|
||||
var/datum/move_loop/movement
|
||||
|
||||
/obj/effect/temp_visual/spike_chaser/Initialize(mapload, atom/target)
|
||||
. = ..()
|
||||
if (!target)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
|
||||
AddElement(/datum/element/floor_loving)
|
||||
AddComponent(/datum/component/spawner, spawn_types = list(/obj/effect/temp_visual/emerging_ground_spike), spawn_time = 0.5 SECONDS)
|
||||
src.target = WEAKREF(target)
|
||||
movement = SSmove_manager.move_towards(src, chasing = target, delay = move_speed, home = TRUE, timeout = duration, flags = MOVEMENT_LOOP_START_FAST)
|
||||
|
||||
RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(on_target_invalid))
|
||||
if (isliving(target))
|
||||
RegisterSignal(target, COMSIG_LIVING_DEATH, PROC_REF(on_target_invalid))
|
||||
|
||||
/// Destroy ourselves if the target is no longer valid
|
||||
/obj/effect/temp_visual/spike_chaser/proc/on_target_invalid()
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
/obj/effect/temp_visual/spike_chaser/Destroy()
|
||||
QDEL_NULL(movement)
|
||||
return ..()
|
||||
|
||||
/// A spike comes out of the ground, dealing damage after a short delay
|
||||
/obj/effect/temp_visual/emerging_ground_spike
|
||||
name = "bone spike"
|
||||
desc = "A sharp spur of bone erupting from the ground!"
|
||||
icon = 'icons/mob/simple/meteor_heart.dmi'
|
||||
icon_state = "spike"
|
||||
duration = 1 SECONDS
|
||||
/// Time until we hurt people stood on us
|
||||
var/harm_delay = 0.3 SECONDS
|
||||
/// Amount by which to vary our position on spawn
|
||||
var/position_variance = 8
|
||||
/// Damage to deal on impale
|
||||
var/impale_damage = 15
|
||||
/// Typecache of types of mobs not to damage
|
||||
var/list/damage_blacklist_typecache = list(
|
||||
/mob/living/basic/meteor_heart,
|
||||
)
|
||||
/// Weighted list of body zones to target while standing
|
||||
var/static/list/standing_damage_zones = list(
|
||||
BODY_ZONE_CHEST = 1,
|
||||
BODY_ZONE_R_LEG = 3,
|
||||
BODY_ZONE_L_LEG = 3,
|
||||
)
|
||||
|
||||
/obj/effect/temp_visual/emerging_ground_spike/Initialize(mapload)
|
||||
. = ..()
|
||||
damage_blacklist_typecache = typecacheof(damage_blacklist_typecache)
|
||||
pixel_x += rand(-position_variance, position_variance)
|
||||
pixel_y += rand(-position_variance, position_variance)
|
||||
addtimer(CALLBACK(src, PROC_REF(impale)), harm_delay, TIMER_DELETE_ME)
|
||||
|
||||
/// Stab people who are stood on us after a delay in the shins
|
||||
/obj/effect/temp_visual/emerging_ground_spike/proc/impale()
|
||||
if (!isturf(loc))
|
||||
return
|
||||
var/hit_someone = FALSE
|
||||
for(var/mob/living/victim in loc)
|
||||
if (is_type_in_typecache(victim, damage_blacklist_typecache))
|
||||
continue
|
||||
hit_someone = TRUE
|
||||
var/target_zone = victim.resting ? BODY_ZONE_CHEST : pick_weight(standing_damage_zones)
|
||||
victim.apply_damage(impale_damage, damagetype = BRUTE, def_zone = target_zone, sharpness = SHARP_POINTY)
|
||||
if (hit_someone)
|
||||
playsound(src, 'sound/weapons/slice.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
|
||||
else
|
||||
playsound(src, 'sound/misc/splort.ogg', vol = 25, vary = TRUE, pressure_affected = FALSE)
|
||||
@@ -0,0 +1,36 @@
|
||||
#define EYEBALL_BLINK_INTERVAL_MIN 10 SECONDS
|
||||
#define EYEBALL_BLINK_INTERVAL_MAX 30 SECONDS
|
||||
|
||||
/// List of all the meteor eyeballs so we can gib them upon meteor death
|
||||
GLOBAL_LIST_EMPTY(meteor_eyeballs)
|
||||
|
||||
/// Basically just an organic floor light
|
||||
/obj/structure/meateor_fluff/eyeball
|
||||
name = "beady eye"
|
||||
desc = "An eyeball growing out of the ground, gross."
|
||||
icon_state = "eyeball"
|
||||
max_integrity = 15
|
||||
layer = LOW_OBJ_LAYER
|
||||
plane = FLOOR_PLANE
|
||||
|
||||
/obj/structure/meateor_fluff/eyeball/Initialize(mapload)
|
||||
. = ..()
|
||||
GLOB.meteor_eyeballs += src
|
||||
set_light(l_range = 4, l_color = COLOR_VERY_SOFT_YELLOW)
|
||||
blink()
|
||||
|
||||
/// Play a blinking animation and queue it again
|
||||
/obj/structure/meateor_fluff/eyeball/proc/blink()
|
||||
flick("eyeball_blink", src)
|
||||
addtimer(CALLBACK(src, PROC_REF(blink)), rand(EYEBALL_BLINK_INTERVAL_MIN, EYEBALL_BLINK_INTERVAL_MAX), TIMER_DELETE_ME)
|
||||
|
||||
/obj/structure/meateor_fluff/eyeball/atom_destruction(damage_flag)
|
||||
new /obj/effect/gibspawner/generic(loc)
|
||||
return ..()
|
||||
|
||||
/obj/structure/meateor_fluff/eyeball/Destroy()
|
||||
GLOB.meteor_eyeballs -= src
|
||||
return ..()
|
||||
|
||||
#undef EYEBALL_BLINK_INTERVAL_MIN
|
||||
#undef EYEBALL_BLINK_INTERVAL_MAX
|
||||
@@ -0,0 +1,131 @@
|
||||
#define HEARTBEAT_NORMAL (1.2 SECONDS)
|
||||
#define HEARTBEAT_FAST (0.6 SECONDS)
|
||||
#define HEARTBEAT_FRANTIC (0.4 SECONDS)
|
||||
|
||||
/mob/living/basic/meteor_heart
|
||||
name = "meteor heart"
|
||||
desc = "A pulsing lump of flesh and bone growing directly out of the ground."
|
||||
icon = 'icons/mob/simple/meteor_heart.dmi'
|
||||
icon_state = "heart"
|
||||
icon_living = "heart"
|
||||
mob_biotypes = MOB_ORGANIC
|
||||
basic_mob_flags = DEL_ON_DEATH
|
||||
mob_size = MOB_SIZE_HUGE
|
||||
health = 600 // 15 PKA shots
|
||||
maxHealth = 600
|
||||
pressure_resistance = 200
|
||||
response_help_continuous = "pets"
|
||||
response_help_simple = "pet"
|
||||
response_disarm_continuous = "gently pushes"
|
||||
response_disarm_simple = "gently push"
|
||||
faction = list()
|
||||
ai_controller = /datum/ai_controller/basic_controller/meteor_heart
|
||||
habitable_atmos = list("min_oxy" = 0, "max_oxy" = 0, "min_plas" = 0, "max_plas" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||
minimum_survivable_temperature = 0
|
||||
maximum_survivable_temperature = 1500
|
||||
combat_mode = TRUE
|
||||
move_resist = INFINITY // This mob IS the floor
|
||||
/// Action which sends a line of spikes chasing a player
|
||||
var/datum/action/cooldown/chasing_spikes/spikes
|
||||
/// Action which summons areas the player can't stand in
|
||||
var/datum/action/cooldown/spine_traps/traps
|
||||
/// Looping heartbeat sound
|
||||
var/datum/looping_sound/heartbeat/soundloop
|
||||
|
||||
/mob/living/basic/meteor_heart/Initialize(mapload)
|
||||
. = ..()
|
||||
ADD_TRAIT(src, TRAIT_IMMOBILIZED, INNATE_TRAIT)
|
||||
AddElement(/datum/element/death_drops, list(/obj/effect/temp_visual/meteor_heart_death))
|
||||
AddElement(/datum/element/relay_attackers)
|
||||
|
||||
spikes = new(src)
|
||||
spikes.Grant(src)
|
||||
ai_controller.blackboard[BB_METEOR_HEART_GROUND_SPIKES] = WEAKREF(spikes)
|
||||
|
||||
traps = new(src)
|
||||
traps.Grant(src)
|
||||
ai_controller.blackboard[BB_METEOR_HEART_SPINE_TRAPS] = WEAKREF(traps)
|
||||
|
||||
ai_controller.set_ai_status(AI_STATUS_OFF)
|
||||
|
||||
RegisterSignal(src, COMSIG_MOB_ABILITY_FINISHED, PROC_REF(used_ability))
|
||||
RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(aggro))
|
||||
|
||||
for (var/obj/structure/meateor_fluff/body_part in view(5, src))
|
||||
RegisterSignal(body_part, COMSIG_ATOM_DESTRUCTION, PROC_REF(aggro))
|
||||
|
||||
soundloop = new(src, start_immediately = FALSE)
|
||||
soundloop.mid_length = HEARTBEAT_NORMAL
|
||||
soundloop.pressure_affected = FALSE
|
||||
soundloop.start()
|
||||
|
||||
/// Called when we get mad at something, either for attacking us or attacking the nearby area
|
||||
/mob/living/basic/meteor_heart/proc/aggro()
|
||||
if (ai_controller.ai_status == AI_STATUS_ON)
|
||||
return
|
||||
ai_controller.reset_ai_status()
|
||||
if (!ai_controller.ai_status == AI_STATUS_ON)
|
||||
return
|
||||
icon_state = "heart_aggro"
|
||||
soundloop.set_mid_length(HEARTBEAT_FAST)
|
||||
|
||||
/// Called when we stop being mad
|
||||
/mob/living/basic/meteor_heart/proc/deaggro()
|
||||
ai_controller.set_ai_status(AI_STATUS_OFF)
|
||||
icon_state = "heart"
|
||||
soundloop.set_mid_length(HEARTBEAT_NORMAL)
|
||||
|
||||
/// Animate when using certain abilities
|
||||
/mob/living/basic/meteor_heart/proc/used_ability(mob/living/owner, datum/action/cooldown/ability)
|
||||
SIGNAL_HANDLER
|
||||
if (ability != spikes)
|
||||
return
|
||||
Shake(1, 0, 1.5 SECONDS)
|
||||
|
||||
/mob/living/basic/meteor_heart/Destroy()
|
||||
QDEL_NULL(spikes)
|
||||
QDEL_NULL(traps)
|
||||
QDEL_NULL(soundloop)
|
||||
return ..()
|
||||
|
||||
/// Dramatic death animation for the meteor heart mob
|
||||
/obj/effect/temp_visual/meteor_heart_death
|
||||
name = "meteor heart"
|
||||
icon = 'icons/mob/simple/meteor_heart.dmi'
|
||||
icon_state = "heart_dying"
|
||||
desc = "You've killed this innocent asteroid, I hope you feel happy."
|
||||
duration = 3 SECONDS
|
||||
/// Looping heartbeat sound
|
||||
var/datum/looping_sound/heartbeat/soundloop
|
||||
|
||||
/obj/effect/temp_visual/meteor_heart_death/Initialize(mapload)
|
||||
. = ..()
|
||||
playsound(src, 'sound/magic/demon_dies.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
|
||||
Shake(2, 0, 3 SECONDS)
|
||||
addtimer(CALLBACK(src, PROC_REF(gib)), duration - 1, TIMER_DELETE_ME)
|
||||
soundloop = new(src, start_immediately = FALSE)
|
||||
soundloop.mid_length = HEARTBEAT_FRANTIC
|
||||
soundloop.pressure_affected = FALSE
|
||||
soundloop.start()
|
||||
|
||||
/obj/effect/temp_visual/meteor_heart_death/Destroy()
|
||||
QDEL_NULL(soundloop)
|
||||
return ..()
|
||||
|
||||
/// Make this place a mess
|
||||
/obj/effect/temp_visual/meteor_heart_death/proc/gib()
|
||||
playsound(loc, 'sound/effects/attackblob.ogg', vol = 100, vary = TRUE, pressure_affected = FALSE)
|
||||
var/turf/my_turf = get_turf(src)
|
||||
new /obj/effect/gibspawner/human(my_turf)
|
||||
for (var/obj/structure/eyeball as anything in GLOB.meteor_eyeballs)
|
||||
if (eyeball.z != src.z)
|
||||
continue
|
||||
addtimer(CALLBACK(eyeball, TYPE_PROC_REF(/atom/, take_damage), eyeball.max_integrity), rand(0.5 SECONDS, 2 SECONDS)) // pop!
|
||||
for (var/mob/murderer in range(10, src))
|
||||
if (!murderer.client || isspaceturf(get_turf(murderer)))
|
||||
continue
|
||||
shake_camera(murderer, duration = 2 SECONDS, strength = 2)
|
||||
|
||||
#undef HEARTBEAT_NORMAL
|
||||
#undef HEARTBEAT_FAST
|
||||
#undef HEARTBEAT_FRANTIC
|
||||
@@ -0,0 +1,32 @@
|
||||
/// A spellcasting AI which does not move
|
||||
/datum/ai_controller/basic_controller/meteor_heart
|
||||
blackboard = list(
|
||||
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic(),
|
||||
BB_TARGETLESS_TIME = 0,
|
||||
)
|
||||
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/ground_spikes,
|
||||
/datum/ai_planning_subtree/use_mob_ability/spine_traps,
|
||||
/datum/ai_planning_subtree/sleep_with_no_target/meteor_heart,
|
||||
)
|
||||
|
||||
/datum/ai_planning_subtree/targeted_mob_ability/ground_spikes
|
||||
ability_key = BB_METEOR_HEART_GROUND_SPIKES
|
||||
finish_planning = FALSE
|
||||
|
||||
/datum/ai_planning_subtree/use_mob_ability/spine_traps
|
||||
ability_key = BB_METEOR_HEART_SPINE_TRAPS
|
||||
|
||||
/// After enough time with no target, deaggro and change animation state
|
||||
/datum/ai_planning_subtree/sleep_with_no_target/meteor_heart
|
||||
sleep_behaviour = /datum/ai_behavior/sleep_after_targetless_time/meteor_heart
|
||||
|
||||
/datum/ai_behavior/sleep_after_targetless_time/meteor_heart
|
||||
|
||||
/datum/ai_behavior/sleep_after_targetless_time/meteor_heart/enter_sleep(datum/ai_controller/controller)
|
||||
var/mob/living/basic/meteor_heart/heart = controller.pawn
|
||||
if (!istype(heart))
|
||||
return ..()
|
||||
heart.deaggro()
|
||||
@@ -0,0 +1,97 @@
|
||||
/// Marks several areas with thrusting spines which damage and slow people
|
||||
/datum/action/cooldown/spine_traps
|
||||
name = "thrusting spines"
|
||||
desc = "Mark several nearby areas with thrusting spines, which will spring up when disturbed."
|
||||
button_icon = 'icons/mob/simple/meteor_heart.dmi'
|
||||
button_icon_state = "spikes_stabbing"
|
||||
cooldown_time = 15 SECONDS
|
||||
/// Create zones at most this far away
|
||||
var/range = 3
|
||||
/// Don't create zones within this radius
|
||||
var/min_range = 2
|
||||
/// Number of zones to place
|
||||
var/zones_to_create = 3
|
||||
|
||||
/datum/action/cooldown/spine_traps/Activate(atom/target)
|
||||
. = ..()
|
||||
|
||||
playsound(owner, 'sound/magic/demon_consume.ogg', vol = 100, falloff_exponent = 2, vary = TRUE, pressure_affected = FALSE)
|
||||
var/list/valid_turfs = list()
|
||||
var/turf/our_turf = get_turf(owner)
|
||||
for (var/turf/zone_turf in orange(range, our_turf))
|
||||
if (!is_valid_turf(zone_turf) || get_dist(zone_turf, our_turf) < min_range)
|
||||
continue
|
||||
valid_turfs += zone_turf
|
||||
|
||||
var/created = 0
|
||||
while(length(valid_turfs) && created < zones_to_create)
|
||||
var/turf/place_turf = pick_n_take(valid_turfs)
|
||||
var/list/covered_turfs = place_zone(place_turf)
|
||||
valid_turfs -= covered_turfs
|
||||
created++
|
||||
|
||||
/// Returns true if we can place a trap at the specified location
|
||||
/datum/action/cooldown/spine_traps/proc/is_valid_turf(turf/target_turf)
|
||||
return !target_turf.is_blocked_turf(exclude_mobs = TRUE) && !isspaceturf(target_turf) && !isopenspaceturf(target_turf)
|
||||
|
||||
/// Places a 3x3 area of spike traps around a central provided point, returns the list of now occupied turfs
|
||||
/datum/action/cooldown/spine_traps/proc/place_zone(turf/target_turf)
|
||||
var/list/used_turfs = list()
|
||||
for (var/turf/zone_turf in range(1, target_turf))
|
||||
if (!is_valid_turf(zone_turf))
|
||||
continue
|
||||
new /obj/effect/temp_visual/thrusting_spines(zone_turf)
|
||||
used_turfs += zone_turf
|
||||
return used_turfs
|
||||
|
||||
/obj/effect/temp_visual/thrusting_spines
|
||||
icon = 'icons/mob/simple/meteor_heart.dmi'
|
||||
icon_state = "spikes_idle"
|
||||
desc = "Sharp spines lying in wait in the ground, you probably don't want to walk on those."
|
||||
duration = 10 SECONDS
|
||||
/// If this will trigger a trap when entered
|
||||
var/active = FALSE
|
||||
/// Damage to deal on activation
|
||||
var/impale_damage = 10
|
||||
/// Time between activations
|
||||
COOLDOWN_DECLARE(thrust_delay)
|
||||
/// Weighted list of body zones to target while standing
|
||||
var/static/list/standing_damage_zones = list(
|
||||
BODY_ZONE_CHEST = 1,
|
||||
BODY_ZONE_R_LEG = 3,
|
||||
BODY_ZONE_L_LEG = 3,
|
||||
)
|
||||
|
||||
/obj/effect/temp_visual/thrusting_spines/Initialize(mapload)
|
||||
. = ..()
|
||||
flick("spikes_emerge", src)
|
||||
addtimer(CALLBACK(src, PROC_REF(ready)), 1 SECONDS, TIMER_DELETE_ME)
|
||||
addtimer(CALLBACK(src, PROC_REF(retract)), duration - (0.5 SECONDS), TIMER_DELETE_ME)
|
||||
|
||||
var/static/list/loc_connections = list(
|
||||
COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
|
||||
)
|
||||
AddElement(/datum/element/connect_loc, loc_connections)
|
||||
|
||||
/// Called when we're ready to start impaling people
|
||||
/obj/effect/temp_visual/thrusting_spines/proc/ready()
|
||||
active = TRUE
|
||||
|
||||
/// Called when it is time to stop impaling people
|
||||
/obj/effect/temp_visual/thrusting_spines/proc/retract()
|
||||
active = FALSE
|
||||
icon_state = "spikes_submerge"
|
||||
|
||||
/// Called when something enters our turf, if it is a non-flying mob then give it a stab
|
||||
/obj/effect/temp_visual/thrusting_spines/proc/on_entered(datum/source, atom/movable/arrived)
|
||||
if (!active || !isliving(arrived) || (arrived.movement_type & (FLYING | FLOATING)))
|
||||
return
|
||||
if (!COOLDOWN_FINISHED(src, thrust_delay))
|
||||
return
|
||||
|
||||
COOLDOWN_START(src, thrust_delay, 0.7 SECONDS)
|
||||
playsound(src, 'sound/weapons/pierce.ogg', vol = 50, vary = TRUE, pressure_affected = FALSE)
|
||||
var/mob/living/victim = arrived
|
||||
flick("spikes_stabbing", src)
|
||||
var/target_zone = victim.resting ? BODY_ZONE_CHEST : pick_weight(standing_damage_zones)
|
||||
victim.apply_damage(impale_damage, damagetype = BRUTE, def_zone = target_zone, sharpness = SHARP_POINTY)
|
||||
@@ -21,8 +21,10 @@
|
||||
obj_damage = 0
|
||||
environment_smash = ENVIRONMENT_SMASH_NONE
|
||||
speak_emote = list("squeaks")
|
||||
/// The mind to transfer to our egg when it hatches
|
||||
var/datum/mind/origin
|
||||
var/egg_lain = 0
|
||||
/// Set to true once we've implanted our egg
|
||||
var/egg_lain = FALSE
|
||||
|
||||
/mob/living/simple_animal/hostile/headcrab/Initialize(mapload)
|
||||
. = ..()
|
||||
@@ -39,20 +41,21 @@
|
||||
I.forceMove(egg)
|
||||
visible_message(span_warning("[src] plants something in [victim]'s flesh!"), \
|
||||
span_danger("We inject our egg into [victim]'s body!"))
|
||||
egg_lain = 1
|
||||
egg_lain = TRUE
|
||||
|
||||
/mob/living/simple_animal/hostile/headcrab/AttackingTarget()
|
||||
. = ..()
|
||||
if(. && !egg_lain && iscarbon(target) && !ismonkey(target))
|
||||
// Changeling egg can survive in aliens!
|
||||
var/mob/living/carbon/C = target
|
||||
if(C.stat == DEAD)
|
||||
if(HAS_TRAIT(C, TRAIT_XENO_HOST))
|
||||
to_chat(src, span_userdanger("A foreign presence repels us from this body. Perhaps we should try to infest another?"))
|
||||
return
|
||||
Infect(target)
|
||||
to_chat(src, span_userdanger("With our egg laid, our death approaches rapidly..."))
|
||||
addtimer(CALLBACK(src, PROC_REF(death)), 100)
|
||||
if (!. || egg_lain || !iscarbon(target) || ismonkey(target))
|
||||
return
|
||||
var/mob/living/carbon/victim = target
|
||||
if(victim.stat != DEAD)
|
||||
return
|
||||
if(HAS_TRAIT(victim, TRAIT_XENO_HOST))
|
||||
target.balloon_alert(src, "already pregnant!") // Maybe the worst balloon alert in the codebase
|
||||
return
|
||||
Infect(target)
|
||||
to_chat(src, span_userdanger("With our egg laid, our death approaches rapidly..."))
|
||||
addtimer(CALLBACK(src, PROC_REF(death)), 10 SECONDS)
|
||||
|
||||
/obj/item/organ/internal/body_egg/changeling_egg
|
||||
name = "changeling egg"
|
||||
|
||||
@@ -284,9 +284,9 @@
|
||||
.=..()
|
||||
AddComponent(\
|
||||
/datum/component/spawner,\
|
||||
mob_types = list(/mob/living/simple_animal/hostile/asteroid/hivelord/legion),\
|
||||
spawn_types = list(/mob/living/simple_animal/hostile/asteroid/hivelord/legion),\
|
||||
spawn_time = 20 SECONDS,\
|
||||
max_mobs = 3,\
|
||||
max_spawned = 3,\
|
||||
spawn_text = "peels itself off from",\
|
||||
faction = faction,\
|
||||
)
|
||||
|
||||
@@ -86,6 +86,19 @@
|
||||
id = /obj/item/card/id/advanced/chameleon
|
||||
id_trim = /datum/id_trim/chameleon/operative
|
||||
|
||||
/obj/effect/mob_spawn/corpse/human/tigercultist
|
||||
name = "Tiger Cooperative Cultist"
|
||||
outfit = /datum/outfit/tigercultcorpse
|
||||
|
||||
/datum/outfit/tigercultcorpse
|
||||
name = "Tiger Cooperative Corpse"
|
||||
uniform = /obj/item/clothing/under/rank/civilian/chaplain
|
||||
suit = /obj/item/clothing/suit/hooded/chaplain_hoodie
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
neck = /obj/item/clothing/neck/fake_heretic_amulet
|
||||
head = /obj/item/clothing/head/hooded/chaplain_hood
|
||||
back = /obj/item/storage/backpack/cultpack
|
||||
|
||||
/obj/effect/mob_spawn/corpse/human/pirate
|
||||
name = "Pirate"
|
||||
skin_tone = "caucasian1" //all pirates are white because it's easier that way
|
||||
|
||||
@@ -43,3 +43,13 @@
|
||||
mob_type = /mob/living/simple_animal/hostile/asteroid/goliath/beast
|
||||
icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi'
|
||||
icon_state = "goliath_dead_helper"
|
||||
|
||||
/// Dead headcrab for changeling-themed ruins
|
||||
/obj/effect/mob_spawn/corpse/headcrab
|
||||
mob_type = /mob/living/simple_animal/hostile/headcrab
|
||||
icon = 'icons/mob/simple/animal.dmi'
|
||||
icon_state = "headcrab_dead"
|
||||
|
||||
/obj/effect/mob_spawn/corpse/headcrab/special(mob/living/simple_animal/hostile/headcrab/crab)
|
||||
. = ..()
|
||||
crab.egg_lain = TRUE // Prevents using mad science to become a changeling
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#_maps/RandomRuins/SpaceRuins/hilbertshoteltestingsite.dmm
|
||||
#_maps/RandomRuins/SpaceRuins/intactemptyship.dmm
|
||||
#_maps/RandomRuins/SpaceRuins/listeningstation.dmm
|
||||
#_maps/RandomRuins/SpaceRuins/meateor.dmm
|
||||
#_maps/RandomRuins/SpaceRuins/mechtransport.dmm
|
||||
#_maps/RandomRuins/SpaceRuins/mrow_thats_right
|
||||
#_maps/RandomRuins/SpaceRuins/old_infiltrator.dmm
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
@@ -763,6 +763,7 @@
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_nearest_target_to_flee.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\sleep_with_no_target.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\speech_subtree.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\target_retaliate.dm"
|
||||
#include "code\datums\ai\basic_mobs\basic_subtrees\targeted_mob_ability.dm"
|
||||
@@ -1164,6 +1165,7 @@
|
||||
#include "code\datums\elements\eyestab.dm"
|
||||
#include "code\datums\elements\falling_hazard.dm"
|
||||
#include "code\datums\elements\firestacker.dm"
|
||||
#include "code\datums\elements\floorloving.dm"
|
||||
#include "code\datums\elements\footstep.dm"
|
||||
#include "code\datums\elements\forced_gravity.dm"
|
||||
#include "code\datums\elements\frozen.dm"
|
||||
@@ -3695,6 +3697,7 @@
|
||||
#include "code\modules\mapfluff\ruins\spaceruin_code\hellfactory.dm"
|
||||
#include "code\modules\mapfluff\ruins\spaceruin_code\hilbertshotel.dm"
|
||||
#include "code\modules\mapfluff\ruins\spaceruin_code\listeningstation.dm"
|
||||
#include "code\modules\mapfluff\ruins\spaceruin_code\meateor.dm"
|
||||
#include "code\modules\mapfluff\ruins\spaceruin_code\oldstation.dm"
|
||||
#include "code\modules\mapfluff\ruins\spaceruin_code\originalcontent.dm"
|
||||
#include "code\modules\mapfluff\ruins\spaceruin_code\spacehotel.dm"
|
||||
@@ -3849,6 +3852,11 @@
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\lay_eggs.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\web.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\giant_spider\spider_abilities\wrap.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\meteor_heart\chasing_spikes.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\meteor_heart\meteor_eyeball.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\meteor_heart\meteor_heart.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\meteor_heart\meteor_heart_ai.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\meteor_heart\spine_traps.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\netherworld\blankbody.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\netherworld\creature.dm"
|
||||
#include "code\modules\mob\living\basic\space_fauna\netherworld\migo.dm"
|
||||
|
||||
Reference in New Issue
Block a user