mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-21 10:26:54 +01:00
- separates the composition of cyborgs into modules, iconsets, chassis, upgrades - modules / chassis atm can provide items, so can upgrades; in the future we will want to probably generalize the item API a bit so components can work - chassis now can (unimplemented) easily be made to give different stats - reorganizes all the cyborg sprites - adds generalized item mounts with redirection support and tears out legacy cyborg items - entirely rebalances modules & writes some new lawsets in for future use --------- Co-authored-by: silicons <silicons@silicons.dev>
200 lines
6.5 KiB
Plaintext
200 lines
6.5 KiB
Plaintext
//* This file is explicitly licensed under the MIT license. *//
|
|
//* Copyright (c) 2024 Citadel Station Developers *//
|
|
|
|
//* Hands are weird. Unlike inventory slots, they tend to be very tightly *//
|
|
//* coupled to organ state, so it's not a good thing to have all behavior *//
|
|
//* on /datum/inventory. *//
|
|
//* *//
|
|
//* To make it worse, hands are not just an inventory concept; interacting *//
|
|
//* with the world requires hands, which requires hand code, meaning hand *//
|
|
//* code cannot only live on /datum/inventory. *//
|
|
//* *//
|
|
//* Therefore, the separation of concerns is that mob-side hands code *//
|
|
//* is only responsible for orchestrating state that concerns the health *//
|
|
//* system like organ integration and whatnot, while /datum/inventory *//
|
|
//* side orchestrates pickup/drop logic and all that stuff. *//
|
|
//* *//
|
|
//* An abstraction layer provides mob integration to the inventory datum. *//
|
|
//* That is what is in this file, along with common mob-level handling *//
|
|
//* for non-inventory-related uses of hands. *//
|
|
//* *//
|
|
//* Any purpose / notion of 'usable' hands also belongs here, because our *//
|
|
//* inventory datum has no notion of a usable hand or an unusable one, let *//
|
|
//* alone manipulation levels, as it only cares about the actual state *//
|
|
//* of whether or not an object is held. *//
|
|
|
|
//* Hands - Abstraction *//
|
|
|
|
/**
|
|
* gets if we have any hands at all
|
|
*/
|
|
/mob/proc/has_hands()
|
|
SHOULD_NOT_OVERRIDE(TRUE)
|
|
return !!get_nominal_hand_count()
|
|
|
|
/**
|
|
* get number of physical hands / arms / whatever that we have and should check for
|
|
*
|
|
* this is not number we can use
|
|
* this is the number we should use for things like rendering
|
|
* a hand stump is still rendered, and we should never render less than 2 hands for mobs
|
|
* that nominally have hands.
|
|
*/
|
|
/mob/proc/get_nominal_hand_count()
|
|
return length(inventory?.held_items)
|
|
|
|
/**
|
|
* get number of usable hands / arms / whatever that we have and should check for
|
|
*
|
|
* this is the number we can use
|
|
* missing = can't use
|
|
* stump = can't use
|
|
* broken = *can* use.
|
|
*
|
|
* basically if a red deny symbol is in the hand it is not usable, otherwise it's usable.
|
|
*/
|
|
/mob/proc/get_usable_hand_count() as num
|
|
return get_nominal_hand_count()
|
|
|
|
/**
|
|
* get indices of usable hands
|
|
*/
|
|
/mob/proc/get_usable_hand_indices() as /list
|
|
RETURN_TYPE(/list)
|
|
. = list()
|
|
for(var/i in 1 to get_nominal_hand_count())
|
|
. += i
|
|
|
|
/**
|
|
* Are usable hands all holding items?
|
|
*
|
|
* * if a hand slot is unusable but still has an item, it's ignored.
|
|
* * if we have no hands, this returns TRUE
|
|
*/
|
|
/mob/proc/are_usable_hands_full()
|
|
if(!length(inventory?.held_items))
|
|
return TRUE
|
|
for(var/i in get_usable_hand_indices())
|
|
if(isnull(inventory.held_items[i]))
|
|
return FALSE
|
|
return TRUE
|
|
|
|
/**
|
|
* usable hands are all empty?
|
|
*
|
|
* * if a hand slot is unusable but still has an item, it's ignored.
|
|
* * if we have no hands, this returns TRUE
|
|
*/
|
|
/mob/proc/are_usable_hands_empty()
|
|
if(!length(inventory?.held_items))
|
|
return TRUE
|
|
for(var/i in get_usable_hand_indices())
|
|
if(isnull(inventory.held_items[i]))
|
|
continue
|
|
return FALSE
|
|
return TRUE
|
|
|
|
//* Hands - Checks *//
|
|
|
|
/**
|
|
* Gets effective manipulation level of a hand index
|
|
*/
|
|
/mob/proc/get_hand_manipulation_level(index)
|
|
return HAND_MANIPULATION_PRECISE
|
|
|
|
// todo: can we combine all these procs?
|
|
|
|
/**
|
|
* Checks if a hand can be used at a given manipulation level.
|
|
*/
|
|
/mob/proc/is_hand_manipulation_sufficient(index, manipulation)
|
|
// until there's a reason to do otherwise, get_hand_manipulation_level() should be what you override!
|
|
SHOULD_NOT_OVERRIDE(TRUE)
|
|
return get_hand_manipulation_level(index) >= manipulation
|
|
|
|
/**
|
|
* get a list of reasons (e.g. 'broken bone', 'stunned', etc)
|
|
* a hand **cannot** be at a certain manipulation level.
|
|
*/
|
|
/mob/proc/why_hand_manipulation_insufficient(index, manipulation)
|
|
RETURN_TYPE(/list)
|
|
return list()
|
|
|
|
//* Hands - Helpers *//
|
|
|
|
/**
|
|
* Runs a standard hand usability check against a target with a given manipulation level required.
|
|
*
|
|
* @params
|
|
* * target - what is being interacted with
|
|
* * index - hand index being used
|
|
* * manipulation - required manipulation level
|
|
* * actor - (optional) interactor
|
|
* * silent - (optional) if set, will not emit error message
|
|
*
|
|
* @return TRUE / FALSE sucecss / fail
|
|
*/
|
|
/mob/proc/standard_hand_usability_check(atom/target, index, manipulation, datum/event_args/actor/actor, silent)
|
|
if(is_hand_manipulation_sufficient(index, manipulation))
|
|
return TRUE
|
|
if(silent)
|
|
return FALSE
|
|
var/list/reasons_we_cant = why_hand_manipulation_insufficient(index, manipulation)
|
|
if(actor)
|
|
actor.chat_feedback(
|
|
SPAN_WARNING("You can't do that right now! ([length(reasons_we_cant) ? english_list(reasons_we_cant) : "hand nonfunctional for unknown reason"])"),
|
|
target = target,
|
|
)
|
|
else
|
|
action_feedback(
|
|
SPAN_WARNING("You can't do that right now! ([length(reasons_we_cant) ? english_list(reasons_we_cant) : "hand nonfunctional for unknown reason"])"),
|
|
target = target,
|
|
)
|
|
return FALSE
|
|
|
|
//* Hands - Identity *//
|
|
|
|
/**
|
|
* Returns something like "left hand", "right hand", "3rd right hand", "left hand #2", etc.
|
|
*/
|
|
/mob/proc/get_hand_generalized_name(index)
|
|
var/number_on_side = round(index / 2)
|
|
return "[index % 2? "left" : "right"] hand[number_on_side > 1 && " #[number_on_side]"]"
|
|
|
|
//* Hands - Legacy / WIP *//
|
|
|
|
/**
|
|
* Swaps our active hand
|
|
*
|
|
* * In the future, we'll want to track active hand, attack intents, etc, by operator, instead of by mob.
|
|
* * This is so remote control abstraction works.
|
|
*/
|
|
/mob/proc/swap_hand(to_index, allow_deselect)
|
|
if(active_hand == to_index)
|
|
return TRUE
|
|
var/hand_count = get_nominal_hand_count()
|
|
var/obj/item/was_active = get_active_held_item()
|
|
var/old_index = active_hand || 1
|
|
|
|
if(isnull(to_index) && !allow_deselect)
|
|
if(active_hand >= hand_count)
|
|
to_index = hand_count? 1 : null
|
|
else
|
|
to_index = active_hand + 1
|
|
else
|
|
if(to_index > hand_count)
|
|
return FALSE
|
|
|
|
if(active_hand == to_index)
|
|
return TRUE
|
|
active_hand = to_index
|
|
|
|
. = TRUE
|
|
|
|
client?.actor_huds?.inventory?.swap_active_hand(old_index, to_index)
|
|
|
|
//! LEGACY
|
|
if(was_active?.zoom)
|
|
was_active?.zoom()
|
|
//! End
|