mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 21:48:39 +01:00
Merge branch 'master' of https://github.com/tgstation/tgstation into upstream-15-10-2025
This commit is contained in:
@@ -109,7 +109,7 @@
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/exenterate/proc/slash_em(atom/caster)
|
||||
for(var/mob/living/victim in range(ability_range, caster))
|
||||
if(faction_check(victim.faction, immune_factions) && owner.CanReach(victim))
|
||||
if(faction_check(victim.faction, immune_factions) && victim.IsReachableBy(owner))
|
||||
continue
|
||||
to_chat(caster, span_warning("You slice [victim]!"))
|
||||
to_chat(victim, span_warning("You are cut by [caster]'s blades!"))
|
||||
|
||||
@@ -372,7 +372,7 @@
|
||||
update_bot_mode(new_mode = BOT_IDLE)
|
||||
return
|
||||
|
||||
if(CanReach(patient))
|
||||
if(patient.IsReachableBy(src))
|
||||
melee_attack(patient)
|
||||
|
||||
/datum/id_trim/medibot
|
||||
|
||||
@@ -597,7 +597,7 @@
|
||||
. = ..()
|
||||
var/list/reachable_turfs = list()
|
||||
for(var/turf/adjacent_turf in RANGE_TURFS(1, owner.loc))
|
||||
if(adjacent_turf == owner.loc || !owner.CanReach(adjacent_turf) || !isopenturf(adjacent_turf))
|
||||
if(adjacent_turf == owner.loc || !adjacent_turf.IsReachableBy(owner) || !isopenturf(adjacent_turf))
|
||||
continue
|
||||
reachable_turfs += adjacent_turf
|
||||
|
||||
@@ -625,10 +625,10 @@
|
||||
/datum/action/cooldown/exquisite_bunch/Trigger(mob/clicker, trigger_flags, atom/target)
|
||||
if(activating)
|
||||
return
|
||||
var/bunch_turf = get_step(owner.loc, owner.dir)
|
||||
var/atom/bunch_turf = get_step(owner.loc, owner.dir)
|
||||
if(!bunch_turf)
|
||||
return
|
||||
if(!owner.CanReach(bunch_turf) || !isopenturf(bunch_turf))
|
||||
if(!bunch_turf.IsReachableBy(owner) || !isopenturf(bunch_turf))
|
||||
owner.balloon_alert(owner, "can't do that here!")
|
||||
return
|
||||
activating = TRUE
|
||||
|
||||
@@ -36,20 +36,19 @@
|
||||
smashes_walls = FALSE
|
||||
melee_attack_cooldown = 2 SECONDS
|
||||
|
||||
/mob/living/basic/construct/juggernaut/bullet_act(obj/projectile/bullet)
|
||||
if(!istype(bullet, /obj/projectile/energy) && !istype(bullet, /obj/projectile/beam))
|
||||
/mob/living/basic/construct/juggernaut/projectile_hit(obj/projectile/hitting_projectile, def_zone, piercing_hit, blocked)
|
||||
if(!istype(hitting_projectile, /obj/projectile/energy) && !istype(hitting_projectile, /obj/projectile/beam))
|
||||
return ..()
|
||||
if(!prob(40 - round(bullet.damage / 3))) // reflect chance
|
||||
if(!prob(40 - round(hitting_projectile.damage / 3))) // reflect chance
|
||||
return ..()
|
||||
|
||||
apply_damage(bullet.damage * 0.5, bullet.damage_type)
|
||||
apply_damage(hitting_projectile.damage * 0.5, hitting_projectile.damage_type)
|
||||
visible_message(
|
||||
span_danger("\The [bullet] is reflected by [src]'s armored shell!"),
|
||||
span_userdanger("\The [bullet] is reflected by your armored shell!"),
|
||||
span_danger("\The [hitting_projectile] is reflected by [src]'s armored shell!"),
|
||||
span_userdanger("\The [hitting_projectile] is reflected by your armored shell!"),
|
||||
)
|
||||
|
||||
bullet.reflect(src)
|
||||
|
||||
hitting_projectile.reflect(src)
|
||||
return BULLET_ACT_FORCE_PIERCE // complete projectile permutation
|
||||
|
||||
// Alternate juggernaut themes
|
||||
|
||||
@@ -295,3 +295,6 @@
|
||||
if(built_in_camera?.can_use())
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/drone/hypnosis_vulnerable()
|
||||
return FALSE //It obeys its laws
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
var/static/list/actions_to_add = list(
|
||||
/datum/action/cooldown/spell/fire_sworn,
|
||||
/datum/action/cooldown/spell/jaunt/ethereal_jaunt/ash,
|
||||
/datum/action/cooldown/spell/pointed/cleave,
|
||||
)
|
||||
grant_actions_by_list(actions_to_add)
|
||||
|
||||
/mob/living/basic/heretic_summon/ash_spirit/Life(seconds_per_tick, times_fired)
|
||||
. = ..()
|
||||
adjustBruteLoss(-3) // 3 health passively healing
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
)
|
||||
AddElement(/datum/element/death_drops, loot)
|
||||
GRANT_ACTION(/datum/action/cooldown/spell/jaunt/mirror_walk)
|
||||
ADD_TRAIT(src, TRAIT_UNHITTABLE_BY_LASERS, INNATE_TRAIT)
|
||||
|
||||
/mob/living/basic/heretic_summon/maid_in_the_mirror/death(gibbed)
|
||||
var/turf/death_turf = get_turf(src)
|
||||
@@ -50,14 +51,14 @@
|
||||
return
|
||||
|
||||
// If we have health, we take some damage
|
||||
if(health > (maxHealth * 0.125))
|
||||
if(health > (maxHealth * 0.02))
|
||||
visible_message(
|
||||
span_warning("[src] seems to fade in and out slightly."),
|
||||
span_userdanger("[user]'s gaze pierces your every being!"),
|
||||
)
|
||||
|
||||
recent_examiner_refs += user_ref
|
||||
apply_damage(maxHealth * 0.1) // We take 10% of our health as damage upon being examined
|
||||
apply_damage(maxHealth * 0.02) // We take 2% of our health as damage upon being examined
|
||||
playsound(src, 'sound/effects/ghost2.ogg', 40, TRUE)
|
||||
addtimer(CALLBACK(src, PROC_REF(clear_recent_examiner), user_ref), recent_examine_damage_cooldown, TIMER_DELETE_ME)
|
||||
animate(src, alpha = 120, time = 0.5 SECONDS, easing = ELASTIC_EASING, loop = 2, flags = ANIMATION_PARALLEL)
|
||||
@@ -78,3 +79,10 @@
|
||||
|
||||
recent_examiner_refs -= mob_ref
|
||||
heal_overall_damage(5)
|
||||
|
||||
/mob/living/basic/heretic_summon/maid_in_the_mirror/melee_attack(atom/target, list/modifiers, ignore_cooldown)
|
||||
. = ..()
|
||||
if(!. || !isliving(target))
|
||||
return
|
||||
var/mob/living/living_target = target
|
||||
living_target.apply_status_effect(/datum/status_effect/void_chill, 1)
|
||||
|
||||
@@ -35,29 +35,94 @@
|
||||
mob_size = MOB_SIZE_HUGE
|
||||
layer = LARGE_MOB_LAYER
|
||||
flags_1 = PREVENT_CONTENTS_EXPLOSION_1
|
||||
lighting_cutoff_red = 12
|
||||
lighting_cutoff_green = 15
|
||||
lighting_cutoff_blue = 34
|
||||
sight = SEE_TURFS|SEE_MOBS|SEE_OBJS
|
||||
|
||||
ai_controller = /datum/ai_controller/basic_controller/star_gazer
|
||||
/// Reference to the mob which summoned us
|
||||
var/datum/weakref/summoner
|
||||
/// How far we can go before being pulled back
|
||||
var/leash_range = 20
|
||||
/// Timer for finding a ghost so it doesn't spam dead chat with requests
|
||||
var/begging_timer
|
||||
///---- Abilities given to the star gazer mob
|
||||
var/list/abilities_to_grant = list(
|
||||
/datum/action/cooldown/spell/conjure/cosmic_expansion,
|
||||
/datum/action/cooldown/spell/pointed/projectile/star_blast,
|
||||
/datum/action/cooldown/recall_stargazer,
|
||||
/datum/action/cooldown/spell/stargazer_laser,
|
||||
)
|
||||
|
||||
/mob/living/basic/heretic_summon/star_gazer/Initialize(mapload)
|
||||
/mob/living/basic/heretic_summon/star_gazer/Initialize(mapload, mob/living/master)
|
||||
. = ..()
|
||||
for(var/datum/action/cooldown/spell/spell as anything in abilities_to_grant)
|
||||
spell = new spell(src)
|
||||
spell.Grant(src)
|
||||
var/datum/action/cooldown/spell/stargazer_laser/laser = locate() in actions
|
||||
if(master)
|
||||
summoner = WEAKREF(master)
|
||||
if(laser)
|
||||
laser.our_master = WEAKREF(master)
|
||||
AddComponent(/datum/component/seethrough_mob)
|
||||
var/static/list/death_loot = list(/obj/effect/temp_visual/cosmic_domain)
|
||||
AddElement(/datum/element/death_drops, death_loot)
|
||||
AddElement(/datum/element/death_drops, /obj/effect/temp_visual/cosmic_domain)
|
||||
AddElement(/datum/element/death_explosion, 3, 6, 12)
|
||||
AddElement(/datum/element/footstep, FOOTSTEP_MOB_SHOE)
|
||||
AddElement(/datum/element/wall_smasher, ENVIRONMENT_SMASH_RWALLS)
|
||||
AddElement(/datum/element/simple_flying)
|
||||
AddElement(/datum/element/effect_trail, /obj/effect/forcefield/cosmic_field/fast)
|
||||
AddElement(/datum/element/effect_trail/cosmic_field/antiprojectile, /obj/effect/forcefield/cosmic_field/fast)
|
||||
AddElement(/datum/element/ai_target_damagesource)
|
||||
AddComponent(/datum/component/regenerator, outline_colour = "#b97a5d")
|
||||
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
|
||||
ADD_TRAIT(src, TRAIT_LAVA_IMMUNE, INNATE_TRAIT)
|
||||
ADD_TRAIT(src, TRAIT_ASHSTORM_IMMUNE, INNATE_TRAIT)
|
||||
ADD_TRAIT(src, TRAIT_NO_TELEPORT, MEGAFAUNA_TRAIT)
|
||||
ADD_TRAIT(src, TRAIT_MARTIAL_ARTS_IMMUNE, MEGAFAUNA_TRAIT)
|
||||
ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT)
|
||||
ADD_TRAIT(src, TRAIT_MAGICALLY_GIFTED, INNATE_TRAIT)
|
||||
set_light(4, l_color = "#dcaa5b")
|
||||
INVOKE_ASYNC(src, PROC_REF(beg_for_ghost))
|
||||
RegisterSignal(src, COMSIG_MOB_GHOSTIZED, PROC_REF(beg_for_ghost))
|
||||
|
||||
/mob/living/basic/heretic_summon/star_gazer/Destroy()
|
||||
deltimer(begging_timer)
|
||||
return ..()
|
||||
|
||||
/// Tries to find a ghost to take control of the mob. If no ghost accepts, ask again in a bit
|
||||
/mob/living/basic/heretic_summon/star_gazer/proc/beg_for_ghost()
|
||||
if(timeleft(begging_timer) && !client)
|
||||
return
|
||||
begging_timer = addtimer(CALLBACK(src, PROC_REF(beg_for_ghost)), 2 MINUTES, TIMER_STOPPABLE | TIMER_UNIQUE) // Keep begging until someone accepts
|
||||
var/mob/chosen_ghost = SSpolling.poll_ghost_candidates(
|
||||
"Do you want to play as an ascended heretic's stargazer?",
|
||||
check_jobban = ROLE_HERETIC,
|
||||
poll_time = 20 SECONDS,
|
||||
ignore_category = POLL_IGNORE_HERETIC_MONSTER,
|
||||
alert_pic = mutable_appearance('icons/effects/eldritch.dmi', "cosmic_diamond"),
|
||||
jump_target = src,
|
||||
role_name_text = "star gazer",
|
||||
amount_to_pick = 1
|
||||
)
|
||||
if(chosen_ghost)
|
||||
PossessByPlayer(chosen_ghost.key)
|
||||
deltimer(begging_timer)
|
||||
|
||||
/// Connects these two mobs by a leash
|
||||
/mob/living/basic/heretic_summon/star_gazer/proc/leash_to(atom/movable/leashed, atom/movable/leashed_to)
|
||||
leashed.AddComponent(\
|
||||
/datum/component/leash,\
|
||||
owner = leashed_to,\
|
||||
distance = leash_range,\
|
||||
force_teleport_out_effect = /obj/effect/temp_visual/guardian/phase/out,\
|
||||
force_teleport_in_effect = /obj/effect/temp_visual/guardian/phase,\
|
||||
)
|
||||
|
||||
// Star gazer attacks everything around itself applies a spooky mark
|
||||
/mob/living/basic/heretic_summon/star_gazer/melee_attack(mob/living/target, list/modifiers, ignore_cooldown)
|
||||
if(target == summoner?.resolve())
|
||||
return FALSE
|
||||
. = ..()
|
||||
if (!. || !isliving(target))
|
||||
return
|
||||
@@ -74,6 +139,261 @@
|
||||
do_attack_animation(nearby_mob, ATTACK_EFFECT_SLASH)
|
||||
log_combat(src, nearby_mob, "slashed")
|
||||
|
||||
/datum/action/cooldown/recall_stargazer
|
||||
name = "Seek master"
|
||||
desc = "Teleports you to your master"
|
||||
background_icon_state = "bg_heretic"
|
||||
overlay_icon_state = "bg_heretic_border"
|
||||
button_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
button_icon_state = "stargazer_menu"
|
||||
check_flags = NONE
|
||||
cooldown_time = 5 SECONDS
|
||||
|
||||
/datum/action/cooldown/recall_stargazer/Activate(atom/target)
|
||||
var/mob/living/basic/heretic_summon/star_gazer/real_owner = owner
|
||||
var/mob/living/master = real_owner.summoner?.resolve()
|
||||
if(!master)
|
||||
return FALSE
|
||||
do_teleport(owner, master, no_effects = TRUE, channel = TELEPORT_CHANNEL_MAGIC, forced = TRUE)
|
||||
StartCooldown()
|
||||
return TRUE
|
||||
|
||||
/datum/action/cooldown/spell/stargazer_laser
|
||||
name = "Star Gaze"
|
||||
desc = "Generates a massive death beam that eradicates everything in it's path. \
|
||||
Has it's own gravitational pull, sucking in new victims."
|
||||
background_icon_state = "bg_heretic"
|
||||
overlay_icon_state = "bg_heretic_border"
|
||||
button_icon = 'icons/mob/actions/actions_ecult.dmi'
|
||||
button_icon_state = "gazer_beam_charge"
|
||||
check_flags = NONE
|
||||
cooldown_time = 30 SECONDS
|
||||
invocation = "SH''P D' W''P"
|
||||
invocation_type = INVOCATION_SHOUT
|
||||
spell_requirements = NONE
|
||||
/// list of turfs we are hitting while shooting our beam
|
||||
var/list/turf/targets
|
||||
/// The laser beam we generate
|
||||
var/datum/beam/giga_laser
|
||||
/// Timer that handles the damage ticking
|
||||
var/damage_timer
|
||||
/// Reference to our summoner so that we don't disintegrate them by accident
|
||||
var/datum/weakref/our_master
|
||||
/// The overlay on the caster when they fire the beam
|
||||
var/obj/effect/abstract/gazer_orb/orb_visual
|
||||
/// The visual effect at the beginning of the laser
|
||||
var/obj/effect/abstract/gazer_beam/beam_visual
|
||||
/// List of visual effects for the beam, in between and in the end
|
||||
var/list/beam_fillings
|
||||
/// Sound loop for the active laser
|
||||
var/datum/looping_sound/gazer_beam/sound_loop
|
||||
/// The visual effect at the end of the laser
|
||||
var/obj/effect/abstract/gazer_beamend/end_visual
|
||||
/// Tracks how many times the beam has processed, after the maximum amount of cycles it will forcibly end the beam
|
||||
var/cycle_tracker = 0
|
||||
|
||||
/datum/action/cooldown/spell/stargazer_laser/cast(atom/target)
|
||||
. = ..()
|
||||
|
||||
cooldown_time = initial(cooldown_time)
|
||||
if(damage_timer)
|
||||
stop_beaming()
|
||||
|
||||
var/turf/check_turf = get_step(owner, owner.dir)
|
||||
var/list/turf/targets_left = list()
|
||||
targets_left += get_step(check_turf, turn(owner.dir, 90))
|
||||
var/list/turf/targets_right = list()
|
||||
targets_right += get_step(check_turf, turn(owner.dir, -90))
|
||||
LAZYINITLIST(targets)
|
||||
while(check_turf && length(targets) < 20)
|
||||
targets += check_turf
|
||||
check_turf = get_step(check_turf, owner.dir)
|
||||
targets_left += get_step(check_turf, turn(owner.dir, 90))
|
||||
targets_right += get_step(check_turf, turn(owner.dir, -90))
|
||||
if(!LAZYLEN(targets))
|
||||
return
|
||||
|
||||
RegisterSignals(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_ATOM_DIR_CHANGE), PROC_REF(stop_beaming))
|
||||
beam_fillings = list()
|
||||
cycle_tracker = 0
|
||||
orb_visual = new(get_step(owner, owner.dir))
|
||||
var/beam_timer = addtimer(CALLBACK(src, PROC_REF(open_laser), owner, targets), 2.2 SECONDS, TIMER_STOPPABLE)
|
||||
playsound(owner, 'sound/mobs/non-humanoids/stargazer/beam_open.ogg', 50, FALSE)
|
||||
if(!do_after(owner, 3 SECONDS, owner))
|
||||
cooldown_time = 1 SECONDS
|
||||
deltimer(beam_timer)
|
||||
QDEL_NULL(orb_visual)
|
||||
QDEL_NULL(beam_visual)
|
||||
QDEL_NULL(end_visual)
|
||||
for(var/atom/to_delete as anything in beam_fillings)
|
||||
QDEL_NULL(to_delete)
|
||||
targets = null
|
||||
return
|
||||
|
||||
sound_loop.start(owner)
|
||||
QDEL_NULL(orb_visual)
|
||||
beam_visual.icon_state = "gazer_beam_active"
|
||||
beam_visual.update_appearance(UPDATE_ICON)
|
||||
end_visual.icon_state = "gazer_beam_end"
|
||||
end_visual.update_appearance(UPDATE_ICON)
|
||||
targets += targets_left
|
||||
targets += targets_right
|
||||
process_beam()
|
||||
|
||||
/datum/action/cooldown/spell/stargazer_laser/Destroy()
|
||||
QDEL_NULL(sound_loop)
|
||||
return ..()
|
||||
|
||||
/datum/action/cooldown/spell/stargazer_laser/New(Target, original)
|
||||
. = ..()
|
||||
sound_loop = new
|
||||
|
||||
/// Spawns the beginning of the laser, uses `targets` to determine the rotation
|
||||
/datum/action/cooldown/spell/stargazer_laser/proc/open_laser(mob/owner, list/turf/targets)
|
||||
beam_visual = new(get_step(get_step(owner, owner.dir), owner.dir), targets[length(targets)])
|
||||
end_visual = new(targets[length(targets)], owner)
|
||||
for(var/turf/to_fill as anything in (get_line(targets[4], targets[length(targets)-2])))
|
||||
var/obj/effect/abstract/gazer_beam_filling/new_filling = new(to_fill, owner.dir)
|
||||
beam_fillings += new_filling
|
||||
|
||||
// Visual effect of the big orb when you start channeling the laser
|
||||
/obj/effect/abstract/gazer_orb
|
||||
icon = 'icons/effects/160x160.dmi'
|
||||
icon_state = "gazer_beam_charge"
|
||||
SET_BASE_VISUAL_PIXEL(-64, -64)
|
||||
|
||||
// Visual effect at the start of the beam, has an opening/active/closing state
|
||||
/obj/effect/abstract/gazer_beam
|
||||
icon = 'icons/effects/beam96x96.dmi'
|
||||
SET_BASE_VISUAL_PIXEL(-32, -32)
|
||||
|
||||
/obj/effect/abstract/gazer_beam/Initialize(mapload, turf/target)
|
||||
. = ..()
|
||||
if(!target)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
var/Angle = get_angle_raw(x, y, pixel_x, pixel_y, target.x , target.y, target.pixel_x, target.pixel_y)
|
||||
var/matrix/transform_matrix = matrix()
|
||||
Angle = round(Angle, 45)
|
||||
transform_matrix.Turn(Angle-90)
|
||||
transform_matrix.Scale(2, 2)
|
||||
transform = transform_matrix
|
||||
flick("gazer_beam_start", src)
|
||||
|
||||
// Visual effect of the middle of the beam, has an opening/active/closing state
|
||||
/obj/effect/abstract/gazer_beam_filling
|
||||
icon = 'icons/effects/beam.dmi'
|
||||
icon_state = "gazer_beam"
|
||||
|
||||
/obj/effect/abstract/gazer_beam_filling/Initialize(mapload, direction)
|
||||
. = ..()
|
||||
if(!direction)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
var/Angle = dir2angle(direction)
|
||||
var/matrix/transform_matrix = matrix()
|
||||
transform_matrix.Turn(Angle)
|
||||
transform_matrix.Scale(2, 2)
|
||||
transform = transform_matrix
|
||||
flick("gazer_beam_end_opening", src)
|
||||
|
||||
/obj/effect/abstract/gazer_beam_filling/proc/pull_victims()
|
||||
for(var/atom/movable/movable_atom in orange(5, src))
|
||||
if((movable_atom.anchored || movable_atom.move_resist >= MOVE_FORCE_EXTREMELY_STRONG))
|
||||
continue
|
||||
if(ismob(movable_atom))
|
||||
var/mob/pulled_mob = movable_atom
|
||||
if(pulled_mob.mob_negates_gravity())
|
||||
continue
|
||||
step_towards(movable_atom, src)
|
||||
|
||||
// Visual effect at the end of the beam, has an opening/active/closing state
|
||||
/obj/effect/abstract/gazer_beamend
|
||||
icon = 'icons/effects/beam.dmi'
|
||||
|
||||
/obj/effect/abstract/gazer_beamend/Initialize(mapload, atom/origin)
|
||||
. = ..()
|
||||
if(!origin)
|
||||
return INITIALIZE_HINT_QDEL
|
||||
var/Angle = get_angle_raw(origin.x , origin.y, origin.pixel_x, origin.pixel_y, x, y, pixel_x, pixel_y)
|
||||
var/matrix/transform_matrix = matrix()
|
||||
Angle = round(Angle, 45)
|
||||
transform_matrix.Turn(Angle)
|
||||
transform_matrix.Scale(2, 2)
|
||||
transform = transform_matrix
|
||||
flick("gazer_beam_end_opening", src)
|
||||
|
||||
/datum/looping_sound/gazer_beam
|
||||
mid_sounds = list('sound/mobs/non-humanoids/stargazer/beam_loop_one.ogg')
|
||||
mid_length = 109
|
||||
volume = 80
|
||||
|
||||
/obj/effect/ebeam/phase_in // Beam subtype that has a "windup" phase
|
||||
alpha = 0
|
||||
|
||||
/obj/effect/ebeam/phase_in/Initialize(mapload)
|
||||
. = ..()
|
||||
animate(src, 2 SECONDS, alpha = 255, transform = matrix(3, 1, MATRIX_SCALE))
|
||||
|
||||
/obj/effect/ebeam/phased_in/Initialize(mapload, beam_owner) // phased in, fully powered laser
|
||||
. = ..()
|
||||
transform = matrix(2, 2, MATRIX_SCALE)
|
||||
|
||||
/// Recursive proc which affects whatever is caught within the beam
|
||||
/datum/action/cooldown/spell/stargazer_laser/proc/process_beam()
|
||||
if(cycle_tracker > 33)
|
||||
stop_beaming()
|
||||
for(var/obj/effect/abstract/gazer_beam_filling/fillings as anything in beam_fillings)
|
||||
if(prob(98))
|
||||
continue
|
||||
// 2% chance to pull you towards the beam
|
||||
fillings.pull_victims()
|
||||
for(var/turf/target as anything in targets)
|
||||
if(iswallturf(target))
|
||||
var/turf/closed/wall/wall_target = target
|
||||
wall_target.dismantle_wall(devastated = TRUE)
|
||||
continue
|
||||
if(isfloorturf(target))
|
||||
var/turf/open/floor/to_burn = target
|
||||
to_burn.burn_tile()
|
||||
for(var/victim in target)
|
||||
if(isobj(victim))
|
||||
var/obj/to_obliterate = victim
|
||||
if(to_obliterate.resistance_flags & INDESTRUCTIBLE)
|
||||
continue
|
||||
to_obliterate.atom_destruction(FIRE)
|
||||
if(isliving(victim))
|
||||
if(victim == our_master?.resolve())
|
||||
continue
|
||||
var/mob/living/living_victim = victim
|
||||
if(living_victim.stat > CONSCIOUS)
|
||||
playsound(living_victim, 'sound/effects/supermatter.ogg', 80, TRUE)
|
||||
living_victim.visible_message(
|
||||
span_danger("You see [living_victim] engulfed in the scorching wrath of the cosmos. \
|
||||
For a moment, you see their silhouette flail in agony before fading to mere atoms."),
|
||||
span_boldbig(span_hypnophrase("THE POWER OF THE COSMOS ITSELF POURS OUT OVER YOUR FORM. \
|
||||
WAVES OF HEAT LATCH ONTO YOUR BODY, PULLING IT APART AT THE SEAMS. \
|
||||
YOUR TOTAL ANNIHILATION TAKES ONLY A MOMENT BEFORE YOU ARE REDUCED BACK TO WHAT YOU ALWAYS WERE. \
|
||||
MOTES OF MERE DUST..."))
|
||||
)
|
||||
living_victim.dust()
|
||||
living_victim.emote("scream")
|
||||
living_victim.apply_status_effect(/datum/status_effect/star_mark)
|
||||
living_victim.apply_damage(damage = 30, damagetype = BURN, spread_damage = TRUE)
|
||||
cycle_tracker++
|
||||
damage_timer = addtimer(CALLBACK(src, PROC_REF(process_beam)), 0.3 SECONDS, TIMER_STOPPABLE)
|
||||
|
||||
/// Stops the beam after we cancel it
|
||||
/datum/action/cooldown/spell/stargazer_laser/proc/stop_beaming()
|
||||
SIGNAL_HANDLER
|
||||
sound_loop.stop()
|
||||
UnregisterSignal(owner, list(COMSIG_MOVABLE_MOVED, COMSIG_ATOM_DIR_CHANGE))
|
||||
QDEL_NULL(giga_laser)
|
||||
QDEL_NULL(beam_visual)
|
||||
QDEL_NULL(end_visual)
|
||||
QDEL_LIST(beam_fillings)
|
||||
deltimer(damage_timer)
|
||||
damage_timer = null
|
||||
targets = null
|
||||
|
||||
/datum/ai_controller/basic_controller/star_gazer
|
||||
blackboard = list(
|
||||
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
if(stat != CONSCIOUS)
|
||||
return COMPONENT_BULLET_PIERCED
|
||||
|
||||
///high penetration bullets should still go through. No goldgrub can save you from the colossus' death bolts.
|
||||
/// High penetration bullets should still go through. No goldgrub can save you from the colossus' death bolts.
|
||||
if(prob(hitting_projectile.armour_penetration))
|
||||
return NONE
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
playsound(src, 'sound/effects/splat.ogg', 50, TRUE)
|
||||
for(var/obj/item/stack/ore/ore in src)
|
||||
ore.forceMove(loc)
|
||||
|
||||
if(!gibbed)
|
||||
visible_message(span_danger("[src] spits out its consumed ores!"))
|
||||
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
/datum/ai_behavior/goliath_dig/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
|
||||
var/turf/target_turf = controller.blackboard[target_key]
|
||||
var/mob/living/basic/basic_mob = controller.pawn
|
||||
if(!basic_mob.CanReach(target_turf))
|
||||
if(!target_turf.IsReachableBy(basic_mob))
|
||||
return AI_BEHAVIOR_DELAY
|
||||
basic_mob.melee_attack(target_turf)
|
||||
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
var/times_to_attack = 4
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/mook_ability/mook_leap/Activate(atom/target)
|
||||
if(owner.CanReach(target))
|
||||
if(target.IsReachableBy(owner))
|
||||
attack_combo(target)
|
||||
StartCooldown()
|
||||
return TRUE
|
||||
@@ -74,14 +74,14 @@
|
||||
owner.throw_at(target = final_turf, range = 7, speed = 1, spin = FALSE, callback = CALLBACK(src, PROC_REF(attack_combo), target))
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/mook_ability/mook_leap/proc/attack_combo(atom/target)
|
||||
if(!owner.CanReach(target))
|
||||
if(!target.IsReachableBy(owner))
|
||||
return FALSE
|
||||
|
||||
for(var/i in 0 to (times_to_attack - 1))
|
||||
addtimer(CALLBACK(src, PROC_REF(attack_target), target), i * attack_interval)
|
||||
|
||||
/datum/action/cooldown/mob_cooldown/mook_ability/mook_leap/proc/attack_target(atom/target)
|
||||
if(!owner.CanReach(target) || owner.stat == DEAD)
|
||||
if(!target.IsReachableBy(owner) || owner.stat == DEAD)
|
||||
return
|
||||
var/mob/living/basic/basic_owner = owner
|
||||
basic_owner.melee_attack(target, ignore_cooldown = TRUE)
|
||||
|
||||
@@ -299,8 +299,7 @@ GLOBAL_LIST_EMPTY(raptor_population)
|
||||
icon_dead = "raptor_black_dead"
|
||||
maxHealth = 400
|
||||
health = 400
|
||||
speed = 1
|
||||
ridable_component = /datum/component/riding/creature/raptor/fast
|
||||
speed = 1.5
|
||||
melee_damage_lower = 20
|
||||
melee_damage_upper = 25
|
||||
raptor_color = RAPTOR_BLACK
|
||||
@@ -312,8 +311,7 @@ GLOBAL_LIST_EMPTY(raptor_population)
|
||||
icon_state = "raptor_yellow"
|
||||
icon_living = "raptor_yellow"
|
||||
icon_dead = "raptor_yellow_dead"
|
||||
ridable_component = /datum/component/riding/creature/raptor/fast
|
||||
speed = 1
|
||||
speed = 1.5
|
||||
raptor_color = RAPTOR_YELLOW
|
||||
dex_description = "This breed possesses greasy fast speed, DEMON speed, making light work of long pilgrimages. It's said that a thunderclap could be heard when this breed reaches its maximum speed."
|
||||
child_path = /mob/living/basic/raptor/baby_raptor/yellow
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
var/list/data = list()
|
||||
var/mob/living/basic/raptor/my_raptor = raptor.resolve()
|
||||
|
||||
data["raptor_image"] = icon2base64(getFlatIcon(image(icon = my_raptor.icon, icon_state = my_raptor.icon_state)))
|
||||
data["raptor_image"] = icon2base64(icon(icon = my_raptor.icon, icon_state = my_raptor.icon_state, frame = 1))
|
||||
data["raptor_attack"] = my_raptor.melee_damage_lower
|
||||
data["raptor_health"] = my_raptor.maxHealth
|
||||
data["raptor_speed"] = my_raptor.speed
|
||||
@@ -33,7 +33,7 @@
|
||||
var/obj/effect/overlay/happiness_overlay/display = new
|
||||
display.set_hearts(happiness_percentage)
|
||||
display.pixel_y = ICON_SIZE_Y * 0.5
|
||||
data["raptor_happiness"] = icon2base64(getFlatIcon(display))
|
||||
data["raptor_happiness"] = icon2base64(getFlatIcon(display, no_anim = TRUE))
|
||||
qdel(display)
|
||||
|
||||
var/datum/raptor_inheritance/inherit = my_raptor.inherited_stats
|
||||
|
||||
@@ -165,7 +165,7 @@
|
||||
|
||||
/mob/living/basic/mining_drone/ui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["bot_icon"] = icon2base64(getFlatIcon(src))
|
||||
data["bot_icon"] = icon2base64(getFlatIcon(src, no_anim = TRUE))
|
||||
data["possible_colors"] = list()
|
||||
for(var/color in possible_colors)
|
||||
data["possible_colors"] += list(list(
|
||||
|
||||
@@ -292,7 +292,7 @@
|
||||
possible_headwear += item
|
||||
if(!length(possible_headwear))
|
||||
for(var/obj/item/item in orange(1))
|
||||
if(ispath(item.dog_fashion, /datum/dog_fashion/head) && CanReach(item))
|
||||
if(ispath(item.dog_fashion, /datum/dog_fashion/head) && item.IsReachableBy(src))
|
||||
possible_headwear += item
|
||||
if(!length(possible_headwear))
|
||||
return
|
||||
|
||||
@@ -81,7 +81,7 @@ GLOBAL_LIST_INIT(pet_options, list(
|
||||
|
||||
for(var/color in possible_colors)
|
||||
demo_carrier.set_greyscale(possible_colors[color])
|
||||
list_to_return[color] = icon2base64(getFlatIcon(demo_carrier))
|
||||
list_to_return[color] = icon2base64(getFlatIcon(demo_carrier, no_anim = TRUE))
|
||||
qdel(demo_carrier)
|
||||
return list_to_return
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
for(var/atom/movable/movable in orange(victim, 1))
|
||||
if(movable == victim)
|
||||
continue
|
||||
if(!victim.CanReach(movable) || movable.invisibility > victim.see_invisible)
|
||||
if(!movable.IsReachableBy(victim) || movable.invisibility > victim.see_invisible)
|
||||
continue
|
||||
candidates += movable
|
||||
if(!length(candidates))
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
/mob/living/basic/ghost/proc/give_identity()
|
||||
if(random_identity)
|
||||
ghost_hairstyle = random_hairstyle() //This only gives us the hairstyle name, not the icon_state (which we need).
|
||||
ghost_hair_color = "#[random_color()]"
|
||||
ghost_hair_color = random_hair_color()
|
||||
|
||||
if(prob(50)) //Only a chance at also getting facial hair
|
||||
ghost_facial_hairstyle = random_facial_hairstyle()
|
||||
|
||||
@@ -299,7 +299,7 @@
|
||||
/mob/living/basic/revenant/narsie_act()
|
||||
return //most humans will now be either bones or harvesters, but we're still un-alive.
|
||||
|
||||
/mob/living/basic/revenant/bullet_act()
|
||||
/mob/living/basic/revenant/projectile_hit(obj/projectile/hitting_projectile, def_zone, piercing_hit, blocked)
|
||||
if(!HAS_TRAIT(src, TRAIT_REVENANT_REVEALED) || dormant)
|
||||
return BULLET_ACT_FORCE_PIERCE
|
||||
return ..()
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
r_hand = /obj/item/knife/combat/survival
|
||||
var/projectile_deflect_chance = 0
|
||||
|
||||
/mob/living/basic/trooper/syndicate/melee/bullet_act(obj/projectile/projectile)
|
||||
/mob/living/basic/trooper/syndicate/melee/projectile_hit(obj/projectile/hitting_projectile, def_zone, piercing_hit, blocked)
|
||||
if(prob(projectile_deflect_chance))
|
||||
visible_message(span_danger("[src] blocks [projectile] with its shield!"))
|
||||
visible_message(span_danger("[src] blocks [hitting_projectile] with its shield!"))
|
||||
return BULLET_ACT_BLOCK
|
||||
return ..()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user