Basic Mob Raw Prophet (#78733)

## About The Pull Request

Might as well start on these now, should be easy enough.
The Raw Prophet actually comes with a couple of new components.

The `wheel` element is sort of like the `waddling` element in that you
give it to any movable atom to have it move like a raw prophet.

![dreamseeker_3qacWEKYQQ](https://github.com/tgstation/tgstation/assets/7483112/d5e1b0b9-79f7-4272-9c88-a21fee049e5f)
Whee!

The focused attacker component can be attached to any mob or item and
causes it to escalate its damage every time you attack the same target.
I'll be honest I consistently forget that the Raw Prophet does this.

The ones in the Ruin have the blinding gaze attack inherited from
Watchers instead of the point-target Blind spell in order to ensure that
you can actually "dodge" it.
I tried to make it jaunt if it got stuck but ran into too many problems.
Another time.

## Why It's Good For The Game

I do this to relax now.

## Changelog

🆑
refactor: Raw Prophets now use the basic mob framework. Please report
any unusual behaviour.
/🆑
This commit is contained in:
Jacquerel
2023-10-03 21:26:50 +01:00
committed by GitHub
parent 5839c02586
commit c78211835d
13 changed files with 216 additions and 117 deletions

View File

@@ -1307,10 +1307,7 @@
/turf/open/floor/iron/dark,
/area/ruin/space/has_grav/dangerous_research/lab)
"re" = (
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet{
AIStatus = 1;
stop_automated_movement = 0
},
/mob/living/basic/heretic_summon/raw_prophet/ruins,
/turf/open/floor/plating/rust,
/area/ruin/space/has_grav/dangerous_research/medical)
"ri" = (
@@ -2020,10 +2017,7 @@
dir = 1
},
/obj/effect/decal/cleanable/blood/footprints,
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet{
AIStatus = 1;
stop_automated_movement = 0
},
/mob/living/basic/heretic_summon/raw_prophet/ruins,
/obj/effect/turf_decal/tile/blue/fourcorners,
/turf/open/floor/iron/white,
/area/ruin/space/has_grav/dangerous_research/medical)

View File

@@ -0,0 +1,71 @@
/**
* Increases our attack damage every time we attack the same target
* Not compatible with any other component or status effect which modifies attack damage
*/
/datum/component/focused_attacker
/// Amount of damage we gain per attack
var/gain_per_attack
/// Maximum amount by which we can increase our attack power
var/maximum_gain
/// The last thing we attacked
var/atom/last_target
/datum/component/focused_attacker/Initialize(gain_per_attack = 5, maximum_gain = 25)
. = ..()
if (!isliving(parent) && !isitem(parent))
return COMPONENT_INCOMPATIBLE
src.maximum_gain = maximum_gain
src.gain_per_attack = gain_per_attack
/datum/component/focused_attacker/Destroy(force, silent)
if (!isnull(last_target))
UnregisterSignal(last_target, COMSIG_QDELETING)
return ..()
/datum/component/focused_attacker/RegisterWithParent()
if (isliving(parent))
RegisterSignals(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HUMAN_MELEE_UNARMED_ATTACK), PROC_REF(pre_mob_attack))
else
RegisterSignal(parent, COMSIG_ITEM_PRE_ATTACK, PROC_REF(pre_item_attack))
/datum/component/focused_attacker/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_LIVING_UNARMED_ATTACK, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_ITEM_PRE_ATTACK))
/// Before a mob attacks, try increasing its attack power
/datum/component/focused_attacker/proc/pre_mob_attack(mob/living/attacker, atom/target)
SIGNAL_HANDLER
if (isnull(target) || isturf(target))
return
if (target == last_target)
if (attacker.melee_damage_lower - initial(attacker.melee_damage_lower) >= maximum_gain)
return
attacker.melee_damage_lower += gain_per_attack
attacker.melee_damage_upper += gain_per_attack
return
attacker.melee_damage_lower = initial(attacker.melee_damage_lower)
attacker.melee_damage_upper = initial(attacker.melee_damage_upper)
register_new_target(target)
/// Before an item attacks, try increasing its attack power
/datum/component/focused_attacker/proc/pre_item_attack(obj/item/weapon, atom/target, mob/user, params)
SIGNAL_HANDLER
if (target == last_target)
if (weapon.force - initial(weapon.force) < maximum_gain)
weapon.force += gain_per_attack
return
weapon.force = initial(weapon.force)
register_new_target(target)
/// Register a new target
/datum/component/focused_attacker/proc/register_new_target(atom/target)
if (!isnull(last_target))
UnregisterSignal(last_target, COMSIG_QDELETING)
last_target = target
RegisterSignal(target, COMSIG_QDELETING, PROC_REF(on_target_deleted))
/// Drop our target ref on deletion
/datum/component/focused_attacker/proc/on_target_deleted(target)
SIGNAL_HANDLER
last_target = null

View File

@@ -4,30 +4,21 @@
. = ..()
if(!ismovable(target))
return ELEMENT_INCOMPATIBLE
if(isliving(target))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(LivingWaddle))
else
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(Waddle))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(Waddle))
/datum/element/waddling/Detach(datum/source)
. = ..()
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
/datum/element/waddling/proc/LivingWaddle(mob/living/target, atom/oldloc, direction, forced)
/datum/element/waddling/proc/Waddle(atom/movable/moved, atom/oldloc, direction, forced)
SIGNAL_HANDLER
if(forced || target.incapacitated() || target.body_position == LYING_DOWN || CHECK_MOVE_LOOP_FLAGS(target, MOVEMENT_LOOP_OUTSIDE_CONTROL))
if(forced || CHECK_MOVE_LOOP_FLAGS(moved, MOVEMENT_LOOP_OUTSIDE_CONTROL))
return
waddling_animation(target)
/datum/element/waddling/proc/Waddle(atom/movable/target, atom/oldloc, direction, forced)
SIGNAL_HANDLER
if(forced || CHECK_MOVE_LOOP_FLAGS(target, MOVEMENT_LOOP_OUTSIDE_CONTROL))
return
waddling_animation(target)
if(isliving(moved))
var/mob/living/living_moved = moved
if (living_moved.incapacitated() || living_moved.body_position == LYING_DOWN)
return
waddling_animation(moved)
/datum/element/waddling/proc/waddling_animation(atom/movable/target)
animate(target, pixel_z = 4, time = 0)

View File

@@ -0,0 +1,28 @@
/// Element which spins you as you move
/datum/element/wheel
/datum/element/wheel/Attach(datum/target)
. = ..()
if(!ismovable(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
/datum/element/wheel/Detach(datum/source)
. = ..()
UnregisterSignal(source, COMSIG_MOVABLE_MOVED)
/datum/element/wheel/proc/on_moved(atom/movable/moved, atom/oldloc, direction, forced)
SIGNAL_HANDLER
if(forced || CHECK_MOVE_LOOP_FLAGS(moved, MOVEMENT_LOOP_OUTSIDE_CONTROL))
return
if(isliving(moved))
var/mob/living/living_moved = moved
if (living_moved.incapacitated() || living_moved.body_position == LYING_DOWN)
return
var/rotation_degree = (360 / 3)
if(direction & SOUTHWEST)
rotation_degree *= -1
var/matrix/to_turn = matrix(moved.transform)
to_turn = turn(moved.transform, rotation_degree)
animate(moved, transform = to_turn, time = 0.1 SECONDS, flags = ANIMATION_PARALLEL)

View File

@@ -251,7 +251,7 @@
/obj/effect/decal/cleanable/blood = 1,
/obj/item/bodypart/arm/left = 1,
)
mob_to_summon = /mob/living/simple_animal/hostile/heretic_summon/raw_prophet
mob_to_summon = /mob/living/basic/heretic_summon/raw_prophet
cost = 1
route = PATH_FLESH
poll_ignore_define = POLL_IGNORE_RAW_PROPHET

View File

@@ -6,7 +6,7 @@
cooldown_time = 20 SECONDS
die_with_shapeshifted_form = FALSE
possible_shapes = list(
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet,
/mob/living/basic/heretic_summon/raw_prophet/ascended,
/mob/living/simple_animal/hostile/heretic_summon/rust_spirit,
/mob/living/simple_animal/hostile/heretic_summon/ash_spirit,
/mob/living/simple_animal/hostile/heretic_summon/stalker,

View File

@@ -1,16 +1,14 @@
/mob/living/basic/heretic_summon
name = "Eldritch Demon"
real_name = "Eldritch Demon"
desc = "A horror from beyond this realm."
desc = "A horror from beyond this realm, summoned by bad code."
icon = 'icons/mob/nonhuman-player/eldritch_mobs.dmi'
faction = list(FACTION_HERETIC)
basic_mob_flags = DEL_ON_DEATH
gender = NEUTER
mob_biotypes = NONE
unsuitable_atmos_damage = 0
unsuitable_cold_damage = 0
unsuitable_heat_damage = 0
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)
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
speed = 0
melee_attack_cooldown = CLICK_CD_MELEE
@@ -20,8 +18,8 @@
response_help_simple = "think better of touching"
response_disarm_continuous = "flails at"
response_disarm_simple = "flail at"
response_harm_continuous = "reaps"
response_harm_simple = "tears"
response_harm_continuous = "rips"
response_harm_simple = "tear"
death_message = "implodes into itself."
combat_mode = TRUE

View File

@@ -0,0 +1,96 @@
/**
* A funny little rolling guy who is great at scouting.
* It can see through walls, jaunt, and create a psychic network to report its findings.
* It can blind people to make a getaway, but also get stronger if it attacks the same target consecutively.
*/
/mob/living/basic/heretic_summon/raw_prophet
name = "Raw Prophet"
real_name = "Raw Prophet"
desc = "An abomination stitched together from a few severed arms and one swollen, orphaned eye."
icon_state = "raw_prophet"
icon_living = "raw_prophet"
status_flags = CANPUSH
melee_damage_lower = 5
melee_damage_upper = 10
maxHealth = 65
health = 65
sight = SEE_MOBS|SEE_OBJS|SEE_TURFS
/// Some ability we use to make people go blind
var/blind_action_type = /datum/action/cooldown/spell/pointed/blind/eldritch
/mob/living/basic/heretic_summon/raw_prophet/Initialize(mapload)
. = ..()
AddElement(/datum/element/wheel)
var/static/list/body_parts = list(/obj/effect/gibspawner/human, /obj/item/bodypart/arm/left, /obj/item/organ/internal/eyes)
AddElement(/datum/element/death_drops, body_parts)
AddComponent(/datum/component/focused_attacker)
var/on_link_message = "You feel something new enter your sphere of mind... \
You hear whispers of people far away, screeches of horror and a huming of welcome to [src]'s Mansus Link."
var/on_unlink_message = "Your mind shatters as [src]'s Mansus Link leaves your mind."
AddComponent( \
/datum/component/mind_linker/active_linking, \
network_name = "Mansus Link", \
chat_color = "#568b00", \
post_unlink_callback = CALLBACK(src, PROC_REF(after_unlink)), \
speech_action_background_icon_state = "bg_heretic", \
speech_action_overlay_state = "bg_heretic_border", \
linker_action_path = /datum/action/cooldown/spell/pointed/manse_link, \
link_message = on_link_message, \
unlink_message = on_unlink_message, \
)
// We don't use these for AI so we can just repeat the same adding process
var/static/list/add_abilities = list(
/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/long,
/datum/action/cooldown/spell/list_target/telepathy/eldritch,
/datum/action/innate/expand_sight,
)
for (var/ability_type in add_abilities)
var/datum/action/new_action = new ability_type(src)
new_action.Grant(src)
var/datum/action/cooldown/blind = new blind_action_type(src)
blind.Grant(src)
ai_controller?.set_blackboard_key(BB_TARGETTED_ACTION, blind)
/*
* Callback for the mind_linker component.
* Stuns people who are ejected from the network.
*/
/mob/living/basic/heretic_summon/raw_prophet/proc/after_unlink(mob/living/unlinked_mob)
if(QDELETED(unlinked_mob) || unlinked_mob.stat == DEAD)
return
INVOKE_ASYNC(unlinked_mob, TYPE_PROC_REF(/mob, emote), "scream")
unlinked_mob.AdjustParalyzed(0.5 SECONDS) //micro stun
/mob/living/basic/heretic_summon/raw_prophet/melee_attack(atom/target, list/modifiers, ignore_cooldown)
SpinAnimation(speed = 5, loops = 1)
if (target == src)
return
return ..()
/// Variant raw prophet used by eldritch transformation with more base attack power
/mob/living/basic/heretic_summon/raw_prophet/ascended
melee_damage_lower = 15
melee_damage_upper = 20
/// NPC variant with a less bullshit ability
/mob/living/basic/heretic_summon/raw_prophet/ruins
ai_controller = /datum/ai_controller/basic_controller/raw_prophet
blind_action_type = /datum/action/cooldown/mob_cooldown/watcher_gaze
/// Walk and attack people, blind them when we can
/datum/ai_controller/basic_controller/raw_prophet
blackboard = list(
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic,
)
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,
/datum/ai_planning_subtree/attack_obstacle_in_path,
/datum/ai_planning_subtree/basic_melee_attack_subtree,
)

View File

@@ -12,6 +12,7 @@
check_flags = AB_CHECK_CONSCIOUS | AB_CHECK_INCAPACITATED
click_to_activate = FALSE
shared_cooldown = NONE
melee_cooldown_time = 0 SECONDS
/// At what range do we check for vision?
var/effect_radius = 7
/// How long does it take to play our various animation stages

View File

@@ -43,89 +43,6 @@
var/datum/action/cooldown/spell/new_spell = new spell(src)
new_spell.Grant(src)
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet
name = "Raw Prophet"
real_name = "Raw Prophet"
desc = "An abomination stitched together from a few severed arms and one lost eye."
icon_state = "raw_prophet"
icon_living = "raw_prophet"
status_flags = CANPUSH
melee_damage_lower = 5
melee_damage_upper = 10
maxHealth = 65
health = 65
sight = SEE_MOBS|SEE_OBJS|SEE_TURFS
loot = list(/obj/effect/gibspawner/human, /obj/item/bodypart/arm/left, /obj/item/organ/internal/eyes)
actions_to_add = list(
/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash/long,
/datum/action/cooldown/spell/list_target/telepathy/eldritch,
/datum/action/cooldown/spell/pointed/blind/eldritch,
/datum/action/innate/expand_sight,
)
/// A weakref to the last target we smacked. Hitting targets consecutively does more damage.
var/datum/weakref/last_target
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet/Initialize(mapload)
. = ..()
var/on_link_message = "You feel something new enter your sphere of mind... \
You hear whispers of people far away, screeches of horror and a huming of welcome to [src]'s Mansus Link."
var/on_unlink_message = "Your mind shatters as [src]'s Mansus Link leaves your mind."
AddComponent( \
/datum/component/mind_linker/active_linking, \
network_name = "Mansus Link", \
chat_color = "#568b00", \
post_unlink_callback = CALLBACK(src, PROC_REF(after_unlink)), \
speech_action_background_icon_state = "bg_heretic", \
speech_action_overlay_state = "bg_heretic_border", \
linker_action_path = /datum/action/cooldown/spell/pointed/manse_link, \
link_message = on_link_message, \
unlink_message = on_unlink_message, \
)
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet/attack_animal(mob/living/simple_animal/user, list/modifiers)
if(user == src) // Easy to hit yourself + very fragile = accidental suicide, prevent that
return
return ..()
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet/AttackingTarget(atom/attacked_target)
if(WEAKREF(attacked_target) == last_target)
melee_damage_lower = min(melee_damage_lower + 5, 30)
melee_damage_upper = min(melee_damage_upper + 5, 35)
else
melee_damage_lower = initial(melee_damage_lower)
melee_damage_upper = initial(melee_damage_upper)
. = ..()
if(!.)
return
SpinAnimation(5, 1)
last_target = WEAKREF(attacked_target)
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE)
. = ..()
var/rotation_degree = (360 / 3)
if(movement_dir & WEST || movement_dir & SOUTH)
rotation_degree *= -1
var/matrix/to_turn = matrix(transform)
to_turn = turn(transform, rotation_degree)
animate(src, transform = to_turn, time = 0.1 SECONDS)
/*
* Callback for the mind_linker component.
* Stuns people who are ejected from the network.
*/
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet/proc/after_unlink(mob/living/unlinked_mob)
if(QDELETED(unlinked_mob) || unlinked_mob.stat == DEAD)
return
INVOKE_ASYNC(unlinked_mob, TYPE_PROC_REF(/mob, emote), "scream")
unlinked_mob.AdjustParalyzed(0.5 SECONDS) //micro stun
// What if we took a linked list... But made it a mob?
/// The "Terror of the Night" / Armsy, a large worm made of multiple bodyparts that occupies multiple tiles
/mob/living/simple_animal/hostile/heretic_summon/armsy

View File

@@ -108,7 +108,6 @@
/mob/living/simple_animal/hostile/heretic_summon/armsy/prime,
/mob/living/simple_animal/hostile/heretic_summon/ash_spirit,
/mob/living/simple_animal/hostile/heretic_summon/maid_in_the_mirror,
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet,
/mob/living/simple_animal/hostile/heretic_summon/rust_spirit,
/mob/living/simple_animal/hostile/heretic_summon/stalker,
/mob/living/simple_animal/hostile/illusion,

View File

@@ -986,6 +986,7 @@
#include "code\datums\components\faction_granter.dm"
#include "code\datums\components\fertile_egg.dm"
#include "code\datums\components\fishing_spot.dm"
#include "code\datums\components\focused_attacker.dm"
#include "code\datums\components\food_storage.dm"
#include "code\datums\components\force_move.dm"
#include "code\datums\components\fov_handler.dm"
@@ -1374,6 +1375,7 @@
#include "code\datums\elements\weapon_description.dm"
#include "code\datums\elements\weather_listener.dm"
#include "code\datums\elements\web_walker.dm"
#include "code\datums\elements\wheel.dm"
#include "code\datums\elements\decals\_decal.dm"
#include "code\datums\elements\decals\blood.dm"
#include "code\datums\elements\food\dunkable.dm"
@@ -4362,6 +4364,7 @@
#include "code\modules\mob\living\basic\farm_animals\cow\cow_wisdom.dm"
#include "code\modules\mob\living\basic\heretic\fire_shark.dm"
#include "code\modules\mob\living\basic\heretic\heretic_summon.dm"
#include "code\modules\mob\living\basic\heretic\raw_prophet.dm"
#include "code\modules\mob\living\basic\heretic\star_gazer.dm"
#include "code\modules\mob\living\basic\icemoon\ice_whelp\ice_whelp.dm"
#include "code\modules\mob\living\basic\icemoon\ice_whelp\ice_whelp_abilities.dm"

View File

@@ -0,0 +1 @@
/mob/living/simple_animal/hostile/heretic_summon/raw_prophet : /mob/living/basic/heretic_summon/raw_prophet/ruins{@OLD;AIStatus=@SKIP;stop_automated_movement=@SKIP}