mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-05-19 13:20:47 +01:00
d73f6b8dbd
* Prevents insta-actions * Do_after sanity NOTE: NUKE do_after_action * Update bonfire.dm * The rest of them Also fixes a tpyo * no minitest :) * . * . * Gets rid of the slowdown for now --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
280 lines
9.7 KiB
Plaintext
280 lines
9.7 KiB
Plaintext
// Does a melee attack.
|
|
/mob/living/simple_mob/proc/attack_target(atom/A)
|
|
set waitfor = FALSE // For attack animations. Don't want the AI processor to get held up.
|
|
|
|
if(!A.Adjacent(src))
|
|
return ATTACK_FAILED
|
|
var/turf/their_T = get_turf(A)
|
|
|
|
face_atom(A)
|
|
|
|
if(melee_attack_delay)
|
|
melee_pre_animation(A)
|
|
. = ATTACK_SUCCESSFUL //Shoving this in here as a 'best guess' since this proc is about to sleep and return and we won't be able to know the real value
|
|
handle_attack_delay(A, melee_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false.
|
|
|
|
// Cooldown testing is done at click code (for players) and interface code (for AI).
|
|
// VOREStation Edit Start: Simplemob Injury
|
|
if(injury_enrages)
|
|
setClickCooldown(get_attack_speed() - ((injury_level / 2) SECONDS)) // Increase how fast we can attack by our injury level / 2
|
|
else
|
|
setClickCooldown(get_attack_speed() + ((injury_level / 2) SECONDS)) // Delay how fast we can attack by our injury level / 2
|
|
// VOREStation Edit Stop: Simplemob Injury
|
|
|
|
// Returns a value, but will be lost if
|
|
. = do_attack(A, their_T)
|
|
|
|
if(melee_attack_delay)
|
|
melee_post_animation(A)
|
|
|
|
// This does the actual attack.
|
|
// This is a seperate proc for the purposes of attack animations.
|
|
// A is the thing getting attacked, T is the turf A is/was on when attack_target was called.
|
|
/mob/living/simple_mob/proc/do_attack(atom/A, turf/T)
|
|
face_atom(A)
|
|
var/missed = FALSE
|
|
if(!isturf(A) && !(A in T) ) // Turfs don't contain themselves so checking contents is pointless if we're targeting a turf.
|
|
missed = TRUE
|
|
else if(!T.AdjacentQuick(src))
|
|
missed = TRUE
|
|
|
|
if(missed) // Most likely we have a slow attack and they dodged it or we somehow got moved.
|
|
add_attack_logs(src, A, "Animal-attacked (dodged)", admin_notify = FALSE)
|
|
playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
|
|
visible_message(span_warning("\The [src] misses their attack."))
|
|
return FALSE
|
|
|
|
var/damage_to_do = rand(melee_damage_lower, melee_damage_upper)
|
|
|
|
damage_to_do = apply_bonus_melee_damage(A, damage_to_do)
|
|
|
|
for(var/datum/modifier/M in modifiers)
|
|
if(!isnull(M.outgoing_melee_damage_percent))
|
|
damage_to_do *= M.outgoing_melee_damage_percent
|
|
|
|
if(isliving(A)) // Check defenses.
|
|
var/mob/living/L = A
|
|
|
|
if(prob(melee_miss_chance))
|
|
add_attack_logs(src, L, "Animal-attacked (miss)", admin_notify = FALSE)
|
|
do_attack_animation(src)
|
|
playsound(src, 'sound/weapons/punchmiss.ogg', 75, 1)
|
|
return FALSE // We missed.
|
|
|
|
if(ishuman(L))
|
|
var/mob/living/carbon/human/H = L
|
|
if(H.check_shields(damage = damage_to_do, damage_source = src, attacker = src, def_zone = null, attack_text = "the attack"))
|
|
return FALSE // We were blocked.
|
|
|
|
if(apply_attack(A, damage_to_do))
|
|
apply_melee_effects(A)
|
|
if(attack_sound)
|
|
playsound(src, attack_sound, 75, 1)
|
|
|
|
return TRUE
|
|
|
|
// Generally used to do the regular attack.
|
|
// Override for doing special stuff with the direct result of the attack.
|
|
/mob/living/simple_mob/proc/apply_attack(atom/A, damage_to_do)
|
|
return A.attack_generic(src, damage_to_do, pick(attacktext))
|
|
|
|
// Override for special effects after a successful attack, like injecting poison or stunning the target.
|
|
/mob/living/simple_mob/proc/apply_melee_effects(atom/A)
|
|
return
|
|
|
|
// Override to modify the amount of damage the mob does conditionally.
|
|
// This must return the amount of outgoing damage.
|
|
// Note that this is done before mob modifiers scale the damage.
|
|
/mob/living/simple_mob/proc/apply_bonus_melee_damage(atom/A, damage_amount)
|
|
return damage_amount
|
|
|
|
//The actual top-level ranged attack proc
|
|
/mob/living/simple_mob/proc/shoot_target(atom/A)
|
|
set waitfor = FALSE
|
|
|
|
if(!istype(A) || QDELETED(A))
|
|
return
|
|
|
|
// VOREStation Edit Start: Simplemob Injury
|
|
if(injury_enrages)
|
|
setClickCooldown(get_attack_speed() - ((injury_level / 2) SECONDS)) // Increase how fast we can attack by our injury level / 2
|
|
else
|
|
setClickCooldown(get_attack_speed() + ((injury_level / 2) SECONDS)) // Delay how fast we can attack by our injury level / 2
|
|
// VOREStation Edit Stop: Simplemob Injury
|
|
|
|
face_atom(A)
|
|
|
|
if(ranged_attack_delay)
|
|
ranged_pre_animation(A)
|
|
handle_attack_delay(A, ranged_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false.
|
|
|
|
if(needs_reload)
|
|
if(reload_count >= reload_max)
|
|
try_reload()
|
|
return FALSE
|
|
|
|
if(ranged_cooldown_time) //If you have a non-zero number in a mob's variables, this pattern begins.
|
|
if(ranged_cooldown <= world.time) //Further down, a timer keeps adding to the ranged_cooldown variable automatically.
|
|
visible_message(span_danger(span_bold("\The [src]") + " fires at \the [A]!")) //Leave notice of shooting.
|
|
shoot(A) //Perform the shoot action
|
|
if(casingtype) //If the mob is designated to leave casings...
|
|
new casingtype(loc) //... leave the casing.
|
|
ranged_cooldown = world.time + ranged_cooldown_time + ((injury_level / 2) SECONDS) //Special addition here. This is a timer. Keeping updating the time after shooting. Add that ranged cooldown time specified in the mob to the world time.
|
|
return TRUE
|
|
|
|
visible_message(span_danger(span_bold("\The [src]") + " fires at \the [A]!"))
|
|
shoot(A)
|
|
if(casingtype)
|
|
new casingtype(loc)
|
|
|
|
if(ranged_attack_delay)
|
|
ranged_post_animation(A)
|
|
|
|
return TRUE
|
|
|
|
// Shoot a bullet at something.
|
|
/mob/living/simple_mob/proc/shoot(atom/A)
|
|
if(A == get_turf(src))
|
|
return
|
|
|
|
face_atom(A)
|
|
|
|
var/obj/item/projectile/P = new projectiletype(src.loc)
|
|
if(!P)
|
|
return
|
|
|
|
// If the projectile has its own sound, use it.
|
|
// Otherwise default to the mob's firing sound.
|
|
playsound(src, P.fire_sound ? P.fire_sound : projectilesound, 80, 1)
|
|
|
|
// For some reason there isn't an argument for accuracy, so access the projectile directly instead.
|
|
// Also, placing dispersion here instead of in forced_spread will randomize the chosen angle between dispersion and -dispersion in fire() instead of having to do that here.
|
|
P.accuracy += calculate_accuracy()
|
|
P.dispersion += calculate_dispersion()
|
|
|
|
P.launch_projectile(target = A, target_zone = null, user = src, params = null, angle_override = null, forced_spread = 0)
|
|
if(needs_reload)
|
|
reload_count++
|
|
|
|
// if(distance >= special_attack_min_range && distance <= special_attack_max_range)
|
|
// return TRUE
|
|
|
|
/mob/living/simple_mob/proc/try_reload()
|
|
set waitfor = FALSE
|
|
set_AI_busy(TRUE)
|
|
|
|
if(do_after(src, reload_time, target = src))
|
|
if(reload_sound)
|
|
playsound(src, reload_sound, 70, 1)
|
|
reload_count = 0
|
|
. = TRUE
|
|
else
|
|
. = FALSE
|
|
set_AI_busy(FALSE)
|
|
|
|
/mob/living/simple_mob/proc/calculate_dispersion()
|
|
. = projectile_dispersion // Start with the basic var.
|
|
|
|
// Some modifiers change dispersion. This makes simple_mobs respect that.
|
|
for(var/datum/modifier/M in modifiers)
|
|
if(!isnull(M.accuracy_dispersion))
|
|
. += M.accuracy_dispersion
|
|
|
|
// Make sure we don't go under zero dispersion.
|
|
. = max(., 0)
|
|
|
|
/mob/living/simple_mob/proc/calculate_accuracy()
|
|
. = projectile_accuracy // Start with the basic var.
|
|
|
|
// Some modifiers make it harder or easier to hit things.
|
|
for(var/datum/modifier/M in modifiers)
|
|
if(!isnull(M.accuracy))
|
|
. += M.accuracy
|
|
|
|
// Can we currently do a special attack?
|
|
/mob/living/simple_mob/proc/can_special_attack(atom/A)
|
|
// Validity check.
|
|
if(!istype(A))
|
|
return FALSE
|
|
|
|
// Ability check.
|
|
if(isnull(special_attack_min_range) || isnull(special_attack_max_range))
|
|
return FALSE
|
|
|
|
// Distance check.
|
|
var/distance = get_dist(src, A)
|
|
if(distance < special_attack_min_range || distance > special_attack_max_range)
|
|
return FALSE
|
|
|
|
// Cooldown check.
|
|
if(!isnull(special_attack_cooldown) && last_special_attack + special_attack_cooldown > world.time)
|
|
return FALSE
|
|
|
|
// Charge check.
|
|
if(!isnull(special_attack_charges) && special_attack_charges <= 0)
|
|
return FALSE
|
|
|
|
return TRUE
|
|
|
|
// Should we do one? Used to make the AI not waste their special attacks. Only checked for AI. Players are free to screw up on their own.
|
|
/mob/living/simple_mob/proc/should_special_attack(atom/A)
|
|
return TRUE
|
|
|
|
// Special attacks, like grenades or blinding spit or whatever.
|
|
// Don't override this, override do_special_attack() for your blinding spit/etc.
|
|
/mob/living/simple_mob/proc/special_attack_target(atom/A)
|
|
face_atom(A)
|
|
|
|
if(special_attack_delay)
|
|
special_pre_animation(A)
|
|
handle_attack_delay(A, special_attack_delay) // This will sleep this proc for a bit, which is why waitfor is false.
|
|
|
|
last_special_attack = world.time
|
|
if(do_special_attack(A))
|
|
if(special_attack_charges)
|
|
special_attack_charges -= 1
|
|
. = TRUE
|
|
else
|
|
. = FALSE
|
|
|
|
if(special_attack_delay)
|
|
special_post_animation(A)
|
|
|
|
// Override this for the actual special attack.
|
|
/mob/living/simple_mob/proc/do_special_attack(atom/A)
|
|
return FALSE
|
|
|
|
// Sleeps the proc that called it for the correct amount of time.
|
|
// Also makes sure the AI doesn't do anything stupid in the middle of the delay.
|
|
/mob/living/simple_mob/proc/handle_attack_delay(atom/A, delay_amount)
|
|
set_AI_busy(TRUE)
|
|
|
|
// Click delay modifiers also affect telegraphing time.
|
|
// This means berserked enemies will leave less time to dodge.
|
|
var/true_attack_delay = delay_amount
|
|
for(var/datum/modifier/M in modifiers)
|
|
if(!isnull(M.attack_speed_percent))
|
|
true_attack_delay *= M.attack_speed_percent
|
|
|
|
setClickCooldown(true_attack_delay) // Insurance against a really long attack being longer than default click delay.
|
|
|
|
sleep(true_attack_delay)
|
|
|
|
set_AI_busy(FALSE)
|
|
|
|
// Override these four for special custom animations (like the GOLEM).
|
|
/mob/living/simple_mob/proc/melee_pre_animation(atom/A)
|
|
do_windup_animation(A, melee_attack_delay)
|
|
|
|
/mob/living/simple_mob/proc/melee_post_animation(atom/A)
|
|
|
|
/mob/living/simple_mob/proc/ranged_pre_animation(atom/A)
|
|
do_windup_animation(A, ranged_attack_delay) // Semi-placeholder.
|
|
|
|
/mob/living/simple_mob/proc/ranged_post_animation(atom/A)
|
|
|
|
/mob/living/simple_mob/proc/special_pre_animation(atom/A)
|
|
do_windup_animation(A, special_attack_delay) // Semi-placeholder.
|
|
|
|
/mob/living/simple_mob/proc/special_post_animation(atom/A)
|