diff --git a/code/datums/spells/wizard.dm b/code/datums/spells/wizard.dm
index 739a03d92c5..6bcba4cd83b 100644
--- a/code/datums/spells/wizard.dm
+++ b/code/datums/spells/wizard.dm
@@ -383,18 +383,18 @@
name = "Repulse"
desc = "This spell throws everything around the user away."
charge_max = 400
- clothes_req = 1
+ clothes_req = TRUE
invocation = "GITTAH WEIGH"
invocation_type = "shout"
range = 5
cooldown_min = 150
selection_type = "view"
+ sound = 'sound/magic/repulse.ogg'
var/maxthrow = 5
var/sparkle_path = /obj/effect/temp_visual/gravpush
action_icon_state = "repulse"
- sound = 'sound/magic/repulse.ogg'
-/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets, mob/user = usr)
+/obj/effect/proc_holder/spell/aoe_turf/repulse/cast(list/targets, mob/user = usr, stun_amt = 2)
var/list/thrownatoms = list()
var/atom/throwtarget
var/distfromcaster
@@ -411,16 +411,16 @@
throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(AM, user)))
distfromcaster = get_dist(user, AM)
if(distfromcaster == 0)
- if(istype(AM, /mob/living))
+ if(isliving(AM))
var/mob/living/M = AM
M.Weaken(5)
M.adjustBruteLoss(5)
to_chat(M, "You're slammed into the floor by a mystical force!")
else
new sparkle_path(get_turf(AM), get_dir(user, AM)) //created sparkles will disappear on their own
- if(istype(AM, /mob/living))
+ if(isliving(AM))
var/mob/living/M = AM
- M.Weaken(2)
+ M.Weaken(stun_amt)
to_chat(M, "You're thrown back by a mystical force!")
spawn(0)
AM.throw_at(throwtarget, ((Clamp((maxthrow - (Clamp(distfromcaster - 2, 0, distfromcaster))), 3, maxthrow))), 1)//So stuff gets tossed around at the same time.
diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
index 3491c043b4c..232396d951a 100644
--- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
+++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm
@@ -82,7 +82,6 @@
/obj/item/clothing/suit/armor/abductor/vest/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
DeactivateStealth()
- return 0
/obj/item/clothing/suit/armor/abductor/vest/IsReflect()
DeactivateStealth()
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index d6dbf1dddbb..f7a0962269d 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -90,6 +90,10 @@
/obj/effect/temp_visual/dir_setting/wraith/out
icon_state = "phase_shift"
+/obj/effect/temp_visual/dir_setting/tailsweep
+ icon_state = "tailsweep"
+ duration = 4
+
/obj/effect/temp_visual/wizard
name = "water"
icon = 'icons/mob/mob.dmi'
diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm
index 376c8cf38d2..d7038fcd752 100644
--- a/code/game/objects/items/weapons/flamethrower.dm
+++ b/code/game/objects/items/weapons/flamethrower.dm
@@ -231,7 +231,8 @@
create_with_tank = TRUE
/obj/item/flamethrower/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(ptank && damage && attack_type == PROJECTILE_ATTACK && prob(15))
+ var/obj/item/projectile/P = hitby
+ if(damage && attack_type == PROJECTILE_ATTACK && P.damage_type != STAMINA && prob(15))
owner.visible_message("[attack_text] hits the fueltank on [owner]'s [src], rupturing it! What a shot!")
var/turf/target_turf = get_turf(owner)
log_game("A projectile ([hitby]) detonated a flamethrower tank held by [key_name(owner)] at [COORD(target_turf)]")
diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm
index 5c1c7587d8c..514c83e89de 100644
--- a/code/game/objects/items/weapons/grenades/chem_grenade.dm
+++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm
@@ -114,7 +114,8 @@
prime()
/obj/item/grenade/hit_reaction(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
- if(damage && attack_type == PROJECTILE_ATTACK && prob(15))
+ var/obj/item/projectile/P = hitby
+ if(damage && attack_type == PROJECTILE_ATTACK && P.damage_type != STAMINA && prob(15))
owner.visible_message("[attack_text] hits [owner]'s [src], setting it off! What a shot!")
var/turf/T = get_turf(src)
log_game("A projectile ([hitby]) detonated a grenade held by [key_name(owner)] at [COORD(T)]")
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 1be32df4d2a..6d81cdd2dac 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -175,7 +175,7 @@
if(ishuman(L))
var/mob/living/carbon/human/H = L
- if(H.check_shields(src, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that
+ if(H.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
return
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index af366683570..b177d7a7aaf 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -269,51 +269,59 @@ Gunshots/explosions/opening doors/less rare audio (done)
px = -32
/obj/effect/hallucination/oh_yeah
- var/turf/simulated/wall/wall
- var/obj/effect/hallucination/simple/bubblegum/bubblegum = null
+ var/obj/effect/hallucination/simple/bubblegum/bubblegum
var/image/fakebroken
var/image/fakerune
-/obj/effect/hallucination/oh_yeah/New(loc, mob/living/carbon/T)
+/obj/effect/hallucination/oh_yeah/New(loc, mob/living/carbon/C)
+ set waitfor = FALSE
..()
- target = T
- for(var/turf/simulated/wall/W in range(7,target))
+ target = C
+ var/turf/simulated/wall/wall
+ for(var/turf/simulated/wall/W in range(7, target))
wall = W
break
- if(wall)
- fakebroken = image('icons/turf/floors.dmi', wall, "plating", layer = TURF_LAYER)
- var/turf/landing = get_turf(target)
- var/turf/landing_image_turf = get_step(landing, SOUTHWEST) //the icon is 3x3
- fakerune = image('icons/effects/96x96.dmi', landing_image_turf, "landing", layer = ABOVE_OPEN_TURF_LAYER)
- fakebroken.override = TRUE
- if(target.client)
- target.client.images |= fakebroken
- target.client.images |= fakerune
- target.playsound_local(wall,'sound/effects/meteorimpact.ogg', 150, 1)
- bubblegum = new(wall, target)
- sleep(10) //ominous wait
- var/charged = FALSE //only get hit once
- while(get_turf(bubblegum) != landing && target)
- bubblegum.forceMove(get_step_towards(bubblegum, landing))
- bubblegum.setDir(get_dir(bubblegum, landing))
- target.playsound_local(get_turf(bubblegum), 'sound/effects/meteorimpact.ogg', 150, 1)
- shake_camera(target, 2, 1)
- if(bubblegum.Adjacent(target) && !charged)
- charged = TRUE
- target.Weaken(4)
- target.adjustStaminaLoss(40)
- step_away(target, bubblegum)
- shake_camera(target, 4, 3)
- target.visible_message("[target] jumps backwards, falling on the ground!","[bubblegum] slams into you!")
- sleep(2)
- sleep(30)
- qdel(bubblegum)
+ if(!wall)
+ qdel(src)
+ return
+
+ fakebroken = image('icons/turf/floors.dmi', wall, "plating", layer = TURF_LAYER)
+ var/turf/landing = get_turf(target)
+ var/turf/landing_image_turf = get_step(landing, SOUTHWEST) //the icon is 3x3
+ fakerune = image('icons/effects/96x96.dmi', landing_image_turf, "landing", layer = ABOVE_OPEN_TURF_LAYER)
+ fakebroken.override = TRUE
+ if(target.client)
+ target.client.images |= fakebroken
+ target.client.images |= fakerune
+ target.playsound_local(wall,'sound/effects/meteorimpact.ogg', 150, 1)
+ bubblegum = new(wall, target)
+ addtimer(CALLBACK(src, .proc/bubble_attack, landing), 10)
+
+/obj/effect/hallucination/oh_yeah/proc/bubble_attack(turf/landing)
+ var/charged = FALSE //only get hit once
+ while(get_turf(bubblegum) != landing && target && target.stat != DEAD)
+ bubblegum.forceMove(get_step_towards(bubblegum, landing))
+ bubblegum.setDir(get_dir(bubblegum, landing))
+ target.playsound_local(get_turf(bubblegum), 'sound/effects/meteorimpact.ogg', 150, 1)
+ shake_camera(target, 2, 1)
+ if(bubblegum.Adjacent(target) && !charged)
+ charged = TRUE
+ target.Weaken(4)
+ target.adjustStaminaLoss(40)
+ step_away(target, bubblegum)
+ shake_camera(target, 4, 3)
+ target.visible_message("[target] jumps backwards, falling on the ground!", "[bubblegum] slams into you!")
+ sleep(2)
+ sleep(30)
qdel(src)
/obj/effect/hallucination/oh_yeah/Destroy()
if(target.client)
target.client.images.Remove(fakebroken)
target.client.images.Remove(fakerune)
+ QDEL_NULL(fakebroken)
+ QDEL_NULL(fakerune)
+ QDEL_NULL(bubblegum)
return ..()
/obj/effect/hallucination/singularity_scare
diff --git a/code/modules/mob/living/carbon/alien/alien_defense.dm b/code/modules/mob/living/carbon/alien/alien_defense.dm
index 5eb8f5766e4..d7881367d13 100644
--- a/code/modules/mob/living/carbon/alien/alien_defense.dm
+++ b/code/modules/mob/living/carbon/alien/alien_defense.dm
@@ -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)
diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm
index 762df8d3b8b..71a3ae3e87c 100644
--- a/code/modules/mob/living/carbon/human/human_damage.dm
+++ b/code/modules/mob/living/carbon/human/human_damage.dm
@@ -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"))
diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm
index af01634de5a..73147afb93a 100644
--- a/code/modules/mob/living/carbon/human/human_defense.dm
+++ b/code/modules/mob/living/carbon/human/human_defense.dm
@@ -437,16 +437,17 @@ emp_act
visible_message("[M] has tried to disarm [src]!")
/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")
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index dbb7b8731fc..87c9c90359b 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -522,7 +522,7 @@
to_chat(M, "You can't use your hand.")
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("[M] attempted to touch [H]!")
return FALSE
diff --git a/code/modules/mob/living/carbon/slime/slime.dm b/code/modules/mob/living/carbon/slime/slime.dm
index ddab7eae1ee..4cba9bb5a12 100644
--- a/code/modules/mob/living/carbon/slime/slime.dm
+++ b/code/modules/mob/living/carbon/slime/slime.dm
@@ -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)
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index f0c3b85ece1..f624b7ab33d 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -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("\The [M] [M.attacktext] [src]!", \
- "\The [M] [M.attacktext] [src]!")
- 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("\The [M] [M.attacktext] [src]!", \
+ "\The [M] [M.attacktext] [src]!")
+ add_attack_logs(M, src, "Animal attacked")
+ return TRUE
/mob/living/attack_larva(mob/living/carbon/alien/larva/L)
switch(L.a_intent)
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 75cca0dfb54..d9f1aac2757 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -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("[M] [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)
diff --git a/code/modules/mob/living/silicon/silicon_defense.dm b/code/modules/mob/living/silicon/silicon_defense.dm
index a903b6999dc..d88faff76f6 100644
--- a/code/modules/mob/living/silicon/silicon_defense.dm
+++ b/code/modules/mob/living/silicon/silicon_defense.dm
@@ -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)
diff --git a/code/modules/mob/living/simple_animal/animal_defense.dm b/code/modules/mob/living/simple_animal/animal_defense.dm
index 3fafd7267f2..119eb1b29c0 100644
--- a/code/modules/mob/living/simple_animal/animal_defense.dm
+++ b/code/modules/mob/living/simple_animal/animal_defense.dm
@@ -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)
..()
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index ea8e80d2e3c..218693b7dc3 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -80,7 +80,7 @@
else
to_chat(M, "You cannot repair your own dents, as you have none!")
else if(src != M)
- ..()
+ return ..()
/mob/living/simple_animal/hostile/construct/narsie_act()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
index e7ad2c5fab6..701a75fd3ef 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
@@ -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'
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index 43274952362..66799ffc6c6 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -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 = "You are now triple charging at the target you click on."
+ 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 = "You are now charging with hallucinations at the target you click on."
+ 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 = "You are now surrounding the target you click on with hallucinations."
+ 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 = "You are now warping to blood around your clicked position."
+ 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("[src] slams into [L]!", "[src] slams into you!")
- 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, "[src] rends you!")
- 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, "[src] drags you through the blood!")
- 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, "[src] drags you through the blood!")
+ 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("[src] sinks into the blood...")
+ 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("And springs back out!")
+ 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("[src] deflects the projectile; [p_they()] can't be hit with ranged weapons while enraged!", "You deflect the projectile!")
+ 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("[src] slams into [L]!", "[src] tramples you into the ground!")
+ 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("[src] sinks into the blood...")
- 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("And springs back out!")
+/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("[src] sprays a stream of gore!")
- 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("[src] summons a shoal of slaughterlings!")
- 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
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index f275f7ce7d0..8eed750804f 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -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("[src] disintegrates [L]!")
- 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 = "You are now firing in a spiral."
+ 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 = "You are now firing in all directions."
+ 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 = "You are now firing shotgun shots where you aim."
+ 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 = "You are now firing in alternating cardinal directions."
+ 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("\"You can't dodge.\"")
@@ -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("\"Judgement.\"")
- 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("\"Judgement.\"")
+ return spiral_shoot()
+
+/mob/living/simple_animal/hostile/megafauna/colossus/proc/double_spiral()
+ visible_message("\"Die.\"")
+
+ 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("[src] disintegrates [L]!")
+ 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("\"Die.\"")
-
- 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"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
index 4ac58ecf0b5..8b6d6fed947 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -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 = "You are now shooting fire at your target."
+ 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 = "You are now shooting fire at your target and raining fire around you."
+ 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 = "You are now shooting mass fire at your target."
+ 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 = "You are now swooping and raining lava at your target."
+ 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("Fire rains from the sky!")
+ 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("Lava starts to pool up around you!")
+ 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("[src] encases you in an arena of fire!")
+ 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("[src] starts to glow vibrantly as its wounds close up!")
+ 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, "You're hit by [source]'s fire breath!")
+
+ // 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("[src] swoops up high!")
+
+ 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("[src] slams down on [L], crushing [L.p_them()]!")
+ 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("[L] is thrown clear of [src]!")
+ 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, "You fall directly into the pool of lava!")
+
+ // 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("Fire rains from the sky!")
- 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, "You're hit by the drake's fire breath!")
- 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("[src] swoops up high!")
- 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("[src] slams down on [L], crushing [L.p_them()]!")
- 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("[L] is thrown clear of [src]!")
-
- 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, "You need to wait 20 seconds between swoop attacks!")
- 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, "You need to wait [(player_cooldown - world.time) / 10] seconds before swooping again!")
+ 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
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
index 130cfb3f847..81c96135b0e 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm
@@ -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(
"[src] devours [L]!",
"You feast on [L], restoring your health!")
- 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
diff --git a/code/modules/surgery/organs/helpers.dm b/code/modules/surgery/organs/helpers.dm
index fb1167edb01..91bba8d4a1b 100644
--- a/code/modules/surgery/organs/helpers.dm
+++ b/code/modules/surgery/organs/helpers.dm
@@ -10,6 +10,9 @@
/mob/proc/get_int_organ_tag(tag) //is it a brain, is it a brain_tumor?
return
+/mob/living/proc/get_organ(zone)
+ return
+
/mob/living/carbon/get_int_organ(typepath)
return (locate(typepath) in internal_organs)
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index bea6f281f7a..89f57b2517d 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/mob/actions/actions.dmi b/icons/mob/actions/actions.dmi
index 66795e14830..4ea648dea13 100644
Binary files a/icons/mob/actions/actions.dmi and b/icons/mob/actions/actions.dmi differ
diff --git a/icons/mob/spacedragon.dmi b/icons/mob/spacedragon.dmi
new file mode 100644
index 00000000000..98f20ea8851
Binary files /dev/null and b/icons/mob/spacedragon.dmi differ