mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-29 16:18:01 +01:00
Basic Mobs: the cooler simple mobs that run on datum AI. (With reworked cockroach AI as proof of concept) (#60694)
Simple_animals / mobs are the biggest lie in this code-base. They're far from simple and have an extreme god-object problem. Especially when you get to /hostile, where there is so many procs, vars, and what not, that you can't make any interesting additions without snowflaking the hell out of the code. This PR hopes to help kill this problem by introducing a new /living subtype, /living/basic. The idea of this refactor is to slowly start moving all old simple_animals to this new system, moving over behaviors like charging and more extravagant mobs like megafauna over bit by bit similar to how newfood was implemented. One of the other big goals of this refactor is to move many of the fringe simple animal behaviors into either AI datums, or components/elements. (Some of which still needs to be done in this PR). As a proof of concept, I created the base mob/living/basic, and moved cockroaches over to the system. Since cockroaches have both a passive, melee and ranged mob. This PR does slightly affect balance as the behavior isn't 1-on-1 due to it no longer running on the janky /hostile behavior, but I tried to keep the effects to a minimum, and the glockroach and hauberoach are not spawnable through many means as far as I know.
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
///Simple animals 2.0, This time, let's really try to keep it simple. This basetype should purely be used as a base-level for implementing simplified behaviours for things such as damage and attacks. Everything else should be in components or AI behaviours.
|
||||
/mob/living/basic
|
||||
name = "basic mob"
|
||||
icon = 'icons/mob/animal.dmi'
|
||||
health = 20
|
||||
maxHealth = 20
|
||||
gender = PLURAL
|
||||
living_flags = MOVES_ON_ITS_OWN
|
||||
status_flags = CANPUSH
|
||||
|
||||
var/basic_mob_flags = NONE
|
||||
|
||||
///Defines how fast the basic mob can move. This is a multiplier
|
||||
var/speed = 1
|
||||
///How much stamina the mob recovers per second
|
||||
var/stamina_recovery = 5
|
||||
|
||||
///how much damage this basic mob does to objects, if any.
|
||||
var/obj_damage = 0
|
||||
///How much armour they ignore, as a flat reduction from the targets armour value.
|
||||
var/armour_penetration = 0
|
||||
///Damage type of a simple mob's melee attack, should it do damage.
|
||||
var/melee_damage_type = BRUTE
|
||||
///How much wounding power it has
|
||||
var/wound_bonus = CANT_WOUND
|
||||
///How much bare wounding power it has
|
||||
var/bare_wound_bonus = 0
|
||||
///If the attacks from this are sharp
|
||||
var/sharpness = NONE
|
||||
|
||||
/// Sound played when the critter attacks.
|
||||
var/attack_sound
|
||||
/// Override for the visual attack effect shown on 'do_attack_animation()'.
|
||||
var/attack_vis_effect
|
||||
///Played when someone punches the creature.
|
||||
var/attacked_sound = "punch" //This should be an element
|
||||
|
||||
///What kind of objects this mob can smash.
|
||||
var/environment_smash = ENVIRONMENT_SMASH_NONE
|
||||
|
||||
/// 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)
|
||||
|
||||
///Verbs used for speaking e.g. "Says" or "Chitters". This can be elementized
|
||||
var/list/speak_emote = list()
|
||||
|
||||
///When someone interacts with the simple animal.
|
||||
///Help-intent verb in present continuous tense.
|
||||
var/response_help_continuous = "pokes"
|
||||
///Help-intent verb in present simple tense.
|
||||
var/response_help_simple = "poke"
|
||||
///Disarm-intent verb in present continuous tense.
|
||||
var/response_disarm_continuous = "shoves"
|
||||
///Disarm-intent verb in present simple tense.
|
||||
var/response_disarm_simple = "shove"
|
||||
///Harm-intent verb in present continuous tense.
|
||||
var/response_harm_continuous = "hits"
|
||||
///Harm-intent verb in present simple tense.
|
||||
var/response_harm_simple = "hit"
|
||||
|
||||
///Basic mob's own attacks verbs,
|
||||
///Attacking verb in present continuous tense.
|
||||
var/attack_verb_continuous = "attacks"
|
||||
///Attacking verb in present simple tense.
|
||||
var/attack_verb_simple = "attack"
|
||||
///Attacking, but without damage, verb in present continuous tense.
|
||||
var/friendly_verb_continuous = "nuzzles"
|
||||
///Attacking, but without damage, verb in present simple tense.
|
||||
var/friendly_verb_simple = "nuzzle"
|
||||
|
||||
////////THIS SECTION COULD BE ITS OWN ELEMENT
|
||||
///Icon to use
|
||||
var/icon_living = ""
|
||||
///Icon when the animal is dead. Don't use animated icons for this.
|
||||
var/icon_dead = ""
|
||||
///We only try to show a gibbing animation if this exists.
|
||||
var/icon_gib = null
|
||||
///Flip the sprite upside down on death. Mostly here for things lacking custom dead sprites.
|
||||
var/flip_on_death = FALSE
|
||||
|
||||
///If the mob can be spawned with a gold slime core. HOSTILE_SPAWN are spawned with plasma, FRIENDLY_SPAWN are spawned with blood.
|
||||
var/gold_core_spawnable = NO_SPAWN
|
||||
///Sentience type, for slime potions. SHOULD BE AN ELEMENT BUT I DONT CARE ABOUT IT FOR NOW
|
||||
var/sentience_type = SENTIENCE_ORGANIC
|
||||
|
||||
|
||||
|
||||
/mob/living/basic/Initialize(mapload)
|
||||
. = ..()
|
||||
|
||||
if(gender == PLURAL)
|
||||
gender = pick(MALE,FEMALE)
|
||||
|
||||
if(!real_name)
|
||||
real_name = name
|
||||
|
||||
if(!loc)
|
||||
stack_trace("Basic mob being instantiated in nullspace")
|
||||
|
||||
update_basic_mob_varspeed()
|
||||
|
||||
if(speak_emote)
|
||||
speak_emote = string_list(speak_emote)
|
||||
|
||||
/mob/living/basic/Life(delta_time = SSMOBS_DT, times_fired)
|
||||
. = ..()
|
||||
///Automatic stamina re-gain
|
||||
if(staminaloss > 0)
|
||||
adjustStaminaLoss(-stamina_recovery * delta_time, FALSE, TRUE)
|
||||
|
||||
/mob/living/basic/say_mod(input, list/message_mods = list())
|
||||
if(length(speak_emote))
|
||||
verb_say = pick(speak_emote)
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/death(gibbed)
|
||||
. = ..()
|
||||
if(basic_mob_flags & DEL_ON_DEATH)
|
||||
qdel(src)
|
||||
else
|
||||
health = 0
|
||||
icon_state = icon_dead
|
||||
if(flip_on_death)
|
||||
transform = transform.Turn(180)
|
||||
set_density(FALSE)
|
||||
|
||||
/mob/living/basic/proc/melee_attack(atom/target)
|
||||
src.face_atom(target)
|
||||
if(SEND_SIGNAL(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, target) & COMPONENT_HOSTILE_NO_ATTACK)
|
||||
return FALSE //but more importantly return before attack_animal called
|
||||
var/result = target.attack_basic_mob(src)
|
||||
SEND_SIGNAL(src, COMSIG_HOSTILE_POST_ATTACKINGTARGET, target, result)
|
||||
return result
|
||||
|
||||
/mob/living/basic/proc/set_varspeed(var_value)
|
||||
speed = var_value
|
||||
update_basic_mob_varspeed()
|
||||
|
||||
/mob/living/basic/proc/update_basic_mob_varspeed()
|
||||
if(speed == 0)
|
||||
remove_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed)
|
||||
add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/simplemob_varspeed, multiplicative_slowdown = speed)
|
||||
SEND_SIGNAL(src, POST_BASIC_MOB_UPDATE_VARSPEED)
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/mob/living/basic/attack_hand(mob/living/carbon/human/user, list/modifiers)
|
||||
// so that martial arts don't double dip
|
||||
if (..())
|
||||
return TRUE
|
||||
|
||||
if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
||||
user.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
playsound(src, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
|
||||
var/shove_dir = get_dir(user, src)
|
||||
if(!Move(get_step(src, shove_dir), shove_dir))
|
||||
log_combat(user, src, "shoved", "failing to move it")
|
||||
user.visible_message(span_danger("[user.name] shoves [src]!"),
|
||||
span_danger("You shove [src]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, list(src))
|
||||
to_chat(src, span_userdanger("You're shoved by [user.name]!"))
|
||||
return TRUE
|
||||
log_combat(user, src, "shoved", "pushing it")
|
||||
user.visible_message(span_danger("[user.name] shoves [src], pushing [p_them()]!"),
|
||||
span_danger("You shove [src], pushing [p_them()]!"), span_hear("You hear aggressive shuffling!"), COMBAT_MESSAGE_RANGE, list(src))
|
||||
to_chat(src, span_userdanger("You're pushed by [user.name]!"))
|
||||
return TRUE
|
||||
|
||||
if(!user.combat_mode)
|
||||
if (stat == DEAD)
|
||||
return
|
||||
visible_message(span_notice("[user] [response_help_continuous] [src]."), \
|
||||
span_notice("[user] [response_help_continuous] you."), null, null, user)
|
||||
to_chat(user, span_notice("You [response_help_simple] [src]."))
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
|
||||
else
|
||||
if(HAS_TRAIT(user, TRAIT_PACIFISM))
|
||||
to_chat(user, span_warning("You don't want to hurt [src]!"))
|
||||
return
|
||||
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
|
||||
visible_message(span_danger("[user] [response_harm_continuous] [src]!"),\
|
||||
span_userdanger("[user] [response_harm_continuous] you!"), null, COMBAT_MESSAGE_RANGE, user)
|
||||
to_chat(user, span_danger("You [response_harm_simple] [src]!"))
|
||||
playsound(loc, attacked_sound, 25, TRUE, -1)
|
||||
|
||||
var/damage = rand(user.dna.species.punchdamagelow, user.dna.species.punchdamagehigh)
|
||||
|
||||
attack_threshold_check(damage)
|
||||
log_combat(user, src, "attacked")
|
||||
updatehealth()
|
||||
return TRUE
|
||||
|
||||
/mob/living/basic/attack_hulk(mob/living/carbon/human/user)
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
playsound(loc, "punch", 25, TRUE, -1)
|
||||
visible_message(span_danger("[user] punches [src]!"), \
|
||||
span_userdanger("You're punched by [user]!"), null, COMBAT_MESSAGE_RANGE, user)
|
||||
to_chat(user, span_danger("You punch [src]!"))
|
||||
adjustBruteLoss(15)
|
||||
|
||||
/mob/living/basic/attack_paw(mob/living/carbon/human/user, list/modifiers)
|
||||
if(..()) //successful monkey bite.
|
||||
if(stat != DEAD)
|
||||
var/damage = rand(1, 3)
|
||||
attack_threshold_check(damage)
|
||||
return 1
|
||||
if (!user.combat_mode)
|
||||
if (health > 0)
|
||||
visible_message(span_notice("[user.name] [response_help_continuous] [src]."), \
|
||||
span_notice("[user.name] [response_help_continuous] you."), null, COMBAT_MESSAGE_RANGE, user)
|
||||
to_chat(user, span_notice("You [response_help_simple] [src]."))
|
||||
playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1)
|
||||
|
||||
|
||||
/mob/living/basic/attack_alien(mob/living/carbon/alien/humanoid/user, list/modifiers)
|
||||
if(..()) //if harm or disarm intent.
|
||||
if(LAZYACCESS(modifiers, RIGHT_CLICK))
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, TRUE, -1)
|
||||
visible_message(span_danger("[user] [response_disarm_continuous] [name]!"), \
|
||||
span_userdanger("[user] [response_disarm_continuous] you!"), null, COMBAT_MESSAGE_RANGE, user)
|
||||
to_chat(user, span_danger("You [response_disarm_simple] [name]!"))
|
||||
log_combat(user, src, "disarmed")
|
||||
else
|
||||
var/damage = rand(15, 30)
|
||||
visible_message(span_danger("[user] slashes at [src]!"), \
|
||||
span_userdanger("You're slashed at by [user]!"), null, COMBAT_MESSAGE_RANGE, user)
|
||||
to_chat(user, span_danger("You slash at [src]!"))
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
|
||||
attack_threshold_check(damage)
|
||||
log_combat(user, src, "attacked")
|
||||
return 1
|
||||
|
||||
/mob/living/basic/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
. = ..()
|
||||
if(. && stat != DEAD) //successful larva bite
|
||||
var/damage = rand(5, 10)
|
||||
. = attack_threshold_check(damage)
|
||||
if(.)
|
||||
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
|
||||
|
||||
/mob/living/basic/attack_basic_mob(mob/living/basic/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
|
||||
return attack_threshold_check(damage, user.melee_damage_type)
|
||||
|
||||
/mob/living/basic/attack_animal(mob/living/simple_animal/user, list/modifiers)
|
||||
. = ..()
|
||||
if(.)
|
||||
var/damage = rand(user.melee_damage_lower, user.melee_damage_upper)
|
||||
return attack_threshold_check(damage, user.melee_damage_type)
|
||||
|
||||
/mob/living/basic/attack_slime(mob/living/simple_animal/slime/M)
|
||||
if(..()) //successful slime attack
|
||||
var/damage = rand(15, 25)
|
||||
if(M.is_adult)
|
||||
damage = rand(20, 35)
|
||||
return attack_threshold_check(damage)
|
||||
|
||||
/mob/living/basic/attack_drone(mob/living/simple_animal/drone/M)
|
||||
if(M.combat_mode) //No kicking dogs even as a rogue drone. Use a weapon.
|
||||
return
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/proc/attack_threshold_check(damage, damagetype = BRUTE, armorcheck = MELEE, actuallydamage = TRUE)
|
||||
var/temp_damage = damage
|
||||
if(!damage_coeff[damagetype])
|
||||
temp_damage = 0
|
||||
else
|
||||
temp_damage *= damage_coeff[damagetype]
|
||||
|
||||
if(actuallydamage)
|
||||
apply_damage(damage, damagetype, null, getarmor(null, armorcheck))
|
||||
return TRUE
|
||||
|
||||
/mob/living/basic/bullet_act(obj/projectile/Proj, def_zone, piercing_hit = FALSE)
|
||||
apply_damage(Proj.damage, Proj.damage_type)
|
||||
Proj.on_hit(src, 0, piercing_hit)
|
||||
return BULLET_ACT_HIT
|
||||
|
||||
/mob/living/basic/ex_act(severity, target, origin)
|
||||
if(origin && istype(origin, /datum/spacevine_mutation) && isvineimmune(src))
|
||||
return FALSE
|
||||
|
||||
. = ..()
|
||||
if(QDELETED(src))
|
||||
return
|
||||
var/bomb_armor = getarmor(null, BOMB)
|
||||
switch (severity)
|
||||
if (EXPLODE_DEVASTATE)
|
||||
if(prob(bomb_armor))
|
||||
adjustBruteLoss(500)
|
||||
else
|
||||
gib()
|
||||
return
|
||||
if (EXPLODE_HEAVY)
|
||||
var/bloss = 60
|
||||
if(prob(bomb_armor))
|
||||
bloss = bloss / 1.5
|
||||
adjustBruteLoss(bloss)
|
||||
|
||||
if (EXPLODE_LIGHT)
|
||||
var/bloss = 30
|
||||
if(prob(bomb_armor))
|
||||
bloss = bloss / 1.5
|
||||
adjustBruteLoss(bloss)
|
||||
|
||||
/mob/living/basic/blob_act(obj/structure/blob/B)
|
||||
adjustBruteLoss(20)
|
||||
return
|
||||
|
||||
/mob/living/basic/do_attack_animation(atom/A, visual_effect_icon, used_item, no_effect)
|
||||
if(!no_effect && !visual_effect_icon && melee_damage_upper)
|
||||
if(attack_vis_effect && !iswallturf(A)) // override the standard visual effect.
|
||||
visual_effect_icon = attack_vis_effect
|
||||
else if(melee_damage_upper < 10)
|
||||
visual_effect_icon = ATTACK_EFFECT_PUNCH
|
||||
else
|
||||
visual_effect_icon = ATTACK_EFFECT_SMASH
|
||||
..()
|
||||
|
||||
|
||||
/mob/living/basic/update_stat()
|
||||
if(status_flags & GODMODE)
|
||||
return
|
||||
if(stat != DEAD)
|
||||
if(health <= 0)
|
||||
death()
|
||||
else
|
||||
set_stat(CONSCIOUS)
|
||||
med_hud_set_status()
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
* Adjusts the health of a simple mob by a set amount and wakes AI if its idle to react
|
||||
*
|
||||
* Arguments:
|
||||
* * amount The amount that will be used to adjust the mob's health
|
||||
* * updating_health If the mob's health should be immediately updated to the new value
|
||||
* * forced If we should force update the adjustment of the mob's health no matter the restrictions, like GODMODE
|
||||
*/
|
||||
/mob/living/basic/proc/adjust_health(amount, updating_health = TRUE, forced = FALSE)
|
||||
. = FALSE
|
||||
if(forced || !(status_flags & GODMODE))
|
||||
bruteloss = round(clamp(bruteloss + amount, 0, maxHealth * 2), DAMAGE_PRECISION)
|
||||
if(updating_health)
|
||||
updatehealth()
|
||||
. = amount
|
||||
if(ckey || stat)
|
||||
return
|
||||
//if(AIStatus == AI_IDLE)
|
||||
// toggle_ai(AI_ON)
|
||||
|
||||
/mob/living/basic/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE)
|
||||
if(forced)
|
||||
. = adjust_health(amount * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
else if(damage_coeff[BRUTE])
|
||||
. = adjust_health(amount * damage_coeff[BRUTE] * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
|
||||
/mob/living/basic/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE)
|
||||
if(forced)
|
||||
. = adjust_health(amount * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
else if(damage_coeff[BURN])
|
||||
. = adjust_health(amount * damage_coeff[BURN] * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
|
||||
/mob/living/basic/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE)
|
||||
if(forced)
|
||||
. = adjust_health(amount * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
else if(damage_coeff[OXY])
|
||||
. = adjust_health(amount * damage_coeff[OXY] * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
|
||||
/mob/living/basic/adjustToxLoss(amount, updating_health = TRUE, forced = FALSE)
|
||||
if(forced)
|
||||
. = adjust_health(amount * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
else if(damage_coeff[TOX])
|
||||
. = adjust_health(amount * damage_coeff[TOX] * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
|
||||
/mob/living/basic/adjustCloneLoss(amount, updating_health = TRUE, forced = FALSE)
|
||||
if(forced)
|
||||
. = adjust_health(amount * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
else if(damage_coeff[CLONE])
|
||||
. = adjust_health(amount * damage_coeff[CLONE] * CONFIG_GET(number/damage_multiplier), updating_health, forced)
|
||||
|
||||
/mob/living/basic/adjustStaminaLoss(amount, updating_health = FALSE, forced = FALSE)
|
||||
if(forced)
|
||||
staminaloss = max(0, min(BASIC_MOB_MAX_STAMINALOSS, staminaloss + amount))
|
||||
else
|
||||
staminaloss = max(0, min(BASIC_MOB_MAX_STAMINALOSS, staminaloss + (amount * damage_coeff[STAMINA])))
|
||||
update_stamina()
|
||||
@@ -0,0 +1,150 @@
|
||||
/mob/living/basic/cockroach
|
||||
name = "cockroach"
|
||||
desc = "This station is just crawling with bugs."
|
||||
icon_state = "cockroach"
|
||||
icon_dead = "cockroach" //Make this work
|
||||
density = FALSE
|
||||
mob_biotypes = MOB_ORGANIC|MOB_BUG
|
||||
mob_size = MOB_SIZE_TINY
|
||||
health = 1
|
||||
maxHealth = 1
|
||||
speed = 1.25
|
||||
gold_core_spawnable = FRIENDLY_SPAWN
|
||||
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
|
||||
|
||||
verb_say = "chitters"
|
||||
verb_ask = "chitters inquisitively"
|
||||
verb_exclaim = "chitters loudly"
|
||||
verb_yell = "chitters loudly"
|
||||
response_disarm_continuous = "shoos"
|
||||
response_disarm_simple = "shoo"
|
||||
response_harm_continuous = "splats"
|
||||
response_harm_simple = "splat"
|
||||
speak_emote = list("chitters")
|
||||
|
||||
basic_mob_flags = DEL_ON_DEATH
|
||||
faction = list("hostile")
|
||||
|
||||
ai_controller = /datum/ai_controller/basic_controller/cockroach
|
||||
|
||||
/mob/living/basic/cockroach/Initialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/death_drops, list(/obj/effect/decal/cleanable/insectguts))
|
||||
AddElement(/datum/element/swabable, CELL_LINE_TABLE_COCKROACH, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 7)
|
||||
AddElement(/datum/element/basic_body_temp_sensetive, 270, INFINITY)
|
||||
AddComponent(/datum/component/squashable, squash_chance = 50, squash_damage = 1)
|
||||
ADD_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS, INNATE_TRAIT)
|
||||
|
||||
/mob/living/basic/cockroach/death(gibbed)
|
||||
if(GLOB.station_was_nuked) //If the nuke is going off, then cockroaches are invincible. Keeps the nuke from killing them, cause cockroaches are immune to nukes.
|
||||
return
|
||||
..()
|
||||
|
||||
/mob/living/basic/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach.
|
||||
return FALSE
|
||||
|
||||
|
||||
/datum/ai_controller/basic_controller/cockroach
|
||||
blackboard = list(
|
||||
BB_TARGETTING_DATUM = new /datum/targetting_datum/basic()
|
||||
)
|
||||
|
||||
ai_traits = STOP_MOVING_WHEN_PULLED
|
||||
ai_movement = /datum/ai_movement/basic_avoidance
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/random_speech/cockroach,
|
||||
/datum/ai_planning_subtree/find_and_hunt_target
|
||||
)
|
||||
|
||||
|
||||
/datum/ai_controller/basic_controller/cockroach/PerformIdleBehavior(delta_time)
|
||||
. = ..()
|
||||
var/mob/living/living_pawn = pawn
|
||||
|
||||
if(DT_PROB(25, delta_time) && (living_pawn.mobility_flags & MOBILITY_MOVE) && isturf(living_pawn.loc) && !living_pawn.pulledby)
|
||||
var/move_dir = pick(GLOB.alldirs)
|
||||
living_pawn.Move(get_step(living_pawn, move_dir), move_dir)
|
||||
|
||||
|
||||
/obj/projectile/glockroachbullet
|
||||
damage = 10 //same damage as a hivebot
|
||||
damage_type = BRUTE
|
||||
|
||||
/obj/item/ammo_casing/glockroach
|
||||
name = "0.9mm bullet casing"
|
||||
desc = "A... 0.9mm bullet casing? What?"
|
||||
projectile_type = /obj/projectile/glockroachbullet
|
||||
|
||||
|
||||
/mob/living/basic/cockroach/glockroach
|
||||
name = "glockroach"
|
||||
desc = "HOLY SHIT, THAT COCKROACH HAS A GUN!"
|
||||
icon_state = "glockroach"
|
||||
melee_damage_lower = 2.5
|
||||
melee_damage_upper = 10
|
||||
obj_damage = 10
|
||||
gold_core_spawnable = HOSTILE_SPAWN
|
||||
faction = list("hostile")
|
||||
ai_controller = /datum/ai_controller/basic_controller/cockroach/glockroach
|
||||
|
||||
/mob/living/basic/cockroach/glockroach/Initialize()
|
||||
. = ..()
|
||||
AddElement(/datum/element/ranged_attacks, /obj/item/ammo_casing/glockroach)
|
||||
|
||||
/datum/ai_controller/basic_controller/cockroach/glockroach
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/random_speech/cockroach,
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach, //If we are attacking someone, this will prevent us from hunting
|
||||
/datum/ai_planning_subtree/find_and_hunt_target
|
||||
)
|
||||
|
||||
/datum/ai_planning_subtree/basic_ranged_attack_subtree/glockroach
|
||||
ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack/glockroach
|
||||
|
||||
/datum/ai_behavior/basic_ranged_attack/glockroach //Slightly slower, as this is being made in feature freeze ;)
|
||||
action_cooldown = 1 SECONDS
|
||||
|
||||
/mob/living/basic/cockroach/hauberoach
|
||||
name = "hauberoach"
|
||||
desc = "Is that cockroach wearing a tiny yet immaculate replica 19th century Prussian spiked helmet? ...Is that a bad thing?"
|
||||
icon_state = "hauberoach"
|
||||
attack_verb_continuous = "rams its spike into"
|
||||
attack_verb_simple = "ram your spike into"
|
||||
melee_damage_lower = 2.5
|
||||
melee_damage_upper = 10
|
||||
obj_damage = 10
|
||||
gold_core_spawnable = HOSTILE_SPAWN
|
||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||
attack_vis_effect = ATTACK_EFFECT_SLASH
|
||||
faction = list("hostile")
|
||||
sharpness = SHARP_POINTY
|
||||
ai_controller = /datum/ai_controller/basic_controller/cockroach/hauberoach
|
||||
|
||||
/mob/living/basic/cockroach/hauberoach/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/caltrop, min_damage = 10, max_damage = 15, flags = (CALTROP_BYPASS_SHOES | CALTROP_SILENT))
|
||||
AddComponent(/datum/component/squashable, squash_chance = 100, squash_damage = 1, squash_callback = /mob/living/basic/cockroach/hauberoach/.proc/on_squish)
|
||||
|
||||
///Proc used to override the squashing behavior of the normal cockroach.
|
||||
/mob/living/basic/cockroach/hauberoach/proc/on_squish(mob/living/cockroach, mob/living/living_target)
|
||||
if(!istype(living_target))
|
||||
return FALSE //We failed to run the invoke. Might be because we're a structure. Let the squashable element handle it then!
|
||||
if(!HAS_TRAIT(living_target, TRAIT_PIERCEIMMUNE))
|
||||
living_target.visible_message(span_danger("[living_target] steps onto [cockroach]'s spike!"), span_userdanger("You step onto [cockroach]'s spike!"))
|
||||
return TRUE
|
||||
living_target.visible_message(span_notice("[living_target] squashes [cockroach], not even noticing its spike."), span_notice("You squashed [cockroach], not even noticing its spike."))
|
||||
return FALSE
|
||||
/datum/ai_controller/basic_controller/cockroach/hauberoach
|
||||
planning_subtrees = list(
|
||||
/datum/ai_planning_subtree/random_speech/cockroach,
|
||||
/datum/ai_planning_subtree/simple_find_target,
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree/hauberoach, //If we are attacking someone, this will prevent us from hunting
|
||||
/datum/ai_planning_subtree/find_and_hunt_target
|
||||
)
|
||||
|
||||
/datum/ai_planning_subtree/basic_melee_attack_subtree/hauberoach
|
||||
melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/hauberoach
|
||||
|
||||
/datum/ai_behavior/basic_melee_attack/hauberoach //Slightly slower, as this is being made in feature freeze ;)
|
||||
action_cooldown = 1 SECONDS
|
||||
Reference in New Issue
Block a user