mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-10 23:54:14 +01:00
ad9934e7b5
## About The Pull Request The title ## Why It's Good For The Game I really dont like the fact that spider or simple lobster just with one dash can turn big scary mech to big scary wreckage. ## Changelog 🆑 fix: Mechs now take a fixed amount of damage if the simplemob "charge" ability was used on them. /🆑
326 lines
13 KiB
Plaintext
326 lines
13 KiB
Plaintext
/datum/action/cooldown/mob_cooldown/charge
|
|
name = "Charge"
|
|
button_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
|
|
/// If the current move is being triggered by us or not
|
|
var/actively_moving = FALSE
|
|
/// List of charging mobs
|
|
var/list/charging = list()
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/Activate(atom/target_atom)
|
|
disable_cooldown_actions()
|
|
// No charging and meleeing (overridded by StartCooldown after charge ends)
|
|
next_melee_use_time = world.time + 100 SECONDS
|
|
charge_sequence(owner, target_atom, charge_delay, charge_past)
|
|
StartCooldown()
|
|
enable_cooldown_actions()
|
|
return TRUE
|
|
|
|
/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
|
|
var/dir = get_dir(charger, target_atom)
|
|
var/turf/target = get_ranged_target_turf(chargeturf, dir, past)
|
|
if(!target)
|
|
return
|
|
|
|
if(charger in charging)
|
|
// Stop any existing charging, this'll clean things up properly
|
|
GLOB.move_manager.stop_looping(charger)
|
|
|
|
charging += charger
|
|
actively_moving = FALSE
|
|
SEND_SIGNAL(owner, COMSIG_STARTED_CHARGE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_BUMP, PROC_REF(on_bump), override = TRUE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_move), override = TRUE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved), override = TRUE)
|
|
RegisterSignal(charger, COMSIG_LIVING_DEATH, PROC_REF(charge_end), override = TRUE)
|
|
charger.setDir(dir)
|
|
do_charge_indicator(charger, target)
|
|
|
|
SLEEP_CHECK_DEATH(delay, charger)
|
|
|
|
var/time_to_hit = min(get_dist(charger, target), charge_distance) * charge_speed
|
|
|
|
var/datum/move_loop/new_loop = GLOB.move_manager.home_onto(charger, target, delay = charge_speed, timeout = time_to_hit, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
|
|
if(!new_loop)
|
|
return
|
|
RegisterSignal(new_loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(pre_move), override = TRUE)
|
|
RegisterSignal(new_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move), override = TRUE)
|
|
RegisterSignal(new_loop, COMSIG_QDELETING, PROC_REF(charge_end), override = TRUE)
|
|
|
|
// Yes this is disgusting. But we need to queue this stuff, and this code just isn't setup to support that right now. So gotta do it with sleeps
|
|
sleep(time_to_hit + charge_speed)
|
|
charger.setDir(dir)
|
|
|
|
return TRUE
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/pre_move(datum)
|
|
SIGNAL_HANDLER
|
|
// If you sleep in Move() you deserve what's coming to you
|
|
actively_moving = TRUE
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/post_move(datum)
|
|
SIGNAL_HANDLER
|
|
actively_moving = FALSE
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/charge_end(datum/source)
|
|
SIGNAL_HANDLER
|
|
var/atom/movable/charger = source
|
|
if(istype(source, /datum/move_loop))
|
|
var/datum/move_loop/move_loop_source = source
|
|
charger = move_loop_source.moving
|
|
UnregisterSignal(charger, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_MOVED, COMSIG_LIVING_DEATH))
|
|
SEND_SIGNAL(owner, COMSIG_FINISHED_CHARGE)
|
|
actively_moving = FALSE
|
|
charging -= charger
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/update_status_on_signal(mob/source, new_stat, old_stat)
|
|
. = ..()
|
|
if(new_stat == DEAD)
|
|
GLOB.move_manager.stop_looping(source) //This will cause the loop to qdel, triggering an end to our charging
|
|
|
|
/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 = COLOR_RED, transform = matrix()*2, time = 3)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/on_move(atom/source, atom/new_loc)
|
|
SIGNAL_HANDLER
|
|
if(!actively_moving)
|
|
return COMPONENT_MOVABLE_BLOCK_PRE_MOVE
|
|
new /obj/effect/temp_visual/decoy/fading(source.loc, source)
|
|
INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/on_moved(atom/source)
|
|
SIGNAL_HANDLER
|
|
playsound(source, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE)
|
|
INVOKE_ASYNC(src, PROC_REF(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/next_turf = get_step(charger, dir)
|
|
if(!next_turf)
|
|
continue
|
|
if(next_turf.Adjacent(charger) && (iswallturf(next_turf) || ismineralturf(next_turf)))
|
|
if(!isanimal(charger))
|
|
SSexplosions.medturf += next_turf
|
|
continue
|
|
next_turf.attack_animal(charger)
|
|
continue
|
|
for(var/obj/object in next_turf.contents)
|
|
if(!object.Adjacent(charger))
|
|
continue
|
|
if(!ismachinery(object) && !isstructure(object))
|
|
continue
|
|
if(!object.density || object.IsObscured())
|
|
continue
|
|
if(!isanimal(charger))
|
|
SSexplosions.med_mov_atom += target
|
|
break
|
|
object.attack_animal(charger)
|
|
break
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/on_bump(atom/movable/source, atom/target)
|
|
SIGNAL_HANDLER
|
|
if(owner == target)
|
|
return
|
|
if(destroy_objects)
|
|
if(isturf(target))
|
|
SSexplosions.medturf += target
|
|
if(isobj(target) && target.density && !ismecha(target))
|
|
target.take_damage(charge_damage)
|
|
if(ismecha(target))
|
|
target.take_damage(charge_damage)
|
|
|
|
INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
|
|
try_hit_target(source, target)
|
|
|
|
/// Attempt to hit someone with our charge
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/try_hit_target(atom/movable/source, atom/target)
|
|
if (can_hit_target(source, target))
|
|
hit_target(source, target, charge_damage)
|
|
|
|
/// Returns true if we're allowed to charge into this target
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/can_hit_target(atom/movable/source, atom/target)
|
|
return isliving(target)
|
|
|
|
/// Actually hit someone
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/hit_target(atom/movable/source, mob/living/target, damage_dealt)
|
|
target.visible_message(span_danger("[source] slams into [target]!"), span_userdanger("[source] tramples you into the ground!"))
|
|
target.apply_damage(damage_dealt, BRUTE, wound_bonus = CANT_WOUND)
|
|
playsound(get_turf(target), 'sound/effects/meteorimpact.ogg', 100, TRUE)
|
|
shake_camera(target, 4, 3)
|
|
shake_camera(source, 2, 3)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/basic_charge
|
|
name = "Basic Charge"
|
|
cooldown_time = 6 SECONDS
|
|
charge_delay = 1.5 SECONDS
|
|
charge_distance = 4
|
|
melee_cooldown_time = 0
|
|
/// How long to shake for before charging
|
|
var/shake_duration = 1 SECONDS
|
|
/// Intensity of shaking animation
|
|
var/shake_pixel_shift = 2
|
|
/// Amount of time to stun self upon impact
|
|
var/recoil_duration = 0.6 SECONDS
|
|
/// Amount of time to knock over an impacted target
|
|
var/knockdown_duration = 0.6 SECONDS
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/basic_charge/do_charge_indicator(atom/charger, atom/charge_target)
|
|
charger.Shake(shake_pixel_shift, shake_pixel_shift, shake_duration)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/basic_charge/can_hit_target(atom/movable/source, atom/target)
|
|
if(!isliving(target))
|
|
if(!target.density || target.CanPass(source, get_dir(target, source)))
|
|
return FALSE
|
|
return TRUE
|
|
return ..()
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/basic_charge/hit_target(atom/movable/source, atom/target, damage_dealt)
|
|
var/mob/living/living_source
|
|
if(isliving(source))
|
|
living_source = source
|
|
|
|
if(!isliving(target))
|
|
source.visible_message(span_danger("[source] smashes into [target]!"))
|
|
living_source?.Stun(recoil_duration, ignore_canstun = TRUE)
|
|
return
|
|
|
|
var/mob/living/living_target = target
|
|
if(ishuman(living_target))
|
|
var/mob/living/carbon/human/human_target = living_target
|
|
if(human_target.check_block(source, 0, "\the [source]", attack_type = LEAP_ATTACK) && living_source)
|
|
living_source.Stun(recoil_duration, ignore_canstun = TRUE)
|
|
return
|
|
|
|
living_target.visible_message(span_danger("[source] charges into [living_target]!"), span_userdanger("[source] charges into you!"))
|
|
living_target.Knockdown(knockdown_duration)
|
|
|
|
/datum/status_effect/tired_post_charge
|
|
id = "tired_post_charge"
|
|
duration = 1 SECONDS
|
|
alert_type = null
|
|
var/tired_movespeed = /datum/movespeed_modifier/status_effect/tired_post_charge
|
|
|
|
/datum/status_effect/tired_post_charge/on_apply()
|
|
. = ..()
|
|
owner.add_movespeed_modifier(tired_movespeed)
|
|
|
|
/datum/status_effect/tired_post_charge/on_remove()
|
|
. = ..()
|
|
owner.remove_movespeed_modifier(tired_movespeed)
|
|
|
|
/datum/status_effect/tired_post_charge/lesser
|
|
id = "tired_post_charge_easy"
|
|
tired_movespeed = /datum/movespeed_modifier/status_effect/tired_post_charge/lesser
|
|
|
|
/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"
|
|
button_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 || prob(33))
|
|
hallucination_charge(target_atom, 6, 8, 0, 6, TRUE)
|
|
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_REF(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"
|
|
button_icon = 'icons/mob/actions/actions_animal.dmi'
|
|
button_icon_state = "expand"
|
|
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)
|