mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
408 lines
14 KiB
Plaintext
408 lines
14 KiB
Plaintext
/*
|
|
|
|
LEGION
|
|
|
|
Legion spawns from the necropolis gate in the far north of lavaland. It is the guardian of the Necropolis and emerges from within whenever an intruder tries to enter through its gate.
|
|
Whenever Legion emerges, everything in lavaland will receive a notice via color, audio, and text. This is because Legion is powerful enough to slaughter the entirety of lavaland with little effort. LOL
|
|
|
|
It has three attacks.
|
|
Spawn Skull. Most of the time it will use this attack. Spawns a single legion skull.
|
|
Spawn Sentinel. The legion will spawn up to three sentinels, depending on its size.
|
|
CHARGE! The legion starts spinning and tries to melee the player. It will try to flick itself towards the player, dealing some damage if it hits.
|
|
|
|
When Legion dies, it will split into three smaller skulls up to three times.
|
|
If you kill all of the smaller ones it drops a staff of storms, which allows its wielder to call and disperse ash storms at will and functions as a powerful melee weapon.
|
|
|
|
Difficulty: Medium
|
|
|
|
SHITCODE AHEAD. BE ADVISED. Also comment extravaganza
|
|
|
|
*/
|
|
|
|
/mob/living/simple_animal/hostile/megafauna/legion
|
|
name = "Legion"
|
|
health = 700
|
|
maxHealth = 700
|
|
spacewalk = TRUE
|
|
icon_state = "mega_legion"
|
|
icon_living = "mega_legion"
|
|
health_doll_icon = "mega_legion"
|
|
desc = "One of many."
|
|
icon = 'icons/mob/lavaland/96x96megafauna.dmi'
|
|
attack_verb_continuous = "chomps"
|
|
attack_verb_simple = "chomp"
|
|
attack_sound = 'sound/magic/demon_attack1.ogg'
|
|
speak_emote = list("echoes")
|
|
armour_penetration = 50
|
|
melee_damage_lower = 25
|
|
melee_damage_upper = 25
|
|
speed = 1
|
|
move_to_delay = 2
|
|
ranged = 1
|
|
del_on_death = 1
|
|
retreat_distance = 5
|
|
minimum_distance = 5
|
|
ranged_cooldown_time = 10
|
|
achievement_type = /datum/award/achievement/boss/legion_kill
|
|
crusher_achievement_type = /datum/award/achievement/boss/legion_crusher
|
|
score_achievement_type = /datum/award/score/legion_score
|
|
pixel_y = -16
|
|
pixel_x = -32
|
|
loot = list(/obj/item/stack/sheet/bone = 3)
|
|
vision_range = 10
|
|
wander = FALSE
|
|
elimination = 1
|
|
appearance_flags = 0
|
|
mouse_opacity = MOUSE_OPACITY_ICON
|
|
wound_bonus = -40
|
|
bare_wound_bonus = 20
|
|
var/size = 3
|
|
var/charging = FALSE
|
|
|
|
/mob/living/simple_animal/hostile/megafauna/legion/Initialize()
|
|
. = ..()
|
|
internal = new/obj/item/gps/internal/legion(src)
|
|
|
|
/mob/living/simple_animal/hostile/megafauna/legion/GiveTarget(new_target)
|
|
. = ..()
|
|
if(target)
|
|
wander = TRUE
|
|
|
|
/mob/living/simple_animal/hostile/megafauna/legion/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
|
if(GLOB.necropolis_gate)
|
|
GLOB.necropolis_gate.toggle_the_gate(null, TRUE) //very clever.
|
|
return ..()
|
|
|
|
/mob/living/simple_animal/hostile/megafauna/legion/AttackingTarget()
|
|
. = ..()
|
|
if(. && ishuman(target))
|
|
var/mob/living/L = target
|
|
if(L.stat == UNCONSCIOUS)
|
|
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc)
|
|
A.infest(L)
|
|
|
|
/mob/living/simple_animal/hostile/megafauna/legion/OpenFire(the_target)
|
|
if(charging)
|
|
return
|
|
ranged_cooldown = world.time + ranged_cooldown_time
|
|
|
|
if(client)
|
|
switch(chosen_attack)
|
|
if(1)
|
|
create_legion_skull()
|
|
if(2)
|
|
charge_target()
|
|
if(3)
|
|
create_legion_turrets()
|
|
return
|
|
|
|
switch(rand(4)) //Larger skulls use more attacks.
|
|
if(0 to 2)
|
|
create_legion_skull()
|
|
if(3)
|
|
charge_target()
|
|
if(4)
|
|
create_legion_turrets()
|
|
|
|
//SKULLS
|
|
|
|
///Attack proc. Spawns a singular legion skull.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/proc/create_legion_skull()
|
|
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc)
|
|
A.GiveTarget(target)
|
|
A.friends = friends
|
|
A.faction = faction
|
|
|
|
//CHARGE
|
|
|
|
///Attack proc. Gives legion some movespeed buffs and switches the AI to melee. At lower sizes, this also throws the skull at the player.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/proc/charge_target()
|
|
visible_message("<span class='warning'><b>[src] charges!</b></span>")
|
|
SpinAnimation(speed = 20, loops = 3, parallel = FALSE)
|
|
ranged = FALSE
|
|
retreat_distance = 0
|
|
minimum_distance = 0
|
|
set_varspeed(0)
|
|
charging = TRUE
|
|
addtimer(CALLBACK(src, .proc/reset_charge), 60)
|
|
var/mob/living/L = target
|
|
if(!istype(L) || L.stat != DEAD) //I know, weird syntax, but it just works.
|
|
addtimer(CALLBACK(src, .proc/throw_thyself), 20)
|
|
|
|
///This is the proc that actually does the throwing. Charge only adds a timer for this.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/proc/throw_thyself()
|
|
playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, TRUE)
|
|
throw_at(target, 7, 1.1, src, FALSE, FALSE, CALLBACK(GLOBAL_PROC, .proc/playsound, src, 'sound/effects/meteorimpact.ogg', 50 * size, TRUE, 2), INFINITY)
|
|
|
|
///Deals some extra damage on throw impact.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/throw_impact(mob/living/hit_atom, datum/thrownthing/throwingdatum)
|
|
. = ..()
|
|
if(istype(hit_atom))
|
|
playsound(src, attack_sound, 100, TRUE)
|
|
hit_atom.apply_damage(22 * size / 2) //It gets pretty hard to dodge the skulls when there are a lot of them. Scales down with size
|
|
hit_atom.safe_throw_at(get_step(src, get_dir(src, hit_atom)), 2) //Some knockback. Prevent the legion from melee directly after the throw.
|
|
|
|
//TURRETS
|
|
|
|
///Attack proc. Creates up to three legion turrets on suitable turfs nearby.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/proc/create_legion_turrets(minimum = 2, maximum = size * 2)
|
|
playsound(src, 'sound/magic/RATTLEMEBONES.ogg', 100, TRUE)
|
|
var/list/possiblelocations = list()
|
|
for(var/turf/T in oview(src, 4)) //Only place the turrets on open turfs
|
|
if(is_blocked_turf(T))
|
|
continue
|
|
possiblelocations += T
|
|
for(var/i in 1 to min(rand(minimum, maximum), LAZYLEN(possiblelocations))) //Makes sure aren't spawning in nullspace.
|
|
var/chosen = pick(possiblelocations)
|
|
new /obj/structure/legionturret(chosen)
|
|
possiblelocations -= chosen
|
|
|
|
/mob/living/simple_animal/hostile/megafauna/legion/GiveTarget(new_target)
|
|
. = ..()
|
|
if(target)
|
|
wander = TRUE
|
|
|
|
///This makes sure that the legion door opens on taking damage, so you can't cheese this boss.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
|
|
if(GLOB.necropolis_gate && true_spawn)
|
|
GLOB.necropolis_gate.toggle_the_gate(null, TRUE) //very clever.
|
|
return ..()
|
|
|
|
///In addition to parent functionality, this will also turn the target into a small legion if they are unconcious.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/AttackingTarget()
|
|
. = ..()
|
|
if(. && ishuman(target))
|
|
var/mob/living/L = target
|
|
if(L.stat == UNCONSCIOUS)
|
|
var/mob/living/simple_animal/hostile/asteroid/hivelordbrood/legion/A = new(loc)
|
|
A.infest(L)
|
|
|
|
///Resets the charge buffs.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/proc/reset_charge()
|
|
ranged = TRUE
|
|
retreat_distance = 5
|
|
minimum_distance = 5
|
|
set_varspeed(2)
|
|
charging = FALSE
|
|
|
|
///Special snowflake death() here. Can only die if size is 1 or lower and HP is 0 or below.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/death()
|
|
//Make sure we didn't get cheesed
|
|
if(health > 0)
|
|
return
|
|
if(Split())
|
|
return
|
|
//We check what loot we should drop.
|
|
var/last_legion = TRUE
|
|
for(var/mob/living/simple_animal/hostile/megafauna/legion/other in GLOB.mob_living_list)
|
|
if(other != src)
|
|
last_legion = FALSE
|
|
break
|
|
if(last_legion)
|
|
loot = list(/obj/item/staff/storm)
|
|
elimination = FALSE
|
|
else if(prob(20)) //20% chance for sick lootz.
|
|
loot = list(/obj/structure/closet/crate/necropolis/tendril/all)
|
|
if(!true_spawn)
|
|
loot = null
|
|
return ..()
|
|
|
|
///Splits legion into smaller skulls.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/proc/Split()
|
|
size--
|
|
if(size < 1)
|
|
return FALSE
|
|
adjustHealth(-maxHealth) //We heal in preparation of the split
|
|
switch(size) //Yay, switches
|
|
if(3 to INFINITY)
|
|
icon = initial(icon)
|
|
pixel_x = initial(pixel_x)
|
|
pixel_y = initial(pixel_y)
|
|
maxHealth = initial(maxHealth)
|
|
if(2)
|
|
icon = 'icons/mob/lavaland/64x64megafauna.dmi'
|
|
pixel_x = -16
|
|
pixel_y = -8
|
|
maxHealth = 350
|
|
if(1)
|
|
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
|
pixel_x = 0
|
|
pixel_y = 0
|
|
maxHealth = 200
|
|
adjustHealth(0) //Make the health HUD look correct.
|
|
visible_message("<span class='boldannounce'>This is getting out of hands. Now there are three of them!</span>")
|
|
for(var/i in 1 to 2) //Create three skulls in total
|
|
var/mob/living/simple_animal/hostile/megafauna/legion/L = new(loc)
|
|
L.setVarsAfterSplit(src)
|
|
return TRUE
|
|
|
|
///Sets the variables for new legion skulls. Usually called after splitting.
|
|
/mob/living/simple_animal/hostile/megafauna/legion/proc/setVarsAfterSplit(var/mob/living/simple_animal/hostile/megafauna/legion/L)
|
|
maxHealth = L.maxHealth
|
|
updatehealth()
|
|
size = L.size
|
|
icon = L.icon
|
|
pixel_x = L.pixel_x
|
|
pixel_y = L.pixel_y
|
|
faction = L.faction.Copy()
|
|
GiveTarget(L.target)
|
|
|
|
/obj/item/gps/internal/legion
|
|
icon_state = null
|
|
gpstag = "Echoing Signal"
|
|
desc = "The message repeats."
|
|
invisibility = 100
|
|
|
|
/obj/item/gps/internal/legion
|
|
icon_state = null
|
|
gpstag = "Echoing Signal"
|
|
desc = "The message repeats."
|
|
invisibility = 100
|
|
|
|
|
|
//Loot
|
|
|
|
/obj/item/staff/storm
|
|
name = "staff of storms"
|
|
desc = "An ancient staff retrieved from the remains of Legion. The wind stirs as you move it."
|
|
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
|
icon_state = "staffofstorms"
|
|
item_state = "staffofstorms"
|
|
icon = 'icons/obj/guns/magic.dmi'
|
|
slot_flags = ITEM_SLOT_BACK
|
|
w_class = WEIGHT_CLASS_BULKY
|
|
force = 25
|
|
damtype = BURN
|
|
hitsound = 'sound/weapons/sear.ogg'
|
|
var/storm_type = /datum/weather/ash_storm
|
|
var/storm_cooldown = 0
|
|
var/static/list/excluded_areas = list(/area/reebe/city_of_cogs)
|
|
|
|
/obj/item/staff/storm/attack_self(mob/user)
|
|
if(storm_cooldown > world.time)
|
|
to_chat(user, "<span class='warning'>The staff is still recharging!</span>")
|
|
return
|
|
|
|
var/area/user_area = get_base_area(user)
|
|
var/turf/user_turf = get_turf(user)
|
|
if(!user_area || !user_turf || (user_area.type in excluded_areas))
|
|
to_chat(user, "<span class='warning'>Something is preventing you from using the staff here.</span>")
|
|
return
|
|
var/datum/weather/A
|
|
for(var/V in SSweather.processing)
|
|
var/datum/weather/W = V
|
|
if((user_turf.z in W.impacted_z_levels) && W.area_type == user_area.type)
|
|
A = W
|
|
break
|
|
|
|
if(A)
|
|
if(A.stage != END_STAGE)
|
|
if(A.stage == WIND_DOWN_STAGE)
|
|
to_chat(user, "<span class='warning'>The storm is already ending! It would be a waste to use the staff now.</span>")
|
|
return
|
|
user.visible_message("<span class='warning'>[user] holds [src] skywards as an orange beam travels into the sky!</span>", \
|
|
"<span class='notice'>You hold [src] skyward, dispelling the storm!</span>")
|
|
playsound(user, 'sound/magic/staff_change.ogg', 200, 0)
|
|
A.wind_down()
|
|
log_game("[user] ([key_name(user)]) has dispelled a storm at [AREACOORD(user_turf)]")
|
|
return
|
|
else
|
|
A = new storm_type(list(user_turf.z))
|
|
A.name = "staff storm"
|
|
log_game("[user] ([key_name(user)]) has summoned [A] at [AREACOORD(user_turf)]")
|
|
if (is_special_character(user))
|
|
message_admins("[A] has been summoned in [ADMIN_VERBOSEJMP(user_turf)] by [user] ([key_name_admin(user)], a non-antagonist")
|
|
A.area_type = user_area.type
|
|
A.telegraph_duration = 100
|
|
A.end_duration = 100
|
|
|
|
user.visible_message("<span class='warning'>[user] holds [src] skywards as red lightning crackles into the sky!</span>", \
|
|
"<span class='notice'>You hold [src] skyward, calling down a terrible storm!</span>")
|
|
playsound(user, 'sound/magic/staff_change.ogg', 200, 0)
|
|
A.telegraph()
|
|
storm_cooldown = world.time + 200
|
|
|
|
///A basic turret that shoots at nearby mobs. Intended to be used for the legion megafauna.
|
|
/obj/structure/legionturret
|
|
name = "\improper Legion sentinel"
|
|
desc = "The eye pierces your soul."
|
|
icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
|
|
icon_state = "legion_turret"
|
|
light_power = 0.5
|
|
light_range = 2
|
|
max_integrity = 80
|
|
luminosity = 6
|
|
anchored = TRUE
|
|
density = TRUE
|
|
layer = ABOVE_OBJ_LAYER
|
|
armor = list("melee" = 0, "bullet" = 0, "laser" = 100,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
|
|
///What kind of projectile the actual damaging part should be.
|
|
var/projectile_type = /obj/item/projectile/beam/legion
|
|
///Time until the tracer gets shot
|
|
var/initial_firing_time = 18
|
|
///How long it takes between shooting the tracer and the projectile.
|
|
var/shot_delay = 8
|
|
///Compared with the targeted mobs. If they have the faction, turret won't shoot.
|
|
var/faction = list("mining")
|
|
|
|
/obj/structure/legionturret/Initialize()
|
|
. = ..()
|
|
addtimer(CALLBACK(src, .proc/set_up_shot), initial_firing_time)
|
|
|
|
///Handles an extremely basic AI
|
|
/obj/structure/legionturret/proc/set_up_shot()
|
|
for(var/mob/living/L in oview(9, src))
|
|
if(L.stat == DEAD || L.stat == UNCONSCIOUS)
|
|
continue
|
|
if(faction_check(faction, L.faction))
|
|
continue
|
|
fire(L)
|
|
return
|
|
fire(get_edge_target_turf(src, pick(GLOB.cardinals)))
|
|
|
|
///Called when attacking a target. Shoots a projectile at the turf underneath the target.
|
|
/obj/structure/legionturret/proc/fire(atom/target)
|
|
var/turf/T = get_turf(target)
|
|
var/turf/T1 = get_turf(src)
|
|
if(!T || !T1)
|
|
return
|
|
//Now we generate the tracer.
|
|
var/angle = Get_Angle(T1, T)
|
|
var/datum/point/vector/V = new(T1.x, T1.y, T1.z, 0, 0, angle)
|
|
generate_tracer_between_points(V, V.return_vector_after_increments(6), /obj/effect/projectile/tracer/legion/tracer, 0, shot_delay, 0, 0, 0, null)
|
|
playsound(src, 'sound/machines/airlockopen.ogg', 100, TRUE)
|
|
addtimer(CALLBACK(src, .proc/fire_beam, angle), shot_delay)
|
|
|
|
///Called shot_delay after the turret shot the tracer. Shoots a projectile into the same direction.
|
|
/obj/structure/legionturret/proc/fire_beam(angle)
|
|
var/obj/item/projectile/ouchie = new projectile_type(loc)
|
|
ouchie.firer = src
|
|
ouchie.fire(angle)
|
|
playsound(src, 'sound/effects/bin_close.ogg', 100, TRUE)
|
|
QDEL_IN(src, 5)
|
|
|
|
///Used for the legion turret.
|
|
/obj/item/projectile/beam/legion
|
|
name = "blood pulse"
|
|
hitsound = 'sound/magic/magic_missile.ogg'
|
|
damage = 19
|
|
range = 6
|
|
eyeblur = 0
|
|
light_color = LIGHT_COLOR_RED
|
|
impact_effect_type = /obj/effect/temp_visual/kinetic_blast
|
|
tracer_type = /obj/effect/projectile/tracer/legion
|
|
muzzle_type = /obj/effect/projectile/tracer/legion
|
|
impact_type = /obj/effect/projectile/tracer/legion
|
|
hitscan = TRUE
|
|
projectile_piercing = ALL
|
|
|
|
///Used for the legion turret tracer.
|
|
/obj/effect/projectile/tracer/legion/tracer
|
|
icon = 'icons/effects/beam.dmi'
|
|
icon_state = "blood_light"
|
|
|
|
///Used for the legion turret beam.
|
|
/obj/effect/projectile/tracer/legion
|
|
icon = 'icons/effects/beam.dmi'
|
|
icon_state = "blood"
|