Skip to content

Commit

Permalink
[MIRROR] Cardboard cutouts are now tactical. (#1013)
Browse files Browse the repository at this point in the history
* Cardboard cutouts are now tactical. (#81245)

## About The Pull Request
Basically, this means players holding cardboard cutouts will now assume
their appearance, just like for potted plants. Good for pranking.

I've had to tweak the tactical component and the waddling element a bit
to get them to work as intended while dealing with the multiple sources
of the waddling element.

## Why It's Good For The Game
![](https://media1.tenor.com/m/X1GimXmuByYAAAAd/tom-cruise-doorbell.gif)

## Changelog

:cl:
add: Players holding cardboard cutouts will now assume their appearance,
just like for potted plants.
/:cl:

* Cardboard cutouts are now tactical.

---------

Co-authored-by: Ghom <[email protected]>
  • Loading branch information
2 people authored and StealsThePRs committed Feb 17, 2024
1 parent e3578ab commit 23b3561
Show file tree
Hide file tree
Showing 17 changed files with 122 additions and 52 deletions.
4 changes: 4 additions & 0 deletions code/__DEFINES/traits/declarations.dm
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_DEL_ON_SPACE_DUMP "del_on_hyperspace_leave"
/// We can walk up or around cliffs, or at least we don't fall off of it
#define TRAIT_CLIFF_WALKER "cliff_walker"
/// This means the user is currently holding/wearing a "tactical camouflage" item (like a potted plant).
#define TRAIT_TACTICALLY_CAMOUFLAGED "tactically_camouflaged"
/// Gets double arcade prizes
#define TRAIT_GAMERGOD "gamer-god"
#define TRAIT_GIANT "giant"
Expand Down Expand Up @@ -632,6 +634,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai

/// Used by the honkspam element to avoid spamming the sound. Amusing considering its name.
#define TRAIT_HONKSPAMMING "trait_honkspamming"
/// Required by the waddling element since there are multiple sources of it.
#define TRAIT_WADDLING "trait_waddling"

///Used for managing KEEP_TOGETHER in [/atom/var/appearance_flags]
#define TRAIT_KEEP_TOGETHER "keep-together"
Expand Down
2 changes: 2 additions & 0 deletions code/_globalvars/traits/_traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_UNIQUE_IMMERSE" = TRAIT_UNIQUE_IMMERSE,
"TRAIT_VOIDSTORM_IMMUNE" = TRAIT_VOIDSTORM_IMMUNE,
"TRAIT_WAS_RENAMED" = TRAIT_WAS_RENAMED,
"TRAIT_WADDLING" = TRAIT_WADDLING,
"TRAIT_WEATHER_IMMUNE" = TRAIT_WEATHER_IMMUNE,
"TRAIT_CHASM_STOPPER" = TRAIT_CHASM_STOPPER,
),
Expand Down Expand Up @@ -449,6 +450,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_TACKLING_FRAIL_ATTACKER" = TRAIT_TACKLING_FRAIL_ATTACKER,
"TRAIT_TACKLING_TAILED_DEFENDER" = TRAIT_TACKLING_TAILED_DEFENDER,
"TRAIT_TACKLING_WINGED_ATTACKER" = TRAIT_TACKLING_WINGED_ATTACKER,
"TRAIT_TACTICALLY_CAMOUFLAGED" = TRAIT_TACTICALLY_CAMOUFLAGED,
"TRAIT_TAGGER" = TRAIT_TAGGER,
"TRAIT_TEMPORARY_BODY" = TRAIT_TEMPORARY_BODY,
"TRAIT_TENACIOUS" = TRAIT_TENACIOUS,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
return AI_CONTROLLER_INCOMPATIBLE
var/obj/machinery/vending/vendor_pawn = new_pawn
vendor_pawn.tiltable = FALSE //Not manually tiltable by hitting it anymore. We are now aggressively doing it ourselves.
vendor_pawn.AddElement(/datum/element/waddling)
vendor_pawn.AddElementTrait(TRAIT_WADDLING, REF(src), /datum/element/waddling)
vendor_pawn.AddElement(/datum/element/footstep, FOOTSTEP_OBJ_MACHINE, 1, -6, sound_vary = TRUE)
vendor_pawn.squish_damage = 15
return ..() //Run parent at end

/datum/ai_controller/vending_machine/UnpossessPawn(destroy)
var/obj/machinery/vending/vendor_pawn = pawn
vendor_pawn.tiltable = TRUE
vendor_pawn.RemoveElement(/datum/element/waddling)
REMOVE_TRAIT(vendor_pawn, TRAIT_WADDLING, REF(src))
vendor_pawn.squish_damage = initial(vendor_pawn.squish_damage)
RemoveElement(/datum/element/footstep, FOOTSTEP_OBJ_MACHINE, 1, -6, sound_vary = TRUE)
return ..() //Run parent at end
Expand Down
54 changes: 35 additions & 19 deletions code/datums/components/tactical.dm
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
///A simple component that replacess the user's appearance with that of the parent item when equipped.
/datum/component/tactical
///The allowed slot(s) for the effect.
var/allowed_slot
///A cached of where the item is currently equipped.
var/current_slot

/datum/component/tactical/Initialize(allowed_slot)
Expand All @@ -11,50 +14,63 @@
/datum/component/tactical/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(modify))
RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(unmodify))
RegisterSignal(parent, COMSIG_ATOM_UPDATED_ICON, PROC_REF(tactical_update))
var/obj/item/item = parent
if(ismob(item.loc))
var/mob/holder = item.loc
modify(item, holder, holder.get_slot_by_item(item))

/datum/component/tactical/UnregisterFromParent()
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
UnregisterSignal(parent, list(
COMSIG_ITEM_EQUIPPED,
COMSIG_ITEM_DROPPED,
COMSIG_ATOM_UPDATED_ICON,
))
unmodify()

/datum/component/tactical/Destroy()
unmodify()
return ..()

/datum/component/tactical/proc/on_z_move(datum/source)
SIGNAL_HANDLER
var/obj/item/master = parent
if(!ismob(master.loc))
return
var/old_slot = current_slot
unmodify(master, master.loc)
modify(master, master.loc, old_slot)

/datum/component/tactical/proc/modify(obj/item/source, mob/user, slot)
SIGNAL_HANDLER

if(allowed_slot && !(slot & allowed_slot))
unmodify()
if(current_slot)
unmodify(source, user)
return

if(current_slot) //If the current slot is set, this means the icon was updated or the item changed z-levels.
user.remove_alt_appearance("sneaking_mission[REF(src)]")
else
RegisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(tactical_update))

current_slot = slot

var/obj/item/master = parent
var/image/I = image(icon = master.icon, icon_state = master.icon_state, loc = user)
I.copy_overlays(master)
I.override = TRUE
source.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission", I)
I.layer = ABOVE_MOB_LAYER
RegisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED, PROC_REF(on_z_move))
var/image/image = image(master, loc = user)
image.copy_overlays(master)
image.override = TRUE
image.layer = ABOVE_MOB_LAYER
image.plane = FLOAT_PLANE
source.add_alt_appearance(/datum/atom_hud/alternate_appearance/basic/everyone, "sneaking_mission[REF(src)]", image)

/datum/component/tactical/proc/unmodify(obj/item/source, mob/user)
SIGNAL_HANDLER

var/obj/item/master = source || parent
var/obj/item/master = parent
if(!user)
if(!ismob(master.loc))
return
user = master.loc

user.remove_alt_appearance("sneaking_mission")
user.remove_alt_appearance("sneaking_mission[REF(src)]")
current_slot = null
UnregisterSignal(parent, COMSIG_MOVABLE_Z_CHANGED)

/datum/component/tactical/proc/tactical_update(datum/source)
SIGNAL_HANDLER
var/obj/item/master = parent
if(!ismob(master.loc))
return
modify(master, master.loc, current_slot)
28 changes: 27 additions & 1 deletion code/datums/elements/_element.dm
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
if(element_flags & ELEMENT_DETACH_ON_HOST_DESTROY)
RegisterSignal(target, COMSIG_QDELETING, PROC_REF(OnTargetDelete), override = TRUE)

/datum/element/proc/OnTargetDelete(datum/source, force)
/datum/element/proc/OnTargetDelete(datum/source)
SIGNAL_HANDLER
Detach(source)

Expand Down Expand Up @@ -75,3 +75,29 @@
ele.Detach(arglist(arguments))
else
ele.Detach(src)

/**
* Used to manage (typically non_bespoke) elements with multiple sources through traits
* so we don't have to make them a components again.
* The element will be later removed once all trait sources are gone, there's no need of a
* "RemoveElementTrait" counterpart.
*/
/datum/proc/AddElementTrait(trait, source, datum/element/eletype, ...)
if(!ispath(eletype, /datum/element))
CRASH("AddElementTrait called, but [eletype] is not of a /datum/element path")
ADD_TRAIT(src, trait, source)
if(HAS_TRAIT_NOT_FROM(src, trait, source))
return
var/list/arguments = list(eletype)
/// 3 is the length of fixed args of this proc, any further one is passed down to AddElement.
if(length(args) > 3)
arguments += args.Copy(4)
/// We actually pass down a copy of the arguments since it's manipulated by the end of the proc.
_AddElement(arguments.Copy())
var/datum/ele = SSdcs.GetElement(arguments)
ele.RegisterSignal(src, SIGNAL_REMOVETRAIT(trait), TYPE_PROC_REF(/datum/element, _detach_on_trait_removed))

/datum/element/proc/_detach_on_trait_removed(datum/source, trait)
SIGNAL_HANDLER
Detach(source)
UnregisterSignal(source, SIGNAL_REMOVETRAIT(trait))
2 changes: 2 additions & 0 deletions code/datums/elements/waddling.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
. = ..()
if(!ismovable(target))
return ELEMENT_INCOMPATIBLE
if(!HAS_TRAIT(target, TRAIT_WADDLING))
stack_trace("[type] added to [target] without adding TRAIT_WADDLING first. Please use AddElementTrait instead.")
RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(Waddle))

/datum/element/waddling/Detach(datum/source)
Expand Down
43 changes: 33 additions & 10 deletions code/game/objects/items/cardboard_cutouts.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,19 @@
var/deceptive = FALSE
/// What cutout datum we spawn at the start? Uses the name, not the path.
var/starting_cutout
/// Reference to the tactical component that should be deleted when the cutout is toppled.
var/datum/component/tactical/tacticool

/obj/item/cardboard_cutout/Initialize(mapload)
. = ..()
if(starting_cutout)
return INITIALIZE_HINT_LATELOAD
if(!pushed_over)
AddComponent(/datum/component/tactical)

/obj/item/cardboard_cutout/Destroy()
tacticool = null
return ..()

/obj/item/cardboard_cutout/LateInitialize()
ASSERT(!isnull(starting_cutout))
Expand All @@ -33,6 +41,8 @@
ASSERT(!isnull(cutout), "No cutout found with name [starting_cutout]")

cutout.apply(src)
if(!pushed_over)
tacticool = AddComponent(/datum/component/tactical)

//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/cardboard_cutout/attack_hand(mob/living/user, list/modifiers)
Expand All @@ -42,12 +52,22 @@
playsound(src, 'sound/weapons/genhit.ogg', 50, TRUE)
push_over()

/obj/item/cardboard_cutout/equipped(mob/living/user, slot)
. = ..()
//Because of the tactical element, the user won't tilt left and right, but it'll still hop.
user.AddElementTrait(TRAIT_WADDLING, REF(src), /datum/element/waddling)

/obj/item/cardboard_cutout/dropped(mob/living/user)
. = ..()
REMOVE_TRAIT(user, TRAIT_WADDLING, REF(src))

/obj/item/cardboard_cutout/proc/push_over()
appearance = initial(appearance)
desc = "[initial(desc)] It's been pushed over."
icon_state = "cutout_pushed_over"
remove_atom_colour(FIXED_COLOUR_PRIORITY)
pushed_over = TRUE
QDEL_NULL(tacticool)

/obj/item/cardboard_cutout/attack_self(mob/living/user)
if(!pushed_over)
Expand All @@ -57,6 +77,7 @@
icon = initial(icon)
icon_state = initial(icon_state) //This resets a cutout to its blank state - this is intentional to allow for resetting
pushed_over = FALSE
tacticool = AddComponent(/datum/component/tactical)

/obj/item/cardboard_cutout/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/toy/crayon))
Expand Down Expand Up @@ -100,7 +121,7 @@
for (var/datum/cardboard_cutout/cutout_subtype as anything in subtypesof(/datum/cardboard_cutout))
var/datum/cardboard_cutout/cutout = get_cardboard_cutout_instance(cutout_subtype)
appearances_by_name[cutout.name] = cutout
possible_appearances[cutout.name] = image(icon = cutout.applied_appearance)
possible_appearances[cutout.name] = image(icon = cutout.preview_appearance)

var/new_appearance = show_radial_menu(user, src, possible_appearances, custom_check = CALLBACK(src, PROC_REF(check_menu), user, crayon), radius = 36, require_near = TRUE)
if(!new_appearance)
Expand Down Expand Up @@ -144,19 +165,16 @@
return FALSE
return TRUE

// Cutouts always face forward
/obj/item/cardboard_cutout/setDir(newdir)
SHOULD_CALL_PARENT(FALSE)
return

/obj/item/cardboard_cutout/adaptive //Purchased by Syndicate agents, these cutouts are indistinguishable from normal cutouts but aren't discolored when their appearance is changed
deceptive = TRUE

/datum/cardboard_cutout
/// Name of the cutout, used for radial selection and the global list.
var/name = "Boardjak"
/// The appearance we apply to the cardboard cutout.
var/mutable_appearance/applied_appearance = null
/// The appearance of the cardboard cutout that we show in the radial menu.
var/mutable_appearance/preview_appearance
/// A flat appearance, with only one direction, that we apply to the cardboard cutout.
var/image/applied_appearance
/// The base name we actually give to to the cardboard cutout. Can be overridden in get_name().
var/applied_name = "boardjak"
/// The desc we give to the cardboard cutout.
Expand All @@ -179,19 +197,24 @@
/datum/cardboard_cutout/New()
. = ..()
if(direct_icon)
applied_appearance = mutable_appearance(direct_icon, direct_icon_state)
preview_appearance = mutable_appearance(direct_icon, direct_icon_state)
else
applied_appearance = get_dynamic_human_appearance(outfit, species, mob_spawner, l_hand, r_hand, animated = FALSE)
preview_appearance = get_dynamic_human_appearance(outfit, species, mob_spawner, l_hand, r_hand, animated = FALSE)

/// This proc returns the name that the cardboard cutout item will use.
/datum/cardboard_cutout/proc/get_name()
return applied_name

/// This proc sets the cardboard cutout item's vars.
/datum/cardboard_cutout/proc/apply(obj/item/cardboard_cutout/cutouts)
if(isnull(applied_appearance))
applied_appearance = image(fcopy_rsc(getFlatIcon(preview_appearance, no_anim = TRUE)))
applied_appearance.plane = cutouts.plane
applied_appearance.layer = cutouts.layer
cutouts.appearance = applied_appearance
cutouts.name = get_name()
cutouts.desc = applied_desc
cutouts.update_appearance() //forces an update on the tactical comp's appearance.

/datum/cardboard_cutout/assistant
name = "Assistant"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@
. = ..()
if (!.)
return
owner.AddElement(/datum/element/waddling)
owner.AddElementTrait(TRAIT_WADDLING, TRAIT_STATUS_EFFECT(id), /datum/element/waddling)
ADD_TRAIT(owner, TRAIT_NO_SLIP_WATER, TRAIT_STATUS_EFFECT(id))
slipperiness = owner.AddComponent(\
/datum/component/slippery,\
Expand All @@ -418,8 +418,7 @@
return owner.body_position == LYING_DOWN

/datum/status_effect/golem/bananium/on_remove()
REMOVE_TRAIT(owner, TRAIT_NO_SLIP_WATER, TRAIT_STATUS_EFFECT(id))
owner.RemoveElement(/datum/element/waddling)
owner.remove_traits(owner, list(TRAIT_WADDLING, TRAIT_NO_SLIP_WATER), TRAIT_STATUS_EFFECT(id))
QDEL_NULL(slipperiness)
return ..()

Expand Down
4 changes: 2 additions & 2 deletions code/modules/clothing/shoes/clown.dm
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,13 @@
. = ..()
if(slot & ITEM_SLOT_FEET)
if(enabled_waddle)
user.AddElement(/datum/element/waddling)
user.AddElementTrait(TRAIT_WADDLING, SHOES_TRAIT, /datum/element/waddling)
if(is_clown_job(user.mind?.assigned_role))
user.add_mood_event("clownshoes", /datum/mood_event/clownshoes)

/obj/item/clothing/shoes/clown_shoes/dropped(mob/living/user)
. = ..()
user.RemoveElement(/datum/element/waddling)
REMOVE_TRAIT(user, TRAIT_WADDLING, SHOES_TRAIT)
if(is_clown_job(user.mind?.assigned_role))
user.clear_mood_event("clownshoes")

Expand Down
4 changes: 2 additions & 2 deletions code/modules/clothing/shoes/costume.dm
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,8 @@
/obj/item/clothing/shoes/ducky_shoes/equipped(mob/living/user, slot)
. = ..()
if(slot & ITEM_SLOT_FEET)
user.AddElement(/datum/element/waddling)
user.AddElementTrait(TRAIT_WADDLING, SHOES_TRAIT, /datum/element/waddling)

/obj/item/clothing/shoes/ducky_shoes/dropped(mob/living/user)
. = ..()
user.RemoveElement(/datum/element/waddling)
REMOVE_TRAIT(user, TRAIT_WADDLING, SHOES_TRAIT)
2 changes: 1 addition & 1 deletion code/modules/mob/living/basic/clown/clown.dm
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
ai_controller.set_blackboard_key(BB_BASIC_MOB_SPEAK_LINES, emotes)
//im not putting dynamic humans or whatever its called here because this is the base path of nonhuman clownstrosities
if(waddles)
AddElement(/datum/element/waddling)
AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling)
if(length(loot))
loot = string_list(loot)
AddElement(/datum/element/death_drops, loot)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/basic/farm_animals/pony.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
AddElement(/datum/element/pet_bonus, "whickers.")
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/ai_flee_while_injured)
AddElement(/datum/element/waddling)
AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling)
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)), unique = unique_tamer)

/mob/living/basic/pony/proc/tamed(mob/living/tamer)
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/basic/pets/penguin.dm
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/ai_flee_while_injured)
AddElement(/datum/element/pet_bonus, "honks happily!")
AddElement(/datum/element/waddling)
AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling)
if(!can_lay_eggs)
return
AddComponent(\
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack))
RegisterSignal(src, COMSIG_MOB_LOGIN, PROC_REF(on_login))

AddElement(/datum/element/waddling)
AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling)
AddElement(/datum/element/ai_retaliate)
AddElement(/datum/element/door_pryer, pry_time = 5 SECONDS, interaction_key = REGALRAT_INTERACTION)
AddComponent(\
Expand Down
Loading

0 comments on commit 23b3561

Please sign in to comment.