mirror of
https://github.com/VOREStation/VOREStation.git
synced 2026-08-22 11:38:12 +01:00
Technomancer Golem Tweaks & Simple Animal Health "Refactor"
Simple animals now use the base health system that most other mobs use. Fixes a bunch of Destroy()s so that they should qdel properly. Mend Synthetic can heal synthetic simple animals. Adds attack and spellcast animations to the golem. Adds system to manually flick because byond is stupid sometimes. Removes shield and reflect spells from the golem since SAs can't use shields at the moment. Adds more helpful spells to the golem. Golem and the Lost Drone now use the 'synthetic_evil' speech bubble.
This commit is contained in:
@@ -29,47 +29,133 @@
|
||||
|
||||
melee_damage_lower = 30 // It has a built in esword.
|
||||
melee_damage_upper = 30
|
||||
attack_sound = 'sound/weapons/blade1.ogg'
|
||||
attacktext = "slashed"
|
||||
attack_sound = null
|
||||
friendly = "hugs"
|
||||
resistance = 0
|
||||
melee_miss_chance = 0
|
||||
|
||||
var/obj/item/weapon/technomancer_core/golem/core = null
|
||||
var/obj/item/weapon/spell/active_spell = null // Shield and ranged spells
|
||||
var/mob/living/master = null
|
||||
|
||||
var/list/known_spells = list(
|
||||
"reflect" = /obj/item/weapon/spell/reflect,
|
||||
"shield" = /obj/item/weapon/spell/shield,
|
||||
"dispel" = /obj/item/weapon/spell/dispel,
|
||||
"mend life" = /obj/item/weapon/spell/modifier/mend_life,
|
||||
"mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic,
|
||||
"repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles,
|
||||
"corona" = /obj/item/weapon/spell/modifier/corona,
|
||||
"beam" = /obj/item/weapon/spell/projectile/beam,
|
||||
"chain lightning" = /obj/item/weapon/spell/projectile/chain_lightning,
|
||||
"force missile" = /obj/item/weapon/spell/projectile/force_missile,
|
||||
"ionic bolt" = /obj/item/weapon/spell/projectile/ionic_bolt,
|
||||
"lightning" = /obj/item/weapon/spell/projectile/lightning
|
||||
"lightning" = /obj/item/weapon/spell/projectile/lightning,
|
||||
"blink" = /obj/item/weapon/spell/blink,
|
||||
"dispel" = /obj/item/weapon/spell/dispel,
|
||||
"oxygenate" = /obj/item/weapon/spell/oxygenate,
|
||||
"mend life" = /obj/item/weapon/spell/modifier/mend_life,
|
||||
"mend synthetic" = /obj/item/weapon/spell/modifier/mend_synthetic,
|
||||
"mend organs" = /obj/item/weapon/spell/mend_organs,
|
||||
"purify" = /obj/item/weapon/spell/modifier/purify,
|
||||
"resurrect" = /obj/item/weapon/spell/resurrect,
|
||||
"passwall" = /obj/item/weapon/spell/passwall,
|
||||
"repel missiles" = /obj/item/weapon/spell/modifier/repel_missiles,
|
||||
"corona" = /obj/item/weapon/spell/modifier/corona,
|
||||
"haste" = /obj/item/weapon/spell/modifier/haste
|
||||
)
|
||||
|
||||
// Holds the overlays, when idle or attacking.
|
||||
var/image/sword_image = null
|
||||
var/image/spell_image = null
|
||||
// These contain icon_states for each frame of an attack animation, which is swapped in and out manually, because BYOND.
|
||||
// They are assoc lists, to hold the frame duration and the frame icon_state in one list.
|
||||
var/list/spell_pre_attack_states = list(
|
||||
"golem_spell_attack_1" = 1,
|
||||
"golem_spell_attack_2" = 2,
|
||||
"golem_spell_attack_3" = 2
|
||||
)
|
||||
var/list/spell_post_attack_states = list(
|
||||
"golem_spell_attack_4" = 2,
|
||||
"golem_spell_attack_5" = 3,
|
||||
"golem_spell_attack_6" = 3
|
||||
)
|
||||
var/list/sword_pre_attack_states = list(
|
||||
"golem_sword_attack_1" = 1,
|
||||
"golem_sword_attack_2" = 5
|
||||
)
|
||||
var/list/sword_post_attack_states = list(
|
||||
"golem_sword_attack_3" = 1,
|
||||
"golem_sword_attack_4" = 3
|
||||
)
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/New()
|
||||
..()
|
||||
core = new(src)
|
||||
sword_image = image(icon, src, "golem_sword")
|
||||
spell_image = image(icon, src, "golem_spell")
|
||||
update_icon()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/Destroy()
|
||||
qdel(core)
|
||||
qdel(sword_image)
|
||||
qdel(spell_image)
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/unref_spell()
|
||||
active_spell = null
|
||||
return ..()
|
||||
|
||||
/mob/living/simple_animal/hostile/hivebot/death()
|
||||
..()
|
||||
visible_message("\The [src] disintegrates!")
|
||||
new /obj/effect/decal/cleanable/blood/gibs/robot(src.loc)
|
||||
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||
s.set_up(3, 1, src)
|
||||
s.start()
|
||||
qdel(src)
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/update_icon()
|
||||
overlays.Cut()
|
||||
overlays.Add(image(icon, src, "golem_sword"))
|
||||
overlays.Add(image(icon, src, "golem_spell"))
|
||||
overlays += sword_image
|
||||
overlays += spell_image
|
||||
update_modifier_visuals()
|
||||
|
||||
// Unfortunately, BYOND does not let you flick() images or other overlays, so we need to do this in a terrible way.
|
||||
/atom/proc/manual_flick(var/list/frames, var/image/I, var/reset_to = null)
|
||||
// Swap in and out each frame manually.
|
||||
for(var/frame in frames)
|
||||
overlays -= I
|
||||
I.icon_state = frame
|
||||
overlays += I
|
||||
sleep(frames[frame])
|
||||
if(reset_to)
|
||||
// One more time to reset it to what it was before.
|
||||
overlays -= I
|
||||
I.icon_state = reset_to
|
||||
overlays += I
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/spellcast_pre_animation()
|
||||
setClickCooldown(5)
|
||||
manual_flick(spell_pre_attack_states, spell_image, reset_to = "golem_spell_attack_3")
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/spellcast_post_animation()
|
||||
setClickCooldown(8)
|
||||
manual_flick(spell_post_attack_states, spell_image, reset_to = "golem_spell")
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/sword_pre_animation()
|
||||
setClickCooldown(6)
|
||||
manual_flick(sword_pre_attack_states, sword_image)
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/proc/sword_post_animation()
|
||||
setClickCooldown(3)
|
||||
manual_flick(sword_post_attack_states, sword_image, reset_to = "golem_sword")
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/DoPunch(var/atom/A)
|
||||
sword_pre_animation()
|
||||
. = ..() // This does the actual attack and will check adjacency again.
|
||||
sword_post_animation()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/isSynthetic()
|
||||
return TRUE // So Mend Synthetic will work on them.
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/speech_bubble_appearance()
|
||||
return "synthetic_evil"
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/place_spell_in_hand(var/path)
|
||||
if(!path || !ispath(path))
|
||||
return 0
|
||||
@@ -84,21 +170,30 @@
|
||||
var/choice = input(usr, "What spell?", "Give spell") as null|anything in known_spells
|
||||
if(choice)
|
||||
place_spell_in_hand(known_spells[choice])
|
||||
else
|
||||
qdel(active_spell)
|
||||
|
||||
// Used to cast spells.
|
||||
/mob/living/simple_animal/technomancer_golem/RangedAttack(var/atom/A, var/params)
|
||||
if(active_spell)
|
||||
spellcast_pre_animation()
|
||||
if(active_spell.cast_methods & CAST_RANGED)
|
||||
active_spell.on_ranged_cast(A, src)
|
||||
spellcast_post_animation()
|
||||
|
||||
/mob/living/simple_animal/technomancer_golem/UnarmedAttack(var/atom/A, var/proximity)
|
||||
if(proximity)
|
||||
if(active_spell)
|
||||
spellcast_pre_animation()
|
||||
if(!Adjacent(A)) // Need to check again since they might've moved while 'warming up'.
|
||||
spellcast_post_animation()
|
||||
return
|
||||
var/effective_cooldown = round(active_spell.cooldown * core.cooldown_modifier, 5)
|
||||
if(active_spell.cast_methods & CAST_MELEE)
|
||||
active_spell.on_melee_cast(A, src)
|
||||
else if(active_spell.cast_methods & CAST_RANGED)
|
||||
active_spell.on_ranged_cast(A, src)
|
||||
var/effective_cooldown = round(active_spell.cooldown * core.cooldown_modifier, 5)
|
||||
spellcast_post_animation()
|
||||
src.setClickCooldown(effective_cooldown)
|
||||
else
|
||||
..()
|
||||
|
||||
@@ -149,10 +149,17 @@
|
||||
// Parameters: 0
|
||||
// Description: Nulls object references so it can qdel() cleanly.
|
||||
/obj/item/weapon/spell/Destroy()
|
||||
owner.unref_spell(src)
|
||||
owner = null
|
||||
core = null
|
||||
return ..()
|
||||
|
||||
// Proc: unref_spells()
|
||||
// Parameters: 0
|
||||
// Description: Nulls object references on specific mobs so it can qdel() cleanly.
|
||||
/mob/proc/unref_spell(var/obj/item/weapon/spell/the_spell)
|
||||
return
|
||||
|
||||
// Proc: update_icon()
|
||||
// Parameters: 0
|
||||
// Description: Applys an overlay if it is a passive spell.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
/obj/item/weapon/spell/aura/Destroy()
|
||||
processing_objects -= src
|
||||
log_and_message_admins("has stopped maintaining [src].")
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/spell/aura/process()
|
||||
return
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
for(var/mob/living/simple_animal/hostile/SM in controlled_mobs)
|
||||
deselect(SM)
|
||||
controlled_mobs = list()
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/spell/control/on_use_cast(mob/living/user)
|
||||
if(controlled_mobs.len != 0)
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
/obj/item/weapon/spell/energy_siphon/Destroy()
|
||||
stop_siphoning()
|
||||
processing_objects -= src
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/spell/energy_siphon/process()
|
||||
if(!siphoning)
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
/obj/item/weapon/spell/flame_tongue/Destroy()
|
||||
qdel(welder)
|
||||
welder = null
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/weldingtool/spell
|
||||
name = "flame"
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
/obj/item/weapon/spell/illusion/Destroy()
|
||||
if(illusion)
|
||||
qdel(illusion)
|
||||
..()
|
||||
return ..()
|
||||
|
||||
// Makes a tiny overlay of the thing the player has copied, so they can easily tell what they currently have.
|
||||
/obj/item/weapon/spell/illusion/update_icon()
|
||||
|
||||
@@ -29,9 +29,6 @@
|
||||
stacks = MODIFIER_STACK_EXTEND
|
||||
|
||||
/datum/modifier/technomancer/mend_synthetic/tick()
|
||||
// if(!holder.isSynthetic()) // Don't heal biologicals!
|
||||
// expire()
|
||||
// return
|
||||
if(!holder.getActualBruteLoss() && !holder.getActualFireLoss()) // No point existing if the spell can't heal.
|
||||
expire()
|
||||
return
|
||||
@@ -42,8 +39,9 @@
|
||||
if(O.robotic >= ORGAN_ROBOT)
|
||||
O.heal_damage(4 * spell_power, 4 * spell_power, 0, 1)
|
||||
else
|
||||
holder.adjustBruteLoss(-4 * spell_power) // Should heal roughly 20 burn/brute over 10 seconds, as tick() is run every 2 seconds.
|
||||
holder.adjustFireLoss(-4 * spell_power) // Ditto.
|
||||
if(holder.isSynthetic())
|
||||
holder.adjustBruteLoss(-4 * spell_power) // Should heal roughly 20 burn/brute over 10 seconds, as tick() is run every 2 seconds.
|
||||
holder.adjustFireLoss(-4 * spell_power) // Ditto.
|
||||
|
||||
holder.adjust_instability(1)
|
||||
if(origin)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
/obj/item/weapon/spell/reflect/Destroy()
|
||||
spark_system = null
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/spell/reflect/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
|
||||
if(user.incapacitated())
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
/obj/item/weapon/spell/shield/Destroy()
|
||||
spark_system = null
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/spell/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
|
||||
if(user.incapacitated())
|
||||
|
||||
@@ -24,7 +24,7 @@ var/list/technomancer_belongings = list()
|
||||
/obj/item/weapon/spell/track/Destroy()
|
||||
tracked = null
|
||||
tracking = 0
|
||||
..()
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/spell/track/on_use_cast(mob/user)
|
||||
if(tracking)
|
||||
|
||||
@@ -293,7 +293,7 @@ default behaviour is:
|
||||
return fireloss
|
||||
|
||||
/mob/living/proc/getActualFireLoss() // Mostly for humans with robolimbs.
|
||||
return getBruteLoss()
|
||||
return getFireLoss()
|
||||
|
||||
/mob/living/proc/adjustFireLoss(var/amount)
|
||||
if(status_flags & GODMODE) return 0 //godmode
|
||||
|
||||
@@ -22,6 +22,9 @@
|
||||
|
||||
playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0)
|
||||
|
||||
/mob/living/silicon/robot/lost/speech_bubble_appearance()
|
||||
return "synthetic_evil"
|
||||
|
||||
/mob/living/silicon/robot/lost/randomlaws
|
||||
|
||||
/mob/living/silicon/robot/lost/randomlaws/init()
|
||||
|
||||
@@ -227,19 +227,14 @@
|
||||
|
||||
//Should we be dead?
|
||||
/mob/living/simple_animal/updatehealth()
|
||||
health = getMaxHealth() - getToxLoss() - getFireLoss() - getBruteLoss()
|
||||
|
||||
//Alive, becoming dead
|
||||
if((stat < DEAD) && (health <= 0))
|
||||
death()
|
||||
|
||||
//Dead, becoming alive
|
||||
else if((stat >= DEAD) && (health > 0))
|
||||
dead_mob_list -= src
|
||||
living_mob_list += src
|
||||
stat = CONSCIOUS
|
||||
density = 1
|
||||
|
||||
//Overhealth
|
||||
else if(health > getMaxHealth())
|
||||
if(health > getMaxHealth())
|
||||
health = getMaxHealth()
|
||||
|
||||
/mob/living/simple_animal/update_icon()
|
||||
@@ -621,6 +616,16 @@
|
||||
var/tally = 0 //Incase I need to add stuff other than "speed" later
|
||||
|
||||
tally = speed
|
||||
|
||||
if(force_max_speed)
|
||||
return -3
|
||||
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.haste) && M.haste == TRUE)
|
||||
return -3
|
||||
if(!isnull(M.slowdown))
|
||||
tally += M.slowdown
|
||||
|
||||
if(purge)//Purged creatures will move more slowly. The more time before their purge stops, the slower they'll move.
|
||||
if(tally <= 0)
|
||||
tally = 1
|
||||
@@ -674,52 +679,6 @@
|
||||
if(3.0)
|
||||
adjustBruteLoss(30)
|
||||
|
||||
// Don't understand why simple animals don't use the regular /mob/living health system.
|
||||
/mob/living/simple_animal/adjustBruteLoss(damage)
|
||||
if(damage > 0)
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.incoming_damage_percent))
|
||||
damage *= M.incoming_damage_percent
|
||||
if(!isnull(M.incoming_brute_damage_percent))
|
||||
damage *= M.incoming_brute_damage_percent
|
||||
else if(damage < 0)
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.incoming_healing_percent))
|
||||
damage *= M.incoming_healing_percent
|
||||
|
||||
health = Clamp(health - damage, 0, getMaxHealth())
|
||||
updatehealth()
|
||||
|
||||
/mob/living/simple_animal/adjustFireLoss(damage)
|
||||
if(damage > 0)
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.incoming_damage_percent))
|
||||
damage *= M.incoming_damage_percent
|
||||
if(!isnull(M.incoming_fire_damage_percent))
|
||||
damage *= M.incoming_brute_damage_percent
|
||||
else if(damage < 0)
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.incoming_healing_percent))
|
||||
damage *= M.incoming_healing_percent
|
||||
|
||||
health = Clamp(health - damage, 0, getMaxHealth())
|
||||
updatehealth()
|
||||
|
||||
/mob/living/simple_animal/adjustToxLoss(damage)
|
||||
if(damage > 0)
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.incoming_damage_percent))
|
||||
damage *= M.incoming_damage_percent
|
||||
if(!isnull(M.incoming_tox_damage_percent))
|
||||
damage *= M.incoming_brute_damage_percent
|
||||
else if(damage < 0)
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(!isnull(M.incoming_healing_percent))
|
||||
damage *= M.incoming_healing_percent
|
||||
|
||||
health = Clamp(health - damage, 0, getMaxHealth())
|
||||
updatehealth()
|
||||
|
||||
// Check target_mob if worthy of attack (i.e. check if they are dead or empty mecha)
|
||||
/mob/living/simple_animal/proc/SA_attackable(target_mob)
|
||||
ai_log("SA_attackable([target_mob])",3)
|
||||
@@ -1238,6 +1197,9 @@
|
||||
if(!isnull(M.outgoing_melee_damage_percent))
|
||||
damage_to_do *= M.outgoing_melee_damage_percent
|
||||
|
||||
if(attack_sound)
|
||||
playsound(src, attack_sound, 75, 1)
|
||||
|
||||
// SA attacks can be blocked with shields.
|
||||
if(ishuman(A))
|
||||
var/mob/living/carbon/human/H = A
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
// Removes all modifiers of a type
|
||||
/mob/living/proc/remove_modifiers_of_type(var/modifier_type, var/silent = FALSE)
|
||||
for(var/datum/modifier/M in modifiers)
|
||||
if(istype(M, modifier_type))
|
||||
if(ispath(M.type, modifier_type))
|
||||
M.expire(silent)
|
||||
|
||||
// Removes all modifiers, useful if the mob's being deleted
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
Reference in New Issue
Block a user