diff --git a/code/game/turfs/open/floor/plating/asteroid.dm b/code/game/turfs/open/floor/plating/asteroid.dm index a52f6496617..efc12cf6bd6 100644 --- a/code/game/turfs/open/floor/plating/asteroid.dm +++ b/code/game/turfs/open/floor/plating/asteroid.dm @@ -213,7 +213,8 @@ GLOBAL_LIST_INIT(megafauna_spawn_list, list(/mob/living/simple_animal/hostile/me digResult = /obj/item/stack/sheet/mineral/snow mob_spawn_list = list(/mob/living/simple_animal/hostile/asteroid/wolf = 50, /obj/structure/spawner/ice_moon = 3, \ /mob/living/simple_animal/hostile/asteroid/polarbear = 30, /obj/structure/spawner/ice_moon/polarbear = 3, \ - /mob/living/simple_animal/hostile/asteroid/hivelord/legion/snow = 50, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10) + /mob/living/simple_animal/hostile/asteroid/hivelord/legion/snow = 50, /mob/living/simple_animal/hostile/asteroid/goldgrub = 10, \ + /mob/living/simple_animal/hostile/asteroid/lobstrosity = 15) flora_spawn_list = list(/obj/structure/flora/tree/pine = 2, /obj/structure/flora/rock/icy = 2, /obj/structure/flora/rock/pile/icy = 2, /obj/structure/flora/grass/both = 12) terrain_spawn_list = list() diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 5ccaa30e8c8..0b8b9a0172f 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -48,10 +48,22 @@ var/attack_same = 0 //Set us to 1 to allow us to attack our own faction var/atom/targets_from = null //all range/attack/etc. calculations should be done from this atom, defaults to the mob itself, useful for Vehicles and such var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects. - var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever + ///When a target is found, will the mob attempt to charge at it's target? + var/charger = FALSE + ///Tracks if the target is actively charging. + var/charge_state = FALSE + ///In a charge, how many tiles will the charger travel? + var/charge_distance = 3 + ///How often can the charging mob actually charge? Effects the cooldown between charges. + var/charge_frequency = 6 SECONDS + ///If the mob is charging, how long will it stun it's target on success, and itself on failure? + var/knockdown_time = 3 SECONDS + ///Declares a cooldown for potential charges right off the bat. + COOLDOWN_DECLARE(charge_cooldown) + /mob/living/simple_animal/hostile/Initialize() . = ..() @@ -274,6 +286,9 @@ if(ranged) //We ranged? Shoot at em if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown OpenFire(target) + if(charger && (target_distance > minimum_distance) && (target_distance <= charge_distance))//Attempt to close the distance with a charge. + enter_charge(target) + return TRUE if(!Process_Spacemove()) //Drifting walk(src,0) return 1 @@ -577,3 +592,61 @@ friends = fren faction = fren.faction.Copy() return ..() + +/** + * Proc that handles a charge attack windup for a mob. + */ +/mob/living/simple_animal/hostile/proc/enter_charge(var/atom/target) + if((mobility_flags & (MOBILITY_MOVE | MOBILITY_STAND)) != (MOBILITY_MOVE | MOBILITY_STAND) || charge_state) + return FALSE + + if(!(COOLDOWN_FINISHED(src, charge_cooldown)) || !has_gravity() || !target.has_gravity()) + return FALSE + Shake(15, 15, 1 SECONDS) + addtimer(CALLBACK(src, .proc/handle_charge_target, target), 1.5 SECONDS, TIMER_STOPPABLE) + +/** + * Proc that throws the mob at the target after the windup. + */ +/mob/living/simple_animal/hostile/proc/handle_charge_target(var/atom/target) + charge_state = TRUE + throw_at(target, charge_distance, 1, src, FALSE, TRUE, callback = CALLBACK(src, .proc/charge_end)) + COOLDOWN_START(src, charge_cooldown, charge_frequency) + return TRUE + +/** + * Proc that handles a charge attack after it's concluded. + */ +/mob/living/simple_animal/hostile/proc/charge_end() + charge_state = FALSE + +/** + * Proc that handles the charge impact of the charging mob. + */ +/mob/living/simple_animal/hostile/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) + if(!charge_state) + return ..() + + if(hit_atom) + if(isliving(hit_atom)) + var/mob/living/L = hit_atom + var/blocked = FALSE + if(ishuman(hit_atom)) + var/mob/living/carbon/human/H = hit_atom + if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK)) + blocked = TRUE + if(!blocked) + L.visible_message("[src] charges on [L]!", "[src] charges into you!") + L.Knockdown(knockdown_time) + else + Stun((knockdown_time * 2), 1, 1) + charge_end() + else if(hit_atom.density && !hit_atom.CanPass(src)) + visible_message("[src] smashes into [hit_atom]!") + Stun((knockdown_time * 2), 1, 1) + + if(charge_state) + charge_state = FALSE + update_icons() + update_mobility() + diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/lobstrosity.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/lobstrosity.dm new file mode 100644 index 00000000000..140042024df --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/lobstrosity.dm @@ -0,0 +1,42 @@ +/** + * Lobstrosities, the poster boy of charging AI mobs. Drops crab meat and bones. + * Outside of charging, it's intended behavior is that it is generally slow moving, but makes up for that with a knockdown attack to score additional hits. + */ +/mob/living/simple_animal/hostile/asteroid/lobstrosity + name = "arctic lobstrosity" + desc = "A marvel of evolution gone wrong, the frosty ice produces underground lakes where these ill tempered seafood gather. Beware its charge." + icon = 'icons/mob/icemoon/icemoon_monsters.dmi' + icon_state = "arctic_lobstrosity" + icon_living = "arctic_lobstrosity" + icon_dead = "arctic_lobstrosity_dead" + mob_biotypes = MOB_ORGANIC|MOB_BEAST + mouse_opacity = MOUSE_OPACITY_ICON + friendly_verb_continuous = "chitters at" + friendly_verb_simple = "chits at" + speak_emote = list("chitters") + speed = 3 + move_to_delay = 20 + maxHealth = 150 + health = 150 + obj_damage = 15 + melee_damage_lower = 15 + melee_damage_upper = 19 + attack_verb_continuous = "snips" + attack_verb_simple = "snip" + attack_sound = 'sound/weapons/bite.ogg' + weather_immunities = list("snow") + vision_range = 5 + aggro_vision_range = 7 + charger = TRUE + charge_distance = 4 + butcher_results = list(/obj/item/reagent_containers/food/snacks/meat/crab = 2, /obj/item/stack/sheet/bone = 2) + robust_searching = TRUE + footstep_type = FOOTSTEP_MOB_CLAW + gold_core_spawnable = HOSTILE_SPAWN + +/mob/living/simple_animal/hostile/asteroid/lobstrosity/lava + name = "tropical lobstrosity" + desc = "A marvel of evolution gone wrong, the sulfur lakes of lavaland have given them a vibrant, red hued shell. Beware its charge." + icon_state = "lobstrosity" + icon_living = "lobstrosity" + icon_dead = "lobstrosity_dead" diff --git a/icons/mob/icemoon/icemoon_monsters.dmi b/icons/mob/icemoon/icemoon_monsters.dmi index 4f0b5df64a2..02b39cc3c70 100644 Binary files a/icons/mob/icemoon/icemoon_monsters.dmi and b/icons/mob/icemoon/icemoon_monsters.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 7d68a88fd78..fe6242f2f55 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2497,6 +2497,7 @@ #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\hivelord.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\ice demon.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\ice whelp.dm" +#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\lobstrosity.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\mining_mobs.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\polarbear.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\wolf.dm"