mirror of
https://github.com/Citadel-Station-13/Citadel-Station-13-RP.git
synced 2026-08-18 11:56:55 +01:00
- all cells in the game are now small, medium, large - super, hyper capacity cells, device, and weapon cells are all removed - cells now have standardized variants, some of which are printable by R&D - energy weapons rebalanced - **advanced energy guns and stun revolvers removed** - use the new modular weapon system and microfission cells. --------- Co-authored-by: silicons <silicons@silicons.dev>
380 lines
15 KiB
Plaintext
380 lines
15 KiB
Plaintext
//* This file is explicitly licensed under the MIT license. *//
|
|
//* Copyright (c) 2023 Citadel Station developers. *//
|
|
|
|
/**
|
|
* a transition from one stage to another
|
|
*/
|
|
/datum/frame_step
|
|
/// step name for tool radials & more
|
|
var/name
|
|
/// "use a [whatever] on [frame] to [action_descriptor: "unscrew the panel"]"
|
|
var/action_descriptor
|
|
/// "you start [x -> 'unscrewing the panel on'][src] ..."
|
|
/// add a space after!
|
|
/// please don't put pronouns in this, it doesn't get interpolated properly.
|
|
var/action_text_leading
|
|
/// "you start ... [src][x -> ', removing the screws in the process.']"
|
|
/// don't forget punctuation at the end.
|
|
/// please don't put pronouns in this, it doesn't get interpolated properly.
|
|
var/action_text_trailing
|
|
/// stage key this moves us to
|
|
/// * [STAGE_DECONSTRUCT] to deconstruct
|
|
/// * [STAGE_FINISH] to finish
|
|
var/stage
|
|
/// direction: [TOOL_DIRECTION_FORWARDS] or [TOOL_DIRECTION_BACKWARDS] or [TOOL_DIRECTION_NEUTRAL]
|
|
/// * this is used as a hint for tool graphics
|
|
/// * this is used as a hint for other visual / textual feedback
|
|
var/direction = TOOL_DIRECTION_NEUTRAL
|
|
|
|
/// FRAME_REQUEST_TYPE_* define
|
|
var/request_type
|
|
/// ergo: stack type, item type, tool function, etc. what this is depends on [step_type]
|
|
/// limited autodetection is allowed.
|
|
///
|
|
/// allowed:
|
|
/// * /datum/prototype/material path
|
|
/// * /obj/item/stack path
|
|
/// * /obj/item path
|
|
/// * TOOL_BEHAVIOR function
|
|
var/request
|
|
/// * stacks: amount
|
|
/// * items: amount; if 0, we just apply the item to it
|
|
/// * rest: unused.
|
|
var/request_amount
|
|
/// * tools: this is cost
|
|
/// * rest: unused
|
|
var/request_cost
|
|
|
|
/// time needed to do this
|
|
/// note that for tool steps, this might impact the total cost!
|
|
var/time = 0 SECONDS
|
|
|
|
/// requires anchored; if null, defaults to stage.
|
|
var/requires_anchored
|
|
|
|
// todo: request_store: null for default, context key to store under context
|
|
|
|
/// what to drop when undertaking this step
|
|
/// can either be:
|
|
/// * /obj/item/stack typepath
|
|
/// * /datum/prototype/material typepath
|
|
/// * /obj/item typepath
|
|
/// todo: text for 'drop context key'
|
|
var/drop
|
|
/// amount to drop
|
|
var/drop_amount = 1
|
|
|
|
/// use custom text?
|
|
var/use_custom_feedback = FALSE
|
|
/// list of tokens to concat into a string to display when beginning the step.
|
|
/// will not be shown if the step is fast enough.
|
|
/// null for default.
|
|
///
|
|
/// FRAME_TEXT_TOKEN_* defines are allowed, and will be automatically replaced during execution to the relevant text.
|
|
var/list/visible_text_begin
|
|
/// list of tokens to concat into a string to display when beginning the step.
|
|
/// will not be shown if the step is fast enough.
|
|
/// null for default.
|
|
///
|
|
/// FRAME_TEXT_TOKEN_* defines are allowed, and will be automatically replaced during execution to the relevant text.
|
|
var/list/audible_text_begin
|
|
/// list of tokens to concat into a string to display when beginning the step.
|
|
/// will not be shown if the step is fast enough.
|
|
/// null for default.
|
|
///
|
|
/// FRAME_TEXT_TOKEN_* defines are allowed, and will be automatically replaced during execution to the relevant text.
|
|
var/list/self_text_begin
|
|
/// list of tokens to concat into a string to display when finishing the step.
|
|
/// null for default.
|
|
///
|
|
/// FRAME_TEXT_TOKEN_* defines are allowed, and will be automatically replaced during execution to the relevant text.
|
|
var/list/visible_text_end
|
|
/// list of tokens to concat into a string to display when finishing the step.
|
|
/// null for default.
|
|
///
|
|
/// FRAME_TEXT_TOKEN_* defines are allowed, and will be automatically replaced during execution to the relevant text.
|
|
var/list/audible_text_end
|
|
/// list of tokens to concat into a string to display when finishing the step.
|
|
/// null for default.
|
|
///
|
|
/// FRAME_TEXT_TOKEN_* defines are allowed, and will be automatically replaced during execution to the relevant text.
|
|
var/list/self_text_end
|
|
|
|
/datum/frame_step/New()
|
|
if(isnull(request_type))
|
|
// autodetect
|
|
var/detected
|
|
if(ispath(request, /datum/prototype/material))
|
|
request_type = FRAME_REQUEST_TYPE_MATERIAL
|
|
else if(ispath(request, /obj/item/stack))
|
|
request_type = FRAME_REQUEST_TYPE_STACK
|
|
else if(ispath(request, /obj/item))
|
|
request_type = FRAME_REQUEST_TYPE_ITEM
|
|
else if(istext(request))
|
|
if(request in global.tool_behaviors_hardcoded)
|
|
request_type = FRAME_REQUEST_TYPE_TOOL
|
|
detected = TRUE
|
|
else if(!detected)
|
|
CRASH("failed to autodetect request")
|
|
|
|
/datum/frame_step/proc/examine(obj/structure/frame2/frame, datum/event_args/actor/actor)
|
|
var/rendered_action = action_descriptor || SPAN_BOLD(name)
|
|
switch(request_type)
|
|
if(FRAME_REQUEST_TYPE_INTERACT)
|
|
. = "<b>Interact</b> with [frame] using an empty hand to [rendered_action]."
|
|
if(FRAME_REQUEST_TYPE_ITEM)
|
|
var/rendered_item
|
|
var/obj/item/casted = request
|
|
rendered_item = initial(casted.name)
|
|
// todo: support amounts
|
|
. = "Use an <b>[rendered_item]</b> on [frame] to [rendered_action]."
|
|
if(FRAME_REQUEST_TYPE_MATERIAL)
|
|
var/rendered_material
|
|
var/rendered_stack_name
|
|
var/datum/prototype/material/resolved = RSmaterials.fetch(request)
|
|
rendered_material = resolved.display_name
|
|
rendered_stack_name = resolved.sheet_plural_name
|
|
. = "Apply [request_amount || 0] [rendered_stack_name] of <b>[rendered_material]</b> to [rendered_action]."
|
|
if(FRAME_REQUEST_TYPE_PROC)
|
|
if(FRAME_REQUEST_TYPE_STACK)
|
|
var/rendered_stack
|
|
var/rendered_stack_name
|
|
var/obj/item/stack/casted = request
|
|
rendered_stack = initial(casted.name)
|
|
rendered_stack_name = initial(casted.name) || "sheets"
|
|
. = "Use [request_amount || 0] [rendered_stack_name] of <b>[rendered_stack]</b> to [rendered_action]."
|
|
if(FRAME_REQUEST_TYPE_TOOL)
|
|
var/rendered_tool = request
|
|
. = "Use a <b>[rendered_tool]</b> to [rendered_action]."
|
|
return SPAN_NOTICE(.)
|
|
|
|
/datum/frame_step/proc/tool_image()
|
|
switch(direction)
|
|
if(TOOL_DIRECTION_BACKWARDS)
|
|
return dyntool_image_backward(request)
|
|
if(TOOL_DIRECTION_FORWARDS)
|
|
return dyntool_image_forward(request)
|
|
if(TOOL_DIRECTION_NEUTRAL)
|
|
return dyntool_image_neutral(request)
|
|
|
|
/**
|
|
* checks if a given person, using a given tool, can undertake this step.
|
|
*/
|
|
/datum/frame_step/proc/valid_interaction(datum/event_args/actor/actor, obj/item/using_tool, datum/frame2/frame_datum, obj/structure/frame2/frame)
|
|
switch(request_type)
|
|
if(FRAME_REQUEST_TYPE_INTERACT)
|
|
return isnull(using_tool)
|
|
if(FRAME_REQUEST_TYPE_ITEM)
|
|
return using_tool?.type == request
|
|
if(FRAME_REQUEST_TYPE_STACK)
|
|
var/obj/item/stack/using_stack = using_tool
|
|
return using_stack.can_use_as_type(request)
|
|
if(FRAME_REQUEST_TYPE_PROC)
|
|
return FALSE // override this proc
|
|
if(FRAME_REQUEST_TYPE_TOOL)
|
|
return using_tool?.tool_check(request, actor, frame, TOOL_OP_SILENT)
|
|
if(FRAME_REQUEST_TYPE_MATERIAL)
|
|
var/obj/item/stack/material/material_stack = using_tool
|
|
return istype(material_stack) && (ispath(request, /datum/prototype/material)? material_stack.material.type == request : material_stack.material.id == request)
|
|
return FALSE
|
|
|
|
/**
|
|
* This proc may assume the item is already type filtered to be the valid item / type / stack / whatever.
|
|
* If it isn't, do not istype(); allow the runtime to happen so we can yell at those responsible.
|
|
*/
|
|
/datum/frame_step/proc/check_consumption(datum/event_args/actor/actor, obj/item/using_tool, datum/frame2/frame_datum, obj/structure/frame2/frame)
|
|
switch(request_type)
|
|
if(FRAME_REQUEST_TYPE_STACK)
|
|
var/obj/item/stack/stack = using_tool
|
|
if(stack.amount < request_amount)
|
|
return FALSE
|
|
return TRUE
|
|
if(FRAME_REQUEST_TYPE_MATERIAL)
|
|
var/obj/item/stack/material/material_stack = using_tool
|
|
if(material_stack.amount < request_amount)
|
|
return FALSE
|
|
return TRUE
|
|
return TRUE
|
|
|
|
/**
|
|
* we take in time_needed, as computed by /datum/frame2.
|
|
*
|
|
* todo: should we be taking in time needed? this seems like the right option for now.
|
|
*/
|
|
/datum/frame_step/proc/perform_usage(datum/event_args/actor/actor, obj/item/using_tool, datum/frame2/frame_datum, obj/structure/frame2/frame, time_needed)
|
|
switch(request_type)
|
|
if(FRAME_REQUEST_TYPE_TOOL)
|
|
return frame.use_tool(
|
|
request,
|
|
using_tool,
|
|
actor,
|
|
delay = time_needed,
|
|
cost = request_cost,
|
|
)
|
|
else
|
|
return do_after(
|
|
actor.performer,
|
|
time_needed,
|
|
frame,
|
|
mobility_flags = MOBILITY_CAN_USE,
|
|
max_distance = using_tool?.reach || 1,
|
|
)
|
|
|
|
/datum/frame_step/proc/handle_consumption(datum/event_args/actor/actor, obj/item/using_tool, datum/frame2/frame_datum, obj/structure/frame2/frame)
|
|
switch(request_type)
|
|
if(FRAME_REQUEST_TYPE_ITEM)
|
|
if(!actor.performer.attempt_void_item_for_installation(using_tool))
|
|
actor.chat_feedback(
|
|
SPAN_WARNING("[using_tool] is stuck to your hand!"),
|
|
target = frame,
|
|
)
|
|
return FALSE
|
|
qdel(using_tool)
|
|
return TRUE
|
|
if(FRAME_REQUEST_TYPE_TOOL)
|
|
// we do the usage in perform_usainge
|
|
return TRUE
|
|
if(FRAME_REQUEST_TYPE_STACK)
|
|
var/obj/item/stack/stack = using_tool
|
|
return stack.use(request_amount)
|
|
if(FRAME_REQUEST_TYPE_MATERIAL)
|
|
var/obj/item/stack/material/material_stack = using_tool
|
|
return material_stack.use(request_amount)
|
|
return TRUE
|
|
|
|
/**
|
|
* called before frame is moved to new stage
|
|
*/
|
|
/datum/frame_step/proc/on_finish(datum/frame2/frame_datum, obj/structure/frame2/frame, datum/event_args/actor/actor, obj/item/using_item)
|
|
if(drop)
|
|
var/atom/drop_where = frame.drop_location()
|
|
if(ispath(drop, /obj/item/stack))
|
|
var/safety = 50
|
|
var/left = drop_amount
|
|
var/obj/item/stack/casted_stack = drop
|
|
do
|
|
var/dropping = min(left, initial(casted_stack.max_amount))
|
|
new drop(drop_where, dropping)
|
|
left -= dropping
|
|
while(--safety > 0 && left > 0)
|
|
else if(ispath(drop, /datum/prototype/material))
|
|
var/safety = 50
|
|
var/left = drop_amount
|
|
var/datum/prototype/material/resolved_material = RSmaterials.fetch(drop)
|
|
do
|
|
var/dropping = min(left, 50)
|
|
// todo: /datum/prototype/material based max stacks.
|
|
resolved_material.place_sheet(drop_where, dropping)
|
|
left -= dropping
|
|
while(--safety > 0 && left > 0)
|
|
else if(ispath(drop, /obj/item))
|
|
for(var/i in 1 to min(50, drop_amount))
|
|
new drop(drop_where)
|
|
|
|
/datum/frame_step/proc/feedback_begin(datum/event_args/actor/actor, datum/frame2/frame_datum, obj/structure/frame2/frame, obj/item/tool, time_needed)
|
|
// don't bother if it's that fast
|
|
if(time_needed <= 0.5 SECONDS)
|
|
return
|
|
if(use_custom_feedback)
|
|
// custom
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
visible = frame_datum.template_action_string(visible_text_begin, actor.performer, frame, tool),
|
|
audible = frame_datum.template_action_string(audible_text_begin, actor.performer, frame, tool),
|
|
otherwise_self = frame_datum.template_action_string(self_text_begin, actor.performer, frame, tool),
|
|
)
|
|
else
|
|
// default
|
|
standard_feedback_handling(actor, frame_datum, frame, tool, time_needed, TRUE)
|
|
|
|
/datum/frame_step/proc/feedback_finish(datum/event_args/actor/actor, datum/frame2/frame_datum, obj/structure/frame2/frame, obj/item/tool, time_taken)
|
|
if(use_custom_feedback)
|
|
// custom
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
visible = frame_datum.template_action_string(visible_text_end, actor.performer, frame, tool),
|
|
audible = frame_datum.template_action_string(audible_text_end, actor.performer, frame, tool),
|
|
otherwise_self = frame_datum.template_action_string(self_text_end, actor.performer, frame, tool),
|
|
)
|
|
else
|
|
// default
|
|
standard_feedback_handling(actor, frame_datum, frame, tool, time_taken, FALSE)
|
|
|
|
/datum/frame_step/proc/standard_feedback_handling(datum/event_args/actor/actor, datum/frame2/frame_datum, obj/structure/frame2/frame, obj/item/tool, time, beginning)
|
|
switch(request_type)
|
|
if(FRAME_REQUEST_TYPE_INTERACT)
|
|
if(beginning)
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
range = MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = "[actor.performer] starts [action_text_leading || "tinkering with "][frame][action_text_trailing || "."]",
|
|
audible = "You hear something being tinkered with.",
|
|
otherwise_self = "You start [action_text_leading || "tinkering with "][frame][action_text_trailing || "."]",
|
|
)
|
|
else
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
range = MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = "[actor.performer] finishes [action_text_leading || "tinkering with "][frame][action_text_trailing || "."]",
|
|
audible = "You hear something being tinkered with.",
|
|
otherwise_self = "You finish [action_text_leading || "tinkering with "][frame][action_text_trailing || "."]",
|
|
)
|
|
if(FRAME_REQUEST_TYPE_ITEM)
|
|
if(beginning)
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
range = MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = "[actor.performer] starts [action_text_leading || "tinkering with "][frame][action_text_trailing || " using [tool]."]",
|
|
audible = "You hear something being tinkered with.",
|
|
otherwise_self = "You start [action_text_leading || "tinkering with "][frame][action_text_trailing || " using [tool]."]",
|
|
)
|
|
else
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
range = MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = "[actor.performer] finishes [action_text_leading || "tinkering with "][frame][action_text_trailing || " using [tool]."]",
|
|
audible = "You hear something being tinkered with.",
|
|
otherwise_self = "You finish [action_text_leading || "tinkering with "][frame][action_text_trailing || " using [tool]."]",
|
|
)
|
|
if(FRAME_REQUEST_TYPE_MATERIAL, FRAME_REQUEST_TYPE_STACK)
|
|
var/name_to_use
|
|
if(request_type == FRAME_REQUEST_TYPE_MATERIAL)
|
|
var/datum/prototype/material/resolved_material = RSmaterials.fetch(request)
|
|
name_to_use = "[resolved_material.name || resolved_material.display_name] [resolved_material.sheet_plural_name]"
|
|
else
|
|
var/obj/item/stack/casted_stack = request
|
|
name_to_use = casted_stack.name
|
|
if(beginning)
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
range = MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = "[actor.performer] starts [action_text_leading || "inserting some [name_to_use] into "][frame][action_text_trailing || "."]",
|
|
audible = "You hear something being tinkered with.",
|
|
otherwise_self = "You start [action_text_leading || "inserting some [name_to_use] into "][frame][action_text_trailing || "."]",
|
|
)
|
|
else
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
range = MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = "[actor.performer] finishes [action_text_leading || "inserting some [name_to_use] into "][frame][action_text_trailing || "."]",
|
|
audible = "You hear something being tinkered with.",
|
|
otherwise_self = "You finish [action_text_leading || "inserting some [name_to_use] into "][frame][action_text_trailing || "."]",
|
|
)
|
|
if(FRAME_REQUEST_TYPE_TOOL)
|
|
if(beginning)
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
range = MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = "[actor.performer] starts [action_text_leading || "tinkering with "][frame][action_text_trailing || " with [tool]."]",
|
|
audible = "You hear something being tinkered with.",
|
|
otherwise_self = "You start [action_text_leading || "tinkering with "][frame][action_text_trailing || " with [tool]."]",
|
|
)
|
|
else
|
|
actor.visible_feedback(
|
|
target = frame,
|
|
range = MESSAGE_RANGE_CONSTRUCTION,
|
|
visible = "[actor.performer] finishes [action_text_leading || "tinkering with "][frame][action_text_trailing || " with [tool]."]",
|
|
audible = "You hear something being tinkered with.",
|
|
otherwise_self = "You finish [action_text_leading || "tinkering with "][frame][action_text_trailing || " with [tool]."]",
|
|
)
|