[Ready for code review] Moves alien verbs to spells (#20002)

* Moves alien verbs to spells

* lol

* Standardization, polish, making alien plasma huds more consistant

* The very cursed way of handling touch spells

* logging + more polish

* snakecase + documentation

* finishing touches

* oops, ty henri

Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com>

* sprites from TGstation

* sprites, again

* oops

* farie's review part 1

* farie's review p2, signals

* Erics PR compat

* farie's review 3

* that too

* the shawnshank redemption

* boink

* removes larva transfer plasma

* you want a sprite cranberry?

* TM fix 1

* FUCK I BROKE LOGGING

* bam

* final touches

---------

Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com>
This commit is contained in:
GDN
2023-02-12 21:34:11 +01:00
committed by GitHub
co-authored by Henri215
parent 98d7440f75
commit aa56d75f93
41 changed files with 548 additions and 403 deletions
-1
View File
@@ -143,7 +143,6 @@
martial_art.remove(current)
else
martial_art.teach(current)
if(active)
new_character.key = key //now transfer the key to link the client to our new body
SEND_SIGNAL(src, COMSIG_MIND_TRANSER_TO, new_character)
@@ -0,0 +1,37 @@
/* This is the handler for alien spells
This relies on a lot of different procs on the spell and carbon level, so if you are editing this, remember to check that the other procs aren't editied in a way that
could change behavior.
You're gonna see a lot of plasmavessel checks and alien checks, as a lot of things need to work a certain way when used by an alien, and a different way when used by a human.
This was also the case with the verb implementation, it's just much more obvious now.
*/
/datum/spell_handler/alien
/// Amount of plasma required to use this ability
var/plasma_cost = 0
/datum/spell_handler/alien/can_cast(mob/living/carbon/user, charge_check, show_message, obj/effect/proc_holder/spell/spell)
var/obj/item/organ/internal/alien/plasmavessel/vessel = user.get_int_organ(/obj/item/organ/internal/alien/plasmavessel)
if(!vessel)
return 0
if(vessel.stored_plasma < plasma_cost)
if(show_message)
to_chat(user, "<span class='warning'>You require at least [plasma_cost] plasma to use this ability!</span>")
return FALSE
return TRUE
/datum/spell_handler/alien/spend_spell_cost(mob/living/carbon/user, obj/effect/proc_holder/spell/spell)
user.use_plasma_spell(plasma_cost, user)
/datum/spell_handler/alien/before_cast(list/targets, mob/living/carbon/user, obj/effect/proc_holder/spell/spell)
to_chat(user, "<span class='boldnotice'>You have [user.get_plasma()] plasma left to use.</span>")
user.update_plasma_display(user)
/datum/spell_handler/alien/revert_cast(mob/living/carbon/user, obj/effect/proc_holder/spell/spell)
user.add_plasma(plasma_cost, user)
to_chat(user, "<span class='boldnotice'>You have [user.get_plasma()] plasma left to use.</span>")
user.update_plasma_display(user)
/mob/living/carbon/proc/get_plasma()
var/obj/item/organ/internal/alien/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/alien/plasmavessel)
if(!vessel)
return 0
return vessel.stored_plasma
@@ -0,0 +1,45 @@
/mob/living/carbon/proc/use_plasma_spell(amount, mob/living/carbon/user)
var/obj/item/organ/internal/alien/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/alien/plasmavessel)
if(amount > vessel.stored_plasma)
return FALSE
add_plasma(-amount)
return TRUE
/* add_plasma just requires the plasma amount, so admins can easily varedit it and stuff
Updates the spell's actions on use as well, so they know when they can or can't use their powers*/
/mob/living/carbon/proc/add_plasma(amount)
var/obj/item/organ/internal/alien/plasmavessel/vessel = get_int_organ(/obj/item/organ/internal/alien/plasmavessel)
if(!vessel)
return
vessel.stored_plasma += amount
if(vessel.stored_plasma > vessel.max_plasma)
vessel.stored_plasma = vessel.max_plasma
update_plasma_display(src)
for(var/datum/action/spell_action/action in actions)
action.UpdateButtonIcon()
/obj/effect/proc_holder/spell/alien_spell
action_background_icon_state = "bg_alien"
clothes_req = FALSE
/// Extremely fast cooldown, only present so the cooldown system doesn't explode
base_cooldown = 1
create_attack_logs = FALSE
/// Every alien spell creates only logs, no attack messages on someone placing weeds, but you DO get attack messages on neurotoxin and corrosive acid
create_custom_logs = TRUE
/// How much plasma it costs to use this
var/plasma_cost = 0
/// Every single alien spell uses a "spell name + plasmacost" format
/obj/effect/proc_holder/spell/alien_spell/Initialize(mapload)
. = ..()
if(plasma_cost)
name = "[name] ([plasma_cost])"
/obj/effect/proc_holder/spell/alien_spell/write_custom_logs(list/targets, mob/user)
user.create_log(ATTACK_LOG, "Cast the spell [name]")
/obj/effect/proc_holder/spell/alien_spell/create_new_handler()
var/datum/spell_handler/alien/handler = new
handler.plasma_cost = plasma_cost
return handler
@@ -0,0 +1,53 @@
/obj/effect/proc_holder/spell/touch/alien_spell
name = "Basetype Alien spell"
desc = "You should not see this in game, if you do file a github report!"
hand_path = "/obj/item/melee/touch_attack/alien"
/// Extremely fast cooldown, only present so the cooldown system doesn't explode
base_cooldown = 1
action_background_icon_state = "bg_alien"
clothes_req = FALSE
create_attack_logs = FALSE
/// Every alien spell creates only logs, no attack messages on someone placing weeds, but you DO get attack messages on neurotoxin and corrosive acid
create_custom_logs = TRUE
/// We want a special on remove message, so we got this variable to do so
on_remove_message = FALSE
/// How much plasma it costs to use this
var/plasma_cost = 0
action_icon_state = "gib"
/obj/effect/proc_holder/spell/touch/alien_spell/Initialize(mapload)
. = ..()
if(plasma_cost)
name = "[name] ([plasma_cost])"
/obj/effect/proc_holder/spell/touch/alien_spell/Click(mob/user = usr)
if(attached_hand)
to_chat(user, "<span class='noticealien'>You withdraw your [src].</span>")
..()
/obj/effect/proc_holder/spell/touch/alien_spell/cast(list/targets, mob/living/carbon/user)
user.add_plasma(plasma_cost)
..()
/obj/effect/proc_holder/spell/touch/alien_spell/write_custom_logs(list/targets, mob/user)
user.create_log(ATTACK_LOG, "Cast the spell [name]")
/obj/effect/proc_holder/spell/touch/alien_spell/create_new_handler()
var/datum/spell_handler/alien/H = new
H.plasma_cost = plasma_cost
return H
/obj/item/melee/touch_attack/alien
name = "Basetype Alien touch_attack"
desc = "You should not see this in game, if you do file a github report!"
/// Alien spells don't have catchphrases
catchphrase = null
/// Beepsky shouldn't be arresting you over this
needs_permit = FALSE
/obj/item/melee/touch_attack/alien/proc/plasma_check(plasma, mob/living/carbon/user)
var/plasma_current = user.get_plasma()
if(plasma_current < plasma)
qdel(src)
return FALSE
return TRUE
@@ -0,0 +1,41 @@
/obj/effect/proc_holder/spell/alien_spell/build_resin
name = "Build Resin Structure"
desc = "Allows you to create resin structures. Does not work while in space."
plasma_cost = 55
action_icon_state = "alien_resin"
/obj/effect/proc_holder/spell/alien_spell/build_resin/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/alien_spell/build_resin/cast(list/targets, mob/living/carbon/user)
var/static/list/resin_buildings = list("Resin Wall (55)" = image(icon = 'icons/obj/smooth_structures/alien/resin_wall.dmi', icon_state = "resin_wall-0"),
"Resin Nest (55)" = image(icon = 'icons/mob/alien.dmi', icon_state = "nest"),
"Resin door (80)" = image(icon = 'icons/obj/smooth_structures/alien/resin_door.dmi', icon_state = "resin"))
var/choice = show_radial_menu(user, user, resin_buildings, src, radius = 40)
var/turf/turf_to_spawn_at = user.loc
if(!choice)
revert_cast(user)
return
if(isspaceturf(turf_to_spawn_at))
to_chat(user, "<span class='alertalien'>You cannot build the [choice] while in space!</span>")
revert_cast(user)
return
var/obj/structure/alien/resin/resin_on_turf = locate() in turf_to_spawn_at
if(resin_on_turf)
to_chat(user, "<span class='danger'>There is already a resin construction here.</span>")
revert_cast(user)
return
visible_message("<span class='alertalien'>[user] vomits up a thick purple substance and shapes it!</span>")
switch(choice)
if("Resin Wall (55)")
new /obj/structure/alien/resin/wall(turf_to_spawn_at)
if("Resin Nest (55)")
new /obj/structure/bed/nest(turf_to_spawn_at)
if("Resin door (80)")
var/plasma_current = user.get_plasma()
if(plasma_current >= 25)
new /obj/structure/alien/resin/door(turf_to_spawn_at)
user.add_plasma(-25)
return
to_chat(user, "<span class='danger'>You don't have enough plasma to place a door down.</span>")
revert_cast(user)
@@ -0,0 +1,29 @@
/obj/effect/proc_holder/spell/touch/alien_spell/corrosive_acid
name = "Corrosive acid"
desc = "Spit acid on someone in range, this acid melts through nearly anything and heavily damages anyone lacking proper safety equipment."
hand_path = "/obj/item/melee/touch_attack/alien/corrosive_acid"
action_icon_state = "alien_acid"
plasma_cost = 200
/obj/item/melee/touch_attack/alien/corrosive_acid
name = "Corrosive acid"
desc = "A fistfull of death."
icon_state = "alien_acid"
/obj/item/melee/touch_attack/alien/corrosive_acid/afterattack(atom/target, mob/living/carbon/user, proximity)
if(target == user)
to_chat(user, "<span class='noticealien'>You withdraw your readied acid.</span>")
..()
return
if(!proximity || isalien(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED)) // Don't want xenos ditching out of cuffs
return
if(!plasma_check(200, user))
to_chat(user, "<span class='noticealien'>You don't have enough plasma to perform this action!</span>")
return
if(target.acid_act(200, 100))
visible_message("<span class='alertalien'>[user] vomits globs of vile stuff all over [target]. It begins to sizzle and melt under the bubbling mess of acid!</span>")
add_attack_logs(user, target, "Applied corrosive acid") // Want this logged
user.add_plasma(-200)
else
to_chat(user, "<span class='noticealien'>You cannot dissolve this object.</span>")
..()
@@ -0,0 +1,45 @@
// Make this reflect amount grown, can't do that currently
/obj/effect/proc_holder/spell/alien_spell/evolve_larva
name = "Evolve."
desc = "Evolve into a fully grown Alien."
action_icon_state = "alien_evolve_larva"
/obj/effect/proc_holder/spell/alien_spell/evolve_larva/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/alien_spell/evolve_larva/cast(list/targets, mob/living/carbon/alien/larva/user)
if(user.stat != CONSCIOUS)
return
if(user.handcuffed || user.legcuffed)
to_chat(user, "<span class='warning'>You cannot evolve when you are cuffed.</span>")
return
if(user.amount_grown < user.max_grown)
to_chat(user, "<span class='warning'>You are not fully grown.</span>")
return
//green is impossible to read, so i made these blue and changed the formatting slightly
to_chat(user, "<span class='boldnotice'>You are growing into a beautiful alien! It is time to choose a caste.</span>")
to_chat(user, "<span class='notice'>There are three to choose from:</span>")
to_chat(user, "<B>Hunters</B> <span class='notice'>are strong and agile, able to hunt away from the hive and rapidly move through ventilation shafts. Hunters generate plasma slowly and have low reserves.</span>")
to_chat(user, "<B>Sentinels</B> <span class='notice'>are tasked with protecting the hive and are deadly up close and at a range. They are not as physically imposing nor fast as the hunters.</span>")
to_chat(user, "<B>Drones</B> <span class='notice'>are the working class, offering the largest plasma storage and generation. They are the only caste which may evolve again, turning into the dreaded alien queen.</span>")
var/static/list/to_evolve = list("Hunter" = image(icon = 'icons/mob/alien.dmi', icon_state = "alienh_s"),
"Sentinel" = image(icon = 'icons/mob/alien.dmi', icon_state = "aliend_s"),
"Drone" = image(icon = 'icons/mob/alien.dmi', icon_state = "aliend_s"))
var/new_xeno = show_radial_menu(user, user, to_evolve, src, radius = 40)
var/turf/T = user.loc
if(!new_xeno)
return
var/to_spawn
switch(new_xeno)
if("Hunter")
to_spawn = new /mob/living/carbon/alien/humanoid/hunter(T)
if("Sentinel")
to_spawn = new /mob/living/carbon/alien/humanoid/sentinel(T)
if("Drone")
to_spawn = new /mob/living/carbon/alien/humanoid/drone(T)
if(user.mind)
user.mind.transfer_to(to_spawn)
SSblackbox.record_feedback("tally", "alien_growth", 1, "[lowertext(new_xeno)]")
qdel(user)
@@ -0,0 +1,7 @@
/obj/effect/proc_holder/spell/alien_spell/plant_weeds/eggs
name = "Plant alien eggs"
desc = "Allows you to plant alien eggs on your current turf, does not work while in space."
plasma_cost = 75
weed_type = /obj/structure/alien/egg
weed_name = "alien egg"
action_icon_state = "alien_egg"
@@ -0,0 +1,36 @@
/obj/effect/proc_holder/spell/alien_spell/neurotoxin
name = "Neurotoxin spit"
desc = "This ability allows you to fire some neurotoxin. Knocks down anyone you hit, applies a small amount of stamina damage as well."
base_cooldown = 5 SECONDS
plasma_cost = 50
selection_activated_message = "<span class='notice'><B>Your prepare some neurotoxin!</B></span>"
selection_deactivated_message = "<span class='notice'><B>You swallow your prepared neurotoxin.</B></span>"
var/neurotoxin_type = /obj/item/projectile/bullet/neurotoxin
action_icon_state = "alien_neurotoxin_0"
active = FALSE
/obj/effect/proc_holder/spell/alien_spell/neurotoxin/create_new_targeting()
return new /datum/spell_targeting/clicked_atom
/obj/effect/proc_holder/spell/alien_spell/neurotoxin/update_icon_state()
if(!action)
return
action.button_icon_state = "alien_neurotoxin_[active]"
action.UpdateButtonIcon()
/obj/effect/proc_holder/spell/alien_spell/neurotoxin/cast(list/targets, mob/living/carbon/user)
var/target = targets[1]
var/turf/T = user.loc
var/turf/U = get_step(user, user.dir) // A little aimbot is fine
if(!istype(U) || !istype(T))
return FALSE
var/obj/item/projectile/bullet/neurotoxin/neurotoxin = new neurotoxin_type(user.loc)
neurotoxin.current = get_turf(user)
neurotoxin.original = target
neurotoxin.firer = user
neurotoxin.preparePixelProjectile(target, get_turf(target), user)
neurotoxin.fire()
user.newtonian_move(get_dir(U, T))
return TRUE
@@ -0,0 +1,25 @@
/obj/effect/proc_holder/spell/alien_spell/plant_weeds
name = "Plant weeds"
desc = "Allows you to plant some alien weeds on the floor below you. Does not work while in space."
plasma_cost = 50
var/weed_type = /obj/structure/alien/weeds/node
var/weed_name = "alien weed node"
action_icon_state = "alien_plant"
/obj/effect/proc_holder/spell/alien_spell/plant_weeds/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/alien_spell/plant_weeds/cast(list/targets, mob/living/carbon/user)
var/turf/T = user.loc
if(locate(weed_type) in T)
to_chat(user, "<span class='noticealien'>There's already an [weed_name] here.</span>")
revert_cast()
return
if(isspaceturf(T))
to_chat(user, "<span class='noticealien'>You cannot plant [weed_name]s in space.</span>")
revert_cast()
return
user.visible_message("<span class='alertalien'>[user] has planted a [weed_name]!</span>")
new weed_type(T)
@@ -0,0 +1,25 @@
/obj/effect/proc_holder/spell/alien_spell/evolve_queen
name = "Evolve into an alien queen"
desc = "Evolve into an alien queen."
plasma_cost = 500
action_icon_state = "alien_evolve_drone"
/obj/effect/proc_holder/spell/alien_spell/evolve_queen/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/alien_spell/evolve_queen/cast(list/targets, mob/living/carbon/user)
/// First we check if there is a living queen
for(var/mob/living/carbon/alien/humanoid/queen/living_queen in GLOB.alive_mob_list)
if(living_queen.key || !living_queen.get_int_organ(/obj/item/organ/internal/brain)) // We do a once over to check the queen didn't end up going away into the magic land of semi-dead
to_chat(user, "<span class='notice'>We already have an alive queen.</span>")
revert_cast(user)
return
// If there is no queen, that means we can evolve
to_chat(user, "<span class='noticealien'>You begin to evolve!</span>")
user.visible_message("<span class='alertalien'>[user] begins to twist and contort!</span>")
var/turf/userloc = user.loc
var/mob/living/carbon/alien/humanoid/queen/new_xeno = new(userloc)
user.mind.transfer_to(new_xeno)
new_xeno.mind.name = new_xeno.name
SSblackbox.record_feedback("tally", "alien_growth", 1, "queen")
qdel(user)
@@ -0,0 +1,15 @@
/obj/effect/proc_holder/spell/alien_spell/regurgitate
name = "Regurgitate"
desc = "Empties the contents of your stomach onto the ground."
action_icon_state = "alien_barf"
/obj/effect/proc_holder/spell/alien_spell/regurgitate/create_new_targeting()
return new /datum/spell_targeting/self
/obj/effect/proc_holder/spell/alien_spell/regurgitate/cast(list/targets, mob/living/carbon/user)
for(var/mob/M in user.stomach_contents)
var/turf/output_loc = user.loc
M.forceMove(output_loc)
visible_message("<span class='alertalien'><B>[user] hurls out the contents of [p_their()] stomach!</span>")
return
visible_message("<span class='alertalien'><B>[user] dry heaves!</span>")
@@ -0,0 +1,28 @@
/obj/effect/proc_holder/spell/touch/alien_spell/transfer_plasma
name = "Transfer Plasma"
desc = "Transfers 50 plasma to a nearby alien"
hand_path = "/obj/item/melee/touch_attack/alien/transfer_plasma"
action_icon_state = "alien_transfer"
plasma_cost = 50
/obj/item/melee/touch_attack/alien/transfer_plasma
name = "plasma transfer"
desc = "Transfers 50 plasma to another alien."
icon_state = "alien_transfer"
/obj/item/melee/touch_attack/alien/transfer_plasma/afterattack(atom/target, mob/living/carbon/user, proximity)
if(target == user)
to_chat(user, "<span class='noticealien'>You withdraw your readied plasma.</span>")
..()
return
if(!proximity || !isalien(target) || !iscarbon(user) || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
return
if(!plasma_check(50, user))
to_chat(user, "<span class='noticealien'>You don't have enough plasma to perform this action!</span>")
return
user.add_plasma(-50)
var/mob/living/carbon/transfering_to = target
transfering_to.add_plasma(50)
to_chat(user, "<span class='noticealien'>You have transfered 50 plasma to [transfering_to].</span>")
to_chat(transfering_to, "<span class='noticealien'>[user] has transfered 50 plasma to you!</span>")
..()
@@ -0,0 +1,28 @@
/obj/effect/proc_holder/spell/alien_spell/whisper
name = "Whisper"
desc = "Whisper into a target's mind."
plasma_cost = 10
action_icon_state = "alien_whisper"
var/target
/obj/effect/proc_holder/spell/alien_spell/whisper/create_new_targeting() // Yeah this is copy and pasted code from cryoken and it's good enough
var/datum/spell_targeting/click/T = new()
T.allowed_type = /mob/living
T.click_radius = 0
T.try_auto_target = FALSE
T.selection_type = SPELL_SELECTION_RANGE
T.include_user = TRUE
return T
/obj/effect/proc_holder/spell/alien_spell/whisper/cast(list/targets, mob/living/carbon/user)
var/mob/living/target = targets[1]
var/msg = sanitize(input("Message:", "Alien Whisper") as text|null)
if(!msg)
revert_cast(user)
return
log_say("(AWHISPER to [key_name(target)]) [msg]", user)
to_chat(target, "<span class='noticealien'>You hear a strange, alien voice in your head...<span class='noticealien'> [msg]")
to_chat(user, "<span class='noticealien'>You said: [msg] to [target]</span>")
for(var/mob/dead/observer/ghosts in GLOB.player_list)
ghosts.show_message("<i>Alien message from <b>[user]</b> ([ghost_follow_link(user, ghost=ghosts)]) to <b>[target]</b> ([ghost_follow_link(target, ghost=ghosts)]): [msg]</i>")
+8 -3
View File
@@ -1,6 +1,7 @@
/obj/effect/proc_holder/spell/touch
var/hand_path = /obj/item/melee/touch_attack
var/obj/item/melee/touch_attack/attached_hand = null
var/on_remove_message = TRUE
invocation_type = "none" //you scream on connecting, not summoning
/obj/effect/proc_holder/spell/touch/create_new_targeting()
@@ -11,15 +12,16 @@
qdel(attached_hand)
cooldown_handler.revert_cast()
attached_hand = null
to_chat(user, "<span class='notice'>You draw the power out of your hand.</span>")
return 0
if(on_remove_message)
to_chat(user, "<span class='notice'>You draw the power out of your hand.</span>")
return FALSE
..()
/obj/effect/proc_holder/spell/touch/cast(list/targets, mob/user = usr)
for(var/mob/living/carbon/target in targets)
if(!attached_hand)
if(!ChargeHand(target))
return 0
return FALSE
while(attached_hand) //hibernate untill the spell is actually used
cooldown_handler.recharge_time++ // adds a tick onto the cooldown each tick
sleep(1)
@@ -27,6 +29,9 @@
/obj/effect/proc_holder/spell/touch/proc/ChargeHand(mob/living/carbon/user)
var/hand_handled = 1
attached_hand = new hand_path(src)
if(isalien(user))
user.put_in_hands(attached_hand)
return
if(user.hand) //left active hand
if(!user.equip_to_slot_if_possible(attached_hand, slot_l_hand, FALSE, TRUE))
if(!user.equip_to_slot_if_possible(attached_hand, slot_r_hand, FALSE, TRUE))