diff --git a/code/game/objects/items/stacks/sheets/runed_metal.dm b/code/game/objects/items/stacks/sheets/runed_metal.dm new file mode 100644 index 00000000000..6d75547401c --- /dev/null +++ b/code/game/objects/items/stacks/sheets/runed_metal.dm @@ -0,0 +1,114 @@ +// Note, the order these in are deliberate, as it affects +// the order they are shown via radial. +GLOBAL_LIST_INIT(runed_metal_recipes, list( \ + new /datum/stack_recipe/radial( \ + title = "pylon", \ + result_type = /obj/structure/destructible/cult/pylon, \ + req_amount = 4, \ + time = 4 SECONDS, \ + one_per_turf = TRUE, \ + on_floor = TRUE, \ + desc = span_cultbold("Pylon: Heals and regenerates the blood of nearby blood cultists and constructs, and also \ + converts nearby floor tiles into engraved flooring, which allows blood cultists to scribe runes faster."), \ + required_noun = "runed metal sheet", \ + ), \ + new /datum/stack_recipe/radial( \ + title = "altar", \ + result_type = /obj/structure/destructible/cult/item_dispenser/altar, \ + req_amount = 3, \ + time = 4 SECONDS, \ + one_per_turf = TRUE, \ + on_floor = TRUE, \ + desc = span_cultbold("Altar: Can make Eldritch Whetstones, Construct Shells, and Flasks of Unholy Water."), \ + required_noun = "runed metal sheet", \ + ), \ + new /datum/stack_recipe/radial( \ + title = "archives", \ + result_type = /obj/structure/destructible/cult/item_dispenser/archives, \ + req_amount = 3, \ + time = 4 SECONDS, \ + one_per_turf = TRUE, \ + on_floor = TRUE, \ + desc = span_cultbold("Archives: Can make Zealot's Blindfolds, Shuttle Curse Orbs, \ + and Veil Walker equipment. Emits Light."), \ + required_noun = "runed metal sheet", \ + ), \ + new /datum/stack_recipe/radial( \ + title = "daemon forge", \ + result_type = /obj/structure/destructible/cult/item_dispenser/forge, \ + req_amount = 3, \ + time = 4 SECONDS, \ + one_per_turf = TRUE, \ + on_floor = TRUE, \ + desc = span_cultbold("Daemon Forge: Can make Nar'Sien Hardened Armor, Flagellant's Robes, \ + and Eldritch Longswords. Emits Light."), \ + required_noun = "runed metal sheet", \ + ), \ + new /datum/stack_recipe/radial( \ + title = "runed door", \ + result_type = /obj/machinery/door/airlock/cult, \ + time = 5 SECONDS, \ + one_per_turf = TRUE, \ + on_floor = TRUE, \ + desc = span_cultbold("Runed Door: A weak door which stuns non-blood cultists who touch it."), \ + required_noun = "runed metal sheet", \ + ), \ + new /datum/stack_recipe/radial( \ + title = "runed girder", \ + result_type = /obj/structure/girder/cult, \ + time = 5 SECONDS, \ + one_per_turf = TRUE, \ + on_floor = TRUE, \ + desc = span_cultbold("Runed Girder: A weak girder that can be instantly destroyed by ritual daggers. Not a recommended usage of runed metal."), \ + required_noun = "runed metal sheet", \ + ), \ +)) + +/obj/item/stack/sheet/runed_metal + name = "runed metal" + desc = "Sheets of cold metal with shifting inscriptions writ upon them." + singular_name = "runed metal sheet" + icon_state = "sheet-runed" + inhand_icon_state = "sheet-runed" + icon = 'icons/obj/stack_objects.dmi' + mats_per_unit = list(/datum/material/runedmetal = MINERAL_MATERIAL_AMOUNT) + sheettype = "runed" + merge_type = /obj/item/stack/sheet/runed_metal + novariants = TRUE + grind_results = list(/datum/reagent/iron = 5, /datum/reagent/blood = 15) + material_type = /datum/material/runedmetal + has_unique_girder = TRUE + use_radial = TRUE + +/obj/item/stack/sheet/runed_metal/interact(mob/user) + if(!IS_CULTIST(user)) + to_chat(user, span_warning("Only one with forbidden knowledge could hope to work this metal...")) + return FALSE + + var/turf/user_turf = get_turf(user) + var/area/user_area = get_area(user) + + var/is_valid_turf = user_turf && (is_station_level(user_turf.z) || is_mining_level(user_turf.z)) + var/is_valid_area = user_area && (user_area.area_flags & CULT_PERMITTED) + + if(!is_valid_turf || !is_valid_area) + to_chat(user, span_warning("The veil is not weak enough here.")) + return FALSE + + return ..() + +/obj/item/stack/sheet/runed_metal/radial_check(mob/builder) + return ..() && IS_CULTIST(builder) + +/obj/item/stack/sheet/runed_metal/get_main_recipes() + . = ..() + . += GLOB.runed_metal_recipes + +/obj/item/stack/sheet/runed_metal/fifty + amount = 50 + +/obj/item/stack/sheet/runed_metal/ten + amount = 10 + +/obj/item/stack/sheet/runed_metal/five + amount = 5 diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 35dc58d86b8..2a45a9323b3 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -564,59 +564,6 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \ else . = ..() - -/* - * Runed Metal - */ - -GLOBAL_LIST_INIT(runed_metal_recipes, list ( \ - new /datum/stack_recipe("runed door (a weak door that stuns non-cultists who touch it)", /obj/machinery/door/airlock/cult, 1, time = 5 SECONDS, one_per_turf = TRUE, on_floor = TRUE), \ - new /datum/stack_recipe("runed girder (not a recommended usage of runed metal)", /obj/structure/girder/cult, 1, time = 5 SECONDS, one_per_turf = TRUE, on_floor = TRUE), \ - new /datum/stack_recipe("pylon (heals (and regenerates the blood of) nearby blood cultists and constructs, but also turns nearby floor tiles into engraved flooring)", /obj/structure/destructible/cult/pylon, 4, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE), \ - new /datum/stack_recipe("daemon forge (can make Nar'Sien hardened armor, flagellant's robes, and eldritch longswords)", /obj/structure/destructible/cult/item_dispenser/forge, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE), \ - new /datum/stack_recipe("archives (can make zealot's blindfolds, shuttle curse orbs, and veil walker equipment)", /obj/structure/destructible/cult/item_dispenser/archives, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE), \ - new /datum/stack_recipe("altar (can make eldritch whetstones, construct shells, and flasks of unholy water)", /obj/structure/destructible/cult/item_dispenser/altar, 3, time = 4 SECONDS, one_per_turf = TRUE, on_floor = TRUE), \ - )) - -/obj/item/stack/sheet/runed_metal - name = "runed metal" - desc = "Sheets of cold metal with shifting inscriptions writ upon them." - singular_name = "runed metal sheet" - icon_state = "sheet-runed" - inhand_icon_state = "sheet-runed" - icon = 'icons/obj/stack_objects.dmi' - mats_per_unit = list(/datum/material/runedmetal = MINERAL_MATERIAL_AMOUNT) - sheettype = "runed" - merge_type = /obj/item/stack/sheet/runed_metal - novariants = TRUE - grind_results = list(/datum/reagent/iron = 5, /datum/reagent/blood = 15) - material_type = /datum/material/runedmetal - has_unique_girder = TRUE - -/obj/item/stack/sheet/runed_metal/attack_self(mob/living/user) - if(!IS_CULTIST(user)) - to_chat(user, span_warning("Only one with forbidden knowledge could hope to work this metal...")) - return - var/turf/T = get_turf(user) //we may have moved. adjust as needed... - var/area/A = get_area(user) - if((!is_station_level(T.z) && !is_mining_level(T.z)) || (A && !(A.area_flags & CULT_PERMITTED))) - to_chat(user, span_warning("The veil is not weak enough here.")) - return FALSE - return ..() - -/obj/item/stack/sheet/runed_metal/get_main_recipes() - . = ..() - . += GLOB.runed_metal_recipes - -/obj/item/stack/sheet/runed_metal/fifty - amount = 50 - -/obj/item/stack/sheet/runed_metal/ten - amount = 10 - -/obj/item/stack/sheet/runed_metal/five - amount = 5 - /* * Bronze */ diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 6573ef69b1e..c98ab48efcf 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -1,8 +1,8 @@ //stack recipe placement check types /// Checks if there is an object of the result type in any of the cardinal directions -#define STACK_CHECK_CARDINALS "cardinals" +#define STACK_CHECK_CARDINALS (1<<0) /// Checks if there is an object of the result type within one tile -#define STACK_CHECK_ADJACENT "adjacent" +#define STACK_CHECK_ADJACENT (1<<1) /* Stack type objects! * Contains: @@ -20,35 +20,60 @@ gender = PLURAL material_modifier = 0.05 //5%, so that a 50 sheet stack has the effect of 5k materials instead of 100k. max_integrity = 100 + /// A list to all recipies this stack item can create. var/list/datum/stack_recipe/recipes + /// What's the name of just 1 of this stack. You have a stack of leather, but one piece of leather var/singular_name + /// How much is in this stack? var/amount = 1 - var/max_amount = 50 //also see stack recipes initialisation, param "max_res_amount" must be equal to this max_amount - var/is_cyborg = FALSE // It's TRUE if module is used by a cyborg, and uses its storage + /// How much is allowed in this stack? + // Also see stack recipes initialisation. "max_res_amount" must be equal to this max_amount + var/max_amount = 50 + /// If TRUE, this stack is a module used by a cyborg (doesn't run out like normal / etc) + var/is_cyborg = FALSE + /// Related to above. If present, the energy we draw from when using stack items, for cyborgs var/datum/robot_energy_storage/source - var/cost = 1 // How much energy from storage it costs - var/merge_type = null // This path and its children should merge with this stack, defaults to src.type - var/full_w_class = WEIGHT_CLASS_NORMAL //The weight class the stack should have at amount > 2/3rds max_amount - var/novariants = TRUE //Determines whether the item should update it's sprites based on amount. - var/list/mats_per_unit //list that tells you how much is in a single unit. - ///Datum material type that this stack is made of + /// Related to above. How much energy it costs from storage to use stack items + var/cost = 1 + /// This path and its children should merge with this stack, defaults to src.type + var/merge_type = null + /// The weight class the stack has at amount > 2/3rds max_amount + var/full_w_class = WEIGHT_CLASS_NORMAL + /// Determines whether the item should update it's sprites based on amount. + var/novariants = TRUE + /// List that tells you how much is in a single unit. + var/list/mats_per_unit + /// Datum material type that this stack is made of var/material_type - //NOTE: When adding grind_results, the amounts should be for an INDIVIDUAL ITEM - these amounts will be multiplied by the stack size in on_grind() - var/obj/structure/table/tableVariant // we tables now (stores table variant to be built from this stack) - - // The following are all for medical treatment, they're here instead of /stack/medical because sticky tape can be used as a makeshift bandage or splint - /// If set and this used as a splint for a broken bone wound, this is used as a multiplier for applicable slowdowns (lower = better) (also for speeding up burn recoveries) - var/splint_factor - /// Like splint_factor but for burns instead of bone wounds. This is a multiplier used to speed up burn recoveries - var/burn_cleanliness_bonus - /// How much blood flow this stack can absorb if used as a bandage on a cut wound, note that absorption is how much we lower the flow rate, not the raw amount of blood we suck up - var/absorption_capacity - /// How quickly we lower the blood flow on a cut wound we're bandaging. Expected lifetime of this bandage in seconds is thus absorption_capacity/absorption_rate, or until the cut heals, whichever comes first - var/absorption_rate - /// Amount of matter for RCD + // NOTE: When adding grind_results, the amounts should be for an INDIVIDUAL ITEM - + // these amounts will be multiplied by the stack size in on_grind() + /// Amount of matter given back to RCDs var/matter_amount = 0 /// Does this stack require a unique girder in order to make a wall? var/has_unique_girder = FALSE + /// What typepath table we create from this stack + var/obj/structure/table/tableVariant + /// If TRUE, we'll use a radial instead when displaying recipes + var/use_radial = FALSE + /// If use_radial is TRUE, this is the radius of the radial + var/radial_radius = 52 + + // The following are all for medical treatment + // They're here instead of /stack/medical + // because sticky tape can be used as a makeshift bandage or splint + + /// If set and this used as a splint for a broken bone wound, + /// This is used as a multiplier for applicable slowdowns (lower = better) (also for speeding up burn recoveries) + var/splint_factor + /// Like splint_factor but for burns instead of bone wounds. This is a multiplier used to speed up burn recoveries + var/burn_cleanliness_bonus + /// How much blood flow this stack can absorb if used as a bandage on a cut wound. + /// note that absorption is how much we lower the flow rate, not the raw amount of blood we suck up + var/absorption_capacity + /// How quickly we lower the blood flow on a cut wound we're bandaging. + /// Expected lifetime of this bandage in seconds is thus absorption_capacity/absorption_rate, + /// or until the cut heals, whichever comes first + var/absorption_rate /obj/item/stack/Initialize(mapload, new_amount, merge = TRUE, list/mat_override=null, mat_amt=1) if(new_amount != null) @@ -75,18 +100,17 @@ INVOKE_ASYNC(src, .proc/merge_without_del, item_stack) if(is_zero_amount(delete_if_zero = FALSE)) return INITIALIZE_HINT_QDEL - var/list/temp_recipes = get_main_recipes() - recipes = temp_recipes.Copy() + + recipes = get_main_recipes().Copy() if(material_type) - var/datum/material/M = GET_MATERIAL_REF(material_type) //First/main material - for(var/i in M.categories) - switch(i) + var/datum/material/what_are_we_made_of = GET_MATERIAL_REF(material_type) //First/main material + for(var/category in what_are_we_made_of.categories) + switch(category) if(MAT_CATEGORY_BASE_RECIPES) - var/list/temp = SSmaterials.base_stack_recipes.Copy() - recipes += temp + recipes |= SSmaterials.base_stack_recipes.Copy() if(MAT_CATEGORY_RIGID) - var/list/temp = SSmaterials.rigid_stack_recipes.Copy() - recipes += temp + recipes |= SSmaterials.rigid_stack_recipes.Copy() + update_weight() update_appearance() var/static/list/loc_connections = list( @@ -128,8 +152,10 @@ return TRUE /obj/item/stack/proc/get_main_recipes() + RETURN_TYPE(/list) SHOULD_CALL_PARENT(TRUE) - return list()//empty list + + return list() //empty list /obj/item/stack/proc/update_weight() if(amount <= (max_amount * (1/3))) @@ -224,6 +250,12 @@ return TRUE return FALSE +/obj/item/stack/interact(mob/user) + if(use_radial) + show_construction_radial(user) + else + ui_interact(user) + /obj/item/stack/ui_state(mob/user) return GLOB.hands_state @@ -250,64 +282,149 @@ switch(action) if("make") - if(get_amount() < 1 && !is_cyborg) - qdel(src) - return var/datum/stack_recipe/recipe = locate(params["ref"]) - if(!is_valid_recipe(recipe, recipes)) //href exploit protection - return var/multiplier = text2num(params["multiplier"]) - if(!multiplier || (multiplier <= 0)) //href exploit protection - return - if(!building_checks(recipe, multiplier)) - return - if(recipe.time) - var/adjusted_time = 0 - usr.visible_message(span_notice("[usr] starts building \a [recipe.title]."), span_notice("You start building \a [recipe.title]...")) - if(HAS_TRAIT(usr, recipe.trait_booster)) - adjusted_time = (recipe.time * recipe.trait_modifier) - else - adjusted_time = recipe.time - if(!do_after(usr, adjusted_time, target = usr)) - return - if(!building_checks(recipe, multiplier)) - return - var/obj/O - if(recipe.max_res_amount > 1) //Is it a stack? - O = new recipe.result_type(usr.drop_location(), recipe.res_amount * multiplier) - else if(ispath(recipe.result_type, /turf)) - var/turf/T = usr.drop_location() - if(!isturf(T)) - return - T.PlaceOnTop(recipe.result_type, flags = CHANGETURF_INHERIT_AIR) - else - O = new recipe.result_type(usr.drop_location()) - if(O) - O.setDir(usr.dir) - use(recipe.req_amount * multiplier) - usr.investigate_log("[key_name(usr)] crafted [recipe.title]", INVESTIGATE_CRAFTING) + return make_item(usr, recipe, multiplier) - if(recipe.applies_mats && LAZYLEN(mats_per_unit)) - if(isstack(O)) - var/obj/item/stack/crafted_stack = O - crafted_stack.set_mats_per_unit(mats_per_unit, recipe.req_amount / recipe.res_amount) - else - O.set_custom_materials(mats_per_unit, recipe.req_amount / recipe.res_amount) +/// The key / title for a radial option that shows the entire list of buildables (uses the old menu) +#define FULL_LIST "view full list" - if(QDELETED(O)) - return //It's a stack and has already been merged +/// Shows a radial consisting of every radial recipe we have in our list. +/obj/item/stack/proc/show_construction_radial(mob/builder) + var/list/options = list() + var/list/titles_to_recipes = list() - O.add_fingerprint(usr) //Add fingerprints first, otherwise O might already be deleted because of stack merging - if(isitem(O)) - usr.put_in_hands(O) + for(var/datum/stack_recipe/radial/recipe in recipes) + var/datum/radial_menu_choice/option = new() + option.image = image( + icon = initial(recipe.result_type.icon), + icon_state = initial(recipe.result_type.icon_state), + ) - //BubbleWrap - so newly formed boxes are empty - if(istype(O, /obj/item/storage)) - for (var/obj/item/I in O) - qdel(I) - //BubbleWrap END - return TRUE + if(recipe.desc) + option.info = recipe.desc + + options[recipe.title] = option + titles_to_recipes[recipe.title] = recipe + + // After everything's been added to the radial, add an option + // that lets the user see the whole list of buildables + options[FULL_LIST] = image( + icon = 'icons/hud/radial.dmi', + icon_state = "radial_full_list", + ) + + var/selection = show_radial_menu( + user = builder, + anchor = builder, + choices = options, + custom_check = CALLBACK(src, .proc/radial_check, builder), + radius = radial_radius, + tooltips = TRUE, + ) + + if(!selection) + return + // Run normal UI interact if we wanna see the full list + if(selection == FULL_LIST) + ui_interact(builder) + return + + // Otherwise go straight to building + var/datum/stack_recipe/picked_recipe = titles_to_recipes[selection] + if(!istype(picked_recipe)) + return + + make_item(builder, picked_recipe, 1) + +/// Used as a callback for radial building. +/obj/item/stack/proc/radial_check(mob/builder) + if(QDELETED(builder) || QDELETED(src)) + return FALSE + if(builder.incapacitated()) + return FALSE + if(!builder.is_holding(src)) + return FALSE + return TRUE + +#undef FULL_LIST + +/// Makes the item with the given recipe. +/obj/item/stack/proc/make_item(mob/builder, datum/stack_recipe/recipe, multiplier) + if(get_amount() < 1 && !is_cyborg) //sanity check as this shouldn't happen + qdel(src) + return + if(!is_valid_recipe(recipe, recipes)) //href exploit protection + return + if(!multiplier || (multiplier <= 0)) //href exploit protection + return + if(!building_checks(builder, recipe, multiplier)) + return + if(recipe.time) + var/adjusted_time = 0 + builder.balloon_alert(builder, "building...") + builder.visible_message( + span_notice("[builder] starts building \a [recipe.title]."), + span_notice("You start building \a [recipe.title]..."), + ) + if(HAS_TRAIT(builder, recipe.trait_booster)) + adjusted_time = (recipe.time * recipe.trait_modifier) + else + adjusted_time = recipe.time + if(!do_after(builder, adjusted_time, target = builder)) + builder.balloon_alert(builder, "interrupted!") + return + if(!building_checks(builder, recipe, multiplier)) + return + + var/atom/created + if(recipe.max_res_amount > 1) // Is it a stack? + created = new recipe.result_type(builder.drop_location(), recipe.res_amount * multiplier) + builder.balloon_alert(builder, "built items") + + else if(ispath(recipe.result_type, /turf)) + var/turf/covered_turf = builder.drop_location() + if(!isturf(covered_turf)) + return + covered_turf.PlaceOnTop(recipe.result_type, flags = CHANGETURF_INHERIT_AIR) + builder.balloon_alert(builder, "placed [ispath(recipe.result_type, /turf/open) ? "floor" : "wall"]") + + else + created = new recipe.result_type(builder.drop_location()) + builder.balloon_alert(builder, "built item") + + if(created) + created.setDir(builder.dir) + + // Use up the material + use(recipe.req_amount * multiplier) + builder.investigate_log("[key_name(builder)] crafted [recipe.title]", INVESTIGATE_CRAFTING) + + // Apply mat datums + if(recipe.applies_mats && LAZYLEN(mats_per_unit)) + if(isstack(created)) + var/obj/item/stack/crafted_stack = created + crafted_stack.set_mats_per_unit(mats_per_unit, recipe.req_amount / recipe.res_amount) + else + created.set_custom_materials(mats_per_unit, recipe.req_amount / recipe.res_amount) + + // We could be qdeleted - like if it's a stack and has already been merged + if(QDELETED(created)) + return TRUE + + // Add fingerprints first, otherwise created might already be deleted because of stack merging + created.add_fingerprint(builder) + if(isitem(created)) + builder.put_in_hands(created) + + //BubbleWrap - so newly formed boxes are empty + if(istype(created, /obj/item/storage)) + for (var/obj/item/thing in created) + qdel(thing) + //BubbleWrap END + + return TRUE /obj/item/stack/vv_edit_var(vname, vval) if(vname == NAMEOF(src, amount)) @@ -319,34 +436,32 @@ return TRUE return ..() -/obj/item/stack/proc/building_checks(datum/stack_recipe/recipe, multiplier) - if (get_amount() < recipe.req_amount*multiplier) - if (recipe.req_amount*multiplier>1) - to_chat(usr, span_warning("You haven't got enough [src] to build \the [recipe.req_amount*multiplier] [recipe.title]\s!")) - else - to_chat(usr, span_warning("You haven't got enough [src] to build \the [recipe.title]!")) +/// Checks if we can build here, validly. +/obj/item/stack/proc/building_checks(mob/builder, datum/stack_recipe/recipe, multiplier) + if (get_amount() < recipe.req_amount * multiplier) + builder.balloon_alert(builder, "not enough material!") return FALSE - var/turf/dest_turf = get_turf(usr) + var/turf/dest_turf = get_turf(builder) // If we're making a window, we have some special snowflake window checks to do. if(ispath(recipe.result_type, /obj/structure/window)) var/obj/structure/window/result_path = recipe.result_type - if(!valid_window_location(dest_turf, usr.dir, is_fulltile = initial(result_path.fulltile))) - to_chat(usr, span_warning("The [recipe.title] won't fit here!")) + if(!valid_window_location(dest_turf, builder.dir, is_fulltile = initial(result_path.fulltile))) + builder.balloon_alert(builder, "won't fit here!") return FALSE if(recipe.one_per_turf && (locate(recipe.result_type) in dest_turf)) - to_chat(usr, span_warning("There is another [recipe.title] here!")) + builder.balloon_alert(builder, "already one here!") return FALSE if(recipe.on_tram) if(!locate(/obj/structure/industrial_lift/tram) in dest_turf) - to_chat(usr, span_warning("\The [recipe.title] must be constructed on a tram floor!")) + builder.balloon_alert(builder, "must be made on a tram!") return FALSE if(recipe.on_floor) if(!isfloorturf(dest_turf)) - to_chat(usr, span_warning("\The [recipe.title] must be constructed on the floor!")) + builder.balloon_alert(builder, "must be made on a floor!") return FALSE for(var/obj/object in dest_turf) @@ -359,21 +474,23 @@ if(!window_structure.fulltile) continue if(object.density || NO_BUILD & object.obj_flags) - to_chat(usr, span_warning("There is \a [object.name] here. You can\'t make \a [recipe.title] here!")) + builder.balloon_alert(builder, "something is in the way!") return FALSE - if(recipe.placement_checks) - switch(recipe.placement_checks) - if(STACK_CHECK_CARDINALS) - var/turf/step - for(var/direction in GLOB.cardinals) - step = get_step(dest_turf, direction) - if(locate(recipe.result_type) in step) - to_chat(usr, span_warning("\The [recipe.title] must not be built directly adjacent to another!")) - return FALSE - if(STACK_CHECK_ADJACENT) - if(locate(recipe.result_type) in range(1, dest_turf)) - to_chat(usr, span_warning("\The [recipe.title] must be constructed at least one tile away from others of its type!")) - return FALSE + + if(recipe.placement_checks & STACK_CHECK_CARDINALS) + var/turf/nearby_turf + for(var/direction in GLOB.cardinals) + nearby_turf = get_step(dest_turf, direction) + if(locate(recipe.result_type) in nearby_turf) + to_chat(builder, span_warning("\The [recipe.title] must not be built directly adjacent to another!")) + builder.balloon_alert(builder, "can't be adjacent to another!") + return FALSE + + if(recipe.placement_checks & STACK_CHECK_ADJACENT) + if(locate(recipe.result_type) in range(1, dest_turf)) + builder.balloon_alert(builder, "can't be near another!") + return FALSE + return TRUE /obj/item/stack/use(used, transfer = FALSE, check = TRUE) // return 0 = borked; return 1 = had enough @@ -394,6 +511,9 @@ /obj/item/stack/tool_use_check(mob/living/user, amount) if(get_amount() < amount) + // general balloon alert that says they don't have enough + user.balloon_alert(user, "not enough material!") + // then a more specific message about how much they need and what they need specifically if(singular_name) if(amount > 1) to_chat(user, span_warning("You need at least [amount] [singular_name]\s to do this!")) @@ -577,48 +697,5 @@ if(istype(M) && M.dirty < 100) M.dirty += amount -/* - * Recipe datum - */ -/datum/stack_recipe - var/title = "ERROR" - var/result_type - var/req_amount = 1 - var/res_amount = 1 - var/max_res_amount = 1 - var/time = 0 - var/one_per_turf = FALSE - var/on_floor = FALSE - var/on_tram = FALSE - var/placement_checks = FALSE - var/applies_mats = FALSE - var/trait_booster = null - var/trait_modifier = 1 - -/datum/stack_recipe/New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1,time = 0, one_per_turf = FALSE, on_floor = FALSE, on_tram = FALSE, window_checks = FALSE, placement_checks = FALSE, applies_mats = FALSE, trait_booster = null, trait_modifier = 1) - src.title = title - src.result_type = result_type - src.req_amount = req_amount - src.res_amount = res_amount - src.max_res_amount = max_res_amount - src.time = time - src.one_per_turf = one_per_turf - src.on_floor = on_floor - src.on_tram = on_tram - src.placement_checks = placement_checks - src.applies_mats = applies_mats - src.trait_booster = trait_booster - src.trait_modifier = trait_modifier -/* - * Recipe list datum - */ -/datum/stack_recipe_list - var/title = "ERROR" - var/list/recipes - -/datum/stack_recipe_list/New(title, recipes) - src.title = title - src.recipes = recipes - #undef STACK_CHECK_CARDINALS #undef STACK_CHECK_ADJACENT diff --git a/code/game/objects/items/stacks/stack_recipe.dm b/code/game/objects/items/stacks/stack_recipe.dm new file mode 100644 index 00000000000..b6dc8d27eb8 --- /dev/null +++ b/code/game/objects/items/stacks/stack_recipe.dm @@ -0,0 +1,104 @@ + +/* + * Recipe datum + */ +/datum/stack_recipe + /// The title of the recipe + var/title = "ERROR" + /// What atom the recipe makes, typepath + var/atom/result_type + /// Amount of stack required to make + var/req_amount = 1 + /// Amount of resulting atoms made + var/res_amount = 1 + /// Max amount of resulting atoms made + var/max_res_amount = 1 + /// How long it takes to make + var/time = 0 + /// If only one of the resulting atom is allowed per turf + var/one_per_turf = FALSE + /// If the atom requires a floor below + var/on_floor = FALSE + /// If the atom requires a tram floor below + var/on_tram = FALSE + /// Bitflag of additional placement checks required to place. (STACK_CHECK_CARDINALS|STACK_CHECK_ADJACENT) + var/placement_checks = NONE + /// If TRUE, the created atom will gain custom mat datums + var/applies_mats = FALSE + /// What trait, if any, boosts the construction speed of this item + var/trait_booster + /// How much the trait above, if supplied, boosts the construct speed of this item + var/trait_modifier = 1 + +/datum/stack_recipe/New( + title, + result_type, + req_amount = 1, + res_amount = 1, + max_res_amount = 1, + time = 0, + one_per_turf = FALSE, + on_floor = FALSE, + on_tram = FALSE, + window_checks = FALSE, + placement_checks = NONE, + applies_mats = FALSE, + trait_booster, + trait_modifier = 1, +) + + src.title = title + src.result_type = result_type + src.req_amount = req_amount + src.res_amount = res_amount + src.max_res_amount = max_res_amount + src.time = time + src.one_per_turf = one_per_turf + src.on_floor = on_floor + src.on_tram = on_tram + src.placement_checks = placement_checks + src.applies_mats = applies_mats + src.trait_booster = trait_booster + src.trait_modifier = trait_modifier + +/datum/stack_recipe/radial + /// Optional info to be shown on the radial option for this item + var/desc + +/datum/stack_recipe/radial/New( + title, + result_type, + req_amount = 1, + res_amount = 1, + max_res_amount = 1, + time = 0, + one_per_turf = FALSE, + on_floor = FALSE, + on_tram = FALSE, + window_checks = FALSE, + placement_checks = NONE, + applies_mats = FALSE, + trait_booster, + trait_modifier = 1, + desc, + required_noun, +) + + if(desc) + src.desc = desc + if(required_noun) + src.desc += span_boldnotice("[desc ? " - ":""]Requires: [req_amount] [required_noun]\s.") + + return ..() + + +/* + * Recipe list datum + */ +/datum/stack_recipe_list + var/title = "ERROR" + var/list/recipes + +/datum/stack_recipe_list/New(title, recipes) + src.title = title + src.recipes = recipes diff --git a/icons/hud/radial.dmi b/icons/hud/radial.dmi index 1741623cf09..5e301600956 100644 Binary files a/icons/hud/radial.dmi and b/icons/hud/radial.dmi differ diff --git a/tgstation.dme b/tgstation.dme index d4afb1b375d..ccd6a7cda1a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1655,6 +1655,7 @@ #include "code\game\objects\items\stacks\medical.dm" #include "code\game\objects\items\stacks\rods.dm" #include "code\game\objects\items\stacks\stack.dm" +#include "code\game\objects\items\stacks\stack_recipe.dm" #include "code\game\objects\items\stacks\tape.dm" #include "code\game\objects\items\stacks\telecrystal.dm" #include "code\game\objects\items\stacks\tickets.dm" @@ -1664,6 +1665,7 @@ #include "code\game\objects\items\stacks\sheets\leather.dm" #include "code\game\objects\items\stacks\sheets\light.dm" #include "code\game\objects\items\stacks\sheets\mineral.dm" +#include "code\game\objects\items\stacks\sheets\runed_metal.dm" #include "code\game\objects\items\stacks\sheets\sheet_types.dm" #include "code\game\objects\items\stacks\sheets\sheets.dm" #include "code\game\objects\items\stacks\tiles\light.dm"