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:
Neerti
2017-09-30 22:24:42 -04:00
parent 34f5b7b6bf
commit b8a7749351
16 changed files with 146 additions and 81 deletions
@@ -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)
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 197 KiB