mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-11 10:43:20 +00:00
Personal crafting & Dependencies
This commit is contained in:
committed by
Darlantan
parent
04a8c35801
commit
d7f20396de
@@ -32,6 +32,12 @@
|
||||
// Reads the length of L, returning 0 if null
|
||||
#define LAZYLEN(L) length(L)
|
||||
|
||||
#define LAZYADDASSOC(L, K, V) if(!L) { L = list(); } L[K] += V;
|
||||
///This is used to add onto lazy assoc list when the value you're adding is a /list/. This one has extra safety over lazyaddassoc because the value could be null (and thus cant be used to += objects)
|
||||
#define LAZYADDASSOCLIST(L, K, V) if(!L) { L = list(); } L[K] += list(V);
|
||||
#define LAZYREMOVEASSOC(L, K, V) if(L) { if(L[K]) { L[K] -= V; if(!length(L[K])) L -= K; } if(!length(L)) L = null; }
|
||||
#define LAZYACCESSASSOC(L, I, K) L ? L[I] ? L[I][K] ? L[I][K] : null : null : null
|
||||
|
||||
// Null-safe L.Cut()
|
||||
#define LAZYCLEARLIST(L) if(L) L.Cut()
|
||||
|
||||
|
||||
27
code/__defines/crafting.dm
Normal file
27
code/__defines/crafting.dm
Normal file
@@ -0,0 +1,27 @@
|
||||
//tablecrafting defines
|
||||
#define CAT_NONE ""
|
||||
#define CAT_WEAPONRY "Weaponry"
|
||||
#define CAT_WEAPON "Weapons"
|
||||
#define CAT_AMMO "Ammunition"
|
||||
#define CAT_ROBOT "Robots"
|
||||
#define CAT_MISC "Misc"
|
||||
#define CAT_PRIMAL "Tribal"
|
||||
#define CAT_CLOTHING "Clothing"
|
||||
#define CAT_FOOD "Foods"
|
||||
#define CAT_BREAD "Breads"
|
||||
#define CAT_BURGER "Burgers"
|
||||
#define CAT_CAKE "Cakes"
|
||||
#define CAT_EGG "Egg-Based Food"
|
||||
#define CAT_MEAT "Meats"
|
||||
#define CAT_MISCFOOD "Misc. Food"
|
||||
#define CAT_MEXICAN "Mexican Food"
|
||||
#define CAT_PASTRY "Pastries"
|
||||
#define CAT_PIE "Pies"
|
||||
#define CAT_PIZZA "Pizzas"
|
||||
#define CAT_SALAD "Salads"
|
||||
#define CAT_SANDWICH "Sandwiches"
|
||||
#define CAT_SOUP "Soups"
|
||||
#define CAT_SPAGHETTI "Spaghettis"
|
||||
#define CAT_ICE "Frozen"
|
||||
#define CAT_DRINK "Drinks"
|
||||
#define CAT_CHEMISTRY "Chemistry"
|
||||
@@ -97,6 +97,10 @@
|
||||
#define COMSIG_ATOM_FIRE_ACT "atom_fire_act"
|
||||
///from base of atom/bullet_act(): (/obj/projectile, def_zone)
|
||||
#define COMSIG_ATOM_BULLET_ACT "atom_bullet_act"
|
||||
///from base of atom/CheckParts(): (list/parts_list, datum/crafting_recipe/R)
|
||||
#define COMSIG_ATOM_CHECKPARTS "atom_checkparts"
|
||||
///from base of atom/CheckParts(): (atom/movable/new_craft) - The atom has just been used in a crafting recipe and has been moved inside new_craft.
|
||||
#define COMSIG_ATOM_USED_IN_CRAFT "atom_used_in_craft"
|
||||
///from base of atom/blob_act(): (/obj/structure/blob)
|
||||
#define COMSIG_ATOM_BLOB_ACT "atom_blob_act"
|
||||
///from base of atom/acid_act(): (acidpwr, acid_volume)
|
||||
@@ -732,3 +736,5 @@
|
||||
///SSalarm signals
|
||||
#define COMSIG_TRIGGERED_ALARM "ssalarm_triggered"
|
||||
#define COMSIG_CANCELLED_ALARM "ssalarm_cancelled"
|
||||
|
||||
#define COMSIG_REAGENTS_CRAFTING_PING "reagents_crafting_ping"
|
||||
21
code/__defines/tools.dm
Normal file
21
code/__defines/tools.dm
Normal file
@@ -0,0 +1,21 @@
|
||||
// Tool types, if you add new ones please add them to /obj/item/debug/omnitool in code/game/objects/items/debug_items.dm
|
||||
#define TOOL_CROWBAR "crowbar"
|
||||
#define TOOL_MULTITOOL "multitool"
|
||||
#define TOOL_SCREWDRIVER "screwdriver"
|
||||
#define TOOL_WIRECUTTER "wirecutter"
|
||||
#define TOOL_WRENCH "wrench"
|
||||
#define TOOL_WELDER "welder"
|
||||
#define TOOL_CABLE_COIL "cablecoil"
|
||||
#define TOOL_ANALYZER "analyzer"
|
||||
#define TOOL_MINING "mining"
|
||||
#define TOOL_SHOVEL "shovel"
|
||||
#define TOOL_RETRACTOR "retractor"
|
||||
#define TOOL_HEMOSTAT "hemostat"
|
||||
#define TOOL_CAUTERY "cautery"
|
||||
#define TOOL_DRILL "drill"
|
||||
#define TOOL_SCALPEL "scalpel"
|
||||
#define TOOL_SAW "saw"
|
||||
#define TOOL_BONESET "bonesetter"
|
||||
#define TOOL_KNIFE "knife"
|
||||
#define TOOL_BLOODFILTER "bloodfilter"
|
||||
#define TOOL_ROLLINGPIN "rollingpin"
|
||||
@@ -8,4 +8,5 @@ GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the
|
||||
GLOBAL_LIST_EMPTY(tagger_locations)
|
||||
|
||||
GLOBAL_LIST_INIT(char_directory_tags, list("Pred", "Prey", "Switch", "Non-Vore", "Unset"))
|
||||
GLOBAL_LIST_INIT(char_directory_erptags, list("Top", "Bottom", "Switch", "No ERP", "Unset"))
|
||||
GLOBAL_LIST_INIT(char_directory_erptags, list("Top", "Bottom", "Switch", "No ERP", "Unset"))
|
||||
GLOBAL_LIST_EMPTY(crafting_recipes) //list of all table craft recipes
|
||||
|
||||
@@ -416,13 +416,9 @@ This actually tests if they have the same entries and values.
|
||||
|
||||
|
||||
|
||||
|
||||
//Mergesort: any value in a list
|
||||
/proc/sortList(var/list/L)
|
||||
if(L.len < 2)
|
||||
return L
|
||||
var/middle = L.len / 2 + 1 // Copy is first,second-1
|
||||
return mergeLists(sortList(L.Copy(0,middle)), sortList(L.Copy(middle))) //second parameter null = to end of list
|
||||
//any value in a list
|
||||
/proc/sortList(list/L, cmp=/proc/cmp_text_asc)
|
||||
return sortTim(L.Copy(), cmp)
|
||||
|
||||
//Mergsorge: uses sortList() but uses the var's name specifically. This should probably be using mergeAtom() instead
|
||||
/proc/sortNames(var/list/L)
|
||||
|
||||
@@ -256,6 +256,7 @@ GLOBAL_LIST_EMPTY(mannequins)
|
||||
var/datum/digest_mode/DM = new T
|
||||
GLOB.digest_modes[DM.id] = DM
|
||||
// VOREStation Add End
|
||||
init_crafting_recipes(GLOB.crafting_recipes)
|
||||
|
||||
/*
|
||||
// Custom species traits
|
||||
@@ -292,8 +293,13 @@ GLOBAL_LIST_EMPTY(mannequins)
|
||||
return 1 // Hooks must return 1
|
||||
|
||||
|
||||
return 1
|
||||
|
||||
/// Inits the crafting recipe list, sorting crafting recipe requirements in the process.
|
||||
/proc/init_crafting_recipes(list/crafting_recipes)
|
||||
for(var/path in subtypesof(/datum/crafting_recipe))
|
||||
var/datum/crafting_recipe/recipe = new path()
|
||||
recipe.reqs = sortList(recipe.reqs, /proc/cmp_crafting_req_priority)
|
||||
crafting_recipes += recipe
|
||||
return crafting_recipes
|
||||
/* // Uncomment to debug chemical reaction list.
|
||||
/client/verb/debug_chemical_list()
|
||||
|
||||
|
||||
@@ -64,4 +64,23 @@
|
||||
return b_score - a_score
|
||||
|
||||
/proc/cmp_typepaths_asc(A, B)
|
||||
return sorttext("[B]","[A]")
|
||||
return sorttext("[B]","[A]")
|
||||
|
||||
/**
|
||||
* Sorts crafting recipe requirements before the crafting recipe is inserted into GLOB.crafting_recipes
|
||||
*
|
||||
* Prioritises [/datum/reagent] to ensure reagent requirements are always processed first when crafting.
|
||||
* This prevents any reagent_containers from being consumed before the reagents they contain, which can
|
||||
* lead to runtimes and item duplication when it happens.
|
||||
*/
|
||||
/proc/cmp_crafting_req_priority(A, B)
|
||||
var/lhs
|
||||
var/rhs
|
||||
|
||||
lhs = ispath(A, /datum/reagent) ? 0 : 1
|
||||
rhs = ispath(B, /datum/reagent) ? 0 : 1
|
||||
|
||||
return lhs - rhs
|
||||
|
||||
/proc/cmp_text_asc(a,b)
|
||||
return sorttext(b,a)
|
||||
|
||||
14
code/_helpers/string_lists.dm
Normal file
14
code/_helpers/string_lists.dm
Normal file
@@ -0,0 +1,14 @@
|
||||
GLOBAL_LIST_EMPTY(string_lists)
|
||||
|
||||
/**
|
||||
* Caches lists with non-numeric stringify-able values (text or typepath).
|
||||
*/
|
||||
/proc/string_list(list/values)
|
||||
var/string_id = values.Join("-")
|
||||
|
||||
. = GLOB.string_lists[string_id]
|
||||
|
||||
if(.)
|
||||
return
|
||||
|
||||
return GLOB.string_lists[string_id] = values
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
/atom/Click(var/location, var/control, var/params) // This is their reaction to being clicked on (standard proc)
|
||||
if(src)
|
||||
SEND_SIGNAL(src, COMSIG_CLICK, location, control, params, usr)
|
||||
usr.ClickOn(src, params)
|
||||
|
||||
/atom/DblClick(var/location, var/control, var/params)
|
||||
|
||||
@@ -187,3 +187,5 @@
|
||||
#define ui_mech_airtoggle "WEST+1:-7, SOUTH+8"
|
||||
#define ui_mech_deco1_f "WEST+2:-7, SOUTH+8"
|
||||
#define ui_mech_deco2_f "WEST+2:-7, SOUTH+9"
|
||||
|
||||
#define ui_crafting "EAST-4:22,SOUTH:5"
|
||||
@@ -246,6 +246,7 @@
|
||||
add_overlay(selecting_appearance)
|
||||
|
||||
/obj/screen/Click(location, control, params)
|
||||
..() // why the FUCK was this not called before
|
||||
if(!usr) return 1
|
||||
switch(name)
|
||||
if("toggle")
|
||||
|
||||
507
code/datums/components/crafting/crafting.dm
Normal file
507
code/datums/components/crafting/crafting.dm
Normal file
@@ -0,0 +1,507 @@
|
||||
/datum/component/personal_crafting/Initialize()
|
||||
if(ismob(parent))
|
||||
RegisterSignal(parent, COMSIG_MOB_CLIENT_LOGIN, .proc/create_mob_button)
|
||||
|
||||
/datum/component/personal_crafting/proc/create_mob_button(mob/user, client/CL)
|
||||
// SIGNAL_HANDLER
|
||||
|
||||
var/datum/hud/H = user.hud_used
|
||||
var/obj/screen/craft/C = new()
|
||||
C.icon = H.ui_style
|
||||
H.other += C
|
||||
CL.screen += C
|
||||
RegisterSignal(C, COMSIG_CLICK, .proc/component_ui_interact)
|
||||
|
||||
/datum/component/personal_crafting
|
||||
var/busy
|
||||
var/viewing_category = 1 //typical powergamer starting on the Weapons tab
|
||||
var/viewing_subcategory = 1
|
||||
var/list/categories = list(
|
||||
CAT_WEAPONRY = list(
|
||||
CAT_WEAPON,
|
||||
CAT_AMMO,
|
||||
),
|
||||
CAT_ROBOT = CAT_NONE,
|
||||
CAT_MISC = CAT_NONE,
|
||||
CAT_PRIMAL = CAT_NONE,
|
||||
CAT_FOOD = list(
|
||||
CAT_BREAD,
|
||||
CAT_BURGER,
|
||||
CAT_CAKE,
|
||||
CAT_EGG,
|
||||
CAT_ICE,
|
||||
CAT_MEAT,
|
||||
CAT_MISCFOOD,
|
||||
CAT_PASTRY,
|
||||
CAT_PIE,
|
||||
CAT_PIZZA,
|
||||
CAT_SALAD,
|
||||
CAT_SANDWICH,
|
||||
CAT_SOUP,
|
||||
CAT_SPAGHETTI,
|
||||
),
|
||||
CAT_DRINK = CAT_NONE,
|
||||
CAT_CLOTHING = CAT_NONE,
|
||||
)
|
||||
|
||||
var/cur_category = CAT_NONE
|
||||
var/cur_subcategory = CAT_NONE
|
||||
var/datum/action/innate/crafting/button
|
||||
var/display_craftable_only = FALSE
|
||||
var/display_compact = TRUE
|
||||
|
||||
/* This is what procs do:
|
||||
get_environment - gets a list of things accessable for crafting by user
|
||||
get_surroundings - takes a list of things and makes a list of key-types to values-amounts of said type in the list
|
||||
check_contents - takes a recipe and a key-type list and checks if said recipe can be done with available stuff
|
||||
check_tools - takes recipe, a key-type list, and a user and checks if there are enough tools to do the stuff, checks bugs one level deep
|
||||
construct_item - takes a recipe and a user, call all the checking procs, calls do_after, checks all the things again, calls del_reqs, creates result, calls CheckParts of said result with argument being list returned by deel_reqs
|
||||
del_reqs - takes recipe and a user, loops over the recipes reqs var and tries to find everything in the list make by get_environment and delete it/add to parts list, then returns the said list
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check that the contents of the recipe meet the requirements.
|
||||
*
|
||||
* user: The /mob that initated the crafting.
|
||||
* R: The /datum/crafting_recipe being attempted.
|
||||
* contents: List of items to search for R's reqs.
|
||||
*/
|
||||
/datum/component/personal_crafting/proc/check_contents(atom/a, datum/crafting_recipe/R, list/contents)
|
||||
var/list/item_instances = contents["instances"]
|
||||
var/list/machines = contents["machinery"]
|
||||
contents = contents["other"]
|
||||
|
||||
|
||||
var/list/requirements_list = list()
|
||||
|
||||
// Process all requirements
|
||||
for(var/requirement_path in R.reqs)
|
||||
// Check we have the appropriate amount available in the contents list
|
||||
var/needed_amount = R.reqs[requirement_path]
|
||||
for(var/content_item_path in contents)
|
||||
// Right path and not blacklisted
|
||||
if(!ispath(content_item_path, requirement_path) || R.blacklist.Find(content_item_path))
|
||||
continue
|
||||
|
||||
needed_amount -= contents[content_item_path]
|
||||
if(needed_amount <= 0)
|
||||
break
|
||||
|
||||
if(needed_amount > 0)
|
||||
return FALSE
|
||||
|
||||
// Store the instances of what we will use for R.check_requirements() for requirement_path
|
||||
var/list/instances_list = list()
|
||||
for(var/instance_path in item_instances)
|
||||
if(ispath(instance_path, requirement_path))
|
||||
instances_list += item_instances[instance_path]
|
||||
|
||||
requirements_list[requirement_path] = instances_list
|
||||
|
||||
for(var/requirement_path in R.chem_catalysts)
|
||||
if(contents[requirement_path] < R.chem_catalysts[requirement_path])
|
||||
return FALSE
|
||||
|
||||
for(var/machinery_path in R.machinery)
|
||||
if(!machines[machinery_path])//We don't care for volume with machines, just if one is there or not
|
||||
return FALSE
|
||||
|
||||
return R.check_requirements(a, requirements_list)
|
||||
|
||||
/datum/component/personal_crafting/proc/get_environment(atom/a, list/blacklist = null, radius_range = 1)
|
||||
. = list()
|
||||
|
||||
if(!isturf(a.loc))
|
||||
return
|
||||
|
||||
for(var/atom/movable/AM in range(radius_range, a))
|
||||
if(/*(AM.flags_1 & HOLOGRAM_1) ||*/ (blacklist && (AM.type in blacklist)))
|
||||
continue
|
||||
. += AM
|
||||
|
||||
|
||||
/datum/component/personal_crafting/proc/get_surroundings(atom/a, list/blacklist=null)
|
||||
. = list()
|
||||
.["tool_qualities"] = list()
|
||||
.["other"] = list()
|
||||
.["instances"] = list()
|
||||
.["machinery"] = list()
|
||||
for(var/obj/object in get_environment(a, blacklist))
|
||||
if(isitem(object))
|
||||
var/obj/item/item = object
|
||||
LAZYADDASSOCLIST(.["instances"], item.type, item)
|
||||
if(istype(item, /obj/item/stack))
|
||||
var/obj/item/stack/stack = item
|
||||
.["other"][item.type] += stack.amount
|
||||
else if(item.tool_qualities)
|
||||
.["tool_qualities"] |= item.tool_qualities
|
||||
.["other"][item.type] += 1
|
||||
else
|
||||
if(istype(item, /obj/item/weapon/reagent_containers))
|
||||
var/obj/item/weapon/reagent_containers/container = item
|
||||
// if(container.is_drainable())
|
||||
if(container.is_open_container()) // this isn't exactly the same
|
||||
for(var/datum/reagent/reagent in container.reagents.reagent_list)
|
||||
.["other"][reagent.type] += reagent.volume
|
||||
.["other"][item.type] += 1
|
||||
else if (istype(object, /obj/machinery))
|
||||
LAZYADDASSOCLIST(.["machinery"], object.type, object)
|
||||
|
||||
|
||||
|
||||
/// Returns a boolean on whether the tool requirements of the input recipe are satisfied by the input source and surroundings.
|
||||
/datum/component/personal_crafting/proc/check_tools(atom/source, datum/crafting_recipe/recipe, list/surroundings)
|
||||
if(!length(recipe.tool_behaviors) && !length(recipe.tool_paths))
|
||||
return TRUE
|
||||
var/list/available_tools = list()
|
||||
var/list/present_qualities = list()
|
||||
|
||||
for(var/obj/item/contained_item in source.contents)
|
||||
// if(contained_item.GetComponent(/datum/component/storage))
|
||||
if(istype(contained_item, /obj/item/weapon/storage)) // cursed
|
||||
for(var/obj/item/subcontained_item in contained_item.contents)
|
||||
available_tools[subcontained_item.type] = TRUE
|
||||
for(var/behavior in subcontained_item.tool_qualities)
|
||||
present_qualities[behavior] = TRUE
|
||||
available_tools[contained_item.type] = TRUE
|
||||
for(var/behavior in contained_item.tool_qualities)
|
||||
present_qualities[behavior] = TRUE
|
||||
|
||||
for(var/quality in surroundings["tool_behaviour"])
|
||||
present_qualities[quality] = TRUE
|
||||
|
||||
for(var/path in surroundings["other"])
|
||||
available_tools[path] = TRUE
|
||||
|
||||
for(var/required_quality in recipe.tool_behaviors)
|
||||
if(present_qualities[required_quality])
|
||||
continue
|
||||
return FALSE
|
||||
|
||||
for(var/required_path in recipe.tool_paths)
|
||||
var/found_this_tool = FALSE
|
||||
for(var/tool_path in available_tools)
|
||||
if(!ispath(required_path, tool_path))
|
||||
continue
|
||||
found_this_tool = TRUE
|
||||
break
|
||||
if(found_this_tool)
|
||||
continue
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/component/personal_crafting/proc/construct_item(atom/a, datum/crafting_recipe/R)
|
||||
var/list/contents = get_surroundings(a,R.blacklist)
|
||||
// var/send_feedback = 1
|
||||
if(check_contents(a, R, contents))
|
||||
if(check_tools(a, R, contents))
|
||||
if(R.one_per_turf)
|
||||
for(var/content in get_turf(a))
|
||||
if(istype(content, R.result))
|
||||
return ", object already present."
|
||||
//If we're a mob we'll try a do_after; non mobs will instead instantly construct the item
|
||||
if(ismob(a) && !do_after(a, R.time, target = a))
|
||||
return "."
|
||||
contents = get_surroundings(a,R.blacklist)
|
||||
if(!check_contents(a, R, contents))
|
||||
return ", missing component."
|
||||
if(!check_tools(a, R, contents))
|
||||
return ", missing tool."
|
||||
var/list/parts = del_reqs(R, a)
|
||||
var/atom/movable/I = new R.result (get_turf(a.loc))
|
||||
I.CheckParts(parts, R)
|
||||
// if(send_feedback)
|
||||
// SSblackbox.record_feedback("tally", "object_crafted", 1, I.type)
|
||||
return I //Send the item back to whatever called this proc so it can handle whatever it wants to do with the new item
|
||||
return ", missing tool."
|
||||
return ", missing component."
|
||||
|
||||
/*Del reqs works like this:
|
||||
|
||||
Loop over reqs var of the recipe
|
||||
Set var amt to the value current cycle req is pointing to, its amount of type we need to delete
|
||||
Get var/surroundings list of things accessable to crafting by get_environment()
|
||||
Check the type of the current cycle req
|
||||
If its reagent then do a while loop, inside it try to locate() reagent containers, inside such containers try to locate needed reagent, if there isn't remove thing from surroundings
|
||||
If there is enough reagent in the search result then delete the needed amount, create the same type of reagent with the same data var and put it into deletion list
|
||||
If there isn't enough take all of that reagent from the container, put into deletion list, substract the amt var by the volume of reagent, remove the container from surroundings list and keep searching
|
||||
While doing above stuff check deletion list if it already has such reagnet, if yes merge instead of adding second one
|
||||
If its stack check if it has enough amount
|
||||
If yes create new stack with the needed amount and put in into deletion list, substract taken amount from the stack
|
||||
If no put all of the stack in the deletion list, substract its amount from amt and keep searching
|
||||
While doing above stuff check deletion list if it already has such stack type, if yes try to merge them instead of adding new one
|
||||
If its anything else just locate() in in the list in a while loop, each find --s the amt var and puts the found stuff in deletion loop
|
||||
|
||||
Then do a loop over parts var of the recipe
|
||||
Do similar stuff to what we have done above, but now in deletion list, until the parts conditions are satisfied keep taking from the deletion list and putting it into parts list for return
|
||||
|
||||
After its done loop over deletion list and delete all the shit that wasn't taken by parts loop
|
||||
|
||||
del_reqs return the list of parts resulting object will receive as argument of CheckParts proc, on the atom level it will add them all to the contents, on all other levels it calls ..() and does whatever is needed afterwards but from contents list already
|
||||
*/
|
||||
|
||||
/datum/component/personal_crafting/proc/del_reqs(datum/crafting_recipe/R, atom/a)
|
||||
var/list/surroundings
|
||||
var/list/Deletion = list()
|
||||
. = list()
|
||||
var/data
|
||||
var/amt
|
||||
var/list/requirements = list()
|
||||
if(R.reqs)
|
||||
requirements += R.reqs
|
||||
if(R.machinery)
|
||||
requirements += R.machinery
|
||||
main_loop:
|
||||
for(var/path_key in requirements)
|
||||
amt = R.reqs[path_key] || R.machinery[path_key]
|
||||
if(!amt)//since machinery can have 0 aka CRAFTING_MACHINERY_USE - i.e. use it, don't consume it!
|
||||
continue main_loop
|
||||
surroundings = get_environment(a, R.blacklist)
|
||||
surroundings -= Deletion
|
||||
if(ispath(path_key, /datum/reagent))
|
||||
var/datum/reagent/RG = new path_key
|
||||
var/datum/reagent/RGNT
|
||||
while(amt > 0)
|
||||
var/obj/item/weapon/reagent_containers/RC = locate() in surroundings
|
||||
RG = RC.reagents.get_reagent(path_key)
|
||||
if(RG)
|
||||
if(!locate(RG.type) in Deletion)
|
||||
Deletion += new RG.type()
|
||||
if(RG.volume > amt)
|
||||
RG.volume -= amt
|
||||
data = RG.data
|
||||
RC.reagents.conditional_update(RC)
|
||||
RG = locate(RG.type) in Deletion
|
||||
RG.volume = amt
|
||||
RG.data += data
|
||||
continue main_loop
|
||||
else
|
||||
surroundings -= RC
|
||||
amt -= RG.volume
|
||||
RC.reagents.reagent_list -= RG
|
||||
RC.reagents.conditional_update(RC)
|
||||
RGNT = locate(RG.type) in Deletion
|
||||
RGNT.volume += RG.volume
|
||||
RGNT.data += RG.data
|
||||
qdel(RG)
|
||||
SEND_SIGNAL(RC.reagents, COMSIG_REAGENTS_CRAFTING_PING) // - [] TODO: Make this entire thing less spaghetti
|
||||
else
|
||||
surroundings -= RC
|
||||
else if(ispath(path_key, /obj/item/stack))
|
||||
var/obj/item/stack/S
|
||||
var/obj/item/stack/SD
|
||||
while(amt > 0)
|
||||
S = locate(path_key) in surroundings
|
||||
if(S.amount >= amt)
|
||||
if(!locate(S.type) in Deletion)
|
||||
SD = new S.type()
|
||||
Deletion += SD
|
||||
S.use(amt)
|
||||
SD = locate(S.type) in Deletion
|
||||
SD.amount += amt
|
||||
continue main_loop
|
||||
else
|
||||
amt -= S.amount
|
||||
if(!locate(S.type) in Deletion)
|
||||
Deletion += S
|
||||
else
|
||||
data = S.amount
|
||||
S = locate(S.type) in Deletion
|
||||
S.add(data)
|
||||
surroundings -= S
|
||||
else
|
||||
var/atom/movable/I
|
||||
while(amt > 0)
|
||||
I = locate(path_key) in surroundings
|
||||
Deletion += I
|
||||
surroundings -= I
|
||||
amt--
|
||||
var/list/partlist = list(R.parts.len)
|
||||
for(var/M in R.parts)
|
||||
partlist[M] = R.parts[M]
|
||||
for(var/part in R.parts)
|
||||
if(istype(part, /datum/reagent))
|
||||
var/datum/reagent/RG = locate(part) in Deletion
|
||||
if(RG.volume > partlist[part])
|
||||
RG.volume = partlist[part]
|
||||
. += RG
|
||||
Deletion -= RG
|
||||
continue
|
||||
else if(istype(part, /obj/item/stack))
|
||||
var/obj/item/stack/ST = locate(part) in Deletion
|
||||
if(ST.amount > partlist[part])
|
||||
ST.amount = partlist[part]
|
||||
. += ST
|
||||
Deletion -= ST
|
||||
continue
|
||||
else
|
||||
while(partlist[part] > 0)
|
||||
var/atom/movable/AM = locate(part) in Deletion
|
||||
. += AM
|
||||
Deletion -= AM
|
||||
partlist[part] -= 1
|
||||
while(Deletion.len)
|
||||
var/DL = Deletion[Deletion.len]
|
||||
Deletion.Cut(Deletion.len)
|
||||
// Snowflake handling of reagent containers and storage atoms.
|
||||
// If we consumed them in our crafting, we should dump their contents out before qdeling them.
|
||||
if(istype(DL, /obj/item/weapon/reagent_containers))
|
||||
var/obj/item/weapon/reagent_containers/container = DL
|
||||
container.reagents.clear_reagents()
|
||||
// container.reagents.expose(container.loc, TOUCH)
|
||||
else if(istype(DL, /obj/item/weapon/storage))
|
||||
var/obj/item/weapon/storage/container = DL
|
||||
container.spill()
|
||||
container.close_all()
|
||||
qdel(DL)
|
||||
|
||||
/datum/component/personal_crafting/proc/component_ui_interact(atom/movable/screen/craft/image, location, control, params, user)
|
||||
// SIGNAL_HANDLER
|
||||
|
||||
if(user == parent)
|
||||
INVOKE_ASYNC(src, .proc/tgui_interact, user)
|
||||
|
||||
/datum/component/personal_crafting/tgui_state(mob/user)
|
||||
return GLOB.tgui_not_incapacitated_turf_state
|
||||
|
||||
//For the UI related things we're going to assume the user is a mob rather than typesetting it to an atom as the UI isn't generated if the parent is an atom
|
||||
/datum/component/personal_crafting/tgui_interact(mob/user, datum/tgui/ui)
|
||||
ui = SStgui.try_update_ui(user, src, ui)
|
||||
if(!ui)
|
||||
cur_category = categories[1]
|
||||
if(islist(categories[cur_category]))
|
||||
var/list/subcats = categories[cur_category]
|
||||
cur_subcategory = subcats[1]
|
||||
else
|
||||
cur_subcategory = CAT_NONE
|
||||
ui = new(user, src, "PersonalCrafting")
|
||||
ui.open()
|
||||
|
||||
/datum/component/personal_crafting/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["busy"] = busy
|
||||
data["category"] = cur_category
|
||||
data["subcategory"] = cur_subcategory
|
||||
data["display_craftable_only"] = display_craftable_only
|
||||
data["display_compact"] = display_compact
|
||||
|
||||
var/list/surroundings = get_surroundings(user)
|
||||
var/list/craftability = list()
|
||||
for(var/rec in GLOB.crafting_recipes)
|
||||
var/datum/crafting_recipe/R = rec
|
||||
|
||||
if(!R.always_available && !(R.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this.
|
||||
continue
|
||||
|
||||
if((R.category != cur_category) || (R.subcategory != cur_subcategory))
|
||||
continue
|
||||
|
||||
craftability["[REF(R)]"] = check_contents(user, R, surroundings)
|
||||
|
||||
data["craftability"] = craftability
|
||||
return data
|
||||
|
||||
/datum/component/personal_crafting/tgui_static_data(mob/user)
|
||||
var/list/data = list()
|
||||
|
||||
var/list/crafting_recipes = list()
|
||||
for(var/rec in GLOB.crafting_recipes)
|
||||
var/datum/crafting_recipe/R = rec
|
||||
|
||||
if(R.name == "") //This is one of the invalid parents that sneaks in
|
||||
continue
|
||||
|
||||
if(!R.always_available && !(R.type in user?.mind?.learned_recipes)) //User doesn't actually know how to make this.
|
||||
continue
|
||||
|
||||
if(isnull(crafting_recipes[R.category]))
|
||||
crafting_recipes[R.category] = list()
|
||||
|
||||
if(R.subcategory == CAT_NONE)
|
||||
crafting_recipes[R.category] += list(build_recipe_data(R))
|
||||
else
|
||||
if(isnull(crafting_recipes[R.category][R.subcategory]))
|
||||
crafting_recipes[R.category][R.subcategory] = list()
|
||||
crafting_recipes[R.category]["has_subcats"] = TRUE
|
||||
crafting_recipes[R.category][R.subcategory] += list(build_recipe_data(R))
|
||||
|
||||
data["crafting_recipes"] = crafting_recipes
|
||||
return data
|
||||
|
||||
/datum/component/personal_crafting/tgui_act(action, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
return
|
||||
switch(action)
|
||||
if("make")
|
||||
var/mob/user = usr
|
||||
var/datum/crafting_recipe/TR = locate(params["recipe"]) in GLOB.crafting_recipes
|
||||
busy = TRUE
|
||||
tgui_interact(user)
|
||||
var/atom/movable/result = construct_item(user, TR)
|
||||
if(!istext(result)) //We made an item and didn't get a fail message
|
||||
if(ismob(user) && isitem(result)) //In case the user is actually possessing a non mob like a machine
|
||||
user.put_in_hands(result)
|
||||
else
|
||||
result.forceMove(user.drop_location())
|
||||
to_chat(user, "<span class='notice'>[TR.name] constructed.</span>")
|
||||
TR.on_craft_completion(user, result)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>Construction failed[result]</span>")
|
||||
busy = FALSE
|
||||
if("toggle_recipes")
|
||||
display_craftable_only = !display_craftable_only
|
||||
. = TRUE
|
||||
if("toggle_compact")
|
||||
display_compact = !display_compact
|
||||
. = TRUE
|
||||
if("set_category")
|
||||
cur_category = params["category"]
|
||||
cur_subcategory = params["subcategory"] || ""
|
||||
. = TRUE
|
||||
|
||||
/datum/component/personal_crafting/proc/build_recipe_data(datum/crafting_recipe/R)
|
||||
var/list/data = list()
|
||||
data["name"] = R.name
|
||||
data["ref"] = "[REF(R)]"
|
||||
var/list/req_text = list()
|
||||
var/list/tool_list = list()
|
||||
var/list/catalyst_text = list()
|
||||
|
||||
for(var/atom/req_atom as anything in R.reqs)
|
||||
//We just need the name, so cheat-typecast to /atom for speed (even tho Reagents are /datum they DO have a "name" var)
|
||||
//Also these are typepaths so sadly we can't just do "[a]"
|
||||
req_text += "[R.reqs[req_atom]] [initial(req_atom.name)]"
|
||||
for(var/obj/machinery/content as anything in R.machinery)
|
||||
req_text += "[R.reqs[content]] [initial(content.name)]"
|
||||
if(R.additional_req_text)
|
||||
req_text += R.additional_req_text
|
||||
data["req_text"] = req_text.Join(", ")
|
||||
|
||||
for(var/atom/req_catalyst as anything in R.chem_catalysts)
|
||||
catalyst_text += "[R.chem_catalysts[req_catalyst]] [initial(req_catalyst.name)]"
|
||||
data["catalyst_text"] = catalyst_text.Join(", ")
|
||||
|
||||
for(var/required_quality in R.tool_behaviors)
|
||||
tool_list += required_quality
|
||||
for(var/obj/item/required_path as anything in R.tool_paths)
|
||||
tool_list += initial(required_path.name)
|
||||
data["tool_text"] = tool_list.Join(", ")
|
||||
|
||||
return data
|
||||
|
||||
//Mind helpers
|
||||
|
||||
/datum/mind/proc/teach_crafting_recipe(R)
|
||||
if(!learned_recipes)
|
||||
learned_recipes = list()
|
||||
learned_recipes |= R
|
||||
|
||||
// Screen objects
|
||||
/obj/screen/craft
|
||||
name = "crafting menu"
|
||||
icon = 'icons/mob/screen/midnight.dmi'
|
||||
icon_state = "craft"
|
||||
screen_loc = ui_crafting
|
||||
34
code/datums/components/crafting/crafting_external.dm
Normal file
34
code/datums/components/crafting/crafting_external.dm
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Ensure a list of atoms/reagents exists inside this atom
|
||||
*
|
||||
* Goes throught he list of passed in parts, if they're reagents, adds them to our reagent holder
|
||||
* creating the reagent holder if it exists.
|
||||
*
|
||||
* If the part is a moveable atom and the previous location of the item was a mob/living,
|
||||
* it calls the inventory handler transferItemToLoc for that mob/living and transfers the part
|
||||
* to this atom
|
||||
*
|
||||
* Otherwise it simply forceMoves the atom into this atom
|
||||
*/
|
||||
/atom/proc/CheckParts(list/parts_list, datum/crafting_recipe/R)
|
||||
SEND_SIGNAL(src, COMSIG_ATOM_CHECKPARTS, parts_list, R)
|
||||
if(parts_list)
|
||||
for(var/A in parts_list)
|
||||
if(istype(A, /datum/reagent))
|
||||
if(!reagents)
|
||||
reagents = new()
|
||||
reagents.reagent_list.Add(A)
|
||||
reagents.conditional_update()
|
||||
else if(ismovable(A))
|
||||
var/atom/movable/M = A
|
||||
if(isliving(M.loc))
|
||||
var/mob/living/L = M.loc
|
||||
L.unEquip(M, target = src)
|
||||
else
|
||||
M.forceMove(src)
|
||||
SEND_SIGNAL(M, COMSIG_ATOM_USED_IN_CRAFT, src)
|
||||
parts_list.Cut()
|
||||
|
||||
/obj/machinery/CheckParts(list/parts_list)
|
||||
..()
|
||||
RefreshParts()
|
||||
56
code/datums/components/crafting/recipes.dm
Normal file
56
code/datums/components/crafting/recipes.dm
Normal file
@@ -0,0 +1,56 @@
|
||||
///If the machine is used/deleted in the crafting process
|
||||
#define CRAFTING_MACHINERY_CONSUME 1
|
||||
///If the machine is only "used" i.e. it checks to see if it's nearby and allows crafting, but doesn't delete it
|
||||
#define CRAFTING_MACHINERY_USE 0
|
||||
|
||||
/datum/crafting_recipe
|
||||
var/name = "" //in-game display name
|
||||
var/list/reqs = list() //type paths of items consumed associated with how many are needed
|
||||
var/list/blacklist = list() //type paths of items explicitly not allowed as an ingredient
|
||||
var/result //type path of item resulting from this craft
|
||||
/// String defines of items needed but not consumed. Lazy list.
|
||||
var/list/tool_behaviors
|
||||
/// Type paths of items needed but not consumed. Lazy list.
|
||||
var/list/tool_paths
|
||||
var/time = 30 //time in deciseconds
|
||||
var/list/parts = list() //type paths of items that will be placed in the result
|
||||
var/list/chem_catalysts = list() //like tool_behaviors but for reagents
|
||||
var/category = CAT_NONE //where it shows up in the crafting UI
|
||||
var/subcategory = CAT_NONE
|
||||
var/always_available = TRUE //Set to FALSE if it needs to be learned first.
|
||||
/// Additonal requirements text shown in UI
|
||||
var/additional_req_text
|
||||
///Required machines for the craft, set the assigned value of the typepath to CRAFTING_MACHINERY_CONSUME or CRAFTING_MACHINERY_USE. Lazy associative list: type_path key -> flag value.
|
||||
var/list/machinery
|
||||
///Should only one object exist on the same turf?
|
||||
var/one_per_turf = FALSE
|
||||
|
||||
/datum/crafting_recipe/New()
|
||||
if(!(result in reqs))
|
||||
blacklist += result
|
||||
if(tool_behaviors)
|
||||
tool_behaviors = string_list(tool_behaviors)
|
||||
if(tool_paths)
|
||||
tool_paths = string_list(tool_paths)
|
||||
|
||||
/**
|
||||
* Run custom pre-craft checks for this recipe
|
||||
*
|
||||
* user: The /mob that initiated the crafting
|
||||
* collected_requirements: A list of lists of /obj/item instances that satisfy reqs. Top level list is keyed by requirement path.
|
||||
*/
|
||||
/datum/crafting_recipe/proc/check_requirements(mob/user, list/collected_requirements)
|
||||
return TRUE
|
||||
|
||||
/datum/crafting_recipe/proc/on_craft_completion(mob/user, atom/result)
|
||||
return
|
||||
|
||||
/datum/crafting_recipe/stunprod
|
||||
name = "Stunprod"
|
||||
result = /obj/item/weapon/melee/baton/cattleprod
|
||||
reqs = list(/obj/item/weapon/handcuffs/cable = 1,
|
||||
/obj/item/stack/rods = 1,
|
||||
/obj/item/weapon/tool/wirecutters = 1)
|
||||
time = 40
|
||||
category = CAT_WEAPONRY
|
||||
subcategory = CAT_WEAPON
|
||||
29
code/datums/components/crafting/tool_quality.dm
Normal file
29
code/datums/components/crafting/tool_quality.dm
Normal file
@@ -0,0 +1,29 @@
|
||||
/obj/item
|
||||
var/list/tool_qualities
|
||||
|
||||
/// Used to check for a specific tool quality on an item.
|
||||
/// Returns TRUE or FALSE depending on whether `tool_quality` is found.
|
||||
/obj/item/proc/has_tool_quality(tool_quality)
|
||||
return !!LAZYFIND(tool_qualities, tool_quality)
|
||||
|
||||
/* Legacy Support */
|
||||
|
||||
/// DEPRECATED PROC: DO NOT USE IN NEW CODE
|
||||
/obj/item/proc/is_screwdriver()
|
||||
return has_tool_quality(TOOL_SCREWDRIVER)
|
||||
|
||||
/// DEPRECATED PROC: DO NOT USE IN NEW CODE
|
||||
/obj/item/proc/is_wrench()
|
||||
return has_tool_quality(TOOL_WRENCH)
|
||||
|
||||
/// DEPRECATED PROC: DO NOT USE IN NEW CODE
|
||||
/obj/item/proc/is_crowbar()
|
||||
return has_tool_quality(TOOL_CROWBAR)
|
||||
|
||||
/// DEPRECATED PROC: DO NOT USE IN NEW CODE
|
||||
/obj/item/proc/is_wirecutter()
|
||||
return has_tool_quality(TOOL_WIRECUTTER)
|
||||
|
||||
/// DEPRECATED PROC: DO NOT USE IN NEW CODE
|
||||
/obj/item/proc/is_multitool()
|
||||
return has_tool_quality(TOOL_MULTITOOL)
|
||||
@@ -58,6 +58,7 @@
|
||||
var/list/purchase_log = new
|
||||
var/used_TC = 0
|
||||
|
||||
var/list/learned_recipes //List of learned recipe TYPES.
|
||||
|
||||
// the world.time since the mob has been brigged, or -1 if not at all
|
||||
var/brigged_since = -1
|
||||
|
||||
@@ -908,31 +908,6 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out.
|
||||
/obj/item/proc/apply_accessories(var/image/standing)
|
||||
return standing
|
||||
|
||||
/*
|
||||
* Assorted tool procs, so any item can emulate any tool, if coded
|
||||
*/
|
||||
/obj/item/proc/is_screwdriver()
|
||||
return FALSE
|
||||
|
||||
/obj/item/proc/is_wrench()
|
||||
return FALSE
|
||||
|
||||
/obj/item/proc/is_crowbar()
|
||||
return FALSE
|
||||
|
||||
/obj/item/proc/is_wirecutter()
|
||||
return FALSE
|
||||
|
||||
// These next three might bug out or runtime, unless someone goes back and finds a way to generalize their specific code
|
||||
/obj/item/proc/is_cable_coil()
|
||||
return FALSE
|
||||
|
||||
/obj/item/proc/is_multitool()
|
||||
return FALSE
|
||||
|
||||
/obj/item/proc/is_welder()
|
||||
return FALSE
|
||||
|
||||
/obj/item/MouseEntered(location,control,params)
|
||||
. = ..()
|
||||
if(usr.is_preference_enabled(/datum/client_preference/inv_tooltips) && ((src in usr) || isstorage(loc))) // If in inventory or in storage we're looking at
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
var/obj/machinery/connectable //Used to connect machinery.
|
||||
var/weakref_wiring //Used to store weak references for integrated circuitry. This is now the Omnitool.
|
||||
toolspeed = 1
|
||||
tool_qualities = list(TOOL_MULTITOOL)
|
||||
|
||||
/obj/item/device/multitool/attack_self(mob/living/user)
|
||||
var/choice = alert("What do you want to do with \the [src]?","Multitool Menu", "Switch Mode", "Clear Buffers", "Cancel")
|
||||
@@ -65,9 +66,6 @@
|
||||
|
||||
return
|
||||
|
||||
/obj/item/device/multitool/is_multitool()
|
||||
return TRUE
|
||||
|
||||
/obj/item/device/multitool/cyborg
|
||||
name = "multitool"
|
||||
desc = "Optimised and stripped-down version of a regular multitool."
|
||||
|
||||
@@ -176,17 +176,6 @@ var/last_chew = 0
|
||||
/obj/item/weapon/handcuffs/cable/white
|
||||
color = "#FFFFFF"
|
||||
|
||||
/obj/item/weapon/handcuffs/cable/attackby(var/obj/item/I, mob/user as mob)
|
||||
..()
|
||||
if(istype(I, /obj/item/stack/rods))
|
||||
var/obj/item/stack/rods/R = I
|
||||
if (R.use(1))
|
||||
var/obj/item/weapon/material/wirerod/W = new(get_turf(user))
|
||||
user.put_in_hands(W)
|
||||
to_chat(user, "<span class='notice'>You wrap the cable restraint around the top of the rod.</span>")
|
||||
qdel(src)
|
||||
update_icon(user)
|
||||
|
||||
/obj/item/weapon/handcuffs/cyborg
|
||||
dispenser = 1
|
||||
|
||||
|
||||
@@ -38,33 +38,3 @@
|
||||
qdel(W)
|
||||
qdel(src)
|
||||
return
|
||||
|
||||
/obj/item/weapon/material/wirerod
|
||||
name = "wired rod"
|
||||
desc = "A rod with some wire wrapped around the top. It'd be easy to attach something to the top bit."
|
||||
icon_state = "wiredrod"
|
||||
item_state = "rods"
|
||||
force = 8
|
||||
throwforce = 10
|
||||
w_class = ITEMSIZE_NORMAL
|
||||
attack_verb = list("hit", "bludgeoned", "whacked", "bonked")
|
||||
force_divisor = 0.1
|
||||
thrown_force_divisor = 0.1
|
||||
|
||||
/obj/item/weapon/material/wirerod/attackby(var/obj/item/I, mob/user as mob)
|
||||
..()
|
||||
var/obj/item/finished
|
||||
if(istype(I, /obj/item/weapon/material/shard) || istype(I, /obj/item/weapon/material/butterflyblade))
|
||||
var/obj/item/weapon/material/tmp_shard = I
|
||||
finished = new /obj/item/weapon/material/twohanded/spear(get_turf(user), tmp_shard.material.name)
|
||||
to_chat(user, "<span class='notice'>You fasten \the [I] to the top of the rod with the cable.</span>")
|
||||
else if(I.is_wirecutter())
|
||||
finished = new /obj/item/weapon/melee/baton/cattleprod(get_turf(user))
|
||||
to_chat(user, "<span class='notice'>You fasten the wirecutters to the top of the rod with the cable, prongs outward.</span>")
|
||||
if(finished)
|
||||
user.drop_from_inventory(src)
|
||||
user.drop_from_inventory(I)
|
||||
qdel(I)
|
||||
qdel(src)
|
||||
user.put_in_hands(finished)
|
||||
update_icon(user)
|
||||
@@ -20,9 +20,7 @@
|
||||
drop_sound = 'sound/items/drop/crowbar.ogg'
|
||||
pickup_sound = 'sound/items/pickup/crowbar.ogg'
|
||||
toolspeed = 1
|
||||
|
||||
/obj/item/weapon/tool/crowbar/is_crowbar()
|
||||
return TRUE
|
||||
tool_qualities = list(TOOL_CROWBAR)
|
||||
|
||||
/obj/item/weapon/tool/crowbar/red
|
||||
icon = 'icons/obj/tools.dmi'
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
attack_verb = list("stabbed")
|
||||
sharp = 1
|
||||
toolspeed = 1
|
||||
tool_qualities = list(TOOL_SCREWDRIVER)
|
||||
var/random_color = TRUE
|
||||
|
||||
/obj/item/weapon/tool/screwdriver/suicide_act(mob/user)
|
||||
@@ -67,10 +68,6 @@
|
||||
M = user
|
||||
return eyestab(M,user)
|
||||
|
||||
/obj/item/weapon/tool/screwdriver/is_screwdriver()
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/category_item/catalogue/anomalous/precursor_a/alien_screwdriver
|
||||
name = "Precursor Alpha Object - Hard Light Torgue Tool"
|
||||
desc = "This appears to be a tool, with a solid handle, and a thin hard light \
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
|
||||
//R&D tech level
|
||||
origin_tech = list(TECH_ENGINEERING = 1)
|
||||
|
||||
tool_qualities = list(TOOL_WELDER)
|
||||
|
||||
//Welding tool specific stuff
|
||||
var/welding = 0 //Whether or not the welding tool is off(0), on(1) or currently welding(2)
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
sharp = 1
|
||||
edge = 1
|
||||
toolspeed = 1
|
||||
tool_qualities = list(TOOL_WIRECUTTER)
|
||||
var/random_color = TRUE
|
||||
|
||||
/obj/item/weapon/tool/wirecutters/New()
|
||||
@@ -43,10 +44,6 @@
|
||||
else
|
||||
..()
|
||||
|
||||
/obj/item/weapon/tool/wirecutters/is_wirecutter()
|
||||
return TRUE
|
||||
|
||||
|
||||
/datum/category_item/catalogue/anomalous/precursor_a/alien_wirecutters
|
||||
name = "Precursor Alpha Object - Wire Seperator"
|
||||
desc = "An object appearing to have a tool shape. It has two handles, and two \
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
toolspeed = 1
|
||||
drop_sound = 'sound/items/drop/wrench.ogg'
|
||||
pickup_sound = 'sound/items/pickup/wrench.ogg'
|
||||
|
||||
/obj/item/weapon/tool/wrench/is_wrench()
|
||||
return TRUE
|
||||
tool_qualities = list(TOOL_WRENCH)
|
||||
|
||||
/obj/item/weapon/tool/wrench/cyborg
|
||||
name = "automatic wrench"
|
||||
|
||||
@@ -266,7 +266,6 @@
|
||||
prob(4);/obj/item/weapon/material/butterfly,
|
||||
prob(6);/obj/item/weapon/material/butterflyblade,
|
||||
prob(6);/obj/item/weapon/material/butterflyhandle,
|
||||
prob(6);/obj/item/weapon/material/wirerod,
|
||||
prob(2);/obj/item/weapon/material/butterfly/switchblade,
|
||||
prob(2);/obj/item/clothing/gloves/knuckledusters,
|
||||
prob(1);/obj/item/weapon/material/knife/tacknife,
|
||||
|
||||
@@ -145,7 +145,6 @@
|
||||
/obj/random/cargopod/item_to_spawn()
|
||||
return pick(prob(10);/obj/item/weapon/contraband/poster,\
|
||||
prob(8);/obj/item/weapon/haircomb,\
|
||||
prob(6);/obj/item/weapon/material/wirerod,\
|
||||
prob(6);/obj/item/weapon/storage/pill_bottle/paracetamol,\
|
||||
prob(6);/obj/item/weapon/material/butterflyblade,\
|
||||
prob(6);/obj/item/weapon/material/butterflyhandle,\
|
||||
|
||||
@@ -255,7 +255,6 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh
|
||||
/obj/item/stack/material/cardboard{amount = 5},
|
||||
/obj/item/weapon/contraband/poster,
|
||||
/obj/item/weapon/contraband/poster/custom,
|
||||
/obj/item/weapon/material/wirerod,
|
||||
/obj/item/weapon/newspaper,
|
||||
/obj/item/weapon/paper/crumpled,
|
||||
/obj/item/weapon/paper/crumpled/bloody
|
||||
|
||||
@@ -52,6 +52,15 @@
|
||||
dna.real_name = real_name
|
||||
sync_organ_dna()
|
||||
|
||||
<<<<<<< HEAD
|
||||
||||||| parent of b383e17875... Merge pull request #10243 from ShadowLarkens/personal_crafting
|
||||
//verbs |= /mob/living/proc/toggle_selfsurgery //VOREStation Removal
|
||||
|
||||
=======
|
||||
//verbs |= /mob/living/proc/toggle_selfsurgery //VOREStation Removal
|
||||
AddComponent(/datum/component/personal_crafting)
|
||||
|
||||
>>>>>>> b383e17875... Merge pull request #10243 from ShadowLarkens/personal_crafting
|
||||
/mob/living/carbon/human/Destroy()
|
||||
human_mob_list -= src
|
||||
for(var/organ in organs)
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
disconnect_time = null //VOREStation Addition: clear the disconnect time
|
||||
sight |= SEE_SELF
|
||||
..()
|
||||
SEND_SIGNAL(src, COMSIG_MOB_LOGIN)
|
||||
|
||||
if(loc && !isturf(loc))
|
||||
client.eye = loc
|
||||
@@ -81,4 +82,5 @@
|
||||
update_client_z(T.z)
|
||||
|
||||
if(cloaked && cloaked_selfimage)
|
||||
client.images += cloaked_selfimage
|
||||
client.images += cloaked_selfimage
|
||||
SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client)
|
||||
@@ -510,6 +510,7 @@ obj/structure/cable/proc/cableColor(var/colorC)
|
||||
stacktype = /obj/item/stack/cable_coil
|
||||
drop_sound = 'sound/items/drop/accessory.ogg'
|
||||
pickup_sound = 'sound/items/pickup/accessory.ogg'
|
||||
tool_qualities = list(TOOL_CABLE_COIL)
|
||||
|
||||
/obj/item/stack/cable_coil/cyborg
|
||||
name = "cable coil synthesizer"
|
||||
|
||||
@@ -210,7 +210,7 @@ GLOBAL_LIST_EMPTY(gravity_generators)
|
||||
update_icon()
|
||||
return
|
||||
if(GRAV_NEEDS_WELDING)
|
||||
if(I.is_welder())
|
||||
if(I.has_tool_quality(TOOL_WELDER))
|
||||
var/obj/item/weapon/weldingtool/W = I
|
||||
if(W.remove_fuel(0,user))
|
||||
to_chat(user, "<span class='notice'>You mend the damaged framework.</span>")
|
||||
|
||||
@@ -80,7 +80,7 @@
|
||||
material_needs[component_needed] = rand(1,3)
|
||||
|
||||
if(ispath(my_guntype, /obj/item/weapon/gun/launcher) && prob(50))
|
||||
var/component_needed = pick(/obj/item/weapon/tape_roll, /obj/item/weapon/material/wirerod)
|
||||
var/component_needed = pick(/obj/item/weapon/tape_roll, /obj/item/stack/rods, /obj/item/weapon/handcuffs/cable)
|
||||
material_needs[component_needed] = 1
|
||||
|
||||
if(ispath(my_guntype, /obj/item/weapon/gun/magnetic) && prob(70))
|
||||
|
||||
@@ -37,7 +37,8 @@
|
||||
CheckParts()
|
||||
FireModeModify()
|
||||
|
||||
/obj/item/weapon/gun/energy/modular/proc/CheckParts() //What parts do we have inside us, and how good are they?
|
||||
/obj/item/weapon/gun/energy/modular/CheckParts() //What parts do we have inside us, and how good are they?
|
||||
..()
|
||||
capacitor_rating = 0
|
||||
laser_rating = 0
|
||||
manipulator_rating = 0
|
||||
|
||||
250
code/modules/reagents/Chemistry-Reagents.dm
Normal file
250
code/modules/reagents/Chemistry-Reagents.dm
Normal file
@@ -0,0 +1,250 @@
|
||||
|
||||
|
||||
|
||||
/datum/reagent
|
||||
var/name = "Reagent"
|
||||
var/id = "reagent"
|
||||
var/description = "A non-descript chemical."
|
||||
var/taste_description = "bitterness"
|
||||
var/taste_mult = 1 //how this taste compares to others. Higher values means it is more noticable
|
||||
var/datum/reagents/holder = null
|
||||
var/reagent_state = SOLID
|
||||
var/list/data = null
|
||||
var/volume = 0
|
||||
var/metabolism = REM // This would be 0.2 normally
|
||||
var/list/filtered_organs = list() // Organs that will slow the processing of this chemical.
|
||||
var/mrate_static = FALSE //If the reagent should always process at the same speed, regardless of species, make this TRUE
|
||||
var/ingest_met = 0
|
||||
var/touch_met = 0
|
||||
var/dose = 0
|
||||
var/max_dose = 0
|
||||
var/overdose = 0 //Amount at which overdose starts
|
||||
var/overdose_mod = 1 //Modifier to overdose damage
|
||||
var/can_overdose_touch = FALSE // Can the chemical OD when processing on touch?
|
||||
var/scannable = 0 // Shows up on health analyzers.
|
||||
|
||||
var/affects_dead = 0 // Does this chem process inside a corpse?
|
||||
var/affects_robots = 0 // Does this chem process inside a Synth?
|
||||
|
||||
var/allergen_type = GENERIC // What potential allergens does this contain?
|
||||
var/allergen_factor = 1 // If the potential allergens are mixed and low-volume, they're a bit less dangerous. Needed for drinks because they're a single reagent compared to food which contains multiple seperate reagents.
|
||||
|
||||
var/cup_icon_state = null
|
||||
var/cup_name = null
|
||||
var/cup_desc = null
|
||||
var/cup_center_of_mass = null
|
||||
|
||||
var/color = "#000000"
|
||||
var/color_weight = 1
|
||||
|
||||
var/glass_icon = DRINK_ICON_DEFAULT
|
||||
var/glass_name = "something"
|
||||
var/glass_desc = "It's a glass of... what, exactly?"
|
||||
var/list/glass_special = null // null equivalent to list()
|
||||
|
||||
/datum/reagent/proc/remove_self(var/amount) // Shortcut
|
||||
if(holder)
|
||||
holder.remove_reagent(id, amount)
|
||||
|
||||
// This doesn't apply to skin contact - this is for, e.g. extinguishers and sprays. The difference is that reagent is not directly on the mob's skin - it might just be on their clothing.
|
||||
/datum/reagent/proc/touch_mob(var/mob/M, var/amount)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/touch_obj(var/obj/O, var/amount) // Acid melting, cleaner cleaning, etc
|
||||
return
|
||||
|
||||
/datum/reagent/proc/touch_turf(var/turf/T, var/amount) // Cleaner cleaning, lube lubbing, etc, all go here
|
||||
return
|
||||
|
||||
/datum/reagent/proc/on_mob_life(var/mob/living/carbon/M, var/alien, var/datum/reagents/metabolism/location) // Currently, on_mob_life is called on carbons. Any interaction with non-carbon mobs (lube) will need to be done in touch_mob.
|
||||
if(!istype(M))
|
||||
return
|
||||
if(!affects_dead && M.stat == DEAD)
|
||||
return
|
||||
if(!affects_robots && M.isSynthetic())
|
||||
return
|
||||
if(!istype(location))
|
||||
return
|
||||
|
||||
var/datum/reagents/metabolism/active_metab = location
|
||||
var/removed = metabolism
|
||||
|
||||
var/ingest_rem_mult = 1
|
||||
var/ingest_abs_mult = 1
|
||||
|
||||
if(!mrate_static == TRUE)
|
||||
// Modifiers
|
||||
for(var/datum/modifier/mod in M.modifiers)
|
||||
if(!isnull(mod.metabolism_percent))
|
||||
removed *= mod.metabolism_percent
|
||||
ingest_rem_mult *= mod.metabolism_percent
|
||||
// Species
|
||||
removed *= M.species.metabolic_rate
|
||||
ingest_rem_mult *= M.species.metabolic_rate
|
||||
// Metabolism
|
||||
removed *= active_metab.metabolism_speed
|
||||
ingest_rem_mult *= active_metab.metabolism_speed
|
||||
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(!H.isSynthetic())
|
||||
if(H.species.has_organ[O_HEART] && (active_metab.metabolism_class == CHEM_BLOOD))
|
||||
var/obj/item/organ/internal/heart/Pump = H.internal_organs_by_name[O_HEART]
|
||||
if(!Pump)
|
||||
removed *= 0.1
|
||||
else if(Pump.standard_pulse_level == PULSE_NONE) // No pulse normally means chemicals process a little bit slower than normal.
|
||||
removed *= 0.8
|
||||
else // Otherwise, chemicals process as per percentage of your current pulse, or, if you have no pulse but are alive, by a miniscule amount.
|
||||
removed *= max(0.1, H.pulse / Pump.standard_pulse_level)
|
||||
|
||||
if(H.species.has_organ[O_STOMACH] && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
var/obj/item/organ/internal/stomach/Chamber = H.internal_organs_by_name[O_STOMACH]
|
||||
if(Chamber)
|
||||
ingest_rem_mult *= max(0.1, 1 - (Chamber.damage / Chamber.max_damage))
|
||||
else
|
||||
ingest_rem_mult = 0.1
|
||||
|
||||
if(H.species.has_organ[O_INTESTINE] && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
var/obj/item/organ/internal/intestine/Tube = H.internal_organs_by_name[O_INTESTINE]
|
||||
if(Tube)
|
||||
ingest_abs_mult *= max(0.1, 1 - (Tube.damage / Tube.max_damage))
|
||||
else
|
||||
ingest_abs_mult = 0.1
|
||||
|
||||
else
|
||||
var/obj/item/organ/internal/heart/machine/Pump = H.internal_organs_by_name[O_PUMP]
|
||||
var/obj/item/organ/internal/stomach/machine/Cycler = H.internal_organs_by_name[O_CYCLER]
|
||||
|
||||
if(active_metab.metabolism_class == CHEM_BLOOD)
|
||||
if(Pump)
|
||||
removed *= 1.1 - Pump.damage / Pump.max_damage
|
||||
else
|
||||
removed *= 0.1
|
||||
|
||||
else if(active_metab.metabolism_class == CHEM_INGEST) // If the pump is damaged, we waste chems from the tank.
|
||||
if(Pump)
|
||||
ingest_abs_mult *= max(0.25, 1 - Pump.damage / Pump.max_damage)
|
||||
|
||||
else
|
||||
ingest_abs_mult *= 0.2
|
||||
|
||||
if(Cycler) // If we're damaged, we empty our tank slower.
|
||||
ingest_rem_mult = max(0.1, 1 - (Cycler.damage / Cycler.max_damage))
|
||||
|
||||
else
|
||||
ingest_rem_mult = 0.1
|
||||
|
||||
else if(active_metab.metabolism_class == CHEM_TOUCH) // Machines don't exactly absorb chemicals.
|
||||
removed *= 0.5
|
||||
|
||||
if(filtered_organs && filtered_organs.len)
|
||||
for(var/organ_tag in filtered_organs)
|
||||
var/obj/item/organ/internal/O = H.internal_organs_by_name[organ_tag]
|
||||
if(O && !O.is_broken() && prob(max(0, O.max_damage - O.damage)))
|
||||
removed *= 0.8
|
||||
if(active_metab.metabolism_class == CHEM_INGEST)
|
||||
ingest_rem_mult *= 0.8
|
||||
|
||||
if(ingest_met && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
removed = ingest_met * ingest_rem_mult
|
||||
if(touch_met && (active_metab.metabolism_class == CHEM_TOUCH))
|
||||
removed = touch_met
|
||||
removed = min(removed, volume)
|
||||
max_dose = max(volume, max_dose)
|
||||
dose = min(dose + removed, max_dose)
|
||||
if(removed >= (metabolism * 0.1) || removed >= 0.1) // If there's too little chemical, don't affect the mob, just remove it
|
||||
switch(active_metab.metabolism_class)
|
||||
if(CHEM_BLOOD)
|
||||
affect_blood(M, alien, removed)
|
||||
if(CHEM_INGEST)
|
||||
affect_ingest(M, alien, removed * ingest_abs_mult)
|
||||
if(CHEM_TOUCH)
|
||||
affect_touch(M, alien, removed)
|
||||
if(overdose && (volume > overdose * M?.species.chemOD_threshold) && (active_metab.metabolism_class != CHEM_TOUCH && !can_overdose_touch))
|
||||
overdose(M, alien, removed)
|
||||
if(M.species.allergens & allergen_type) //uhoh, we can't handle this!
|
||||
var/damage_severity = M.species.allergen_damage_severity*allergen_factor
|
||||
var/disable_severity = M.species.allergen_disable_severity*allergen_factor
|
||||
if(M.species.allergen_reaction & AG_TOX_DMG)
|
||||
M.adjustToxLoss(damage_severity)
|
||||
if(M.species.allergen_reaction & AG_OXY_DMG)
|
||||
M.adjustOxyLoss(damage_severity)
|
||||
if(prob(2.5*disable_severity))
|
||||
M.emote(pick("cough","gasp","choke"))
|
||||
if(M.species.allergen_reaction & AG_EMOTE)
|
||||
if(prob(2.5*disable_severity)) //this has a higher base chance, but not *too* high
|
||||
M.emote(pick("pale","shiver","twitch"))
|
||||
if(M.species.allergen_reaction & AG_PAIN)
|
||||
M.adjustHalLoss(disable_severity)
|
||||
if(M.species.allergen_reaction & AG_WEAKEN)
|
||||
M.Weaken(disable_severity)
|
||||
if(M.species.allergen_reaction & AG_BLURRY)
|
||||
M.eye_blurry = max(M.eye_blurry, disable_severity)
|
||||
if(M.species.allergen_reaction & AG_SLEEPY)
|
||||
M.drowsyness = max(M.drowsyness, disable_severity)
|
||||
remove_self(removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
M.bloodstr.add_reagent(id, removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_touch(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/overdose(var/mob/living/carbon/M, var/alien, var/removed) // Overdose effect.
|
||||
if(alien == IS_DIONA)
|
||||
return
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
overdose_mod *= H.species.chemOD_mod
|
||||
// 6 damage per unit at minimum, scales with excessive reagents. Rounding should help keep damage consistent between ingest / inject, but isn't perfect.
|
||||
// Hardcapped at 3.6 damage per tick, or 18 damage per unit at 0.2 metabolic rate so that you can't instakill people with overdoses by feeding them infinite periadaxon.
|
||||
// Overall, max damage is slightly less effective than hydrophoron, and 1/5 as effective as cyanide.
|
||||
M.adjustToxLoss(min(removed * overdose_mod * round(3 + 3 * volume / overdose), 3.6))
|
||||
|
||||
/datum/reagent/proc/initialize_data(var/newdata) // Called when the reagent is created.
|
||||
if(!isnull(newdata))
|
||||
data = newdata
|
||||
return
|
||||
|
||||
/datum/reagent/proc/mix_data(var/newdata, var/newamount) // You have a reagent with data, and new reagent with its own data get added, how do you deal with that?
|
||||
return
|
||||
|
||||
/datum/reagent/proc/get_data() // Just in case you have a reagent that handles data differently.
|
||||
if(data && istype(data, /list))
|
||||
return data.Copy()
|
||||
else if(data)
|
||||
return data
|
||||
return null
|
||||
|
||||
/datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references
|
||||
holder = null
|
||||
. = ..()
|
||||
|
||||
// Called when reagents are removed from a container, most likely after metabolizing in a mob
|
||||
/datum/reagent/proc/on_remove(var/atom/A)
|
||||
return
|
||||
|
||||
// Called when a mob dies
|
||||
/datum/reagent/proc/on_mob_death(var/mob/M)
|
||||
return
|
||||
|
||||
//on transfer to new container, return 1 to allow it to continue
|
||||
/datum/reagent/proc/on_transfer(var/volume)
|
||||
return 1
|
||||
|
||||
/* DEPRECATED - TODO: REMOVE EVERYWHERE */
|
||||
|
||||
/datum/reagent/proc/reaction_turf(var/turf/target)
|
||||
touch_turf(target)
|
||||
|
||||
/datum/reagent/proc/reaction_obj(var/obj/target)
|
||||
touch_obj(target)
|
||||
|
||||
/datum/reagent/proc/reaction_mob(var/mob/target)
|
||||
touch_mob(target)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,251 +1,251 @@
|
||||
|
||||
|
||||
|
||||
/datum/reagent
|
||||
var/name = "Reagent"
|
||||
var/id = "reagent"
|
||||
var/description = "A non-descript chemical."
|
||||
var/taste_description = "bitterness"
|
||||
var/taste_mult = 1 //how this taste compares to others. Higher values means it is more noticable
|
||||
var/datum/reagents/holder = null
|
||||
var/reagent_state = SOLID
|
||||
var/list/data = null
|
||||
var/volume = 0
|
||||
var/metabolism = REM // This would be 0.2 normally
|
||||
var/list/filtered_organs = list() // Organs that will slow the processing of this chemical.
|
||||
var/mrate_static = FALSE //If the reagent should always process at the same speed, regardless of species, make this TRUE
|
||||
var/ingest_met = 0
|
||||
var/touch_met = 0
|
||||
var/dose = 0
|
||||
var/max_dose = 0
|
||||
var/overdose = 0 //Amount at which overdose starts
|
||||
var/overdose_mod = 1 //Modifier to overdose damage
|
||||
var/can_overdose_touch = FALSE // Can the chemical OD when processing on touch?
|
||||
var/scannable = 0 // Shows up on health analyzers.
|
||||
|
||||
var/affects_dead = 0 // Does this chem process inside a corpse?
|
||||
var/affects_robots = 0 // Does this chem process inside a Synth?
|
||||
|
||||
var/allergen_type = GENERIC // What potential allergens does this contain?
|
||||
var/allergen_factor = 1 // If the potential allergens are mixed and low-volume, they're a bit less dangerous. Needed for drinks because they're a single reagent compared to food which contains multiple seperate reagents.
|
||||
|
||||
var/cup_icon_state = null
|
||||
var/cup_name = null
|
||||
var/cup_desc = null
|
||||
var/cup_center_of_mass = null
|
||||
|
||||
var/color = "#000000"
|
||||
var/color_weight = 1
|
||||
|
||||
var/glass_icon = DRINK_ICON_DEFAULT
|
||||
var/glass_name = "something"
|
||||
var/glass_desc = "It's a glass of... what, exactly?"
|
||||
var/list/glass_special = null // null equivalent to list()
|
||||
|
||||
/datum/reagent/proc/remove_self(var/amount) // Shortcut
|
||||
if(holder)
|
||||
holder.remove_reagent(id, amount)
|
||||
|
||||
// This doesn't apply to skin contact - this is for, e.g. extinguishers and sprays. The difference is that reagent is not directly on the mob's skin - it might just be on their clothing.
|
||||
/datum/reagent/proc/touch_mob(var/mob/M, var/amount)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/touch_obj(var/obj/O, var/amount) // Acid melting, cleaner cleaning, etc
|
||||
return
|
||||
|
||||
/datum/reagent/proc/touch_turf(var/turf/T, var/amount) // Cleaner cleaning, lube lubbing, etc, all go here
|
||||
return
|
||||
|
||||
/datum/reagent/proc/on_mob_life(var/mob/living/carbon/M, var/alien, var/datum/reagents/metabolism/location) // Currently, on_mob_life is called on carbons. Any interaction with non-carbon mobs (lube) will need to be done in touch_mob.
|
||||
if(!istype(M))
|
||||
return
|
||||
if(!affects_dead && M.stat == DEAD)
|
||||
return
|
||||
if(!affects_robots && M.isSynthetic())
|
||||
return
|
||||
if(!istype(location))
|
||||
return
|
||||
|
||||
var/datum/reagents/metabolism/active_metab = location
|
||||
var/removed = metabolism
|
||||
|
||||
var/ingest_rem_mult = 1
|
||||
var/ingest_abs_mult = 1
|
||||
|
||||
if(!mrate_static == TRUE)
|
||||
// Modifiers
|
||||
for(var/datum/modifier/mod in M.modifiers)
|
||||
if(!isnull(mod.metabolism_percent))
|
||||
removed *= mod.metabolism_percent
|
||||
ingest_rem_mult *= mod.metabolism_percent
|
||||
// Species
|
||||
removed *= M.species.metabolic_rate
|
||||
ingest_rem_mult *= M.species.metabolic_rate
|
||||
// Metabolism
|
||||
removed *= active_metab.metabolism_speed
|
||||
ingest_rem_mult *= active_metab.metabolism_speed
|
||||
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(!H.isSynthetic())
|
||||
if(H.species.has_organ[O_HEART] && (active_metab.metabolism_class == CHEM_BLOOD))
|
||||
var/obj/item/organ/internal/heart/Pump = H.internal_organs_by_name[O_HEART]
|
||||
if(!Pump)
|
||||
removed *= 0.1
|
||||
else if(Pump.standard_pulse_level == PULSE_NONE) // No pulse normally means chemicals process a little bit slower than normal.
|
||||
removed *= 0.8
|
||||
else // Otherwise, chemicals process as per percentage of your current pulse, or, if you have no pulse but are alive, by a miniscule amount.
|
||||
removed *= max(0.1, H.pulse / Pump.standard_pulse_level)
|
||||
|
||||
if(H.species.has_organ[O_STOMACH] && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
var/obj/item/organ/internal/stomach/Chamber = H.internal_organs_by_name[O_STOMACH]
|
||||
if(Chamber)
|
||||
ingest_rem_mult *= max(0.1, 1 - (Chamber.damage / Chamber.max_damage))
|
||||
else
|
||||
ingest_rem_mult = 0.1
|
||||
|
||||
if(H.species.has_organ[O_INTESTINE] && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
var/obj/item/organ/internal/intestine/Tube = H.internal_organs_by_name[O_INTESTINE]
|
||||
if(Tube)
|
||||
ingest_abs_mult *= max(0.1, 1 - (Tube.damage / Tube.max_damage))
|
||||
else
|
||||
ingest_abs_mult = 0.1
|
||||
|
||||
else
|
||||
var/obj/item/organ/internal/heart/machine/Pump = H.internal_organs_by_name[O_PUMP]
|
||||
var/obj/item/organ/internal/stomach/machine/Cycler = H.internal_organs_by_name[O_CYCLER]
|
||||
|
||||
if(active_metab.metabolism_class == CHEM_BLOOD)
|
||||
if(Pump)
|
||||
removed *= 1.1 - Pump.damage / Pump.max_damage
|
||||
else
|
||||
removed *= 0.1
|
||||
|
||||
else if(active_metab.metabolism_class == CHEM_INGEST) // If the pump is damaged, we waste chems from the tank.
|
||||
if(Pump)
|
||||
ingest_abs_mult *= max(0.25, 1 - Pump.damage / Pump.max_damage)
|
||||
|
||||
else
|
||||
ingest_abs_mult *= 0.2
|
||||
|
||||
if(Cycler) // If we're damaged, we empty our tank slower.
|
||||
ingest_rem_mult = max(0.1, 1 - (Cycler.damage / Cycler.max_damage))
|
||||
|
||||
else
|
||||
ingest_rem_mult = 0.1
|
||||
|
||||
else if(active_metab.metabolism_class == CHEM_TOUCH) // Machines don't exactly absorb chemicals.
|
||||
removed *= 0.5
|
||||
|
||||
if(filtered_organs && filtered_organs.len)
|
||||
for(var/organ_tag in filtered_organs)
|
||||
var/obj/item/organ/internal/O = H.internal_organs_by_name[organ_tag]
|
||||
if(O && !O.is_broken() && prob(max(0, O.max_damage - O.damage)))
|
||||
removed *= 0.8
|
||||
if(active_metab.metabolism_class == CHEM_INGEST)
|
||||
ingest_rem_mult *= 0.8
|
||||
|
||||
if(ingest_met && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
removed = ingest_met * ingest_rem_mult
|
||||
if(touch_met && (active_metab.metabolism_class == CHEM_TOUCH))
|
||||
removed = touch_met
|
||||
removed = min(removed, volume)
|
||||
max_dose = max(volume, max_dose)
|
||||
dose = min(dose + removed, max_dose)
|
||||
if(removed >= (metabolism * 0.1) || removed >= 0.1) // If there's too little chemical, don't affect the mob, just remove it
|
||||
switch(active_metab.metabolism_class)
|
||||
if(CHEM_BLOOD)
|
||||
affect_blood(M, alien, removed)
|
||||
if(CHEM_INGEST)
|
||||
affect_ingest(M, alien, removed * ingest_abs_mult)
|
||||
if(CHEM_TOUCH)
|
||||
affect_touch(M, alien, removed)
|
||||
if(overdose && (volume > overdose * M?.species.chemOD_threshold) && (active_metab.metabolism_class != CHEM_TOUCH && !can_overdose_touch))
|
||||
overdose(M, alien, removed)
|
||||
if(M.species.allergens & allergen_type) //uhoh, we can't handle this!
|
||||
var/damage_severity = M.species.allergen_damage_severity*allergen_factor
|
||||
var/disable_severity = M.species.allergen_disable_severity*allergen_factor
|
||||
if(M.species.allergen_reaction & AG_TOX_DMG)
|
||||
M.adjustToxLoss(damage_severity)
|
||||
if(M.species.allergen_reaction & AG_OXY_DMG)
|
||||
M.adjustOxyLoss(damage_severity)
|
||||
if(prob(2.5*disable_severity))
|
||||
M.emote(pick("cough","gasp","choke"))
|
||||
if(M.species.allergen_reaction & AG_EMOTE)
|
||||
if(prob(2.5*disable_severity)) //this has a higher base chance, but not *too* high
|
||||
M.emote(pick("pale","shiver","twitch"))
|
||||
if(M.species.allergen_reaction & AG_PAIN)
|
||||
M.adjustHalLoss(disable_severity)
|
||||
if(M.species.allergen_reaction & AG_WEAKEN)
|
||||
M.Weaken(disable_severity)
|
||||
if(M.species.allergen_reaction & AG_BLURRY)
|
||||
M.eye_blurry = max(M.eye_blurry, disable_severity)
|
||||
if(M.species.allergen_reaction & AG_SLEEPY)
|
||||
M.drowsyness = max(M.drowsyness, disable_severity)
|
||||
remove_self(removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
M.bloodstr.add_reagent(id, removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_touch(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/overdose(var/mob/living/carbon/M, var/alien, var/removed) // Overdose effect.
|
||||
if(alien == IS_DIONA)
|
||||
return
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
overdose_mod *= H.species.chemOD_mod
|
||||
// 6 damage per unit at minimum, scales with excessive reagents. Rounding should help keep damage consistent between ingest / inject, but isn't perfect.
|
||||
// Hardcapped at 3.6 damage per tick, or 18 damage per unit at 0.2 metabolic rate so that you can't instakill people with overdoses by feeding them infinite periadaxon.
|
||||
// Overall, max damage is slightly less effective than hydrophoron, and 1/5 as effective as cyanide.
|
||||
M.adjustToxLoss(min(removed * overdose_mod * round(3 + 3 * volume / overdose), 3.6))
|
||||
|
||||
/datum/reagent/proc/initialize_data(var/newdata) // Called when the reagent is created.
|
||||
if(!isnull(newdata))
|
||||
data = newdata
|
||||
return
|
||||
|
||||
/datum/reagent/proc/mix_data(var/newdata, var/newamount) // You have a reagent with data, and new reagent with its own data get added, how do you deal with that?
|
||||
return
|
||||
|
||||
/datum/reagent/proc/get_data() // Just in case you have a reagent that handles data differently.
|
||||
if(data && istype(data, /list))
|
||||
return data.Copy()
|
||||
else if(data)
|
||||
return data
|
||||
return null
|
||||
|
||||
/datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references
|
||||
holder = null
|
||||
. = ..()
|
||||
|
||||
//YW edit start
|
||||
// Called when reagents are removed from a container, most likely after metabolizing in a mob
|
||||
/datum/reagent/proc/on_remove(var/atom/A)
|
||||
return
|
||||
|
||||
// Called when a mob dies
|
||||
/datum/reagent/proc/on_mob_death(var/mob/M)
|
||||
return
|
||||
|
||||
//on transfer to new container, return 1 to allow it to continue
|
||||
/datum/reagent/proc/on_transfer(var/volume)
|
||||
return 1
|
||||
//YW edit end
|
||||
|
||||
/* DEPRECATED - TODO: REMOVE EVERYWHERE */
|
||||
|
||||
/datum/reagent/proc/reaction_turf(var/turf/target)
|
||||
touch_turf(target)
|
||||
|
||||
/datum/reagent/proc/reaction_obj(var/obj/target)
|
||||
touch_obj(target)
|
||||
|
||||
/datum/reagent/proc/reaction_mob(var/mob/target)
|
||||
touch_mob(target)
|
||||
|
||||
|
||||
|
||||
/datum/reagent
|
||||
var/name = "Reagent"
|
||||
var/id = "reagent"
|
||||
var/description = "A non-descript chemical."
|
||||
var/taste_description = "bitterness"
|
||||
var/taste_mult = 1 //how this taste compares to others. Higher values means it is more noticable
|
||||
var/datum/reagents/holder = null
|
||||
var/reagent_state = SOLID
|
||||
var/list/data = null
|
||||
var/volume = 0
|
||||
var/metabolism = REM // This would be 0.2 normally
|
||||
var/list/filtered_organs = list() // Organs that will slow the processing of this chemical.
|
||||
var/mrate_static = FALSE //If the reagent should always process at the same speed, regardless of species, make this TRUE
|
||||
var/ingest_met = 0
|
||||
var/touch_met = 0
|
||||
var/dose = 0
|
||||
var/max_dose = 0
|
||||
var/overdose = 0 //Amount at which overdose starts
|
||||
var/overdose_mod = 1 //Modifier to overdose damage
|
||||
var/can_overdose_touch = FALSE // Can the chemical OD when processing on touch?
|
||||
var/scannable = 0 // Shows up on health analyzers.
|
||||
|
||||
var/affects_dead = 0 // Does this chem process inside a corpse?
|
||||
var/affects_robots = 0 // Does this chem process inside a Synth?
|
||||
|
||||
var/allergen_type = GENERIC // What potential allergens does this contain?
|
||||
var/allergen_factor = 1 // If the potential allergens are mixed and low-volume, they're a bit less dangerous. Needed for drinks because they're a single reagent compared to food which contains multiple seperate reagents.
|
||||
|
||||
var/cup_icon_state = null
|
||||
var/cup_name = null
|
||||
var/cup_desc = null
|
||||
var/cup_center_of_mass = null
|
||||
|
||||
var/color = "#000000"
|
||||
var/color_weight = 1
|
||||
|
||||
var/glass_icon = DRINK_ICON_DEFAULT
|
||||
var/glass_name = "something"
|
||||
var/glass_desc = "It's a glass of... what, exactly?"
|
||||
var/list/glass_special = null // null equivalent to list()
|
||||
|
||||
/datum/reagent/proc/remove_self(var/amount) // Shortcut
|
||||
if(holder)
|
||||
holder.remove_reagent(id, amount)
|
||||
|
||||
// This doesn't apply to skin contact - this is for, e.g. extinguishers and sprays. The difference is that reagent is not directly on the mob's skin - it might just be on their clothing.
|
||||
/datum/reagent/proc/touch_mob(var/mob/M, var/amount)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/touch_obj(var/obj/O, var/amount) // Acid melting, cleaner cleaning, etc
|
||||
return
|
||||
|
||||
/datum/reagent/proc/touch_turf(var/turf/T, var/amount) // Cleaner cleaning, lube lubbing, etc, all go here
|
||||
return
|
||||
|
||||
/datum/reagent/proc/on_mob_life(var/mob/living/carbon/M, var/alien, var/datum/reagents/metabolism/location) // Currently, on_mob_life is called on carbons. Any interaction with non-carbon mobs (lube) will need to be done in touch_mob.
|
||||
if(!istype(M))
|
||||
return
|
||||
if(!affects_dead && M.stat == DEAD)
|
||||
return
|
||||
if(!affects_robots && M.isSynthetic())
|
||||
return
|
||||
if(!istype(location))
|
||||
return
|
||||
|
||||
var/datum/reagents/metabolism/active_metab = location
|
||||
var/removed = metabolism
|
||||
|
||||
var/ingest_rem_mult = 1
|
||||
var/ingest_abs_mult = 1
|
||||
|
||||
if(!mrate_static == TRUE)
|
||||
// Modifiers
|
||||
for(var/datum/modifier/mod in M.modifiers)
|
||||
if(!isnull(mod.metabolism_percent))
|
||||
removed *= mod.metabolism_percent
|
||||
ingest_rem_mult *= mod.metabolism_percent
|
||||
// Species
|
||||
removed *= M.species.metabolic_rate
|
||||
ingest_rem_mult *= M.species.metabolic_rate
|
||||
// Metabolism
|
||||
removed *= active_metab.metabolism_speed
|
||||
ingest_rem_mult *= active_metab.metabolism_speed
|
||||
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(!H.isSynthetic())
|
||||
if(H.species.has_organ[O_HEART] && (active_metab.metabolism_class == CHEM_BLOOD))
|
||||
var/obj/item/organ/internal/heart/Pump = H.internal_organs_by_name[O_HEART]
|
||||
if(!Pump)
|
||||
removed *= 0.1
|
||||
else if(Pump.standard_pulse_level == PULSE_NONE) // No pulse normally means chemicals process a little bit slower than normal.
|
||||
removed *= 0.8
|
||||
else // Otherwise, chemicals process as per percentage of your current pulse, or, if you have no pulse but are alive, by a miniscule amount.
|
||||
removed *= max(0.1, H.pulse / Pump.standard_pulse_level)
|
||||
|
||||
if(H.species.has_organ[O_STOMACH] && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
var/obj/item/organ/internal/stomach/Chamber = H.internal_organs_by_name[O_STOMACH]
|
||||
if(Chamber)
|
||||
ingest_rem_mult *= max(0.1, 1 - (Chamber.damage / Chamber.max_damage))
|
||||
else
|
||||
ingest_rem_mult = 0.1
|
||||
|
||||
if(H.species.has_organ[O_INTESTINE] && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
var/obj/item/organ/internal/intestine/Tube = H.internal_organs_by_name[O_INTESTINE]
|
||||
if(Tube)
|
||||
ingest_abs_mult *= max(0.1, 1 - (Tube.damage / Tube.max_damage))
|
||||
else
|
||||
ingest_abs_mult = 0.1
|
||||
|
||||
else
|
||||
var/obj/item/organ/internal/heart/machine/Pump = H.internal_organs_by_name[O_PUMP]
|
||||
var/obj/item/organ/internal/stomach/machine/Cycler = H.internal_organs_by_name[O_CYCLER]
|
||||
|
||||
if(active_metab.metabolism_class == CHEM_BLOOD)
|
||||
if(Pump)
|
||||
removed *= 1.1 - Pump.damage / Pump.max_damage
|
||||
else
|
||||
removed *= 0.1
|
||||
|
||||
else if(active_metab.metabolism_class == CHEM_INGEST) // If the pump is damaged, we waste chems from the tank.
|
||||
if(Pump)
|
||||
ingest_abs_mult *= max(0.25, 1 - Pump.damage / Pump.max_damage)
|
||||
|
||||
else
|
||||
ingest_abs_mult *= 0.2
|
||||
|
||||
if(Cycler) // If we're damaged, we empty our tank slower.
|
||||
ingest_rem_mult = max(0.1, 1 - (Cycler.damage / Cycler.max_damage))
|
||||
|
||||
else
|
||||
ingest_rem_mult = 0.1
|
||||
|
||||
else if(active_metab.metabolism_class == CHEM_TOUCH) // Machines don't exactly absorb chemicals.
|
||||
removed *= 0.5
|
||||
|
||||
if(filtered_organs && filtered_organs.len)
|
||||
for(var/organ_tag in filtered_organs)
|
||||
var/obj/item/organ/internal/O = H.internal_organs_by_name[organ_tag]
|
||||
if(O && !O.is_broken() && prob(max(0, O.max_damage - O.damage)))
|
||||
removed *= 0.8
|
||||
if(active_metab.metabolism_class == CHEM_INGEST)
|
||||
ingest_rem_mult *= 0.8
|
||||
|
||||
if(ingest_met && (active_metab.metabolism_class == CHEM_INGEST))
|
||||
removed = ingest_met * ingest_rem_mult
|
||||
if(touch_met && (active_metab.metabolism_class == CHEM_TOUCH))
|
||||
removed = touch_met
|
||||
removed = min(removed, volume)
|
||||
max_dose = max(volume, max_dose)
|
||||
dose = min(dose + removed, max_dose)
|
||||
if(removed >= (metabolism * 0.1) || removed >= 0.1) // If there's too little chemical, don't affect the mob, just remove it
|
||||
switch(active_metab.metabolism_class)
|
||||
if(CHEM_BLOOD)
|
||||
affect_blood(M, alien, removed)
|
||||
if(CHEM_INGEST)
|
||||
affect_ingest(M, alien, removed * ingest_abs_mult)
|
||||
if(CHEM_TOUCH)
|
||||
affect_touch(M, alien, removed)
|
||||
if(overdose && (volume > overdose * M?.species.chemOD_threshold) && (active_metab.metabolism_class != CHEM_TOUCH && !can_overdose_touch))
|
||||
overdose(M, alien, removed)
|
||||
if(M.species.allergens & allergen_type) //uhoh, we can't handle this!
|
||||
var/damage_severity = M.species.allergen_damage_severity*allergen_factor
|
||||
var/disable_severity = M.species.allergen_disable_severity*allergen_factor
|
||||
if(M.species.allergen_reaction & AG_TOX_DMG)
|
||||
M.adjustToxLoss(damage_severity)
|
||||
if(M.species.allergen_reaction & AG_OXY_DMG)
|
||||
M.adjustOxyLoss(damage_severity)
|
||||
if(prob(2.5*disable_severity))
|
||||
M.emote(pick("cough","gasp","choke"))
|
||||
if(M.species.allergen_reaction & AG_EMOTE)
|
||||
if(prob(2.5*disable_severity)) //this has a higher base chance, but not *too* high
|
||||
M.emote(pick("pale","shiver","twitch"))
|
||||
if(M.species.allergen_reaction & AG_PAIN)
|
||||
M.adjustHalLoss(disable_severity)
|
||||
if(M.species.allergen_reaction & AG_WEAKEN)
|
||||
M.Weaken(disable_severity)
|
||||
if(M.species.allergen_reaction & AG_BLURRY)
|
||||
M.eye_blurry = max(M.eye_blurry, disable_severity)
|
||||
if(M.species.allergen_reaction & AG_SLEEPY)
|
||||
M.drowsyness = max(M.drowsyness, disable_severity)
|
||||
remove_self(removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_blood(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
M.bloodstr.add_reagent(id, removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/affect_touch(var/mob/living/carbon/M, var/alien, var/removed)
|
||||
return
|
||||
|
||||
/datum/reagent/proc/overdose(var/mob/living/carbon/M, var/alien, var/removed) // Overdose effect.
|
||||
if(alien == IS_DIONA)
|
||||
return
|
||||
if(ishuman(M))
|
||||
var/mob/living/carbon/human/H = M
|
||||
overdose_mod *= H.species.chemOD_mod
|
||||
// 6 damage per unit at minimum, scales with excessive reagents. Rounding should help keep damage consistent between ingest / inject, but isn't perfect.
|
||||
// Hardcapped at 3.6 damage per tick, or 18 damage per unit at 0.2 metabolic rate so that you can't instakill people with overdoses by feeding them infinite periadaxon.
|
||||
// Overall, max damage is slightly less effective than hydrophoron, and 1/5 as effective as cyanide.
|
||||
M.adjustToxLoss(min(removed * overdose_mod * round(3 + 3 * volume / overdose), 3.6))
|
||||
|
||||
/datum/reagent/proc/initialize_data(var/newdata) // Called when the reagent is created.
|
||||
if(!isnull(newdata))
|
||||
data = newdata
|
||||
return
|
||||
|
||||
/datum/reagent/proc/mix_data(var/newdata, var/newamount) // You have a reagent with data, and new reagent with its own data get added, how do you deal with that?
|
||||
return
|
||||
|
||||
/datum/reagent/proc/get_data() // Just in case you have a reagent that handles data differently.
|
||||
if(data && istype(data, /list))
|
||||
return data.Copy()
|
||||
else if(data)
|
||||
return data
|
||||
return null
|
||||
|
||||
/datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references
|
||||
holder = null
|
||||
. = ..()
|
||||
|
||||
//YW edit start
|
||||
// Called when reagents are removed from a container, most likely after metabolizing in a mob
|
||||
/datum/reagent/proc/on_remove(var/atom/A)
|
||||
return
|
||||
|
||||
// Called when a mob dies
|
||||
/datum/reagent/proc/on_mob_death(var/mob/M)
|
||||
return
|
||||
|
||||
//on transfer to new container, return 1 to allow it to continue
|
||||
/datum/reagent/proc/on_transfer(var/volume)
|
||||
return 1
|
||||
//YW edit end
|
||||
|
||||
/* DEPRECATED - TODO: REMOVE EVERYWHERE */
|
||||
|
||||
/datum/reagent/proc/reaction_turf(var/turf/target)
|
||||
touch_turf(target)
|
||||
|
||||
/datum/reagent/proc/reaction_obj(var/obj/target)
|
||||
touch_obj(target)
|
||||
|
||||
/datum/reagent/proc/reaction_mob(var/mob/target)
|
||||
touch_mob(target)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
187
tgui/packages/tgui/interfaces/PersonalCrafting.js
Normal file
187
tgui/packages/tgui/interfaces/PersonalCrafting.js
Normal file
@@ -0,0 +1,187 @@
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Button, Dimmer, Flex, Icon, LabeledList, Section, Tabs } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const PersonalCrafting = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
busy,
|
||||
display_craftable_only,
|
||||
display_compact,
|
||||
} = data;
|
||||
const crafting_recipes = data.crafting_recipes || {};
|
||||
// Sort everything into flat categories
|
||||
const categories = [];
|
||||
const recipes = [];
|
||||
for (let category of Object.keys(crafting_recipes)) {
|
||||
const subcategories = crafting_recipes[category];
|
||||
if ('has_subcats' in subcategories) {
|
||||
for (let subcategory of Object.keys(subcategories)) {
|
||||
if (subcategory === 'has_subcats') {
|
||||
continue;
|
||||
}
|
||||
// Push category
|
||||
categories.push({
|
||||
name: subcategory,
|
||||
category,
|
||||
subcategory,
|
||||
});
|
||||
// Push recipes
|
||||
const _recipes = subcategories[subcategory];
|
||||
for (let recipe of _recipes) {
|
||||
recipes.push({
|
||||
...recipe,
|
||||
category: subcategory,
|
||||
});
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// Push category
|
||||
categories.push({
|
||||
name: category,
|
||||
category,
|
||||
});
|
||||
// Push recipes
|
||||
const _recipes = crafting_recipes[category];
|
||||
for (let recipe of _recipes) {
|
||||
recipes.push({
|
||||
...recipe,
|
||||
category,
|
||||
});
|
||||
}
|
||||
}
|
||||
// Sort out the tab state
|
||||
const [tab, setTab] = useLocalState(
|
||||
context, 'tab', categories[0]?.name);
|
||||
const shownRecipes = recipes
|
||||
.filter(recipe => recipe.category === tab);
|
||||
return (
|
||||
<Window
|
||||
title="Crafting Menu"
|
||||
width={700}
|
||||
height={800}>
|
||||
<Window.Content scrollable>
|
||||
{!!busy && (
|
||||
<Dimmer fontSize="32px">
|
||||
<Icon name="cog" spin={1} />
|
||||
{' Crafting...'}
|
||||
</Dimmer>
|
||||
)}
|
||||
<Section
|
||||
title="Personal Crafting"
|
||||
buttons={(
|
||||
<>
|
||||
<Button.Checkbox
|
||||
content="Compact"
|
||||
checked={display_compact}
|
||||
onClick={() => act('toggle_compact')} />
|
||||
<Button.Checkbox
|
||||
content="Craftable Only"
|
||||
checked={display_craftable_only}
|
||||
onClick={() => act('toggle_recipes')} />
|
||||
</>
|
||||
)}>
|
||||
<Flex>
|
||||
<Flex.Item>
|
||||
<Tabs vertical>
|
||||
{categories.map(category => (
|
||||
<Tabs.Tab
|
||||
key={category.name}
|
||||
selected={category.name === tab}
|
||||
onClick={() => {
|
||||
setTab(category.name);
|
||||
act('set_category', {
|
||||
category: category.category,
|
||||
subcategory: category.subcategory,
|
||||
});
|
||||
}}>
|
||||
{category.name}
|
||||
</Tabs.Tab>
|
||||
))}
|
||||
</Tabs>
|
||||
</Flex.Item>
|
||||
<Flex.Item grow={1} basis={0}>
|
||||
<CraftingList craftables={shownRecipes} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const CraftingList = (props, context) => {
|
||||
const {
|
||||
craftables = [],
|
||||
} = props;
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
craftability = {},
|
||||
display_compact,
|
||||
display_craftable_only,
|
||||
} = data;
|
||||
return craftables.map(craftable => {
|
||||
if (display_craftable_only && !craftability[craftable.ref]) {
|
||||
return null;
|
||||
}
|
||||
// Compact display
|
||||
if (display_compact) {
|
||||
return (
|
||||
<LabeledList.Item
|
||||
key={craftable.name}
|
||||
label={craftable.name}
|
||||
className="candystripe"
|
||||
buttons={(
|
||||
<Button
|
||||
icon="cog"
|
||||
content="Craft"
|
||||
disabled={!craftability[craftable.ref]}
|
||||
tooltip={craftable.tool_text && (
|
||||
'Tools needed: ' + craftable.tool_text
|
||||
)}
|
||||
tooltipPosition="left"
|
||||
onClick={() => act('make', {
|
||||
recipe: craftable.ref,
|
||||
})} />
|
||||
)}>
|
||||
{craftable.req_text}
|
||||
</LabeledList.Item>
|
||||
);
|
||||
}
|
||||
// Full display
|
||||
return (
|
||||
<Section
|
||||
key={craftable.name}
|
||||
title={craftable.name}
|
||||
level={2}
|
||||
buttons={(
|
||||
<Button
|
||||
icon="cog"
|
||||
content="Craft"
|
||||
disabled={!craftability[craftable.ref]}
|
||||
onClick={() => act('make', {
|
||||
recipe: craftable.ref,
|
||||
})} />
|
||||
)}>
|
||||
<LabeledList>
|
||||
{!!craftable.req_text && (
|
||||
<LabeledList.Item label="Required">
|
||||
{craftable.req_text}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{!!craftable.catalyst_text && (
|
||||
<LabeledList.Item label="Catalyst">
|
||||
{craftable.catalyst_text}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{!!craftable.tool_text && (
|
||||
<LabeledList.Item label="Tools">
|
||||
{craftable.tool_text}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
});
|
||||
};
|
||||
File diff suppressed because one or more lines are too long
@@ -43,6 +43,7 @@
|
||||
#include "code\__defines\chemistry_vr.dm"
|
||||
#include "code\__defines\color.dm"
|
||||
#include "code\__defines\construction.dm"
|
||||
#include "code\__defines\crafting.dm"
|
||||
#include "code\__defines\damage_organs.dm"
|
||||
#include "code\__defines\dna.dm"
|
||||
#include "code\__defines\exosuit_fab.dm"
|
||||
@@ -96,6 +97,7 @@
|
||||
#include "code\__defines\tgs.config.dm"
|
||||
#include "code\__defines\tgs.dm"
|
||||
#include "code\__defines\tgui.dm"
|
||||
#include "code\__defines\tools.dm"
|
||||
#include "code\__defines\turfs.dm"
|
||||
#include "code\__defines\typeids.dm"
|
||||
#include "code\__defines\unit_tests.dm"
|
||||
@@ -140,6 +142,7 @@
|
||||
#include "code\_helpers\names.dm"
|
||||
#include "code\_helpers\sanitize_values.dm"
|
||||
#include "code\_helpers\storage.dm"
|
||||
#include "code\_helpers\string_lists.dm"
|
||||
#include "code\_helpers\text.dm"
|
||||
#include "code\_helpers\text_vr.dm"
|
||||
#include "code\_helpers\time.dm"
|
||||
@@ -355,6 +358,10 @@
|
||||
#include "code\datums\autolathe\tools_vr.dm"
|
||||
#include "code\datums\components\_component.dm"
|
||||
#include "code\datums\components\resize_guard.dm"
|
||||
#include "code\datums\components\crafting\crafting.dm"
|
||||
#include "code\datums\components\crafting\crafting_external.dm"
|
||||
#include "code\datums\components\crafting\recipes.dm"
|
||||
#include "code\datums\components\crafting\tool_quality.dm"
|
||||
#include "code\datums\elements\_element.dm"
|
||||
#include "code\datums\game_masters\_common.dm"
|
||||
#include "code\datums\helper_datums\construction_datum.dm"
|
||||
|
||||
Reference in New Issue
Block a user