[MIRROR] Mob Abilities [MDB IGNORE] (#10374)

* Mob Abilities

* Fixing dem conflicts

Co-authored-by: Whoneedspacee <yougotreallyowned@gmail.com>
Co-authored-by: GoldenAlpharex <jerego1234@hotmail.com>
This commit is contained in:
SkyratBot
2022-01-03 22:06:10 +00:00
committed by GitHub
co-authored by Whoneedspacee GoldenAlpharex
parent f40113db17
commit 7bf46acd8b
30 changed files with 1664 additions and 1245 deletions
+5
View File
@@ -55,6 +55,11 @@
// mob cooldowns
#define COOLDOWN_YAWN_PROPAGATION "yawn_propagation_cooldown"
//Shared cooldowns for actions
#define MOB_SHARED_COOLDOWN_1 "mob_shared_cooldown_1"
#define MOB_SHARED_COOLDOWN_2 "mob_shared_cooldown_2"
#define MOB_SHARED_COOLDOWN_3 "mob_shared_cooldown_3"
//TIMER COOLDOWN MACROS
#define COMSIG_CD_STOP(cd_index) "cooldown_[cd_index]"
@@ -0,0 +1,21 @@
// Mob ability signals
/// from base of /datum/action/cooldown/proc/PreActivate(): (datum/action/cooldown/activated)
#define COMSIG_ABILITY_STARTED "mob_ability_base_started"
#define COMPONENT_BLOCK_ABILITY_START (1<<0)
/// from base of /datum/action/cooldown/proc/PreActivate(): (datum/action/cooldown/finished)
#define COMSIG_ABILITY_FINISHED "mob_ability_base_finished"
/// from base of /datum/action/cooldown/mob_cooldown/blood_warp/proc/blood_warp(): ()
#define COMSIG_BLOOD_WARP "mob_ability_blood_warp"
/// from base of /datum/action/cooldown/mob_cooldown/charge/proc/do_charge(): ()
#define COMSIG_STARTED_CHARGE "mob_ability_charge_started"
/// from base of /datum/action/cooldown/mob_cooldown/charge/proc/on_bump(): (atom/target)
#define COMSIG_BUMPED_CHARGE "mob_ability_charge_bumped"
#define COMPONENT_OVERRIDE_CHARGE_BUMP (1<<0)
/// from base of /datum/action/cooldown/mob_cooldown/charge/proc/do_charge(): ()
#define COMSIG_FINISHED_CHARGE "mob_ability_charge_finished"
/// from base of /datum/action/cooldown/mob_cooldown/lava_swoop/proc/swoop_attack(): ()
#define COMSIG_SWOOP_INVULNERABILITY_STARTED "mob_swoop_invulnerability_started"
/// from base of /datum/action/cooldown/mob_cooldown/lava_swoop/proc/swoop_attack(): ()
#define COMSIG_LAVA_ARENA_FAILED "mob_lava_arena_failed"
+8 -1
View File
@@ -405,7 +405,14 @@
// Bit mask of possible return values by can_defib that would result in a revivable patient
#define DEFIB_REVIVABLE_STATES (DEFIB_FAIL_NO_HEART | DEFIB_FAIL_FAILING_HEART | DEFIB_FAIL_HUSK | DEFIB_FAIL_TISSUE_DAMAGE | DEFIB_FAIL_FAILING_BRAIN | DEFIB_POSSIBLE)
#define SLEEP_CHECK_DEATH(X) sleep(X); if(QDELETED(src) || stat == DEAD) return;
#define SLEEP_CHECK_DEATH(X, A) \
sleep(X); \
if(QDELETED(A)) return; \
if(ismob(A)) { \
var/mob/sleep_check_death_mob = A; \
if(sleep_check_death_mob.stat == DEAD) return; \
}
#define DOING_INTERACTION(user, interaction_key) (LAZYACCESS(user.do_afters, interaction_key))
#define DOING_INTERACTION_LIMIT(user, interaction_key, max_interaction_count) ((LAZYACCESS(user.do_afters, interaction_key) || 0) >= max_interaction_count)
+80 -13
View File
@@ -145,7 +145,7 @@
button.color = transparent_when_unavailable ? rgb(128,0,0,128) : rgb(128,0,0)
else
button.color = rgb(255,255,255,255)
return 1
return TRUE
/datum/action/proc/ApplyIcon(atom/movable/screen/movable/action_button/current_button, force = FALSE)
if(icon_icon && button_icon_state && ((current_button.button_icon_state != button_icon_state) || force))
@@ -663,8 +663,16 @@
/datum/action/cooldown
check_flags = NONE
transparent_when_unavailable = FALSE
// The default cooldown applied when StartCooldown() is called
var/cooldown_time = 0
// The actual next time this ability can be used
var/next_use_time = 0
// Whether or not you want the cooldown for the ability to display in text form
var/text_cooldown = TRUE
// Setting for intercepting clicks before activating the ability
var/click_to_activate = FALSE
// Shares cooldowns with other cooldown abilities of the same value, not active if null
var/shared_cooldown
/datum/action/cooldown/New()
..()
@@ -675,25 +683,84 @@
button.maptext_height = 12
/datum/action/cooldown/IsAvailable()
return next_use_time <= world.time
return ..() && (next_use_time <= world.time)
/datum/action/cooldown/proc/StartCooldown()
next_use_time = world.time + cooldown_time
button.maptext = MAPTEXT("<b>[round(cooldown_time/10, 0.1)]</b>")
/// Starts a cooldown time to be shared with similar abilities, will use default cooldown time if an override is not specified
/datum/action/cooldown/proc/StartCooldown(override_cooldown_time)
if(shared_cooldown)
for(var/datum/action/cooldown/shared_ability in owner.actions - src)
if(shared_cooldown == shared_ability.shared_cooldown)
if(isnum(override_cooldown_time))
shared_ability.StartCooldownSelf(override_cooldown_time)
else
shared_ability.StartCooldownSelf(cooldown_time)
StartCooldownSelf(override_cooldown_time)
/// Starts a cooldown time for this ability only, will use default cooldown time if an override is not specified
/datum/action/cooldown/proc/StartCooldownSelf(override_cooldown_time)
if(isnum(override_cooldown_time))
next_use_time = world.time + override_cooldown_time
else
next_use_time = world.time + cooldown_time
UpdateButtonIcon()
START_PROCESSING(SSfastprocess, src)
/datum/action/cooldown/process()
/datum/action/cooldown/Trigger(atom/target)
. = ..()
if(!.)
return
if(!owner)
return FALSE
if(click_to_activate)
if(target)
// For automatic / mob handling
return InterceptClickOn(owner, null, target)
if(owner.click_intercept == src)
owner.click_intercept = null
else
owner.click_intercept = src
for(var/datum/action/cooldown/ability in owner.actions)
ability.UpdateButtonIcon()
return TRUE
return PreActivate(owner)
/// Intercepts client owner clicks to activate the ability
/datum/action/cooldown/proc/InterceptClickOn(mob/living/caller, params, atom/target)
if(!IsAvailable())
return FALSE
if(!target)
return FALSE
PreActivate(target)
caller.click_intercept = null
return TRUE
/// For signal calling
/datum/action/cooldown/proc/PreActivate(atom/target)
if(SEND_SIGNAL(owner, COMSIG_ABILITY_STARTED, src) & COMPONENT_BLOCK_ABILITY_START)
return
. = Activate(target)
SEND_SIGNAL(owner, COMSIG_ABILITY_FINISHED, src)
/// To be implemented by subtypes
/datum/action/cooldown/proc/Activate(atom/target)
return
/datum/action/cooldown/UpdateButtonIcon(status_only = FALSE, force = FALSE)
. = ..()
var/time_left = max(next_use_time - world.time, 0)
if(button)
if(text_cooldown)
button.maptext = MAPTEXT("<b>[round(time_left/10, 0.1)]</b>")
if(!owner || time_left == 0)
button.maptext = ""
if(IsAvailable() && owner.click_intercept == src)
button.color = COLOR_GREEN
/datum/action/cooldown/process()
var/time_left = max(next_use_time - world.time, 0)
if(!owner || time_left == 0)
STOP_PROCESSING(SSfastprocess, src)
var/timeleft = max(next_use_time - world.time, 0)
if(timeleft == 0)
button.maptext = ""
UpdateButtonIcon()
STOP_PROCESSING(SSfastprocess, src)
else
button.maptext = MAPTEXT("<b>[round(timeleft/10, 0.1)]</b>")
UpdateButtonIcon()
/datum/action/cooldown/Grant(mob/M)
..()
+72
View File
@@ -0,0 +1,72 @@
/datum/action/cooldown/mob_cooldown/blood_warp
name = "Blood Warp"
icon_icon = 'icons/effects/blood.dmi'
button_icon_state = "floor1"
desc = "Allows you to teleport to blood at a clicked position."
cooldown_time = 0
shared_cooldown = MOB_SHARED_COOLDOWN_1
/// The range of turfs to try to jaunt to from around the target
var/pick_range = 5
/// The range of turfs if a client is using this ability
var/client_pick_range = 0
/// Whether or not to remove the inside of our radius from the possible pools to jaunt to
var/remove_inner_pools = TRUE
/datum/action/cooldown/mob_cooldown/blood_warp/Activate(atom/target_atom)
StartCooldown(10 SECONDS)
blood_warp(target_atom)
StartCooldown()
/datum/action/cooldown/mob_cooldown/blood_warp/proc/blood_warp(atom/target)
if(owner.Adjacent(target))
return FALSE
var/list/can_jaunt = get_bloodcrawlable_pools(get_turf(owner), 1)
if(!can_jaunt.len)
return FALSE
var/chosen_pick_range = get_pick_range()
var/list/pools = get_bloodcrawlable_pools(get_turf(target), chosen_pick_range)
if(remove_inner_pools)
var/list/pools_to_remove = get_bloodcrawlable_pools(get_turf(target), chosen_pick_range - 1)
pools -= pools_to_remove
if(!pools.len)
return FALSE
var/obj/effect/temp_visual/decoy/DA = new /obj/effect/temp_visual/decoy(owner.loc, owner)
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(0.3 SECONDS, owner)
qdel(DA)
var/obj/effect/decal/cleanable/blood/found_bloodpool
pools = get_bloodcrawlable_pools(get_turf(target), chosen_pick_range)
if(remove_inner_pools)
var/list/pools_to_remove = get_bloodcrawlable_pools(get_turf(target), chosen_pick_range - 1)
pools -= pools_to_remove
if(pools.len)
shuffle_inplace(pools)
found_bloodpool = pick(pools)
if(found_bloodpool)
owner.visible_message("<span class='danger'>[owner] sinks into the blood...</span>")
playsound(get_turf(owner), 'sound/magic/enter_blood.ogg', 100, TRUE, -1)
owner.forceMove(get_turf(found_bloodpool))
playsound(get_turf(owner), 'sound/magic/exit_blood.ogg', 100, TRUE, -1)
owner.visible_message("<span class='danger'>And springs back out!</span>")
SEND_SIGNAL(owner, COMSIG_BLOOD_WARP)
return TRUE
return FALSE
/datum/action/cooldown/mob_cooldown/blood_warp/proc/get_pick_range()
if(owner.client)
return client_pick_range
return pick_range
/proc/get_bloodcrawlable_pools(turf/T, range)
if(range < 0)
return list()
. = list()
for(var/obj/effect/decal/cleanable/nearby in view(T, range))
if(nearby.can_bloodcrawl_in())
. += nearby
+277
View File
@@ -0,0 +1,277 @@
/datum/action/cooldown/mob_cooldown/charge
name = "Charge"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
desc = "Allows you to charge at a chosen position."
cooldown_time = 1.5 SECONDS
/// Delay before the charge actually occurs
var/charge_delay = 0.3 SECONDS
/// The amount of turfs we move past the target
var/charge_past = 2
/// The maximum distance we can charge
var/charge_distance = 50
/// The sleep time before moving in deciseconds while charging
var/charge_speed = 0.5
/// The damage the charger does when bumping into something
var/charge_damage = 30
/// If we destroy objects while charging
var/destroy_objects = TRUE
/// Associative boolean list of chargers that are currently charging
var/list/charging = list()
/// Associative list of chargers and their hit targets
var/list/already_hit = list()
/// Associative direction list of chargers that lets our move signal know how we are supposed to move
var/list/next_move_allowed = list()
/datum/action/cooldown/mob_cooldown/charge/New(Target, delay, past, distance, speed, damage, destroy)
. = ..()
if(delay)
charge_delay = delay
if(past)
charge_past = past
if(distance)
charge_distance = distance
if(speed)
charge_speed = speed
if(damage)
charge_damage = damage
if(destroy)
destroy_objects = destroy
/datum/action/cooldown/mob_cooldown/charge/Activate(atom/target_atom)
// start pre-cooldown so that the ability can't come up while the charge is happening
StartCooldown(10 SECONDS)
charge_sequence(owner, target_atom, charge_delay, charge_past)
StartCooldown()
/datum/action/cooldown/mob_cooldown/charge/proc/charge_sequence(atom/movable/charger, atom/target_atom, delay, past)
do_charge(owner, target_atom, charge_delay, charge_past)
/datum/action/cooldown/mob_cooldown/charge/proc/do_charge(atom/movable/charger, atom/target_atom, delay, past)
if(!target_atom || target_atom == owner)
return
var/chargeturf = get_turf(target_atom)
if(!chargeturf)
return
charger.setDir(get_dir(charger, target_atom))
var/turf/T = get_ranged_target_turf(chargeturf, charger.dir, past)
if(!T)
return
SEND_SIGNAL(owner, COMSIG_STARTED_CHARGE)
RegisterSignal(charger, COMSIG_MOVABLE_BUMP, .proc/on_bump)
RegisterSignal(charger, COMSIG_MOVABLE_PRE_MOVE, .proc/on_move)
RegisterSignal(charger, COMSIG_MOVABLE_MOVED, .proc/on_moved)
charging[charger] = TRUE
already_hit[charger] = list()
DestroySurroundings(charger)
charger.setDir(get_dir(charger, target_atom))
do_charge_indicator(charger, T)
SLEEP_CHECK_DEATH(delay, charger)
var/distance = min(get_dist(charger, T), charge_distance)
for(var/i in 1 to distance)
// Prevents movement from the user during the charge
SLEEP_CHECK_DEATH(charge_speed, charger)
next_move_allowed[charger] = get_dir(charger, T)
step_towards(charger, T)
next_move_allowed.Remove(charger)
UnregisterSignal(charger, COMSIG_MOVABLE_BUMP)
UnregisterSignal(charger, COMSIG_MOVABLE_PRE_MOVE)
UnregisterSignal(charger, COMSIG_MOVABLE_MOVED)
charging.Remove(charger)
already_hit.Remove(charger)
SEND_SIGNAL(owner, COMSIG_FINISHED_CHARGE)
return TRUE
/datum/action/cooldown/mob_cooldown/charge/proc/do_charge_indicator(atom/charger, atom/charge_target)
var/turf/target_turf = get_turf(charge_target)
if(!target_turf)
return
new /obj/effect/temp_visual/dragon_swoop/bubblegum(target_turf)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(charger.loc, charger)
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3)
/datum/action/cooldown/mob_cooldown/charge/proc/on_move(atom/source, atom/new_loc)
SIGNAL_HANDLER
var/expected_dir = next_move_allowed[source]
if(!expected_dir)
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
var/real_dir = get_dir(source, new_loc)
if(!(expected_dir & real_dir))
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
// Disable the flag for the direction we moved (this is so diagonal movements can be fully completed)
next_move_allowed[source] = expected_dir & ~real_dir
if(charging[source])
new /obj/effect/temp_visual/decoy/fading(source.loc, source)
INVOKE_ASYNC(src, .proc/DestroySurroundings, source)
/datum/action/cooldown/mob_cooldown/charge/proc/on_moved(atom/source)
SIGNAL_HANDLER
if(charging[source])
playsound(source, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE)
INVOKE_ASYNC(src, .proc/DestroySurroundings, source)
/datum/action/cooldown/mob_cooldown/charge/proc/DestroySurroundings(atom/movable/charger)
if(!destroy_objects)
return
if(!isanimal(charger))
return
for(var/dir in GLOB.cardinals)
var/turf/T = get_step(charger, dir)
if(QDELETED(T))
continue
if(T.Adjacent(charger))
if(iswallturf(T) || ismineralturf(T))
if(!isanimal(charger))
SSexplosions.medturf += T
continue
T.attack_animal(charger)
continue
for(var/obj/O in T.contents)
if(!O.Adjacent(charger))
continue
if((ismachinery(O) || isstructure(O)) && O.density && !O.IsObscured())
if(!isanimal(charger))
SSexplosions.med_mov_atom += target
break
O.attack_animal(charger)
break
/datum/action/cooldown/mob_cooldown/charge/proc/on_bump(atom/movable/source, atom/target)
SIGNAL_HANDLER
if(SEND_SIGNAL(owner, COMSIG_BUMPED_CHARGE, target) & COMPONENT_OVERRIDE_CHARGE_BUMP)
return
if(charging[source])
if(owner == target)
return
if(isturf(target) || isobj(target) && target.density)
if(isobj(target))
SSexplosions.med_mov_atom += target
else
SSexplosions.medturf += target
INVOKE_ASYNC(src, .proc/DestroySurroundings, source)
hit_target(source, target, charge_damage)
/datum/action/cooldown/mob_cooldown/charge/proc/hit_target(atom/movable/source, atom/target, damage_dealt)
var/list/hit_things = already_hit[source]
if(!isliving(target) || hit_things.Find(target))
return
hit_things.Add(target)
var/mob/living/living_target = target
living_target.visible_message("<span class='danger'>[source] slams into [living_target]!</span>", "<span class='userdanger'>[source] tramples you into the ground!</span>")
source.forceMove(get_turf(living_target))
living_target.apply_damage(damage_dealt, BRUTE, wound_bonus = CANT_WOUND)
playsound(get_turf(living_target), 'sound/effects/meteorimpact.ogg', 100, TRUE)
shake_camera(living_target, 4, 3)
shake_camera(source, 2, 3)
/datum/action/cooldown/mob_cooldown/charge/basic_charge
name = "Basic Charge"
cooldown_time = 6 SECONDS
charge_distance = 4
/datum/action/cooldown/mob_cooldown/charge/basic_charge/do_charge_indicator(atom/charger, atom/charge_target)
charger.Shake(15, 15, 1 SECONDS)
/datum/action/cooldown/mob_cooldown/charge/basic_charge/hit_target(atom/movable/source, atom/target, damage_dealt)
if(!isliving(target))
if(target.density && !target.CanPass(source, get_dir(target, source)))
source.visible_message(span_danger("[source] smashes into [target]!"))
if(isliving(source))
var/mob/living/living_source = source
living_source.Stun(6, ignore_canstun = TRUE)
return
var/mob/living/living_target = target
var/blocked = FALSE
if(ishuman(living_target))
var/mob/living/carbon/human/human_target = living_target
if(human_target.check_shields(source, 0, "the [source.name]", attack_type = LEAP_ATTACK))
blocked = TRUE
if(!blocked)
living_target.visible_message(span_danger("[source] charges on [living_target]!"), span_userdanger("[source] charges into you!"))
living_target.Knockdown(6)
else
if(isliving(source))
var/mob/living/living_source = source
living_source.Stun(6, ignore_canstun = TRUE)
/datum/action/cooldown/mob_cooldown/charge/triple_charge
name = "Triple Charge"
desc = "Allows you to charge three times at a chosen position."
charge_delay = 0.6 SECONDS
/datum/action/cooldown/mob_cooldown/charge/triple_charge/charge_sequence(atom/movable/charger, atom/target_atom, delay, past)
for(var/i in 0 to 2)
do_charge(owner, target_atom, charge_delay - 2 * i, charge_past)
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge
name = "Hallucination Charge"
icon_icon = 'icons/effects/bubblegum.dmi'
button_icon_state = "smack ya one"
desc = "Allows you to create hallucinations that charge around your target."
cooldown_time = 2 SECONDS
charge_delay = 0.6 SECONDS
/// The damage the hallucinations in our charge do
var/hallucination_damage = 15
/// Check to see if we are enraged, enraged ability does more
var/enraged = FALSE
/// Check to see if we should spawn blood
var/spawn_blood = FALSE
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/charge_sequence(atom/movable/charger, atom/target_atom, delay, past)
if(!enraged)
hallucination_charge(target_atom, 6, 8, 0, 6, TRUE)
StartCooldown(cooldown_time * 0.5)
return
for(var/i in 0 to 2)
hallucination_charge(target_atom, 4, 9 - 2 * i, 0, 4, TRUE)
for(var/i in 0 to 2)
do_charge(owner, target_atom, charge_delay - 2 * i, charge_past)
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/do_charge(atom/movable/charger, atom/target_atom, delay, past)
. = ..()
if(charger != owner)
qdel(charger)
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/proc/hallucination_charge(atom/target_atom, clone_amount, delay, past, radius, use_self)
var/starting_angle = rand(1, 360)
if(!radius)
return
var/angle_difference = 360 / clone_amount
var/self_placed = FALSE
for(var/i = 1 to clone_amount)
var/angle = (starting_angle + angle_difference * i)
var/turf/place = locate(target_atom.x + cos(angle) * radius, target_atom.y + sin(angle) * radius, target_atom.z)
if(!place)
continue
if(use_self && !self_placed)
owner.forceMove(place)
self_placed = TRUE
continue
var/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/our_clone = new /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination(place)
our_clone.appearance = owner.appearance
our_clone.name = "[owner]'s hallucination"
our_clone.alpha = 127.5
our_clone.move_through_mob = owner
our_clone.spawn_blood = spawn_blood
INVOKE_ASYNC(src, .proc/do_charge, our_clone, target_atom, delay, past)
if(use_self)
do_charge(owner, target_atom, delay, past)
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hit_target(atom/movable/source, atom/A, damage_dealt)
var/applied_damage = charge_damage
if(source != owner)
applied_damage = hallucination_damage
. = ..(source, A, applied_damage)
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hallucination_surround
name = "Surround Target"
icon_icon = 'icons/turf/walls/wall.dmi'
button_icon_state = "wall-0"
desc = "Allows you to create hallucinations that charge around your target."
charge_delay = 0.6 SECONDS
charge_past = 2
/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hallucination_surround/charge_sequence(atom/movable/charger, atom/target_atom, delay, past)
for(var/i in 0 to 4)
hallucination_charge(target_atom, 2, 8, 2, 2, FALSE)
do_charge(owner, target_atom, charge_delay, charge_past)
+62
View File
@@ -0,0 +1,62 @@
/datum/action/cooldown/mob_cooldown/dash
name = "Dash"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
desc = "Allows you to dash towards a position."
cooldown_time = 1.5 SECONDS
/// The range of the dash
var/dash_range = 4
/// The distance you will be from the target after you dash
var/pick_range = 5
/// The pick range if a client is using this ability
var/client_pick_range = 0
/datum/action/cooldown/mob_cooldown/dash/Activate(atom/target_atom)
StartCooldown(10 SECONDS)
dash_to(target_atom)
StartCooldown()
/datum/action/cooldown/mob_cooldown/dash/proc/dash_to(atom/dash_target)
var/list/accessable_turfs = list()
var/self_dist_to_target = 0
var/turf/own_turf = get_turf(owner)
if(!QDELETED(dash_target))
self_dist_to_target += get_dist(dash_target, own_turf)
for(var/turf/open/check_turf in RANGE_TURFS(dash_range, own_turf))
var/turf_dist_to_target = 0
if(!QDELETED(dash_target))
turf_dist_to_target += get_dist(dash_target, check_turf)
if(get_dist(owner, check_turf) >= dash_range && turf_dist_to_target <= self_dist_to_target && !islava(check_turf) && !ischasm(check_turf))
var/valid = TRUE
for(var/turf/T in get_line(own_turf, check_turf))
if(T.is_blocked_turf(TRUE))
valid = FALSE
continue
if(valid)
accessable_turfs[check_turf] = turf_dist_to_target
var/turf/target_turf
if(!QDELETED(dash_target))
var/closest_dist = dash_range
for(var/t in accessable_turfs)
if(accessable_turfs[t] < closest_dist)
closest_dist = accessable_turfs[t]
for(var/t in accessable_turfs)
if(accessable_turfs[t] != closest_dist)
accessable_turfs -= t
if(!LAZYLEN(accessable_turfs))
return
target_turf = pick(accessable_turfs)
var/turf/step_back_turf = get_step(target_turf, get_cardinal_dir(target_turf, own_turf))
var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf))
new /obj/effect/temp_visual/small_smoke/halfsecond(step_back_turf)
new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf)
var/obj/effect/temp_visual/decoy/fading/halfsecond/D = new (own_turf, owner)
owner.forceMove(step_back_turf)
playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, TRUE, -1)
owner.alpha = 0
animate(owner, alpha = 255, time = 5)
SLEEP_CHECK_DEATH(0.2 SECONDS, owner)
D.forceMove(step_forward_turf)
owner.forceMove(target_turf)
playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, TRUE, -1)
SLEEP_CHECK_DEATH(0.1 SECONDS, owner)
+63
View File
@@ -0,0 +1,63 @@
/datum/action/cooldown/mob_cooldown/fire_breath
name = "Fire Breath"
icon_icon = 'icons/obj/wizard.dmi'
button_icon_state = "fireball"
desc = "Allows you to shoot fire towards a target."
cooldown_time = 3 SECONDS
/// The range of the fire
var/fire_range = 15
/// The sound played when you use this ability
var/fire_sound = 'sound/magic/fireball.ogg'
/// If the fire should be icey fire
var/ice_breath = FALSE
/datum/action/cooldown/mob_cooldown/fire_breath/Activate(atom/target_atom)
StartCooldown(10 SECONDS)
attack_sequence(target_atom)
StartCooldown()
/datum/action/cooldown/mob_cooldown/fire_breath/proc/attack_sequence(atom/target)
playsound(owner.loc, fire_sound, 200, TRUE)
fire_line(target, 0)
/datum/action/cooldown/mob_cooldown/fire_breath/proc/fire_line(atom/target, offset)
SLEEP_CHECK_DEATH(0, owner)
var/list/turfs = line_target(offset, fire_range, target)
dragon_fire_line(owner, turfs, ice_breath)
/datum/action/cooldown/mob_cooldown/fire_breath/proc/line_target(offset, range, atom/target)
if(!target)
return
var/turf/T = get_ranged_target_turf_direct(owner, target, range, offset)
return (get_line(owner, T) - get_turf(owner))
/datum/action/cooldown/mob_cooldown/fire_breath/cone
name = "Fire Cone"
desc = "Allows you to shoot fire towards a target with surrounding lines of fire."
/// The angles relative to the target that shoot lines of fire
var/list/angles = list(-40, 0, 40)
/datum/action/cooldown/mob_cooldown/fire_breath/cone/attack_sequence(atom/target)
playsound(owner.loc, fire_sound, 200, TRUE)
for(var/offset in angles)
INVOKE_ASYNC(src, .proc/fire_line, target, offset)
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire
name = "Mass Fire"
icon_icon = 'icons/effects/fire.dmi'
button_icon_state = "1"
desc = "Allows you to shoot fire in all directions."
cooldown_time = 3 SECONDS
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/attack_sequence(atom/target)
shoot_mass_fire(target, 12, 2.5 SECONDS, 3)
/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/proc/shoot_mass_fire(atom/target, spiral_count, delay_time, times)
SLEEP_CHECK_DEATH(0, owner)
for(var/i = 1 to times)
playsound(owner.loc, fire_sound, 200, TRUE)
var/increment = 360 / spiral_count
for(var/j = 1 to spiral_count)
INVOKE_ASYNC(src, .proc/fire_line, target, j * increment + i * increment / 2)
SLEEP_CHECK_DEATH(delay_time, owner)
+240
View File
@@ -0,0 +1,240 @@
#define SWOOP_HEIGHT 270 //how high up swoops go, in pixels
#define SWOOP_DIRECTION_CHANGE_RANGE 5 //the range our x has to be within to not change the direction we slam from
/datum/action/cooldown/mob_cooldown/lava_swoop
name = "Lava Swoop"
icon_icon = 'icons/effects/effects.dmi'
button_icon_state = "lavastaff_warn"
desc = "Allows you to chase a target while raining lava down."
cooldown_time = 4 SECONDS
/// Check to see if we are enraged
var/enraged = FALSE
/// Check if we are currently swooping
var/swooping = FALSE
/datum/action/cooldown/mob_cooldown/lava_swoop/Grant(mob/M)
. = ..()
ADD_TRAIT(M, TRAIT_LAVA_IMMUNE, src)
ADD_TRAIT(M, TRAIT_NOFIRE, src)
/datum/action/cooldown/mob_cooldown/lava_swoop/Remove(mob/M)
. = ..()
REMOVE_TRAIT(M, TRAIT_LAVA_IMMUNE, src)
REMOVE_TRAIT(M, TRAIT_NOFIRE, src)
/datum/action/cooldown/mob_cooldown/lava_swoop/Activate(atom/target_atom)
StartCooldown(30 SECONDS)
attack_sequence(target_atom)
StartCooldown()
/datum/action/cooldown/mob_cooldown/lava_swoop/proc/attack_sequence(atom/target)
if(enraged)
swoop_attack(target, TRUE)
return
INVOKE_ASYNC(src, .proc/lava_pools, target)
swoop_attack(target)
/datum/action/cooldown/mob_cooldown/lava_swoop/proc/swoop_attack(atom/target, lava_arena = FALSE)
if(swooping || !target)
return
// stop swooped target movement
//RegisterSignal(charger, COMSIG_MOVABLE_PRE_MOVE, .proc/on_move)
swooping = TRUE
owner.set_density(FALSE)
owner.visible_message(span_boldwarning("[owner] swoops up high!"))
var/negative
var/initial_x = owner.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(owner.loc, negative)
negative = !negative //invert it for the swoop down later
var/oldtransform = owner.transform
owner.alpha = 255
animate(owner, alpha = 204, transform = matrix()*0.9, time = 3, easing = BOUNCE_EASING)
for(var/i in 1 to 3)
sleep(1)
if(QDELETED(owner) || owner.stat == DEAD) //we got hit and died, rip us
qdel(F)
if(owner.stat == DEAD)
swooping = FALSE
animate(owner, alpha = 255, transform = oldtransform, time = 0, flags = ANIMATION_END_NOW) //reset immediately
return
animate(owner, alpha = 100, transform = matrix()*0.7, time = 7)
owner.status_flags |= GODMODE
SEND_SIGNAL(owner, COMSIG_SWOOP_INVULNERABILITY_STARTED)
owner.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
SLEEP_CHECK_DEATH(7, owner)
while(target && owner.loc != get_turf(target))
owner.forceMove(get_step(owner, get_dir(owner, target)))
SLEEP_CHECK_DEATH(0.5, owner)
// 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(target)
//ensure swoop direction continuity.
if(negative)
if(ISINRANGE(owner.x, initial_x + 1, initial_x + SWOOP_DIRECTION_CHANGE_RANGE))
negative = FALSE
else
if(ISINRANGE(owner.x, initial_x - SWOOP_DIRECTION_CHANGE_RANGE, initial_x - 1))
negative = TRUE
new /obj/effect/temp_visual/dragon_flight/end(owner.loc, negative)
new /obj/effect/temp_visual/dragon_swoop(owner.loc)
animate(owner, alpha = 255, transform = oldtransform, descentTime)
SLEEP_CHECK_DEATH(descentTime, owner)
owner.mouse_opacity = initial(owner.mouse_opacity)
playsound(owner.loc, 'sound/effects/meteorimpact.ogg', 200, TRUE)
for(var/mob/living/L in orange(1, owner) - owner)
if(L.stat)
owner.visible_message(span_warning("[owner] 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(owner, L)
if(L.loc == owner.loc)
throw_dir = pick(GLOB.alldirs)
var/throwtarget = get_edge_target_turf(owner, throw_dir)
L.throw_at(throwtarget, 3)
owner.visible_message(span_warning("[L] is thrown clear of [owner]!"))
for(var/obj/vehicle/sealed/mecha/M in orange(1, owner))
M.take_damage(75, BRUTE, MELEE, 1)
for(var/mob/M in range(7, owner))
shake_camera(M, 15, 1)
owner.set_density(TRUE)
SLEEP_CHECK_DEATH(1, owner)
swooping = FALSE
if(!lava_success)
SEND_SIGNAL(owner, COMSIG_LAVA_ARENA_FAILED)
owner.status_flags &= ~GODMODE
/datum/action/cooldown/mob_cooldown/lava_swoop/proc/lava_pools(atom/target, amount = 30, delay = 0.8)
if(!target)
return
target.visible_message(span_boldwarning("Lava starts to pool up around you!"))
while(amount > 0)
if(QDELETED(target))
break
var/turf/TT = get_turf(target)
var/turf/T = pick(RANGE_TURFS(1,TT))
var/obj/effect/temp_visual/lava_warning/LW = new /obj/effect/temp_visual/lava_warning(T, 60) // longer reset time for the lava
LW.owner = owner
amount--
SLEEP_CHECK_DEATH(delay, owner)
/datum/action/cooldown/mob_cooldown/lava_swoop/proc/lava_arena(atom/target)
if(!target || !isliving(target))
return
target.visible_message(span_boldwarning("[owner] encases you in an arena of fire!"))
var/amount = 3
var/turf/center = get_turf(owner)
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/open/indestructible))
continue
if(!istype(T, /turf/closed/indestructible))
T.ChangeTurf(/turf/open/floor/plating/asteroid/basalt/lava_land_surface, flags = CHANGETURF_INHERIT_AIR)
else
indestructible_turfs += T
SLEEP_CHECK_DEATH(1 SECONDS, owner) // give them a bit of time to realize what attack is actually happening
var/list/turfs = RANGE_TURFS(2, center)
var/list/mobs_with_clients = list()
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 = FALSE
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 = TRUE
mobs_with_clients |= L
for(var/obj/vehicle/sealed/mecha/M in T.contents)
empty += pick(((RANGE_TURFS(2, M) - RANGE_TURFS(1, M)) & turfs) - empty)
any_attack = TRUE
if(!any_attack) // nothing to attack in the arena, time for enraged attack if we still have a cliented target.
for(var/obj/effect/temp_visual/drakewall/D in drakewalls)
qdel(D)
for(var/a in mobs_with_clients)
var/mob/living/L = a
if(!QDELETED(L) && L.client)
return FALSE
return TRUE
for(var/turf/T in turfs)
if(!(T in empty))
new /obj/effect/temp_visual/lava_warning(T)
else if(!istype(T, /turf/closed/indestructible))
new /obj/effect/temp_visual/lava_safe(T)
amount--
SLEEP_CHECK_DEATH(2.4 SECONDS, owner)
return TRUE // attack finished completely
/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 = 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 = -SWOOP_HEIGHT*0.1, pixel_z = SWOOP_HEIGHT*0.15, time = 3, easing = BOUNCE_EASING)
else
animate(src, pixel_x = SWOOP_HEIGHT*0.1, pixel_z = SWOOP_HEIGHT*0.15, time = 3, easing = BOUNCE_EASING)
sleep(3)
icon_state = "swoop"
if(negative)
animate(src, pixel_x = -SWOOP_HEIGHT, pixel_z = SWOOP_HEIGHT, time = 7)
else
animate(src, pixel_x = SWOOP_HEIGHT, pixel_z = SWOOP_HEIGHT, time = 7)
/obj/effect/temp_visual/dragon_flight/end
pixel_x = SWOOP_HEIGHT
pixel_z = SWOOP_HEIGHT
duration = 10
/obj/effect/temp_visual/dragon_flight/end/flight(negative)
if(negative)
pixel_x = -SWOOP_HEIGHT
animate(src, pixel_x = -16, pixel_z = 0, time = 5)
else
animate(src, pixel_x = -16, pixel_z = 0, time = 5)
#undef SWOOP_HEIGHT
#undef SWOOP_DIRECTION_CHANGE_RANGE
+21
View File
@@ -0,0 +1,21 @@
/datum/action/cooldown/mob_cooldown/meteors
name = "Meteors"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
desc = "Allows you to rain meteors down around yourself."
cooldown_time = 3 SECONDS
shared_cooldown = MOB_SHARED_COOLDOWN_1
/datum/action/cooldown/mob_cooldown/meteors/Activate(atom/target_atom)
StartCooldown(10 SECONDS)
create_meteors(target_atom)
StartCooldown()
/datum/action/cooldown/mob_cooldown/meteors/proc/create_meteors(atom/target)
if(!target)
return
target.visible_message(span_boldwarning("Fire rains from the sky!"))
var/turf/targetturf = get_turf(target)
for(var/turf/turf as anything in RANGE_TURFS(9,targetturf))
if(prob(11))
new /obj/effect/temp_visual/target(turf)
+10
View File
@@ -0,0 +1,10 @@
/datum/action/cooldown/mob_cooldown
name = "Standard Mob Cooldown Ability"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
desc = "Click this ability to attack."
check_flags = AB_CHECK_CONSCIOUS
cooldown_time = 1.5 SECONDS
text_cooldown = TRUE
click_to_activate = TRUE
shared_cooldown = MOB_SHARED_COOLDOWN_1
@@ -0,0 +1,252 @@
/datum/action/cooldown/mob_cooldown/projectile_attack
name = "Projectile Attack"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
desc = "Fires a set of projectiles at a selected target."
cooldown_time = 1.5 SECONDS
/// The type of the projectile to be fired
var/projectile_type
/// The sound played when a projectile is fired
var/projectile_sound
/// If the projectile should home in on its target
var/has_homing = FALSE
/// The turning speed if there is homing
var/homing_turn_speed = 30
/// The variance in the projectiles direction
var/default_projectile_spread = 0
/// The multiplier to the projectiles speed (a value of 2 makes it twice as slow, 0.5 makes it twice as fast)
var/projectile_speed_multiplier = 1
/datum/action/cooldown/mob_cooldown/projectile_attack/New(Target, projectile, homing, spread)
. = ..()
if(projectile)
projectile_type = projectile
if(homing)
has_homing = homing
if(spread)
default_projectile_spread = spread
/datum/action/cooldown/mob_cooldown/projectile_attack/Activate(atom/target_atom)
StartCooldown(100)
attack_sequence(owner, target_atom)
StartCooldown()
/datum/action/cooldown/mob_cooldown/projectile_attack/proc/attack_sequence(mob/living/firer, atom/target)
shoot_projectile(firer, target, null, firer, rand(-default_projectile_spread, default_projectile_spread), null)
/datum/action/cooldown/mob_cooldown/projectile_attack/proc/shoot_projectile(atom/origin, atom/target, set_angle, mob/firer, projectile_spread, speed_multiplier, override_projectile_type, override_homing)
var/turf/startloc = get_turf(origin)
var/turf/endloc = get_turf(target)
if(!startloc || !endloc)
return
var/obj/projectile/our_projectile
if(override_projectile_type)
our_projectile = new override_projectile_type(startloc)
else
our_projectile = new projectile_type(startloc)
if(!isnum(speed_multiplier))
speed_multiplier = projectile_speed_multiplier
our_projectile.speed *= speed_multiplier
our_projectile.preparePixelProjectile(endloc, startloc, null, projectile_spread)
our_projectile.firer = firer
if(target)
our_projectile.original = target
if(override_homing == null && has_homing || override_homing)
our_projectile.homing_turn_speed = homing_turn_speed
our_projectile.set_homing_target(target)
if(isnum(set_angle))
our_projectile.fire(set_angle)
return
our_projectile.fire()
return our_projectile
/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire
name = "Rapid Fire"
icon_icon = 'icons/obj/guns/energy.dmi'
button_icon_state = "kineticgun"
desc = "Fires projectiles repeatedly at a given target."
cooldown_time = 1.5 SECONDS
projectile_type = /obj/projectile/colossus/snowball
default_projectile_spread = 45
/// Total shot count
var/shot_count = 60
/// Delay between shots
var/shot_delay = 0.1 SECONDS
/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/attack_sequence(mob/living/firer, atom/target)
for(var/i in 1 to shot_count)
shoot_projectile(firer, target, null, firer, rand(-default_projectile_spread, default_projectile_spread), null)
SLEEP_CHECK_DEATH(shot_delay, src)
/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/shrapnel
name = "Shrapnel Fire"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
desc = "Fires projectiles that will split into shrapnel after a period of time."
cooldown_time = 6 SECONDS
projectile_type = /obj/projectile/colossus/frost_orb
has_homing = TRUE
default_projectile_spread = 180
shot_count = 8
shot_delay = 1 SECONDS
var/shrapnel_projectile_type = /obj/projectile/colossus/ice_blast
var/shrapnel_angles = list(0, 60, 120, 180, 240, 300)
var/shrapnel_spread = 10
var/break_time = 2 SECONDS
/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/shrapnel/attack_sequence(mob/living/firer, atom/target)
for(var/i in 1 to shot_count)
var/obj/projectile/to_explode = shoot_projectile(firer, target, null, firer, rand(-default_projectile_spread, default_projectile_spread), null)
addtimer(CALLBACK(src, .proc/explode_into_shrapnel, firer, target, to_explode), break_time)
SLEEP_CHECK_DEATH(shot_delay, src)
/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/shrapnel/proc/explode_into_shrapnel(mob/living/firer, atom/target, obj/projectile/to_explode)
if(!to_explode)
return
for(var/angle in shrapnel_angles)
// no speed multiplier for shrapnel
shoot_projectile(to_explode, target, angle + rand(-shrapnel_spread, shrapnel_spread), firer, null, 1, shrapnel_projectile_type, FALSE)
qdel(to_explode)
/datum/action/cooldown/mob_cooldown/projectile_attack/spiral_shots
name = "Spiral Shots"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
desc = "Fires projectiles in a spiral pattern."
cooldown_time = 3 SECONDS
projectile_type = /obj/projectile/colossus
projectile_sound = 'sound/magic/clockwork/invoke_general.ogg'
/// Whether or not the attack is the enraged form
var/enraged = FALSE
/datum/action/cooldown/mob_cooldown/projectile_attack/spiral_shots/attack_sequence(mob/living/firer, atom/target)
if(enraged)
SLEEP_CHECK_DEATH(1 SECONDS, firer)
INVOKE_ASYNC(src, .proc/create_spiral_attack, firer, target, TRUE)
create_spiral_attack(firer, target, FALSE)
return
create_spiral_attack(firer, target)
/datum/action/cooldown/mob_cooldown/projectile_attack/spiral_shots/proc/create_spiral_attack(mob/living/firer, atom/target, negative = pick(TRUE, FALSE))
var/counter = 8
for(var/i in 1 to 80)
if(negative)
counter--
else
counter++
if(counter > 16)
counter = 1
if(counter < 1)
counter = 16
shoot_projectile(firer, target, counter * 22.5, firer, null, null)
playsound(get_turf(firer), projectile_sound, 20, TRUE)
SLEEP_CHECK_DEATH(0.1 SECONDS, firer)
/datum/action/cooldown/mob_cooldown/projectile_attack/random_aoe
name = "All Directions"
icon_icon = 'icons/effects/effects.dmi'
button_icon_state = "at_shield2"
desc = "Fires projectiles in all directions."
cooldown_time = 3 SECONDS
projectile_type = /obj/projectile/colossus
projectile_sound = 'sound/magic/clockwork/invoke_general.ogg'
/datum/action/cooldown/mob_cooldown/projectile_attack/random_aoe/attack_sequence(mob/living/firer, atom/target)
var/turf/U = get_turf(firer)
playsound(U, projectile_sound, 300, TRUE, 5)
for(var/i in 1 to 32)
shoot_projectile(firer, target, rand(0, 360), firer, null, null)
/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast
name = "Shotgun Fire"
icon_icon = 'icons/obj/guns/ballistic.dmi'
button_icon_state = "shotgun"
desc = "Fires projectiles in a shotgun pattern."
cooldown_time = 2 SECONDS
projectile_type = /obj/projectile/colossus
projectile_sound = 'sound/magic/clockwork/invoke_general.ogg'
var/list/shot_angles = list(12.5, 7.5, 2.5, -2.5, -7.5, -12.5)
/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/New(Target, projectile, homing, spread, list/angles)
. = ..()
if(angles)
shot_angles = angles
/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/attack_sequence(mob/living/firer, atom/target)
fire_shotgun(firer, target, shot_angles)
/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/proc/fire_shotgun(mob/living/firer, atom/target, list/chosen_angles)
playsound(firer, projectile_sound, 200, TRUE, 2)
for(var/spread in chosen_angles)
shoot_projectile(firer, target, null, firer, spread, null)
/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/pattern
name = "Alternating Shotgun Fire"
desc = "Fires projectiles in an alternating shotgun pattern."
projectile_type = /obj/projectile/colossus/ice_blast
projectile_sound = null
shot_angles = list(list(-40, -20, 0, 20, 40), list(-30, -10, 10, 30))
var/shot_count = 5
/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/pattern/attack_sequence(mob/living/firer, atom/target)
for(var/i in 1 to shot_count)
var/list/pattern = shot_angles[i % length(shot_angles) + 1] // changing patterns
fire_shotgun(firer, target, pattern)
SLEEP_CHECK_DEATH(0.8 SECONDS, firer)
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots
name = "Directional Shots"
icon_icon = 'icons/obj/guns/ballistic.dmi'
button_icon_state = "pistol"
desc = "Fires projectiles in specific directions."
cooldown_time = 4 SECONDS
projectile_type = /obj/projectile/colossus
projectile_sound = 'sound/magic/clockwork/invoke_general.ogg'
var/list/firing_directions
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/New(Target, projectile, homing, spread, list/dirs)
. = ..()
if(dirs)
firing_directions = dirs
else
firing_directions = GLOB.alldirs.Copy()
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/attack_sequence(mob/living/firer, atom/target)
fire_in_directions(firer, target, firing_directions)
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/proc/fire_in_directions(mob/living/firer, atom/target, list/dirs)
if(!islist(dirs))
dirs = GLOB.alldirs.Copy()
playsound(firer, projectile_sound, 200, TRUE, 2)
for(var/d in dirs)
var/turf/E = get_step(firer, d)
shoot_projectile(firer, E, null, firer, null, null)
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/alternating
name = "Alternating Shots"
desc = "Fires projectiles in alternating directions."
/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/alternating/attack_sequence(mob/living/firer, atom/target)
fire_in_directions(firer, target, GLOB.diagonals)
SLEEP_CHECK_DEATH(1 SECONDS, firer)
fire_in_directions(firer, target, GLOB.cardinals)
SLEEP_CHECK_DEATH(1 SECONDS, firer)
fire_in_directions(firer, target, GLOB.diagonals)
SLEEP_CHECK_DEATH(1 SECONDS, firer)
fire_in_directions(firer, target, GLOB.cardinals)
/datum/action/cooldown/mob_cooldown/projectile_attack/kinetic_accelerator
name = "Fire Kinetic Accelerator"
icon_icon = 'icons/obj/guns/energy.dmi'
button_icon_state = "kineticgun"
desc = "Fires a kinetic accelerator projectile at the target."
cooldown_time = 1.5 SECONDS
projectile_type = /obj/projectile/kinetic/miner
projectile_sound = 'sound/weapons/kenetic_accel.ogg'
/datum/action/cooldown/mob_cooldown/projectile_attack/kinetic_accelerator/Activate(atom/target_atom)
. = ..()
playsound(owner, projectile_sound, 200, TRUE, 2)
owner.visible_message(span_danger("[owner] fires the proto-kinetic accelerator!"))
owner.face_atom(target_atom)
new /obj/effect/temp_visual/dir_setting/firing_effect(owner.loc, owner.dir)
@@ -0,0 +1,27 @@
/datum/action/cooldown/mob_cooldown/transform_weapon
name = "Transform Weapon"
icon_icon = 'icons/obj/lavaland/artefacts.dmi'
button_icon_state = "cleaving_saw"
desc = "Transform weapon into a different state."
cooldown_time = 5 SECONDS
shared_cooldown = MOB_SHARED_COOLDOWN_2
/// The max possible cooldown, cooldown is random between the default cooldown time and this
var/max_cooldown_time = 10 SECONDS
/datum/action/cooldown/mob_cooldown/transform_weapon/Activate(atom/target_atom)
StartCooldown(100)
do_transform(target_atom)
StartCooldown(rand(cooldown_time, max_cooldown_time))
/datum/action/cooldown/mob_cooldown/transform_weapon/proc/do_transform(atom/target)
if(!istype(owner, /mob/living/simple_animal/hostile/megafauna/blood_drunk_miner))
return
var/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/BDM = owner
var/obj/item/melee/cleaving_saw/miner/miner_saw = BDM.miner_saw
miner_saw.attack_self(owner)
if(!miner_saw.is_open)
BDM.rapid_melee = 5 // 4 deci cooldown before changes, npcpool subsystem wait is 20, 20/4 = 5
else
BDM.rapid_melee = 3 // same thing but halved (slightly rounded up)
BDM.icon_state = "miner[miner_saw.is_open ? "_transformed":""]"
BDM.icon_living = "miner[miner_saw.is_open ? "_transformed":""]"
+1
View File
@@ -117,6 +117,7 @@ GLOBAL_LIST_INIT(admin_verbs_fun, list(
/client/proc/show_tip,
/client/proc/smite,
/client/proc/admin_away,
/client/proc/add_mob_ability,
/datum/admins/proc/station_traits_panel,
/client/proc/spawn_pollution, // SKYRAT EDIT ADDITION
))
+26
View File
@@ -505,3 +505,29 @@
message_admins("[key_name_admin(usr)] started weather of type [weather_type] on the z-level [z_level].")
log_admin("[key_name(usr)] started weather of type [weather_type] on the z-level [z_level].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Run Weather")
/client/proc/add_mob_ability()
set category = "Admin.Events"
set name = "Add Mob Ability"
set desc = "Adds an ability to a marked mob."
if(!holder)
return
if(!holder.marked_datum || !istype(holder.marked_datum, /mob/living))
return
var/mob/living/marked_mob = holder.marked_datum
var/ability_type = input("Choose an ability", "Ability") as null|anything in sort_list(subtypesof(/datum/action/cooldown/mob_cooldown), /proc/cmp_typepaths_asc)
if(!ability_type)
return
var/datum/action/cooldown/mob_cooldown/add_ability = new ability_type()
add_ability.Grant(marked_mob)
message_admins("[key_name_admin(usr)] added mob ability [ability_type] to mob [marked_mob].")
log_admin("[key_name(usr)] added mob ability [ability_type] to mob [marked_mob].")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Add Mob Ability") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -49,7 +49,7 @@
successfulshocks = 0
if(shockallchains())
successfulshocks++
SLEEP_CHECK_DEATH(3)
SLEEP_CHECK_DEATH(3, src)
/mob/living/simple_animal/hostile/guardian/beam/Recall()
. = ..()
@@ -191,17 +191,25 @@
status_flags = NONE
mob_size = MOB_SIZE_LARGE
gold_core_spawnable = NO_SPAWN
charger = TRUE
charge_distance = 4
menu_description = "Tank spider variant with an enormous amount of health and damage, but is very slow when not on webbing. It also has a charge ability to close distance with a target after a small windup. Does not inject toxin."
///Whether or not the tarantula is currently walking on webbing.
/// Whether or not the tarantula is currently walking on webbing.
var/silk_walking = TRUE
/// Charging ability
var/datum/action/cooldown/mob_cooldown/charge/basic_charge/charge
/mob/living/simple_animal/hostile/giant_spider/tarantula/ranged_secondary_attack(atom/target, modifiers)
if(COOLDOWN_FINISHED(src, charge_cooldown))
INVOKE_ASYNC(src, /mob/living/simple_animal/hostile/.proc/enter_charge, target)
else
to_chat(src, span_notice("Your charge is still on cooldown!"))
/mob/living/simple_animal/hostile/giant_spider/tarantula/Initialize(mapload)
. = ..()
charge = new /datum/action/cooldown/mob_cooldown/charge/basic_charge()
charge.Grant(src)
/mob/living/simple_animal/hostile/giant_spider/tarantula/Destroy()
QDEL_NULL(charge)
return ..()
/mob/living/simple_animal/hostile/giant_spider/tarantula/OpenFire()
if(client)
return
charge.Trigger(target)
/mob/living/simple_animal/hostile/giant_spider/tarantula/Moved(atom/oldloc, dir)
. = ..()
@@ -54,19 +54,6 @@
var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach
var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever
///When a target is found, will the mob attempt to charge at it's target?
var/charger = FALSE
///Tracks if the target is actively charging.
var/charge_state = FALSE
///In a charge, how many tiles will the charger travel?
var/charge_distance = 3
///How often can the charging mob actually charge? Effects the cooldown between charges.
var/charge_frequency = 6 SECONDS
///If the mob is charging, how long will it stun it's target on success, and itself on failure?
var/knockdown_time = 3 SECONDS
///Declares a cooldown for potential charges right off the bat.
COOLDOWN_DECLARE(charge_cooldown)
/mob/living/simple_animal/hostile/Initialize(mapload)
. = ..()
wanted_objects = typecacheof(wanted_objects)
@@ -295,9 +282,6 @@
if(ranged) //We ranged? Shoot at em
if(!target.Adjacent(target_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown
OpenFire(target)
if(charger && (target_distance > minimum_distance) && (target_distance <= charge_distance))//Attempt to close the distance with a charge.
enter_charge(target)
return TRUE
if(!Process_Spacemove()) //Drifting
walk(src,0)
return 1
@@ -604,71 +588,6 @@
else if (M.loc.type in hostile_machines)
. += M.loc
/**
* Proc that handles a charge attack windup for a mob.
*/
/mob/living/simple_animal/hostile/proc/enter_charge(atom/target)
if(charge_state || !(COOLDOWN_FINISHED(src, charge_cooldown)))
return FALSE
if(!can_charge_target(target))
return FALSE
Shake(15, 15, 1 SECONDS)
addtimer(CALLBACK(src, .proc/handle_charge_target, target), 1.5 SECONDS, TIMER_STOPPABLE)
/**
* Proc that checks if the mob can charge attack.
*/
/mob/living/simple_animal/hostile/proc/can_charge_target(atom/target)
if(stat == DEAD || body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_IMMOBILIZED) || !has_gravity() || !target.has_gravity())
return FALSE
return TRUE
/**
* Proc that throws the mob at the target after the windup.
*/
/mob/living/simple_animal/hostile/proc/handle_charge_target(atom/target)
if(!can_charge_target(target))
return FALSE
charge_state = TRUE
throw_at(target, charge_distance, 1, src, FALSE, TRUE, callback = CALLBACK(src, .proc/charge_end))
COOLDOWN_START(src, charge_cooldown, charge_frequency)
return TRUE
/**
* Proc that handles a charge attack after it's concluded.
*/
/mob/living/simple_animal/hostile/proc/charge_end()
charge_state = FALSE
/**
* Proc that handles the charge impact of the charging mob.
*/
/mob/living/simple_animal/hostile/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(!charge_state)
return ..()
if(hit_atom)
if(isliving(hit_atom))
var/mob/living/L = hit_atom
var/blocked = FALSE
if(ishuman(hit_atom))
var/mob/living/carbon/human/H = hit_atom
if(H.check_shields(src, 0, "the [name]", attack_type = LEAP_ATTACK))
blocked = TRUE
if(!blocked)
L.visible_message(span_danger("[src] charges on [L]!"), span_userdanger("[src] charges into you!"))
L.Knockdown(knockdown_time)
else
Stun((knockdown_time * 2), ignore_canstun = TRUE)
charge_end()
else if(hit_atom.density && !hit_atom.CanPass(src, get_dir(hit_atom, src)))
visible_message(span_danger("[src] smashes into [hit_atom]!"))
Stun((knockdown_time * 2), ignore_canstun = TRUE)
if(charge_state)
charge_state = FALSE
update_icons()
/mob/living/simple_animal/hostile/proc/get_targets_from()
var/atom/target_from = targets_from.resolve()
if(!target_from)
@@ -1,5 +1,3 @@
#define MINER_DASH_RANGE 4
/*
BLOOD-DRUNK MINER
@@ -35,8 +33,6 @@ Difficulty: Medium
speak_emote = list("roars")
speed = 3
move_to_delay = 3
projectiletype = /obj/projectile/kinetic/miner
projectilesound = 'sound/weapons/kenetic_accel.ogg'
ranged = TRUE
ranged_cooldown_time = 1.6 SECONDS
pixel_x = -16
@@ -51,74 +47,47 @@ Difficulty: Medium
crusher_achievement_type = /datum/award/achievement/boss/blood_miner_crusher
score_achievement_type = /datum/award/score/blood_miner_score
var/obj/item/melee/cleaving_saw/miner/miner_saw
var/time_until_next_transform = 0
var/dashing = FALSE
var/dash_cooldown = 0
var/dash_cooldown_time = 1.5 SECONDS
var/guidance = FALSE
var/transform_stop_attack = FALSE // stops the blood drunk miner from attacking after transforming his weapon until the next attack chain
deathmessage = "falls to the ground, decaying into glowing particles."
deathsound = "bodyfall"
footstep_type = FOOTSTEP_MOB_HEAVY
attack_action_types = list(/datum/action/innate/megafauna_attack/dash,
/datum/action/innate/megafauna_attack/kinetic_accelerator,
/datum/action/innate/megafauna_attack/transform_weapon)
move_force = MOVE_FORCE_NORMAL //Miner beeing able to just move structures like bolted doors and glass looks kinda strange
/// Dash ability
var/datum/action/cooldown/mob_cooldown/dash/dash
/// Kinetic accelerator ability
var/datum/action/cooldown/mob_cooldown/projectile_attack/kinetic_accelerator/kinetic_accelerator
/// Transform weapon ability
var/datum/action/cooldown/mob_cooldown/transform_weapon/transform_weapon
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Initialize(mapload)
. = ..()
miner_saw = new(src)
ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT)
dash = new /datum/action/cooldown/mob_cooldown/dash()
kinetic_accelerator = new /datum/action/cooldown/mob_cooldown/projectile_attack/kinetic_accelerator()
transform_weapon = new /datum/action/cooldown/mob_cooldown/transform_weapon()
dash.Grant(src)
kinetic_accelerator.Grant(src)
transform_weapon.Grant(src)
/datum/action/innate/megafauna_attack/dash
name = "Dash To Target"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
chosen_message = "<span class='colossus'>You are now dashing to your target.</span>"
chosen_attack_num = 1
/datum/action/innate/megafauna_attack/kinetic_accelerator
name = "Fire Kinetic Accelerator"
icon_icon = 'icons/obj/guns/energy.dmi'
button_icon_state = "kineticgun"
chosen_message = "<span class='colossus'>You are now shooting your kinetic accelerator.</span>"
chosen_attack_num = 2
/datum/action/innate/megafauna_attack/transform_weapon
name = "Transform Weapon"
icon_icon = 'icons/obj/lavaland/artefacts.dmi'
button_icon_state = "cleaving_saw"
chosen_message = "<span class='colossus'>You are now transforming your weapon.</span>"
chosen_attack_num = 3
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/update_cooldowns(list/cooldown_updates, ignore_staggered = FALSE)
. = ..()
if(cooldown_updates[COOLDOWN_UPDATE_SET_DASH])
dash_cooldown = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_DASH]
if(cooldown_updates[COOLDOWN_UPDATE_ADD_DASH])
dash_cooldown += cooldown_updates[COOLDOWN_UPDATE_ADD_DASH]
if(cooldown_updates[COOLDOWN_UPDATE_SET_TRANSFORM])
time_until_next_transform = world.time + cooldown_updates[COOLDOWN_UPDATE_SET_TRANSFORM]
if(cooldown_updates[COOLDOWN_UPDATE_ADD_TRANSFORM])
time_until_next_transform += cooldown_updates[COOLDOWN_UPDATE_ADD_TRANSFORM]
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Destroy()
QDEL_NULL(dash)
QDEL_NULL(kinetic_accelerator)
QDEL_NULL(transform_weapon)
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/OpenFire()
if(client)
switch(chosen_attack)
if(1)
dash(target)
if(2)
shoot_ka()
if(3)
transform_weapon()
return
Goto(target, move_to_delay, minimum_distance)
if(get_dist(src, target) > MINER_DASH_RANGE && dash_cooldown <= world.time)
dash_attack()
if(get_dist(src, target) > 4)
if(dash.Trigger(target))
kinetic_accelerator.StartCooldown(0)
kinetic_accelerator.Trigger(target)
else
shoot_ka()
transform_weapon()
kinetic_accelerator.Trigger(target)
transform_weapon.Trigger(target)
/obj/item/melee/cleaving_saw/miner //nerfed saw because it is very murdery
force = 6
@@ -133,7 +102,7 @@ Difficulty: Medium
damage = 20
speed = 0.9
icon_state = "ka_tracer"
range = MINER_DASH_RANGE
range = 4
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
var/adjustment_amount = amount * 0.1
@@ -147,23 +116,17 @@ Difficulty: Medium
new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc)
if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid!
if(newloc && newloc.z == z && (islava(newloc) || ischasm(newloc))) //we're not stupid!
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/ex_act(severity, target)
if(dash())
if(dash.Trigger(target))
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/MeleeAction(patience = TRUE)
transform_stop_attack = FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/AttackingTarget()
if(client)
transform_stop_attack = FALSE
if(QDELETED(target) || transform_stop_attack)
if(QDELETED(target))
return
face_atom(target)
if(isliving(target))
@@ -195,82 +158,6 @@ Difficulty: Medium
if(. && target && !targets_the_same)
wander = TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash_attack()
INVOKE_ASYNC(src, .proc/dash, target)
shoot_ka()
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/shoot_ka()
if(ranged_cooldown <= world.time && get_dist(src, target) <= MINER_DASH_RANGE && !Adjacent(target))
update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = ranged_cooldown_time), ignore_staggered = TRUE)
visible_message(span_danger("[src] fires the proto-kinetic accelerator!"))
face_atom(target)
new /obj/effect/temp_visual/dir_setting/firing_effect(loc, dir)
Shoot(target)
changeNext_move(CLICK_CD_RANGE)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/dash(atom/dash_target)
if(world.time < dash_cooldown)
return
var/list/accessable_turfs = list()
var/self_dist_to_target = 0
var/turf/own_turf = get_turf(src)
if(!QDELETED(dash_target))
self_dist_to_target += get_dist(dash_target, own_turf)
for(var/turf/open/O in RANGE_TURFS(MINER_DASH_RANGE, own_turf))
var/turf_dist_to_target = 0
if(!QDELETED(dash_target))
turf_dist_to_target += get_dist(dash_target, O)
if(get_dist(src, O) >= MINER_DASH_RANGE && turf_dist_to_target <= self_dist_to_target && !islava(O) && !ischasm(O))
var/valid = TRUE
for(var/turf/T in get_line(own_turf, O))
if(T.is_blocked_turf(TRUE))
valid = FALSE
continue
if(valid)
accessable_turfs[O] = turf_dist_to_target
var/turf/target_turf
if(!QDELETED(dash_target))
var/closest_dist = MINER_DASH_RANGE
for(var/t in accessable_turfs)
if(accessable_turfs[t] < closest_dist)
closest_dist = accessable_turfs[t]
for(var/t in accessable_turfs)
if(accessable_turfs[t] != closest_dist)
accessable_turfs -= t
if(!LAZYLEN(accessable_turfs))
return
update_cooldowns(list(COOLDOWN_UPDATE_SET_DASH = dash_cooldown_time))
target_turf = pick(accessable_turfs)
var/turf/step_back_turf = get_step(target_turf, get_cardinal_dir(target_turf, own_turf))
var/turf/step_forward_turf = get_step(own_turf, get_cardinal_dir(own_turf, target_turf))
new /obj/effect/temp_visual/small_smoke/halfsecond(step_back_turf)
new /obj/effect/temp_visual/small_smoke/halfsecond(step_forward_turf)
var/obj/effect/temp_visual/decoy/fading/halfsecond/D = new (own_turf, src)
forceMove(step_back_turf)
playsound(own_turf, 'sound/weapons/punchmiss.ogg', 40, TRUE, -1)
dashing = TRUE
alpha = 0
animate(src, alpha = 255, time = 5)
SLEEP_CHECK_DEATH(2)
D.forceMove(step_forward_turf)
forceMove(target_turf)
playsound(target_turf, 'sound/weapons/punchmiss.ogg', 40, TRUE, -1)
SLEEP_CHECK_DEATH(1)
dashing = FALSE
return TRUE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/proc/transform_weapon()
if(time_until_next_transform <= world.time)
miner_saw.attack_self(src)
if(!miner_saw.is_open)
rapid_melee = 5 // 4 deci cooldown before changes, npcpool subsystem wait is 20, 20/4 = 5
else
rapid_melee = 3 // same thing but halved (slightly rounded up)
transform_stop_attack = TRUE
icon_state = "miner[miner_saw.is_open ? "_transformed":""]"
icon_living = "miner[miner_saw.is_open ? "_transformed":""]"
update_cooldowns(list(COOLDOWN_UPDATE_SET_TRANSFORM = rand(5 SECONDS, 10 SECONDS)))
/obj/effect/temp_visual/dir_setting/miner_death
icon_state = "miner_death"
duration = 15
@@ -298,7 +185,7 @@ Difficulty: Medium
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/hunter/AttackingTarget()
. = ..()
if(. && prob(12))
INVOKE_ASYNC(src, .proc/dash)
INVOKE_ASYNC(dash, /datum/action/proc/Trigger, target)
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/doom
name = "hostile-environment miner"
@@ -306,6 +193,7 @@ Difficulty: Medium
speed = 8
move_to_delay = 8
ranged_cooldown_time = 0.8 SECONDS
dash_cooldown = 0.8 SECONDS
#undef MINER_DASH_RANGE
/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/doom/Initialize(mapload)
. = ..()
dash.cooldown_time = 0.8 SECONDS
@@ -59,55 +59,51 @@ Difficulty: Hard
crusher_loot = list(/obj/structure/closet/crate/necropolis/bubblegum/crusher)
loot = list(/obj/structure/closet/crate/necropolis/bubblegum)
blood_volume = BLOOD_VOLUME_MAXIMUM //BLEED FOR ME
var/charging = FALSE
var/enrage_till = 0
var/enrage_time = 7 SECONDS
var/revving_charge = FALSE
gps_name = "Bloody Signal"
achievement_type = /datum/award/achievement/boss/bubblegum_kill
crusher_achievement_type = /datum/award/achievement/boss/bubblegum_crusher
score_achievement_type = /datum/award/score/bubblegum_score
deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... "
deathsound = 'sound/magic/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)
small_sprite_type = /datum/action/small_sprite/megafauna/bubblegum
faction = list("mining", "boss", "hell")
/// Check to see if we should spawn blood
var/spawn_blood = TRUE
/// Actual time where enrage ends
var/enrage_till = 0
/// Duration of enrage ability
var/enrage_time = 7 SECONDS
/// Triple charge ability
var/datum/action/cooldown/mob_cooldown/charge/triple_charge/triple_charge
/// Hallucination charge ability
var/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hallucination_charge
/// Hallucination charge surround ability
var/datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hallucination_surround/hallucination_charge_surround
/// Blood warp ability
var/datum/action/cooldown/mob_cooldown/blood_warp/blood_warp
/mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize(mapload)
/mob/living/simple_animal/hostile/megafauna/bubblegum/Initialize()
. = ..()
ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT)
triple_charge = new /datum/action/cooldown/mob_cooldown/charge/triple_charge()
hallucination_charge = new /datum/action/cooldown/mob_cooldown/charge/hallucination_charge()
hallucination_charge_surround = new /datum/action/cooldown/mob_cooldown/charge/hallucination_charge/hallucination_surround()
blood_warp = new /datum/action/cooldown/mob_cooldown/blood_warp()
triple_charge.Grant(src)
hallucination_charge.Grant(src)
hallucination_charge_surround.Grant(src)
blood_warp.Grant(src)
hallucination_charge.spawn_blood = TRUE
hallucination_charge_surround.spawn_blood = TRUE
RegisterSignal(src, COMSIG_BLOOD_WARP, .proc/blood_enrage)
RegisterSignal(src, COMSIG_FINISHED_CHARGE, .proc/after_charge)
/datum/action/innate/megafauna_attack/triple_charge
name = "Triple Charge"
icon_icon = 'icons/mob/actions/actions_items.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
/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
/datum/action/innate/megafauna_attack/hallucination_surround
name = "Surround Target"
icon_icon = 'icons/turf/walls/wall.dmi'
button_icon_state = "wall-0"
chosen_message = "<span class='colossus'>You are now surrounding the target you click on with hallucinations.</span>"
chosen_attack_num = 3
/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/Destroy()
QDEL_NULL(triple_charge)
QDEL_NULL(hallucination_charge)
QDEL_NULL(hallucination_charge_surround)
QDEL_NULL(blood_warp)
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/update_cooldowns(list/cooldown_updates, ignore_staggered = FALSE)
. = ..()
@@ -117,94 +113,25 @@ Difficulty: Hard
enrage_till += cooldown_updates[COOLDOWN_UPDATE_ADD_ENRAGE]
/mob/living/simple_animal/hostile/megafauna/bubblegum/OpenFire()
if(charging)
return
anger_modifier = clamp(((maxHealth - health)/60),0,20)
enrage_time = initial(enrage_time) * clamp(anger_modifier / 20, 0.5, 1)
update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = 5 SECONDS))
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()
blood_warp.Trigger(target)
if(!BUBBLEGUM_SMASH)
triple_charge()
triple_charge.Trigger(target)
else
if(prob(50 + anger_modifier))
hallucination_charge()
hallucination_charge.Trigger(target)
else
surround_with_hallucinations()
hallucination_charge_surround.Trigger(target)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/triple_charge()
charge(delay = 6)
charge(delay = 4)
charge(delay = 2)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 1.5 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 1.5 SECONDS))
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/hallucination_charge()
if(!BUBBLEGUM_SMASH || prob(33))
hallucination_charge_around(times = 6, delay = 8)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 1 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 1 SECONDS))
else
hallucination_charge_around(times = 4, delay = 9)
hallucination_charge_around(times = 4, delay = 8)
hallucination_charge_around(times = 4, delay = 7)
triple_charge()
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 2 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 2 SECONDS))
/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)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 2 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 2 SECONDS))
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/charge(atom/chargeat = target, delay = 3, chargepast = 2)
if(!chargeat)
return
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)
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()
charging = FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood()
var/list/targets = ListTargets()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_mobs_on_blood(mob/target)
var/list/targets = list(target)
. = list()
for(var/mob/living/L in targets)
var/list/bloodpool = get_pools(get_turf(L), 0)
var/list/bloodpool = get_bloodcrawlable_pools(get_turf(L), 0)
if(bloodpool.len && (!faction_check_mob(L) || L.stat == DEAD))
. += L
@@ -243,7 +170,7 @@ Difficulty: Hard
bloodsmack(target_two_turf, handedness)
if(target_one)
var/list/pools = get_pools(get_turf(target_one), 0)
var/list/pools = get_bloodcrawlable_pools(get_turf(target_one), 0)
if(pools.len)
target_one_turf = get_turf(target_one)
if(target_one_turf)
@@ -253,7 +180,7 @@ Difficulty: Hard
bloodsmack(target_one_turf, !handedness)
if(!target_two && target_one)
var/list/poolstwo = get_pools(get_turf(target_one), 0)
var/list/poolstwo = get_bloodcrawlable_pools(get_turf(target_one), 0)
if(poolstwo.len)
target_one_turf = get_turf(target_one)
if(target_one_turf)
@@ -267,14 +194,14 @@ Difficulty: Hard
new /obj/effect/temp_visual/bubblegum_hands/rightsmack(T)
else
new /obj/effect/temp_visual/bubblegum_hands/leftsmack(T)
SLEEP_CHECK_DEATH(4)
SLEEP_CHECK_DEATH(4, src)
for(var/mob/living/L in T)
if(!faction_check_mob(L))
to_chat(L, span_userdanger("[src] rends you!"))
playsound(T, attack_sound, 100, TRUE, -1)
var/limb_to_hit = L.get_bodypart(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), wound_bonus = CANT_WOUND)
SLEEP_CHECK_DEATH(3)
SLEEP_CHECK_DEATH(3, src)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/bloodgrab(turf/T, handedness)
if(handedness)
@@ -283,7 +210,7 @@ Difficulty: Hard
else
new /obj/effect/temp_visual/bubblegum_hands/leftpaw(T)
new /obj/effect/temp_visual/bubblegum_hands/leftthumb(T)
SLEEP_CHECK_DEATH(6)
SLEEP_CHECK_DEATH(6, src)
for(var/mob/living/L in T)
if(!faction_check_mob(L))
if(L.stat != CONSCIOUS)
@@ -293,45 +220,8 @@ Difficulty: Hard
L.forceMove(targetturf)
playsound(targetturf, 'sound/magic/exit_blood.ogg', 100, TRUE, -1)
addtimer(CALLBACK(src, .proc/devour, L), 2)
SLEEP_CHECK_DEATH(1)
SLEEP_CHECK_DEATH(1, src)
/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_danger("[src] sinks into the blood..."))
playsound(get_turf(src), 'sound/magic/enter_blood.ogg', 100, TRUE, -1)
forceMove(get_turf(found_bloodpool))
playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, TRUE, -1)
visible_message(span_danger("And springs back out!"))
blood_enrage()
return TRUE
return FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/be_aggressive()
@@ -339,7 +229,6 @@ Difficulty: Hard
return TRUE
return isliving(target) && HAS_TRAIT(target, TRAIT_INCAPACITATED)
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/get_retreat_distance()
return (be_aggressive() ? null : initial(retreat_distance))
@@ -351,15 +240,20 @@ Difficulty: Hard
minimum_distance = get_minimum_distance()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage()
SIGNAL_HANDLER
if(!BUBBLEGUM_CAN_ENRAGE)
return FALSE
update_cooldowns(list(COOLDOWN_UPDATE_SET_ENRAGE = enrage_time))
enrage_till = world.time + enrage_time
update_approach()
change_move_delay(3.75)
INVOKE_ASYNC(src, .proc/change_move_delay, 3.75)
add_atom_colour(COLOR_BUBBLEGUM_RED, 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/after_charge()
SIGNAL_HANDLER
try_bloodattack()
/mob/living/simple_animal/hostile/megafauna/bubblegum/proc/blood_enrage_end()
update_approach()
change_move_delay()
@@ -370,46 +264,11 @@ Difficulty: Hard
set_varspeed(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(src.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, forced = FALSE)
. = ..()
anger_modifier = clamp(((maxHealth - health)/60),0,20)
enrage_time = initial(enrage_time) * clamp(anger_modifier / 20, 0.5, 1)
hallucination_charge.enraged = BUBBLEGUM_SMASH
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))
@@ -417,29 +276,15 @@ Difficulty: Hard
else
B.setDir(pick(GLOB.cardinals))
/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/grant_achievement(medaltype,scoretype)
. = ..()
if(!(flags_1 & ADMIN_SPAWNED_1))
SSshuttle.shuttle_purchase_requirements_met[SHUTTLE_UNLOCK_BUBBLEGUM] = TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect)
if(!charging)
..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/AttackingTarget()
if(!charging)
. = ..()
if(.)
update_cooldowns(list(COOLDOWN_UPDATE_ADD_MELEE = 2 SECONDS)) // can only attack melee once every 2 seconds but rapid_melee gives higher priority
. = ..()
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/projectile/P)
if(BUBBLEGUM_IS_ENRAGED)
@@ -455,53 +300,70 @@ Difficulty: Hard
severity = EXPLODE_LIGHT // puny mortals
return ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination))
return TRUE
/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)
. = ..()
if(spawn_blood)
new /obj/effect/decal/cleanable/blood/bubblegum(src.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)
if(isobj(A))
SSexplosions.med_mov_atom += A
else
SSexplosions.medturf += A
DestroySurroundings()
if(isliving(A))
var/mob/living/L = A
L.visible_message(span_danger("[src] slams into [L]!"), span_userdanger("[src] tramples you into the ground!"))
src.forceMove(get_turf(L))
L.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE, wound_bonus = CANT_WOUND)
playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE)
shake_camera(L, 4, 3)
shake_camera(src, 2, 3)
..()
/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
achievement_type = null
crusher_achievement_type = null
score_achievement_type = null
deathmessage = "Explodes into a pool of blood!"
deathsound = 'sound/effects/splat.ogg'
true_spawn = FALSE
var/move_through_mob
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize()
. = ..()
toggle_ai(AI_OFF)
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Destroy()
if(spawn_blood)
new /obj/effect/decal/cleanable/blood(get_turf(src))
. = ..()
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Life(delta_time = SSMOBS_DT, times_fired)
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE)
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/OpenFire()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/AttackingTarget()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack()
return
/obj/effect/decal/cleanable/blood/bubblegum
bloodiness = 0
/obj/effect/decal/cleanable/blood/bubblegum/can_bloodcrawl_in()
return TRUE
/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
/obj/effect/temp_visual/dragon_swoop/bubblegum
duration = 10
@@ -529,50 +391,3 @@ Difficulty: Hard
/obj/effect/temp_visual/bubblegum_hands/leftsmack
icon_state = "leftsmack"
/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
achievement_type = null
crusher_achievement_type = null
score_achievement_type = null
deathmessage = "Explodes into a pool of blood!"
deathsound = 'sound/effects/splat.ogg'
true_spawn = FALSE
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Initialize(mapload)
. = ..()
toggle_ai(AI_OFF)
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/charge(atom/chargeat = target, delay = 3, chargepast = 2)
..()
qdel(src)
/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/CanAllowThrough(atom/movable/mover, border_dir)
. = ..()
if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) // hallucinations should not be stopping bubblegum or eachother
return TRUE
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/Life(delta_time = SSMOBS_DT, times_fired)
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE)
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/OpenFire()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/AttackingTarget()
return
/mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination/try_bloodattack()
return
@@ -1,3 +1,5 @@
#define COLOSSUS_ENRAGED (health < maxHealth/3)
/**
* COLOSSUS
*
@@ -51,80 +53,83 @@
loot = list(/obj/structure/closet/crate/necropolis/colossus)
deathmessage = "disintegrates, leaving a glowing core in its wake."
deathsound = 'sound/magic/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)
small_sprite_type = /datum/action/small_sprite/megafauna/colossus
/// Spiral shots ability
var/datum/action/cooldown/mob_cooldown/projectile_attack/spiral_shots/spiral_shots
/// Random shots ablity
var/datum/action/cooldown/mob_cooldown/projectile_attack/random_aoe/random_shots
/// Shotgun blast ability
var/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/shotgun_blast
/// Directional shots ability
var/datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/alternating/dir_shots
/mob/living/simple_animal/hostile/megafauna/colossus/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT) //we don't want this guy to float, messes up his animations.
spiral_shots = new /datum/action/cooldown/mob_cooldown/projectile_attack/spiral_shots()
random_shots = new /datum/action/cooldown/mob_cooldown/projectile_attack/random_aoe()
shotgun_blast = new /datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast()
dir_shots = new /datum/action/cooldown/mob_cooldown/projectile_attack/dir_shots/alternating()
spiral_shots.Grant(src)
random_shots.Grant(src)
shotgun_blast.Grant(src)
dir_shots.Grant(src)
RegisterSignal(src, COMSIG_ABILITY_STARTED, .proc/start_attack)
RegisterSignal(src, COMSIG_ABILITY_FINISHED, .proc/finished_attack)
/datum/action/innate/megafauna_attack/spiral_attack
name = "Spiral Shots"
icon_icon = 'icons/mob/actions/actions_items.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 = 'modular_skyrat/modules/fixing_missing_icons/ballistic.dmi' //skyrat edit
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 = 'modular_skyrat/modules/fixing_missing_icons/ballistic.dmi' //skyrat edit
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/Destroy()
QDEL_NULL(spiral_shots)
QDEL_NULL(random_shots)
QDEL_NULL(shotgun_blast)
QDEL_NULL(dir_shots)
return ..()
/mob/living/simple_animal/hostile/megafauna/colossus/OpenFire()
anger_modifier = clamp(((maxHealth - health)/50),0,20)
update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = 12 SECONDS))
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_colossus("\"<b>You can't dodge.</b>\""))
update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = 3 SECONDS))
ranged_cooldown = world.time + 30
telegraph()
dir_shots(GLOB.alldirs)
dir_shots.fire_in_directions(src, target, GLOB.alldirs)
move_to_delay = 3
return
else
move_to_delay = initial(move_to_delay)
if(prob(20+anger_modifier)) //Major attack
select_spiral_attack()
spiral_shots.Trigger(target)
else if(prob(20))
random_shots()
random_shots.Trigger(target)
else
if(prob(70))
blast()
shotgun_blast.Trigger(target)
else
alternating_dir_shots()
dir_shots.Trigger(target)
/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/clockwork/narsie_attack.ogg', 200, TRUE)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/start_attack(mob/living/owner, datum/action/cooldown/activated)
SIGNAL_HANDLER
if(activated == spiral_shots)
spiral_shots.enraged = COLOSSUS_ENRAGED
telegraph()
icon_state = "eva_attack"
visible_message(COLOSSUS_ENRAGED ? span_colossus("\"<b>Die.</b>\"") : span_colossus("\"<b>Judgement.</b>\""))
/mob/living/simple_animal/hostile/megafauna/colossus/proc/finished_attack(mob/living/owner, datum/action/cooldown/finished)
SIGNAL_HANDLER
if(finished == spiral_shots)
icon_state = initial(icon_state)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/enrage(mob/living/L)
if(ishuman(L))
@@ -135,95 +140,6 @@
if (is_species(H, /datum/species/golem/sand))
. = TRUE
/mob/living/simple_animal/hostile/megafauna/colossus/proc/alternating_dir_shots()
update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = 4 SECONDS))
dir_shots(GLOB.diagonals)
SLEEP_CHECK_DEATH(10)
dir_shots(GLOB.cardinals)
SLEEP_CHECK_DEATH(10)
dir_shots(GLOB.diagonals)
SLEEP_CHECK_DEATH(10)
dir_shots(GLOB.cardinals)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/select_spiral_attack()
telegraph()
icon_state = "eva_attack"
if(health < maxHealth/3)
return double_spiral()
visible_message(span_colossus("\"<b>Judgement.</b>\""))
return spiral_shoot()
/mob/living/simple_animal/hostile/megafauna/colossus/proc/double_spiral()
visible_message(span_colossus("\"<b>Die.</b>\""))
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(GLOB.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)
icon_state = initial(icon_state)
/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/projectile/P = new /obj/projectile/colossus(startloc)
P.preparePixelProjectile(marker, startloc)
P.firer = src
if(target)
P.original = target
P.fire(set_angle)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/random_shots()
update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = 3 SECONDS))
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)
update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = 2 SECONDS))
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 = GLOB.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/clockwork/narsie_attack.ogg', 200, TRUE)
/mob/living/simple_animal/hostile/megafauna/colossus/devour(mob/living/L)
visible_message(span_colossus("[src] disintegrates [L]!"))
L.dust()
@@ -1,3 +1,4 @@
#define FROST_MINER_SHOULD_ENRAGE (health <= maxHealth*0.25 && !enraged)
GLOBAL_LIST_EMPTY(frost_miner_prisms)
/*
@@ -42,58 +43,40 @@ Difficulty: Extremely Hard
deathmessage = "falls to the ground, decaying into plasma particles."
deathsound = "bodyfall"
footstep_type = FOOTSTEP_MOB_HEAVY
attack_action_types = list(/datum/action/innate/megafauna_attack/frost_orbs,
/datum/action/innate/megafauna_attack/snowball_machine_gun,
/datum/action/innate/megafauna_attack/ice_shotgun)
/// Modifies the speed of the projectiles the demonic frost miner shoots out
var/projectile_speed_multiplier = 1
/// If the demonic frost miner is in its enraged state
var/enraged = FALSE
/// If the demonic frost miner is currently transforming to its enraged state
var/enraging = FALSE
/// Frost orbs ability
var/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/shrapnel/frost_orbs
/// Snowball Machine gun Ability
var/datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/snowball_machine_gun
/// Ice Shotgun Ability
var/datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/pattern/ice_shotgun
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/Initialize(mapload)
. = ..()
frost_orbs = new /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire/shrapnel()
snowball_machine_gun = new /datum/action/cooldown/mob_cooldown/projectile_attack/rapid_fire()
ice_shotgun = new /datum/action/cooldown/mob_cooldown/projectile_attack/shotgun_blast/pattern()
frost_orbs.Grant(src)
snowball_machine_gun.Grant(src)
ice_shotgun.Grant(src)
for(var/obj/structure/frost_miner_prism/prism_to_set in GLOB.frost_miner_prisms)
prism_to_set.set_prism_light(LIGHT_COLOR_BLUE, 5)
RegisterSignal(src, COMSIG_ABILITY_STARTED, .proc/start_attack)
AddElement(/datum/element/knockback, 7, FALSE, TRUE)
AddElement(/datum/element/lifesteal, 50)
ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT)
/datum/action/innate/megafauna_attack/frost_orbs
name = "Fire Frost Orbs"
icon_icon = 'icons/mob/actions/actions_items.dmi'
button_icon_state = "sniper_zoom"
chosen_message = "<span class='colossus'>You are now sending out frost orbs to track in on a target.</span>"
chosen_attack_num = 1
/datum/action/innate/megafauna_attack/snowball_machine_gun
name = "Fire Snowball Machine Gun"
icon_icon = 'icons/obj/guns/energy.dmi'
button_icon_state = "kineticgun"
chosen_message = "<span class='colossus'>You are now firing a snowball machine gun at a target.</span>"
chosen_attack_num = 2
/datum/action/innate/megafauna_attack/ice_shotgun
name = "Fire Ice Shotgun"
icon_icon = 'modular_skyrat/modules/fixing_missing_icons/ballistic.dmi' //skyrat edit
button_icon_state = "shotgun"
chosen_message = "<span class='colossus'>You are now firing shotgun ice blasts.</span>"
chosen_attack_num = 3
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/Destroy()
QDEL_NULL(frost_orbs)
QDEL_NULL(snowball_machine_gun)
QDEL_NULL(ice_shotgun)
return ..()
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/OpenFire()
check_enraged()
projectile_speed_multiplier = 1 - enraged * 0.5
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 10 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 10 SECONDS))
if(client)
switch(chosen_attack)
if(1)
frost_orbs()
if(2)
snowball_machine_gun()
if(3)
ice_shotgun()
return
var/easy_attack = prob(80 - enraged * 40)
@@ -101,20 +84,107 @@ Difficulty: Extremely Hard
switch(chosen_attack)
if(1)
if(easy_attack)
frost_orbs(10, 8)
frost_orbs.shot_count = 8
frost_orbs.shot_delay = 10
frost_orbs.Trigger(target)
else
frost_orbs(5, 16)
frost_orbs.shot_count = 16
frost_orbs.shot_delay = 5
frost_orbs.Trigger(target)
if(2)
if(easy_attack)
snowball_machine_gun()
else
INVOKE_ASYNC(src, .proc/ice_shotgun, 5, list(list(-180, -140, -100, -60, -20, 20, 60, 100, 140), list(-160, -120, -80, -40, 0, 40, 80, 120, 160)))
snowball_machine_gun(5 * 8, 5)
snowball_machine_gun.shot_count = 60
snowball_machine_gun.default_projectile_spread = 45
snowball_machine_gun.Trigger(target)
else if(ice_shotgun.IsAvailable())
ice_shotgun.shot_angles = list(list(-180, -140, -100, -60, -20, 20, 60, 100, 140), list(-160, -120, -80, -40, 0, 40, 80, 120, 160))
INVOKE_ASYNC(ice_shotgun, /datum/action/proc/Trigger, target)
snowball_machine_gun.shot_count = 5 * 8
snowball_machine_gun.default_projectile_spread = 5
snowball_machine_gun.StartCooldown(0)
snowball_machine_gun.Trigger(target)
if(3)
if(easy_attack)
ice_shotgun()
// static lists? remind me later
ice_shotgun.shot_angles = list(list(-40, -20, 0, 20, 40), list(-30, -10, 10, 30))
ice_shotgun.Trigger(target)
else
ice_shotgun(5, list(list(0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330), list(-30, -15, 0, 15, 30)))
ice_shotgun.shot_angles = list(list(0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330), list(-30, -15, 0, 15, 30))
ice_shotgun.Trigger(target)
/// Pre-ability usage stuff
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/proc/start_attack(mob/living/owner, datum/action/cooldown/activated)
SIGNAL_HANDLER
if(enraging)
return COMPONENT_BLOCK_ABILITY_START
if(FROST_MINER_SHOULD_ENRAGE)
INVOKE_ASYNC(src, .proc/check_enraged)
return COMPONENT_BLOCK_ABILITY_START
var/projectile_speed_multiplier = 1 - enraged * 0.5
frost_orbs.projectile_speed_multiplier = projectile_speed_multiplier
snowball_machine_gun.projectile_speed_multiplier = projectile_speed_multiplier
ice_shotgun.projectile_speed_multiplier = projectile_speed_multiplier
/// Checks if the demonic frost miner is ready to be enraged
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/proc/check_enraged()
if(!FROST_MINER_SHOULD_ENRAGE)
return
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 8 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 8 SECONDS))
frost_orbs.StartCooldown(8 SECONDS)
adjustHealth(-maxHealth)
enraged = TRUE
enraging = TRUE
animate(src, pixel_y = pixel_y + 96, time = 100, easing = ELASTIC_EASING)
spin(100, 10)
SLEEP_CHECK_DEATH(60, src)
playsound(src, 'sound/effects/explosion3.ogg', 100, TRUE)
icon_state = "demonic_miner_phase2"
animate(src, pixel_y = pixel_y - 96, time = 8, flags = ANIMATION_END_NOW)
spin(8, 2)
for(var/obj/structure/frost_miner_prism/prism_to_set in GLOB.frost_miner_prisms)
prism_to_set.set_prism_light(LIGHT_COLOR_PURPLE, 5)
SLEEP_CHECK_DEATH(8, src)
for(var/mob/living/L in viewers(src))
shake_camera(L, 3, 2)
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
ADD_TRAIT(src, TRAIT_MOVE_FLYING, FROSTMINER_ENRAGE_TRAIT)
enraging = FALSE
adjustHealth(-maxHealth)
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/ex_act(severity, target)
adjustBruteLoss(-30 * severity)
visible_message(span_danger("[src] absorbs the explosion!"), span_userdanger("You absorb the explosion!"))
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/Goto(target, delay, minimum_distance)
if(enraging)
return
return ..()
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/MoveToTarget(list/possible_targets)
if(enraging)
return
return ..()
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/Move()
if(enraging)
return
return ..()
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/death(gibbed, list/force_grant)
if(health > 0)
return
var/turf/T = get_turf(src)
var/loot = rand(1, 3)
for(var/obj/structure/frost_miner_prism/prism_to_set in GLOB.frost_miner_prisms)
prism_to_set.set_prism_light(COLOR_GRAY, 1)
switch(loot)
if(1)
new /obj/item/resurrection_crystal(T)
if(2)
new /obj/item/clothing/shoes/winterboots/ice_boots/ice_trail(T)
if(3)
new /obj/item/pickaxe/drill/jackhammer/demonic(T)
return ..()
/obj/projectile/colossus/frost_orb
name = "frost orb"
@@ -152,139 +222,6 @@ Difficulty: Extremely Hard
if(isturf(target) || isobj(target))
target.ex_act(EXPLODE_HEAVY)
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/ex_act(severity, target)
adjustBruteLoss(-30 * severity)
visible_message(span_danger("[src] absorbs the explosion!"), span_userdanger("You absorb the explosion!"))
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/Goto(target, delay, minimum_distance)
if(enraging)
return
return ..()
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/MoveToTarget(list/possible_targets)
if(enraging)
return
return ..()
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/Move()
if(enraging)
return
return ..()
/// Shoots out homing frost orbs that explode into ice blast projectiles after a couple seconds
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/proc/frost_orbs(added_delay = 10, shoot_times = 8)
for(var/i in 1 to shoot_times)
var/turf/startloc = get_turf(src)
var/turf/endloc = get_turf(target)
if(!endloc)
break
var/obj/projectile/colossus/frost_orb/P = new(startloc)
P.preparePixelProjectile(endloc, startloc)
P.firer = src
if(target)
P.original = target
P.set_homing_target(target)
P.fire(rand(0, 360))
addtimer(CALLBACK(P, /obj/projectile/colossus/frost_orb/proc/orb_explosion, projectile_speed_multiplier), 20) // make the orbs home in after a second
SLEEP_CHECK_DEATH(added_delay)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 4 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 6 SECONDS))
/// Called when the orb is exploding, shoots out projectiles
/obj/projectile/colossus/frost_orb/proc/orb_explosion(projectile_speed_multiplier)
for(var/i in 0 to 5)
var/angle = i * 60
var/turf/startloc = get_turf(src)
var/turf/endloc = get_turf(original)
if(!startloc || !endloc)
break
var/obj/projectile/colossus/ice_blast/P = new(startloc)
P.speed *= projectile_speed_multiplier
P.preparePixelProjectile(endloc, startloc, null, angle + rand(-10, 10))
P.firer = firer
if(original)
P.original = original
P.fire()
qdel(src)
/// Shoots out snowballs with a random spread
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/proc/snowball_machine_gun(shots = 60, spread = 45)
for(var/i in 1 to shots)
var/turf/startloc = get_turf(src)
var/turf/endloc = get_turf(target)
if(!endloc)
break
var/obj/projectile/P = new /obj/projectile/colossus/snowball(startloc)
P.speed *= projectile_speed_multiplier
P.preparePixelProjectile(endloc, startloc, null, rand(-spread, spread))
P.firer = src
if(target)
P.original = target
P.fire()
SLEEP_CHECK_DEATH(1)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 1.5 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 1.5 SECONDS))
/// Shoots out ice blasts in a shotgun like pattern
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/proc/ice_shotgun(shots = 5, list/patterns = list(list(-40, -20, 0, 20, 40), list(-30, -10, 10, 30)))
for(var/i in 1 to shots)
var/list/pattern = patterns[i % length(patterns) + 1] // alternating patterns
for(var/spread in pattern)
var/turf/startloc = get_turf(src)
var/turf/endloc = get_turf(target)
if(!endloc)
break
var/obj/projectile/P = new /obj/projectile/colossus/ice_blast(startloc)
P.speed *= projectile_speed_multiplier
P.preparePixelProjectile(endloc, startloc, null, spread)
P.firer = src
if(target)
P.original = target
P.fire()
SLEEP_CHECK_DEATH(8)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 1.5 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 2 SECONDS))
/// Checks if the demonic frost miner is ready to be enraged
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/proc/check_enraged()
if(enraged)
return
if(health > maxHealth*0.25)
return
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 8 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 8 SECONDS))
adjustHealth(-maxHealth)
enraged = TRUE
enraging = TRUE
animate(src, pixel_y = pixel_y + 96, time = 100, easing = ELASTIC_EASING)
spin(100, 10)
SLEEP_CHECK_DEATH(60)
playsound(src, 'sound/effects/explosion3.ogg', 100, TRUE)
icon_state = "demonic_miner_phase2"
animate(src, pixel_y = pixel_y - 96, time = 8, flags = ANIMATION_END_NOW)
spin(8, 2)
for(var/obj/structure/frost_miner_prism/prism_to_set in GLOB.frost_miner_prisms)
prism_to_set.set_prism_light(LIGHT_COLOR_PURPLE, 5)
SLEEP_CHECK_DEATH(8)
for(var/mob/living/L in viewers(src))
shake_camera(L, 3, 2)
playsound(src, 'sound/effects/meteorimpact.ogg', 100, TRUE)
ADD_TRAIT(src, TRAIT_MOVE_FLYING, FROSTMINER_ENRAGE_TRAIT)
enraging = FALSE
adjustHealth(-maxHealth)
/mob/living/simple_animal/hostile/megafauna/demonic_frost_miner/death(gibbed, list/force_grant)
if(health > 0)
return
var/turf/T = get_turf(src)
var/loot = rand(1, 3)
for(var/obj/structure/frost_miner_prism/prism_to_set in GLOB.frost_miner_prisms)
prism_to_set.set_prism_light(COLOR_GRAY, 1)
switch(loot)
if(1)
new /obj/item/resurrection_crystal(T)
if(2)
new /obj/item/clothing/shoes/winterboots/ice_boots/ice_trail(T)
if(3)
new /obj/item/pickaxe/drill/jackhammer/demonic(T)
return ..()
/obj/item/resurrection_crystal
name = "resurrection crystal"
desc = "When used by anything holding it, this crystal gives them a second chance at life if they die."
@@ -1,13 +1,12 @@
#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
///used whenever the drake generates a hotspot
#define DRAKE_FIRE_TEMP 500
///used whenever the drake generates a hotspot
#define DRAKE_FIRE_EXPOSURE 50
///used to see if the drake is enraged or not
#define DRAKE_ENRAGED (health < maxHealth*0.5)
#define SWOOP_DAMAGEABLE 1
#define SWOOP_INVULNERABLE 2
/*£
*
@@ -72,211 +71,100 @@
deathmessage = "collapses into a pile of bones, its flesh sloughing away."
deathsound = 'sound/magic/demon_dies.ogg'
footstep_type = FOOTSTEP_MOB_HEAVY
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)
small_sprite_type = /datum/action/small_sprite/megafauna/drake
/// Fire cone ability
var/datum/action/cooldown/mob_cooldown/fire_breath/cone/fire_cone
/// Meteors ability
var/datum/action/cooldown/mob_cooldown/meteors/meteors
/// Mass fire ability
var/datum/action/cooldown/mob_cooldown/fire_breath/mass_fire/mass_fire
/// Lava swoop ability
var/datum/action/cooldown/mob_cooldown/lava_swoop/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
/mob/living/simple_animal/hostile/megafauna/dragon/Initialize()
. = ..()
fire_cone = new /datum/action/cooldown/mob_cooldown/fire_breath/cone()
meteors = new /datum/action/cooldown/mob_cooldown/meteors()
mass_fire = new /datum/action/cooldown/mob_cooldown/fire_breath/mass_fire()
lava_swoop = new /datum/action/cooldown/mob_cooldown/lava_swoop()
fire_cone.Grant(src)
meteors.Grant(src)
mass_fire.Grant(src)
lava_swoop.Grant(src)
RegisterSignal(src, COMSIG_ABILITY_STARTED, .proc/start_attack)
RegisterSignal(src, COMSIG_ABILITY_FINISHED, .proc/finished_attack)
RegisterSignal(src, COMSIG_SWOOP_INVULNERABILITY_STARTED, .proc/swoop_invulnerability_started)
RegisterSignal(src, COMSIG_LAVA_ARENA_FAILED, .proc/on_arena_fail)
/datum/action/innate/megafauna_attack/fire_cone_meteors
name = "Fire Cone With Meteors"
icon_icon = 'icons/mob/actions/actions_items.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
/mob/living/simple_animal/hostile/megafauna/dragon/Destroy()
QDEL_NULL(fire_cone)
QDEL_NULL(meteors)
QDEL_NULL(mass_fire)
QDEL_NULL(lava_swoop)
return ..()
/mob/living/simple_animal/hostile/megafauna/dragon/OpenFire()
if(swooping)
return
anger_modifier = clamp(((maxHealth - health)/50),0,20)
update_cooldowns(list(COOLDOWN_UPDATE_SET_RANGED = ranged_cooldown_time), ignore_staggered = TRUE)
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)
if(DRAKE_ENRAGED)
// Lava Arena
lava_swoop.Trigger(target)
return
// Lava Pools
if(lava_swoop.Trigger(target))
SLEEP_CHECK_DEATH(0, src)
fire_cone.StartCooldown(0)
fire_cone.Trigger(target)
meteors.StartCooldown(0)
INVOKE_ASYNC(meteors, /datum/action/proc/Trigger, target)
return
else if(prob(10+anger_modifier) && DRAKE_ENRAGED)
mass_fire.Trigger(target)
return
target.visible_message(span_boldwarning("Fire rains from the sky!"))
var/turf/targetturf = get_turf(target)
for(var/turf/turf as anything in RANGE_TURFS(9,targetturf))
if(prob(11))
new /obj/effect/temp_visual/target(turf)
if(fire_cone.Trigger(target))
if(prob(50))
meteors.StartCooldown(0)
meteors.Trigger(target)
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_pools(amount, delay = 0.8)
if(!target)
return
target.visible_message(span_boldwarning("Lava starts to pool up around you!"))
/mob/living/simple_animal/hostile/megafauna/dragon/proc/start_attack(mob/living/owner, datum/action/cooldown/activated)
SIGNAL_HANDLER
if(activated == lava_swoop)
icon_state = "shadow"
swooping = SWOOP_DAMAGEABLE
while(amount > 0)
if(QDELETED(target))
break
var/turf/TT = get_turf(target)
var/turf/T = pick(RANGE_TURFS(1,TT))
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/swoop_invulnerability_started()
SIGNAL_HANDLER
swooping = SWOOP_INVULNERABLE
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_swoop(amount = 30)
if(health < maxHealth * 0.5)
return swoop_attack(lava_arena = TRUE, swoop_cooldown = 6 SECONDS)
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()
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 4 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 4 SECONDS))
/mob/living/simple_animal/hostile/megafauna/dragon/proc/finished_attack(mob/living/owner, datum/action/cooldown/finished)
SIGNAL_HANDLER
if(finished == lava_swoop)
icon_state = initial(icon_state)
swooping = NONE
/mob/living/simple_animal/hostile/megafauna/dragon/proc/mass_fire(spiral_count = 12, range = 15, times = 3)
SLEEP_CHECK_DEATH(0)
for(var/i = 1 to times)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 5 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 5 SECONDS))
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)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 3 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 3 SECONDS))
/mob/living/simple_animal/hostile/megafauna/dragon/proc/lava_arena()
if(!target)
return
target.visible_message(span_boldwarning("[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/open/indestructible))
continue
if(!istype(T, /turf/closed/indestructible))
T.ChangeTurf(/turf/open/floor/plating/asteroid/basalt/lava_land_surface, flags = CHANGETURF_INHERIT_AIR)
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)
var/list/mobs_with_clients = list()
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 = FALSE
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 = TRUE
mobs_with_clients |= L
for(var/obj/vehicle/sealed/mecha/M in T.contents)
empty += pick(((RANGE_TURFS(2, M) - RANGE_TURFS(1, M)) & turfs) - empty)
any_attack = TRUE
if(!any_attack) // nothing to attack in the arena, time for enraged attack if we still have a cliented target.
for(var/obj/effect/temp_visual/drakewall/D in drakewalls)
qdel(D)
for(var/a in mobs_with_clients)
var/mob/living/L = a
if(!QDELETED(L) && L.client)
return FALSE
return TRUE
for(var/turf/T in turfs)
if(!(T in empty))
new /obj/effect/temp_visual/lava_warning(T)
else if(!istype(T, /turf/closed/indestructible))
new /obj/effect/temp_visual/lava_safe(T)
amount--
SLEEP_CHECK_DEATH(24)
return TRUE // attack finished completely
/mob/living/simple_animal/hostile/megafauna/dragon/proc/on_arena_fail()
SIGNAL_HANDLER
INVOKE_ASYNC(src, .proc/arena_escape_enrage)
/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)
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 8 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 8 SECONDS))
SLEEP_CHECK_DEATH(0, src)
visible_message(span_boldwarning("[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
set_light_range(10)
SLEEP_CHECK_DEATH(10) // run.
mass_fire(20, 15, 3)
SLEEP_CHECK_DEATH(5 SECONDS, src) // run.
mass_fire.Activate(target)
mass_fire.StartCooldown(8 SECONDS)
move_to_delay = initial(move_to_delay)
remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
set_light_range(initial(light_range))
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_cone(atom/at = target, 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(offset, range, atom/at = target)
if(!at)
return
var/turf/T = get_ranged_target_turf_direct(src, at, range, offset)
return (get_line(src, T) - get_turf(src))
/mob/living/simple_animal/hostile/megafauna/dragon/proc/fire_line(list/turfs)
SLEEP_CHECK_DEATH(0)
dragon_fire_line(src, turfs)
//fire line keeps going even if dragon is deleted
/proc/dragon_fire_line(atom/source, list/turfs, frozen = FALSE)
var/list/hit_list = list()
@@ -307,105 +195,14 @@
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, swoop_cooldown = 30)
if(stat || swooping)
return
if(manual_target)
GiveTarget(manual_target)
if(!target)
return
stop_automated_movement = TRUE
swooping |= SWOOP_DAMAGEABLE
set_density(FALSE)
icon_state = "shadow"
visible_message(span_boldwarning("[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(span_warning("[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(GLOB.alldirs)
var/throwtarget = get_edge_target_turf(src, throw_dir)
L.throw_at(throwtarget, 3)
visible_message(span_warning("[L] is thrown clear of [src]!"))
for(var/obj/vehicle/sealed/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)
set_density(TRUE)
SLEEP_CHECK_DEATH(1)
swooping &= ~SWOOP_DAMAGEABLE
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = swoop_cooldown, COOLDOWN_UPDATE_SET_RANGED = swoop_cooldown))
if(!lava_success)
arena_escape_enrage()
/mob/living/simple_animal/hostile/megafauna/dragon/ex_act(severity, target)
if(severity <= EXPLODE_LIGHT)
return FALSE
return ..()
/mob/living/simple_animal/hostile/megafauna/dragon/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
anger_modifier = clamp(((maxHealth - health)/60),0,20)
lava_swoop.enraged = DRAKE_ENRAGED
if(!forced && (swooping & SWOOP_INVULNERABLE))
return FALSE
return ..()
@@ -436,6 +233,7 @@
layer = BELOW_MOB_LAYER
light_range = 2
duration = 13
var/mob/owner
/obj/effect/temp_visual/lava_warning/Initialize(mapload, reset_time = 10)
. = ..()
@@ -449,7 +247,7 @@
sleep(duration)
playsound(T,'sound/magic/fireball.ogg', 200, TRUE)
for(var/mob/living/L in T.contents)
for(var/mob/living/L in T.contents - owner)
if(istype(L, /mob/living/simple_animal/hostile/megafauna/dragon))
continue
L.adjustFireLoss(10)
@@ -485,53 +283,6 @@
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 = 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"
@@ -593,15 +344,19 @@
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_warning("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 + 20 SECONDS // needs separate cooldown or cant use fire attacks
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/Initialize()
. = ..()
fire_cone.Remove(src)
meteors.Remove(src)
mass_fire.Remove(src)
lava_swoop.cooldown_time = 20 SECONDS
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/adjustHealth(amount, updating_health = TRUE, forced = FALSE)
. = ..()
lava_swoop.enraged = FALSE
/mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype)
return
#undef SWOOP_DAMAGEABLE
#undef SWOOP_INVULNERABLE
@@ -220,7 +220,7 @@ Difficulty: Hard
visible_message(span_hierophant("\"Mx ampp rsx iwgeti.\""))
var/oldcolor = color
animate(src, color = "#660099", time = 6)
SLEEP_CHECK_DEATH(6)
SLEEP_CHECK_DEATH(6, src)
while(!QDELETED(target) && blink_counter)
if(loc == target.loc || loc == target) //we're on the same tile as them after about a second we can stop now
break
@@ -228,10 +228,10 @@ Difficulty: Hard
blinking = FALSE
blink(target)
blinking = TRUE
SLEEP_CHECK_DEATH(4 + target_slowness)
SLEEP_CHECK_DEATH(4 + target_slowness, src)
animate(src, color = oldcolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
SLEEP_CHECK_DEATH(8)
SLEEP_CHECK_DEATH(8, src)
blinking = FALSE
else
blink(target)
@@ -242,17 +242,17 @@ Difficulty: Hard
blinking = TRUE
var/oldcolor = color
animate(src, color = "#660099", time = 6)
SLEEP_CHECK_DEATH(6)
SLEEP_CHECK_DEATH(6, src)
while(!QDELETED(target) && cross_counter)
cross_counter--
if(prob(60))
INVOKE_ASYNC(src, .proc/blasts, target, GLOB.cardinals)
else
INVOKE_ASYNC(src, .proc/blasts, target, GLOB.diagonals)
SLEEP_CHECK_DEATH(6 + target_slowness)
SLEEP_CHECK_DEATH(6 + target_slowness, src)
animate(src, color = oldcolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
SLEEP_CHECK_DEATH(8)
SLEEP_CHECK_DEATH(8, src)
blinking = FALSE
@@ -262,7 +262,7 @@ Difficulty: Hard
blinking = TRUE
var/oldcolor = color
animate(src, color = "#660099", time = 6)
SLEEP_CHECK_DEATH(6)
SLEEP_CHECK_DEATH(6, src)
var/list/targets = ListTargets()
var/list/cardinal_copy = GLOB.cardinals.Copy()
while(targets.len && cardinal_copy.len)
@@ -276,11 +276,11 @@ Difficulty: Hard
var/obj/effect/temp_visual/hierophant/chaser/C = new(loc, src, pickedtarget, chaser_speed, FALSE)
C.moving = 3
C.moving_dir = pick_n_take(cardinal_copy)
SLEEP_CHECK_DEATH(8 + target_slowness)
SLEEP_CHECK_DEATH(8 + target_slowness, src)
update_cooldowns(list(COOLDOWN_UPDATE_SET_CHASER = chaser_cooldown_time))
animate(src, color = oldcolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
SLEEP_CHECK_DEATH(8)
SLEEP_CHECK_DEATH(8, src)
blinking = FALSE
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blasts(mob/victim, list/directions = GLOB.cardinals) //fires cross blasts with a delay
@@ -294,7 +294,7 @@ Difficulty: Hard
else
new /obj/effect/temp_visual/hierophant/telegraph(T, src)
playsound(T,'sound/effects/bin_close.ogg', 200, TRUE)
SLEEP_CHECK_DEATH(2)
SLEEP_CHECK_DEATH(2, src)
new /obj/effect/temp_visual/hierophant/blast/damaging(T, src, FALSE)
for(var/d in directions)
INVOKE_ASYNC(src, .proc/blast_wall, T, d)
@@ -332,7 +332,7 @@ Difficulty: Hard
HS.setDir(set_dir)
previousturf = J
J = get_step(previousturf, set_dir)
SLEEP_CHECK_DEATH(0.5)
SLEEP_CHECK_DEATH(0.5, src)
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/blink(mob/victim) //blink to a target
if(blinking || !victim)
@@ -344,7 +344,7 @@ Difficulty: Hard
playsound(T,'sound/magic/wand_teleport.ogg', 200, TRUE)
playsound(source,'sound/machines/airlockopen.ogg', 200, TRUE)
blinking = TRUE
SLEEP_CHECK_DEATH(2) //short delay before we start...
SLEEP_CHECK_DEATH(2, src) //short delay before we start...
new /obj/effect/temp_visual/hierophant/telegraph/teleport(T, src)
new /obj/effect/temp_visual/hierophant/telegraph/teleport(source, src)
for(var/t in RANGE_TURFS(1, T))
@@ -354,17 +354,17 @@ Difficulty: Hard
var/obj/effect/temp_visual/hierophant/blast/damaging/B = new(t, src, FALSE)
B.damage = 30
animate(src, alpha = 0, time = 2, easing = EASE_OUT) //fade out
SLEEP_CHECK_DEATH(1)
SLEEP_CHECK_DEATH(1, src)
visible_message(span_hierophant_warning("[src] fades out!"))
set_density(FALSE)
SLEEP_CHECK_DEATH(2)
SLEEP_CHECK_DEATH(2, src)
forceMove(T)
SLEEP_CHECK_DEATH(1)
SLEEP_CHECK_DEATH(1, src)
animate(src, alpha = 255, time = 2, easing = EASE_IN) //fade IN
SLEEP_CHECK_DEATH(1)
SLEEP_CHECK_DEATH(1, src)
set_density(TRUE)
visible_message(span_hierophant_warning("[src] fades in!"))
SLEEP_CHECK_DEATH(1) //at this point the blasts we made detonate
SLEEP_CHECK_DEATH(1, src) //at this point the blasts we made detonate
blinking = FALSE
/mob/living/simple_animal/hostile/megafauna/hierophant/proc/melee_blast(mob/victim) //make a 3x3 blast around a target
@@ -375,7 +375,7 @@ Difficulty: Hard
return
new /obj/effect/temp_visual/hierophant/telegraph(T, src)
playsound(T,'sound/effects/bin_close.ogg', 200, TRUE)
SLEEP_CHECK_DEATH(2)
SLEEP_CHECK_DEATH(2, src)
for(var/t in RANGE_TURFS(1, T))
new /obj/effect/temp_visual/hierophant/blast/damaging(t, src, FALSE)
@@ -206,17 +206,17 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/wendigo/proc/shockwave_scream()
can_move = FALSE
COOLDOWN_START(src, scream_cooldown, scream_cooldown_time)
SLEEP_CHECK_DEATH(5)
SLEEP_CHECK_DEATH(5, src)
playsound(loc, 'sound/magic/demon_dies.ogg', 600, FALSE, 10)
animate(src, pixel_z = rand(5, 15), time = 1, loop = 20)
animate(pixel_z = 0, time = 1)
for(var/mob/living/dizzy_target in get_hearers_in_view(7, src) - src)
dizzy_target.Dizzy(6)
to_chat(dizzy_target, span_danger("The wendigo screams loudly!"))
SLEEP_CHECK_DEATH(1 SECONDS)
SLEEP_CHECK_DEATH(1 SECONDS, src)
spiral_attack()
update_cooldowns(list(COOLDOWN_UPDATE_SET_MELEE = 3 SECONDS, COOLDOWN_UPDATE_SET_RANGED = 3 SECONDS))
SLEEP_CHECK_DEATH(3 SECONDS)
SLEEP_CHECK_DEATH(3 SECONDS, src)
can_move = TRUE
/// Shoots shockwave projectiles in a random preset pattern
@@ -236,7 +236,7 @@ Difficulty: Hard
shockwave.firer = src
shockwave.speed = 3 - WENDIGO_ENRAGED
shockwave.fire(angle)
SLEEP_CHECK_DEATH(6 - WENDIGO_ENRAGED * 2)
SLEEP_CHECK_DEATH(6 - WENDIGO_ENRAGED * 2, src)
if("Spiral")
var/shots_spiral = WENDIGO_SPIRAL_SHOTCOUNT
var/angle_to_target = get_angle(src, target)
@@ -250,7 +250,7 @@ Difficulty: Hard
shockwave.firer = src
shockwave.damage = 15
shockwave.fire(angle)
SLEEP_CHECK_DEATH(1)
SLEEP_CHECK_DEATH(1, src)
if("Wave")
var/shots_per = WENDIGO_WAVE_SHOTCOUNT
var/difference = 360 / shots_per
@@ -264,7 +264,7 @@ Difficulty: Hard
shockwave.speed = 8
shockwave.wave_speed = 10 * wave_direction
shockwave.fire(angle)
SLEEP_CHECK_DEATH(2)
SLEEP_CHECK_DEATH(2, src)
/mob/living/simple_animal/hostile/megafauna/wendigo/death(gibbed, list/force_grant)
if(health > 0)
@@ -69,7 +69,7 @@
return ..()
var/turf/end = pick(possible_ends)
do_teleport(src, end, 0, channel=TELEPORT_CHANNEL_BLUESPACE, forced = TRUE)
SLEEP_CHECK_DEATH(8)
SLEEP_CHECK_DEATH(8, src)
return ..()
/mob/living/simple_animal/hostile/asteroid/ice_demon/Life(delta_time = SSMOBS_DT, times_fired)
@@ -28,18 +28,26 @@
weather_immunities = list(TRAIT_SNOWSTORM_IMMUNE)
vision_range = 5
aggro_vision_range = 7
charger = TRUE
charge_distance = 4
butcher_results = list(/obj/item/food/meat/crab = 2, /obj/item/stack/sheet/bone = 2)
robust_searching = TRUE
footstep_type = FOOTSTEP_MOB_CLAW
gold_core_spawnable = HOSTILE_SPAWN
/// Charging ability
var/datum/action/cooldown/mob_cooldown/charge/basic_charge/charge
/mob/living/simple_animal/hostile/asteroid/lobstrosity/ranged_secondary_attack(atom/target, modifiers)
if(COOLDOWN_FINISHED(src, charge_cooldown))
INVOKE_ASYNC(src, /mob/living/simple_animal/hostile/.proc/enter_charge, target)
else
to_chat(src, span_notice("Your charge is still on cooldown!"))
/mob/living/simple_animal/hostile/asteroid/lobstrosity/Initialize(mapload)
. = ..()
charge = new /datum/action/cooldown/mob_cooldown/charge/basic_charge()
charge.Grant(src)
/mob/living/simple_animal/hostile/asteroid/lobstrosity/Destroy()
QDEL_NULL(charge)
return ..()
/mob/living/simple_animal/hostile/asteroid/lobstrosity/OpenFire()
if(client)
return
charge.Trigger(target)
/mob/living/simple_animal/hostile/asteroid/lobstrosity/lava
name = "tropical lobstrosity"
@@ -109,7 +109,6 @@
minbodytemp = 0
maxbodytemp = 1500
charger = TRUE
loot = list(/obj/item/stack/sheet/bluespace_crystal)
alert_sounds = list(
'modular_skyrat/master_files/sound/blackmesa/houndeye/he_alert1.ogg',
@@ -118,15 +117,23 @@
'modular_skyrat/master_files/sound/blackmesa/houndeye/he_alert4.ogg',
'modular_skyrat/master_files/sound/blackmesa/houndeye/he_alert5.ogg'
)
/// Charging ability
var/datum/action/cooldown/mob_cooldown/charge/basic_charge/charge
/mob/living/simple_animal/hostile/blackmesa/xen/houndeye/enter_charge(atom/target)
playsound(src, pick(list(
'modular_skyrat/master_files/sound/blackmesa/houndeye/charge3.ogg',
'modular_skyrat/master_files/sound/blackmesa/houndeye/charge3.ogg',
'modular_skyrat/master_files/sound/blackmesa/houndeye/charge3.ogg'
)), 100)
/mob/living/simple_animal/hostile/blackmesa/xen/houndeye/Initialize(mapload)
. = ..()
charge = new /datum/action/cooldown/mob_cooldown/charge/basic_charge()
charge.Grant(src)
/mob/living/simple_animal/hostile/blackmesa/xen/houndeye/Destroy()
QDEL_NULL(charge)
return ..()
/mob/living/simple_animal/hostile/blackmesa/xen/houndeye/OpenFire()
if(client)
return
charge.Trigger(target)
/mob/living/simple_animal/hostile/blackmesa/xen/headcrab
name = "headcrab"
desc = "Don't let it latch onto your hea-... hey, that's kinda cool."
@@ -149,23 +156,29 @@
melee_damage_upper = 17
attack_sound = 'sound/weapons/bite.ogg'
gold_core_spawnable = HOSTILE_SPAWN
charger = TRUE
charge_frequency = 3 SECONDS
loot = list(/obj/item/stack/sheet/bone)
alert_sounds = list(
'modular_skyrat/master_files/sound/blackmesa/headcrab/alert1.ogg'
)
var/is_zombie = FALSE
var/mob/living/carbon/human/oldguy
/// Charging ability
var/datum/action/cooldown/mob_cooldown/charge/basic_charge/charge
/mob/living/simple_animal/hostile/blackmesa/xen/headcrab/handle_charge_target(atom/target)
playsound(src, pick(list(
'modular_skyrat/master_files/sound/blackmesa/headcrab/attack1.ogg',
'modular_skyrat/master_files/sound/blackmesa/headcrab/attack2.ogg',
'modular_skyrat/master_files/sound/blackmesa/headcrab/attack3.ogg'
)), 100)
/mob/living/simple_animal/hostile/blackmesa/xen/headcrab/Initialize(mapload)
. = ..()
charge = new /datum/action/cooldown/mob_cooldown/charge/basic_charge()
charge.Grant(src)
/mob/living/simple_animal/hostile/blackmesa/xen/headcrab/Destroy()
QDEL_NULL(charge)
return ..()
/mob/living/simple_animal/hostile/blackmesa/xen/headcrab/OpenFire()
if(client)
return
charge.Trigger(target)
/mob/living/simple_animal/hostile/blackmesa/xen/headcrab/death(gibbed)
. = ..()
playsound(src, pick(list(
@@ -208,7 +221,6 @@
oldguy = zombified_human
update_appearance()
visible_message(span_warning("The corpse of [zombified_human.name] suddenly rises!"))
charger = FALSE
return TRUE
/mob/living/simple_animal/hostile/blackmesa/xen/headcrab/death(gibbed)
@@ -255,7 +255,7 @@
visible_message(span_userdanger("[src] lifts his ancient blade, and prepares to spin!"))
spinning = TRUE
animate(src, color = "#ff6666", 10)
SLEEP_CHECK_DEATH(5)
SLEEP_CHECK_DEATH(5, src)
var/list/spinningturfs = list()
var/current_angle = 360
while(current_angle > 0)
@@ -291,7 +291,7 @@
face_atom(target)
visible_message(span_userdanger("[src] lifts his arm, and prepares to charge!"))
animate(src, color = "#ff6666", 3)
SLEEP_CHECK_DEATH(4)
SLEEP_CHECK_DEATH(4, src)
face_atom(target)
minimum_distance = 0
charging = TRUE
@@ -310,7 +310,7 @@
/mob/living/simple_animal/hostile/megafauna/gladiator/proc/teleport(atom/target)
var/turf/targeted = get_step(target, target.dir)
new /obj/effect/temp_visual/small_smoke/halfsecond(get_turf(src))
SLEEP_CHECK_DEATH(4)
SLEEP_CHECK_DEATH(4, src)
if(istype(targeted) && !ischasm(targeted) && !istype(targeted, /turf/open/openspace))
new /obj/effect/temp_visual/small_smoke/halfsecond(targeted)
forceMove(targeted)
+10
View File
@@ -230,6 +230,7 @@
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movable.dm"
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_movement.dm"
#include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_x_act.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_abilities.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_carbon.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_living.dm"
#include "code\__DEFINES\dcs\signals\signals_mob\signals_mob_main.dm"
@@ -604,6 +605,15 @@
#include "code\datums\achievements\misc_scores.dm"
#include "code\datums\achievements\skill_achievements.dm"
#include "code\datums\actions\beam_rifle.dm"
#include "code\datums\actions\mobs\blood_warp.dm"
#include "code\datums\actions\mobs\charge.dm"
#include "code\datums\actions\mobs\dash.dm"
#include "code\datums\actions\mobs\fire_breath.dm"
#include "code\datums\actions\mobs\lava_swoop.dm"
#include "code\datums\actions\mobs\meteors.dm"
#include "code\datums\actions\mobs\mobcooldown.dm"
#include "code\datums\actions\mobs\projectileattack.dm"
#include "code\datums\actions\mobs\transform_weapon.dm"
#include "code\datums\ai\_ai_behavior.dm"
#include "code\datums\ai\_ai_controller.dm"
#include "code\datums\ai\_ai_planning_subtree.dm"