From f5efc752a2a2294a37c2bdbea20db961285e536b Mon Sep 17 00:00:00 2001 From: Bloop Date: Wed, 12 Apr 2023 15:55:28 -0400 Subject: [PATCH] [MODULAR] Refactors glassblowing + gives it a new UI (#20495) * Refactors glassblowing, adds a new tgui for it * quality pass 1 * quality pass 2 * Final passes * Final touches * this was a bit too horizontal * ewps * just some last minute tidying of unneeded bits * no touch hot glass * Merge branch 'glass-fix' of https://github.com/vinylspiders/Skyrat-customization-expansion into glass-fix --- .../primitive_production/code/glassblowing.dm | 488 ++++++++++++------ .../modules/reagent_forging/code/forge.dm | 1 + .../packages/tgui/interfaces/GlassBlowing.tsx | 257 +++++++++ 3 files changed, 596 insertions(+), 150 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/GlassBlowing.tsx diff --git a/modular_skyrat/modules/primitive_production/code/glassblowing.dm b/modular_skyrat/modules/primitive_production/code/glassblowing.dm index e04c93f1366..ad734ef6980 100644 --- a/modular_skyrat/modules/primitive_production/code/glassblowing.dm +++ b/modular_skyrat/modules/primitive_production/code/glassblowing.dm @@ -1,4 +1,9 @@ #define DEFAULT_TIMED (4 SECONDS) +#define STEP_BLOW "blow" +#define STEP_SPIN "spin" +#define STEP_PADDLE "paddle" +#define STEP_SHEAR "shear" +#define STEP_JACKS "jacks" /obj/item/glassblowing icon = 'modular_skyrat/modules/primitive_production/icons/prim_fun.dmi' @@ -13,14 +18,16 @@ /datum/export/glassblowing cost = CARGO_CRATE_VALUE * 5 unit_name = "glassblowing product" - export_types = list(/obj/item/glassblowing/glass_lens, - /obj/item/glassblowing/glass_globe, - /obj/item/reagent_containers/cup/bowl/blowing_glass, - /obj/item/reagent_containers/cup/beaker/large/blowing_glass, - /obj/item/plate/blowing_glass) + export_types = list( + /obj/item/glassblowing/glass_lens, + /obj/item/glassblowing/glass_globe, + /obj/item/reagent_containers/cup/bowl/blowing_glass, + /obj/item/reagent_containers/cup/beaker/large/blowing_glass, + /obj/item/plate/blowing_glass + ) /datum/export/glassblowing/sell_object(obj/O, datum/export_report/report, dry_run, apply_elastic = FALSE) //I really dont want them to feel gimped - . = ..() + return ..() /obj/item/glassblowing/glass_lens name = "glass lens" @@ -55,87 +62,98 @@ name = "molten glass" desc = "A glob of molten glass, ready to be shaped into art." icon_state = "molten_glass" - //the cooldown if its still molten / requires heating up + ///the cooldown if its still molten / requires heating up COOLDOWN_DECLARE(remaining_heat) - ///the list of required steps to produce the chosen_item: blowing, spinning, paddles, shears, jacks - var/list/required_actions = list(0,0,0,0,0) - ///the list of current steps: blowing, spinning, paddles, shears, jacks - var/list/current_actions = list(0,0,0,0,0) ///the typepath of the item that will be produced when the required actions are met var/chosen_item + ///the list of steps remaining + var/list/steps_remaining + ///the amount of time this glass will stay heated, updated each time it gets put in the forge based on the user's skill + var/total_time + ///whether this glass's chosen item has completed all its steps. So we don't have to keep checking this a million times once it's done. + var/is_finished = FALSE /obj/item/glassblowing/molten_glass/examine(mob/user) . = ..() - if(COOLDOWN_FINISHED(src, remaining_heat)) - . += span_warning("[src] has cooled down and will require reheating to modify!") - if(required_actions[1]) - . += "You require [required_actions[1]] blowing actions!" - . += "You currently have [current_actions[1]] blowing actions!" - if(required_actions[2]) - . += "You require [required_actions[2]] spinning actions!" - . += "You currently have [current_actions[2]] spinning actions!" - if(required_actions[3]) - . += "You require [required_actions[3]] paddling actions!" - . += "You currently have [current_actions[3]] paddling actions!" - if(required_actions[4]) - . += "You require [required_actions[4]] shearing actions!" - . += "You currently have [current_actions[4]] shearing actions!" - if(required_actions[5]) - . += "You require [required_actions[5]] jacking actions!" - . += "You currently have [current_actions[5]] jacking actions!" + . += get_examine_message(src) -/obj/item/glassblowing/molten_glass/pickup(mob/user) - if(!isliving(user)) +/obj/item/glassblowing/molten_glass/pickup(mob/living/user) + if(!istype(user)) return ..() + . = ..() - var/mob/living/living_user = user + + try_burn_user(user) + +/** + * Tries to burn the user if the glass is still molten hot. + * + * Arguments: + * * mob/living/user - user to burn + */ +/obj/item/glassblowing/molten_glass/proc/try_burn_user(mob/living/user) if(!COOLDOWN_FINISHED(src, remaining_heat)) - to_chat(living_user, span_warning("You burn your hands trying to pick up [src]!")) - living_user.adjustFireLoss(15) + to_chat(user, span_warning("You burn your hands trying to pick up [src]!")) + user.emote("scream") user.dropItemToGround(src) + var/obj/item/bodypart/affecting = user.get_active_hand() + return affecting?.receive_damage(0, 15, wound_bonus = CANT_WOUND) + user.investigate_log("was burned their hand on [src] for [15] at [AREACOORD(user)]", INVESTIGATE_CRAFTING) + +/obj/item/glassblowing/blowing_rod + name = "blowing rod" + desc = "A tool that is used to hold the molten glass as well as help shape it." + icon_state = "blow_pipe_empty" + tool_behaviour = TOOL_BLOWROD + /// Whether the rod is in use currently; will try to prevent many other actions on it + var/in_use = FALSE + /// A ref to the glass item being blown + var/datum/weakref/glass_ref /obj/item/glassblowing/blowing_rod/examine(mob/user) . = ..() - var/obj/item/glassblowing/molten_glass/find_glass = locate() in contents - if(!find_glass) + var/obj/item/glassblowing/molten_glass/glass = glass_ref.resolve() + if(!glass) return - if(COOLDOWN_FINISHED(find_glass, remaining_heat)) - . += span_warning("[src] has cooled down and will require reheating to modify!") - if(find_glass.required_actions[1]) - . += "You require [find_glass.required_actions[1]] blowing actions!" - . += "You currently have [find_glass.current_actions[1]] blowing actions!" - if(find_glass.required_actions[2]) - . += "You require [find_glass.required_actions[2]] spinning actions!" - . += "You currently have [find_glass.current_actions[2]] spinning actions!" - if(find_glass.required_actions[3]) - . += "You require [find_glass.required_actions[3]] paddling actions!" - . += "You currently have [find_glass.current_actions[3]] paddling actions!" - if(find_glass.required_actions[4]) - . += "You require [find_glass.required_actions[4]] shearing actions!" - . += "You currently have [find_glass.current_actions[4]] shearing actions!" - if(find_glass.required_actions[5]) - . += "You require [find_glass.required_actions[5]] jacking actions!" - . += "You currently have [find_glass.current_actions[5]] jacking actions!" + . += get_examine_message(glass) -/obj/item/glassblowing/blowing_rod/proc/check_valid_table(mob/living/user) - var/skill_level = user.mind.get_skill_level(/datum/skill/production) - if(skill_level >= SKILL_LEVEL_MASTER) //as a master, you can skip tables - return TRUE - for(var/obj/structure/table/check_table in range(1, get_turf(src))) - if(!(check_table.resistance_flags & FLAMMABLE)) - return TRUE //if you can find a table that is not flammable, good - return FALSE + +/** + * Create the examine message and return it. + * + * This will include all the remaining steps and whether the glass has cooled down or not. + * + * Arguments: + * * obj/item/glassblowing/molten_glass/glass - the glass object being examined + * + * Returns the examine message. + */ +/obj/item/glassblowing/proc/get_examine_message(obj/item/glassblowing/molten_glass/glass) + if(COOLDOWN_FINISHED(glass, remaining_heat)) + . += span_warning("The glass has cooled down and will require reheating to modify! ") + if(glass.steps_remaining[STEP_BLOW]) + . += "The glass requires [glass.steps_remaining[STEP_BLOW]] more blowing actions! " + if(glass.steps_remaining[STEP_SPIN]) + . += "The glass requires [glass.steps_remaining[STEP_SPIN]] more spinning actions! " + if(glass.steps_remaining[STEP_PADDLE]) + . += "The glass requires [glass.steps_remaining[STEP_PADDLE]] more paddling actions! " + if(glass.steps_remaining[STEP_SHEAR]) + . += "The glass requires [glass.steps_remaining[STEP_SHEAR]] more shearing actions! " + if(glass.steps_remaining[STEP_JACKS]) + . += "The glass requires [glass.steps_remaining[STEP_JACKS]] more jacking actions!" /obj/item/glassblowing/blowing_rod/afterattack(atom/target, mob/user, proximity_flag, click_parameters) if(!proximity_flag) return ..() if(istype(target, /obj/item/glassblowing/molten_glass)) var/obj/item/glassblowing/molten_glass/attacking_glass = target - var/obj/item/glassblowing/molten_glass/find_glass = locate() in contents - if(find_glass) + var/obj/item/glassblowing/molten_glass/glass = glass_ref?.resolve() + if(glass) to_chat(user, span_warning("[src] already has some glass on it!")) return - attacking_glass.forceMove(src) + if(!user.transferItemToLoc(attacking_glass, src)) + return + glass_ref = WEAKREF(attacking_glass) to_chat(user, span_notice("[src] picks up [target].")) icon_state = "blow_pipe_full" return @@ -143,138 +161,303 @@ /obj/item/glassblowing/blowing_rod/attackby(obj/item/attacking_item, mob/living/user, params) var/actioning_speed = user.mind.get_skill_modifier(/datum/skill/production, SKILL_SPEED_MODIFIER) * DEFAULT_TIMED - var/obj/item/glassblowing/molten_glass/find_glass = locate() in contents + var/obj/item/glassblowing/molten_glass/glass = glass_ref?.resolve() if(istype(attacking_item, /obj/item/glassblowing/molten_glass)) - if(find_glass) + if(glass) to_chat(user, span_warning("[src] already has some glass on it still!")) return - attacking_item.forceMove(src) + if(!user.transferItemToLoc(attacking_item, src)) + return + glass_ref = WEAKREF(attacking_item) to_chat(user, span_notice("[src] picks up [attacking_item].")) icon_state = "blow_pipe_full" return if(istype(attacking_item, /obj/item/glassblowing/paddle)) - do_glass_step(3, user, "paddle", actioning_speed) + do_glass_step(STEP_PADDLE, user, actioning_speed, glass) return if(istype(attacking_item, /obj/item/glassblowing/shears)) - do_glass_step(4, user, "shear", actioning_speed) + do_glass_step(STEP_SHEAR, user, actioning_speed, glass) return if(istype(attacking_item, /obj/item/glassblowing/jacks)) - do_glass_step(5, user, "jack", actioning_speed) + do_glass_step(STEP_JACKS, user, actioning_speed, glass) return return ..() /obj/item/glassblowing/blowing_rod/attack_self(mob/user, modifiers) - var/obj/item/glassblowing/molten_glass/find_glass = locate() in contents - var/actioning_speed = user.mind.get_skill_modifier(/datum/skill/production, SKILL_SPEED_MODIFIER) * DEFAULT_TIMED - - if(find_glass) - if(COOLDOWN_FINISHED(find_glass, remaining_heat)) - to_chat(user, span_warning("The glass has cooled down far too much to be handled...")) - return - if(in_use) - to_chat(user, span_warning("[src] is busy being used!")) - return - in_use = TRUE - if(!find_glass.chosen_item) - var/choice = tgui_input_list(user, "What would you like to make?", "Choice Selection", list("Plate", "Bowl", "Globe", "Cup", "Lens", "Bottle")) - if(!choice) - in_use = FALSE - return - switch(choice) - if("Plate") - find_glass.chosen_item = /obj/item/plate/blowing_glass - find_glass.required_actions = list(3,3,3,0,0) //blowing, spinning, paddling - if("Bowl") - find_glass.chosen_item = /obj/item/reagent_containers/cup/bowl/blowing_glass - find_glass.required_actions = list(2,2,2,0,3) //blowing, spinning, paddling - if("Globe") - find_glass.chosen_item = /obj/item/glassblowing/glass_globe - find_glass.required_actions = list(6,3,0,0,0) //blowing, spinning - if("Cup") - find_glass.chosen_item = /obj/item/reagent_containers/cup/beaker/large/blowing_glass - find_glass.required_actions = list(3,3,3,0,0) //blowing, spinning, paddling - if("Lens") - find_glass.chosen_item = /obj/item/glassblowing/glass_lens - find_glass.required_actions = list(0,0,3,3,3) //paddling, shearing, jacking - if("Bottle") - find_glass.chosen_item = /obj/item/reagent_containers/cup/glass/bottle/small - find_glass.required_actions = list(3,2,3,0,0) //blowing, spinning, paddling - in_use = FALSE - return - else - in_use = FALSE - var/action_choice = tgui_alert(user, "What would you like to do?", "Action Selection", list("Blow", "Spin", "Remove")) - if(!action_choice) - return - switch(action_choice) - if("Blow") - do_glass_step(1, user, "blow", actioning_speed) - if("Spin") - do_glass_step(2, user, "spin", actioning_speed) - if("Remove") - for(var/iterate in 1 to 5) - if(find_glass.current_actions[iterate] < find_glass.required_actions[iterate]) - remove_glass() - return - new find_glass.chosen_item(get_turf(src)) - user.mind.adjust_experience(/datum/skill/production, 30) - qdel(find_glass) - icon_state = "blow_pipe_empty" - return - return return ..() +/obj/item/glassblowing/blowing_rod/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GlassBlowing", name) + ui.open() + +/obj/item/glassblowing/blowing_rod/ui_data() + var/obj/item/glassblowing/molten_glass/glass = glass_ref?.resolve() + + var/data = list() + data["inUse"] = in_use + + if(glass) + data["glass"] = list( + timeLeft = COOLDOWN_TIMELEFT(glass, remaining_heat), + totalTime = glass.total_time, + chosenItem = null, + stepsRemaining = glass.steps_remaining, + isFinished = glass.is_finished + ) + + var/obj/item_path = glass.chosen_item + data["glass"]["chosenItem"] = item_path ? list(name = initial(item_path.name), type = item_path) : null + else + data["glass"] = null + + return data + +/obj/item/glassblowing/blowing_rod/ui_act(action, params) + . = ..() + if(.) + return + if(!Adjacent(usr)) + return + add_fingerprint(usr) + usr.set_machine(src) + + var/obj/item/glassblowing/molten_glass/glass = glass_ref?.resolve() + var/actioning_speed = usr.mind.get_skill_modifier(/datum/skill/production, SKILL_SPEED_MODIFIER) * DEFAULT_TIMED + + if(!glass) + return + + if(action == "Remove") + if(!glass.chosen_item) + remove_glass(usr, glass) + in_use = FALSE + return + + if(glass.is_finished) + create_item(usr, glass) + in_use = FALSE + else + remove_glass(usr, glass) + return + + if(!glass.chosen_item) + switch(action) + if("Plate") + glass.chosen_item = /obj/item/plate/blowing_glass + glass.steps_remaining = list(blow=3,spin=3,paddle=3,shear=0,jacks=0) //blowing, spinning, paddling + if("Bowl") + glass.chosen_item = /obj/item/reagent_containers/cup/bowl/blowing_glass + glass.steps_remaining = list(blow=2,spin=2,paddle=2,shear=0,jacks=3) //blowing, spinning, paddling + if("Globe") + glass.chosen_item = /obj/item/glassblowing/glass_globe + glass.steps_remaining = list(blow=6,spin=3,paddle=0,shear=0,jacks=0) //blowing, spinning + if("Cup") + glass.chosen_item = /obj/item/reagent_containers/cup/beaker/large/blowing_glass + glass.steps_remaining = list(blow=3,spin=3,paddle=3,shear=0,jacks=0) //blowing, spinning, paddling + if("Lens") + glass.chosen_item = /obj/item/glassblowing/glass_lens + glass.steps_remaining = list(blow=0,spin=0,paddle=3,shear=3,jacks=3) //paddling, shearing, jacking + if("Bottle") + glass.chosen_item = /obj/item/reagent_containers/cup/glass/bottle/small + glass.steps_remaining = list(blow=3,spin=2,paddle=3,shear=0,jacks=0) //blowing, spinning, paddling + else + switch(action) + if("Blow") + do_glass_step(STEP_BLOW, usr, actioning_speed, glass) + if("Spin") + do_glass_step(STEP_SPIN, usr, actioning_speed, glass) + if("Paddle") + do_glass_step(STEP_PADDLE, usr, actioning_speed, glass) + if("Shear") + do_glass_step(STEP_SHEAR, usr, actioning_speed, glass) + if("Jacks") + do_glass_step(STEP_JACKS, usr, actioning_speed, glass) + if("Cancel") + glass.chosen_item = null + glass.steps_remaining = null + glass.is_finished = FALSE + to_chat(usr, span_notice("You start over with the [src].")) + + +/** + * Removes the glass object from the rod. + * + * Try to put the glass into the user's hands, or on the floor if that fails. + * + * Arguments: + * * mob/user - the mob doing the removing + * * obj/item/glassblowing/molten_glass/glass - the glass object + * + * Returns TRUE or FALSE. + */ +/obj/item/glassblowing/blowing_rod/proc/remove_glass(mob/user, obj/item/glassblowing/molten_glass/glass) + if(!glass) + return + + in_use = FALSE + user.put_in_hands(glass) + glass.try_burn_user(user) + glass_ref = null + icon_state = "blow_pipe_empty" + +/** + * Creates the finished product and delete the glass object used to make it. + * + * Try to put the finished product into the user's hands + * + * Arguments: + * * mob/user - the user doing the creating + * * obj/item/glassblowing/molten_glass/glass - the glass object + * + * Returns TRUE or FALSE. + */ +/obj/item/glassblowing/blowing_rod/proc/create_item(mob/user, obj/item/glassblowing/molten_glass/glass) + if(!glass) + return + if(in_use) + return + + in_use = TRUE + user.put_in_hands(new glass.chosen_item) + user.mind.adjust_experience(/datum/skill/production, 30) + glass_ref = null + qdel(glass) + icon_state = "blow_pipe_empty" + return + +/** + * Display fail message and reset in_use. + * + * Craft is finished when all steps in steps_remaining are 0. + * + * Arguments: + * * mob/user - mob to display to + * * message - to display + */ /obj/item/glassblowing/blowing_rod/proc/fail_message(message, mob/user) to_chat(user, span_warning(message)) in_use = FALSE -/obj/item/glassblowing/blowing_rod/proc/do_glass_step(number, mob/user, message, actioning_speed) - var/obj/item/glassblowing/molten_glass/find_glass = locate() in contents - if(!find_glass) +/** + * Try to do a glassblowing action. + * + * Checks for a table and valid tool if applicable, and updates the steps_remaining on the glass object. + * + * Arguments: + * * step_id - the step id e.g. STEP_BLOW + * * actioning_speed - the speed based on the user's production skill + * * obj/item/glassblowing/molten_glass/glass - the glass object + */ +/obj/item/glassblowing/blowing_rod/proc/do_glass_step(step_id, mob/user, actioning_speed, obj/item/glassblowing/molten_glass/glass) + if(!glass) return if(in_use) return + in_use = TRUE + if(!check_valid_table(user)) fail_message("You must be near a non-flammable table!", user) return - to_chat(user, span_notice("You begin to [message] [src].")) + if(!check_valid_tool(user, step_id)) + return + to_chat(user, span_notice("You begin to [step_id] [src].")) if(!do_after(user, actioning_speed, target = src)) fail_message("You interrupt an action!", user) return - if(!check_valid_table(user)) - fail_message("You must be near a non-flammable table!", user) - return - find_glass.current_actions[number]++ - to_chat(user, span_notice("You finish trying to [message] [src].")) + + if(glass.steps_remaining) + // We do not want to have negative values here + if(glass.steps_remaining[step_id] > 0) + glass.steps_remaining[step_id]-- + if(check_finished(glass)) + glass.is_finished = TRUE + in_use = FALSE + + to_chat(user, span_notice("You finish trying to [step_id] [src].")) user.mind.adjust_experience(/datum/skill/production, 10) -/obj/item/glassblowing/blowing_rod/proc/remove_glass() - var/obj/item/glassblowing/molten_glass/find_glass = locate() in contents - if(!find_glass) - return + +/** + * Check if there is a non-flammable table nearby to do the crafting on. + * + * If the user is a master in the production skill, they can skip tables. + * + * Arguments: + * * mob/living/user - the mob doing the action + * + * Returns TRUE or FALSE. + */ +/obj/item/glassblowing/blowing_rod/proc/check_valid_table(mob/living/user) + var/skill_level = user.mind.get_skill_level(/datum/skill/production) + if(skill_level >= SKILL_LEVEL_MASTER) // + return TRUE + for(var/obj/structure/table/check_table in range(1, get_turf(src))) + if(!(check_table.resistance_flags & FLAMMABLE)) + return TRUE + return FALSE + +/** + * Check if user is carrying the proper tool for the step. + * + * Arguments: + * * mob/living/carbon/human/user - the mob doing the action + * * step_id - the step id of the action being done + * + * Returns TRUE or FALSE. + */ +/obj/item/glassblowing/blowing_rod/proc/check_valid_tool(mob/living/carbon/human/user, step_id) + if(!istype(user)) + return FALSE + + var/obj/item/glassblowing/tool_path + switch(step_id) + if(STEP_BLOW) + return TRUE + if(STEP_SPIN) + return TRUE + if(STEP_PADDLE) + tool_path = /obj/item/glassblowing/paddle + if(STEP_SHEAR) + tool_path = /obj/item/glassblowing/shears + if(STEP_JACKS) + tool_path = /obj/item/glassblowing/jacks + + if(user.is_holding_item_of_type(tool_path)) + return TRUE + + balloon_alert(user, "need to be holding [initial(tool_path.name)]!") in_use = FALSE - find_glass.forceMove(get_turf(src)) - icon_state = "blow_pipe_empty" + return FALSE + +/** + * Checks if the glass is ready to craft into its chosen item. + * + * Craft is finished when all steps in steps_remaining are 0. + * + * Arguments: + * * obj/item/glassblowing/molten_glass/glass - the glass object + * + * Returns TRUE or FALSE. + */ +/obj/item/glassblowing/blowing_rod/proc/check_finished(obj/item/glassblowing/molten_glass/glass) + for(var/step_id in glass.steps_remaining) + if(glass.steps_remaining[step_id] != 0) + return FALSE + return TRUE /datum/crafting_recipe/glassblowing_recipe reqs = list(/obj/item/stack/sheet/iron = 5) category = CAT_MISC -/obj/item/glassblowing/blowing_rod - name = "blowing rod" - desc = "A tool that is used to hold the molten glass as well as help shape it." - icon_state = "blow_pipe_empty" - ///whether the item is in use currently; will try to prevent many other actions on it - var/in_use = FALSE - tool_behaviour = TOOL_BLOWROD - /datum/crafting_recipe/glassblowing_recipe/glass_blowing_rod name = "Glass-blowing Blowing Rod" result = /obj/item/glassblowing/blowing_rod @@ -326,3 +509,8 @@ return ..() #undef DEFAULT_TIMED +#undef STEP_BLOW +#undef STEP_SPIN +#undef STEP_PADDLE +#undef STEP_SHEAR +#undef STEP_JACKS diff --git a/modular_skyrat/modules/reagent_forging/code/forge.dm b/modular_skyrat/modules/reagent_forging/code/forge.dm index 06873fc6082..ba95e294210 100644 --- a/modular_skyrat/modules/reagent_forging/code/forge.dm +++ b/modular_skyrat/modules/reagent_forging/code/forge.dm @@ -861,6 +861,7 @@ return TOOL_ACT_TOOLTYPE_SUCCESS COOLDOWN_START(find_glass, remaining_heat, glassblowing_amount) + find_glass.total_time = glassblowing_amount to_chat(user, span_notice("You finish heating up [blowing_item].")) user.mind.adjust_experience(/datum/skill/smithing, 5) user.mind.adjust_experience(/datum/skill/production, 10) diff --git a/tgui/packages/tgui/interfaces/GlassBlowing.tsx b/tgui/packages/tgui/interfaces/GlassBlowing.tsx new file mode 100644 index 00000000000..3116033910a --- /dev/null +++ b/tgui/packages/tgui/interfaces/GlassBlowing.tsx @@ -0,0 +1,257 @@ +import { Box, Button, Flex, Stack, Section, ProgressBar, AnimatedNumber, Table } from '../components'; +import { toFixed } from 'common/math'; +import { BooleanLike } from 'common/react'; +import { useBackend } from '../backend'; +import { Window } from '../layouts'; + +type GlassData = { + hasGlass: BooleanLike; + inUse: BooleanLike; + glass: Glass; +}; + +type Glass = { + chosenItem: CraftItem; + stepsRemaining: RemainingSteps; + timeLeft: number; + totalTime: number; + isFinished: BooleanLike; +}; + +type CraftItem = { + name: string; + type: string; +}; + +type RemainingSteps = { + blow: number; + spin: number; + paddle: number; + shear: number; + jacks: number; +}; + +export const GlassBlowing = (props, context) => { + const { act, data } = useBackend(context); + const { glass, inUse } = data; + + return ( + + +
act('Remove')} + /> + } + /> + {glass && !glass.chosenItem && ( +
+ + + What will you craft? + + + +
+ )} + {glass && glass.chosenItem && ( + <> +
+ + + + You are crafting a {glass.chosenItem.name}. +
+
+
+
+ + + {glass.stepsRemaining.blow !== 0 && ( + +
+
+
+
+ + +
+ + )} + {glass && glass.timeLeft !== 0 && ( +
+ + toFixed(value, 1)} + /> + {'/' + glass.totalTime.toFixed(1)} + +
+ )} + {glass && glass.timeLeft === 0 && ( +
+ + + +
+ )} + + + ); +};