Skip to content

Commit

Permalink
Refactors bardrone area based godmode into an element (tgstation#76619)
Browse files Browse the repository at this point in the history
Let's this be used for more than just bardrones and for more than just
the exit shuttle in the future
  • Loading branch information
ZephyrTFA authored Jul 13, 2023
1 parent 42a2b0a commit 763112f
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 24 deletions.
4 changes: 2 additions & 2 deletions code/__DEFINES/dcs/flags.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// Return this from `/datum/component/Initialize` or `datum/component/OnTransfer` to have the component be deleted if it's applied to an incorrect type.
/// Return this from `/datum/component/Initialize` or `/datum/component/OnTransfer` or `/datum/component/on_source_add` to have the component be deleted if it's applied to an incorrect type.
/// `parent` must not be modified if this is to be returned.
/// This will be noted in the runtime logs
#define COMPONENT_INCOMPATIBLE 1
Expand Down Expand Up @@ -33,8 +33,8 @@
/**
* Component uses source tracking to manage adding and removal logic.
* Add a source/spawn to/the component by using AddComponentFrom(source, component_type, args...)
* Only the first args will be respected, and you should instead handle most of your logic in the on_source_added proc.
* Removing the last source will automatically remove the component from the parent.
* Arguments will be passed to on_source_add(source, args...); ensure that Initialize and on_source_add have the same signature.
*/
#define COMPONENT_DUPE_SOURCES 3
/// old component is given the initialization args of the new
Expand Down
24 changes: 17 additions & 7 deletions code/datums/components/_component.dm
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,13 @@
return

/**
* Called when the component has a new source registered
* Called when the component has a new source registered.
* Return COMPONENT_INCOMPATIBLE to signal that the source is incompatible and should not be added
*/
/datum/component/proc/on_source_add(source)
/datum/component/proc/on_source_add(source, ...)
SHOULD_CALL_PARENT(TRUE)
if(dupe_mode != COMPONENT_DUPE_SOURCES)
CRASH("Component '[type]' does not use sources but has been given a source")
return COMPONENT_INCOMPATIBLE
LAZYOR(sources, source)

/**
Expand Down Expand Up @@ -339,7 +340,7 @@
var/datum/component/old_component

raw_args[1] = src
if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE)
if(dupe_mode != COMPONENT_DUPE_ALLOWED && dupe_mode != COMPONENT_DUPE_SELECTIVE && dupe_mode != COMPONENT_DUPE_SOURCES)
if(!dupe_type)
old_component = GetExactComponent(component_type)
else
Expand Down Expand Up @@ -372,10 +373,14 @@
if(COMPONENT_DUPE_SOURCES)
if(source in old_component.sources)
return old_component // source already registered, no work to do
old_component.on_source_add(source)

if(old_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE)
stack_trace("incompatible source added to a [old_component.type]. Args: [json_encode(raw_args)]")
return null

else if(!new_component)
new_component = new component_type(raw_args) // There's a valid dupe mode but there's no old component, act like normal

else if(dupe_mode == COMPONENT_DUPE_SELECTIVE)
var/list/arguments = raw_args.Copy()
arguments[1] = new_component
Expand All @@ -387,12 +392,17 @@
break
if(!new_component && make_new_component)
new_component = new component_type(raw_args)

else if(dupe_mode == COMPONENT_DUPE_SOURCES)
new_component = new component_type(raw_args)
if(new_component.on_source_add(arglist(list(source) + raw_args.Copy(2))) == COMPONENT_INCOMPATIBLE)
stack_trace("incompatible source added to a [new_component.type]. Args: [json_encode(raw_args)]")
return null

else if(!new_component)
new_component = new component_type(raw_args) // Dupes are allowed, act like normal

if(!old_component && !QDELETED(new_component)) // Nothing related to duplicate components happened and the new component is healthy
if(uses_sources) // make sure they have the source added if they use sources
new_component.on_source_add(source)
SEND_SIGNAL(src, COMSIG_COMPONENT_ADDED, new_component)
return new_component

Expand Down
123 changes: 123 additions & 0 deletions code/datums/components/area_based_godmode.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#define MAP_AREA_TYPE "area_type"
#define MAP_ALLOW_AREA_SUBTYPES "allow_area_subtypes"
#define DEFAULT_GAIN_MESSAGE span_big(span_green("You are now invulnerable."))
#define DEFAULT_LOSE_MESSAGE span_big(span_red("You are no longer invulnerable."))

/**
* Area-based godmode.
* Gain and Lose message can only be set once, at initial component creation; adding a source will not update them.
*/
/datum/component/area_based_godmode
dupe_mode = COMPONENT_DUPE_SOURCES

/// The type of area that will trigger godmode.
var/list/sources_to_area_type

/// Whether or not to allow subtypes of the area type to trigger godmode.
var/allow_area_subtypes

/// The message to send to the mob when they gain godmode.
var/gain_message

/// The message to send to the mob when they lose godmode.
var/lose_message

/// Cached state of check_area, prevents recalculating on source add
var/check_area_cached_state = FALSE

/datum/component/area_based_godmode/Initialize(
area_type,
allow_area_subtypes,
gain_message = DEFAULT_GAIN_MESSAGE,
lose_message = DEFAULT_LOSE_MESSAGE,
)
var/mob/mob_target = parent
if(!istype(mob_target))
return COMPONENT_INCOMPATIBLE
if(initial(mob_target.status_flags) & GODMODE)
return COMPONENT_INCOMPATIBLE

sources_to_area_type = list()
src.gain_message = gain_message
src.lose_message = lose_message
RegisterSignal(mob_target, COMSIG_ENTER_AREA, PROC_REF(check_area))

/datum/component/area_based_godmode/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_ENTER_AREA)

/datum/component/area_based_godmode/on_source_add(
source,
area_type,
allow_area_subtypes = FALSE,
gain_message = DEFAULT_GAIN_MESSAGE,
lose_message = DEFAULT_LOSE_MESSAGE,
)
. = ..()
if(. == COMPONENT_INCOMPATIBLE)
return

var/list/information_map = list(
MAP_AREA_TYPE = area_type,
MAP_ALLOW_AREA_SUBTYPES = allow_area_subtypes,
)
sources_to_area_type[source] = information_map

var/mob/mob_target = parent // no need to istype here, done at creation
mob_target.become_area_sensitive("[REF(src)]:[source]")
if(!check_area_cached_state)
check_area(mob_target)

/datum/component/area_based_godmode/on_source_remove(source)
sources_to_area_type -= source
var/mob/mob_target = parent
mob_target.lose_area_sensitivity("[REF(src)]:[source]")
if(check_area_cached_state)
check_area(mob_target)
return ..()

/datum/component/area_based_godmode/proc/check_in_valid_area(mob/checking)
var/list/area/allowed_areas = list()
for(var/source in sources_to_area_type)
var/list/source_map = sources_to_area_type[source]
var/area/top_level = source_map[MAP_AREA_TYPE]
if(!allowed_areas[top_level])
allowed_areas[top_level] = source_map[MAP_ALLOW_AREA_SUBTYPES]

if(!length(allowed_areas))
stack_trace("called check_in_valid_area with zero sources")
return FALSE

var/area/area = get_area(checking)
if(area.type in allowed_areas)
return TRUE

for(var/area/allowed_area as anything in allowed_areas)
if(!allowed_areas[allowed_area])
continue
if(istype(area, allowed_area))
return TRUE

return FALSE

/datum/component/area_based_godmode/proc/check_area(mob/source)
SIGNAL_HANDLER

var/has_godmode = source.status_flags & GODMODE
if(!check_in_valid_area(source))
if(has_godmode)
to_chat(source, lose_message)
source.status_flags ^= GODMODE
check_area_cached_state = FALSE
return

check_area_cached_state = TRUE
if(has_godmode)
return

to_chat(source, gain_message)
source.status_flags ^= GODMODE

#undef MAP_AREA_TYPE
#undef MAP_ALLOW_AREA_SUBTYPES
#undef DEFAULT_GAIN_MESSAGE
#undef DEFAULT_LOSE_MESSAGE
14 changes: 13 additions & 1 deletion code/modules/admin/view_variables/topic_basic.dm
Original file line number Diff line number Diff line change
Expand Up @@ -61,22 +61,34 @@
names += componentsubtypes
names += "---Elements---"
names += sort_list(subtypesof(/datum/element), GLOBAL_PROC_REF(cmp_typepaths_asc))

var/result = tgui_input_list(usr, "Choose a component/element to add", "Add Component", names)
if(isnull(result))
return
if(!usr || result == "---Components---" || result == "---Elements---")
return

if(QDELETED(src))
to_chat(usr, "That thing doesn't exist anymore!", confidential = TRUE)
return

var/add_source
if(ispath(result, /datum/component))
var/datum/component/comp_path = result
if(initial(comp_path.dupe_mode) == COMPONENT_DUPE_SOURCES)
add_source = tgui_input_text(usr, "Enter a source for the component", "Add Component", "ADMIN-ABUSE")
if(isnull(add_source))
return

var/list/lst = get_callproc_args()
if(!lst)
return

var/datumname = "error"
lst.Insert(1, result)
if(result in componentsubtypes)
datumname = "component"
target._AddComponent(lst)
target._AddComponent(lst, add_source)
else
datumname = "element"
target._AddElement(lst)
Expand Down
16 changes: 2 additions & 14 deletions code/modules/shuttle/special.dm
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,7 @@
/mob/living/simple_animal/drone/snowflake/bardrone/Initialize(mapload)
. = ..()
access_card.add_access(list(ACCESS_CENT_BAR))
become_area_sensitive(ROUNDSTART_TRAIT)
RegisterSignal(src, COMSIG_ENTER_AREA, PROC_REF(check_barstaff_godmode))
check_barstaff_godmode()
AddComponentFrom(ROUNDSTART_TRAIT, /datum/component/area_based_godmode, area_type = /area/shuttle/escape, allow_area_subtypes = TRUE)

/mob/living/simple_animal/hostile/alien/maid/barmaid
gold_core_spawnable = NO_SPAWN
Expand All @@ -192,22 +190,12 @@
access_card.add_access(cap_trim.access + cap_trim.wildcard_access + list(ACCESS_CENT_BAR))

ADD_TRAIT(access_card, TRAIT_NODROP, ABSTRACT_ITEM_TRAIT)
become_area_sensitive(ROUNDSTART_TRAIT)
RegisterSignal(src, COMSIG_ENTER_AREA, PROC_REF(check_barstaff_godmode))
check_barstaff_godmode()
AddComponentFrom(ROUNDSTART_TRAIT, /datum/component/area_based_godmode, area_type = /area/shuttle/escape, allow_area_subtypes = TRUE)

/mob/living/simple_animal/hostile/alien/maid/barmaid/Destroy()
qdel(access_card)
. = ..()

/mob/living/simple_animal/proc/check_barstaff_godmode()
SIGNAL_HANDLER

if(istype(get_area(loc), /area/shuttle/escape))
status_flags |= GODMODE
else
status_flags &= ~GODMODE

// Bar table, a wooden table that kicks you in a direction if you're not
// barstaff (defined as someone who was a roundstart bartender or someone
// with CENTCOM_BARSTAFF)
Expand Down
1 change: 1 addition & 0 deletions tgstation.dme
Original file line number Diff line number Diff line change
Expand Up @@ -870,6 +870,7 @@
#include "code\datums\components\ai_retaliate_advanced.dm"
#include "code\datums\components\anti_magic.dm"
#include "code\datums\components\aquarium.dm"
#include "code\datums\components\area_based_godmode.dm"
#include "code\datums\components\area_sound_manager.dm"
#include "code\datums\components\areabound.dm"
#include "code\datums\components\armor_plate.dm"
Expand Down

0 comments on commit 763112f

Please sign in to comment.