mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-01-01 04:21:42 +00:00
## About The Pull Request Unit tests will now fail if there's a decal in a wall or open space turf. Open space turf could be limiting to mappers but I don't think it makes sense for decals (like dirt, glass shards, etc) to be floating around in space in the exact same spot. If there's a decal you want to put in space, decals have a ``turf_loc_check`` var that will bypass this. **Important note: This is not changing existing behavior. Decals already delete themselves when they spawn in these incorrect locations, we're just avoiding them from spawning in the first place.** ### Changes I made - Ash flora are now lava immune, rivers spawn after flora does, so I decided that it would be easiest (and more flavorful) to have them be lava-immune rather than to not have them spawn at all. - Decals can now be spawned in non-turf locations. This is currently done by mail, which can give you bones as part of the mail. Currently it will just delete itself instead. - Trading Card button is now on the same tile as their display, which now uses an offset. Before it would spawn it on the tile next to it, which could be a wall in some instances. - Mirrors now have floating movement type. They ARE floating since they're attached to the wall, and it prevents them from burning up due to lava in the Pride ruin. - I also added a broken mirror subtype because I thought the icon_state check was terrible. - Bubblegum called ``DestroySurroundings`` several times on the same thing, I hopefully fixed some of that. Their charge ability also registered ``COMSIG_MOB_STATCHANGE`` despite ``/datum/action`` doing it by default, so I fixed that too. ## Why It's Good For The Game Decals in walls is already a bad idea, but currently all it does is delete it on Initialize. It would be better if we ensured they wouldn't spawn in the first place. ## Changelog 🆑 fix: Lava will no longer burn 6 of the mirrors in pride ruin fix: Lava will no longer burn plants that spawn in them. /🆑
286 lines
11 KiB
Plaintext
286 lines
11 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)
|
|
StartCooldown(360 SECONDS, 360 SECONDS)
|
|
charge_sequence(owner, target_atom, charge_delay, charge_past)
|
|
StartCooldown()
|
|
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
|
|
SSmove_manager.stop_looping(charger)
|
|
|
|
charging += charger
|
|
actively_moving = FALSE
|
|
SEND_SIGNAL(owner, COMSIG_STARTED_CHARGE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_BUMP, PROC_REF(on_bump), TRUE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(on_move), TRUE)
|
|
RegisterSignal(charger, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved), 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 = SSmove_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))
|
|
RegisterSignal(new_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_move))
|
|
RegisterSignal(new_loop, COMSIG_QDELETING, PROC_REF(charge_end))
|
|
|
|
// 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/move_loop/source)
|
|
SIGNAL_HANDLER
|
|
var/atom/movable/charger = source.moving
|
|
UnregisterSignal(charger, list(COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_PRE_MOVE, COMSIG_MOVABLE_MOVED))
|
|
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)
|
|
SSmove_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 = "#FF0000", 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)
|
|
SSexplosions.med_mov_atom += target
|
|
|
|
INVOKE_ASYNC(src, PROC_REF(DestroySurroundings), source)
|
|
hit_target(source, target, charge_damage)
|
|
|
|
/datum/action/cooldown/mob_cooldown/charge/proc/hit_target(atom/movable/source, atom/target, damage_dealt)
|
|
if(!isliving(target))
|
|
return
|
|
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_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
|
|
|
|
/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/hit_target(atom/movable/source, atom/target, damage_dealt)
|
|
var/mob/living/living_source
|
|
if(isliving(source))
|
|
living_source = source
|
|
|
|
if(!isliving(target))
|
|
if(!target.density || target.CanPass(source, get_dir(target, source)))
|
|
return
|
|
source.visible_message(span_danger("[source] smashes into [target]!"))
|
|
if(!living_source)
|
|
return
|
|
living_source.Stun(6, 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_shields(source, 0, "the [source.name]", attack_type = LEAP_ATTACK) && living_source)
|
|
living_source.Stun(6, ignore_canstun = TRUE)
|
|
return
|
|
|
|
living_target.visible_message(span_danger("[source] charges on [living_target]!"), span_userdanger("[source] charges into you!"))
|
|
living_target.Knockdown(6)
|
|
|
|
/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/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)
|