mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-10 02:09:41 +00:00
[MIRROR] Facing component (#11763)
Co-authored-by: Cameron Lennox <killer65311@gmail.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
3c80dd2df3
commit
fa2aaa5293
@@ -530,6 +530,10 @@
|
||||
#define COMSIG_ITEM_EQUIPPED "item_equip"
|
||||
///from base of obj/item/dropped(): (mob/user)
|
||||
#define COMSIG_ITEM_DROPPED "item_drop"
|
||||
/// A mob has just equipped an item. Called on [/mob] from base of [/obj/item/equipped()]: (/obj/item/equipped_item, slot)
|
||||
#define COMSIG_MOB_EQUIPPED_ITEM "mob_equipped_item"
|
||||
/// A mob has just unequipped an item.
|
||||
#define COMSIG_MOB_UNEQUIPPED_ITEM "mob_unequipped_item"
|
||||
///from base of obj/item/pickup(): (/mob/taker)
|
||||
#define COMSIG_ITEM_PICKUP "item_pickup"
|
||||
///from base of mob/living/carbon/attacked_by(): (mob/living/carbon/target, mob/living/user, hit_zone)
|
||||
|
||||
@@ -995,6 +995,20 @@ GLOBAL_LIST_EMPTY(json_cache)
|
||||
UNTYPED_LIST_ADD(keys, key)
|
||||
return keys
|
||||
|
||||
///compare two lists, returns TRUE if they are the same
|
||||
/proc/compare_list(list/l,list/d)
|
||||
if(!islist(l) || !islist(d))
|
||||
return FALSE
|
||||
|
||||
if(l.len != d.len)
|
||||
return FALSE
|
||||
|
||||
for(var/i in 1 to l.len)
|
||||
if(l[i] != d[i])
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
//TG sort_list
|
||||
///uses sort_list() but uses the var's name specifically. This should probably be using mergeAtom() instead
|
||||
/proc/sort_names(list/list_to_sort, order=1)
|
||||
@@ -1063,18 +1077,4 @@ GLOBAL_LIST_EMPTY(json_cache)
|
||||
return_list += bit
|
||||
|
||||
return return_list
|
||||
|
||||
///compare two lists, returns TRUE if they are the same
|
||||
/proc/compare_list(list/l,list/d)
|
||||
if(!islist(l) || !islist(d))
|
||||
return FALSE
|
||||
|
||||
if(l.len != d.len)
|
||||
return FALSE
|
||||
|
||||
for(var/i in 1 to l.len)
|
||||
if(l[i] != d[i])
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
//CHOMPAdd end
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* This component behaves similar to connect_loc_behalf but for all turfs in range, hooking into a signal on each of them.
|
||||
* Just like connect_loc_behalf, It can react to that signal on behalf of a seperate listener.
|
||||
* Just like connect_loc_behalf, It can react to that signal on behalf of a separate listener.
|
||||
* Good for components, though it carries some overhead. Can't be an element as that may lead to bugs.
|
||||
*/
|
||||
/datum/component/connect_range
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
/// An assoc list of signal -> procpath to register to the loc this object is on.
|
||||
var/list/connections
|
||||
/// The turfs currently connected to this component
|
||||
var/list/turfs = list()
|
||||
/**
|
||||
* The atom the component is tracking. The component will delete itself if the tracked is deleted.
|
||||
* Signals will also be updated whenever it moves (if it's a movable).
|
||||
@@ -24,8 +26,8 @@
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
src.connections = connections
|
||||
src.range = range
|
||||
set_tracked(tracked)
|
||||
src.works_in_containers = works_in_containers
|
||||
set_tracked(tracked)
|
||||
|
||||
/datum/component/connect_range/Destroy()
|
||||
set_tracked(null)
|
||||
@@ -41,7 +43,7 @@
|
||||
if(src.range == range && src.works_in_containers == works_in_containers)
|
||||
return
|
||||
//Unregister the signals with the old settings.
|
||||
unregister_signals(isturf(tracked) ? tracked : tracked.loc)
|
||||
unregister_signals(isturf(tracked) ? tracked : tracked.loc, turfs)
|
||||
src.range = range
|
||||
src.works_in_containers = works_in_containers
|
||||
//Re-register the signals with the new settings.
|
||||
@@ -49,7 +51,7 @@
|
||||
|
||||
/datum/component/connect_range/proc/set_tracked(atom/new_tracked)
|
||||
if(tracked) //Unregister the signals from the old tracked and its surroundings
|
||||
unregister_signals(isturf(tracked) ? tracked : tracked.loc)
|
||||
unregister_signals(isturf(tracked) ? tracked : tracked.loc, turfs)
|
||||
UnregisterSignal(tracked, list(
|
||||
COMSIG_MOVABLE_MOVED,
|
||||
COMSIG_QDELETING,
|
||||
@@ -66,28 +68,37 @@
|
||||
SIGNAL_HANDLER
|
||||
qdel(src)
|
||||
|
||||
/datum/component/connect_range/proc/update_signals(atom/target, atom/old_loc, forced = FALSE)
|
||||
/datum/component/connect_range/proc/update_signals(atom/target, atom/old_loc)
|
||||
var/turf/current_turf = get_turf(target)
|
||||
var/on_same_turf = current_turf == get_turf(old_loc) //Only register/unregister turf signals if it's moved to a new turf.
|
||||
unregister_signals(old_loc, on_same_turf)
|
||||
|
||||
if(isnull(current_turf))
|
||||
unregister_signals(old_loc, turfs)
|
||||
turfs = list()
|
||||
return
|
||||
|
||||
if(ismovable(target.loc))
|
||||
var/loc_is_movable = ismovable(target.loc)
|
||||
|
||||
if(loc_is_movable)
|
||||
if(!works_in_containers)
|
||||
unregister_signals(old_loc, turfs)
|
||||
turfs = list()
|
||||
return
|
||||
|
||||
//Only register/unregister turf signals if it's moved to a new turf.
|
||||
if(current_turf == get_turf(old_loc))
|
||||
unregister_signals(old_loc, null)
|
||||
return
|
||||
var/list/old_turfs = turfs
|
||||
turfs = RANGE_TURFS(range, current_turf)
|
||||
unregister_signals(old_loc, old_turfs - turfs)
|
||||
if(loc_is_movable)
|
||||
//Keep track of possible movement of all movables the target is in.
|
||||
for(var/atom/movable/container as anything in get_nested_locs(target))
|
||||
RegisterSignal(container, COMSIG_MOVABLE_MOVED, PROC_REF(on_moved))
|
||||
|
||||
if(on_same_turf && !forced)
|
||||
return
|
||||
for(var/turf/target_turf in RANGE_TURFS(range, current_turf))
|
||||
for(var/turf/target_turf as anything in turfs - old_turfs)
|
||||
for(var/signal in connections)
|
||||
parent.RegisterSignal(target_turf, signal, connections[signal])
|
||||
|
||||
/datum/component/connect_range/proc/unregister_signals(atom/location, on_same_turf = FALSE)
|
||||
/datum/component/connect_range/proc/unregister_signals(atom/location, list/remove_from)
|
||||
//The location is null or is a container and the component shouldn't have register signals on it
|
||||
if(isnull(location) || (!works_in_containers && !isturf(location)))
|
||||
return
|
||||
@@ -96,10 +107,9 @@
|
||||
for(var/atom/movable/target as anything in (get_nested_locs(location) + location))
|
||||
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
|
||||
|
||||
if(on_same_turf)
|
||||
if(!length(remove_from))
|
||||
return
|
||||
var/turf/previous_turf = get_turf(location)
|
||||
for(var/turf/target_turf in RANGE_TURFS(range, previous_turf))
|
||||
for(var/turf/target_turf as anything in remove_from)
|
||||
parent.UnregisterSignal(target_turf, connections)
|
||||
|
||||
/datum/component/connect_range/proc/on_moved(atom/movable/movable, atom/old_loc)
|
||||
140
code/datums/components/reactive_icon_update.dm
Normal file
140
code/datums/components/reactive_icon_update.dm
Normal file
@@ -0,0 +1,140 @@
|
||||
///Component that updates the icon_state of an item when something approaches.
|
||||
///NOTE: This uses the initial icon of the object, meaning it will not work properly with items that change their icon_state for other reasons.
|
||||
/datum/component/reactive_icon_update
|
||||
dupe_mode = COMPONENT_DUPE_UNIQUE
|
||||
///What we want to append to our icon_state when our conditions are filled
|
||||
var/icon_prefix
|
||||
///List of which directions we want to be valid. Can be NORTH/SOUTH/EAST/WEST along with NORTHEAST/SOUTHEAST/SOUTHWEST/NORTHWEST
|
||||
var/list/directions
|
||||
///Range that we want it to look out for.
|
||||
var/range
|
||||
///What type of mobs trigger the icon change.
|
||||
var/list/triggering_mobs = list(/mob/living)
|
||||
|
||||
/datum/component/reactive_icon_update/Initialize(icon_prefix, list/directions, range, triggering_mobs)
|
||||
if(!isobj(parent) || !isnum(range) || (!directions || !LAZYLEN(directions)) || (triggering_mobs && !LAZYLEN(triggering_mobs)))
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
src.icon_prefix = icon_prefix
|
||||
src.directions = directions
|
||||
src.range = range
|
||||
if(triggering_mobs)
|
||||
src.triggering_mobs = triggering_mobs
|
||||
|
||||
var/static/list/connections = list(
|
||||
COMSIG_ATOM_ENTERED = PROC_REF(update_proximity_icon),
|
||||
)
|
||||
AddComponent(/datum/component/connect_range, parent, connections, range)
|
||||
|
||||
/datum/component/reactive_icon_update/UnregisterFromParent()
|
||||
|
||||
directions.Cut()
|
||||
triggering_mobs.Cut()
|
||||
|
||||
/datum/component/reactive_icon_update/proc/update_proximity_icon(atom/current_loc, atom/movable/AM, atom/old_loc)
|
||||
SIGNAL_HANDLER
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
var/obj/our_item = parent
|
||||
if(!ismob(AM) || !mob_check(AM))
|
||||
return
|
||||
var/mob/M = AM
|
||||
if(M == our_item.loc) //Ignore the mob wearing us
|
||||
return
|
||||
|
||||
///What direction the mob is relative to us.
|
||||
var/mob_direction_x
|
||||
var/mob_direction_y
|
||||
var/actual_direction
|
||||
|
||||
///First, we get the X and Y coordinates
|
||||
var/x
|
||||
var/y
|
||||
if(our_item.x && our_item.y)
|
||||
x = our_item.x
|
||||
y = our_item.y
|
||||
if(our_item.x == 0 || our_item.y == 0) //We're inside of something! Clothing is a great example of this.
|
||||
if(our_item.loc)
|
||||
x = our_item.loc.x
|
||||
y = our_item.loc.y
|
||||
if(x == 0 || y == 0) //We're two layers deep, just give up.
|
||||
return
|
||||
if(M.x > x)
|
||||
mob_direction_x = EAST
|
||||
else if(M.x < x)
|
||||
mob_direction_x = WEST
|
||||
else
|
||||
mob_direction_x = null //Same X as us
|
||||
|
||||
if(M.y > y)
|
||||
mob_direction_y = NORTH
|
||||
else if(M.y < y)
|
||||
mob_direction_y = SOUTH
|
||||
else
|
||||
mob_direction_y = null //Same Y as us
|
||||
|
||||
///Then we combine them to get our actual direction, not just cardinals.
|
||||
if(mob_direction_x == EAST && mob_direction_y == SOUTH) //Diagonals first
|
||||
actual_direction = SOUTHEAST
|
||||
else if(mob_direction_x == EAST && mob_direction_y == NORTH)
|
||||
actual_direction = NORTHEAST
|
||||
else if(mob_direction_x == WEST && mob_direction_y == SOUTH)
|
||||
actual_direction = SOUTHWEST
|
||||
else if(mob_direction_x == WEST && mob_direction_y == NORTH)
|
||||
actual_direction = NORTHWEST
|
||||
else if(mob_direction_x) //Only horizontal
|
||||
actual_direction = mob_direction_x
|
||||
else if(mob_direction_y) //Only vertical
|
||||
actual_direction = mob_direction_y
|
||||
else //Exactly ontop of us, so we don't care about direction.
|
||||
return
|
||||
|
||||
///We then make sure the actual_direction is in our directions list.
|
||||
if(actual_direction && !(actual_direction in directions)) //Not a valid direction. Convert to N/E/S/W
|
||||
//East and west take priority because those are generally the most visually striking.
|
||||
if((WEST in directions) && (actual_direction == (NORTHWEST || SOUTHWEST)))
|
||||
actual_direction = WEST
|
||||
else if((EAST in directions) && (actual_direction == (NORTHEAST || SOUTHEAST)))
|
||||
actual_direction = EAST
|
||||
else if((SOUTH in directions) && (actual_direction == (SOUTHWEST || SOUTHEAST)))
|
||||
actual_direction = SOUTH
|
||||
else if((NORTH in directions) && (actual_direction == (NORTHWEST || NORTHEAST)))
|
||||
actual_direction = NORTH
|
||||
else
|
||||
return
|
||||
var/directional_name
|
||||
switch(actual_direction)
|
||||
if(NORTH)
|
||||
directional_name = "north"
|
||||
if(EAST)
|
||||
directional_name = "east"
|
||||
if(SOUTH)
|
||||
directional_name = "south"
|
||||
if(WEST)
|
||||
directional_name = "west"
|
||||
if(NORTHEAST)
|
||||
directional_name = "northeast"
|
||||
if(SOUTHEAST)
|
||||
directional_name = "southeast"
|
||||
if(SOUTHWEST)
|
||||
directional_name = "southwest"
|
||||
if(NORTHWEST)
|
||||
directional_name = "northwest"
|
||||
//We then update our icon state. For example:
|
||||
//We have an item that has the icon_state of "cloak" and the prefix of "_direction" and we're facing NORTH
|
||||
//The icon_state will be changed to cloak_direction_north
|
||||
our_item.icon_state = initial(our_item.icon_state) + icon_prefix + "_" + directional_name
|
||||
|
||||
//Example item for testing directions.
|
||||
/obj/item/tool/screwdriver/test_driver
|
||||
icon = 'icons/obj/directional_test.dmi'
|
||||
|
||||
/obj/item/tool/screwdriver/test_driver/Initialize(mapload)
|
||||
..()
|
||||
icon_state = "screwdriver"
|
||||
AddComponent(/datum/component/reactive_icon_update, directions = list(NORTH, EAST, SOUTH, WEST, SOUTHWEST, SOUTHEAST, NORTHEAST, NORTHWEST), range = 3)
|
||||
|
||||
/datum/component/reactive_icon_update/proc/mob_check(mob/triggering_mob)
|
||||
SHOULD_NOT_OVERRIDE(TRUE)
|
||||
if(is_type_in_list(triggering_mob, triggering_mobs))
|
||||
return TRUE
|
||||
return FALSE
|
||||
@@ -477,6 +477,12 @@
|
||||
else if(slot == slot_l_hand || slot == slot_r_hand)
|
||||
if(!muffled_by_belly(user))
|
||||
playsound(src, pickup_sound, 20, preference = /datum/preference/toggle/pickup_sounds)
|
||||
SEND_SIGNAL(src, COMSIG_ITEM_EQUIPPED, user, slot)
|
||||
SEND_SIGNAL(user, COMSIG_MOB_EQUIPPED_ITEM, src, slot)
|
||||
var/mob/living/M = loc
|
||||
if(!istype(M))
|
||||
return
|
||||
M.update_held_icons()
|
||||
|
||||
/// Gives one of our item actions to a mob, when equipped to a certain slot
|
||||
/obj/item/proc/give_item_action(datum/action/action, mob/to_who, slot)
|
||||
|
||||
@@ -240,6 +240,8 @@ var/list/slot_equipment_priority = list( \
|
||||
else
|
||||
I.dropInto(drop_location())
|
||||
I.dropped(src)
|
||||
//SEND_SIGNAL(item_dropping, COMSIG_ITEM_POST_UNEQUIP, O, target)
|
||||
SEND_SIGNAL(src, COMSIG_MOB_UNEQUIPPED_ITEM, O, target)
|
||||
return TRUE
|
||||
|
||||
//Returns the item equipped to the specified slot, if any.
|
||||
|
||||
@@ -211,8 +211,6 @@
|
||||
else
|
||||
visible_message(span_danger("[M] has pushed [src]!"))
|
||||
break_all_grabs(M)
|
||||
for(var/obj/item/I in holding)
|
||||
drop_from_inventory(I)
|
||||
else
|
||||
visible_message(span_warning("[M] attempted to push [src]!"))
|
||||
return
|
||||
|
||||
BIN
icons/obj/directional_test.dmi
Normal file
BIN
icons/obj/directional_test.dmi
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -594,16 +594,17 @@
|
||||
#include "code\datums\components\connect_containers_ch.dm"
|
||||
#include "code\datums\components\connect_loc_behalf.dm"
|
||||
#include "code\datums\components\connect_mob_behalf.dm"
|
||||
#include "code\datums\components\connect_range_ch.dm"
|
||||
#include "code\datums\components\connect_range.dm"
|
||||
#include "code\datums\components\holographic_nature.dm"
|
||||
#include "code\datums\components\infective.dm"
|
||||
#include "code\datums\components\orbiter.dm"
|
||||
#include "code\datums\components\overlay_lighting.dm"
|
||||
#include "code\datums\components\reactive_icon_update.dm"
|
||||
#include "code\datums\components\recursive_move.dm"
|
||||
#include "code\datums\components\resize_guard.dm"
|
||||
#include "code\datums\components\swarm.dm"
|
||||
#include "code\datums\components\turfslip.dm"
|
||||
#include "code\datums\components\tethered_item.dm"
|
||||
#include "code\datums\components\turfslip.dm"
|
||||
#include "code\datums\components\animations\dizzy.dm"
|
||||
#include "code\datums\components\animations\jittery.dm"
|
||||
#include "code\datums\components\antags\antag.dm"
|
||||
|
||||
Reference in New Issue
Block a user