Skip to content

Commit

Permalink
Refactors Morphs into Basic Mobs (there is now a swag action for morp…
Browse files Browse the repository at this point in the history
…hification) (tgstation#77503)

I was bored, so did this. Probably one of the neatest refactors I've
done, sorry if there's some oddities because I was experimenting with
some other stuff in this so just tell me to clean them up whenever I
can.

Anyways, morphs are basic mobs now. We are able to easily refactor the
whole "eat items and corpses" stuff in the basic mob framework, but the
whole "morph into objects and people" turned out to be a bit trickier.
That was easily rectified with a datum mob cooldown action and
copy-pasting the old code into that code, as well as doing some nice
stuff with traits and signals to ensure the one-way communication from
the action to the mob.

Old Morph AI didn't seem to be existant whatsoever, they inappropriately
leveraged some old procs and I have no idea how to make it work with new
AI. They DEFINITELY don't spawn outside of admin interference/ the event
anymore, and will always be controlled by a player, so this shouldn't be
too bad of an issue. I gave them something to seem alive just in case
though, but I think adding legitimate prop-hunt AI would be such a
laborious task that I am unwilling to do it in this PR.

If admins want to add the ability for Ian to assume the form of the HoP,
they can do that now! The datum action cooldown is quite nice for simple
and basic mobs... but it is currently not compatible with carbons. That
is not within scope for this PR, but I am dwelling on ways to extend it
to carbon but they all sound really awfully bad.

Also morphs are smarter, and we tick another simple animal in need of
refactoring off the list.
:cl:
refactor: Morphs are now basic mobs with a nice new ability to help you
change forms rather than the old shift-click method, much more
intuitive.
admin: With the morph rework comes a new ability you can add to mobs,
"Assume Form". Feel free to add that to any simple or basic mob for le
funnies as Runtime turns into a pen or something.
/:cl:

~~Does anyone know if there's a (sane) way to alias a cooldown action as
a keypress? I can't think of a good way to retain the old shift-click
functionality, because that does feel _kinda_ nice, but I think it can
be lived without.~~ I added it. Kinda fugly but whatever.
  • Loading branch information
san7890 authored and dwasint committed Aug 22, 2023
1 parent 6c5c8c9 commit f1998f5
Show file tree
Hide file tree
Showing 15 changed files with 355 additions and 219 deletions.
3 changes: 3 additions & 0 deletions code/__DEFINES/dcs/signals/signals_action.dm
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,6 @@

///From /datum/action/vehicle/sealed/mecha/mech_toggle_safeties/proc/update_action_icon(): ()
#define COMSIG_MECH_SAFETIES_TOGGLE "mech_safeties_toggle"

/// From /datum/action/cooldown/mob_cooldown/assume_form/proc/assume_appearances(), sent to the action owner: (atom/movable/target)
#define COMSIG_ACTION_DISGUISED_APPEARANCE "mob_ability_disguise_appearance"
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
#define COMSIG_CLICK "atom_click"
///from base of atom/ShiftClick(): (/mob)
#define COMSIG_CLICK_SHIFT "shift_click"
#define COMPONENT_ALLOW_EXAMINATE (1<<0) //Allows the user to examinate regardless of client.eye.
#define COMPONENT_ALLOW_EXAMINATE (1<<0) //! Allows the user to examinate regardless of client.eye.
///from base of atom/CtrlClickOn(): (/mob)
#define COMSIG_CLICK_CTRL "ctrl_click"
///from base of atom/AltClick(): (/mob)
Expand Down
19 changes: 18 additions & 1 deletion code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,26 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_NOBLOOD "noblood"
#define TRAIT_NOMETABOLISM "no_metabolism"
// Use when you want a mob to be able to metabolize plasma temporarily (e.g. plasma fixation disease symptom)
#define TRAIT_PLASMA_LOVER_METABOLISM "plasma_lover_metabolism"
/// This just means that the carbon will always have functional liverless metabolism
#define TRAIT_LIVERLESS_METABOLISM "liverless_metabolism"
/// Humans with this trait cannot be turned into zombies
#define TRAIT_NO_ZOMBIFY "no_zombify"
/// Humans with this trait cannot be affected by changeling transformation stings
#define TRAIT_NO_TRANSFORMATION_STING "no_transformation_sting"
/// Carbons with this trait can't have their DNA copied by diseases nor changelings
#define TRAIT_NO_DNA_COPY "no_dna_copy"
/// Carbons with this trait can eat blood to regenerate their own blood volume, instead of injecting it
#define TRAIT_DRINKS_BLOOD "drinks_blood"
/// Mob is immune to clone (cellular) damage
#define TRAIT_NOCLONELOSS "no_cloneloss"
/// Mob is immune to toxin damage
#define TRAIT_TOXIMMUNE "toxin_immune"
/// Mob is immune to oxygen damage, does not need to breathe
#define TRAIT_NOBREATH "no_breath"
/// Mob is currently disguised as something else (like a morph being another mob or an object). Holds a reference to the thing that applied the trait.
#define TRAIT_DISGUISED "disguised"
/// Use when you want a mob to be able to metabolize plasma temporarily (e.g. plasma fixation disease symptom)
#define TRAIT_PLASMA_LOVER_METABOLISM "plasma_lover_metabolism"
#define TRAIT_EASYDISMEMBER "easy_dismember"
#define TRAIT_LIMBATTACHMENT "limb_attach"
#define TRAIT_NOLIMBDISABLE "no_limb_disable"
Expand Down
3 changes: 2 additions & 1 deletion code/_onclick/click.dm
Original file line number Diff line number Diff line change
Expand Up @@ -343,9 +343,10 @@

/atom/proc/ShiftClick(mob/user)
var/flags = SEND_SIGNAL(user, COMSIG_CLICK_SHIFT, src)
if(flags & COMSIG_MOB_CANCEL_CLICKON)
return
if(user.client && (user.client.eye == user || user.client.eye == user.loc || flags & COMPONENT_ALLOW_EXAMINATE))
user.examinate(src)
return

/**
* Ctrl click
Expand Down
87 changes: 87 additions & 0 deletions code/datums/actions/mobs/assume_form.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/// Allows a mob to assume the form of another item or mob.
/// Warning, this will likely shit the bricks if you add this action to anything more sophisticated than a basic mob- this isn't built for anything carbon-wise.
/datum/action/cooldown/mob_cooldown/assume_form
name = "Assume Form"
desc = "Choose something that you wish to blend into the environment as. Click on yourself to reset your appearance."
button_icon_state = "sniper_zoom"
background_icon_state = "bg_alien"
overlay_icon_state = "bg_alien_border"
ranged_mousepointer = 'icons/effects/mouse_pointers/supplypod_target.dmi'
check_flags = AB_CHECK_CONSCIOUS
cooldown_time = 1.5 SECONDS

/// Stuff that we can not disguise as.
var/static/list/blacklist_typecache = typecacheof(list(
/atom/movable/screen,
/obj/effect,
/obj/energy_ball,
/obj/narsie,
/obj/singularity,
))

/datum/action/cooldown/mob_cooldown/assume_form/Grant(mob/grant_to)
. = ..()
RegisterSignal(owner, COMSIG_LIVING_DEATH, PROC_REF(reset_appearances))

/datum/action/cooldown/mob_cooldown/assume_form/Remove(mob/remove_from)
reset_appearances()
UnregisterSignal(owner, COMSIG_LIVING_DEATH)
return ..()

/datum/action/cooldown/mob_cooldown/assume_form/Activate(atom/target_atom)
StartCooldown(360 SECONDS, 360 SECONDS)
determine_intent(target_atom)
StartCooldown()
return TRUE

/// Rapid proc to test if we can assume the form of a given atom. Returns TRUE if we can, FALSE if we can't. Done like this so we can be nice and explicit.
/datum/action/cooldown/mob_cooldown/assume_form/proc/can_assume_form(atom/target_atom)
if(is_type_in_typecache(target_atom, blacklist_typecache) || (!isobj(target_atom) && !ismob(target_atom)))
return FALSE

return TRUE

/// Determines what our user meant by their action. If they clicked on themselves, we reset our appearance. Otherwise, we assume the appearance of the clicked-on item.
/datum/action/cooldown/mob_cooldown/assume_form/proc/determine_intent(atom/target_atom)
if(!can_assume_form(target_atom))
return

if(target_atom == owner)
reset_appearances()
return

assume_appearances(target_atom)

/// Assumes the appearance of a desired movable and applies it to our mob. Target is the movable in question.
/datum/action/cooldown/mob_cooldown/assume_form/proc/assume_appearances(atom/movable/target_atom)
owner.appearance = target_atom.appearance
owner.copy_overlays(target_atom)
owner.alpha = max(target_atom.alpha, 150) //fucking chameleons
owner.transform = initial(target_atom.transform)
owner.pixel_x = target_atom.base_pixel_x
owner.pixel_y = target_atom.base_pixel_y

// important: do this at the very end because we might have SIGNAL_ADDTRAIT for this on the mob that's dependent on the above logic
SEND_SIGNAL(owner, COMSIG_ACTION_DISGUISED_APPEARANCE, target_atom)
ADD_TRAIT(owner, TRAIT_DISGUISED, REF(src))

/// Resets the appearances of the mob to the default.
/datum/action/cooldown/mob_cooldown/assume_form/proc/reset_appearances()
SIGNAL_HANDLER

if(!HAS_TRAIT(owner, TRAIT_DISGUISED))
return // in case we're being invoked on death and we aren't disguised (or we just click on ourselves randomly), no need to do this additional work.

owner.animate_movement = SLIDE_STEPS
owner.maptext = null
owner.alpha = initial(owner.alpha)
owner.color = initial(owner.color)
owner.desc = initial(owner.desc)

owner.name = initial(owner.name)
owner.icon = initial(owner.icon)
owner.icon_state = initial(owner.icon_state)
owner.cut_overlays()

// important: do this very end because we might have SIGNAL_REMOVETRAIT for this on the mob that's dependent on the above logic
REMOVE_TRAIT(owner, TRAIT_DISGUISED, REF(src))
2 changes: 1 addition & 1 deletion code/datums/diseases/transformation.dm
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@
stage3 = list(span_danger("Your appendages are melting away."), span_danger("Your limbs begin to lose their shape."))
stage4 = list(span_danger("You're ravenous."))
stage5 = list(span_danger("You have become a morph."))
new_form = /mob/living/simple_animal/hostile/morph
new_form = /mob/living/basic/morph
infectable_biotypes = MOB_ORGANIC|MOB_MINERAL|MOB_UNDEAD //magic!
transformed_antag_datum = /datum/antagonist/morph

Expand Down
9 changes: 5 additions & 4 deletions code/datums/memory/_memory.dm
Original file line number Diff line number Diff line change
Expand Up @@ -235,18 +235,20 @@
/mob/living/basic/carp/magic/chaos,
/mob/living/basic/cow,
/mob/living/basic/cow/wisdom,
/mob/living/basic/giant_spider,
/mob/living/basic/giant_spider/hunter,
/mob/living/basic/mining/goliath,
/mob/living/basic/crab,
/mob/living/basic/headslug,
/mob/living/basic/killer_tomato,
/mob/living/basic/lizard,
/mob/living/basic/mining/goliath,
/mob/living/basic/morph,
/mob/living/basic/mouse,
/mob/living/basic/mushroom,
/mob/living/basic/pet/dog/breaddog,
/mob/living/basic/pet/dog/corgi,
/mob/living/basic/pet/dog/pug,
/mob/living/basic/pet/fox,
/mob/living/basic/spider/giant,
/mob/living/basic/spider/giant/hunter,
/mob/living/basic/statue,
/mob/living/basic/stickman,
/mob/living/basic/stickman/dog,
Expand All @@ -258,7 +260,6 @@
/mob/living/simple_animal/hostile/blob/blobbernaut/independent,
/mob/living/simple_animal/hostile/gorilla,
/mob/living/simple_animal/hostile/megafauna/dragon/lesser,
/mob/living/simple_animal/hostile/morph,
/mob/living/simple_animal/hostile/retaliate/goat,
/mob/living/simple_animal/parrot,
/mob/living/simple_animal/pet/cat,
Expand Down
12 changes: 6 additions & 6 deletions code/modules/events/ghost_role/morph_event.dm
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@
if(isnull(spawn_loc))
return MAP_ERROR

var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(spawn_loc)
player_mind.transfer_to(S)
var/mob/living/basic/morph/corpus_accipientis = new(spawn_loc)
player_mind.transfer_to(corpus_accipientis)
player_mind.set_assigned_role(SSjob.GetJobType(/datum/job/morph))
player_mind.special_role = ROLE_MORPH
player_mind.add_antag_datum(/datum/antagonist/morph)
SEND_SOUND(S, sound('sound/magic/mutate.ogg'))
message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a morph by an event.")
S.log_message("was spawned as a morph by an event.", LOG_GAME)
spawned_mobs += S
SEND_SOUND(corpus_accipientis, sound('sound/magic/mutate.ogg'))
message_admins("[ADMIN_LOOKUPFLW(corpus_accipientis)] has been made into a morph by an event.")
corpus_accipientis.log_message("was spawned as a morph by an event.", LOG_GAME)
spawned_mobs += corpus_accipientis
return SUCCESSFUL_SPAWN
2 changes: 1 addition & 1 deletion code/modules/mapfluff/ruins/objects_and_mobs/sin_ruins.dm
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@
return TRUE
else
to_chat(H, span_warning("You're repulsed by even looking at [src]. Only a pig could force themselves to go through it."))
if(istype(mover, /mob/living/simple_animal/hostile/morph))
if(istype(mover, /mob/living/basic/morph))
return TRUE

//can't be bothered to do sloth right now, will make later
Expand Down
Loading

0 comments on commit f1998f5

Please sign in to comment.