diff --git a/_maps/RandomZLevels/caves.dmm b/_maps/RandomZLevels/caves.dmm index a4c071a17d0..e3025fc7faf 100644 --- a/_maps/RandomZLevels/caves.dmm +++ b/_maps/RandomZLevels/caves.dmm @@ -1331,7 +1331,7 @@ /turf/open/floor/iron, /area/awaymission/caves/listeningpost) "ml" = ( -/mob/living/simple_animal/hostile/asteroid/fugu, +/mob/living/basic/wumborian_fugu, /turf/open/misc/asteroid/basalt{ initial_gas_mix = "n2=23;o2=14;TEMP=2.7" }, diff --git a/code/__DEFINES/ai.dm b/code/__DEFINES/ai.dm index e7dc62a72c4..f6d1768d725 100644 --- a/code/__DEFINES/ai.dm +++ b/code/__DEFINES/ai.dm @@ -243,3 +243,7 @@ #define BB_BILEWORM_SPEW_BILE "BB_bileworm_spew_bile" #define BB_BILEWORM_RESURFACE "BB_bileworm_resurface" #define BB_BILEWORM_DEVOUR "BB_bileworm_devour" + +/// Fugu AI Keys + +#define BB_FUGU_INFLATE "BB_fugu_inflate" diff --git a/code/datums/ai/basic_mobs/base_basic_controller.dm b/code/datums/ai/basic_mobs/base_basic_controller.dm index 93e9e795d57..b4f7c27c2f9 100644 --- a/code/datums/ai/basic_mobs/base_basic_controller.dm +++ b/code/datums/ai/basic_mobs/base_basic_controller.dm @@ -8,7 +8,7 @@ update_speed(basic_mob) - RegisterSignal(basic_mob, POST_BASIC_MOB_UPDATE_VARSPEED, PROC_REF(update_speed)) + RegisterSignals(basic_mob, list(POST_BASIC_MOB_UPDATE_VARSPEED, COMSIG_MOB_MOVESPEED_UPDATED), PROC_REF(update_speed)) return ..() //Run parent at end diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm index 7c457d143c3..1fa012fc466 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/basic_attacking.dm @@ -1,5 +1,5 @@ /datum/ai_behavior/basic_melee_attack - action_cooldown = 0.6 SECONDS + action_cooldown = 2 SECONDS behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION /datum/ai_behavior/basic_melee_attack/setup(datum/ai_controller/controller, target_key, targetting_datum_key, hiding_location_key) diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/try_mob_ability.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm similarity index 62% rename from code/datums/ai/basic_mobs/basic_ai_behaviors/try_mob_ability.dm rename to code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm index aad52b924bb..5e366c0f4fd 100644 --- a/code/datums/ai/basic_mobs/basic_ai_behaviors/try_mob_ability.dm +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/targeted_mob_ability.dm @@ -1,11 +1,13 @@ /** - * # Try Mob Ability + * # Targeted Mob Ability * Attempts to use a mob's cooldown ability on a target */ -/datum/ai_behavior/try_mob_ability +/datum/ai_behavior/targeted_mob_ability + +/datum/ai_behavior/targeted_mob_ability/perform(delta_time, datum/ai_controller/controller, ability_key, target_key) + var/datum/weakref/weak_ability = controller.blackboard[ability_key] + var/datum/action/cooldown/ability = weak_ability?.resolve() -/datum/ai_behavior/try_mob_ability/perform(delta_time, datum/ai_controller/controller, ability_key, target_key) - var/datum/action/cooldown/mob_cooldown/ability = controller.blackboard[ability_key] var/datum/weakref/weak_target = controller.blackboard[target_key] var/mob/living/target = weak_target?.resolve() if(!ability || QDELETED(target)) @@ -15,7 +17,7 @@ var/result = ability.InterceptClickOn(pawn, null, target) finish_action(controller, result, ability_key, target_key) -/datum/ai_behavior/try_mob_ability/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) +/datum/ai_behavior/targeted_mob_ability/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) . = ..() var/datum/weakref/weak_target = controller.blackboard[target_key] var/atom/target = weak_target?.resolve() @@ -33,9 +35,9 @@ * Attempts to use a mob's cooldown ability on a target and then move the target into a special target blackboard datum * Doesn't need another subtype to clear BB_BASIC_MOB_EXECUTION_TARGET because it will be the target key for the normal action */ -/datum/ai_behavior/try_mob_ability/and_plan_execute +/datum/ai_behavior/targeted_mob_ability/and_plan_execute -/datum/ai_behavior/try_mob_ability/and_plan_execute/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) +/datum/ai_behavior/targeted_mob_ability/and_plan_execute/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) controller.blackboard[BB_BASIC_MOB_EXECUTION_TARGET] = controller.blackboard[target_key] return ..() @@ -43,8 +45,8 @@ * # Try Mob Ability and clear target * Attempts to use a mob's cooldown ability on a target and releases the target when the action completes */ -/datum/ai_behavior/try_mob_ability/and_clear_target +/datum/ai_behavior/targeted_mob_ability/and_clear_target -/datum/ai_behavior/try_mob_ability/and_clear_target/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) +/datum/ai_behavior/targeted_mob_ability/and_clear_target/finish_action(datum/ai_controller/controller, succeeded, ability_key, target_key) . = ..() controller.blackboard[target_key] = null diff --git a/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm b/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm index b2496b15e2f..151a4125bf6 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/attack_obstacle_in_path.dm @@ -22,7 +22,7 @@ /// Something is in our way, get it outta here /datum/ai_behavior/attack_obstructions - action_cooldown = 1 SECONDS + action_cooldown = 2 SECONDS /// If we should attack walls, be prepared for complaints about breaches var/can_attack_turfs = FALSE /// Tries to bump open airlocks with an attack diff --git a/code/datums/ai/basic_mobs/basic_subtrees/targetted_mob_ability.dm b/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm similarity index 59% rename from code/datums/ai/basic_mobs/basic_subtrees/targetted_mob_ability.dm rename to code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm index 4450f9fa331..f748a877f05 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/targetted_mob_ability.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/targeted_mob_ability.dm @@ -1,13 +1,13 @@ /// Attempts to use a mob ability on a target -/datum/ai_planning_subtree/targetted_mob_ability +/datum/ai_planning_subtree/targeted_mob_ability /// Blackboard key for the ability var/ability_key /// Blackboard key for where the target ref is stored var/target_key = BB_BASIC_MOB_CURRENT_TARGET /// Behaviour to perform using ability - var/use_ability_behaviour = /datum/ai_behavior/try_mob_ability + var/use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability -/datum/ai_planning_subtree/targetted_mob_ability/SelectBehaviors(datum/ai_controller/controller, delta_time) +/datum/ai_planning_subtree/targeted_mob_ability/SelectBehaviors(datum/ai_controller/controller, delta_time) if (!ability_key) CRASH("You forgot to tell this mob where to find its ability") @@ -16,10 +16,9 @@ if (QDELETED(target)) return - var/datum/action/cooldown/using_action = controller.blackboard[ability_key] - if (QDELETED(using_action)) - return - if (!using_action.IsAvailable()) + var/datum/weakref/weak_ability = controller.blackboard[ability_key] + var/datum/action/cooldown/using_action = weak_ability?.resolve() + if (!using_action || !using_action.IsAvailable()) return controller.queue_behavior(use_ability_behaviour, ability_key, target_key) diff --git a/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm b/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm new file mode 100644 index 00000000000..c7eaf4a996f --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/use_mob_ability.dm @@ -0,0 +1,35 @@ +/** + * Simple behaviours which simply try to use an ability whenever it is available. + * For something which wants a target try `targetted_mob_ability`. + */ +/datum/ai_planning_subtree/use_mob_ability + /// Blackboard key for the ability + var/ability_key + /// Behaviour to perform using ability + var/use_ability_behaviour = /datum/ai_behavior/use_mob_ability + /// If true we terminate planning after trying to use the ability. + var/finish_planning = FALSE + +/datum/ai_planning_subtree/use_mob_ability/SelectBehaviors(datum/ai_controller/controller, delta_time) + if (!ability_key) + CRASH("You forgot to tell this mob where to find its ability") + + var/datum/weakref/weak_ability = controller.blackboard[ability_key] + var/datum/action/cooldown/using_action = weak_ability?.resolve() + if (!using_action || !using_action.IsAvailable()) + return + + controller.queue_behavior(use_ability_behaviour, ability_key) + if (finish_planning) + return SUBTREE_RETURN_FINISH_PLANNING + +/datum/ai_behavior/use_mob_ability + +/datum/ai_behavior/use_mob_ability/perform(delta_time, datum/ai_controller/controller, ability_key) + var/datum/weakref/weak_ability = controller.blackboard[ability_key] + var/datum/action/cooldown/using_action = weak_ability?.resolve() + if (!using_action) + finish_action(controller, FALSE, ability_key) + return + var/result = using_action.Trigger() + finish_action(controller, result, ability_key) diff --git a/code/datums/elements/wall_smasher.dm b/code/datums/elements/wall_smasher.dm index b9f764e66ac..3c6c1d92da7 100644 --- a/code/datums/elements/wall_smasher.dm +++ b/code/datums/elements/wall_smasher.dm @@ -20,13 +20,13 @@ RegisterSignals(target, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HUMAN_EARLY_UNARMED_ATTACK), PROC_REF(on_unarm_attack)) // Players RegisterSignal(target, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_pre_attackingtarget)) // AI - if (isanimal(target)) + if (isanimal_or_basicmob(target)) var/mob/living/simple_animal/animal_target = target animal_target.environment_smash = strength_flag /datum/element/wall_smasher/Detach(datum/target) UnregisterSignal(target, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, COMSIG_HOSTILE_PRE_ATTACKINGTARGET)) - if (isanimal(target)) + if (isanimal_or_basicmob(target)) var/mob/living/simple_animal/animal_target = target animal_target.environment_smash = initial(animal_target.environment_smash) diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm index e1f17470ef1..7c11ea04edb 100644 --- a/code/game/objects/structures/spawner.dm +++ b/code/game/objects/structures/spawner.dm @@ -65,7 +65,7 @@ max_mobs = 3 icon = 'icons/mob/simple/lavaland/nest.dmi' spawn_text = "crawls out of" - mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/simple_animal/hostile/asteroid/hivelord, /mob/living/simple_animal/hostile/asteroid/basilisk, /mob/living/simple_animal/hostile/asteroid/fugu) + mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/simple_animal/hostile/asteroid/hivelord, /mob/living/simple_animal/hostile/asteroid/basilisk, /mob/living/basic/wumborian_fugu) faction = list("mining") /obj/structure/spawner/mining/goldgrub @@ -91,7 +91,7 @@ /obj/structure/spawner/mining/wumborian name = "wumborian fugu den" desc = "A den housing a nest of wumborian fugus, how do they all even fit in there?" - mob_types = list(/mob/living/simple_animal/hostile/asteroid/fugu) + mob_types = list(/mob/living/basic/wumborian_fugu) /obj/structure/spawner/nether name = "netherworld link" @@ -137,3 +137,4 @@ newmob.desc = "It's [living_mob], but [living_mob.p_their()] flesh has an ashy texture, and [living_mob.p_their()] face is featureless save an eerie smile." src.visible_message(span_warning("[living_mob] reemerges from the link!")) qdel(living_mob) + diff --git a/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm b/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm new file mode 100644 index 00000000000..0643b73ead9 --- /dev/null +++ b/code/modules/antagonists/wizard/grand_ritual/grand_side_effect.dm @@ -0,0 +1,423 @@ +/** + * Describes something which can happen in a local area when the grand ritual is completed. + */ +/datum/grand_side_effect + /// If true then this effect is a holder for behaviour and should not be selected. + var/abstract = TRUE + +/** + * Returns true if you can trigger this effect. + * * ritual_location - Central turf of the ritual rune. + */ +/datum/grand_side_effect/proc/can_trigger(turf/ritual_location) + return !abstract + +/** + * Triggers some kind of effect in the area of the ritual. + * Arguments + * * potency - How many times a ritual has been cast previously. + * * ritual_location - Central turf of the ritual rune. + * * invoker - Mob who cast the spell. + */ +/datum/grand_side_effect/proc/trigger(potency, turf/ritual_location, mob/invoker) + return // Do something cool in the override + +/** + * A side effect which just casts a spell at its position + */ +/datum/grand_side_effect/spell + /// Path of spell to cast + var/spell_path + /// Time to spend before ending spell + var/duration = 0 + /// Sound effect to play + var/sound + +/// Casts dimensional instability on the area +/datum/grand_side_effect/scramble_turfs + abstract = FALSE + +/datum/grand_side_effect/scramble_turfs/trigger(potency, turf/ritual_location, mob/invoker) + playsound(ritual_location, 'sound/magic/timeparadox2.ogg', 60, TRUE) + var/datum/action/cooldown/spell/spell = new /datum/action/cooldown/spell/spacetime_dist() + spell.cast(ritual_location) + + var/duration = LERP((10 SECONDS), (15 SECONDS), potency/GRAND_RITUAL_FINALE_COUNT) + QDEL_IN(spell, duration) + +/// Transform the surrounding area into something else. +/datum/grand_side_effect/transmogrify_area + abstract = FALSE + +/datum/grand_side_effect/transmogrify_area/trigger(potency, turf/ritual_location, mob/invoker) + var/new_theme_path = pick(subtypesof(/datum/dimension_theme)) + var/datum/dimension_theme/theme = new new_theme_path() + var/range = round(LERP(2, 4, potency/GRAND_RITUAL_FINALE_COUNT)) + + var/list/turfs_to_transform = list() + + for (var/turf/turf in orange(range, ritual_location)) + if (!theme.can_convert(turf)) + continue + turfs_to_transform += turf + + if (theme.can_convert(ritual_location)) + theme.apply_theme(ritual_location) + + for (var/iterator in 1 to range) + var/list/range_turfs = list() + for (var/turf/turf as anything in turfs_to_transform) + var/dist_between = get_dist(ritual_location, turf) + if (dist_between != iterator) + continue + range_turfs += turf + addtimer(CALLBACK(src, PROC_REF(staggered_transform), theme, range_turfs), (0.5 SECONDS) * iterator) + +/datum/grand_side_effect/transmogrify_area/proc/staggered_transform(datum/dimension_theme/theme, list/transform_turfs) + for (var/turf/target_turf as anything in transform_turfs) + theme.apply_theme(target_turf) + +/// Minimum number of anomalies to create +#define MIN_ANOMALIES_CREATED 1 +/// Maximum number of anomalies to create +#define MAX_ANOMALIES_CREATED 4 + +/// Spawn some anomalies in the area, ones which are not too dangerous +/datum/grand_side_effect/create_anomalies + abstract = FALSE + /// List of anomaly types we are allowed to create, paired with a maximum to create of each + var/static/list/permitted_anomalies = list( + /obj/effect/anomaly/bioscrambler = 1, + /obj/effect/anomaly/hallucination = 2, + /obj/effect/anomaly/grav = 2, + /obj/effect/anomaly/flux/minor = 3, + ) + +/datum/grand_side_effect/create_anomalies/trigger(potency, turf/ritual_location, mob/invoker) + var/to_create = rand(MIN_ANOMALIES_CREATED, MAX_ANOMALIES_CREATED) + var/potency_add = LERP(-1, 1, potency/GRAND_RITUAL_FINALE_COUNT) + to_create = clamp(round(to_create + potency_add), MIN_ANOMALIES_CREATED, MAX_ANOMALIES_CREATED) + + var/list/can_create = permitted_anomalies.Copy() + var/list/anomaly_positions = list() + for (var/turf/potential_turf in orange(4, ritual_location)) + if (potential_turf.is_blocked_turf(exclude_mobs = TRUE)) + continue + anomaly_positions += potential_turf + + while(to_create > 0) + var/create_path = pick(can_create) + if (can_create[create_path] == 0) + continue + can_create[create_path] = can_create[create_path] - 1 + new create_path(pick(anomaly_positions), new_lifespan = rand(150, 300), drops_core = FALSE) + to_create-- + +#undef MIN_ANOMALIES_CREATED +#undef MAX_ANOMALIES_CREATED + +/// EMP nearby machines +/datum/grand_side_effect/emp + abstract = FALSE + +/datum/grand_side_effect/emp/trigger(potency, turf/ritual_location, mob/invoker) + var/heavy = LERP(0, 3, potency/GRAND_RITUAL_FINALE_COUNT) + var/light = LERP(3, 6, potency/GRAND_RITUAL_FINALE_COUNT) + empulse(ritual_location, heavy, light) + +/// Swap locations of nearby mobs arbitrarily and confuse them +/datum/grand_side_effect/translocate + abstract = FALSE + +/// Don't run if there's nobody to swap +/datum/grand_side_effect/translocate/can_trigger(turf/ritual_location) + . = ..() + if (!.) + return + + var/list/mobs = list() + for (var/mob/living/victim in range(5, ritual_location)) + mobs += victim + if (length(mobs) < 2) + return FALSE + return TRUE + +/datum/grand_side_effect/translocate/trigger(potency, turf/ritual_location, mob/invoker) + var/list/mobs = list() + var/list/mob_locations = list() + + for (var/mob/living/victim in range(5, ritual_location)) + mob_locations += victim.loc + mobs += victim + + if (!length(mobs)) + return + + shuffle_inplace(mob_locations) + shuffle_inplace(mobs) + + for (var/mob/living/victim as anything in mobs) + if (!length(mob_locations)) + break //locs aren't always unique, so this may come into play + var/obj/effect/particle_effect/fluid/smoke/poof = new(get_turf(victim)) + poof.lifetime = 2 SECONDS + var/atom/new_loc = pop(mob_locations) + do_teleport(victim, new_loc, channel = TELEPORT_CHANNEL_MAGIC) + +/// Spawn lube in the area +/datum/grand_side_effect/slippery + abstract = FALSE + +/datum/grand_side_effect/slippery/trigger(potency, turf/ritual_location, mob/invoker) + var/range = LERP(2, 4, potency/GRAND_RITUAL_FINALE_COUNT) + var/datum/reagents/lube = new(1000) + lube.add_reagent(/datum/reagent/lube, 100) + lube.my_atom = ritual_location + lube.create_foam(/datum/effect_system/fluid_spread/foam, DIAMOND_AREA(range)) + qdel(lube) + +/// Grabs one person and pulls them to this location, after a delay +/datum/grand_side_effect/summon_crewmate + abstract = FALSE + /// Weak reference to someone we're going to grab and pull to our location + var/datum/weakref/victim + +/// Don't run if there's nobody to summon +/datum/grand_side_effect/summon_crewmate/can_trigger(turf/ritual_location) + . = ..() + if (!.) + return + var/area/our_area = get_area(ritual_location) + for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list) + if (is_valid_crewmate(crewmate, our_area)) + return TRUE + return FALSE + +/datum/grand_side_effect/summon_crewmate/proc/is_valid_crewmate(mob/living/carbon/human/crewmate, area/our_area) + if (!crewmate.mind || IS_HUMAN_INVADER(crewmate)) + return FALSE + return get_area(crewmate) != our_area + +#define CREWMATE_SUMMON_TELEPORT_DELAY 9 SECONDS + +/datum/grand_side_effect/summon_crewmate/trigger(potency, turf/ritual_location, mob/invoker) + playsound(ritual_location, 'sound/magic/lightning_chargeup.ogg', 65, TRUE) + var/list/potential_victims = list() + var/area/our_area = get_area(ritual_location) + for (var/mob/living/carbon/human/crewmate as anything in GLOB.human_list) + if (!is_valid_crewmate(crewmate, our_area)) + continue + potential_victims += crewmate + + var/list/nearby_turfs = list() + for (var/turf/potential_turf in range(1, ritual_location)) + if (potential_turf.is_blocked_turf(exclude_mobs = TRUE)) + continue + nearby_turfs += potential_turf + + var/turf/landing_pos = pick(nearby_turfs) + new /obj/effect/temp_visual/teleport_abductor(landing_pos) + + var/mob/living/carbon/human/victim = pick(potential_victims) + playsound(get_turf(victim),'sound/magic/repulse.ogg', 60, TRUE) + victim.Immobilize(CREWMATE_SUMMON_TELEPORT_DELAY) + victim.AddElement(/datum/element/forced_gravity, 0) + victim.add_filter("teleport_glow", 2, list("type" = "outline", "color" = "#de3aff48", "size" = 2)) + victim.visible_message(span_warning("[victim] suddenly floats up into the air!"), span_warning("You feel a tug in your chest, and are lifted upwards into the air!")) + addtimer(CALLBACK(src, PROC_REF(summon_crewmate), victim, landing_pos), CREWMATE_SUMMON_TELEPORT_DELAY) + +#undef CREWMATE_SUMMON_TELEPORT_DELAY + +/datum/grand_side_effect/summon_crewmate/proc/summon_crewmate(mob/victim, turf/destination) + var/turf/was_position = victim.loc + victim.RemoveElement(/datum/element/forced_gravity, 0) + victim.remove_filter("teleport_glow") + + if (do_teleport(victim, destination, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_MAGIC)) + var/obj/effect/particle_effect/fluid/smoke/poof = new(was_position) + poof.lifetime = 2 SECONDS + was_position.visible_message(span_warning("[victim] disappears in a puff of smoke!")) + else + victim.visible_message(span_notice("[victim] sinks back to the ground.")) + +/// Create colourful smoke +/datum/grand_side_effect/smoke + abstract = FALSE + +/datum/grand_side_effect/smoke/trigger(potency, turf/ritual_location, mob/invoker) + playsound(src, 'sound/magic/smoke.ogg', 50, TRUE) + var/range = LERP(2, 4, potency/GRAND_RITUAL_FINALE_COUNT) + var/datum/effect_system/fluid_spread/smoke/colourful/smoke = new + smoke.set_up(range, holder = ritual_location, location = ritual_location) + smoke.start() + +/// Spawns randomly coloured smoke +/datum/effect_system/fluid_spread/smoke/colourful + effect_type = /obj/effect/particle_effect/fluid/smoke/colourful + +/// Randomly coloured smoke +/obj/effect/particle_effect/fluid/smoke/colourful + /// Colours that the smoke can be + var/static/list/colours = list( + "#ff0033", + "#3366ff", + "#10802d", + "#ee55ba", + "#e9ea53", + "#3f484e", + "#d6e1f0", + "#6b30bc", + "#72491e", + "#39e2dd", + "#50f038", + ) + +/obj/effect/particle_effect/fluid/smoke/colourful/Initialize(mapload, datum/fluid_group/group, ...) + . = ..() + color = pick(colours) + +/// Make a bloody mess +/datum/grand_side_effect/gore + abstract = FALSE + +/datum/grand_side_effect/gore/trigger(potency, turf/ritual_location, mob/invoker) + var/list/nearby_turfs = list() + for (var/turf/potential_turf in range(2, ritual_location)) + if (potential_turf.is_blocked_turf(exclude_mobs = TRUE)) + continue + nearby_turfs += potential_turf + + for (var/iterator in 1 to 4) + new /obj/effect/gibspawner/generic(pick(nearby_turfs)) + +/// Rain food in the area +/datum/grand_side_effect/create_food + abstract = FALSE + +/datum/grand_side_effect/create_food/trigger(potency, turf/ritual_location, mob/invoker) + var/duration = LERP((10 SECONDS), (30 SECONDS), potency/GRAND_RITUAL_FINALE_COUNT) + new /obj/effect/abstract/local_food_rain(ritual_location, duration) + +/// Makes food land near it until it expires +/obj/effect/abstract/local_food_rain + var/max_foods_per_second = 1 + var/range = 3 + +/obj/effect/abstract/local_food_rain/Initialize(mapload, duration, max_foods_per_second = 3, range = 3) + . = ..() + src.max_foods_per_second = max_foods_per_second + src.range = range + addtimer(CALLBACK(src, PROC_REF(end_rain)), duration) + create_food(2 SECONDS) + START_PROCESSING(SSprocessing, src) + +/obj/effect/abstract/local_food_rain/Destroy(force) + STOP_PROCESSING(SSprocessing, src) + return ..() + +/obj/effect/abstract/local_food_rain/process(delta_time) + create_food(delta_time) + +/obj/effect/abstract/local_food_rain/proc/create_food(delta_time) + var/to_create = rand(0, max_foods_per_second * delta_time) + if (to_create == 0) + return + + var/list/valid_turfs = list() + for (var/turf/turf in range(range, src)) + if(turf.is_blocked_turf(exclude_mobs = TRUE)) + continue + valid_turfs += turf + + while(to_create > 0 && length(valid_turfs) > 0) + to_create-- + addtimer(CALLBACK(src, PROC_REF(drop_food), pick_n_take(valid_turfs)), rand(0, (1 SECONDS) * delta_time)) + +/obj/effect/abstract/local_food_rain/proc/drop_food(turf/landing_zone) + podspawn(list( + "target" = landing_zone, + "style" = STYLE_SEETHROUGH, + "spawn" = get_random_food(), + "delays" = list(POD_TRANSIT = 0, POD_FALLING = (3 SECONDS), POD_OPENING = 0, POD_LEAVING = 0), + "effectStealth" = TRUE, + "effectQuiet" = TRUE, + ) + ) + +/obj/effect/abstract/local_food_rain/proc/end_rain() + qdel(src) + +/// Spawn some mobs after a delay +/datum/grand_side_effect/spawn_delayed_mobs + abstract = FALSE + /// Typepaths of mobs to create + var/static/list/permitted_mobs = list( + /mob/living/basic/wumborian_fugu, + /mob/living/simple_animal/hostile/skeleton, + /mob/living/simple_animal/hostile/killertomato, + /mob/living/simple_animal/hostile/ooze, + /mob/living/simple_animal/hostile/illusion, + /mob/living/basic/carp, + ) + +/datum/grand_side_effect/spawn_delayed_mobs/trigger(potency, turf/ritual_location, mob/invoker) + var/count = LERP(1, 4, potency/GRAND_RITUAL_FINALE_COUNT) + var/list/valid_turfs = list() + for (var/turf/turf in range(3, ritual_location)) + if(turf.is_blocked_turf(exclude_mobs = TRUE)) + continue + valid_turfs += turf + + var/mob_type = pick(permitted_mobs) + while(count > 0 && length(valid_turfs) > 0) + count-- + var/turf/spawn_loc = pick_n_take(valid_turfs) + addtimer(CALLBACK(src, PROC_REF(create_portal), mob_type, spawn_loc), rand(0, 1 SECONDS)) + +/datum/grand_side_effect/spawn_delayed_mobs/proc/create_portal(mob_type, turf/spawn_loc) + var/spawn_delay = rand(10 SECONDS, 15 SECONDS) + new /obj/effect/temp_visual/delayed_mob_portal(spawn_loc, spawn_delay) + addtimer(CALLBACK(src, PROC_REF(create_mob), mob_type, spawn_loc), spawn_delay) + +/datum/grand_side_effect/spawn_delayed_mobs/proc/create_mob(mob_path, loc) + if (!loc) + return + if (!mob_path) + return + playsound(get_turf(src),'sound/magic/teleport_app.ogg', 60, TRUE) + do_sparks(5, FALSE, loc) + new mob_path(loc) + +/// Spawns a mob when it expires +/obj/effect/temp_visual/delayed_mob_portal + icon_state = "rift" + +/obj/effect/temp_visual/delayed_mob_portal/Initialize(mapload, duration = 15 SECONDS) + src.duration = duration + animate(src, transform = matrix()*0, time = 0) + animate(transform = matrix(), time = 2) + add_filter("portal_ripple", 2, list("type" = "ripple", "flags" = WAVE_BOUNDED, "radius" = 0, "size" = 2)) + var/filter = get_filter("portal_ripple") + animate(filter, radius = 0, time = 0.2 SECONDS, size = 2, easing = JUMP_EASING, loop = -1, flags = ANIMATION_PARALLEL) + animate(radius = 32, time = 1.5 SECONDS, size = 0) + return ..() + +/// Provides musical accompaniment +/datum/grand_side_effect/orchestra + abstract = FALSE + +/datum/grand_side_effect/orchestra/trigger(potency, turf/ritual_location, mob/invoker) + var/count = LERP(1, 4, potency/GRAND_RITUAL_FINALE_COUNT) + var/list/valid_turfs = list() + for (var/turf/turf in range(2, ritual_location)) + if(turf.is_blocked_turf(exclude_mobs = TRUE)) + continue + valid_turfs += turf + + while(count > 0 && length(valid_turfs) > 0) + count-- + var/mob/living/carbon/human/species/monkey/monke = new(pick_n_take(valid_turfs)) + monke.equip_to_slot_or_del(new /obj/item/clothing/under/suit/waiter(monke), ITEM_SLOT_ICLOTHING) + var/instrument_path = pick(subtypesof(/obj/item/instrument)) + var/obj/item/instrument/instrument = new instrument_path() + monke.put_in_hands(instrument) diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm index 43c9548fd94..6fbde7470c3 100644 --- a/code/modules/mob/living/basic/basic.dm +++ b/code/modules/mob/living/basic/basic.dm @@ -36,8 +36,8 @@ ///Played when someone punches the creature. var/attacked_sound = SFX_PUNCH //This should be an element - ///What kind of objects this mob can smash. - var/environment_smash = ENVIRONMENT_SMASH_NONE + /// Variable maintained for compatibility with attack_animal procs until simple animals can be refactored away. Use element instead of setting manually. + var/environment_smash = ENVIRONMENT_SMASH_STRUCTURES /// 1 for full damage, 0 for none, -1 for 1:1 heal from that source. var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) diff --git a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm index 0c0c7047bdd..5f065ec53e4 100644 --- a/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm +++ b/code/modules/mob/living/basic/farm_animals/cow/cow_moonicorn.dm @@ -56,16 +56,9 @@ /datum/ai_planning_subtree/simple_find_target, //...or something to eat, possibly. both types of target handled by melee attack subtree /datum/ai_planning_subtree/find_food, - /datum/ai_planning_subtree/basic_melee_attack_subtree/moonicorn, + /datum/ai_planning_subtree/basic_melee_attack_subtree, ) -/datum/ai_planning_subtree/basic_melee_attack_subtree/moonicorn - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/moonicorn - -/datum/ai_behavior/basic_melee_attack/moonicorn - //it's a fairly strong attack and it applies pax, so they do not attack often - action_cooldown = 2 SECONDS - ///moonicorns will not attack people holding something that could tame them. /datum/targetting_datum/basic/allow_items/moonicorn diff --git a/code/modules/mob/living/basic/farm_animals/pig.dm b/code/modules/mob/living/basic/farm_animals/pig.dm index e931a549122..abe5b9c3a74 100644 --- a/code/modules/mob/living/basic/farm_animals/pig.dm +++ b/code/modules/mob/living/basic/farm_animals/pig.dm @@ -58,11 +58,5 @@ /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, /datum/ai_planning_subtree/flee_target, /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree/pig, + /datum/ai_planning_subtree/basic_melee_attack_subtree, ) - -/datum/ai_planning_subtree/basic_melee_attack_subtree/pig - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/pig - -/datum/ai_behavior/basic_melee_attack/pig - action_cooldown = 2 SECONDS diff --git a/code/modules/mob/living/basic/lavaland/bileworm/_bileworm.dm b/code/modules/mob/living/basic/lavaland/bileworm/_bileworm.dm index ae87d20b371..c8df6d001f4 100644 --- a/code/modules/mob/living/basic/lavaland/bileworm/_bileworm.dm +++ b/code/modules/mob/living/basic/lavaland/bileworm/_bileworm.dm @@ -56,6 +56,6 @@ devour.Grant(src) var/datum/action/adjust_vision/bileworm/adjust_vision = new(src) adjust_vision.Grant(src) - ai_controller.blackboard[BB_BILEWORM_SPEW_BILE] = spew_bile - ai_controller.blackboard[BB_BILEWORM_RESURFACE] = resurface - ai_controller.blackboard[BB_BILEWORM_DEVOUR] = devour + ai_controller.blackboard[BB_BILEWORM_SPEW_BILE] = WEAKREF(spew_bile) + ai_controller.blackboard[BB_BILEWORM_RESURFACE] = WEAKREF(resurface) + ai_controller.blackboard[BB_BILEWORM_DEVOUR] = WEAKREF(devour) diff --git a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm index 3abfc0430b6..f6205a341bd 100644 --- a/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm +++ b/code/modules/mob/living/basic/lavaland/bileworm/bileworm_ai.dm @@ -18,14 +18,15 @@ if(QDELETED(target)) return - var/datum/action/cooldown/mob_cooldown/resurface = controller.blackboard[BB_BILEWORM_RESURFACE] + var/datum/weakref/weak_action = controller.blackboard[BB_BILEWORM_RESURFACE] + var/datum/action/cooldown/mob_cooldown/resurface = weak_action?.resolve() //because one ability is always INFINITY cooldown, this actually works to check which ability should be used //sometimes it will try to spew bile on infinity cooldown, but that's okay because as soon as resurface is ready it will attempt that - if(resurface.next_use_time <= world.time) - controller.queue_behavior(/datum/ai_behavior/try_mob_ability/and_plan_execute, BB_BILEWORM_RESURFACE, BB_BASIC_MOB_CURRENT_TARGET) + if(resurface && resurface.next_use_time <= world.time) + controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_plan_execute, BB_BILEWORM_RESURFACE, BB_BASIC_MOB_CURRENT_TARGET) else - controller.queue_behavior(/datum/ai_behavior/try_mob_ability/and_plan_execute, BB_BILEWORM_SPEW_BILE, BB_BASIC_MOB_CURRENT_TARGET) + controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability/and_plan_execute, BB_BILEWORM_SPEW_BILE, BB_BASIC_MOB_CURRENT_TARGET) return SUBTREE_RETURN_FINISH_PLANNING //focus on the fight /datum/ai_planning_subtree/bileworm_execute @@ -37,5 +38,5 @@ if(QDELETED(target) || target.stat < UNCONSCIOUS) return - controller.queue_behavior(/datum/ai_behavior/try_mob_ability, BB_BILEWORM_DEVOUR, BB_BASIC_MOB_EXECUTION_TARGET) + controller.queue_behavior(/datum/ai_behavior/targeted_mob_ability, BB_BILEWORM_DEVOUR, BB_BASIC_MOB_EXECUTION_TARGET) return SUBTREE_RETURN_FINISH_PLANNING //focus on devouring this fool diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp.dm b/code/modules/mob/living/basic/space_fauna/carp/carp.dm index f506d77f7de..169eaa77ce9 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp.dm @@ -105,7 +105,7 @@ teleport = new(src) teleport.Grant(src) - ai_controller.blackboard[BB_CARP_RIFT] = teleport + ai_controller.blackboard[BB_CARP_RIFT] = WEAKREF(teleport) /mob/living/basic/carp/Destroy() QDEL_NULL(teleport) diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm index abda57a55ed..2eea1f3edfb 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_actions.dm @@ -42,7 +42,8 @@ /datum/ai_planning_subtree/find_nearest_magicarp_spell_target /datum/ai_planning_subtree/find_nearest_magicarp_spell_target/SelectBehaviors(datum/ai_controller/controller, delta_time) - var/datum/action/cooldown/using_action = controller.blackboard[BB_MAGICARP_SPELL] + var/datum/weakref/weak_action = controller.blackboard[BB_MAGICARP_SPELL] + var/datum/action/cooldown/using_action = weak_action?.resolve() if (QDELETED(using_action)) return if (!using_action.IsAvailable()) @@ -68,7 +69,7 @@ /datum/ai_behavior/find_potential_targets/nearest/magicarp /// Then use it on that target -/datum/ai_planning_subtree/targetted_mob_ability/magicarp +/datum/ai_planning_subtree/targeted_mob_ability/magicarp ability_key = BB_MAGICARP_SPELL target_key = BB_MAGICARP_SPELL_TARGET - use_ability_behaviour = /datum/ai_behavior/try_mob_ability/and_clear_target + use_ability_behaviour = /datum/ai_behavior/targeted_mob_ability/and_clear_target diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm index 4f8cf327e75..71be65aadd7 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_ai_rift_actions.dm @@ -17,10 +17,9 @@ if (!target) return - var/datum/action/cooldown/using_action = controller.blackboard[BB_CARP_RIFT] - if (isnull(using_action)) - return - if (!using_action.IsAvailable()) + var/datum/weakref/weak_action = controller.blackboard[BB_CARP_RIFT] + var/datum/action/cooldown/using_action = weak_action?.resolve() + if (isnull(using_action) || !using_action.IsAvailable()) return controller.queue_behavior(rift_behaviour, BB_CARP_RIFT, BB_BASIC_MOB_CURRENT_TARGET) @@ -54,7 +53,8 @@ /datum/ai_behavior/make_carp_rift /datum/ai_behavior/make_carp_rift/setup(datum/ai_controller/controller, ability_key, target_key) - var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = controller.blackboard[ability_key] + var/datum/weakref/weak_action = controller.blackboard[ability_key] + var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = weak_action?.resolve() if (!ability) return FALSE var/datum/weakref/weak_target = controller.blackboard[target_key] @@ -63,7 +63,8 @@ /datum/ai_behavior/make_carp_rift/perform(delta_time, datum/ai_controller/controller, ability_key, target_key) . = ..() - var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = controller.blackboard[ability_key] + var/datum/weakref/weak_action = controller.blackboard[ability_key] + var/datum/action/cooldown/mob_cooldown/lesser_carp_rift/ability = weak_action?.resolve() var/datum/weakref/weak_target = controller.blackboard[target_key] var/atom/target = weak_target?.resolve() diff --git a/code/modules/mob/living/basic/space_fauna/carp/carp_controllers.dm b/code/modules/mob/living/basic/space_fauna/carp/carp_controllers.dm index 4210853c02a..75c915c16a1 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/carp_controllers.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/carp_controllers.dm @@ -63,7 +63,7 @@ /datum/ai_planning_subtree/flee_target, /datum/ai_planning_subtree/find_food, /datum/ai_planning_subtree/find_nearest_magicarp_spell_target, - /datum/ai_planning_subtree/targetted_mob_ability/magicarp, + /datum/ai_planning_subtree/targeted_mob_ability/magicarp, /datum/ai_planning_subtree/simple_find_target, /datum/ai_planning_subtree/attack_obstacle_in_path/carp, /datum/ai_planning_subtree/shortcut_to_target_through_carp_rift, diff --git a/code/modules/mob/living/basic/space_fauna/carp/magicarp.dm b/code/modules/mob/living/basic/space_fauna/carp/magicarp.dm index 3ee5c35c5e7..90418d7e467 100644 --- a/code/modules/mob/living/basic/space_fauna/carp/magicarp.dm +++ b/code/modules/mob/living/basic/space_fauna/carp/magicarp.dm @@ -88,7 +88,7 @@ GLOBAL_LIST_INIT(magicarp_spell_colours, list( spell.projectile_type = spell_type spell.button_icon_state = initial(spell_type.icon_state) spell.Grant(src) - ai_controller.blackboard[BB_MAGICARP_SPELL] = spell + ai_controller.blackboard[BB_MAGICARP_SPELL] = WEAKREF(spell) assign_spell_ai(spell_type) /// If you have certain spells, use a different targetting datum diff --git a/code/modules/mob/living/basic/space_fauna/ghost.dm b/code/modules/mob/living/basic/space_fauna/ghost.dm index e8f65ae4737..c20903e2773 100644 --- a/code/modules/mob/living/basic/space_fauna/ghost.dm +++ b/code/modules/mob/living/basic/space_fauna/ghost.dm @@ -99,17 +99,10 @@ planning_subtrees = list( /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/basic_melee_attack_subtree/ghost, + /datum/ai_planning_subtree/basic_melee_attack_subtree, ) -/datum/ai_planning_subtree/basic_melee_attack_subtree/ghost - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/ghost - -/datum/ai_behavior/basic_melee_attack/ghost - action_cooldown = 2 SECONDS - - -/// Weaker variant of ghosts that can smash structures. Meant to be summoned in swarms via the ectoplasmic anomaly and associated ghost portal. +/// Weaker variant of ghosts. Meant to be summoned in swarms via the ectoplasmic anomaly and associated ghost portal. /mob/living/basic/ghost/swarm name = "vengeful spirit" desc = "Back from the grave, and not happy about it." @@ -121,4 +114,3 @@ melee_damage_upper = 10 death_message = "wails as it is torn back to the realm from which it came!" random_identity = FALSE - environment_smash = ENVIRONMENT_SMASH_STRUCTURES diff --git a/code/modules/mob/living/basic/space_fauna/giant_spider/giant_spider_ai.dm b/code/modules/mob/living/basic/space_fauna/giant_spider/giant_spider_ai.dm index 826982e491d..ca7b8cce21c 100644 --- a/code/modules/mob/living/basic/space_fauna/giant_spider/giant_spider_ai.dm +++ b/code/modules/mob/living/basic/space_fauna/giant_spider/giant_spider_ai.dm @@ -1,5 +1,3 @@ -#define SPIDER_ATTACK_COOLDOWN (2 SECONDS) - /// For now, essentially just a Simple Hostile but room for expansion /datum/ai_controller/basic_controller/giant_spider blackboard = list( @@ -12,11 +10,18 @@ planning_subtrees = list( /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path/giant_spider, - /datum/ai_planning_subtree/basic_melee_attack_subtree/giant_spider, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, /datum/ai_planning_subtree/random_speech/insect, // Space spiders are taxonomically insects not arachnids, don't DM me ) +/// Giant spider which won't attack structures +/datum/ai_controller/basic_controller/giant_spider/weak + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/random_speech/insect, + ) /// Used by Araneus, who only attacks those who attack first /datum/ai_controller/basic_controller/giant_spider/retaliate @@ -26,21 +31,7 @@ planning_subtrees = list( /datum/ai_planning_subtree/target_retaliate, - /datum/ai_planning_subtree/attack_obstacle_in_path/giant_spider, - /datum/ai_planning_subtree/basic_melee_attack_subtree/giant_spider, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, /datum/ai_planning_subtree/random_speech/insect, ) - -/datum/ai_planning_subtree/attack_obstacle_in_path/giant_spider - attack_behaviour = /datum/ai_behavior/attack_obstructions/giant_spider - -/datum/ai_behavior/attack_obstructions/giant_spider - action_cooldown = SPIDER_ATTACK_COOLDOWN - -/datum/ai_planning_subtree/basic_melee_attack_subtree/giant_spider - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/giant_spider - -/datum/ai_behavior/basic_melee_attack/giant_spider - action_cooldown = SPIDER_ATTACK_COOLDOWN - -#undef SPIDER_ATTACK_COOLDOWN diff --git a/code/modules/mob/living/basic/space_fauna/giant_spider/spider_variants.dm b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_variants.dm index 2eacd437ecf..a01bdd109cd 100644 --- a/code/modules/mob/living/basic/space_fauna/giant_spider/spider_variants.dm +++ b/code/modules/mob/living/basic/space_fauna/giant_spider/spider_variants.dm @@ -212,13 +212,13 @@ */ /mob/living/basic/giant_spider/hunter/scrawny name = "scrawny spider" - environment_smash = ENVIRONMENT_SMASH_NONE health = 60 maxHealth = 60 melee_damage_lower = 5 melee_damage_upper = 10 desc = "Furry and black, it makes you shudder to look at it. This one has sparkling purple eyes, and looks abnormally thin and frail." menu_description = "Fast spider variant specializing in catching running prey and toxin injection, but has less damage than a normal hunter spider at the cost of a little more health." + ai_controller = /datum/ai_controller/basic_controller/giant_spider/weak /** * # Scrawny Tarantula @@ -228,13 +228,13 @@ */ /mob/living/basic/giant_spider/tarantula/scrawny name = "scrawny tarantula" - environment_smash = ENVIRONMENT_SMASH_NONE health = 150 maxHealth = 150 melee_damage_lower = 20 melee_damage_upper = 25 desc = "Furry and black, it makes you shudder to look at it. This one has abyssal red eyes, and looks abnormally thin and frail." menu_description = "A weaker variant of the tarantula with reduced amount of health and damage, very slow when not on webbing. It also has a charge ability to close distance with a target after a small windup." + ai_controller = /datum/ai_controller/basic_controller/giant_spider/weak /** * # Scrawny Nurse Spider @@ -245,11 +245,11 @@ */ /mob/living/basic/giant_spider/nurse/scrawny name = "scrawny nurse spider" - environment_smash = ENVIRONMENT_SMASH_NONE health = 30 maxHealth = 30 desc = "Furry and black, it makes you shudder to look at it. This one has brilliant green eyes, and looks abnormally thin and frail." menu_description = "Weaker version of the nurse spider, specializing in healing their brethren and placing webbings very swiftly, but has very low amount of health and deals low damage." + ai_controller = /datum/ai_controller/basic_controller/giant_spider/weak /** * # Flesh Spider @@ -314,7 +314,7 @@ name = "Sergeant Araneus" real_name = "Sergeant Araneus" desc = "A fierce companion of the Head of Security, this spider has been carefully trained by Nanotrasen specialists. Its beady, staring eyes send shivers down your spine." - faction = list("spiders") + faction = list("spider") maxHealth = 250 health = 250 melee_damage_lower = 15 diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm new file mode 100644 index 00000000000..485343c0a49 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/fugu_gland.dm @@ -0,0 +1,42 @@ +/// Item you use on a mob to make it bigger and stronger +/obj/item/fugu_gland + name = "wumborian fugu gland" + desc = "The key to the wumborian fugu's ability to increase its mass arbitrarily, this disgusting remnant can apply the same effect to other creatures, giving them great strength." + icon = 'icons/obj/medical/organs/organs.dmi' + icon_state = "fugu_gland" + item_flags = NOBLUDGEON + w_class = WEIGHT_CLASS_NORMAL + layer = MOB_LAYER + /// List of mob types which you can't apply the gland to + var/static/list/fugu_blacklist + +/obj/item/fugu_gland/Initialize(mapload) + . = ..() + if(fugu_blacklist) + return + fugu_blacklist = typecacheof(list( + /mob/living/simple_animal/hostile/guardian, + )) + +/obj/item/fugu_gland/afterattack(atom/target, mob/user, proximity_flag) + . = ..() + if(!proximity_flag || !isanimal_or_basicmob(target) || fugu_blacklist[target.type]) + return + var/mob/living/animal = target + + if(animal.stat == DEAD || HAS_TRAIT(animal, TRAIT_FAKEDEATH)) + balloon_alert(user, "it's dead!") + return + if(HAS_TRAIT(animal, TRAIT_FUGU_GLANDED)) + balloon_alert(user, "already large!") + return + + ADD_TRAIT(animal, TRAIT_FUGU_GLANDED, type) + animal.maxHealth *= 1.5 + animal.health = min(animal.maxHealth, animal.health * 1.5) + animal.melee_damage_lower = max((animal.melee_damage_lower * 2), 10) + animal.melee_damage_upper = max((animal.melee_damage_upper * 2), 10) + animal.transform *= 2 + animal.AddElement(/datum/element/wall_smasher, strength_flag = ENVIRONMENT_SMASH_RWALLS) + to_chat(user, span_info("You increase the size of [animal], giving [animal.p_them()] a surge of strength!")) + qdel(src) diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm new file mode 100644 index 00000000000..e6e3d2200d7 --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/inflation.dm @@ -0,0 +1,94 @@ +/** + * Action which inflates you, making you larger and stronger for the duration. Also invulnerable. + * This is pretty much all just handled by a status effect. + * Unfortunately the requirements here are specific enough that it kind of only works for the mob it is designed for. + */ +/datum/action/cooldown/fugu_expand + name = "Inflate" + desc = "Temporarily increases your size, making you significantly more dangerous and durable!" + button_icon = 'icons/mob/actions/actions_animal.dmi' + button_icon_state = "expand" + background_icon_state = "bg_fugu" + overlay_icon_state = "bg_fugu_border" + cooldown_time = 16 SECONDS + melee_cooldown_time = 0 SECONDS + +/datum/action/cooldown/fugu_expand/IsAvailable(feedback) + . = ..() + if (!.) + return FALSE + if(!istype(owner, /mob/living/basic/wumborian_fugu)) // A shame but there's not any good way to make this work on other mobs + if (feedback) + owner.balloon_alert(owner, "not stretchy enough!") + return FALSE + if(HAS_TRAIT(owner, TRAIT_FUGU_GLANDED)) + if (feedback) + owner.balloon_alert(owner, "already large!") + return FALSE + return TRUE + +/datum/action/cooldown/fugu_expand/Activate(atom/target) + . = ..() + var/mob/living/living_owner = owner + living_owner.apply_status_effect(/datum/status_effect/inflated) + +/** + * Status effect from the Expand action, makes you big and round and strong. + */ +/datum/status_effect/inflated + id = "wumbo_inflated" + duration = 10 SECONDS + alert_type = /atom/movable/screen/alert/status_effect/inflated + +/atom/movable/screen/alert/status_effect/inflated + name = "WUMBO" + desc = "You feel big and strong!" + icon_state = "gross" + +/datum/status_effect/inflated/on_creation(mob/living/new_owner, ...) + if (!istype(new_owner, /mob/living/basic/wumborian_fugu)) + return FALSE + return ..() + +/datum/status_effect/inflated/on_apply() + . = ..() + var/mob/living/basic/wumborian_fugu/fugu = owner + if (!istype(fugu)) + return FALSE + RegisterSignal(fugu, COMSIG_MOB_STATCHANGE, PROC_REF(check_death)) + fugu.add_movespeed_modifier(/datum/movespeed_modifier/status_effect/inflated) + ADD_TRAIT(fugu, TRAIT_FUGU_GLANDED, TRAIT_STATUS_EFFECT(id)) + fugu.AddElement(/datum/element/wall_smasher) + fugu.mob_size = MOB_SIZE_LARGE + fugu.icon_state = "Fugu1" + fugu.melee_damage_lower = 15 + fugu.melee_damage_upper = 20 + fugu.status_flags |= GODMODE + fugu.obj_damage = 60 + fugu.ai_controller.blackboard[BB_BASIC_MOB_FLEEING] = FALSE + fugu.ai_controller.CancelActions() + +/datum/status_effect/inflated/on_remove() + . = ..() + var/mob/living/basic/wumborian_fugu/fugu = owner + if (!istype(fugu)) + return // Check again in case you changed mob after application but somehow kept the status effect + UnregisterSignal(fugu, COMSIG_MOB_STATCHANGE) + fugu.remove_movespeed_modifier(/datum/movespeed_modifier/status_effect/inflated) + REMOVE_TRAIT(fugu, TRAIT_FUGU_GLANDED, TRAIT_STATUS_EFFECT(id)) + fugu.RemoveElement(/datum/element/wall_smasher) + fugu.mob_size = MOB_SIZE_SMALL + fugu.melee_damage_lower = 0 + fugu.melee_damage_upper = 0 + fugu.status_flags &= ~GODMODE + if (fugu.stat != DEAD) + fugu.icon_state = "Fugu0" + fugu.obj_damage = 0 + fugu.ai_controller.blackboard[BB_BASIC_MOB_FLEEING] = TRUE + fugu.ai_controller.CancelActions() + +/// Remove status effect if we die +/datum/status_effect/inflated/proc/check_death(mob/living/source, new_stat) + SIGNAL_HANDLER + if (new_stat == DEAD) + qdel(src) diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_ai.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_ai.dm new file mode 100644 index 00000000000..f632a38dc1b --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_ai.dm @@ -0,0 +1,37 @@ +#define WUMBO_ATTACK_COOLDOWN 2.5 SECONDS + +/// Cowardly when small, aggressive when big. Tries to transform whenever possible. +/datum/ai_controller/basic_controller/wumborian_fugu + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic(), + BB_BASIC_MOB_FLEEING = TRUE, + ) + + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/targeted_mob_ability/inflate, + /datum/ai_planning_subtree/flee_target, + /datum/ai_planning_subtree/attack_obstacle_in_path/wumborian_fugu, + /datum/ai_planning_subtree/basic_melee_attack_subtree/wumborian_fugu, + ) + +/datum/ai_planning_subtree/basic_melee_attack_subtree/wumborian_fugu + melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/wumborian_fugu + +/datum/ai_behavior/basic_melee_attack/wumborian_fugu + action_cooldown = WUMBO_ATTACK_COOLDOWN + +/datum/ai_planning_subtree/attack_obstacle_in_path/wumborian_fugu + attack_behaviour = /datum/ai_behavior/attack_obstructions/wumborian_fugu + +/datum/ai_behavior/attack_obstructions/wumborian_fugu + can_attack_turfs = TRUE + action_cooldown = WUMBO_ATTACK_COOLDOWN + +/datum/ai_planning_subtree/targeted_mob_ability/inflate + ability_key = BB_FUGU_INFLATE + +#undef WUMBO_ATTACK_COOLDOWN diff --git a/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm new file mode 100644 index 00000000000..ab9c91d160e --- /dev/null +++ b/code/modules/mob/living/basic/space_fauna/wumborian_fugu/wumborian_fugu.dm @@ -0,0 +1,61 @@ +/** + * # Wumborian Fugu + * + * A strange alien creature capable of increasing its mass when threatened, when not inflated it is virtually defenceless. + * Mostly only appears from xenobiology, or the occasional wizard. + * On death, the "fugu gland" is dropped, which can be used on mobs to increase their size, health, strength, and lets them smash walls. + */ +/mob/living/basic/wumborian_fugu + name = "wumborian fugu" + desc = "The wumborian fugu rapidly increases its body mass in order to ward off its prey. Great care should be taken to avoid it while it's in this state as it is nearly invincible, but it cannot maintain its form forever." + icon = 'icons/mob/simple/lavaland/64x64megafauna.dmi' + icon_state = "Fugu0" + icon_living = "Fugu0" + icon_dead = "Fugu_dead" + icon_gib = "syndicate_gib" + health_doll_icon = "Fugu0" + pixel_x = -16 + base_pixel_x = -16 + status_flags = NONE + gold_core_spawnable = HOSTILE_SPAWN + mob_biotypes = MOB_ORGANIC | MOB_BEAST + mob_size = MOB_SIZE_SMALL + speed = 0 + maxHealth = 50 + health = 50 + combat_mode = TRUE + obj_damage = 0 + melee_damage_lower = 0 + melee_damage_upper = 0 + attack_sound = 'sound/weapons/punch1.ogg' + attack_vis_effect = ATTACK_EFFECT_BITE + attack_verb_continuous = "chomps" + attack_verb_simple = "chomp" + friendly_verb_continuous = "floats near" + friendly_verb_simple = "float near" + speak_emote = list("puffs") + faction = list("mining") + see_in_dark = 8 + // Nice and dark purple, to match le vibes + lighting_cutoff_red = 20 + lighting_cutoff_green = 10 + lighting_cutoff_blue = 40 + 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 = INFINITY + ai_controller = /datum/ai_controller/basic_controller/wumborian_fugu + /// Ability used by the mob to become large, dangerous, and invulnerable + var/datum/action/cooldown/fugu_expand/expand + +/mob/living/basic/wumborian_fugu/Initialize(mapload) + . = ..() + AddElement(/datum/element/death_drops, loot = list(/obj/item/fugu_gland)) + ADD_TRAIT(src, TRAIT_LAVA_IMMUNE, ROUNDSTART_TRAIT) + ADD_TRAIT(src, TRAIT_ASHSTORM_IMMUNE, ROUNDSTART_TRAIT) + expand = new(src) + expand.Grant(src) + ai_controller.blackboard[BB_FUGU_INFLATE] = WEAKREF(expand) + +/mob/living/basic/wumborian_fugu/Destroy() + QDEL_NULL(expand) + return ..() diff --git a/code/modules/mob/living/basic/syndicate/syndicate.dm b/code/modules/mob/living/basic/syndicate/syndicate.dm index 723d7bf5ec8..47005333c28 100644 --- a/code/modules/mob/living/basic/syndicate/syndicate.dm +++ b/code/modules/mob/living/basic/syndicate/syndicate.dm @@ -10,7 +10,6 @@ health = 100 basic_mob_flags = DEL_ON_DEATH speed = 1.1 - environment_smash = ENVIRONMENT_SMASH_STRUCTURES melee_damage_lower = 10 melee_damage_upper = 10 attack_verb_continuous = "punches" @@ -251,7 +250,7 @@ bubble_icon = "syndibot" gold_core_spawnable = HOSTILE_SPAWN death_message = "is smashed into pieces!" - ai_controller = /datum/ai_controller/basic_controller/viscerator + ai_controller = /datum/ai_controller/basic_controller/syndicate/viscerator /mob/living/basic/viscerator/Initialize(mapload) . = ..() diff --git a/code/modules/mob/living/basic/syndicate/syndicate_ai.dm b/code/modules/mob/living/basic/syndicate/syndicate_ai.dm index 151e10017af..9e2c86e3602 100644 --- a/code/modules/mob/living/basic/syndicate/syndicate_ai.dm +++ b/code/modules/mob/living/basic/syndicate/syndicate_ai.dm @@ -7,6 +7,7 @@ idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( /datum/ai_planning_subtree/simple_find_target, + /datum/ai_planning_subtree/attack_obstacle_in_path/syndicate, /datum/ai_planning_subtree/basic_melee_attack_subtree/syndicate ) @@ -19,6 +20,12 @@ /datum/ai_behavior/basic_melee_attack/syndicate action_cooldown = 1.2 SECONDS +/datum/ai_planning_subtree/attack_obstacle_in_path/syndicate + attack_behaviour = /datum/ai_behavior/attack_obstructions/syndicate + +/datum/ai_behavior/attack_obstructions/syndicate + action_cooldown = 1.2 SECONDS + /datum/ai_controller/basic_controller/syndicate/ranged planning_subtrees = list( /datum/ai_planning_subtree/simple_find_target, @@ -58,14 +65,7 @@ action_cooldown = 3 SECONDS required_distance = 1 -/datum/ai_controller/basic_controller/viscerator +/datum/ai_controller/basic_controller/syndicate/viscerator blackboard = list( BB_TARGETTING_DATUM = new /datum/targetting_datum/basic() ) - - ai_movement = /datum/ai_movement/dumb - idle_behavior = /datum/idle_behavior/idle_random_walk - planning_subtrees = list( - /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/basic_melee_attack_subtree/syndicate - ) diff --git a/code/modules/mob/living/basic/vermin/mouse.dm b/code/modules/mob/living/basic/vermin/mouse.dm index 8f6a7f8d687..a84c9f71a4f 100644 --- a/code/modules/mob/living/basic/vermin/mouse.dm +++ b/code/modules/mob/living/basic/vermin/mouse.dm @@ -406,21 +406,9 @@ planning_subtrees = list( /datum/ai_planning_subtree/pet_planning, /datum/ai_planning_subtree/simple_find_target, - /datum/ai_planning_subtree/attack_obstacle_in_path/rat, - /datum/ai_planning_subtree/basic_melee_attack_subtree/rat, + /datum/ai_planning_subtree/attack_obstacle_in_path, + /datum/ai_planning_subtree/basic_melee_attack_subtree, /datum/ai_planning_subtree/find_and_hunt_target/look_for_cheese, /datum/ai_planning_subtree/random_speech/mouse, /datum/ai_planning_subtree/find_and_hunt_target/look_for_cables, ) - -/datum/ai_planning_subtree/basic_melee_attack_subtree/rat - melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/rat - -/datum/ai_behavior/basic_melee_attack/rat - action_cooldown = 2 SECONDS - -/datum/ai_planning_subtree/attack_obstacle_in_path/rat - attack_behaviour = /datum/ai_behavior/attack_obstructions/rat - -/datum/ai_behavior/attack_obstructions/rat - action_cooldown = 2 SECONDS diff --git a/code/modules/mob/living/simple_animal/hostile/regalrat.dm b/code/modules/mob/living/simple_animal/hostile/regalrat.dm index c69c15f63e0..fe33bb85e29 100644 --- a/code/modules/mob/living/simple_animal/hostile/regalrat.dm +++ b/code/modules/mob/living/simple_animal/hostile/regalrat.dm @@ -400,7 +400,7 @@ command_feedback = "squeak!" // Frogs and roaches can squeak too it's fine pointed_reaction = "and squeaks aggressively" refuse_reaction = "quivers" - attack_behaviour = /datum/ai_behavior/basic_melee_attack/rat + attack_behaviour = /datum/ai_behavior/basic_melee_attack // Command you can give to a mouse to make it kill someone /datum/pet_command/point_targetting/attack/glockroach diff --git a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm b/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm deleted file mode 100644 index 56c308e7565..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/wumborian_fugu.dm +++ /dev/null @@ -1,177 +0,0 @@ -/** - * - * # Wumborian Fugu - * - * A strange alien creature capable of increasing its mass when threatened, - * giving it unmatched defensive capabilities temporarily. The rest of the - * time, it is quite fragile. - * - * On death, the "fugu gland" is dropped, which can be used on simple mobs - * to increase their size, health, strength, and lets them smash walls. - */ -/mob/living/simple_animal/hostile/asteroid/fugu - name = "wumborian fugu" - desc = "The wumborian fugu rapidly increases its body mass in order to ward off its prey. Great care should be taken to avoid it while it's in this state as it is nearly invincible, but it cannot maintain its form forever." - icon = 'icons/mob/simple/lavaland/64x64megafauna.dmi' - icon_state = "Fugu0" - icon_living = "Fugu0" - icon_aggro = "Fugu0" - icon_dead = "Fugu_dead" - icon_gib = "syndicate_gib" - health_doll_icon = "Fugu0" - mob_biotypes = MOB_ORGANIC|MOB_BEAST - mouse_opacity = MOUSE_OPACITY_ICON - move_to_delay = 5 - friendly_verb_continuous = "floats near" - friendly_verb_simple = "float near" - speak_emote = list("puffs") - vision_range = 5 - speed = 0 - maxHealth = 50 - health = 50 - pixel_x = -16 - base_pixel_x = -16 - harm_intent_damage = 5 - obj_damage = 0 - melee_damage_lower = 0 - melee_damage_upper = 0 - attack_verb_continuous = "chomps" - attack_verb_simple = "chomp" - attack_sound = 'sound/weapons/punch1.ogg' - attack_vis_effect = ATTACK_EFFECT_BITE - throw_message = "is avoided by the" - aggro_vision_range = 9 - mob_size = MOB_SIZE_SMALL - environment_smash = ENVIRONMENT_SMASH_NONE - gold_core_spawnable = HOSTILE_SPAWN - var/wumbo = 0 - var/inflate_cooldown = 0 - var/datum/action/innate/fugu/expand/E - loot = list(/obj/item/fugu_gland{layer = ABOVE_MOB_LAYER}) - -/mob/living/simple_animal/hostile/asteroid/fugu/Initialize(mapload) - . = ..() - E = new - E.Grant(src) - -/mob/living/simple_animal/hostile/asteroid/fugu/Destroy() - QDEL_NULL(E) - return ..() - -/mob/living/simple_animal/hostile/asteroid/fugu/Life(delta_time = SSMOBS_DT, times_fired) - if(!wumbo) - inflate_cooldown = max((inflate_cooldown - (0.5 * delta_time)), 0) - if(target && AIStatus == AI_ON) - E.Activate() - ..() - -/mob/living/simple_animal/hostile/asteroid/fugu/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - if(!forced && wumbo) - return FALSE - . = ..() - -/mob/living/simple_animal/hostile/asteroid/fugu/Aggro() - ..() - E.Activate() - -/datum/action/innate/fugu - button_icon = 'icons/mob/actions/actions_animal.dmi' - background_icon_state = "bg_fugu" - overlay_icon_state = "bg_fugu_border" - -/datum/action/innate/fugu/expand - name = "Inflate" - desc = "Temporarily increases your size, and makes you significantly more dangerous and tough! Do not bully the fugu!" - button_icon_state = "expand" - -/datum/action/innate/fugu/expand/Activate() - var/mob/living/simple_animal/hostile/asteroid/fugu/F = owner - if(F.wumbo) - to_chat(F, span_warning("YOU'RE ALREADY WUMBO!")) - return - if(F.inflate_cooldown) - to_chat(F, span_warning("You need time to gather your strength!")) - return - if(HAS_TRAIT(F, TRAIT_FUGU_GLANDED)) - to_chat(F, span_warning("Something is interfering with your growth!")) - return - F.wumbo = 1 - F.icon_state = "Fugu1" - F.obj_damage = 60 - F.melee_damage_lower = 15 - F.melee_damage_upper = 20 - F.harm_intent_damage = 0 - F.throw_message = "is absorbed by the girth of the" - F.retreat_distance = null - F.minimum_distance = 1 - F.move_to_delay = 6 - F.AddElement(/datum/element/wall_smasher) - F.mob_size = MOB_SIZE_LARGE - F.speed = 1 - addtimer(CALLBACK(F, TYPE_PROC_REF(/mob/living/simple_animal/hostile/asteroid/fugu, Deflate)), 100) - -// Why is half of this in an action and the other hand a proc on the mob? -/mob/living/simple_animal/hostile/asteroid/fugu/proc/Deflate() - if(wumbo) - SSmove_manager.stop_looping(src) - wumbo = 0 - icon_state = "Fugu0" - obj_damage = 0 - melee_damage_lower = 0 - melee_damage_upper = 0 - harm_intent_damage = 5 - throw_message = "is avoided by the" - retreat_distance = 9 - minimum_distance = 9 - move_to_delay = 2 - inflate_cooldown = 4 - RemoveElement(/datum/element/wall_smasher) - mob_size = MOB_SIZE_SMALL - speed = 0 - -/mob/living/simple_animal/hostile/asteroid/fugu/death(gibbed) - Deflate() - ..(gibbed) - -/obj/item/fugu_gland - name = "wumborian fugu gland" - desc = "The key to the wumborian fugu's ability to increase its mass arbitrarily, this disgusting remnant can apply the same effect to other creatures, giving them great strength." - icon = 'icons/obj/medical/organs/organs.dmi' - icon_state = "fugu_gland" - item_flags = NOBLUDGEON - w_class = WEIGHT_CLASS_NORMAL - layer = MOB_LAYER - var/static/list/fugu_blacklist - -/obj/item/fugu_gland/Initialize(mapload) - . = ..() - if(!fugu_blacklist) - fugu_blacklist = typecacheof(list( - /mob/living/simple_animal/hostile/guardian, - )) - -/obj/item/fugu_gland/afterattack(atom/target, mob/user, proximity_flag) - . = ..() - if(!proximity_flag) - return - if(!isanimal(target) || fugu_blacklist[target.type]) - return - var/mob/living/simple_animal/animal = target - - if(animal.stat == DEAD || HAS_TRAIT(animal, TRAIT_FAKEDEATH)) - to_chat(user, span_warning("[src] can only multiply strength, not grant it to the dead.")) - return - if(HAS_TRAIT(animal, TRAIT_FUGU_GLANDED)) - to_chat(user, span_warning("[animal] has already been affected by \a [src].")) - return - - ADD_TRAIT(animal, TRAIT_FUGU_GLANDED, type) - - animal.maxHealth *= 1.5 - animal.health = min(animal.maxHealth, animal.health * 1.5) - animal.melee_damage_lower = max((animal.melee_damage_lower * 2), 10) - animal.melee_damage_upper = max((animal.melee_damage_upper * 2), 10) - animal.transform *= 2 - animal.AddElement(/datum/element/wall_smasher, strength_flag = ENVIRONMENT_SMASH_RWALLS) - to_chat(user, span_info("You increase the size of [animal], giving [animal.p_them()] a surge of strength!")) - qdel(src) diff --git a/code/modules/movespeed/modifiers/status_effects.dm b/code/modules/movespeed/modifiers/status_effects.dm index a201a2363cd..d2120541cfa 100644 --- a/code/modules/movespeed/modifiers/status_effects.dm +++ b/code/modules/movespeed/modifiers/status_effects.dm @@ -28,3 +28,6 @@ /datum/movespeed_modifier/status_effect/brimdust_concussion multiplicative_slowdown = 1.5 + +/datum/movespeed_modifier/status_effect/inflated + multiplicative_slowdown = 3.5 diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 31937b23fb7..8bc18b68e64 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -78,7 +78,6 @@ /mob/living/simple_animal/hostile/asteroid/elite/legionnaire, /mob/living/simple_animal/hostile/asteroid/elite/legionnairehead, /mob/living/simple_animal/hostile/asteroid/elite/pandora, - /mob/living/simple_animal/hostile/asteroid/fugu, /mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/simple_animal/hostile/asteroid/goliath/beast, diff --git a/modular_skyrat/modules/planets/code/rockplanet.dm b/modular_skyrat/modules/planets/code/rockplanet.dm index 9b759cc72f7..31924c940c7 100644 --- a/modular_skyrat/modules/planets/code/rockplanet.dm +++ b/modular_skyrat/modules/planets/code/rockplanet.dm @@ -12,7 +12,7 @@ /mob/living/simple_animal/hostile/asteroid/goliath = 20, /mob/living/simple_animal/hostile/netherworld/mine_mob = 10, /mob/living/simple_animal/hostile/ooze/grapes/mine_mob = 20, - /mob/living/simple_animal/hostile/asteroid/fugu = 30, + /mob/living/basic/wumborian_fugu = 30, /mob/living/simple_animal/hostile/asteroid/basilisk = 40, /mob/living/simple_animal/hostile/asteroid/hivelord = 20, /mob/living/simple_animal/hostile/netherworld/migo/mine_mob = 10, diff --git a/tgstation.dme b/tgstation.dme index 47044aebc43..a324c44c306 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -813,10 +813,10 @@ #include "code\datums\ai\basic_mobs\basic_ai_behaviors\pick_up_item.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\run_away_from_target.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\step_towards_turf.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\targeted_mob_ability.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\targetting.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\travel_towards.dm" -#include "code\datums\ai\basic_mobs\basic_ai_behaviors\try_mob_ability.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\attack_obstacle_in_path.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\find_food.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\flee_target.dm" @@ -825,8 +825,9 @@ #include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_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\targetted_mob_ability.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\targeted_mob_ability.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\tipped_subtree.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\use_mob_ability.dm" #include "code\datums\ai\basic_mobs\pet_commands\fetch.dm" #include "code\datums\ai\basic_mobs\pet_commands\pet_command_planning.dm" #include "code\datums\ai\basic_mobs\pet_commands\pet_follow_friend.dm" @@ -3863,6 +3864,10 @@ #include "code\modules\mob\living\basic\space_fauna\netherworld\creature.dm" #include "code\modules\mob\living\basic\space_fauna\netherworld\migo.dm" #include "code\modules\mob\living\basic\space_fauna\statue\statue.dm" +#include "code\modules\mob\living\basic\space_fauna\wumborian_fugu\fugu_gland.dm" +#include "code\modules\mob\living\basic\space_fauna\wumborian_fugu\inflation.dm" +#include "code\modules\mob\living\basic\space_fauna\wumborian_fugu\wumborian_ai.dm" +#include "code\modules\mob\living\basic\space_fauna\wumborian_fugu\wumborian_fugu.dm" #include "code\modules\mob\living\basic\syndicate\syndicate.dm" #include "code\modules\mob\living\basic\syndicate\syndicate_ai.dm" #include "code\modules\mob\living\basic\vermin\axolotl.dm" @@ -4098,7 +4103,6 @@ #include "code\modules\mob\living\simple_animal\hostile\vatbeast.dm" #include "code\modules\mob\living\simple_animal\hostile\venus_human_trap.dm" #include "code\modules\mob\living\simple_animal\hostile\wizard.dm" -#include "code\modules\mob\living\simple_animal\hostile\wumborian_fugu.dm" #include "code\modules\mob\living\simple_animal\hostile\zombie.dm" #include "code\modules\mob\living\simple_animal\hostile\bosses\boss.dm" #include "code\modules\mob\living\simple_animal\hostile\bosses\paperwizard.dm" diff --git a/tools/UpdatePaths/Scripts/73415_simple_fugu_to_basic.txt b/tools/UpdatePaths/Scripts/73415_simple_fugu_to_basic.txt new file mode 100644 index 00000000000..a884cb3d370 --- /dev/null +++ b/tools/UpdatePaths/Scripts/73415_simple_fugu_to_basic.txt @@ -0,0 +1,3 @@ +# replace simple animal fugu with basic animal + +/mob/living/simple_animal/hostile/asteroid/fugu : /mob/living/basic/wumborian_fugu{@OLD}