Merge remote-tracking branch 'upstream/master' into too-many-monkeys-jumping-on-the-bed

This commit is contained in:
Coul
2019-09-15 10:46:27 -04:00
108 changed files with 2470 additions and 1312 deletions
@@ -59,7 +59,8 @@ In all, this is a lot like the monkey code. /N
return 0
/mob/living/carbon/alien/attack_animal(mob/living/simple_animal/M)
if(..())
. = ..()
if(.)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
switch(M.melee_damage_type)
if(BRUTE)
@@ -328,7 +328,7 @@ This function restores all organs.
return 0
/mob/living/carbon/human/proc/get_organ(zone)
/mob/living/carbon/human/get_organ(zone)
if(!zone)
zone = "chest"
if(zone in list("eyes", "mouth"))
@@ -437,16 +437,17 @@ emp_act
visible_message("<span class='danger'>[M] has tried to disarm [src]!</span>")
/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M)
if(..())
. = ..()
if(.)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
if(check_shields(M, damage, "the [M.name]", null, MELEE_ATTACK, M.armour_penetration))
return 0
if(check_shields(M, damage, "the [M.name]", MELEE_ATTACK, M.armour_penetration))
return FALSE
var/dam_zone = pick("head", "chest", "groin", "l_arm", "l_hand", "r_arm", "r_hand", "l_leg", "l_foot", "r_leg", "r_foot")
var/obj/item/organ/external/affecting = get_organ(ran_zone(dam_zone))
if(!affecting)
affecting = get_organ("chest")
affecting.add_autopsy_data(M.name, damage) // Add the mob's name to the autopsy data
var/armor = run_armor_check(affecting, "melee", armour_penetration = M.armour_penetration)
var/obj/item/organ/external/affected = src.get_organ(dam_zone)
if(affected)
affected.add_autopsy_data(M.name, damage) // Add the mob's name to the autopsy data
apply_damage(damage, M.melee_damage_type, affecting, armor)
updatehealth("animal attack")
@@ -519,7 +519,7 @@
to_chat(M, "<span class='warning'>You can't use your hand.</span>")
return
if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(0, M.name, attack_type = UNARMED_ATTACK))
if((M != H) && M.a_intent != INTENT_HELP && H.check_shields(M, 0, M.name, attack_type = UNARMED_ATTACK))
add_attack_logs(M, H, "Melee attacked with fists (miss/block)")
H.visible_message("<span class='warning'>[M] attempted to touch [H]!</span>")
return FALSE
@@ -258,7 +258,8 @@
M.updatehealth()
/mob/living/carbon/slime/attack_animal(mob/living/simple_animal/M)
if(..())
. = ..()
if(.)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
attacked += 10
adjustBruteLoss(damage)
+9 -9
View File
@@ -303,17 +303,17 @@
return
/mob/living/attack_animal(mob/living/simple_animal/M)
M.face_atom(src)
if((M.a_intent == INTENT_HELP && M.ckey) || M.melee_damage_upper == 0)
M.custom_emote(1, "[M.friendly] [src].")
return 0
else
if(M.attack_sound)
playsound(loc, M.attack_sound, 50, 1, 1)
M.do_attack_animation(src)
visible_message("<span class='danger'>\The [M] [M.attacktext] [src]!</span>", \
"<span class='userdanger'>\The [M] [M.attacktext] [src]!</span>")
add_attack_logs(M, src, "Animal attacked")
return 1
return FALSE
if(M.attack_sound)
playsound(loc, M.attack_sound, 50, 1, 1)
M.do_attack_animation(src)
visible_message("<span class='danger'>\The [M] [M.attacktext] [src]!</span>", \
"<span class='userdanger'>\The [M] [M.attacktext] [src]!</span>")
add_attack_logs(M, src, "Animal attacked")
return TRUE
/mob/living/attack_larva(mob/living/carbon/alien/larva/L)
switch(L.a_intent)
+2 -8
View File
@@ -227,14 +227,8 @@
// See software.dm for Topic()
/mob/living/silicon/pai/attack_animal(mob/living/simple_animal/M)
if((M.a_intent == INTENT_HELP && M.ckey) || M.melee_damage_upper == 0)
M.custom_emote(1, "[M.friendly] [src].")
else
M.do_attack_animation(src)
if(M.attack_sound)
playsound(loc, M.attack_sound, 50, 1, 1)
for(var/mob/O in viewers(src, null))
O.show_message("<span class='danger'>[M]</span> [M.attacktext] [src]!", 1)
. = ..()
if(.)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
add_attack_logs(M, src, "Animal attacked for [damage] damage")
adjustBruteLoss(damage)
@@ -20,7 +20,8 @@
return
/mob/living/silicon/attack_animal(mob/living/simple_animal/M)
if(..())
. = ..()
if(.)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
switch(M.melee_damage_type)
if(BRUTE)
@@ -53,9 +53,10 @@
L.amount_grown = min(L.amount_grown + damage, L.max_grown)
/mob/living/simple_animal/attack_animal(mob/living/simple_animal/M)
if(..())
. = ..()
if(.)
var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
attack_threshold_check(damage, M.melee_damage_type)
return attack_threshold_check(damage, M.melee_damage_type)
/mob/living/simple_animal/attack_slime(mob/living/carbon/slime/M)
..()
@@ -80,7 +80,7 @@
else
to_chat(M, "<span class='cult'>You cannot repair your own dents, as you have none!</span>")
else if(src != M)
..()
return ..()
/mob/living/simple_animal/hostile/construct/narsie_act()
@@ -30,7 +30,7 @@ Difficulty: Medium
icon = 'icons/mob/alienqueen.dmi'
light_color = "#E4C7C5"
speak_emote = list("roars")
speed = 1
speed = 3
move_to_delay = 3
projectiletype = /obj/item/projectile/kinetic/miner
projectilesound = 'sound/weapons/kenetic_accel.ogg'
@@ -1,3 +1,8 @@
#define BUBBLEGUM_SMASH (health <= maxHealth * 0.5) // angery
#define BUBBLEGUM_CAN_ENRAGE (enrage_till + (enrage_time * 2) <= world.time)
#define BUBBLEGUM_IS_ENRAGED (enrage_till > world.time)
/*
BUBBLEGUM
@@ -5,14 +10,16 @@ BUBBLEGUM
Bubblegum spawns randomly wherever a lavaland creature is able to spawn. It is the most powerful slaughter demon in existence.
Bubblegum's footsteps are heralded by shaking booms, proving its tremendous size.
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage.
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power
It tries to strike at its target through any bloodpools under them; if it fails to do that, it will spray blood and then attempt to warp to a bloodpool near the target.
If it fails to warp to a target, it may summon up to 6 slaughterlings from the blood around it.
If it does not summon all 6 slaughterlings, it will instead charge at its target, dealing massive damage to anything it hits and spraying a stream of blood.
At half health, it will either charge three times or warp, then charge, instead of doing a single charge.
It leaves blood trails behind wherever it goes, its clones do as well.
It tries to strike at its target through any bloodpools under them; if it fails to do that.
If it does warp it will enter an enraged state, becoming immune to all projectiles, becoming much faster, and dealing damage and knockback to anything that gets in the cloud around it.
It may summon clones charging from all sides, one of these charges being bubblegum himself.
It can charge at its target, and also heavily damaging anything directly hit in the charge.
If at half health it will start to charge from all sides with clones.
When Bubblegum dies, it leaves behind a chest that can contain three things:
When Bubblegum dies, it leaves behind a H.E.C.K. mining suit as well as a chest that can contain three things:
1. A bottle that, when activated, drives everyone nearby into a frenzy
2. A contract that marks for death the chosen target
3. A spellblade that can slice off limbs at range
@@ -37,19 +44,31 @@ Difficulty: Hard
armour_penetration = 40
melee_damage_lower = 40
melee_damage_upper = 40
speed = 1
move_to_delay = 10
ranged = 1
speed = 5
move_to_delay = 5
retreat_distance = 5
minimum_distance = 5
rapid_melee = 8 // every 1/4 second
melee_queue_distance = 20 // as far as possible really, need this because of blood warp
ranged = TRUE
pixel_x = -32
del_on_death = 1
del_on_death = TRUE
crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher)
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
var/charging = 0
blood_volume = BLOOD_VOLUME_MAXIMUM //BLEED FOR ME
var/charging = FALSE
var/enrage_till = 0
var/enrage_time = 70
var/revving_charge = FALSE
internal_type = /obj/item/gps/internal/bubblegum
medal_type = BOSS_MEDAL_BUBBLEGUM
score_type = BUBBLEGUM_SCORE
deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... "
death_sound = 'sound/misc/enter_blood.ogg'
attack_action_types = list(/datum/action/innate/megafauna_attack/triple_charge,
/datum/action/innate/megafauna_attack/hallucination_charge,
/datum/action/innate/megafauna_attack/hallucination_surround,
/datum/action/innate/megafauna_attack/blood_warp)
/obj/item/gps/internal/bubblegum
icon_state = null
@@ -57,54 +76,6 @@ Difficulty: Hard
desc = "You're not quite sure how a signal can be bloody."
invisibility = 100
/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount)
if(amount > 0 && prob(25))
var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc)
if(prob(40))
step(B, pick(cardinal))
else
B.dir = pick(cardinal)
. = ..()
/obj/effect/decal/cleanable/blood/gibs/bubblegum
name = "thick blood"
desc = "Thick, splattered blood."
random_icon_states = list("gib3", "gib5", "gib6")
amount = 20
/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/Life(seconds, times_fired)
..()
move_to_delay = Clamp((health/maxHealth) * 10, 5, 10)
/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire()
anger_modifier = Clamp(((maxHealth - health)/60),0,20)
if(charging)
return
ranged_cooldown = world.time + ranged_cooldown_time
var/warped = FALSE
if(!try_bloodattack())
spawn(0)
blood_spray()
warped = blood_warp()
if(warped && prob(100 - anger_modifier))
return
if(prob(90 - anger_modifier) || slaughterlings())
if(health > maxHealth * 0.5)
spawn(0)
charge()
else
if(prob(70) || warped)
spawn(0)
triple_charge()
else
spawn(0)
warp_charge()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize(mapload)
. = ..()
if(true_spawn)
@@ -112,88 +83,118 @@ Difficulty: Hard
if(B != src)
qdel(src) //There can be only one
return
var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
AddSpell(bloodspell)
if(istype(loc, /obj/effect/dummy/slaughter))
bloodspell.phased = 1
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, used_item, no_effect, end_pixel_y)
if(!charging)
..()
/datum/action/innate/megafauna_attack/triple_charge
name = "Triple Charge"
icon_icon = 'icons/mob/actions/actions.dmi'
button_icon_state = "sniper_zoom"
chosen_message = "<span class='colossus'>You are now triple charging at the target you click on.</span>"
chosen_attack_num = 1
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget()
if(!charging)
return ..()
/datum/action/innate/megafauna_attack/hallucination_charge
name = "Hallucination Charge"
icon_icon = 'icons/effects/bubblegum.dmi'
button_icon_state = "smack ya one"
chosen_message = "<span class='colossus'>You are now charging with hallucinations at the target you click on.</span>"
chosen_attack_num = 2
/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance)
if(!charging)
..()
/datum/action/innate/megafauna_attack/hallucination_surround
name = "Surround Target"
icon_icon = 'icons/turf/walls/wall.dmi'
button_icon_state = "wall"
chosen_message = "<span class='colossus'>You are now surrounding the target you click on with hallucinations.</span>"
chosen_attack_num = 3
/mob/living/simple_animal/hostile/megafauna/bubblegum/MoveToTarget(list/possible_targets)
if(!charging)
..()
/datum/action/innate/megafauna_attack/blood_warp
name = "Blood Warp"
icon_icon = 'icons/effects/blood.dmi'
button_icon_state = "floor1"
chosen_message = "<span class='colossus'>You are now warping to blood around your clicked position.</span>"
chosen_attack_num = 4
/mob/living/simple_animal/hostile/megafauna/bubblegum/Move()
/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire()
if(charging)
new /obj/effect/temp_visual/decoy/fading(loc, src)
DestroySurroundings()
. = ..()
if(!stat && .)
playsound(src, 'sound/effects/meteorimpact.ogg', 200, 1, 2, 1)
if(charging)
DestroySurroundings()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/warp_charge()
blood_warp()
charge()
anger_modifier = Clamp(((maxHealth - health)/60),0,20)
enrage_time = initial(enrage_time) * Clamp(anger_modifier / 20, 0.5, 1)
ranged_cooldown = world.time + 50
if(client)
switch(chosen_attack)
if(1)
triple_charge()
if(2)
hallucination_charge()
if(3)
surround_with_hallucinations()
if(4)
blood_warp()
return
if(!try_bloodattack() || prob(25 + anger_modifier))
blood_warp()
if(!BUBBLEGUM_SMASH)
triple_charge()
else
if(prob(50 + anger_modifier))
hallucination_charge()
else
surround_with_hallucinations()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge()
charge()
charge()
charge()
charge(delay = 6)
charge(delay = 4)
charge(delay = 2)
SetRecoveryTime(15)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge()
var/turf/T = get_turf(target)
if(!T || T == loc)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/hallucination_charge()
if(!BUBBLEGUM_SMASH || prob(33))
hallucination_charge_around(times = 6, delay = 8)
SetRecoveryTime(10)
else
hallucination_charge_around(times = 4, delay = 9)
hallucination_charge_around(times = 4, delay = 8)
hallucination_charge_around(times = 4, delay = 7)
triple_charge()
SetRecoveryTime(20)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/surround_with_hallucinations()
for(var/i = 1 to 5)
INVOKE_ASYNC(src, .proc/hallucination_charge_around, 2, 8, 2, 0, 4)
if(ismob(target))
charge(delay = 6)
else
SLEEP_CHECK_DEATH(6)
SetRecoveryTime(20)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(atom/chargeat = target, delay = 3, chargepast = 2)
if(!chargeat)
return
new /obj/effect/temp_visual/dragon_swoop(T)
charging = 1
var/chargeturf = get_turf(chargeat)
if(!chargeturf)
return
var/dir = get_dir(src, chargeturf)
var/turf/T = get_ranged_target_turf(chargeturf, dir, chargepast)
if(!T)
return
new /obj/effect/temp_visual/dragon_swoop/bubblegum(T)
charging = TRUE
revving_charge = TRUE
DestroySurroundings()
walk(src, 0)
dir = get_dir(src, T)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc, src)
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 5)
sleep(5)
throw_at(T, get_dist(src, T), 1, src, 0)
charging = 0
setDir(dir)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src)
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3)
SLEEP_CHECK_DEATH(delay)
revving_charge = FALSE
var/movespeed = 0.7
walk_towards(src, T, movespeed)
SLEEP_CHECK_DEATH(get_dist(src, T) * movespeed)
walk(src, 0) // cancel the movement
try_bloodattack()
if(target)
Goto(target, move_to_delay, minimum_distance)
/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A)
if(charging)
if(isturf(A) || isobj(A) && A.density)
A.ex_act(2)
DestroySurroundings()
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/throw_impact(atom/A)
if(!charging)
return ..()
else if(isliving(A))
var/mob/living/L = A
L.visible_message("<span class='danger'>[src] slams into [L]!</span>", "<span class='userdanger'>[src] slams into you!</span>")
L.apply_damage(40, BRUTE)
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, 1)
shake_camera(L, 4, 3)
shake_camera(src, 2, 3)
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(L, src)))
L.throw_at(throwtarget, 3)
charging = 0
charging = FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood()
var/list/targets = ListTargets()
@@ -206,9 +207,7 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/try_bloodattack()
var/list/targets = get_mobs_on_blood()
if(targets.len)
spawn(0)
bloodattack(targets, prob(50))
INVOKE_ASYNC(src, .proc/bloodattack, targets, prob(50))
return TRUE
return FALSE
@@ -249,14 +248,14 @@ Difficulty: Hard
new /obj/effect/temp_visual/bubblegum_hands/rightsmack(T)
else
new /obj/effect/temp_visual/bubblegum_hands/leftsmack(T)
sleep(2.5)
SLEEP_CHECK_DEATH(4)
for(var/mob/living/L in T)
if(!faction_check_mob(L))
to_chat(L, "<span class='userdanger'>[src] rends you!</span>")
playsound(T, attack_sound, 100, 1, -1)
var/limb_to_hit = pick("head", "chest", "r_arm", "l_arm", "r_leg", "l_leg")
L.apply_damage(25, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
sleep(3)
playsound(T, attack_sound, 100, TRUE, -1)
var/limb_to_hit = L.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
L.apply_damage(10, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
SLEEP_CHECK_DEATH(3)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodgrab(turf/T, handedness)
if(handedness)
@@ -265,18 +264,221 @@ Difficulty: Hard
else
new /obj/effect/temp_visual/bubblegum_hands/leftpaw(T)
new /obj/effect/temp_visual/bubblegum_hands/leftthumb(T)
sleep(6)
SLEEP_CHECK_DEATH(6)
for(var/mob/living/L in T)
if(!faction_check_mob(L))
to_chat(L, "<span class='userdanger'>[src] drags you through the blood!</span>")
playsound(T, 'sound/misc/enter_blood.ogg', 100, 1, -1)
var/turf/targetturf = get_step(src, dir)
L.forceMove(targetturf)
playsound(targetturf, 'sound/misc/exit_blood.ogg', 100, 1, -1)
if(L.stat != CONSCIOUS)
spawn(2)
devour(L)
sleep(1)
to_chat(L, "<span class='userdanger'>[src] drags you through the blood!</span>")
playsound(T, 'sound/misc/enter_blood.ogg', 100, TRUE, -1)
var/turf/targetturf = get_step(src, dir)
L.forceMove(targetturf)
playsound(targetturf, 'sound/misc/exit_blood.ogg', 100, TRUE, -1)
addtimer(CALLBACK(src, .proc/devour, L), 2)
SLEEP_CHECK_DEATH(1)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp()
if(Adjacent(target))
return FALSE
var/list/can_jaunt = get_pools(get_turf(src), 1)
if(!can_jaunt.len)
return FALSE
var/list/pools = get_pools(get_turf(target), 5)
var/list/pools_to_remove = get_pools(get_turf(target), 4)
pools -= pools_to_remove
if(!pools.len)
return FALSE
var/obj/effect/temp_visual/decoy/DA = new /obj/effect/temp_visual/decoy(loc,src)
DA.color = "#FF0000"
var/oldtransform = DA.transform
DA.transform = matrix()*2
animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 3)
SLEEP_CHECK_DEATH(3)
qdel(DA)
var/obj/effect/decal/cleanable/blood/found_bloodpool
pools = get_pools(get_turf(target), 5)
pools_to_remove = get_pools(get_turf(target), 4)
pools -= pools_to_remove
if(pools.len)
shuffle_inplace(pools)
found_bloodpool = pick(pools)
if(found_bloodpool)
visible_message("<span class='danger'>[src] sinks into the blood...</span>")
playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, TRUE, -1)
forceMove(get_turf(found_bloodpool))
playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, TRUE, -1)
visible_message("<span class='danger'>And springs back out!</span>")
blood_enrage()
return TRUE
return FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/be_aggressive()
if(BUBBLEGUM_IS_ENRAGED)
return TRUE
if(isliving(target))
var/mob/living/livingtarget = target
return (livingtarget.stat != CONSCIOUS || livingtarget.lying)
return FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_retreat_distance()
return (be_aggressive() ? null : initial(retreat_distance))
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_minimum_distance()
return (be_aggressive() ? 1 : initial(minimum_distance))
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/update_approach()
retreat_distance = get_retreat_distance()
minimum_distance = get_minimum_distance()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage()
if(!BUBBLEGUM_CAN_ENRAGE)
return FALSE
enrage_till = world.time + enrage_time
update_approach()
change_move_delay(3.75)
var/newcolor = rgb(149, 10, 10)
add_atom_colour(newcolor, TEMPORARY_COLOUR_PRIORITY)
var/datum/callback/cb = CALLBACK(src, .proc/blood_enrage_end)
addtimer(cb, enrage_time)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage_end(newcolor = rgb(149, 10, 10))
update_approach()
change_move_delay()
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY, newcolor)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/change_move_delay(newmove = initial(move_to_delay))
move_to_delay = newmove
speed = move_to_delay
handle_automated_action() // need to recheck movement otherwise move_to_delay won't update until the next checking aka will be wrong speed for a bit
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range)
. = list()
for(var/obj/effect/decal/cleanable/nearby in view(T, range))
if(nearby.can_bloodcrawl_in())
. += nearby
/obj/effect/decal/cleanable/blood/bubblegum
bloodiness = 0
/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in()
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/hallucination_charge_around(times = 4, delay = 6, chargepast = 0, useoriginal = 1, radius)
var/startingangle = rand(1, 360)
if(!target)
return
var/turf/chargeat = get_turf(target)
var/srcplaced = FALSE
if(!radius)
radius = times
for(var/i = 1 to times)
var/ang = (startingangle + 360/times * i)
if(!chargeat)
return
var/turf/place = locate(chargeat.x + cos(ang) * radius, chargeat.y + sin(ang) * radius, chargeat.z)
if(!place)
continue
if(!nest || nest && nest.parent && get_dist(nest.parent, place) <= nest_range)
if(!srcplaced && useoriginal)
forceMove(place)
srcplaced = TRUE
continue
var/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/B = new /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination(loc)
B.forceMove(place)
INVOKE_ASYNC(B, .proc/charge, chargeat, delay, chargepast)
if(useoriginal)
charge(chargeat, delay, chargepast)
/mob/living/simple_animal/hostile/megafauna/bubblegum/adjustBruteLoss(amount, updating_health = TRUE)
. = ..()
if(. > 0 && prob(25))
var/obj/effect/decal/cleanable/blood/gibs/bubblegum/B = new /obj/effect/decal/cleanable/blood/gibs/bubblegum(loc)
if(prob(40))
step(B, pick(cardinal))
else
B.setDir(pick(cardinal))
/obj/effect/decal/cleanable/blood/gibs/bubblegum
name = "thick blood"
desc = "Thick, splattered blood."
random_icon_states = list("gib3", "gib5", "gib6")
bloodiness = 20
/obj/effect/decal/cleanable/blood/gibs/bubblegum/can_bloodcrawl_in()
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon)
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget()
if(!charging)
. = ..()
if(.)
recovery_time = world.time + 20 // can only attack melee once every 2 seconds but rapid_melee gives higher priority
/mob/living/simple_animal/hostile/megafauna/bubblegum/bullet_act(obj/item/projectile/P)
if(BUBBLEGUM_IS_ENRAGED)
visible_message("<span class='danger'>[src] deflects the projectile; [p_they()] can't be hit with ranged weapons while enraged!</span>", "<span class='userdanger'>You deflect the projectile!</span>")
playsound(src, pick('sound/weapons/bulletflyby.ogg', 'sound/weapons/bulletflyby2.ogg', 'sound/weapons/bulletflyby3.ogg'), 300, TRUE)
return
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/ex_act(severity, target)
if(severity >= EXPLODE_LIGHT)
return
severity = EXPLODE_LIGHT // puny mortals
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination))
return TRUE
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Goto(target, delay, minimum_distance)
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/MoveToTarget(list/possible_targets)
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Move()
update_approach()
if(revving_charge)
return FALSE
if(charging)
new /obj/effect/temp_visual/decoy/fading(loc,src)
DestroySurroundings()
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Moved(atom/OldLoc, Dir, Forced = FALSE)
if(Dir)
new /obj/effect/decal/cleanable/blood/bubblegum(loc)
if(charging)
DestroySurroundings()
playsound(src, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE)
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A)
if(charging)
if(isturf(A) || isobj(A) && A.density)
A.ex_act(EXPLODE_HEAVY)
DestroySurroundings()
if(isliving(A))
var/mob/living/L = A
L.visible_message("<span class='danger'>[src] slams into [L]!</span>", "<span class='userdanger'>[src] tramples you into the ground!</span>")
forceMove(get_turf(L))
L.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE)
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE)
shake_camera(L, 4, 3)
shake_camera(src, 2, 3)
..()
/obj/effect/temp_visual/dragon_swoop/bubblegum
duration = 10
/obj/effect/temp_visual/bubblegum_hands
icon = 'icons/effects/bubblegum.dmi'
@@ -290,11 +492,11 @@ Difficulty: Hard
/obj/effect/temp_visual/bubblegum_hands/rightpaw
icon_state = "rightpawgrab"
layer = MOB_LAYER - 0.1
layer = BELOW_MOB_LAYER
/obj/effect/temp_visual/bubblegum_hands/leftpaw
icon_state = "leftpawgrab"
layer = MOB_LAYER - 0.1
layer = BELOW_MOB_LAYER
/obj/effect/temp_visual/bubblegum_hands/rightsmack
icon_state = "rightsmack"
@@ -302,94 +504,48 @@ Difficulty: Hard
/obj/effect/temp_visual/bubblegum_hands/leftsmack
icon_state = "leftsmack"
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_warp()
if(Adjacent(target))
return FALSE
var/list/can_jaunt = get_pools(get_turf(src), 1)
if(!can_jaunt.len)
return FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination
name = "bubblegum's hallucination"
desc = "Is that really just a hallucination?"
health = 1
maxHealth = 1
alpha = 127.5
crusher_loot = null
loot = null
medal_type = null
score_type = null
deathmessage = "Explodes into a pool of blood!"
death_sound = 'sound/effects/splat.ogg'
true_spawn = FALSE
var/list/pools = get_pools(get_turf(target), 2)
var/list/pools_to_remove = get_pools(get_turf(target), 1)
pools -= pools_to_remove
if(!pools.len)
return FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize(mapload)
. = ..()
toggle_ai(AI_OFF)
var/obj/effect/temp_visual/decoy/DA = new /obj/effect/temp_visual/decoy(loc, src)
DA.color = "#FF0000"
var/oldtransform = DA.transform
DA.transform = matrix()*2
animate(DA, alpha = 255, color = initial(DA.color), transform = oldtransform, time = 5)
sleep(5)
qdel(DA)
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/charge(atom/chargeat = target, delay = 3, chargepast = 2)
..()
qdel(src)
var/obj/effect/decal/cleanable/blood/found_bloodpool
pools = get_pools(get_turf(target), 2)
pools_to_remove = get_pools(get_turf(target), 1)
pools -= pools_to_remove
if(pools.len)
shuffle(pools)
found_bloodpool = pick(pools)
if(found_bloodpool)
visible_message("<span class='danger'>[src] sinks into the blood...</span>")
playsound(get_turf(src), 'sound/misc/enter_blood.ogg', 100, 1, -1)
forceMove(get_turf(found_bloodpool))
playsound(get_turf(src), 'sound/misc/exit_blood.ogg', 100, 1, -1)
visible_message("<span class='danger'>And springs back out!</span>")
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Destroy()
new /obj/effect/decal/cleanable/blood(get_turf(src))
. = ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) // hallucinations should not be stopping bubblegum or eachother
return TRUE
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_pools(turf/T, range)
. = list()
for(var/obj/effect/decal/cleanable/nearby in view(T, range))
if(nearby.can_bloodcrawl_in())
. += nearby
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Life()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_spray()
visible_message("<span class='danger'>[src] sprays a stream of gore!</span>")
var/range = 6 + round(anger_modifier * 0.4)
var/turf/previousturf = get_turf(src)
var/turf/J = previousturf
var/targetdir = get_dir(src, target)
if(target.loc == loc)
targetdir = dir
face_atom(target)
new /obj/effect/decal/cleanable/blood/bubblegum(J)
for(var/i in 1 to range)
J = get_step(previousturf, targetdir)
new /obj/effect/temp_visual/dir_setting/bloodsplatter(previousturf, get_dir(previousturf, J))
playsound(previousturf,'sound/effects/splat.ogg', 100, 1, -1)
if(!J || !previousturf.CanAtmosPass(J))
break
new /obj/effect/decal/cleanable/blood/bubblegum(J)
previousturf = J
sleep(1)
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/adjustBruteLoss(amount, updating_health = TRUE)
return
/obj/effect/decal/cleanable/blood/bubblegum
amount = 0
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/OpenFire()
return
/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in()
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/AttackingTarget()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/slaughterlings()
visible_message("<span class='danger'>[src] summons a shoal of slaughterlings!</span>")
var/max_amount = 6
for(var/H in get_pools(get_turf(src), 1))
if(!max_amount)
break
max_amount--
var/obj/effect/decal/cleanable/blood/B = H
new /mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter(B.loc)
return max_amount
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter
name = "slaughterling"
desc = "Though not yet strong enough to create a true physical form, it's nonetheless determined to murder you."
density = 0
faction = list("mining", "boss")
weather_immunities = list("lava","ash")
/mob/living/simple_animal/hostile/asteroid/hivelordbrood/blood/slaughter/CanPass(atom/movable/mover, turf/target, height = 0)
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum))
return 1
return 0
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack()
return
@@ -1,4 +1,3 @@
/*
COLOSSUS
@@ -31,35 +30,74 @@ Difficulty: Very Hard
attack_sound = 'sound/magic/ratvar_attack.ogg'
icon_state = "eva"
icon_living = "eva"
icon_dead = "dragon_dead"
icon_dead = ""
friendly = "stares down"
icon = 'icons/mob/lavaland/96x96megafauna.dmi'
speak_emote = list("roars")
armour_penetration = 40
melee_damage_lower = 40
melee_damage_upper = 40
speed = 1
speed = 10
move_to_delay = 10
ranged = 1
ranged = TRUE
pixel_x = -32
del_on_death = 1
del_on_death = TRUE
internal_type = /obj/item/gps/internal/colossus
medal_type = BOSS_MEDAL_COLOSSUS
score_type = COLOSSUS_SCORE
crusher_loot = list(/obj/structure/closet/crate/necropolis/colossus/crusher)
loot = list(/obj/structure/closet/crate/necropolis/colossus)
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30)
deathmessage = "disintegrates, leaving a glowing core in its wake."
death_sound = 'sound/misc/demon_dies.ogg'
attack_action_types = list(/datum/action/innate/megafauna_attack/spiral_attack,
/datum/action/innate/megafauna_attack/aoe_attack,
/datum/action/innate/megafauna_attack/shotgun,
/datum/action/innate/megafauna_attack/alternating_cardinals)
/mob/living/simple_animal/hostile/megafauna/colossus/devour(mob/living/L)
visible_message("<span class='colossus'>[src] disintegrates [L]!</span>")
L.dust()
/datum/action/innate/megafauna_attack/spiral_attack
name = "Spiral Shots"
icon_icon = 'icons/mob/actions/actions.dmi'
button_icon_state = "sniper_zoom"
chosen_message = "<span class='colossus'>You are now firing in a spiral.</span>"
chosen_attack_num = 1
/datum/action/innate/megafauna_attack/aoe_attack
name = "All Directions"
icon_icon = 'icons/effects/effects.dmi'
button_icon_state = "at_shield2"
chosen_message = "<span class='colossus'>You are now firing in all directions.</span>"
chosen_attack_num = 2
/datum/action/innate/megafauna_attack/shotgun
name = "Shotgun Fire"
icon_icon = 'icons/obj/guns/projectile.dmi'
button_icon_state = "shotgun"
chosen_message = "<span class='colossus'>You are now firing shotgun shots where you aim.</span>"
chosen_attack_num = 3
/datum/action/innate/megafauna_attack/alternating_cardinals
name = "Alternating Shots"
icon_icon = 'icons/obj/guns/projectile.dmi'
button_icon_state = "pistol"
chosen_message = "<span class='colossus'>You are now firing in alternating cardinal directions.</span>"
chosen_attack_num = 4
/mob/living/simple_animal/hostile/megafauna/colossus/OpenFire()
anger_modifier = Clamp(((maxHealth - health)/50),0,20)
ranged_cooldown = world.time + 120
if(client)
switch(chosen_attack)
if(1)
select_spiral_attack()
if(2)
random_shots()
if(3)
blast()
if(4)
alternating_dir_shots()
return
if(enrage(target))
if(move_to_delay == initial(move_to_delay))
visible_message("<span class='colossus'>\"<b>You can't dodge.</b>\"</span>")
@@ -72,26 +110,111 @@ Difficulty: Very Hard
move_to_delay = initial(move_to_delay)
if(prob(20+anger_modifier)) //Major attack
telegraph()
if(health < maxHealth/3)
double_spiral()
else
visible_message("<span class='colossus'>\"<b>Judgement.</b>\"</span>")
spawn(0)
spiral_shoot(rand(0, 1))
select_spiral_attack()
else if(prob(20))
ranged_cooldown = world.time + 30
random_shots()
else
if(prob(70))
ranged_cooldown = world.time + 20
blast()
else
ranged_cooldown = world.time + 40
spawn(0)
alternating_dir_shots()
alternating_dir_shots()
/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L)
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.martial_art && prob(H.martial_art.deflection_chance))
. = TRUE
/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots()
ranged_cooldown = world.time + 40
dir_shots(diagonals)
SLEEP_CHECK_DEATH(10)
dir_shots(cardinal)
SLEEP_CHECK_DEATH(10)
dir_shots(diagonals)
SLEEP_CHECK_DEATH(10)
dir_shots(cardinal)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/select_spiral_attack()
telegraph()
if(health < maxHealth/3)
return double_spiral()
visible_message("<span class='colossus'>\"<b>Judgement.</b>\"</span>")
return spiral_shoot()
/mob/living/simple_animal/hostile/megafauna/colossus/proc/double_spiral()
visible_message("<span class='colossus'>\"<b>Die.</b>\"</span>")
SLEEP_CHECK_DEATH(10)
INVOKE_ASYNC(src, .proc/spiral_shoot, FALSE)
INVOKE_ASYNC(src, .proc/spiral_shoot, TRUE)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = pick(TRUE, FALSE), counter_start = 8)
var/turf/start_turf = get_step(src, pick(alldirs))
var/counter = counter_start
for(var/i in 1 to 80)
if(negative)
counter--
else
counter++
if(counter > 16)
counter = 1
if(counter < 1)
counter = 16
shoot_projectile(start_turf, counter * 22.5)
playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, TRUE)
SLEEP_CHECK_DEATH(1)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/shoot_projectile(turf/marker, set_angle)
if(!isnum(set_angle) && (!marker || marker == loc))
return
var/turf/startloc = get_turf(src)
var/obj/item/projectile/P = new /obj/item/projectile/colossus(startloc)
P.preparePixelProjectile(marker, marker, startloc)
P.firer = src
if(target)
P.original = target
P.fire(set_angle)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/random_shots()
ranged_cooldown = world.time + 30
var/turf/U = get_turf(src)
playsound(U, 'sound/magic/clockwork/invoke_general.ogg', 300, TRUE, 5)
for(var/T in RANGE_TURFS(12, U) - U)
if(prob(5))
shoot_projectile(T)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/blast(set_angle)
ranged_cooldown = world.time + 20
var/turf/target_turf = get_turf(target)
playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 2)
newtonian_move(get_dir(target_turf, src))
var/angle_to_target = Get_Angle(src, target_turf)
if(isnum(set_angle))
angle_to_target = set_angle
var/static/list/colossus_shotgun_shot_angles = list(12.5, 7.5, 2.5, -2.5, -7.5, -12.5)
for(var/i in colossus_shotgun_shot_angles)
shoot_projectile(target_turf, angle_to_target + i)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs)
if(!islist(dirs))
dirs = alldirs.Copy()
playsound(src, 'sound/magic/clockwork/invoke_general.ogg', 200, TRUE, 2)
for(var/d in dirs)
var/turf/E = get_step(src, d)
shoot_projectile(E)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/telegraph()
for(var/mob/M in range(10,src))
if(M.client)
flash_color(M.client, "#C80000", 1)
shake_camera(M, 4, 3)
playsound(src, 'sound/magic/narsie_attack.ogg', 200, TRUE)
/mob/living/simple_animal/hostile/megafauna/colossus/devour(mob/living/L)
visible_message("<span class='colossus'>[src] disintegrates [L]!</span>")
L.dust()
/obj/effect/temp_visual/at_shield
name = "anti-toolbox field"
@@ -99,15 +222,14 @@ Difficulty: Very Hard
icon = 'icons/effects/effects.dmi'
icon_state = "at_shield2"
layer = FLY_LAYER
luminosity = 2
light_range = 2
duration = 8
var/target
/obj/effect/temp_visual/at_shield/New(new_loc, new_target)
..()
/obj/effect/temp_visual/at_shield/Initialize(mapload, new_target)
. = ..()
target = new_target
spawn(0)
orbit(target, 0, FALSE, 0, 0, FALSE, TRUE)
INVOKE_ASYNC(src, /atom/movable/proc/orbit, target, 0, FALSE, 0, 0, FALSE, TRUE)
/mob/living/simple_animal/hostile/megafauna/colossus/bullet_act(obj/item/projectile/P)
if(!stat)
@@ -117,126 +239,7 @@ Difficulty: Very Hard
var/random_y = rand(0, 72)
AT.pixel_y += random_y
..()
/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L)
if(ishuman(L))
var/mob/living/carbon/human/H = L
if(H.martial_art && prob(H.martial_art.deflection_chance))
. = TRUE
/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots()
dir_shots(diagonals)
sleep(10)
dir_shots(cardinal)
sleep(10)
dir_shots(diagonals)
sleep(10)
dir_shots(cardinal)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/double_spiral()
visible_message("<span class='colossus'>\"<b>Die.</b>\"</span>")
sleep(10)
spawn(0)
spiral_shoot()
spawn(0)
spiral_shoot(1)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = 0, counter_start = 1)
var/counter = counter_start
var/turf/marker
for(var/i in 1 to 80)
switch(counter)
if(1)
marker = locate(x, y - 2, z)
if(2)
marker = locate(x - 1, y - 2, z)
if(3)
marker = locate(x - 2, y - 2, z)
if(4)
marker = locate(x - 2, y - 1, z)
if(5)
marker = locate(x - 2, y, z)
if(6)
marker = locate(x - 2, y + 1, z)
if(7)
marker = locate(x - 2, y + 2, z)
if(8)
marker = locate(x - 1, y + 2, z)
if(9)
marker = locate(x, y + 2, z)
if(10)
marker = locate(x + 1, y + 2, z)
if(11)
marker = locate(x + 2, y + 2, z)
if(12)
marker = locate(x + 2, y + 1, z)
if(13)
marker = locate(x + 2, y, z)
if(14)
marker = locate(x + 2, y - 1, z)
if(15)
marker = locate(x + 2, y - 2, z)
if(16)
marker = locate(x + 1, y - 2, z)
if(negative)
counter--
else
counter++
if(counter > 16)
counter = 1
if(counter < 1)
counter = 16
shoot_projectile(marker)
playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 20, 1)
sleep(1)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/shoot_projectile(turf/marker)
if(!marker || marker == loc)
return
var/turf/startloc = get_turf(src)
var/obj/item/projectile/P = new /obj/item/projectile/colossus(startloc)
P.current = startloc
P.starting = startloc
P.firer = src
P.yo = marker.y - startloc.y
P.xo = marker.x - startloc.x
if(target)
P.original = target
else
P.original = marker
P.fire()
/mob/living/simple_animal/hostile/megafauna/colossus/proc/random_shots()
var/turf/U = get_turf(src)
playsound(U, 'sound/magic/invoke_general.ogg', 300, 1, 5)
for(var/T in RANGE_TURFS(12, U) - U)
if(prob(5))
shoot_projectile(T)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/blast()
playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 200, 1, 2)
var/turf/T = get_turf(target)
newtonian_move(get_dir(T, targets_from))
for(var/turf/turf in range(1, T))
shoot_projectile(turf)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs)
if(!islist(dirs))
dirs = alldirs.Copy()
playsound(get_turf(src), 'sound/magic/invoke_general.ogg', 200, 1, 2)
for(var/d in dirs)
var/turf/E = get_step(src, d)
shoot_projectile(E)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/telegraph()
for(var/mob/M in range(10,src))
if(M.client)
flash_color(M.client, rgb(200, 0, 0), 1)
shake_camera(M, 4, 3)
playsound(get_turf(src),'sound/magic/narsie_attack.ogg', 200, 1)
return ..()
/obj/item/projectile/colossus
name ="death bolt"
@@ -1,3 +1,9 @@
#define DRAKE_SWOOP_HEIGHT 270 //how high up drakes go, in pixels
#define DRAKE_SWOOP_DIRECTION_CHANGE_RANGE 5 //the range our x has to be within to not change the direction we slam from
#define SWOOP_DAMAGEABLE 1
#define SWOOP_INVULNERABLE 2
/*
ASH DRAKE
@@ -6,10 +12,11 @@ Ash drakes spawn randomly wherever a lavaland creature is able to spawn. They ar
It acts as a melee creature, chasing down and attacking its target while also using different attacks to augment its power that increase as it takes damage.
Whenever possible, the drake will breathe fire in the four cardinal directions, igniting and heavily damaging anything caught in the blast.
It also often causes fire to rain from the sky - many nearby turfs will flash red as a fireball crashes into them, dealing damage to anything on the turfs.
The drake also utilizes its wings to fly into the sky and crash down onto a specified point. Anything on this point takes tremendous damage.
Whenever possible, the drake will breathe fire directly at it's target, igniting and heavily damaging anything caught in the blast.
It also often causes lava to pool from the ground around you - many nearby turfs will temporarily turn into lava, dealing damage to anything on the turfs.
The drake also utilizes its wings to fly into the sky, flying after its target and attempting to slam down on them. Anything near when it slams down takes huge damage.
- Sometimes it will chain these swooping attacks over and over, making swiftness a necessity.
- Sometimes, it will encase its target in an arena of lava
When an ash drake dies, it leaves behind a chest that can contain four things:
1. A spectral blade that allows its wielder to call ghosts to it, enhancing its power
@@ -39,31 +46,358 @@ Difficulty: Medium
armour_penetration = 40
melee_damage_lower = 40
melee_damage_upper = 40
speed = 1
move_to_delay = 10
ranged = 1
speed = 5
move_to_delay = 5
ranged = TRUE
pixel_x = -16
crusher_loot = list(/obj/structure/closet/crate/necropolis/dragon/crusher)
loot = list(/obj/structure/closet/crate/necropolis/dragon)
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30)
var/swooping = 0
var/swoop_cooldown = 0
var/swooping = NONE
var/player_cooldown = 0
internal_type = /obj/item/gps/internal/dragon
medal_type = BOSS_MEDAL_DRAKE
score_type = DRAKE_SCORE
deathmessage = "collapses into a pile of bones, its flesh sloughing away."
death_sound = 'sound/misc/demon_dies.ogg'
attack_action_types = list(/datum/action/innate/megafauna_attack/fire_cone,
/datum/action/innate/megafauna_attack/fire_cone_meteors,
/datum/action/innate/megafauna_attack/mass_fire,
/datum/action/innate/megafauna_attack/lava_swoop)
/datum/action/innate/megafauna_attack/fire_cone
name = "Fire Cone"
icon_icon = 'icons/obj/wizard.dmi'
button_icon_state = "fireball"
chosen_message = "<span class='colossus'>You are now shooting fire at your target.</span>"
chosen_attack_num = 1
/datum/action/innate/megafauna_attack/fire_cone_meteors
name = "Fire Cone With Meteors"
icon_icon = 'icons/mob/actions/actions.dmi'
button_icon_state = "sniper_zoom"
chosen_message = "<span class='colossus'>You are now shooting fire at your target and raining fire around you.</span>"
chosen_attack_num = 2
/datum/action/innate/megafauna_attack/mass_fire
name = "Mass Fire Attack"
icon_icon = 'icons/effects/fire.dmi'
button_icon_state = "1"
chosen_message = "<span class='colossus'>You are now shooting mass fire at your target.</span>"
chosen_attack_num = 3
/datum/action/innate/megafauna_attack/lava_swoop
name = "Lava Swoop"
icon_icon = 'icons/effects/effects.dmi'
button_icon_state = "lavastaff_warn"
chosen_message = "<span class='colossus'>You are now swooping and raining lava at your target.</span>"
chosen_attack_num = 4
/obj/item/gps/internal/dragon
icon_state = null
gpstag = "Fiery Signal"
desc = "Here there be dragons."
invisibility = 100
/mob/living/simple_animal/hostile/megafauna/dragon/OpenFire()
if(swooping)
return
anger_modifier = Clamp(((maxHealth - health)/50),0,20)
ranged_cooldown = world.time + ranged_cooldown_time
if(client)
switch(chosen_attack)
if(1)
fire_cone(meteors = FALSE)
if(2)
fire_cone()
if(3)
mass_fire()
if(4)
lava_swoop()
return
if(prob(15 + anger_modifier))
lava_swoop()
else if(prob(10+anger_modifier))
shoot_fire_attack()
else
fire_cone()
/mob/living/simple_animal/hostile/megafauna/dragon/proc/shoot_fire_attack()
if(health < maxHealth*0.5)
mass_fire()
else
fire_cone()
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_rain()
if(!target)
return
target.visible_message("<span class='boldwarning'>Fire rains from the sky!</span>")
for(var/turf/turf in range(9,get_turf(target)))
if(prob(11))
new /obj/effect/temp_visual/target(turf)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_pools(var/amount, var/delay = 0.8)
if(!target)
return
target.visible_message("<span class='boldwarning'>Lava starts to pool up around you!</span>")
while(amount > 0)
if(QDELETED(target))
break
var/turf/T = pick(RANGE_TURFS(1, target))
new /obj/effect/temp_visual/lava_warning(T, 60) // longer reset time for the lava
amount--
SLEEP_CHECK_DEATH(delay)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_swoop(var/amount = 30)
if(health < maxHealth * 0.5)
return swoop_attack(lava_arena = TRUE, swoop_cooldown = 60)
INVOKE_ASYNC(src, .proc/lava_pools, amount)
swoop_attack(FALSE, target, 1000) // longer cooldown until it gets reset below
SLEEP_CHECK_DEATH(0)
fire_cone()
if(health < maxHealth*0.5)
SLEEP_CHECK_DEATH(10)
fire_cone()
SLEEP_CHECK_DEATH(10)
fire_cone()
SetRecoveryTime(40)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/mass_fire(var/spiral_count = 12, var/range = 15, var/times = 3)
SLEEP_CHECK_DEATH(0)
for(var/i = 1 to times)
SetRecoveryTime(50)
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE)
var/increment = 360 / spiral_count
for(var/j = 1 to spiral_count)
var/list/turfs = line_target(j * increment + i * increment / 2, range, src)
INVOKE_ASYNC(src, .proc/fire_line, turfs)
SLEEP_CHECK_DEATH(25)
SetRecoveryTime(30)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_arena()
if(!target)
return
target.visible_message("<span class='boldwarning'>[src] encases you in an arena of fire!</span>")
var/amount = 3
var/turf/center = get_turf(target)
var/list/walled = RANGE_TURFS(3, center) - RANGE_TURFS(2, center)
var/list/drakewalls = list()
for(var/turf/T in walled)
drakewalls += new /obj/effect/temp_visual/drakewall(T) // no people with lava immunity can just run away from the attack for free
var/list/indestructible_turfs = list()
for(var/turf/T in RANGE_TURFS(2, center))
if(istype(T, /turf/simulated/floor/indestructible))
continue
if(!istype(T, /turf/simulated/wall/indestructible))
T.ChangeTurf(/turf/simulated/floor/plating/asteroid/basalt/lava_land_surface)
else
indestructible_turfs += T
SLEEP_CHECK_DEATH(10) // give them a bit of time to realize what attack is actually happening
var/list/turfs = RANGE_TURFS(2, center)
while(amount > 0)
var/list/empty = indestructible_turfs.Copy() // can't place safe turfs on turfs that weren't changed to be open
var/any_attack = 0
for(var/turf/T in turfs)
for(var/mob/living/L in T.contents)
if(L.client)
empty += pick(((RANGE_TURFS(2, L) - RANGE_TURFS(1, L)) & turfs) - empty) // picks a turf within 2 of the creature not outside or in the shield
any_attack = 1
for(var/obj/mecha/M in T.contents)
empty += pick(((RANGE_TURFS(2, M) - RANGE_TURFS(1, M)) & turfs) - empty)
any_attack = 1
if(!any_attack)
for(var/obj/effect/temp_visual/drakewall/D in drakewalls)
qdel(D)
return 0 // nothing to attack in the arena time for enraged attack if we still have a target
for(var/turf/T in turfs)
if(!(T in empty))
new /obj/effect/temp_visual/lava_warning(T)
else if(!istype(T, /turf/simulated/wall/indestructible))
new /obj/effect/temp_visual/lava_safe(T)
amount--
SLEEP_CHECK_DEATH(24)
return 1 // attack finished completely
/mob/living/simple_animal/hostile/megafauna/dragon/proc/arena_escape_enrage() // you ran somehow / teleported away from my arena attack now i'm mad fucker
SLEEP_CHECK_DEATH(0)
SetRecoveryTime(80)
visible_message("<span class='boldwarning'>[src] starts to glow vibrantly as its wounds close up!</span>")
adjustBruteLoss(-250) // yeah you're gonna pay for that, don't run nerd
add_atom_colour(rgb(255, 255, 0), TEMPORARY_COLOUR_PRIORITY)
move_to_delay = move_to_delay / 2
light_range = 10
SLEEP_CHECK_DEATH(10) // run.
mass_fire(20, 15, 3)
move_to_delay = initial(move_to_delay)
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
light_range = initial(light_range)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_cone(var/atom/at = target, var/meteors = TRUE)
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE)
SLEEP_CHECK_DEATH(0)
if(prob(50) && meteors)
INVOKE_ASYNC(src, .proc/fire_rain)
var/range = 15
var/list/turfs = list()
turfs = line_target(-40, range, at)
INVOKE_ASYNC(src, .proc/fire_line, turfs)
turfs = line_target(0, range, at)
INVOKE_ASYNC(src, .proc/fire_line, turfs)
turfs = line_target(40, range, at)
INVOKE_ASYNC(src, .proc/fire_line, turfs)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/line_target(var/offset, var/range, var/atom/at = target)
if(!at)
return
var/angle = Atan2(at.x - src.x, at.y - src.y) + offset
var/turf/T = get_turf(src)
for(var/i in 1 to range)
var/turf/check = locate(src.x + cos(angle) * i, src.y + sin(angle) * i, src.z)
if(!check)
break
T = check
return (getline(src, T) - get_turf(src))
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_line(var/list/turfs)
SLEEP_CHECK_DEATH(0)
dragon_fire_line(src, turfs)
//fire line keeps going even if dragon is deleted
/proc/dragon_fire_line(var/source, var/list/turfs)
var/list/hit_list = list()
for(var/turf/T in turfs)
if(T.density)
break
new /obj/effect/hotspot(T)
T.hotspot_expose(700,50,1)
for(var/mob/living/L in T.contents)
if(L in hit_list || L == source)
continue
hit_list += L
L.adjustFireLoss(20)
to_chat(L, "<span class='userdanger'>You're hit by [source]'s fire breath!</span>")
// deals damage to mechs
for(var/obj/mecha/M in T.contents)
if(M in hit_list)
continue
hit_list += M
M.take_damage(45, BRUTE, "melee", 1)
sleep(1.5)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/swoop_attack(lava_arena = FALSE, atom/movable/manual_target, var/swoop_cooldown = 30)
if(stat || swooping)
return
if(manual_target)
target = manual_target
if(!target)
return
stop_automated_movement = TRUE
swooping |= SWOOP_DAMAGEABLE
density = FALSE
icon_state = "shadow"
visible_message("<span class='boldwarning'>[src] swoops up high!</span>")
var/negative
var/initial_x = x
if(target.x < initial_x) //if the target's x is lower than ours, swoop to the left
negative = TRUE
else if(target.x > initial_x)
negative = FALSE
else if(target.x == initial_x) //if their x is the same, pick a direction
negative = prob(50)
var/obj/effect/temp_visual/dragon_flight/F = new /obj/effect/temp_visual/dragon_flight(loc, negative)
negative = !negative //invert it for the swoop down later
var/oldtransform = transform
alpha = 255
animate(src, alpha = 204, transform = matrix()*0.9, time = 3, easing = BOUNCE_EASING)
for(var/i in 1 to 3)
sleep(1)
if(QDELETED(src) || stat == DEAD) //we got hit and died, rip us
qdel(F)
if(stat == DEAD)
swooping &= ~SWOOP_DAMAGEABLE
animate(src, alpha = 255, transform = oldtransform, time = 0, flags = ANIMATION_END_NOW) //reset immediately
return
animate(src, alpha = 100, transform = matrix()*0.7, time = 7)
swooping |= SWOOP_INVULNERABLE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
SLEEP_CHECK_DEATH(7)
while(target && loc != get_turf(target))
forceMove(get_step(src, get_dir(src, target)))
SLEEP_CHECK_DEATH(0.5)
// Ash drake flies onto its target and rains fire down upon them
var/descentTime = 10
var/lava_success = TRUE
if(lava_arena)
lava_success = lava_arena()
//ensure swoop direction continuity.
if(negative)
if(IsInRange(x, initial_x + 1, initial_x + DRAKE_SWOOP_DIRECTION_CHANGE_RANGE))
negative = FALSE
else
if(IsInRange(x, initial_x - DRAKE_SWOOP_DIRECTION_CHANGE_RANGE, initial_x - 1))
negative = TRUE
new /obj/effect/temp_visual/dragon_flight/end(loc, negative)
new /obj/effect/temp_visual/dragon_swoop(loc)
animate(src, alpha = 255, transform = oldtransform, descentTime)
SLEEP_CHECK_DEATH(descentTime)
swooping &= ~SWOOP_INVULNERABLE
mouse_opacity = initial(mouse_opacity)
icon_state = "dragon"
playsound(loc, 'sound/effects/meteorimpact.ogg', 200, TRUE)
for(var/mob/living/L in orange(1, src))
if(L.stat)
visible_message("<span class='warning'>[src] slams down on [L], crushing [L.p_them()]!</span>")
L.gib()
else
L.adjustBruteLoss(75)
if(L && !QDELETED(L)) // Some mobs are deleted on death
var/throw_dir = get_dir(src, L)
if(L.loc == loc)
throw_dir = pick(alldirs)
var/throwtarget = get_edge_target_turf(src, throw_dir)
L.throw_at(throwtarget, 3)
visible_message("<span class='warning'>[L] is thrown clear of [src]!</span>")
for(var/obj/mecha/M in orange(1, src))
M.take_damage(75, BRUTE, "melee", 1)
for(var/mob/M in range(7, src))
shake_camera(M, 15, 1)
density = TRUE
SLEEP_CHECK_DEATH(1)
swooping &= ~SWOOP_DAMAGEABLE
SetRecoveryTime(swoop_cooldown)
if(!lava_success)
arena_escape_enrage()
/mob/living/simple_animal/hostile/megafauna/dragon/ex_act(severity, target)
if(severity == 3)
if(severity == EXPLODE_LIGHT)
return
..()
/mob/living/simple_animal/hostile/megafauna/dragon/adjustHealth(amount, updating_health = TRUE)
if(swooping)
if(swooping & SWOOP_INVULNERABLE)
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/dragon/visible_message()
if(swooping & SWOOP_INVULNERABLE) //to suppress attack messages without overriding every single proc that could send a message saying we got hit
return
return ..()
/mob/living/simple_animal/hostile/megafauna/dragon/AttackingTarget()
if(!swooping)
return ..()
@@ -83,181 +417,161 @@ Difficulty: Medium
/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = 0)
return 1
/obj/effect/temp_visual/fireball
icon = 'icons/obj/wizard.dmi'
icon_state = "fireball"
name = "fireball"
desc = "Get out of the way!"
layer = 6
randomdir = 0
duration = 12
pixel_z = 500
/obj/effect/temp_visual/lava_warning
icon_state = "lavastaff_warn"
layer = BELOW_MOB_LAYER
light_range = 2
duration = 13
/obj/effect/temp_visual/fireball/New(loc)
..()
animate(src, pixel_z = 0, time = 12)
/obj/effect/temp_visual/lava_warning/ex_act()
return
/obj/effect/temp_visual/target
icon = 'icons/mob/actions/actions.dmi'
icon_state = "sniper_zoom"
layer = MOB_LAYER - 0.1
luminosity = 2
duration = 12
/obj/effect/temp_visual/lava_warning/Initialize(mapload, var/reset_time = 10)
. = ..()
INVOKE_ASYNC(src, .proc/fall, reset_time)
src.alpha = 63.75
animate(src, alpha = 255, time = duration)
/obj/effect/temp_visual/lava_warning/proc/fall(var/reset_time)
var/turf/T = get_turf(src)
playsound(T,'sound/magic/fleshtostone.ogg', 80, TRUE)
sleep(duration)
playsound(T,'sound/magic/fireball.ogg', 200, TRUE)
for(var/mob/living/L in T.contents)
if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon))
continue
L.adjustFireLoss(10)
to_chat(L, "<span class='userdanger'>You fall directly into the pool of lava!</span>")
// deals damage to mechs
for(var/obj/mecha/M in T.contents)
M.take_damage(45, BRUTE, "melee", 1)
// changes turf to lava temporarily
if(!T.density && !istype(T, /turf/simulated/floor/plating/lava))
var/lava_turf = /turf/simulated/floor/plating/lava/smooth
var/reset_turf = T.type
T.ChangeTurf(lava_turf)
sleep(reset_time)
T.ChangeTurf(reset_turf)
/obj/effect/temp_visual/drakewall
desc = "An ash drakes true flame."
name = "Fire Barrier"
icon = 'icons/effects/fire.dmi'
icon_state = "1"
anchored = TRUE
opacity = 0
density = TRUE
duration = 82
color = COLOR_DARK_ORANGE
/obj/effect/temp_visual/drakewall/CanAtmosPass()
return !density
/obj/effect/temp_visual/lava_safe
icon = 'icons/obj/hand_of_god_structures.dmi'
icon_state = "trap-earth"
layer = BELOW_MOB_LAYER
light_range = 2
duration = 13
/obj/effect/temp_visual/dragon_swoop
name = "certain death"
desc = "Don't just stand there, move!"
icon = 'icons/effects/96x96.dmi'
icon_state = "landing"
layer = MOB_LAYER - 0.1
layer = BELOW_MOB_LAYER
pixel_x = -32
pixel_y = -32
color = "#FF0000"
duration = 10
/obj/effect/temp_visual/dragon_flight
icon = 'icons/mob/lavaland/64x64megafauna.dmi'
icon_state = "dragon"
layer = ABOVE_ALL_MOB_LAYER
pixel_x = -16
duration = 10
randomdir = FALSE
/obj/effect/temp_visual/dragon_flight/Initialize(mapload, negative)
. = ..()
INVOKE_ASYNC(src, .proc/flight, negative)
/obj/effect/temp_visual/dragon_flight/proc/flight(negative)
if(negative)
animate(src, pixel_x = -DRAKE_SWOOP_HEIGHT*0.1, pixel_z = DRAKE_SWOOP_HEIGHT*0.15, time = 3, easing = BOUNCE_EASING)
else
animate(src, pixel_x = DRAKE_SWOOP_HEIGHT*0.1, pixel_z = DRAKE_SWOOP_HEIGHT*0.15, time = 3, easing = BOUNCE_EASING)
sleep(3)
icon_state = "swoop"
if(negative)
animate(src, pixel_x = -DRAKE_SWOOP_HEIGHT, pixel_z = DRAKE_SWOOP_HEIGHT, time = 7)
else
animate(src, pixel_x = DRAKE_SWOOP_HEIGHT, pixel_z = DRAKE_SWOOP_HEIGHT, time = 7)
/obj/effect/temp_visual/dragon_flight/end
pixel_x = DRAKE_SWOOP_HEIGHT
pixel_z = DRAKE_SWOOP_HEIGHT
duration = 10
/obj/effect/temp_visual/dragon_flight/end/flight(negative)
if(negative)
pixel_x = -DRAKE_SWOOP_HEIGHT
animate(src, pixel_x = -16, pixel_z = 0, time = 5)
else
animate(src, pixel_x = -16, pixel_z = 0, time = 5)
obj/effect/temp_visual/fireball
icon = 'icons/obj/wizard.dmi'
icon_state = "fireball"
name = "fireball"
desc = "Get out of the way!"
layer = FLY_LAYER
randomdir = FALSE
duration = 9
pixel_z = 270
/obj/effect/temp_visual/fireball/Initialize()
. = ..()
animate(src, pixel_z = 0, time = duration)
/obj/effect/temp_visual/target
icon = 'icons/mob/actions/actions.dmi'
icon_state = "sniper_zoom"
layer = BELOW_MOB_LAYER
light_range = 2
duration = 9
/obj/effect/temp_visual/target/ex_act()
return
/obj/effect/temp_visual/target/New(loc)
..()
spawn(0)
fall()
/obj/effect/temp_visual/target/Initialize(mapload, list/flame_hit)
. = ..()
INVOKE_ASYNC(src, .proc/fall, flame_hit)
/obj/effect/temp_visual/target/proc/fall()
/obj/effect/temp_visual/target/proc/fall(list/flame_hit)
var/turf/T = get_turf(src)
playsound(T,'sound/magic/fireball.ogg', 200, 1)
playsound(T,'sound/magic/fleshtostone.ogg', 80, TRUE)
new /obj/effect/temp_visual/fireball(T)
sleep(12)
explosion(T, 0, 0, 1, 0, 0, 0, 1, 1)
/mob/living/simple_animal/hostile/megafauna/dragon/OpenFire()
anger_modifier = Clamp(((maxHealth - health)/50),0,20)
ranged_cooldown = world.time + ranged_cooldown_time
if(swooping)
fire_rain()
return
if(prob(15 + anger_modifier) && !client)
if(health < maxHealth/2)
spawn(0)
swoop_attack(1)
else
fire_rain()
else if(prob(10+anger_modifier) && !client)
if(health > maxHealth/2)
spawn(0)
swoop_attack(0)
else
spawn(0)
triple_swoop(0)
else
fire_walls()
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_rain()
visible_message("<span class='boldwarning'>Fire rains from the sky!</span>")
for(var/turf/turf in range(12,get_turf(src)))
if(prob(10))
new /obj/effect/temp_visual/target(turf)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_walls()
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, 1)
for(var/d in cardinal)
spawn(0)
fire_wall(d)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_wall(dir)
var/list/hit_things = list(src)
var/turf/E = get_edge_target_turf(src, dir)
var/range = 10
var/turf/previousturf = get_turf(src)
for(var/turf/J in getline(src,E))
if(!range || !previousturf.CanAtmosPass(J))
break
range--
new /obj/effect/hotspot(J)
J.hotspot_expose(700,50,1)
for(var/mob/living/L in J.contents - hit_things)
L.adjustFireLoss(20)
sleep(duration)
if(ismineralturf(T))
var/turf/simulated/mineral/M = T
M.gets_drilled()
playsound(T, "explosion", 80, TRUE)
new /obj/effect/hotspot(T)
T.hotspot_expose(700, 50, 1)
for(var/mob/living/L in T.contents)
if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon))
continue
if(islist(flame_hit) && !flame_hit[L])
L.adjustFireLoss(40)
to_chat(L, "<span class='userdanger'>You're hit by the drake's fire breath!</span>")
hit_things += L
previousturf = J
sleep(1)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/triple_swoop()
swoop_attack()
swoop_attack()
swoop_attack()
/mob/living/simple_animal/hostile/megafauna/dragon/proc/swoop_attack(fire_rain = 0, atom/movable/manual_target)
if(stat || swooping)
return
swoop_cooldown = world.time + 200
var/atom/swoop_target
if(manual_target)
swoop_target = manual_target
else
swoop_target = target
stop_automated_movement = TRUE
swooping = 1
density = 0
icon_state = "swoop"
visible_message("<span class='boldwarning'>[src] swoops up high!</span>")
if(prob(50))
animate(src, pixel_x = 500, pixel_z = 500, time = 10)
else
animate(src, pixel_x = -500, pixel_z = 500, time = 10)
sleep(30)
var/turf/tturf
if(fire_rain)
fire_rain()
icon_state = "dragon"
if(swoop_target && !QDELETED(swoop_target) && swoop_target.z == src.z)
tturf = get_turf(swoop_target)
else
tturf = get_turf(src)
forceMove(tturf)
new /obj/effect/temp_visual/dragon_swoop(tturf)
animate(src, pixel_x = initial(pixel_x), pixel_z = 0, time = 10)
sleep(10)
playsound(src.loc, 'sound/effects/meteorimpact.ogg', 200, 1)
for(var/mob/living/L in orange(1, src))
if(L.stat)
visible_message("<span class='warning'>[src] slams down on [L], crushing [L.p_them()]!</span>")
L.gib()
flame_hit[L] = TRUE
else
L.adjustBruteLoss(75)
if(L && !QDELETED(L)) // Some mobs are deleted on death
var/throw_dir = get_dir(src, L)
if(L.loc == loc)
throw_dir = pick(alldirs)
var/throwtarget = get_edge_target_turf(src, throw_dir)
L.throw_at(throwtarget, 3)
visible_message("<span class='warning'>[L] is thrown clear of [src]!</span>")
for(var/mob/M in range(7, src))
shake_camera(M, 15, 1)
stop_automated_movement = FALSE
swooping = 0
density = 1
/mob/living/simple_animal/hostile/megafauna/dragon/AltClickOn(atom/movable/A)
if(!istype(A))
return
if(swoop_cooldown >= world.time)
to_chat(src, "<span class='warning'>You need to wait 20 seconds between swoop attacks!</span>")
return
swoop_attack(1, A)
/obj/item/gps/internal/dragon
icon_state = null
gpstag = "Fiery Signal"
desc = "Here there be dragons."
invisibility = 100
L.adjustFireLoss(10) //if we've already hit them, do way less damage
/mob/living/simple_animal/hostile/megafauna/dragon/lesser
name = "lesser ash drake"
@@ -267,9 +581,92 @@ Difficulty: Medium
obj_damage = 80
melee_damage_upper = 30
melee_damage_lower = 30
mouse_opacity = MOUSE_OPACITY_ICON
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1)
loot = list()
crusher_loot = list()
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
attack_action_types = list()
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/AltClickOn(atom/movable/A)
if(!istype(A))
return
if(player_cooldown >= world.time)
to_chat(src, "<span class='warning'>You need to wait [(player_cooldown - world.time) / 10] seconds before swooping again!</span>")
return
swoop_attack(FALSE, A)
lava_pools(10, 2) // less pools but longer delay before spawns
player_cooldown = world.time + 200 // needs seperate cooldown or cant use fire attacks
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype)
return
/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon
name = "space dragon"
maxHealth = 250
health = 250
faction = list("neutral")
desc = "A space carp turned dragon by vile magic. Has the same ferocity of a space carp, but also a much more enabling body."
icon = 'icons/mob/spacedragon.dmi'
icon_state = "spacedragon"
icon_living = "spacedragon"
icon_dead = "spacedragon_dead"
obj_damage = 80
melee_damage_upper = 35
melee_damage_lower = 35
speed = 0
mouse_opacity = MOUSE_OPACITY_ICON
loot = list()
crusher_loot = list()
butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/bone = 30)
move_force = MOVE_FORCE_NORMAL
move_resist = MOVE_FORCE_NORMAL
pull_force = MOVE_FORCE_NORMAL
deathmessage = "screeches as its wings turn to dust and it collapses on the floor, life estinguished."
attack_action_types = list()
/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/grant_achievement(medaltype, scoretype)
return
/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/Initialize(mapload)
var/obj/effect/proc_holder/spell/aoe_turf/repulse/spacedragon/repulse_action = new /obj/effect/proc_holder/spell/aoe_turf/repulse/spacedragon(src)
repulse_action.action.Grant(src)
mob_spell_list += repulse_action
. = ..()
/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/proc/fire_stream(var/atom/at = target)
playsound(get_turf(src),'sound/magic/fireball.ogg', 200, TRUE)
SLEEP_CHECK_DEATH(0)
var/range = 20
var/list/turfs = list()
turfs = line_target(0, range, at)
INVOKE_ASYNC(src, .proc/fire_line, turfs)
/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/OpenFire()
if(swooping)
return
ranged_cooldown = world.time + ranged_cooldown_time
fire_stream()
/obj/effect/proc_holder/spell/aoe_turf/repulse/spacedragon
name = "Tail Sweep"
desc = "Throw back attackers with a sweep of your tail."
sound = 'sound/magic/tail_swing.ogg'
charge_max = 150
clothes_req = FALSE
range = 1
cooldown_min = 150
invocation_type = "none"
sparkle_path = /obj/effect/temp_visual/dir_setting/tailsweep
action_icon_state = "tailsweep"
action_background_icon_state = "bg_alien"
/obj/effect/proc_holder/spell/aoe_turf/repulse/spacedragon/cast(list/targets, mob/user = usr)
if(iscarbon(user))
var/mob/living/carbon/C = user
playsound(C.loc, 'sound/effects/hit_punch.ogg', 80, TRUE, TRUE)
C.spin(6, 1)
..(targets, user, 3)
/mob/living/simple_animal/hostile/megafauna/dragon/space_dragon/AltClickOn(atom/movable/A)
return
File diff suppressed because it is too large Load Diff
@@ -7,11 +7,11 @@
sentience_type = SENTIENCE_BOSS
environment_smash = ENVIRONMENT_SMASH_RWALLS
obj_damage = 400
luminosity = 3
light_range = 3
faction = list("mining", "boss")
weather_immunities = list("lava","ash")
flying = 1
robust_searching = 1
flying = TRUE
robust_searching = TRUE
ranged_ignores_vision = TRUE
stat_attack = DEAD
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)
@@ -20,6 +20,12 @@
maxbodytemp = INFINITY
vision_range = 5
aggro_vision_range = 18
move_force = MOVE_FORCE_OVERPOWERING
move_resist = MOVE_FORCE_OVERPOWERING
pull_force = MOVE_FORCE_OVERPOWERING
mob_size = MOB_SIZE_LARGE
layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise
mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway
var/list/crusher_loot
var/medal_type
var/score_type = BOSS_SCORE
@@ -27,13 +33,9 @@
var/anger_modifier = 0
var/obj/item/gps/internal_gps
var/internal_type
move_force = MOVE_FORCE_OVERPOWERING
move_resist = MOVE_FORCE_OVERPOWERING
pull_force = MOVE_FORCE_OVERPOWERING
mob_size = MOB_SIZE_LARGE
layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise
mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway
var/recovery_time = 0
var/true_spawn = TRUE // if this is a megafauna that should grant achievements, or have a gps signal
var/nest_range = 10
var/chosen_attack = 1 // chosen attack num
var/list/attack_action_types = list()
@@ -50,6 +52,16 @@
QDEL_NULL(internal_gps)
return ..()
/mob/living/simple_animal/hostile/megafauna/Moved()
if(nest && nest.parent && get_dist(nest.parent, src) > nest_range)
var/turf/closest = get_turf(nest.parent)
for(var/i = 1 to nest_range)
closest = get_step(closest, get_dir(closest, src))
forceMove(closest) // someone teleported out probably and the megafauna kept chasing them
target = null
return
return ..()
/mob/living/simple_animal/hostile/megafauna/can_die()
return ..() && health <= 0
@@ -68,6 +80,8 @@
loot = crusher_loot
/mob/living/simple_animal/hostile/megafauna/AttackingTarget()
if(recovery_time >= world.time)
return
. = ..()
if(. && isliving(target))
var/mob/living/L = target
@@ -90,13 +104,14 @@
/mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L)
if(!L)
return
return FALSE
visible_message(
"<span class='danger'>[src] devours [L]!</span>",
"<span class='userdanger'>You feast on [L], restoring your health!</span>")
if(!is_station_level(z) && !client) //NPC monsters won't heal while on station
if(!is_station_level(z) || client) //NPC monsters won't heal while on station
adjustBruteLoss(-L.maxHealth/2)
L.gib()
return TRUE
/mob/living/simple_animal/hostile/megafauna/ex_act(severity, target)
switch(severity)
@@ -109,6 +124,10 @@
if(3)
adjustBruteLoss(50)
/mob/living/simple_animal/hostile/megafauna/proc/SetRecoveryTime(buffer_time)
recovery_time = world.time + buffer_time
ranged_cooldown = world.time + buffer_time
/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill)
if(!medal_type || admin_spawned || !SSmedals.hub_enabled) //Don't award medals if the medal type isn't set
return FALSE