Merge pull request #2528
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
/mob/living/simple_animal/hostile/jungle
|
||||||
|
vision_range = 5
|
||||||
|
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
||||||
|
faction = list("jungle")
|
||||||
|
weather_immunities = list("acid")
|
||||||
|
obj_damage = 30
|
||||||
|
environment_smash = ENVIRONMENT_SMASH_WALLS
|
||||||
|
minbodytemp = 0
|
||||||
|
maxbodytemp = 450
|
||||||
|
response_help = "pokes"
|
||||||
|
response_disarm = "shoves"
|
||||||
|
response_harm = "strikes"
|
||||||
|
status_flags = NONE
|
||||||
|
a_intent = INTENT_HARM
|
||||||
|
see_in_dark = 4
|
||||||
|
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
||||||
|
mob_size = MOB_SIZE_LARGE
|
||||||
@@ -0,0 +1,263 @@
|
|||||||
|
#define PLAYER_HOP_DELAY 25
|
||||||
|
|
||||||
|
//Huge, carnivorous toads that spit an immobilizing toxin at its victims before leaping onto them.
|
||||||
|
//It has no melee attack, and its damage comes from the toxin in its bubbles and its crushing leap.
|
||||||
|
//Its eyes will turn red to signal an imminent attack!
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper
|
||||||
|
name = "leaper"
|
||||||
|
desc = "Commonly referred to as 'leapers', the Geron Toad is a massive beast that spits out highly pressurized bubbles containing a unique toxin, knocking down its prey and then crushing it with its girth."
|
||||||
|
icon = 'icons/mob/jungle/leaper.dmi'
|
||||||
|
icon_state = "leaper"
|
||||||
|
icon_living = "leaper"
|
||||||
|
icon_dead = "leaper_dead"
|
||||||
|
maxHealth = 300
|
||||||
|
health = 300
|
||||||
|
ranged = TRUE
|
||||||
|
projectiletype = /obj/item/projectile/leaper
|
||||||
|
projectilesound = 'sound/weapons/pierce.ogg'
|
||||||
|
ranged_cooldown_time = 30
|
||||||
|
pixel_x = -16
|
||||||
|
layer = LARGE_MOB_LAYER
|
||||||
|
speed = 10
|
||||||
|
stat_attack = 1
|
||||||
|
robust_searching = 1
|
||||||
|
var/hopping = FALSE
|
||||||
|
var/hop_cooldown = 0 //Strictly for player controlled leapers
|
||||||
|
var/projectile_ready = FALSE //Stopping AI leapers from firing whenever they want, and only doing it after a hop has finished instead
|
||||||
|
|
||||||
|
/obj/item/projectile/leaper
|
||||||
|
name = "leaper bubble"
|
||||||
|
icon_state = "leaper"
|
||||||
|
knockdown = 50
|
||||||
|
damage = 0
|
||||||
|
range = 7
|
||||||
|
hitsound = 'sound/effects/snap.ogg'
|
||||||
|
nondirectional_sprite = TRUE
|
||||||
|
impact_effect_type = /obj/effect/temp_visual/leaper_projectile_impact
|
||||||
|
|
||||||
|
/obj/item/projectile/leaper/on_hit(atom/target, blocked = FALSE)
|
||||||
|
..()
|
||||||
|
if(iscarbon(target))
|
||||||
|
var/mob/living/carbon/C = target
|
||||||
|
C.reagents.add_reagent("leaper_venom", 5)
|
||||||
|
return
|
||||||
|
if(isanimal(target))
|
||||||
|
var/mob/living/simple_animal/L = target
|
||||||
|
L.adjustHealth(25)
|
||||||
|
|
||||||
|
/obj/item/projectile/leaper/on_range()
|
||||||
|
var/turf/T = get_turf(src)
|
||||||
|
..()
|
||||||
|
new /obj/structure/leaper_bubble(T)
|
||||||
|
|
||||||
|
/obj/effect/temp_visual/leaper_projectile_impact
|
||||||
|
name = "leaper bubble"
|
||||||
|
icon = 'icons/obj/projectiles.dmi'
|
||||||
|
icon_state = "leaper_bubble_pop"
|
||||||
|
layer = ABOVE_ALL_MOB_LAYER
|
||||||
|
duration = 3
|
||||||
|
|
||||||
|
/obj/effect/temp_visual/leaper_projectile_impact/Initialize()
|
||||||
|
. = ..()
|
||||||
|
new /obj/effect/decal/cleanable/leaper_sludge(get_turf(src))
|
||||||
|
|
||||||
|
/obj/effect/decal/cleanable/leaper_sludge
|
||||||
|
name = "leaper sludge"
|
||||||
|
desc = "A small pool of sludge, containing trace amounts of leaper venom."
|
||||||
|
icon = 'icons/effects/tomatodecal.dmi'
|
||||||
|
icon_state = "tomato_floor1"
|
||||||
|
|
||||||
|
/obj/structure/leaper_bubble
|
||||||
|
name = "leaper bubble"
|
||||||
|
desc = "A floating bubble containing leaper venom. The contents are under a surprising amount of pressure."
|
||||||
|
icon = 'icons/obj/projectiles.dmi'
|
||||||
|
icon_state = "leaper"
|
||||||
|
max_integrity = 10
|
||||||
|
density = FALSE
|
||||||
|
|
||||||
|
/obj/structure/leaper_bubble/Initialize()
|
||||||
|
. = ..()
|
||||||
|
float(on = TRUE)
|
||||||
|
QDEL_IN(src, 100)
|
||||||
|
|
||||||
|
/obj/structure/leaper_bubble/Destroy()
|
||||||
|
new /obj/effect/temp_visual/leaper_projectile_impact(get_turf(src))
|
||||||
|
playsound(src,'sound/effects/snap.ogg',50, 1, -1)
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/structure/leaper_bubble/Crossed(atom/movable/AM)
|
||||||
|
if(isliving(AM))
|
||||||
|
var/mob/living/L = AM
|
||||||
|
if(!istype(L, /mob/living/simple_animal/hostile/jungle/leaper))
|
||||||
|
playsound(src,'sound/effects/snap.ogg',50, 1, -1)
|
||||||
|
L.Knockdown(50)
|
||||||
|
if(iscarbon(L))
|
||||||
|
var/mob/living/carbon/C = L
|
||||||
|
C.reagents.add_reagent("leaper_venom", 5)
|
||||||
|
if(isanimal(L))
|
||||||
|
var/mob/living/simple_animal/A = L
|
||||||
|
A.adjustHealth(25)
|
||||||
|
qdel(src)
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/datum/reagent/toxin/leaper_venom
|
||||||
|
name = "Leaper venom"
|
||||||
|
id = "leaper_venom"
|
||||||
|
description = "A toxin spat out by leapers that, while harmless in small doses, quickly creates a toxic reaction if too much is in the body."
|
||||||
|
color = "#801E28" // rgb: 128, 30, 40
|
||||||
|
toxpwr = 0
|
||||||
|
taste_description = "french cuisine"
|
||||||
|
taste_mult = 1.3
|
||||||
|
|
||||||
|
/datum/reagent/toxin/leaper_venom/on_mob_life(mob/living/M)
|
||||||
|
if(volume >= 10)
|
||||||
|
M.adjustToxLoss(5, 0)
|
||||||
|
..()
|
||||||
|
|
||||||
|
/obj/effect/temp_visual/leaper_crush
|
||||||
|
name = "grim tidings"
|
||||||
|
desc = "Incoming leaper!"
|
||||||
|
icon = 'icons/effects/96x96.dmi'
|
||||||
|
icon_state = "lily_pad"
|
||||||
|
layer = BELOW_MOB_LAYER
|
||||||
|
pixel_x = -32
|
||||||
|
pixel_y = -32
|
||||||
|
duration = 30
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/Initialize()
|
||||||
|
. = ..()
|
||||||
|
verbs -= /mob/living/verb/pulled
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/CtrlClickOn(atom/A)
|
||||||
|
face_atom(A)
|
||||||
|
target = A
|
||||||
|
if(!isturf(loc))
|
||||||
|
return
|
||||||
|
if(next_move > world.time)
|
||||||
|
return
|
||||||
|
if(hopping)
|
||||||
|
return
|
||||||
|
if(isliving(A))
|
||||||
|
var/mob/living/L = A
|
||||||
|
if(L.incapacitated())
|
||||||
|
BellyFlop()
|
||||||
|
return
|
||||||
|
if(hop_cooldown <= world.time)
|
||||||
|
Hop(player_hop = TRUE)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/AttackingTarget()
|
||||||
|
if(isliving(target))
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/handle_automated_action()
|
||||||
|
if(hopping || projectile_ready)
|
||||||
|
return
|
||||||
|
. = ..()
|
||||||
|
if(target)
|
||||||
|
if(isliving(target))
|
||||||
|
var/mob/living/L = target
|
||||||
|
if(L.incapacitated())
|
||||||
|
BellyFlop()
|
||||||
|
return
|
||||||
|
if(!hopping)
|
||||||
|
Hop()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/Life()
|
||||||
|
. = ..()
|
||||||
|
update_icons()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
||||||
|
if(prob(33) && !ckey)
|
||||||
|
ranged_cooldown = 0 //Keeps em on their toes instead of a constant rotation
|
||||||
|
..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/OpenFire()
|
||||||
|
face_atom(target)
|
||||||
|
if(ranged_cooldown <= world.time)
|
||||||
|
if(ckey)
|
||||||
|
if(hopping)
|
||||||
|
return
|
||||||
|
if(isliving(target))
|
||||||
|
var/mob/living/L = target
|
||||||
|
if(L.incapacitated())
|
||||||
|
return //No stunlocking. Hop on them after you stun them, you donk.
|
||||||
|
if(AIStatus == AI_ON && !projectile_ready && !ckey)
|
||||||
|
return
|
||||||
|
. = ..(target)
|
||||||
|
projectile_ready = FALSE
|
||||||
|
update_icons()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/proc/Hop(player_hop = FALSE)
|
||||||
|
if(z != target.z)
|
||||||
|
return
|
||||||
|
hopping = TRUE
|
||||||
|
density = FALSE
|
||||||
|
pass_flags |= PASSMOB
|
||||||
|
notransform = TRUE
|
||||||
|
var/turf/new_turf = locate((target.x + rand(-3,3)),(target.y + rand(-3,3)),target.z)
|
||||||
|
if(player_hop)
|
||||||
|
new_turf = get_turf(target)
|
||||||
|
hop_cooldown = world.time + PLAYER_HOP_DELAY
|
||||||
|
if(AIStatus == AI_ON && ranged_cooldown <= world.time)
|
||||||
|
projectile_ready = TRUE
|
||||||
|
update_icons()
|
||||||
|
throw_at(new_turf, max(3,get_dist(src,new_turf)), 1, src, FALSE, callback = CALLBACK(src, .FinishHop))
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/proc/FinishHop()
|
||||||
|
density = TRUE
|
||||||
|
notransform = FALSE
|
||||||
|
pass_flags &= ~PASSMOB
|
||||||
|
hopping = FALSE
|
||||||
|
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 100, 1)
|
||||||
|
if(target && AIStatus == AI_ON && projectile_ready && !ckey)
|
||||||
|
face_atom(target)
|
||||||
|
addtimer(CALLBACK(src, .proc/OpenFire, target), 5)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlop()
|
||||||
|
var/turf/new_turf = get_turf(target)
|
||||||
|
hopping = TRUE
|
||||||
|
notransform = TRUE
|
||||||
|
new /obj/effect/temp_visual/leaper_crush(new_turf)
|
||||||
|
addtimer(CALLBACK(src, .proc/BellyFlopHop, new_turf), 30)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlopHop(turf/T)
|
||||||
|
density = FALSE
|
||||||
|
throw_at(T, get_dist(src,T),1,src, FALSE, callback = CALLBACK(src, .proc/Crush))
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/proc/Crush()
|
||||||
|
hopping = FALSE
|
||||||
|
density = TRUE
|
||||||
|
notransform = FALSE
|
||||||
|
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1)
|
||||||
|
for(var/mob/living/L in orange(1, src))
|
||||||
|
L.adjustBruteLoss(35)
|
||||||
|
if(!QDELETED(L)) // Some mobs are deleted on death
|
||||||
|
var/throw_dir = get_dir(src, L)
|
||||||
|
if(L.loc == loc)
|
||||||
|
throw_dir = pick(GLOB.alldirs)
|
||||||
|
var/throwtarget = get_edge_target_turf(src, throw_dir)
|
||||||
|
L.throw_at(throwtarget, 3, 1)
|
||||||
|
visible_message("<span class='warning'>[L] is thrown clear of [src]!</span>")
|
||||||
|
if(ckey)//Lessens ability to chain stun as a player
|
||||||
|
ranged_cooldown = ranged_cooldown_time + world.time
|
||||||
|
update_icons()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/Goto()
|
||||||
|
return
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/throw_impact()
|
||||||
|
return
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/leaper/update_icons()
|
||||||
|
. = ..()
|
||||||
|
if(stat)
|
||||||
|
icon_state = "leaper_dead"
|
||||||
|
return
|
||||||
|
if(ranged_cooldown <= world.time)
|
||||||
|
if(AIStatus == AI_ON && projectile_ready || ckey)
|
||||||
|
icon_state = "leaper_alert"
|
||||||
|
return
|
||||||
|
icon_state = "leaper"
|
||||||
|
|
||||||
|
#undef PLAYER_HOP_DELAY
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
//Large and powerful, but timid. It won't engage anything above 50 health, or anything without legcuffs.
|
||||||
|
//It can fire fleshy snares that legcuff anyone that it hits, making them look especially tasty to the arachnid.
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mega_arachnid
|
||||||
|
name = "mega arachnid"
|
||||||
|
desc = "Though physically imposing, it prefers to ambush its prey, and it will only engage with an already crippled opponent."
|
||||||
|
icon = 'icons/mob/jungle/arachnid.dmi'
|
||||||
|
icon_state = "arachnid"
|
||||||
|
icon_living = "arachnid"
|
||||||
|
icon_dead = "arachnid_dead"
|
||||||
|
melee_damage_lower = 30
|
||||||
|
melee_damage_upper = 30
|
||||||
|
maxHealth = 300
|
||||||
|
health = 300
|
||||||
|
speed = 1
|
||||||
|
ranged = 1
|
||||||
|
pixel_x = -16
|
||||||
|
move_to_delay = 10
|
||||||
|
aggro_vision_range = 9
|
||||||
|
speak_emote = list("chitters")
|
||||||
|
attack_sound = 'sound/weapons/bladeslice.ogg'
|
||||||
|
ranged_cooldown_time = 60
|
||||||
|
projectiletype = /obj/item/projectile/mega_arachnid
|
||||||
|
projectilesound = 'sound/weapons/pierce.ogg'
|
||||||
|
alpha = 50
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mega_arachnid/Life()
|
||||||
|
..()
|
||||||
|
if(target && ranged_cooldown > world.time && iscarbon(target))
|
||||||
|
var/mob/living/carbon/C = target
|
||||||
|
if(!C.legcuffed && C.health < 50)
|
||||||
|
retreat_distance = 9
|
||||||
|
minimum_distance = 9
|
||||||
|
alpha = 125
|
||||||
|
return
|
||||||
|
retreat_distance = 0
|
||||||
|
minimum_distance = 0
|
||||||
|
alpha = 255
|
||||||
|
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mega_arachnid/Aggro()
|
||||||
|
..()
|
||||||
|
alpha = 255
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mega_arachnid/LoseAggro()
|
||||||
|
..()
|
||||||
|
alpha = 50
|
||||||
|
|
||||||
|
/obj/item/projectile/mega_arachnid
|
||||||
|
name = "flesh snare"
|
||||||
|
nodamage = 1
|
||||||
|
damage = 0
|
||||||
|
icon_state = "tentacle_end"
|
||||||
|
|
||||||
|
/obj/item/projectile/mega_arachnid/on_hit(atom/target, blocked = FALSE)
|
||||||
|
if(iscarbon(target) && blocked < 100)
|
||||||
|
var/obj/item/restraints/legcuffs/beartrap/mega_arachnid/B = new /obj/item/restraints/legcuffs/beartrap/mega_arachnid(get_turf(target))
|
||||||
|
B.Crossed(target)
|
||||||
|
..()
|
||||||
|
|
||||||
|
/obj/item/restraints/legcuffs/beartrap/mega_arachnid
|
||||||
|
name = "fleshy restraints"
|
||||||
|
desc = "Used by mega arachnids to immobilize their prey."
|
||||||
|
flags_1 = DROPDEL_1
|
||||||
|
icon_state = "tentacle_end"
|
||||||
|
icon = 'icons/obj/projectiles.dmi'
|
||||||
@@ -0,0 +1,224 @@
|
|||||||
|
#define MOOK_ATTACK_NEUTRAL 0
|
||||||
|
#define MOOK_ATTACK_WARMUP 1
|
||||||
|
#define MOOK_ATTACK_ACTIVE 2
|
||||||
|
#define MOOK_ATTACK_RECOVERY 3
|
||||||
|
#define ATTACK_INTERMISSION_TIME 5
|
||||||
|
|
||||||
|
//Fragile but highly aggressive wanderers that pose a large threat in numbers.
|
||||||
|
//They'll attempt to leap at their target from afar using their hatchets.
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook
|
||||||
|
name = "wanderer"
|
||||||
|
desc = "This unhealthy looking primitive is wielding a rudimentary hatchet, swinging it with wild abandon. One isn't much of a threat, but in numbers they can quickly overwhelm a superior opponent."
|
||||||
|
icon = 'icons/mob/jungle/mook.dmi'
|
||||||
|
icon_state = "mook"
|
||||||
|
icon_living = "mook"
|
||||||
|
icon_dead = "mook_dead"
|
||||||
|
pixel_x = -16
|
||||||
|
maxHealth = 45
|
||||||
|
health = 45
|
||||||
|
melee_damage_lower = 30
|
||||||
|
melee_damage_upper = 30
|
||||||
|
pixel_y = -8
|
||||||
|
ranged = TRUE
|
||||||
|
ranged_cooldown_time = 10
|
||||||
|
pass_flags = LETPASSTHROW
|
||||||
|
robust_searching = TRUE
|
||||||
|
stat_attack = UNCONSCIOUS
|
||||||
|
attack_sound = 'sound/weapons/rapierhit.ogg'
|
||||||
|
death_sound = 'sound/voice/mook_death.ogg'
|
||||||
|
aggro_vision_range = 15 //A little more aggressive once in combat to balance out their really low HP
|
||||||
|
var/attack_state = MOOK_ATTACK_NEUTRAL
|
||||||
|
var/struck_target_leap = FALSE
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/CanPass(atom/movable/O)
|
||||||
|
if(istype(O, /mob/living/simple_animal/hostile/jungle/mook))
|
||||||
|
var/mob/living/simple_animal/hostile/jungle/mook/M = O
|
||||||
|
if(M.attack_state == MOOK_ATTACK_ACTIVE && M.throwing)
|
||||||
|
return TRUE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/death()
|
||||||
|
desc = "A deceased primitive. Upon closer inspection, it was suffering from severe cellular degeneration and its garments are machine made..."//Can you guess the twist
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/AttackingTarget()
|
||||||
|
if(isliving(target))
|
||||||
|
if(ranged_cooldown <= world.time && attack_state == MOOK_ATTACK_NEUTRAL)
|
||||||
|
var/mob/living/L = target
|
||||||
|
if(L.incapacitated())
|
||||||
|
WarmupAttack(forced_slash_combo = TRUE)
|
||||||
|
return
|
||||||
|
WarmupAttack()
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/Goto()
|
||||||
|
if(attack_state != MOOK_ATTACK_NEUTRAL)
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/Move()
|
||||||
|
if(attack_state == MOOK_ATTACK_WARMUP || attack_state == MOOK_ATTACK_RECOVERY)
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/proc/WarmupAttack(forced_slash_combo = FALSE)
|
||||||
|
if(attack_state == MOOK_ATTACK_NEUTRAL && target)
|
||||||
|
attack_state = MOOK_ATTACK_WARMUP
|
||||||
|
walk(src,0)
|
||||||
|
update_icons()
|
||||||
|
if(prob(50) && get_dist(src,target) <= 3 || forced_slash_combo)
|
||||||
|
addtimer(CALLBACK(src, .proc/SlashCombo), ATTACK_INTERMISSION_TIME)
|
||||||
|
return
|
||||||
|
addtimer(CALLBACK(src, .proc/LeapAttack), ATTACK_INTERMISSION_TIME + rand(0,3))
|
||||||
|
return
|
||||||
|
attack_state = MOOK_ATTACK_RECOVERY
|
||||||
|
ResetNeutral()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/proc/SlashCombo()
|
||||||
|
if(attack_state == MOOK_ATTACK_WARMUP && !stat)
|
||||||
|
attack_state = MOOK_ATTACK_ACTIVE
|
||||||
|
update_icons()
|
||||||
|
SlashAttack()
|
||||||
|
addtimer(CALLBACK(src, .proc/SlashAttack), 3)
|
||||||
|
addtimer(CALLBACK(src, .proc/SlashAttack), 6)
|
||||||
|
addtimer(CALLBACK(src, .proc/AttackRecovery), 9)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/proc/SlashAttack()
|
||||||
|
if(target && !stat && attack_state == MOOK_ATTACK_ACTIVE)
|
||||||
|
melee_damage_lower = 15
|
||||||
|
melee_damage_upper = 15
|
||||||
|
var/mob_direction = get_dir(src,target)
|
||||||
|
if(get_dist(src,target) > 1)
|
||||||
|
step(src,mob_direction)
|
||||||
|
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && isliving(target))
|
||||||
|
var/mob/living/L = target
|
||||||
|
L.attack_animal(src)
|
||||||
|
return
|
||||||
|
var/swing_turf = get_step(src,mob_direction)
|
||||||
|
new /obj/effect/temp_visual/kinetic_blast(swing_turf)
|
||||||
|
playsound(src, 'sound/weapons/slashmiss.ogg', 50, 1)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/proc/LeapAttack()
|
||||||
|
if(target && !stat && attack_state == MOOK_ATTACK_WARMUP)
|
||||||
|
attack_state = MOOK_ATTACK_ACTIVE
|
||||||
|
density = FALSE
|
||||||
|
melee_damage_lower = 30
|
||||||
|
melee_damage_upper = 30
|
||||||
|
update_icons()
|
||||||
|
new /obj/effect/temp_visual/mook_dust(get_turf(src))
|
||||||
|
playsound(src, 'sound/weapons/thudswoosh.ogg', 25, 1)
|
||||||
|
playsound(src, 'sound/voice/mook_leap_yell.ogg', 100, 1)
|
||||||
|
var/target_turf = get_turf(target)
|
||||||
|
throw_at(target_turf, 7, 1, src, FALSE, callback = CALLBACK(src, .proc/AttackRecovery))
|
||||||
|
return
|
||||||
|
attack_state = MOOK_ATTACK_RECOVERY
|
||||||
|
ResetNeutral()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/proc/AttackRecovery()
|
||||||
|
if(attack_state == MOOK_ATTACK_ACTIVE && !stat)
|
||||||
|
attack_state = MOOK_ATTACK_RECOVERY
|
||||||
|
density = TRUE
|
||||||
|
face_atom(target)
|
||||||
|
if(!struck_target_leap)
|
||||||
|
update_icons()
|
||||||
|
struck_target_leap = FALSE
|
||||||
|
if(prob(40))
|
||||||
|
attack_state = MOOK_ATTACK_NEUTRAL
|
||||||
|
if(target)
|
||||||
|
if(isliving(target))
|
||||||
|
var/mob/living/L = target
|
||||||
|
if(L.incapacitated() && L.stat != DEAD)
|
||||||
|
addtimer(CALLBACK(src, .proc/WarmupAttack, TRUE), ATTACK_INTERMISSION_TIME)
|
||||||
|
return
|
||||||
|
addtimer(CALLBACK(src, .proc/WarmupAttack), ATTACK_INTERMISSION_TIME)
|
||||||
|
return
|
||||||
|
addtimer(CALLBACK(src, .proc/ResetNeutral), ATTACK_INTERMISSION_TIME)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/proc/ResetNeutral()
|
||||||
|
if(attack_state == MOOK_ATTACK_RECOVERY)
|
||||||
|
attack_state = MOOK_ATTACK_NEUTRAL
|
||||||
|
ranged_cooldown = world.time + ranged_cooldown_time
|
||||||
|
update_icons()
|
||||||
|
if(target && !stat)
|
||||||
|
update_icons()
|
||||||
|
Goto(target, move_to_delay, minimum_distance)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/throw_impact(atom/hit_atom, throwingdatum)
|
||||||
|
. = ..()
|
||||||
|
if(isliving(hit_atom) && attack_state == MOOK_ATTACK_ACTIVE)
|
||||||
|
var/mob/living/L = hit_atom
|
||||||
|
if(CanAttack(L))
|
||||||
|
L.attack_animal(src)
|
||||||
|
struck_target_leap = TRUE
|
||||||
|
density = TRUE
|
||||||
|
update_icons()
|
||||||
|
var/mook_under_us = FALSE
|
||||||
|
for(var/A in get_turf(src))
|
||||||
|
if(struck_target_leap && mook_under_us)
|
||||||
|
break
|
||||||
|
if(A == src)
|
||||||
|
continue
|
||||||
|
if(isliving(A))
|
||||||
|
var/mob/living/ML = A
|
||||||
|
if(!struck_target_leap && CanAttack(ML))//Check if some joker is attempting to use rest to evade us
|
||||||
|
struck_target_leap = TRUE
|
||||||
|
ML.attack_animal(src)
|
||||||
|
density = TRUE
|
||||||
|
struck_target_leap = TRUE
|
||||||
|
update_icons()
|
||||||
|
continue
|
||||||
|
if(istype(ML, /mob/living/simple_animal/hostile/jungle/mook) && !mook_under_us)//If we land on the same tile as another mook, spread out so we don't stack our sprite on the same tile
|
||||||
|
var/mob/living/simple_animal/hostile/jungle/mook/M = ML
|
||||||
|
if(!M.stat)
|
||||||
|
mook_under_us = TRUE
|
||||||
|
var/anydir = pick(GLOB.cardinals)
|
||||||
|
Move(get_step(src, anydir), anydir)
|
||||||
|
continue
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/handle_automated_action()
|
||||||
|
if(attack_state)
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/OpenFire()
|
||||||
|
if(isliving(target))
|
||||||
|
var/mob/living/L = target
|
||||||
|
if(L.incapacitated())
|
||||||
|
return
|
||||||
|
WarmupAttack()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/mook/update_icons()
|
||||||
|
. = ..()
|
||||||
|
if(!stat)
|
||||||
|
switch(attack_state)
|
||||||
|
if(MOOK_ATTACK_NEUTRAL)
|
||||||
|
icon_state = "mook"
|
||||||
|
if(MOOK_ATTACK_WARMUP)
|
||||||
|
icon_state = "mook_warmup"
|
||||||
|
if(MOOK_ATTACK_ACTIVE)
|
||||||
|
if(!density)
|
||||||
|
icon_state = "mook_leap"
|
||||||
|
return
|
||||||
|
if(struck_target_leap)
|
||||||
|
icon_state = "mook_strike"
|
||||||
|
return
|
||||||
|
icon_state = "mook_slash_combo"
|
||||||
|
if(MOOK_ATTACK_RECOVERY)
|
||||||
|
icon_state = "mook"
|
||||||
|
|
||||||
|
/obj/effect/temp_visual/mook_dust
|
||||||
|
name = "dust"
|
||||||
|
desc = "it's just a dust cloud!"
|
||||||
|
icon = 'icons/mob/jungle/mook.dmi'
|
||||||
|
icon_state = "mook_leap_cloud"
|
||||||
|
layer = BELOW_MOB_LAYER
|
||||||
|
pixel_x = -16
|
||||||
|
pixel_y = -16
|
||||||
|
duration = 10
|
||||||
|
|
||||||
|
#undef MOOK_ATTACK_NEUTRAL
|
||||||
|
#undef MOOK_ATTACK_WARMUP
|
||||||
|
#undef MOOK_ATTACK_ACTIVE
|
||||||
|
#undef MOOK_ATTACK_RECOVERY
|
||||||
|
#undef ATTACK_INTERMISSION_TIME
|
||||||
@@ -0,0 +1,244 @@
|
|||||||
|
#define SEEDLING_STATE_NEUTRAL 0
|
||||||
|
#define SEEDLING_STATE_WARMUP 1
|
||||||
|
#define SEEDLING_STATE_ACTIVE 2
|
||||||
|
#define SEEDLING_STATE_RECOVERY 3
|
||||||
|
|
||||||
|
//A plant rooted in the ground that forfeits its melee attack in favor of ranged barrages.
|
||||||
|
//It will fire flurries of solar energy, and occasionally charge up a powerful blast that makes it vulnerable to attack.
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling
|
||||||
|
name = "seedling"
|
||||||
|
desc = "This oversized, predatory flower conceals what can only be described as an organic energy cannon, and it will not die until its hidden vital organs are sliced out. \
|
||||||
|
The concentrated streams of energy it sometimes produces require its full attention, attacking it during this time will prevent it from finishing its attack."
|
||||||
|
icon = 'icons/mob/jungle/seedling.dmi'
|
||||||
|
icon_state = "seedling"
|
||||||
|
icon_living = "seedling"
|
||||||
|
icon_dead = "seedling_dead"
|
||||||
|
maxHealth = 100
|
||||||
|
health = 100
|
||||||
|
melee_damage_lower = 30
|
||||||
|
melee_damage_upper = 30
|
||||||
|
pixel_x = -16
|
||||||
|
pixel_y = -14
|
||||||
|
minimum_distance = 3
|
||||||
|
move_to_delay = 20
|
||||||
|
vision_range = 9
|
||||||
|
aggro_vision_range = 15
|
||||||
|
ranged = TRUE
|
||||||
|
ranged_cooldown_time = 10
|
||||||
|
projectiletype = /obj/item/projectile/seedling
|
||||||
|
projectilesound = 'sound/weapons/pierce.ogg'
|
||||||
|
robust_searching = TRUE
|
||||||
|
stat_attack = UNCONSCIOUS
|
||||||
|
anchored = TRUE
|
||||||
|
var/combatant_state = SEEDLING_STATE_NEUTRAL
|
||||||
|
var/obj/seedling_weakpoint/weak_point
|
||||||
|
var/mob/living/beam_debuff_target
|
||||||
|
var/solar_beam_identifier = 0
|
||||||
|
|
||||||
|
/obj/item/projectile/seedling
|
||||||
|
name = "solar energy"
|
||||||
|
icon_state = "seedling"
|
||||||
|
damage = 10
|
||||||
|
damage_type = BURN
|
||||||
|
light_range = 2
|
||||||
|
flag = "energy"
|
||||||
|
light_color = LIGHT_COLOR_YELLOW
|
||||||
|
hitsound = 'sound/weapons/sear.ogg'
|
||||||
|
hitsound_wall = 'sound/weapons/effects/searwall.ogg'
|
||||||
|
nondirectional_sprite = TRUE
|
||||||
|
|
||||||
|
/obj/item/projectile/seedling/Collide(atom/A)//Stops seedlings from destroying other jungle mobs through FF
|
||||||
|
if(isliving(A))
|
||||||
|
var/mob/living/L = A
|
||||||
|
if("jungle" in L.faction)
|
||||||
|
return FALSE
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/obj/effect/temp_visual/solarbeam_killsat
|
||||||
|
name = "beam of solar energy"
|
||||||
|
icon_state = "solar_beam"
|
||||||
|
icon = 'icons/effects/beam.dmi'
|
||||||
|
layer = LIGHTING_LAYER
|
||||||
|
duration = 5
|
||||||
|
randomdir = FALSE
|
||||||
|
|
||||||
|
/datum/status_effect/seedling_beam_indicator
|
||||||
|
id = "seedling beam indicator"
|
||||||
|
duration = 30
|
||||||
|
status_type = STATUS_EFFECT_MULTIPLE
|
||||||
|
alert_type = null
|
||||||
|
tick_interval = 1
|
||||||
|
var/obj/screen/seedling/seedling_screen_object
|
||||||
|
var/atom/target
|
||||||
|
|
||||||
|
|
||||||
|
/datum/status_effect/seedling_beam_indicator/on_creation(mob/living/new_owner, target_plant)
|
||||||
|
. = ..()
|
||||||
|
if(.)
|
||||||
|
target = target_plant
|
||||||
|
tick()
|
||||||
|
|
||||||
|
/datum/status_effect/seedling_beam_indicator/on_apply()
|
||||||
|
if(owner.client)
|
||||||
|
seedling_screen_object = new /obj/screen/seedling()
|
||||||
|
owner.client.screen += seedling_screen_object
|
||||||
|
tick()
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/datum/status_effect/seedling_beam_indicator/Destroy()
|
||||||
|
if(owner)
|
||||||
|
if(owner.client)
|
||||||
|
owner.client.screen -= seedling_screen_object
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/datum/status_effect/seedling_beam_indicator/tick()
|
||||||
|
var/target_angle = Get_Angle(owner, target)
|
||||||
|
var/matrix/final = matrix()
|
||||||
|
final.Turn(target_angle)
|
||||||
|
seedling_screen_object.transform = final
|
||||||
|
|
||||||
|
/obj/screen/seedling
|
||||||
|
icon = 'icons/mob/jungle/arachnid.dmi'
|
||||||
|
icon_state = "seedling_beam_indicator"
|
||||||
|
screen_loc = "CENTER:-16,CENTER:-16"
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/Goto()
|
||||||
|
if(combatant_state != SEEDLING_STATE_NEUTRAL)
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/AttackingTarget()
|
||||||
|
if(isliving(target))
|
||||||
|
if(ranged_cooldown <= world.time && combatant_state == SEEDLING_STATE_NEUTRAL)
|
||||||
|
OpenFire(target)
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/OpenFire()
|
||||||
|
WarmupAttack()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/proc/WarmupAttack()
|
||||||
|
if(combatant_state == SEEDLING_STATE_NEUTRAL)
|
||||||
|
combatant_state = SEEDLING_STATE_WARMUP
|
||||||
|
walk(src,0)
|
||||||
|
update_icons()
|
||||||
|
var/target_dist = get_dist(src,target)
|
||||||
|
var/living_target_check = isliving(target)
|
||||||
|
if(living_target_check)
|
||||||
|
if(target_dist > 7)//Offscreen check
|
||||||
|
SolarBeamStartup(target)
|
||||||
|
return
|
||||||
|
if(get_dist(src,target) >= 4 && prob(40))
|
||||||
|
SolarBeamStartup(target)
|
||||||
|
return
|
||||||
|
addtimer(CALLBACK(src, .proc/Volley), 5)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/proc/SolarBeamStartup(mob/living/living_target)//It's more like requiem than final spark
|
||||||
|
if(combatant_state == SEEDLING_STATE_WARMUP && target)
|
||||||
|
combatant_state = SEEDLING_STATE_ACTIVE
|
||||||
|
living_target.apply_status_effect(/datum/status_effect/seedling_beam_indicator, src)
|
||||||
|
beam_debuff_target = living_target
|
||||||
|
playsound(src,'sound/effects/seedling_chargeup.ogg', 100, 0)
|
||||||
|
if(get_dist(src,living_target) > 7)
|
||||||
|
playsound(living_target,'sound/effects/seedling_chargeup.ogg', 100, 0)
|
||||||
|
solar_beam_identifier = world.time
|
||||||
|
addtimer(CALLBACK(src, .proc/Beamu, living_target, solar_beam_identifier), 35)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/proc/Beamu(mob/living/living_target, beam_id = 0)
|
||||||
|
if(combatant_state == SEEDLING_STATE_ACTIVE && living_target && beam_id == solar_beam_identifier)
|
||||||
|
if(living_target.z == z)
|
||||||
|
update_icons()
|
||||||
|
var/obj/effect/temp_visual/solarbeam_killsat/S = new (get_turf(src))
|
||||||
|
var/matrix/starting = matrix()
|
||||||
|
starting.Scale(1,32)
|
||||||
|
starting.Translate(0,520)
|
||||||
|
S.transform = starting
|
||||||
|
var/obj/effect/temp_visual/solarbeam_killsat/K = new (get_turf(living_target))
|
||||||
|
var/matrix/final = matrix()
|
||||||
|
final.Scale(1,32)
|
||||||
|
final.Translate(0,512)
|
||||||
|
K.transform = final
|
||||||
|
living_target.adjustFireLoss(30)
|
||||||
|
living_target.adjust_fire_stacks(0.2)//Just here for the showmanship
|
||||||
|
living_target.IgniteMob()
|
||||||
|
playsound(living_target,'sound/weapons/sear.ogg', 50, 1)
|
||||||
|
addtimer(CALLBACK(src, .proc/AttackRecovery), 5)
|
||||||
|
return
|
||||||
|
AttackRecovery()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/proc/Volley()
|
||||||
|
if(combatant_state == SEEDLING_STATE_WARMUP && target)
|
||||||
|
combatant_state = SEEDLING_STATE_ACTIVE
|
||||||
|
update_icons()
|
||||||
|
var/datum/callback/cb = CALLBACK(src, .proc/InaccurateShot)
|
||||||
|
for(var/i in 1 to 13)
|
||||||
|
addtimer(cb, i)
|
||||||
|
addtimer(CALLBACK(src, .proc/AttackRecovery), 14)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/proc/InaccurateShot()
|
||||||
|
if(!QDELETED(target) && combatant_state == SEEDLING_STATE_ACTIVE && !stat)
|
||||||
|
if(get_dist(src,target) <= 3)//If they're close enough just aim straight at them so we don't miss at point blank ranges
|
||||||
|
Shoot(target)
|
||||||
|
return
|
||||||
|
var/turf/our_turf = get_turf(src)
|
||||||
|
var/obj/item/projectile/seedling/readied_shot = new /obj/item/projectile/seedling(our_turf)
|
||||||
|
readied_shot.current = our_turf
|
||||||
|
readied_shot.starting = our_turf
|
||||||
|
readied_shot.firer = src
|
||||||
|
readied_shot.original = target
|
||||||
|
readied_shot.yo = target.y - our_turf.y + rand(-1,1)
|
||||||
|
readied_shot.xo = target.x - our_turf.x + rand(-1,1)
|
||||||
|
readied_shot.fire()
|
||||||
|
playsound(src, projectilesound, 100, 1)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/proc/AttackRecovery()
|
||||||
|
if(combatant_state == SEEDLING_STATE_ACTIVE)
|
||||||
|
combatant_state = SEEDLING_STATE_RECOVERY
|
||||||
|
update_icons()
|
||||||
|
ranged_cooldown = world.time + ranged_cooldown_time
|
||||||
|
if(target)
|
||||||
|
face_atom(target)
|
||||||
|
addtimer(CALLBACK(src, .proc/ResetNeutral), 10)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/proc/ResetNeutral()
|
||||||
|
combatant_state = SEEDLING_STATE_NEUTRAL
|
||||||
|
if(target && !stat)
|
||||||
|
update_icons()
|
||||||
|
Goto(target, move_to_delay, minimum_distance)
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/adjustHealth()
|
||||||
|
. = ..()
|
||||||
|
if(combatant_state == SEEDLING_STATE_ACTIVE && beam_debuff_target)
|
||||||
|
beam_debuff_target.remove_status_effect(/datum/status_effect/seedling_beam_indicator)
|
||||||
|
beam_debuff_target = null
|
||||||
|
solar_beam_identifier = 0
|
||||||
|
AttackRecovery()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/update_icons()
|
||||||
|
. = ..()
|
||||||
|
if(!stat)
|
||||||
|
switch(combatant_state)
|
||||||
|
if(SEEDLING_STATE_NEUTRAL)
|
||||||
|
icon_state = "seedling"
|
||||||
|
if(SEEDLING_STATE_WARMUP)
|
||||||
|
icon_state = "seedling_charging"
|
||||||
|
if(SEEDLING_STATE_ACTIVE)
|
||||||
|
icon_state = "seedling_fire"
|
||||||
|
if(SEEDLING_STATE_RECOVERY)
|
||||||
|
icon_state = "seedling"
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/GiveTarget()
|
||||||
|
if(target)
|
||||||
|
if(combatant_state == SEEDLING_STATE_WARMUP || combatant_state == SEEDLING_STATE_ACTIVE)//So it doesn't 180 and blast you in the face while it's firing at someone else
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
/mob/living/simple_animal/hostile/jungle/seedling/LoseTarget()
|
||||||
|
if(combatant_state == SEEDLING_STATE_WARMUP || combatant_state == SEEDLING_STATE_ACTIVE)
|
||||||
|
return
|
||||||
|
return ..()
|
||||||
|
|
||||||
|
#undef SEEDLING_STATE_NEUTRAL
|
||||||
|
#undef SEEDLING_STATE_WARMUP
|
||||||
|
#undef SEEDLING_STATE_ACTIVE
|
||||||
|
#undef SEEDLING_STATE_RECOVERY
|
||||||
@@ -1,833 +0,0 @@
|
|||||||
/mob/living/simple_animal/hostile/jungle
|
|
||||||
vision_range = 5
|
|
||||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
|
||||||
faction = list("jungle")
|
|
||||||
weather_immunities = list("acid")
|
|
||||||
obj_damage = 30
|
|
||||||
environment_smash = ENVIRONMENT_SMASH_WALLS
|
|
||||||
minbodytemp = 0
|
|
||||||
maxbodytemp = 450
|
|
||||||
response_help = "pokes"
|
|
||||||
response_disarm = "shoves"
|
|
||||||
response_harm = "strikes"
|
|
||||||
status_flags = 0
|
|
||||||
a_intent = INTENT_HARM
|
|
||||||
see_in_dark = 4
|
|
||||||
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
|
|
||||||
mob_size = MOB_SIZE_LARGE
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Mega arachnid
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mega_arachnid
|
|
||||||
name = "mega arachnid"
|
|
||||||
desc = "Though physically imposing, it prefers to ambush its prey, and it will only engage with an already crippled opponent."
|
|
||||||
melee_damage_lower = 30
|
|
||||||
melee_damage_upper = 30
|
|
||||||
maxHealth = 300
|
|
||||||
health = 300
|
|
||||||
speed = 1
|
|
||||||
ranged = 1
|
|
||||||
pixel_x = -16
|
|
||||||
move_to_delay = 10
|
|
||||||
aggro_vision_range = 9
|
|
||||||
speak_emote = list("chitters")
|
|
||||||
attack_sound = 'sound/weapons/bladeslice.ogg'
|
|
||||||
ranged_cooldown_time = 60
|
|
||||||
projectiletype = /obj/item/projectile/mega_arachnid
|
|
||||||
projectilesound = 'sound/weapons/pierce.ogg'
|
|
||||||
icon = 'icons/mob/jungle/arachnid.dmi'
|
|
||||||
icon_state = "arachnid"
|
|
||||||
icon_living = "arachnid"
|
|
||||||
icon_dead = "dead_purple"
|
|
||||||
alpha = 50
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mega_arachnid/Life()
|
|
||||||
..()
|
|
||||||
if(target && ranged_cooldown > world.time && iscarbon(target))
|
|
||||||
var/mob/living/carbon/C = target
|
|
||||||
if(!C.legcuffed && C.health < 50)
|
|
||||||
retreat_distance = 9
|
|
||||||
minimum_distance = 9
|
|
||||||
alpha = 125
|
|
||||||
return
|
|
||||||
retreat_distance = 0
|
|
||||||
minimum_distance = 0
|
|
||||||
alpha = 255
|
|
||||||
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mega_arachnid/Aggro()
|
|
||||||
..()
|
|
||||||
alpha = 255
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mega_arachnid/LoseAggro()
|
|
||||||
..()
|
|
||||||
alpha = 50
|
|
||||||
|
|
||||||
/obj/item/projectile/mega_arachnid
|
|
||||||
name = "flesh snare"
|
|
||||||
nodamage = 1
|
|
||||||
damage = 0
|
|
||||||
icon_state = "tentacle_end"
|
|
||||||
|
|
||||||
/obj/item/projectile/mega_arachnid/on_hit(atom/target, blocked = FALSE)
|
|
||||||
if(iscarbon(target) && blocked < 100)
|
|
||||||
var/obj/item/restraints/legcuffs/beartrap/mega_arachnid/B = new /obj/item/restraints/legcuffs/beartrap/mega_arachnid(get_turf(target))
|
|
||||||
B.Crossed(target)
|
|
||||||
..()
|
|
||||||
|
|
||||||
/obj/item/restraints/legcuffs/beartrap/mega_arachnid
|
|
||||||
name = "fleshy restraints"
|
|
||||||
desc = "Used by mega arachnids to immobilize their prey."
|
|
||||||
flags_1 = DROPDEL_1
|
|
||||||
icon_state = "tentacle_end"
|
|
||||||
icon = 'icons/obj/projectiles.dmi'
|
|
||||||
|
|
||||||
////Leaper////
|
|
||||||
|
|
||||||
#define PLAYER_HOP_DELAY 25
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper
|
|
||||||
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
|
||||||
faction = list("jungle")
|
|
||||||
weather_immunities = list("acid")
|
|
||||||
obj_damage = 30
|
|
||||||
environment_smash = ENVIRONMENT_SMASH_WALLS
|
|
||||||
maxHealth = 300
|
|
||||||
health = 300
|
|
||||||
minbodytemp = 0
|
|
||||||
maxbodytemp = 450
|
|
||||||
response_help = "pokes"
|
|
||||||
response_disarm = "shoves"
|
|
||||||
response_harm = "strikes"
|
|
||||||
status_flags = 0
|
|
||||||
a_intent = INTENT_HARM
|
|
||||||
see_in_dark = 4
|
|
||||||
ranged = TRUE
|
|
||||||
projectiletype = /obj/item/projectile/leaper
|
|
||||||
projectilesound = 'sound/weapons/pierce.ogg'
|
|
||||||
ranged_cooldown_time = 30
|
|
||||||
pixel_x = -16
|
|
||||||
icon = 'icons/mob/jungle/arachnid.dmi'
|
|
||||||
icon_state = "leaper"
|
|
||||||
icon_living = "leaper"
|
|
||||||
icon_dead = "leaper_dead"
|
|
||||||
layer = LARGE_MOB_LAYER
|
|
||||||
name = "leaper"
|
|
||||||
desc = "Commonly referred to as 'leapers', the Geron Toad is a massive beast that spits out highly pressurized bubbles containing a unique toxin, knocking down its prey and then crushing it with its girth."
|
|
||||||
speed = 10
|
|
||||||
stat_attack = 1
|
|
||||||
robust_searching = 1
|
|
||||||
var/hopping = FALSE
|
|
||||||
var/hop_cooldown = 0 //Strictly for player controlled leapers
|
|
||||||
var/projectile_ready = FALSE //Stopping AI leapers from firing whenever they want, and only doing it after a hop has finished instead
|
|
||||||
|
|
||||||
/obj/item/projectile/leaper
|
|
||||||
name = "leaper bubble"
|
|
||||||
icon_state = "leaper"
|
|
||||||
knockdown = 50
|
|
||||||
damage = 0
|
|
||||||
range = 7
|
|
||||||
hitsound = 'sound/effects/snap.ogg'
|
|
||||||
nondirectional_sprite = TRUE
|
|
||||||
impact_effect_type = /obj/effect/temp_visual/leaper_projectile_impact
|
|
||||||
|
|
||||||
/obj/item/projectile/leaper/on_hit(atom/target, blocked = FALSE)
|
|
||||||
..()
|
|
||||||
if(iscarbon(target))
|
|
||||||
var/mob/living/carbon/C = target
|
|
||||||
C.reagents.add_reagent("leaper_venom", 5)
|
|
||||||
return
|
|
||||||
if(isanimal(target))
|
|
||||||
var/mob/living/simple_animal/L = target
|
|
||||||
L.adjustHealth(25)
|
|
||||||
|
|
||||||
/obj/item/projectile/leaper/on_range()
|
|
||||||
var/turf/T = get_turf(src)
|
|
||||||
..()
|
|
||||||
new /obj/structure/leaper_bubble(T)
|
|
||||||
|
|
||||||
/obj/effect/temp_visual/leaper_projectile_impact
|
|
||||||
name = "leaper bubble"
|
|
||||||
icon = 'icons/obj/projectiles.dmi'
|
|
||||||
icon_state = "leaper_bubble_pop"
|
|
||||||
layer = ABOVE_ALL_MOB_LAYER
|
|
||||||
duration = 3
|
|
||||||
|
|
||||||
/obj/effect/temp_visual/leaper_projectile_impact/Initialize()
|
|
||||||
. = ..()
|
|
||||||
new /obj/effect/decal/cleanable/leaper_sludge(get_turf(src))
|
|
||||||
|
|
||||||
/obj/effect/decal/cleanable/leaper_sludge
|
|
||||||
name = "leaper sludge"
|
|
||||||
desc = "A small pool of sludge, containing trace amounts of leaper venom"
|
|
||||||
icon = 'icons/effects/tomatodecal.dmi'
|
|
||||||
icon_state = "tomato_floor1"
|
|
||||||
|
|
||||||
/obj/structure/leaper_bubble
|
|
||||||
name = "leaper bubble"
|
|
||||||
desc = "A floating bubble containing leaper venom, the contents are under a surprising amount of pressure."
|
|
||||||
icon = 'icons/obj/projectiles.dmi'
|
|
||||||
icon_state = "leaper"
|
|
||||||
max_integrity = 10
|
|
||||||
density = FALSE
|
|
||||||
|
|
||||||
/obj/structure/leaper_bubble/Initialize()
|
|
||||||
. = ..()
|
|
||||||
float(on = TRUE)
|
|
||||||
QDEL_IN(src, 100)
|
|
||||||
|
|
||||||
/obj/structure/leaper_bubble/Destroy()
|
|
||||||
new /obj/effect/temp_visual/leaper_projectile_impact(get_turf(src))
|
|
||||||
playsound(src,'sound/effects/snap.ogg',50, 1, -1)
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/obj/structure/leaper_bubble/Crossed(atom/movable/AM)
|
|
||||||
if(isliving(AM))
|
|
||||||
var/mob/living/L = AM
|
|
||||||
if(!istype(L, /mob/living/simple_animal/hostile/jungle/leaper))
|
|
||||||
playsound(src,'sound/effects/snap.ogg',50, 1, -1)
|
|
||||||
L.Knockdown(50)
|
|
||||||
if(iscarbon(L))
|
|
||||||
var/mob/living/carbon/C = L
|
|
||||||
C.reagents.add_reagent("leaper_venom", 5)
|
|
||||||
if(isanimal(L))
|
|
||||||
var/mob/living/simple_animal/A = L
|
|
||||||
A.adjustHealth(25)
|
|
||||||
qdel(src)
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/datum/reagent/toxin/leaper_venom
|
|
||||||
name = "Leaper venom"
|
|
||||||
id = "leaper_venom"
|
|
||||||
description = "A toxin spat out by leapers that while harmless in small doses, quickly creates a toxic reaction if too much is in the body."
|
|
||||||
color = "#801E28" // rgb: 128, 30, 40
|
|
||||||
toxpwr = 0
|
|
||||||
taste_description = "french cuisine"
|
|
||||||
taste_mult = 1.3
|
|
||||||
|
|
||||||
/datum/reagent/toxin/leaper_venom/on_mob_life(mob/living/M)
|
|
||||||
if(volume >= 10)
|
|
||||||
M.adjustToxLoss(5, 0)
|
|
||||||
..()
|
|
||||||
|
|
||||||
/obj/effect/temp_visual/leaper_crush
|
|
||||||
name = "Grim tidings"
|
|
||||||
desc = "Incoming leaper!"
|
|
||||||
icon = 'icons/effects/96x96.dmi'
|
|
||||||
icon_state = "lily_pad"
|
|
||||||
layer = BELOW_MOB_LAYER
|
|
||||||
pixel_x = -32
|
|
||||||
pixel_y = -32
|
|
||||||
duration = 30
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/Initialize()
|
|
||||||
. = ..()
|
|
||||||
verbs -= /mob/living/verb/pulled
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/CtrlClickOn(atom/A)
|
|
||||||
face_atom(A)
|
|
||||||
target = A
|
|
||||||
if(!isturf(loc))
|
|
||||||
return
|
|
||||||
if(next_move > world.time)
|
|
||||||
return
|
|
||||||
if(hopping)
|
|
||||||
return
|
|
||||||
if(isliving(A))
|
|
||||||
var/mob/living/L = A
|
|
||||||
if(L.incapacitated())
|
|
||||||
BellyFlop()
|
|
||||||
return
|
|
||||||
if(hop_cooldown <= world.time)
|
|
||||||
Hop(player_hop = TRUE)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/AttackingTarget()
|
|
||||||
if(isliving(target))
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/handle_automated_action()
|
|
||||||
if(hopping || projectile_ready)
|
|
||||||
return
|
|
||||||
. = ..()
|
|
||||||
if(target)
|
|
||||||
if(isliving(target))
|
|
||||||
var/mob/living/L = target
|
|
||||||
if(L.incapacitated())
|
|
||||||
BellyFlop()
|
|
||||||
return
|
|
||||||
if(!hopping)
|
|
||||||
Hop()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/Life()
|
|
||||||
. = ..()
|
|
||||||
update_icons()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
|
||||||
if(prob(33) && !ckey)
|
|
||||||
ranged_cooldown = 0 //Keeps em on their toes instead of a constant rotation
|
|
||||||
..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/OpenFire()
|
|
||||||
face_atom(target)
|
|
||||||
if(ranged_cooldown <= world.time)
|
|
||||||
if(ckey)
|
|
||||||
if(hopping)
|
|
||||||
return
|
|
||||||
if(isliving(target))
|
|
||||||
var/mob/living/L = target
|
|
||||||
if(L.incapacitated())
|
|
||||||
return //No stunlocking. Hop on them after you stun them, you donk.
|
|
||||||
if(AIStatus == AI_ON && !projectile_ready && !ckey)
|
|
||||||
return
|
|
||||||
. = ..(target)
|
|
||||||
projectile_ready = FALSE
|
|
||||||
update_icons()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/proc/Hop(player_hop = FALSE)
|
|
||||||
if(z != target.z)
|
|
||||||
return
|
|
||||||
hopping = TRUE
|
|
||||||
density = FALSE
|
|
||||||
pass_flags |= PASSMOB
|
|
||||||
notransform = TRUE
|
|
||||||
var/turf/new_turf = locate((target.x + rand(-3,3)),(target.y + rand(-3,3)),target.z)
|
|
||||||
if(player_hop)
|
|
||||||
new_turf = get_turf(target)
|
|
||||||
hop_cooldown = world.time + PLAYER_HOP_DELAY
|
|
||||||
if(AIStatus == AI_ON && ranged_cooldown <= world.time)
|
|
||||||
projectile_ready = TRUE
|
|
||||||
update_icons()
|
|
||||||
throw_at(new_turf, max(3,get_dist(src,new_turf)), 1, src, FALSE, callback = CALLBACK(src, .FinishHop))
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/proc/FinishHop()
|
|
||||||
density = TRUE
|
|
||||||
notransform = FALSE
|
|
||||||
pass_flags &= ~PASSMOB
|
|
||||||
hopping = FALSE
|
|
||||||
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 100, 1)
|
|
||||||
if(target && AIStatus == AI_ON && projectile_ready && !ckey)
|
|
||||||
face_atom(target)
|
|
||||||
addtimer(CALLBACK(src, .proc/OpenFire, target), 5)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlop()
|
|
||||||
var/turf/new_turf = get_turf(target)
|
|
||||||
hopping = TRUE
|
|
||||||
notransform = TRUE
|
|
||||||
new /obj/effect/temp_visual/leaper_crush(new_turf)
|
|
||||||
addtimer(CALLBACK(src, .proc/BellyFlopHop, new_turf), 30)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/proc/BellyFlopHop(turf/T)
|
|
||||||
density = FALSE
|
|
||||||
throw_at(T, get_dist(src,T),1,src, FALSE, callback = CALLBACK(src, .proc/Crush))
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/proc/Crush()
|
|
||||||
hopping = FALSE
|
|
||||||
density = TRUE
|
|
||||||
notransform = FALSE
|
|
||||||
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1)
|
|
||||||
for(var/mob/living/L in orange(1, src))
|
|
||||||
L.adjustBruteLoss(35)
|
|
||||||
if(!QDELETED(L)) // Some mobs are deleted on death
|
|
||||||
var/throw_dir = get_dir(src, L)
|
|
||||||
if(L.loc == loc)
|
|
||||||
throw_dir = pick(GLOB.alldirs)
|
|
||||||
var/throwtarget = get_edge_target_turf(src, throw_dir)
|
|
||||||
L.throw_at(throwtarget, 3, 1)
|
|
||||||
visible_message("<span class='warning'>[L] is thrown clear of [src]!</span>")
|
|
||||||
if(ckey)//Lessens ability to chain stun as a player
|
|
||||||
ranged_cooldown = ranged_cooldown_time + world.time
|
|
||||||
update_icons()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/Goto()
|
|
||||||
return
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/throw_impact()
|
|
||||||
return
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/leaper/update_icons()
|
|
||||||
. = ..()
|
|
||||||
if(stat)
|
|
||||||
icon_state = "leaper_dead"
|
|
||||||
return
|
|
||||||
if(ranged_cooldown <= world.time)
|
|
||||||
if(AIStatus == AI_ON && projectile_ready || ckey)
|
|
||||||
icon_state = "leaper_alert"
|
|
||||||
return
|
|
||||||
icon_state = "leaper"
|
|
||||||
|
|
||||||
#undef PLAYER_HOP_DELAY
|
|
||||||
|
|
||||||
////JUNGLE MOOK////
|
|
||||||
|
|
||||||
#define MOOK_ATTACK_NEUTRAL 0
|
|
||||||
#define MOOK_ATTACK_WARMUP 1
|
|
||||||
#define MOOK_ATTACK_ACTIVE 2
|
|
||||||
#define MOOK_ATTACK_RECOVERY 3
|
|
||||||
|
|
||||||
#define ATTACK_INTERMISSION_TIME 5
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook
|
|
||||||
name = "wanderer"
|
|
||||||
desc = "This unhealthy looking primitive is wielding a rudimentary hatchet, swinging it with wild abandon. One isn't much of a threat, but in numbers they can quickly overwhelm a superior opponent."
|
|
||||||
maxHealth = 45
|
|
||||||
health = 45
|
|
||||||
melee_damage_lower = 30
|
|
||||||
melee_damage_upper = 30
|
|
||||||
icon = 'icons/mob/jungle/arachnid.dmi'
|
|
||||||
icon_state = "mook"
|
|
||||||
icon_living = "mook"
|
|
||||||
icon_dead = "mook_dead"
|
|
||||||
pixel_x = -16
|
|
||||||
pixel_y = -8
|
|
||||||
ranged = TRUE
|
|
||||||
ranged_cooldown_time = 10
|
|
||||||
pass_flags = LETPASSTHROW
|
|
||||||
robust_searching = TRUE
|
|
||||||
stat_attack = UNCONSCIOUS
|
|
||||||
attack_sound = 'sound/weapons/rapierhit.ogg'
|
|
||||||
death_sound = 'sound/voice/mook_death.ogg'
|
|
||||||
aggro_vision_range = 15 //A little more aggressive once in combat to balance out their really low HP
|
|
||||||
var/attack_state = MOOK_ATTACK_NEUTRAL
|
|
||||||
var/struck_target_leap = FALSE
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/CanPass(atom/movable/O)
|
|
||||||
if(istype(O, /mob/living/simple_animal/hostile/jungle/mook))
|
|
||||||
var/mob/living/simple_animal/hostile/jungle/mook/M = O
|
|
||||||
if(M.attack_state == MOOK_ATTACK_ACTIVE && M.throwing)
|
|
||||||
return TRUE
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/death()
|
|
||||||
desc = "A deceased primitive. Upon closer inspection, it was suffering from severe cellular degeneration and its garments are machine made..."//Can you guess the twist
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/AttackingTarget()
|
|
||||||
if(isliving(target))
|
|
||||||
if(ranged_cooldown <= world.time && attack_state == MOOK_ATTACK_NEUTRAL)
|
|
||||||
var/mob/living/L = target
|
|
||||||
if(L.incapacitated())
|
|
||||||
WarmupAttack(forced_slash_combo = TRUE)
|
|
||||||
return
|
|
||||||
WarmupAttack()
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/Goto()
|
|
||||||
if(attack_state != MOOK_ATTACK_NEUTRAL)
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/Move()
|
|
||||||
if(attack_state == MOOK_ATTACK_WARMUP || attack_state == MOOK_ATTACK_RECOVERY)
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/proc/WarmupAttack(forced_slash_combo = FALSE)
|
|
||||||
if(attack_state == MOOK_ATTACK_NEUTRAL && target)
|
|
||||||
attack_state = MOOK_ATTACK_WARMUP
|
|
||||||
walk(src,0)
|
|
||||||
update_icons()
|
|
||||||
if(prob(50) && get_dist(src,target) <= 3 || forced_slash_combo)
|
|
||||||
addtimer(CALLBACK(src, .proc/SlashCombo), ATTACK_INTERMISSION_TIME)
|
|
||||||
return
|
|
||||||
addtimer(CALLBACK(src, .proc/LeapAttack), ATTACK_INTERMISSION_TIME + rand(0,3))
|
|
||||||
return
|
|
||||||
attack_state = MOOK_ATTACK_RECOVERY
|
|
||||||
ResetNeutral()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/proc/SlashCombo()
|
|
||||||
if(attack_state == MOOK_ATTACK_WARMUP && !stat)
|
|
||||||
attack_state = MOOK_ATTACK_ACTIVE
|
|
||||||
update_icons()
|
|
||||||
SlashAttack()
|
|
||||||
addtimer(CALLBACK(src, .proc/SlashAttack), 3)
|
|
||||||
addtimer(CALLBACK(src, .proc/SlashAttack), 6)
|
|
||||||
addtimer(CALLBACK(src, .proc/AttackRecovery), 9)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/proc/SlashAttack()
|
|
||||||
if(target && !stat && attack_state == MOOK_ATTACK_ACTIVE)
|
|
||||||
melee_damage_lower = 15
|
|
||||||
melee_damage_upper = 15
|
|
||||||
var/mob_direction = get_dir(src,target)
|
|
||||||
if(get_dist(src,target) > 1)
|
|
||||||
step(src,mob_direction)
|
|
||||||
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from) && isliving(target))
|
|
||||||
var/mob/living/L = target
|
|
||||||
L.attack_animal(src)
|
|
||||||
return
|
|
||||||
var/swing_turf = get_step(src,mob_direction)
|
|
||||||
new /obj/effect/temp_visual/kinetic_blast(swing_turf)
|
|
||||||
playsound(src, 'sound/weapons/slashmiss.ogg', 50, 1)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/proc/LeapAttack()
|
|
||||||
if(target && !stat && attack_state == MOOK_ATTACK_WARMUP)
|
|
||||||
attack_state = MOOK_ATTACK_ACTIVE
|
|
||||||
density = FALSE
|
|
||||||
melee_damage_lower = 30
|
|
||||||
melee_damage_upper = 30
|
|
||||||
update_icons()
|
|
||||||
new /obj/effect/temp_visual/mook_dust(get_turf(src))
|
|
||||||
playsound(src, 'sound/weapons/thudswoosh.ogg', 25, 1)
|
|
||||||
playsound(src, 'sound/voice/mook_leap_yell.ogg', 100, 1)
|
|
||||||
var/target_turf = get_turf(target)
|
|
||||||
throw_at(target_turf, 7, 1, src, FALSE, callback = CALLBACK(src, .proc/AttackRecovery))
|
|
||||||
return
|
|
||||||
attack_state = MOOK_ATTACK_RECOVERY
|
|
||||||
ResetNeutral()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/proc/AttackRecovery()
|
|
||||||
if(attack_state == MOOK_ATTACK_ACTIVE && !stat)
|
|
||||||
attack_state = MOOK_ATTACK_RECOVERY
|
|
||||||
density = TRUE
|
|
||||||
face_atom(target)
|
|
||||||
if(!struck_target_leap)
|
|
||||||
update_icons()
|
|
||||||
struck_target_leap = FALSE
|
|
||||||
if(prob(40))
|
|
||||||
attack_state = MOOK_ATTACK_NEUTRAL
|
|
||||||
if(target)
|
|
||||||
if(isliving(target))
|
|
||||||
var/mob/living/L = target
|
|
||||||
if(L.incapacitated() && L.stat != DEAD)
|
|
||||||
addtimer(CALLBACK(src, .proc/WarmupAttack, TRUE), ATTACK_INTERMISSION_TIME)
|
|
||||||
return
|
|
||||||
addtimer(CALLBACK(src, .proc/WarmupAttack), ATTACK_INTERMISSION_TIME)
|
|
||||||
return
|
|
||||||
addtimer(CALLBACK(src, .proc/ResetNeutral), ATTACK_INTERMISSION_TIME)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/proc/ResetNeutral()
|
|
||||||
if(attack_state == MOOK_ATTACK_RECOVERY)
|
|
||||||
attack_state = MOOK_ATTACK_NEUTRAL
|
|
||||||
ranged_cooldown = world.time + ranged_cooldown_time
|
|
||||||
update_icons()
|
|
||||||
if(target && !stat)
|
|
||||||
update_icons()
|
|
||||||
Goto(target, move_to_delay, minimum_distance)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/throw_impact(atom/hit_atom, throwingdatum)
|
|
||||||
. = ..()
|
|
||||||
if(isliving(hit_atom) && attack_state == MOOK_ATTACK_ACTIVE)
|
|
||||||
var/mob/living/L = hit_atom
|
|
||||||
if(CanAttack(L))
|
|
||||||
L.attack_animal(src)
|
|
||||||
struck_target_leap = TRUE
|
|
||||||
density = TRUE
|
|
||||||
update_icons()
|
|
||||||
var/mook_under_us = FALSE
|
|
||||||
for(var/A in get_turf(src))
|
|
||||||
if(struck_target_leap && mook_under_us)
|
|
||||||
break
|
|
||||||
if(A == src)
|
|
||||||
continue
|
|
||||||
if(isliving(A))
|
|
||||||
var/mob/living/ML = A
|
|
||||||
if(!struck_target_leap && CanAttack(ML))//Check if some joker is attempting to use rest to evade us
|
|
||||||
struck_target_leap = TRUE
|
|
||||||
ML.attack_animal(src)
|
|
||||||
density = TRUE
|
|
||||||
struck_target_leap = TRUE
|
|
||||||
update_icons()
|
|
||||||
continue
|
|
||||||
if(istype(ML, /mob/living/simple_animal/hostile/jungle/mook) && !mook_under_us)//If we land on the same tile as another mook, spread out so we don't stack our sprite on the same tile
|
|
||||||
var/mob/living/simple_animal/hostile/jungle/mook/M = ML
|
|
||||||
if(!M.stat)
|
|
||||||
mook_under_us = TRUE
|
|
||||||
var/anydir = pick(GLOB.cardinals)
|
|
||||||
Move(get_step(src, anydir), anydir)
|
|
||||||
continue
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/handle_automated_action()
|
|
||||||
if(attack_state)
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/OpenFire()
|
|
||||||
if(isliving(target))
|
|
||||||
var/mob/living/L = target
|
|
||||||
if(L.incapacitated())
|
|
||||||
return
|
|
||||||
WarmupAttack()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/mook/update_icons()
|
|
||||||
. = ..()
|
|
||||||
if(!stat)
|
|
||||||
switch(attack_state)
|
|
||||||
if(MOOK_ATTACK_NEUTRAL)
|
|
||||||
icon_state = "mook"
|
|
||||||
if(MOOK_ATTACK_WARMUP)
|
|
||||||
icon_state = "mook_warmup"
|
|
||||||
if(MOOK_ATTACK_ACTIVE)
|
|
||||||
if(!density)
|
|
||||||
icon_state = "mook_leap"
|
|
||||||
return
|
|
||||||
if(struck_target_leap)
|
|
||||||
icon_state = "mook_strike"
|
|
||||||
return
|
|
||||||
icon_state = "mook_slash_combo"
|
|
||||||
if(MOOK_ATTACK_RECOVERY)
|
|
||||||
icon_state = "mook"
|
|
||||||
|
|
||||||
/obj/effect/temp_visual/mook_dust
|
|
||||||
name = "dust"
|
|
||||||
desc = "it's just a dust cloud!"
|
|
||||||
icon = 'icons/mob/jungle/arachnid.dmi'
|
|
||||||
icon_state = "mook_leap_cloud"
|
|
||||||
layer = BELOW_MOB_LAYER
|
|
||||||
pixel_x = -16
|
|
||||||
pixel_y = -16
|
|
||||||
duration = 10
|
|
||||||
|
|
||||||
#undef MOOK_ATTACK_NEUTRAL
|
|
||||||
#undef MOOK_ATTACK_WARMUP
|
|
||||||
#undef MOOK_ATTACK_ACTIVE
|
|
||||||
#undef MOOK_ATTACK_RECOVERY
|
|
||||||
#undef ATTACK_INTERMISSION_TIME
|
|
||||||
|
|
||||||
////Jungle Seedling////
|
|
||||||
|
|
||||||
#define SEEDLING_STATE_NEUTRAL 0
|
|
||||||
#define SEEDLING_STATE_WARMUP 1
|
|
||||||
#define SEEDLING_STATE_ACTIVE 2
|
|
||||||
#define SEEDLING_STATE_RECOVERY 3
|
|
||||||
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling
|
|
||||||
name = "seedling"
|
|
||||||
desc = "This oversized, predatory flower conceals what can only be described as an organic energy cannon, and it will not die until its hidden vital organs are sliced out. \
|
|
||||||
The concentrated streams of energy it sometimes produces require its full attention, attacking it during this time will prevent it from finishing its attack."
|
|
||||||
maxHealth = 100
|
|
||||||
health = 100
|
|
||||||
melee_damage_lower = 30
|
|
||||||
melee_damage_upper = 30
|
|
||||||
icon = 'icons/mob/jungle/arachnid.dmi'
|
|
||||||
icon_state = "seedling"
|
|
||||||
icon_living = "seedling"
|
|
||||||
icon_dead = "seedling_dead"
|
|
||||||
pixel_x = -16
|
|
||||||
pixel_y = -14
|
|
||||||
minimum_distance = 3
|
|
||||||
move_to_delay = 20
|
|
||||||
vision_range = 9
|
|
||||||
aggro_vision_range = 15
|
|
||||||
ranged = TRUE
|
|
||||||
ranged_cooldown_time = 10
|
|
||||||
projectiletype = /obj/item/projectile/seedling
|
|
||||||
projectilesound = 'sound/weapons/pierce.ogg'
|
|
||||||
robust_searching = TRUE
|
|
||||||
stat_attack = UNCONSCIOUS
|
|
||||||
anchored = TRUE
|
|
||||||
var/combatant_state = SEEDLING_STATE_NEUTRAL
|
|
||||||
var/obj/seedling_weakpoint/weak_point
|
|
||||||
var/mob/living/beam_debuff_target
|
|
||||||
var/solar_beam_identifier = 0
|
|
||||||
|
|
||||||
/obj/item/projectile/seedling
|
|
||||||
name = "solar energy"
|
|
||||||
icon_state = "seedling"
|
|
||||||
damage = 10
|
|
||||||
damage_type = BURN
|
|
||||||
light_range = 2
|
|
||||||
flag = "energy"
|
|
||||||
light_color = LIGHT_COLOR_YELLOW
|
|
||||||
hitsound = 'sound/weapons/sear.ogg'
|
|
||||||
hitsound_wall = 'sound/weapons/effects/searwall.ogg'
|
|
||||||
nondirectional_sprite = TRUE
|
|
||||||
|
|
||||||
/obj/item/projectile/seedling/Collide(atom/A)//Stops seedlings from destroying other jungle mobs through FF
|
|
||||||
if(isliving(A))
|
|
||||||
var/mob/living/L = A
|
|
||||||
if("jungle" in L.faction)
|
|
||||||
return FALSE
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/obj/effect/temp_visual/solarbeam_killsat
|
|
||||||
name = "beam of solar energy"
|
|
||||||
icon_state = "solar_beam"
|
|
||||||
icon = 'icons/effects/beam.dmi'
|
|
||||||
layer = LIGHTING_LAYER
|
|
||||||
duration = 5
|
|
||||||
randomdir = FALSE
|
|
||||||
|
|
||||||
/datum/status_effect/seedling_beam_indicator
|
|
||||||
id = "seedling beam indicator"
|
|
||||||
duration = 30
|
|
||||||
status_type = STATUS_EFFECT_MULTIPLE
|
|
||||||
alert_type = null
|
|
||||||
tick_interval = 1
|
|
||||||
var/obj/screen/seedling/seedling_screen_object
|
|
||||||
var/atom/target
|
|
||||||
|
|
||||||
|
|
||||||
/datum/status_effect/seedling_beam_indicator/on_creation(mob/living/new_owner, target_plant)
|
|
||||||
. = ..()
|
|
||||||
if(.)
|
|
||||||
target = target_plant
|
|
||||||
tick()
|
|
||||||
|
|
||||||
/datum/status_effect/seedling_beam_indicator/on_apply()
|
|
||||||
if(owner.client)
|
|
||||||
seedling_screen_object = new /obj/screen/seedling()
|
|
||||||
owner.client.screen += seedling_screen_object
|
|
||||||
tick()
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/datum/status_effect/seedling_beam_indicator/Destroy()
|
|
||||||
if(owner)
|
|
||||||
if(owner.client)
|
|
||||||
owner.client.screen -= seedling_screen_object
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/datum/status_effect/seedling_beam_indicator/tick()
|
|
||||||
var/target_angle = Get_Angle(owner, target)
|
|
||||||
var/matrix/final = matrix()
|
|
||||||
final.Turn(target_angle)
|
|
||||||
seedling_screen_object.transform = final
|
|
||||||
|
|
||||||
/obj/screen/seedling
|
|
||||||
icon = 'icons/mob/jungle/arachnid.dmi'
|
|
||||||
icon_state = "seedling_beam_indicator"
|
|
||||||
screen_loc = "CENTER:-16,CENTER:-16"
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/Goto()
|
|
||||||
if(combatant_state != SEEDLING_STATE_NEUTRAL)
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/AttackingTarget()
|
|
||||||
if(isliving(target))
|
|
||||||
if(ranged_cooldown <= world.time && combatant_state == SEEDLING_STATE_NEUTRAL)
|
|
||||||
OpenFire(target)
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/OpenFire()
|
|
||||||
WarmupAttack()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/proc/WarmupAttack()
|
|
||||||
if(combatant_state == SEEDLING_STATE_NEUTRAL)
|
|
||||||
combatant_state = SEEDLING_STATE_WARMUP
|
|
||||||
walk(src,0)
|
|
||||||
update_icons()
|
|
||||||
var/target_dist = get_dist(src,target)
|
|
||||||
var/living_target_check = isliving(target)
|
|
||||||
if(living_target_check)
|
|
||||||
if(target_dist > 7)//Offscreen check
|
|
||||||
SolarBeamStartup(target)
|
|
||||||
return
|
|
||||||
if(get_dist(src,target) >= 4 && prob(40))
|
|
||||||
SolarBeamStartup(target)
|
|
||||||
return
|
|
||||||
addtimer(CALLBACK(src, .proc/Volley), 5)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/proc/SolarBeamStartup(mob/living/living_target)//It's more like requiem than final spark
|
|
||||||
if(combatant_state == SEEDLING_STATE_WARMUP && target)
|
|
||||||
combatant_state = SEEDLING_STATE_ACTIVE
|
|
||||||
living_target.apply_status_effect(/datum/status_effect/seedling_beam_indicator, src)
|
|
||||||
beam_debuff_target = living_target
|
|
||||||
playsound(src,'sound/effects/seedling_chargeup.ogg', 100, 0)
|
|
||||||
if(get_dist(src,living_target) > 7)
|
|
||||||
playsound(living_target,'sound/effects/seedling_chargeup.ogg', 100, 0)
|
|
||||||
solar_beam_identifier = world.time
|
|
||||||
addtimer(CALLBACK(src, .proc/Beamu, living_target, solar_beam_identifier), 35)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/proc/Beamu(mob/living/living_target, beam_id = 0)
|
|
||||||
if(combatant_state == SEEDLING_STATE_ACTIVE && living_target && beam_id == solar_beam_identifier)
|
|
||||||
if(living_target.z == z)
|
|
||||||
update_icons()
|
|
||||||
var/obj/effect/temp_visual/solarbeam_killsat/S = new (get_turf(src))
|
|
||||||
var/matrix/starting = matrix()
|
|
||||||
starting.Scale(1,32)
|
|
||||||
starting.Translate(0,520)
|
|
||||||
S.transform = starting
|
|
||||||
var/obj/effect/temp_visual/solarbeam_killsat/K = new (get_turf(living_target))
|
|
||||||
var/matrix/final = matrix()
|
|
||||||
final.Scale(1,32)
|
|
||||||
final.Translate(0,512)
|
|
||||||
K.transform = final
|
|
||||||
living_target.adjustFireLoss(30)
|
|
||||||
living_target.adjust_fire_stacks(0.2)//Just here for the showmanship
|
|
||||||
living_target.IgniteMob()
|
|
||||||
playsound(living_target,'sound/weapons/sear.ogg', 50, 1)
|
|
||||||
addtimer(CALLBACK(src, .proc/AttackRecovery), 5)
|
|
||||||
return
|
|
||||||
AttackRecovery()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/proc/Volley()
|
|
||||||
if(combatant_state == SEEDLING_STATE_WARMUP && target)
|
|
||||||
combatant_state = SEEDLING_STATE_ACTIVE
|
|
||||||
update_icons()
|
|
||||||
var/datum/callback/cb = CALLBACK(src, .proc/InaccurateShot)
|
|
||||||
for(var/i in 1 to 13)
|
|
||||||
addtimer(cb, i)
|
|
||||||
addtimer(CALLBACK(src, .proc/AttackRecovery), 14)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/proc/InaccurateShot()
|
|
||||||
if(!QDELETED(target) && combatant_state == SEEDLING_STATE_ACTIVE && !stat)
|
|
||||||
if(get_dist(src,target) <= 3)//If they're close enough just aim straight at them so we don't miss at point blank ranges
|
|
||||||
Shoot(target)
|
|
||||||
return
|
|
||||||
var/turf/our_turf = get_turf(src)
|
|
||||||
var/obj/item/projectile/seedling/readied_shot = new /obj/item/projectile/seedling(our_turf)
|
|
||||||
readied_shot.current = our_turf
|
|
||||||
readied_shot.starting = our_turf
|
|
||||||
readied_shot.firer = src
|
|
||||||
readied_shot.original = target
|
|
||||||
readied_shot.yo = target.y - our_turf.y + rand(-1,1)
|
|
||||||
readied_shot.xo = target.x - our_turf.x + rand(-1,1)
|
|
||||||
readied_shot.fire()
|
|
||||||
playsound(src, projectilesound, 100, 1)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/proc/AttackRecovery()
|
|
||||||
if(combatant_state == SEEDLING_STATE_ACTIVE)
|
|
||||||
combatant_state = SEEDLING_STATE_RECOVERY
|
|
||||||
update_icons()
|
|
||||||
ranged_cooldown = world.time + ranged_cooldown_time
|
|
||||||
if(target)
|
|
||||||
face_atom(target)
|
|
||||||
addtimer(CALLBACK(src, .proc/ResetNeutral), 10)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/proc/ResetNeutral()
|
|
||||||
combatant_state = SEEDLING_STATE_NEUTRAL
|
|
||||||
if(target && !stat)
|
|
||||||
update_icons()
|
|
||||||
Goto(target, move_to_delay, minimum_distance)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/adjustHealth()
|
|
||||||
. = ..()
|
|
||||||
if(combatant_state == SEEDLING_STATE_ACTIVE && beam_debuff_target)
|
|
||||||
beam_debuff_target.remove_status_effect(/datum/status_effect/seedling_beam_indicator)
|
|
||||||
beam_debuff_target = null
|
|
||||||
solar_beam_identifier = 0
|
|
||||||
AttackRecovery()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/update_icons()
|
|
||||||
. = ..()
|
|
||||||
if(!stat)
|
|
||||||
switch(combatant_state)
|
|
||||||
if(SEEDLING_STATE_NEUTRAL)
|
|
||||||
icon_state = "seedling"
|
|
||||||
if(SEEDLING_STATE_WARMUP)
|
|
||||||
icon_state = "seedling_charging"
|
|
||||||
if(SEEDLING_STATE_ACTIVE)
|
|
||||||
icon_state = "seedling_fire"
|
|
||||||
if(SEEDLING_STATE_RECOVERY)
|
|
||||||
icon_state = "seedling"
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/GiveTarget()
|
|
||||||
if(target)
|
|
||||||
if(combatant_state == SEEDLING_STATE_WARMUP || combatant_state == SEEDLING_STATE_ACTIVE)//So it doesn't 180 and blast you in the face while it's firing at someone else
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/jungle/seedling/LoseTarget()
|
|
||||||
if(combatant_state == SEEDLING_STATE_WARMUP || combatant_state == SEEDLING_STATE_ACTIVE)
|
|
||||||
return
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
#undef SEEDLING_STATE_NEUTRAL
|
|
||||||
#undef SEEDLING_STATE_WARMUP
|
|
||||||
#undef SEEDLING_STATE_ACTIVE
|
|
||||||
#undef SEEDLING_STATE_RECOVERY
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 6.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
+5
-1
@@ -1785,7 +1785,6 @@
|
|||||||
#include "code\modules\mob\living\simple_animal\hostile\hivebot.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\hivebot.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\hostile.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\hostile.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\illusion.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\illusion.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\jungle_mobs.dm"
|
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\killertomato.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\killertomato.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\mecha_pilot.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\mecha_pilot.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\mimic.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\mimic.dm"
|
||||||
@@ -1806,6 +1805,11 @@
|
|||||||
#include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\gorilla\gorilla.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\gorilla\gorilla.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\gorilla\visuals_icons.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\gorilla\visuals_icons.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\jungle\_jungle_mobs.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\jungle\leaper.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\jungle\mega_arachnid.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\jungle\mook.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\jungle\seedling.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\megafauna\blood_drunk_miner.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\megafauna\blood_drunk_miner.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm"
|
||||||
#include "code\modules\mob\living\simple_animal\hostile\megafauna\colossus.dm"
|
#include "code\modules\mob\living\simple_animal\hostile\megafauna\colossus.dm"
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
diff a/tgstation.dme b/tgstation.dme (rejected hunks)
|
||||||
|
@@ -1746,6 +1745,11 @@
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\gorilla\emotes.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\gorilla\gorilla.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\gorilla\visuals_icons.dm"
|
||||||
|
+#include "code\modules\mob\living\simple_animal\hostile\jungle\_jungle_mobs.dm"
|
||||||
|
+#include "code\modules\mob\living\simple_animal\hostile\jungle\leaper.dm"
|
||||||
|
+#include "code\modules\mob\living\simple_animal\hostile\jungle\mega_arachnid.dm"
|
||||||
|
+#include "code\modules\mob\living\simple_animal\hostile\jungle\mook.dm"
|
||||||
|
+#include "code\modules\mob\living\simple_animal\hostile\jungle\seedling.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\megafauna\blood_drunk_miner.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm"
|
||||||
|
#include "code\modules\mob\living\simple_animal\hostile\megafauna\colossus.dm"
|
||||||
Reference in New Issue
Block a user