Files
Bubberstation/code/datums/mutations/hulk.dm
carlarctg 66b8748091 Adds Summon Simians & Buffs/QoLs Mutate (#77196)
## About The Pull Request

Adds Summon Simians, a spell that summons four monkeys or lesser
gorillas, with the amount increasing per upgrade. The monkeys have
various fun gear depending on how lucky you get and how leveled the
spell is. If the spell is maximum level, it only summons normal
gorillas.

Added further support for nonhuman robed casting: Monkeys, cyborgs, and
drones can all now cast robed spells as long as they're wearing a
wizardly hat as well.

Made monkeys able to wield things again.

Wizard Mutate spell works on non-human races. It also gives you
Gigantism now (funny). If the Race can't support tinted bodyparts, your
whole sprite is temporarily turned green.

Made Laser eyes projectiles a subtype of actual lasers, which has
various properties such as on-hit effects and upping the damage to 30.

Improved some monkey AI code.

## Why It's Good For The Game

> Adds Summon Simians, a spell that summons four monkeys or lesser
gorillas, with the amount increasing per upgrade. The monkeys have
various fun gear depending on how lucky you get and how leveled the
spell is. If the spell is maximum level, it only summons normal
gorillas.

It's criminal we don't have a monky spell, and this is a really fun spin
on it. Total chaos, but total monky chaos. It's surprisingly strong,
but! it can very well backfire if you stay near the angry monkeys too
long and your protection fades away. Unless you become a monkey
yourself!!

> Wizard Mutate spell works on non-human races. 

This spell is great but it's hampered by the mutation's human
requirement, which is reasonable in normal gameplay. Wizards don't need
to care about that, and the human restriction hinders a lot of possible
gimmicks, so off it goes. Also, wizard hulk does't cause chunky fingers
for similar reasons

> Made Laser eyes projectiles a subtype of actual lasers, which has
various properties such as on-hit effects and upping the damage to 30.

Don't really caer about the damage so much, this is more so that it has
effects such as on-hit visuals. Can lower the damage if required, but
honestly anything that competes against troll mjolnir is good.

> Added further support for nonhuman robed casting: Monkeys, cyborgs,
and drones can all now cast robed spells as long as they're wearing a
wizardly hat as well.

SS13 is known for 'The Dev Team Thinks of Everything' and I believe this
is a sorely lacking part of this or something. It's funny.
I want to see a monkey wizard.

> Made monkeys able to wield things again.

I really don't know why this was a thing and it was breaking my axe and
spear wielding primal monkeys. Like, why?

## Changelog

🆑
add: Adds Summon Simians, a spell that summons four monkeys or lesser
gorillas, with the amount increasing per upgrade. The monkeys have
various fun gear depending on how lucky you get and how leveled the
spell is. If the spell is maximum level, it only summons normal
gorillas.
balance: Wizard Mutate spell works on non-human races. It also gives you
Gigantism now (funny). If the Race can't support tinted bodyparts, your
whole sprite is temporarily turned green.
balance: Made Laser eyes projectiles a subtype of actual lasers, which
has various properties such as on-hit effects and upping the damage to
30.
add: Added further support for nonhuman robed casting: Monkeys, cyborgs,
and drones can all now cast robed spells as long as they're wearing a
wizardly hat as well.
balance: Made monkeys able to wield two-handed things again.
/🆑

---------

Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
2023-08-09 21:28:46 +01:00

273 lines
12 KiB
Plaintext

//Hulk turns your skin green, makes you strong, and allows you to shrug off stun effect.
/datum/mutation/human/hulk
name = "Hulk"
desc = "A poorly understood genome that causes the holder's muscles to expand, inhibit speech and gives the person a bad skin condition."
quality = POSITIVE
locked = TRUE
difficulty = 16
text_gain_indication = "<span class='notice'>Your muscles hurt!</span>"
species_allowed = list(SPECIES_HUMAN) //no skeleton/lizard hulk
health_req = 25
instability = 40
var/scream_delay = 50
var/last_scream = 0
/// List of traits to add/remove when someone gets this mutation.
var/list/mutation_traits = list(
TRAIT_CHUNKYFINGERS,
TRAIT_HULK,
TRAIT_IGNOREDAMAGESLOWDOWN,
TRAIT_PUSHIMMUNE,
TRAIT_STUNIMMUNE,
)
/datum/mutation/human/hulk/on_acquiring(mob/living/carbon/human/owner)
if(..())
return
owner.add_traits(mutation_traits, GENETIC_MUTATION)
for(var/obj/item/bodypart/part as anything in owner.bodyparts)
part.variable_color = "#00aa00"
owner.update_body_parts()
owner.add_mood_event("hulk", /datum/mood_event/hulk)
RegisterSignal(owner, COMSIG_HUMAN_EARLY_UNARMED_ATTACK, PROC_REF(on_attack_hand))
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(handle_speech))
RegisterSignal(owner, COMSIG_MOB_CLICKON, PROC_REF(check_swing))
/datum/mutation/human/hulk/proc/on_attack_hand(mob/living/carbon/human/source, atom/target, proximity, modifiers)
SIGNAL_HANDLER
if(!proximity)
return
if(!source.combat_mode || LAZYACCESS(modifiers, RIGHT_CLICK))
return
if(target.attack_hulk(owner))
if(world.time > (last_scream + scream_delay))
last_scream = world.time
INVOKE_ASYNC(src, PROC_REF(scream_attack), source)
log_combat(source, target, "punched", "hulk powers")
source.do_attack_animation(target, ATTACK_EFFECT_SMASH)
source.changeNext_move(CLICK_CD_MELEE)
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/mutation/human/hulk/proc/scream_attack(mob/living/carbon/human/source)
source.say("WAAAAAAAAAAAAAAGH!", forced="hulk")
/**
*Checks damage of a hulk's arm and applies bone wounds as necessary.
*
*Called by specific atoms being attacked, such as walls. If an atom
*does not call this proc, than punching that atom will not cause
*arm breaking (even if the atom deals recoil damage to hulks).
*Arguments:
*arg1 is the arm to evaluate damage of and possibly break.
*/
/datum/mutation/human/hulk/proc/break_an_arm(obj/item/bodypart/arm)
switch(arm.brute_dam)
if(45 to 50)
arm.force_wound_upwards(/datum/wound/blunt/critical, wound_source = "hulk smashing")
if(41 to 45)
arm.force_wound_upwards(/datum/wound/blunt/severe, wound_source = "hulk smashing")
if(35 to 41)
arm.force_wound_upwards(/datum/wound/blunt/moderate, wound_source = "hulk smashing")
/datum/mutation/human/hulk/on_life(seconds_per_tick, times_fired)
if(owner.health < owner.crit_threshold)
on_losing(owner)
to_chat(owner, span_danger("You suddenly feel very weak."))
/datum/mutation/human/hulk/on_losing(mob/living/carbon/human/owner)
if(..())
return
owner.remove_traits(mutation_traits, GENETIC_MUTATION)
for(var/obj/item/bodypart/part as anything in owner.bodyparts)
part.variable_color = null
owner.update_body_parts()
owner.clear_mood_event("hulk")
UnregisterSignal(owner, COMSIG_HUMAN_EARLY_UNARMED_ATTACK)
UnregisterSignal(owner, COMSIG_MOB_SAY)
UnregisterSignal(owner, COMSIG_MOB_CLICKON)
/datum/mutation/human/hulk/proc/handle_speech(datum/source, list/speech_args)
SIGNAL_HANDLER
var/message = speech_args[SPEECH_MESSAGE]
if(message)
message = "[replacetext(message, ".", "!")]!!"
speech_args[SPEECH_MESSAGE] = message
// the reason we don't just uppertext(message) in this proc is so that our hulk speech
// can uppercase all other speech moidifiers after they are done (by returning COMPONENT_UPPERCASE_SPEECH)
return COMPONENT_UPPERCASE_SPEECH
/// How many steps it takes to throw the mob
#define HULK_TAILTHROW_STEPS 28
/// Run a barrage of checks to see if any given click is actually able to swing
/datum/mutation/human/hulk/proc/check_swing(mob/living/carbon/human/user, atom/clicked_atom, list/modifiers)
SIGNAL_HANDLER
/// Basically, we only proceed if we're in throw mode with a tailed carbon in our grasp with at least a neck grab and we're not restrained in some way
if(LAZYACCESS(modifiers, ALT_CLICK) || LAZYACCESS(modifiers, SHIFT_CLICK) || LAZYACCESS(modifiers, CTRL_CLICK) || LAZYACCESS(modifiers, MIDDLE_CLICK))
return
if(!user.throw_mode || user.get_active_held_item() || user.zone_selected != BODY_ZONE_PRECISE_GROIN)
return
if(user.grab_state < GRAB_NECK || !iscarbon(user.pulling) || user.buckled || user.incapacitated())
return
var/mob/living/carbon/possible_throwable = user.pulling
if(!possible_throwable.get_organ_slot(ORGAN_SLOT_EXTERNAL_TAIL))
return
if(ishuman(possible_throwable))
var/mob/living/carbon/human/human_throwable = possible_throwable
if(human_throwable.wear_suit && (human_throwable.wear_suit.flags_inv & HIDEJUMPSUIT))
to_chat(user, span_warning("You can't reach [human_throwable]'s tail through [human_throwable.p_their()] [human_throwable.wear_suit.name]!"))
return
user.face_atom(clicked_atom)
INVOKE_ASYNC(src, PROC_REF(setup_swing), user, possible_throwable)
return(COMSIG_MOB_CANCEL_CLICKON)
/// Do a short 2 second do_after before starting the actual swing
/datum/mutation/human/hulk/proc/setup_swing(mob/living/carbon/human/the_hulk, mob/living/carbon/yeeted_person)
var/original_dir = the_hulk.dir // so no matter if the hulk tries to mess up their direction, they always face where they started when they throw
yeeted_person.forceMove(the_hulk.loc)
yeeted_person.setDir(get_dir(yeeted_person, the_hulk))
log_combat(the_hulk, yeeted_person, "has started swinging by tail")
yeeted_person.Stun(2 SECONDS)
yeeted_person.visible_message(span_danger("[the_hulk] starts grasping [yeeted_person] by the tail..."), \
span_userdanger("[the_hulk] begins grasping your tail!"), span_hear("You hear aggressive shuffling!"), null, the_hulk)
to_chat(the_hulk, span_danger("You start grasping [yeeted_person] by the tail..."))
if(!do_after(the_hulk, 2 SECONDS, yeeted_person))
yeeted_person.visible_message(span_danger("[yeeted_person] breaks free of [the_hulk]'s grasp!"), \
span_userdanger("You break free from [the_hulk]'s grasp!"), span_hear("You hear aggressive shuffling!"), null, the_hulk)
to_chat(the_hulk, span_danger("You lose your grasp on [yeeted_person]'s tail!"))
return
// we're officially a-go!
yeeted_person.Paralyze(8 SECONDS)
yeeted_person.visible_message(span_danger("[the_hulk] starts spinning [yeeted_person] around by [yeeted_person.p_their()] tail!"), \
span_userdanger("[the_hulk] starts spinning you around by your tail!"), span_hear("You hear wooshing sounds!"), null, the_hulk)
to_chat(the_hulk, span_danger("You start spinning [yeeted_person] around by [yeeted_person.p_their()] tail!"))
the_hulk.emote("scream")
yeeted_person.emote("scream")
swing_loop(the_hulk, yeeted_person, 0, original_dir)
/**
* Does the animations for the hulk swing loop
*
* This code is based in part on the wrestling swing code ported from goon, see [code/datums/martial/wrestling.dm]
* credit to: cogwerks, pistoleer, spyguy, angriestibm, marquesas, and stuntwaffle.
* For each step of the swinging, with the delay getting shorter along the way. Checks to see we still have them in our grasp at each step.
*/
/datum/mutation/human/hulk/proc/swing_loop(mob/living/carbon/human/the_hulk, mob/living/carbon/yeeted_person, step, original_dir)
if(!yeeted_person || !the_hulk || the_hulk.incapacitated())
return
if(get_dist(the_hulk, yeeted_person) > 1 || !isturf(the_hulk.loc) || !isturf(yeeted_person.loc))
to_chat(the_hulk, span_warning("You lose your grasp on [yeeted_person]!"))
return
var/delay = 5
switch (step)
if(24 to INFINITY)
delay = 0.1
if(20 to 23)
delay = 0.5
if(16 to 19)
delay = 1
if(13 to 15)
delay = 2
if(8 to 12)
delay = 3
if(4 to 7)
delay = 3.5
if(0 to 3)
delay = 4
the_hulk.setDir(turn(the_hulk.dir, 90))
var/turf/current_spin_turf = yeeted_person.loc
var/turf/intermediate_spin_turf = get_step(yeeted_person, the_hulk.dir) // the diagonal
var/turf/next_spin_turf = get_step(the_hulk, the_hulk.dir)
var/direction = get_dir(current_spin_turf, intermediate_spin_turf)
if((isturf(current_spin_turf) && current_spin_turf.Exit(yeeted_person, direction)) && (isturf(next_spin_turf) && next_spin_turf.Enter(yeeted_person)))
yeeted_person.forceMove(next_spin_turf)
yeeted_person.face_atom(the_hulk)
var/list/collateral_check = intermediate_spin_turf.contents + next_spin_turf.contents // check the cardinal and the diagonal tiles we swung past
var/turf/collat_throw_target = get_edge_target_turf(yeeted_person, get_dir(current_spin_turf, next_spin_turf)) // what direction we're swinging
for(var/mob/living/collateral_mob in collateral_check)
if(!collateral_mob.density || collateral_mob == yeeted_person)
continue
yeeted_person.adjustBruteLoss(step*0.5)
playsound(collateral_mob,'sound/weapons/punch1.ogg',50,TRUE)
log_combat(the_hulk, collateral_mob, "has smacked with tail swing victim")
log_combat(the_hulk, yeeted_person, "has smacked this person into someone while tail swinging") // i have no idea how to better word this
if(collateral_mob == the_hulk) // if the hulk moves wrong and crosses himself
the_hulk.visible_message(span_warning("[the_hulk] smacks [the_hulk.p_them()]self with [yeeted_person]!"), span_userdanger("You end up smacking [yeeted_person] into yourself!"), ignored_mobs = yeeted_person)
to_chat(yeeted_person, span_userdanger("[the_hulk] smacks you into [the_hulk.p_them()]self, turning you free!"))
the_hulk.adjustBruteLoss(step)
return
yeeted_person.visible_message(span_warning("[the_hulk] swings [yeeted_person] directly into [collateral_mob], sending [collateral_mob.p_them()] flying!"), \
span_userdanger("You're smacked into [collateral_mob]!"), ignored_mobs = collateral_mob)
to_chat(collateral_mob, span_userdanger("[the_hulk] swings [yeeted_person] directly into you, sending you flying!"))
collateral_mob.adjustBruteLoss(step*0.5)
collateral_mob.throw_at(collat_throw_target, round(step * 0.25) + 1, round(step * 0.25) + 1)
step -= 5
delay += 5
step++
if(step >= HULK_TAILTHROW_STEPS)
finish_swing(the_hulk, yeeted_person, original_dir)
else if(step < 0)
the_hulk.visible_message(span_danger("[the_hulk] loses [the_hulk.p_their()] momentum on [yeeted_person]!"), span_warning("You lose your momentum on swinging [yeeted_person]!"), ignored_mobs = yeeted_person)
to_chat(yeeted_person, span_userdanger("[the_hulk] loses [the_hulk.p_their()] momentum and lets go of you!"))
else
addtimer(CALLBACK(src, PROC_REF(swing_loop), the_hulk, yeeted_person, step, original_dir), delay)
/// Time to toss the victim at high speed
/datum/mutation/human/hulk/proc/finish_swing(mob/living/carbon/human/the_hulk, mob/living/carbon/yeeted_person, original_dir)
if(!yeeted_person || !the_hulk || the_hulk.incapacitated())
return
if(get_dist(the_hulk, yeeted_person) > 1 || !isturf(the_hulk.loc) || !isturf(yeeted_person.loc))
to_chat(the_hulk, span_warning("You lose your grasp on [yeeted_person]!"))
return
the_hulk.setDir(original_dir)
yeeted_person.forceMove(the_hulk.loc) // Maybe this will help with the wallthrowing bug.
yeeted_person.visible_message(span_danger("[the_hulk] throws [yeeted_person]!"), \
span_userdanger("You're thrown by [the_hulk]!"), span_hear("You hear aggressive shuffling and a loud thud!"), null, the_hulk)
to_chat(the_hulk, span_danger("You throw [yeeted_person]!"))
playsound(the_hulk.loc, SFX_SWING_HIT, 50, TRUE)
var/turf/T = get_edge_target_turf(the_hulk, the_hulk.dir)
if(!isturf(T))
return
if(!yeeted_person.stat)
yeeted_person.emote("scream")
yeeted_person.throw_at(T, 10, 6, the_hulk, TRUE, TRUE)
log_combat(the_hulk, yeeted_person, "has thrown by tail")
/datum/mutation/human/hulk/wizardly
species_allowed = null //yes skeleton/lizard hulk - note that species that dont have skintone changing (like skellies) get custom handling
health_req = 0
instability = 0
scream_delay = 2.5 SECONDS // halved to be more annoying (spell doesn't last long anyways)
/// List of traits to add/remove when someone gets this mutation.
mutation_traits = list(
TRAIT_HULK,
TRAIT_IGNOREDAMAGESLOWDOWN,
TRAIT_PUSHIMMUNE,
TRAIT_STUNIMMUNE,
) // no chunk
#undef HULK_TAILTHROW_STEPS