diff --git a/aurorastation.dme b/aurorastation.dme index 84d9fb61d0f..ac44de05ce8 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -542,6 +542,7 @@ #include "code\datums\components\skills\service\entertaining_skill_component.dm" #include "code\datums\components\skills\service\gardening_skill_component.dm" #include "code\datums\components\synthetic_endoskeleton\synthetic_endoskeleton.dm" +#include "code\datums\components\tools\tool_quality.dm" #include "code\datums\components\turf_click\turf_hand.dm" #include "code\datums\elements\_element.dm" #include "code\datums\elements\connect_loc.dm" diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm index 44da1d758a5..bf4a98969b9 100644 --- a/code/__DEFINES/components.dm +++ b/code/__DEFINES/components.dm @@ -26,6 +26,7 @@ #define FORENSICS_SKILL_COMPONENT /datum/component/skill/forensics #define PHARMACOLOGY_SKILL_COMPONENT /datum/component/skill/pharmacology #define SURGERY_SKILL_COMPONENT /datum/component/skill/surgery +#define TOOL_COMPONENT /datum/component/tool_quality_container #define CAROUSING_SKILL_COMPONENT /datum/component/skill/carousing #define TENACITY_SKILL_COMPONENT /datum/component/skill/tenacity diff --git a/code/__DEFINES/tools.dm b/code/__DEFINES/tools.dm index c682eb1f0f2..b1d1219a238 100644 --- a/code/__DEFINES/tools.dm +++ b/code/__DEFINES/tools.dm @@ -26,6 +26,12 @@ #define TOOL_HAMMER "hammer" #define TOOL_PEN "pen" +/** + * The "default value" a tool quality should be. Lower than this means a worse tool, higher than this means a better tool. + * This is a floating point. I'm not making any more defines than this, tool quality can be any real number between +/- INFINITY. + */ +#define STANDARD_TOOL_LEVEL 3.0 + // If delay between the start and the end of tool operation is less than MIN_TOOL_SOUND_DELAY, // tool sound is only played when op is started. If not, it's played twice. #define MIN_TOOL_SOUND_DELAY 20 @@ -75,3 +81,40 @@ return return_value return null + +/** + * Retrieves the given tool level from a target, which can either be null (logically 0 if you're doing math) or an actual number. + * This can safely be used in math equations, but is slow if you need more than one tool check. Do not ever divide by this or use it in a ternary operator. + * Use GET_TOOL_QUALITIES(target) instead if you are checking bulk tool qualities. + */ +#define GET_TOOL_LEVEL(target, tool) astype(target.GetComponent(/datum/component/tool_quality_container), /datum/component/tool_quality_container)?.tool_qualities[tool] + +/** + * Retrieves an associative list of all of a target's tool qualities, if any. + * You should still null check this before using it. + * This is not safe to drop-in to math equations. + * Use GET_TOOL_LEVEL(target, tool) instead if you only need one numerical tool level. + */ +#define GET_TOOL_QUALITIES(target) astype(target.GetComponent(/datum/component/tool_quality_container), /datum/component/tool_quality_container)?.tool_qualities + +/** + * The primary intended method of INITIALIZING tool qualities. + * This will declare a variable as per outvar which is typed as a /datum/component/tool_quality_container, + * and since this variable works via LoadComponent, it will NEVER require null checking. + * + * Beware that this macro will erase any pre-existing tool qualities if present, so don't use it outside of Initialize(). + * If you want to change tool qualities AFTER initializing, do so via SET_TOOL_QUALITIES. + */ +#define LOAD_TOOL_QUALITIES(target, qualities, outvar) \ + var/datum/component/tool_quality_container/outvar = target.LoadComponent(/datum/component/tool_quality_container); \ + outvar.tool_qualities = qualities + +/** + * A variant of LOAD_TOOL_QUALITIES that will set the matching key/value pairs for an item's tool qualities to a defined set. + * EG: Item A has qualities "TOOL_CROWBAR = 3, TOOL_MULTITOOL = 1", and I call SET_TOOL_QUALITIES(src, alist(TOOL_MULTITOOL = 5, TOOL_SCALPEL = 1), toolComp) + * Then the it will have the qualities Crowbar 3, Multitool 5, Scalpel 1. + */ +#define SET_TOOL_QUALITIES(target, qualities, outvar) \ + var/datum/component/tool_quality_container/outvar = target.LoadComponent(/datum/component/tool_quality_container); \ + for(var/key, value in qualities) \ + outvar.tool_qualities[key] = value diff --git a/code/datums/components/tools/tool_quality.dm b/code/datums/components/tools/tool_quality.dm new file mode 100644 index 00000000000..28553830c4b --- /dev/null +++ b/code/datums/components/tools/tool_quality.dm @@ -0,0 +1,9 @@ + +/datum/component/tool_quality_container + /** + * The set of all tool qualities stored on a datum. Associative list of tool type to tool level. + * Formatted like alist(TOOL_CROWBAR = 3, TOOL_CHISEL = 1). + * Access tool qualities directly with GET_TOOL_LEVEL(target, TOOL_CROWBAR) + * or fetch the entire associative list via GET_TOOL_QUALITIES(target) + */ + var/alist/tool_qualities = alist() diff --git a/code/game/gamemodes/changeling/implements/items.dm b/code/game/gamemodes/changeling/implements/items.dm index 999f44c8bc7..75b176f8118 100644 --- a/code/game/gamemodes/changeling/implements/items.dm +++ b/code/game/gamemodes/changeling/implements/items.dm @@ -20,6 +20,7 @@ canremove = FALSE var/mob/living/creator tool_behaviour = TOOL_CROWBAR + tool_quality = STANDARD_TOOL_LEVEL + 1 /obj/item/melee/arm_blade/New() ..() diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 78e6f666c9d..963d75056d5 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -234,8 +234,16 @@ /// Requires the usual implementation requirements for new persistent types but provides a single implementation for trash logic var/persistency_considered_trash = FALSE - /// How a tool acts when you use it on something, such as wirecutters cutting wires while multitools measure power + /** + * How a tool acts when you use it on something, such as wirecutters cutting wires while multitools measure power. + * This merely sets the initial starting tool quality for a given tool. + * If a tool wants to have more than one quality, it can do so via SET_TOOL_QUALITIES() in its own Initialize() call + * + * Do note that this is merely just an expedience for /obj/item. Any /atom/ is allowed to have tool qualities. + */ var/tool_behaviour = null + /// Determines the starting tool level for a tool's basic use if any. tool_behavior must be set for this to apply. + var/tool_quality = STANDARD_TOOL_LEVEL /obj/item/Initialize(mapload, ...) . = ..() @@ -243,6 +251,9 @@ set_initial_maptext() check_maptext() + if (tool_behaviour) + LOAD_TOOL_QUALITIES(src, alist(tool_behavior = tool_quality), toolComp) + /obj/item/Destroy() if(ismob(loc)) var/mob/m = loc diff --git a/code/game/objects/items/tools/crowbar.dm b/code/game/objects/items/tools/crowbar.dm index 5ba79d571c0..4fc2304bd4a 100644 --- a/code/game/objects/items/tools/crowbar.dm +++ b/code/game/objects/items/tools/crowbar.dm @@ -24,6 +24,10 @@ toolspeed = 1 var/force_opens = FALSE +/obj/item/crowbar/Initialize() + . = ..() + SET_TOOL_QUALITIES(src, alist(TOOL_RETRACTOR = STANDARD_TOOL_LEVEL - 2), toolComp) + /obj/item/crowbar/red icon = 'icons/obj/tools.dmi' icon_state = "crowbar_red" diff --git a/code/game/objects/items/tools/tools.dm b/code/game/objects/items/tools/tools.dm index bda3042b8e3..965f1d1d36e 100644 --- a/code/game/objects/items/tools/tools.dm +++ b/code/game/objects/items/tools/tools.dm @@ -736,6 +736,7 @@ "multitool" ) var/current_tool = 1 + var/combitool_level = STANDARD_TOOL_LEVEL - 1 /obj/item/combitool/feedback_hints(mob/user, distance, is_adjacent) . = list() @@ -764,22 +765,27 @@ usesound = 'sound/items/wrench.ogg' surgerysound = 'sound/items/surgery/bonesetter.ogg' tool_behaviour = TOOL_WRENCH + LOAD_TOOL_QUALITIES(src, alist(TOOL_WRENCH = combitool_level), toolComp) if("screwdriver") usesound = 'sound/items/screwdriver.ogg' surgerysound = 'sound/items/screwdriver.ogg' tool_behaviour = TOOL_SCREWDRIVER + LOAD_TOOL_QUALITIES(src, alist(TOOL_SCREWDRIVER = combitool_level), toolComp) if("wirecutters") usesound = 'sound/items/wirecutter.ogg' surgerysound = 'sound/items/surgery/hemostat.ogg' tool_behaviour = TOOL_WIRECUTTER + LOAD_TOOL_QUALITIES(src, alist(TOOL_WIRECUTTER = combitool_level), toolComp) if("crowbar") usesound = SFX_CROWBAR surgerysound = 'sound/items/surgery/retractor.ogg' tool_behaviour = TOOL_CROWBAR + LOAD_TOOL_QUALITIES(src, alist(TOOL_CROWBAR = combitool_level), toolComp) if("multitool") usesound = null surgerysound = null tool_behaviour = TOOL_MULTITOOL + LOAD_TOOL_QUALITIES(src, alist(TOOL_MULTITOOL = combitool_level), toolComp) update_tool() return 1 @@ -801,6 +807,7 @@ "screwdriver", "wrench" ) + var/powerdrill_level = STANDARD_TOOL_LEVEL + 1 /obj/item/powerdrill/mechanics_hints(mob/user, distance, is_adjacent) . += ..() @@ -810,6 +817,7 @@ . = ..() // When spawned, it has the screwdriver bit enabled. Reflect that. tool_behaviour = TOOL_SCREWDRIVER + LOAD_TOOL_QUALITIES(src, alist(TOOL_SCREWDRIVER = powerdrill_level), toolComp) update_tool() /obj/item/powerdrill/set_initial_maptext() @@ -832,11 +840,13 @@ icon_state = "impact_wrench-screw" check_maptext(SMALL_FONTS(7, "S")) tool_behaviour = TOOL_SCREWDRIVER + LOAD_TOOL_QUALITIES(src, alist(TOOL_SCREWDRIVER = powerdrill_level), toolComp) else if(tool == TOOL_WRENCH) usesound = 'sound/items/air_wrench.ogg' icon_state = "impact_wrench-wrench" check_maptext(SMALL_FONTS(7, "W")) tool_behaviour = TOOL_WRENCH + LOAD_TOOL_QUALITIES(src, alist(TOOL_WRENCH = powerdrill_level), toolComp) /obj/item/powerdrill/attack_self(var/mob/user) if(++current_tool > tools.len) diff --git a/code/game/objects/items/weapons/surgery_tools.dm b/code/game/objects/items/weapons/surgery_tools.dm index e8039b116c2..fe19cdb433c 100644 --- a/code/game/objects/items/weapons/surgery_tools.dm +++ b/code/game/objects/items/weapons/surgery_tools.dm @@ -123,6 +123,7 @@ matter = list(DEFAULT_WALL_MATERIAL = 12500, MATERIAL_GLASS = 7500) damtype = "fire" force = 18 + tool_quality = STANDARD_TOOL_LEVEL + 1 /obj/item/surgery/scalpel/manager name = "incision management system" @@ -131,6 +132,7 @@ surgerysound = 'sound/items/surgery/cautery.ogg' matter = list (DEFAULT_WALL_MATERIAL = 12500, MATERIAL_GLASS = 7500, MATERIAL_SILVER = 1500, MATERIAL_GOLD = 1500, MATERIAL_DIAMOND = 750) force = 8 + tool_quality = STANDARD_TOOL_LEVEL + 2 /* * Circular Saw diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index b0aad7f71f6..a2b47e00008 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -366,6 +366,7 @@ attack_verb = list("bashed", "bludgeoned", "thrashed", "whacked", "slashed", "cut") sharp = TRUE tool_behaviour = TOOL_CROWBAR + tool_quality = STANDARD_TOOL_LEVEL - 2 // Flags. diff --git a/code/modules/psionics/equipment/psipower_tinker.dm b/code/modules/psionics/equipment/psipower_tinker.dm index 4be16e71e91..a52cab7192c 100644 --- a/code/modules/psionics/equipment/psipower_tinker.dm +++ b/code/modules/psionics/equipment/psipower_tinker.dm @@ -3,7 +3,7 @@ icon_state = "crowbar" force = 11 tool_behaviour = TOOL_CROWBAR - var/list/tools = list("crowbar", "wrench", "screwdriver", "cutters") + var/list/tools = list(TOOL_CROWBAR, TOOL_WRENCH, TOOL_SCREWDRIVER, TOOL_WIRECUTTER) /obj/item/psychic_power/tinker/Initialize(mapload, ...) . = ..() @@ -26,7 +26,8 @@ if(!owner || loc != owner) return - tool_behaviour = lowertext(choice) + tool_behaviour = choice + LOAD_TOOL_QUALITIES(src, alist(choice = owner.check_psi_sensitivity()), toolComp) name = "psychokinetic [tool_behaviour]" icon_state = tool_behaviour to_chat(owner, SPAN_NOTICE("You begin emulating \a [tool_behaviour].")) diff --git a/html/changelogs/hellfirejag-tool-quality-framework.yml b/html/changelogs/hellfirejag-tool-quality-framework.yml new file mode 100644 index 00000000000..97ce068c205 --- /dev/null +++ b/html/changelogs/hellfirejag-tool-quality-framework.yml @@ -0,0 +1,4 @@ +author: Hellfirejag +delete-after: True +changes: + - rscadd: "Added a code framework for Tool Quality Levels."