more updates!

This commit is contained in:
Letter N
2021-02-12 17:52:59 +08:00
parent 6286d40082
commit 5db87b9065
9 changed files with 402 additions and 277 deletions

View File

@@ -158,6 +158,10 @@ GLOBAL_LIST_INIT(ai_core_display_screens, list(
else
if(input == "Random")
input = pick(GLOB.ai_core_display_screens - "Random")
if(input == "Portrait")
var/datum/portrait_picker/tgui = new(usr)//create the datum
tgui.ui_interact(usr)//datum has a tgui component, here we open the window
return "ai-portrait" //just take this until they decide
return "ai-[lowertext(input)]"
GLOBAL_LIST_INIT(security_depts_prefs, list(SEC_DEPT_RANDOM, SEC_DEPT_NONE, SEC_DEPT_ENGINEERING, SEC_DEPT_MEDICAL, SEC_DEPT_SCIENCE, SEC_DEPT_SUPPLY))

View File

@@ -1,6 +1,8 @@
/*! How material datums work
Materials are now instanced datums, with an associative list of them being kept in SSmaterials. We only instance the materials once and then re-use these instances for everything.
These materials call on_applied() on whatever item they are applied to, common effects are adding components, changing color and changing description. This allows us to differentiate items based on the material they are made out of.area
*/
SUBSYSTEM_DEF(materials)
@@ -14,12 +16,16 @@ SUBSYSTEM_DEF(materials)
var/list/materialtypes_by_category
///A cache of all material combinations that have been used
var/list/list/material_combos
///List of stackcrafting recipes for materials using rigid materials
///List of stackcrafting recipes for materials using base recipes
var/list/base_stack_recipes = list(
new /datum/stack_recipe("Chair", /obj/structure/chair/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("Toilet", /obj/structure/toilet/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("Sink Frame", /obj/structure/sink/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("Floor tile", /obj/item/stack/tile/material, 1, 4, 20, applies_mats = TRUE),
)
///List of stackcrafting recipes for materials using rigid recipes
var/list/rigid_stack_recipes = list(
new /datum/stack_recipe("chair", /obj/structure/chair/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("toilet", /obj/structure/toilet/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("sink", /obj/structure/sink/greyscale, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
new /datum/stack_recipe("Floor tile", /obj/item/stack/tile/material, 1, 4, 20, applies_mats = TRUE)
// new /datum/stack_recipe("Carving block", /obj/structure/carving_block, 5, one_per_turf = TRUE, on_floor = TRUE, applies_mats = TRUE),
)
///Ran on initialize, populated the materials and materials_by_category dictionaries with their appropiate vars (See these variables for more info)
@@ -29,7 +35,11 @@ SUBSYSTEM_DEF(materials)
materialtypes_by_category = list()
material_combos = list()
for(var/type in subtypesof(/datum/material))
var/datum/material/ref = new type
var/datum/material/ref = type
// if(!(initial(ref.init_flags) & MATERIAL_INIT_MAPLOAD))
// continue // Do not initialize
ref = new ref
materials[type] = ref
for(var/c in ref.categories)
materials_by_category[c] += list(ref)
@@ -40,7 +50,6 @@ SUBSYSTEM_DEF(materials)
InitializeMaterials()
return materials[fakemat] || fakemat
///Returns a list to be used as an object's custom_materials. Lists will be cached and re-used based on the parameters.
/datum/controller/subsystem/materials/proc/FindOrCreateMaterialCombo(list/materials_declaration, multiplier)
if(!material_combos)

View File

@@ -8,15 +8,17 @@
/*
* Stacks
*/
/obj/item/stack
icon = 'icons/obj/stack_objects.dmi'
gender = PLURAL
material_modifier = 0.01
material_modifier = 0.05 //5%, so that a 50 sheet stack has the effect of 5k materials instead of 100k.
max_integrity = 100
var/list/datum/stack_recipe/recipes
var/singular_name
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 = 0 // It's 1 if module is used by a cyborg, and uses its storage
var/is_cyborg = FALSE // It's TRUE if module is used by a cyborg, and uses its storage
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
@@ -25,7 +27,6 @@
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
var/material_type
max_integrity = 100
//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)
@@ -36,16 +37,8 @@
var/absorption_capacity
/// How quickly we lower the blood flow on a cut wound we're bandaging. Expected lifetime of this bandage in ticks is thus absorption_capacity/absorption_rate, or until the cut heals, whichever comes first
var/absorption_rate
/obj/item/stack/on_grind()
for(var/i in 1 to grind_results.len) //This should only call if it's ground, so no need to check if grind_results exists
grind_results[grind_results[i]] *= get_amount() //Gets the key at position i, then the reagent amount of that key, then multiplies it by stack size
/obj/item/stack/grind_requirements()
if(is_cyborg)
to_chat(usr, "<span class='danger'>[src] is electronically synthesized in your chassis and can't be ground up!</span>")
return
return TRUE
/// Amount of matter for RCD
var/matter_amount = 0
/obj/item/stack/Initialize(mapload, new_amount, merge = TRUE)
if(new_amount != null)
@@ -55,16 +48,16 @@
new type(loc, max_amount, FALSE)
if(!merge_type)
merge_type = type
if(custom_materials && custom_materials.len)
mats_per_unit = list()
var/in_process_mat_list = custom_materials.Copy()
for(var/i in custom_materials)
mats_per_unit[SSmaterials.GetMaterialRef(i)] = in_process_mat_list[i]
custom_materials[i] *= amount
if(LAZYLEN(mats_per_unit))
set_mats_per_unit(mats_per_unit, 1)
else if(LAZYLEN(custom_materials))
set_mats_per_unit(custom_materials, amount ? 1/amount : 1)
. = ..()
if(merge)
for(var/obj/item/stack/S in loc)
if(S.merge_type == merge_type)
if(can_merge(S))
INVOKE_ASYNC(src, .proc/merge, S)
var/list/temp_recipes = get_main_recipes()
recipes = temp_recipes.Copy()
@@ -73,12 +66,42 @@
for(var/i in M.categories)
switch(i)
if(MAT_CATEGORY_BASE_RECIPES)
var/list/temp = SSmaterials.base_stack_recipes.Copy()
recipes += temp
if(MAT_CATEGORY_RIGID)
var/list/temp = SSmaterials.rigid_stack_recipes.Copy()
recipes += temp
update_weight()
update_icon()
/** Sets the amount of materials per unit for this stack.
*
* Arguments:
* - [mats][/list]: The value to set the mats per unit to.
* - multiplier: The amount to multiply the mats per unit by. Defaults to 1.
*/
/obj/item/stack/proc/set_mats_per_unit(list/mats, multiplier=1)
mats_per_unit = SSmaterials.FindOrCreateMaterialCombo(mats, multiplier)
update_custom_materials()
/** Updates the custom materials list of this stack.
*/
/obj/item/stack/proc/update_custom_materials()
set_custom_materials(mats_per_unit, amount)
/obj/item/stack/on_grind()
. = ..()
for(var/i in 1 to length(grind_results)) //This should only call if it's ground, so no need to check if grind_results exists
grind_results[grind_results[i]] *= get_amount() //Gets the key at position i, then the reagent amount of that key, then multiplies it by stack size
/obj/item/stack/grind_requirements()
if(is_cyborg)
to_chat(usr, "<span class='warning'>[src] is electronically synthesized in your chassis and can't be ground up!</span>")
return
return TRUE
/obj/item/stack/proc/get_main_recipes()
SHOULD_CALL_PARENT(TRUE)
return list()//empty list
/obj/item/stack/proc/update_weight()
@@ -99,12 +122,6 @@
else
icon_state = "[initial(icon_state)]_3"
/obj/item/stack/Destroy()
if (usr && usr.machine==src)
usr << browse(null, "window=stack")
. = ..()
/obj/item/stack/examine(mob/user)
. = ..()
if (is_cyborg)
@@ -126,151 +143,158 @@
/obj/item/stack/proc/get_amount()
if(is_cyborg)
. = round(source.energy / cost)
. = round(source?.energy / cost)
else
. = (amount)
/obj/item/stack/attack_self(mob/user)
interact(user)
/**
* Builds all recipes in a given recipe list and returns an association list containing them
*
* Arguments:
* * recipe_to_iterate - The list of recipes we are using to build recipes
*/
/obj/item/stack/proc/recursively_build_recipes(list/recipe_to_iterate)
var/list/L = list()
for(var/recipe in recipe_to_iterate)
if(istype(recipe, /datum/stack_recipe_list))
var/datum/stack_recipe_list/R = recipe
L["[R.title]"] = recursively_build_recipes(R.recipes)
if(istype(recipe, /datum/stack_recipe))
var/datum/stack_recipe/R = recipe
L["[R.title]"] = build_recipe(R)
return L
/obj/item/stack/interact(mob/user, sublist)
ui_interact(user, sublist)
/**
* Returns a list of properties of a given recipe
*
* Arguments:
* * R - The stack recipe we are using to get a list of properties
*/
/obj/item/stack/proc/build_recipe(datum/stack_recipe/R)
return list(
"res_amount" = R.res_amount,
"max_res_amount" = R.max_res_amount,
"req_amount" = R.req_amount,
"ref" = "\ref[R]",
)
/obj/item/stack/ui_interact(mob/user, recipes_sublist)
/**
* Checks if the recipe is valid to be used
*
* Arguments:
* * R - The stack recipe we are checking if it is valid
* * recipe_list - The list of recipes we are using to check the given recipe
*/
/obj/item/stack/proc/is_valid_recipe(datum/stack_recipe/R, list/recipe_list)
for(var/S in recipe_list)
if(S == R)
return TRUE
if(istype(S, /datum/stack_recipe_list))
var/datum/stack_recipe_list/L = S
if(is_valid_recipe(R, L.recipes))
return TRUE
return FALSE
/obj/item/stack/ui_state(mob/user)
return GLOB.hands_state
/obj/item/stack/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Stack", name)
ui.open()
/obj/item/stack/ui_data(mob/user)
var/list/data = list()
data["amount"] = get_amount()
return data
/obj/item/stack/ui_static_data(mob/user)
var/list/data = list()
data["recipes"] = recursively_build_recipes(recipes)
return data
/obj/item/stack/ui_act(action, params)
. = ..()
if (!recipes)
if(.)
return
if (!src || get_amount() <= 0)
user << browse(null, "window=stack")
user.set_machine(src) //for correct work of onclose
var/list/recipe_list = recipes
if (recipes_sublist && recipe_list[recipes_sublist] && istype(recipe_list[recipes_sublist], /datum/stack_recipe_list))
var/datum/stack_recipe_list/srl = recipe_list[recipes_sublist]
recipe_list = srl.recipes
var/t1 = "Amount Left: [get_amount()]<br>"
for(var/i in 1 to length(recipe_list))
var/E = recipe_list[i]
if (isnull(E))
t1 += "<hr>"
continue
if (i>1 && !isnull(recipe_list[i-1]))
t1+="<br>"
if (istype(E, /datum/stack_recipe_list))
var/datum/stack_recipe_list/srl = E
t1 += "<a href='?src=[REF(src)];sublist=[i]'>[srl.title]</a>"
if (istype(E, /datum/stack_recipe))
var/datum/stack_recipe/R = E
var/max_multiplier = round(get_amount() / R.req_amount)
var/title
var/can_build = 1
can_build = can_build && (max_multiplier>0)
if (R.res_amount>1)
title+= "[R.res_amount]x [R.title]\s"
else
title+= "[R.title]"
title+= " ([R.req_amount] [singular_name]\s)"
if (can_build)
t1 += text("<A href='?src=[REF(src)];sublist=[recipes_sublist];make=[i];multiplier=1'>[title]</A> ")
else
t1 += text("[]", title)
continue
if (R.max_res_amount>1 && max_multiplier>1)
max_multiplier = min(max_multiplier, round(R.max_res_amount/R.res_amount))
t1 += " |"
var/list/multipliers = list(5,10,25)
for (var/n in multipliers)
if (max_multiplier>=n)
t1 += " <A href='?src=[REF(src)];make=[i];multiplier=[n]'>[n*R.res_amount]x</A>"
if (!(max_multiplier in multipliers))
t1 += " <A href='?src=[REF(src)];make=[i];multiplier=[max_multiplier]'>[max_multiplier*R.res_amount]x</A>"
var/datum/browser/popup = new(user, "stack", name, 400, 400)
popup.set_content(t1)
popup.open(0)
onclose(user, "stack")
/obj/item/stack/Topic(href, href_list)
..()
if (usr.restrained() || usr.stat || usr.get_active_held_item() != src)
return
if (href_list["sublist"] && !href_list["make"])
interact(usr, text2num(href_list["sublist"]))
if (href_list["make"])
if (get_amount() < 1 && !is_cyborg)
qdel(src)
var/list/recipes_list = recipes
if (href_list["sublist"])
var/datum/stack_recipe_list/srl = recipes_list[text2num(href_list["sublist"])]
recipes_list = srl.recipes
var/datum/stack_recipe/R = recipes_list[text2num(href_list["make"])]
var/multiplier = text2num(href_list["multiplier"])
if (!multiplier ||(multiplier <= 0)) //href protection
return
if(!building_checks(R, multiplier))
return
if (R.time)
var/adjusted_time = 0
usr.visible_message("<span class='notice'>[usr] starts building [R.title].</span>", "<span class='notice'>You start building [R.title]...</span>")
if(HAS_TRAIT(usr, R.trait_booster))
adjusted_time = (R.time * R.trait_modifier)
else
adjusted_time = R.time
if (!do_after(usr, adjusted_time, target = usr))
switch(action)
if("make")
if(get_amount() < 1 && !is_cyborg)
qdel(src)
return
var/datum/stack_recipe/R = locate(params["ref"])
if(!is_valid_recipe(R, recipes)) //href exploit protection
return
var/multiplier = text2num(params["multiplier"])
if(!multiplier || (multiplier <= 0)) //href exploit protection
return
if(!building_checks(R, multiplier))
return
if(R.time)
var/adjusted_time = 0
usr.visible_message("<span class='notice'>[usr] starts building \a [R.title].</span>", "<span class='notice'>You start building \a [R.title]...</span>")
if(HAS_TRAIT(usr, R.trait_booster))
adjusted_time = (R.time * R.trait_modifier)
else
adjusted_time = R.time
if(!do_after(usr, adjusted_time, target = usr))
return
if(!building_checks(R, multiplier))
return
var/obj/O
if(R.max_res_amount > 1) //Is it a stack?
O = new R.result_type(usr.drop_location(), R.res_amount * multiplier)
else if(ispath(R.result_type, /turf))
var/turf/T = usr.drop_location()
if(!isturf(T))
return
T.PlaceOnTop(R.result_type, flags = CHANGETURF_INHERIT_AIR)
else
O = new R.result_type(get_turf(usr))
if(O)
O.setDir(usr.dir)
log_craft("[O] crafted by [usr] at [loc_name(O.loc)]")
var/obj/O
if(R.max_res_amount > 1) //Is it a stack?
O = new R.result_type(usr.drop_location(), R.res_amount * multiplier)
else if(ispath(R.result_type, /turf))
var/turf/T = usr.drop_location()
if(!isturf(T))
return
T.PlaceOnTop(R.result_type, flags = CHANGETURF_INHERIT_AIR)
else
O = new R.result_type(usr.drop_location())
if(O)
O.setDir(usr.dir)
use(R.req_amount * multiplier)
use(R.req_amount * multiplier)
if(R.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, R.req_amount / R.res_amount)
else
O.set_custom_materials(mats_per_unit, R.req_amount / R.res_amount)
if(R.applies_mats && custom_materials && custom_materials.len)
var/list/used_materials = list()
for(var/i in custom_materials)
used_materials[SSmaterials.GetMaterialRef(i)] = R.req_amount / R.res_amount * (MINERAL_MATERIAL_AMOUNT / custom_materials.len)
O.set_custom_materials(used_materials)
if(istype(O, /obj/structure/windoor_assembly))
var/obj/structure/windoor_assembly/W = O
W.ini_dir = W.dir
else if(istype(O, /obj/structure/window))
var/obj/structure/window/W = O
W.ini_dir = W.dir
//START: oh fuck i'm so sorry
if(istype(O, /obj/structure/windoor_assembly))
var/obj/structure/windoor_assembly/W = O
W.ini_dir = W.dir
else if(istype(O, /obj/structure/window))
var/obj/structure/window/W = O
W.ini_dir = W.dir
//END: oh fuck i'm so sorry
if(QDELETED(O))
return //It's a stack and has already been merged
else if(istype(O, /obj/item/restraints/handcuffs/cable))
var/obj/item/cuffs = O
cuffs.color = color
if(isitem(O))
usr.put_in_hands(O)
O.add_fingerprint(usr)
if (QDELETED(O))
return //It's a stack and has already been merged
//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 (isitem(O))
usr.put_in_hands(O)
O.add_fingerprint(usr)
//BubbleWrap - so newly formed boxes are empty
if ( istype(O, /obj/item/storage) )
for (var/obj/item/I in O)
qdel(I)
//BubbleWrap END
/obj/item/stack/vv_edit_var(vname, vval)
if(vname == NAMEOF(src, amount))
add(clamp(vval, 1-amount, max_amount - amount)) //there must always be one.
return TRUE
else if(vname == NAMEOF(src, max_amount))
max_amount = max(vval, 1)
add((max_amount < amount) ? (max_amount - amount) : 0) //update icon, weight, ect
return TRUE
return ..()
/obj/item/stack/proc/building_checks(datum/stack_recipe/R, multiplier)
if (get_amount() < R.req_amount*multiplier)
@@ -330,10 +354,7 @@
if(check && zero_amount())
return TRUE
if(length(mats_per_unit))
var/temp_materials = custom_materials.Copy()
for(var/i in mats_per_unit)
temp_materials[i] = mats_per_unit[i] * src.amount
set_custom_materials(temp_materials)
update_custom_materials()
update_icon()
update_weight()
return TRUE
@@ -357,22 +378,36 @@
return source.energy < cost
if(amount < 1)
qdel(src)
return 1
return 0
return TRUE
return FALSE
/obj/item/stack/proc/add(amount)
/** Adds some number of units to this stack.
*
* Arguments:
* - _amount: The number of units to add to this stack.
*/
/obj/item/stack/proc/add(_amount)
if (is_cyborg)
source.add_charge(amount * cost)
source.add_charge(_amount * cost)
else
src.amount += amount
amount += _amount
if(length(mats_per_unit))
var/temp_materials = custom_materials.Copy()
for(var/i in mats_per_unit)
temp_materials[i] = mats_per_unit[i] * src.amount
set_custom_materials(temp_materials)
update_custom_materials()
update_icon()
update_weight()
/** Checks whether this stack can merge itself into another stack.
*
* Arguments:
* - [check][/obj/item/stack]: The stack to check for mergeability.
*/
/obj/item/stack/proc/can_merge(obj/item/stack/check)
if(!istype(check, merge_type))
return FALSE
if(!check.is_cyborg && (mats_per_unit != check.mats_per_unit)) // Cyborg stacks don't have materials. This lets them recycle sheets and floor tiles.
return FALSE
return TRUE
/obj/item/stack/proc/merge(obj/item/stack/S) //Merge src into S, as much as possible
if(QDELETED(S) || QDELETED(src) || S == src) //amusingly this can cause a stack to consume itself, let's not allow that.
return
@@ -388,50 +423,51 @@
S.add(transfer)
return transfer
/obj/item/stack/Crossed(obj/o)
if(istype(o, merge_type) && !o.throwing)
merge(o)
/obj/item/stack/Crossed(atom/movable/crossing)
if(!crossing.throwing && can_merge(crossing))
merge(crossing)
. = ..()
/obj/item/stack/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(istype(AM, merge_type))
merge(AM)
/obj/item/stack/hitby(atom/movable/hitting, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum)
if(can_merge(hitting))
merge(hitting)
. = ..()
/obj/item/stack/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/item/stack/on_attack_hand(mob/user)
if(user.get_inactive_held_item() == src)
if(zero_amount())
return
return change_stack(user,1)
else
. = ..()
return split_stack(user, 1)
/obj/item/stack/AltClick(mob/living/user)
. = ..()
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
if(isturf(loc)) // to prevent people that are alt clicking a tile to see its content from getting undesidered pop ups
return
if(is_cyborg)
if(is_cyborg || !user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, FALSE) || zero_amount()) //, !iscyborg(user)
return
else
if(zero_amount())
return
//get amount from user
var/max = get_amount()
var/stackmaterial = round(input(user,"How many sheets do you wish to take out of this stack? (Maximum [max])") as null|num)
max = get_amount()
stackmaterial = min(max, stackmaterial)
if(stackmaterial == null || stackmaterial <= 0 || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
return TRUE
else
change_stack(user, stackmaterial)
to_chat(user, "<span class='notice'>You take [stackmaterial] sheets out of the stack</span>")
return TRUE
//get amount from user
var/max = get_amount()
var/stackmaterial = round(input(user,"How many sheets do you wish to take out of this stack? (Maximum [max])") as null|num)
max = get_amount()
stackmaterial = min(max, stackmaterial)
if(stackmaterial == null || stackmaterial <= 0 || !user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, FALSE)) //, !iscyborg(user)
return
split_stack(user, stackmaterial)
to_chat(user, "<span class='notice'>You take [stackmaterial] sheets out of the stack.</span>")
/obj/item/stack/proc/change_stack(mob/user, amount)
/** Splits the stack into two stacks.
*
* Arguments:
* - [user][/mob]: The mob splitting the stack.
* - amount: The number of units to split from this stack.
*/
/obj/item/stack/proc/split_stack(mob/user, amount)
if(!use(amount, TRUE, FALSE))
return FALSE
return null
var/obj/item/stack/F = new type(user? user : drop_location(), amount, FALSE)
. = F
F.set_mats_per_unit(mats_per_unit, 1) // Required for greyscale sheets and tiles.
F.copy_evidences(src)
if(user)
if(!user.put_in_hands(F, merge_stacks = FALSE))
@@ -441,7 +477,7 @@
zero_amount()
/obj/item/stack/attackby(obj/item/W, mob/user, params)
if(istype(W, merge_type))
if(can_merge(W))
var/obj/item/stack/S = W
if(merge(S))
to_chat(user, "<span class='notice'>Your [S.name] stack now contains [S.get_amount()] [S.singular_name]\s.</span>")
@@ -449,14 +485,11 @@
. = ..()
/obj/item/stack/proc/copy_evidences(obj/item/stack/from)
if(from.blood_DNA)
blood_DNA = from.blood_DNA.Copy()
if(from.fingerprints)
fingerprints = from.fingerprints.Copy()
if(from.fingerprintshidden)
fingerprintshidden = from.fingerprintshidden.Copy()
if(from.fingerprintslast)
fingerprintslast = from.fingerprintslast
add_blood_DNA(from.return_blood_DNA())
add_fingerprint_list(from.return_fingerprints())
add_hiddenprint_list(from.return_hiddenprints())
fingerprintslast = from.fingerprintslast
//TODO bloody overlay
/obj/item/stack/microwave_act(obj/machinery/microwave/M)
if(istype(M) && M.dirty < 100)

View File

@@ -7,7 +7,9 @@
desc = "Controls a stacking machine... in theory."
density = FALSE
circuit = /obj/item/circuitboard/machine/stacking_unit_console
/// Connected stacking machine
var/obj/machinery/mineral/stacking_machine/machine
/// Direction for which console looks for stacking machine to connect to
var/machinedir = SOUTHEAST
/obj/machinery/mineral/stacking_unit_console/Initialize()
@@ -16,50 +18,53 @@
if (machine)
machine.CONSOLE = src
/obj/machinery/mineral/stacking_unit_console/ui_interact(mob/user)
. = ..()
if(!machine)
to_chat(user, "<span class='notice'>[src] is not linked to a machine!</span>")
return
var/obj/item/stack/sheet/s
var/dat
dat += text("<b>Stacking unit console</b><br><br>")
for(var/O in machine.stack_list)
s = machine.stack_list[O]
if(s.amount > 0)
dat += text("[capitalize(s.name)]: [s.amount] <A href='?src=[REF(src)];release=[s.type]'>Release</A><br>")
dat += text("<br>Stacking: [machine.stack_amt]<br><br>")
user << browse(dat, "window=console_stacking_machine")
/obj/machinery/mineral/stacking_unit_console/multitool_act(mob/living/user, obj/item/I)
if(istype(I, /obj/item/multitool))
var/obj/item/multitool/M = I
M.buffer = src
to_chat(user, "<span class='notice'>You store linkage information in [I]'s buffer.</span>")
return TRUE
/obj/machinery/mineral/stacking_unit_console/Topic(href, href_list)
if(..())
if(!multitool_check_buffer(user, I))
return
usr.set_machine(src)
src.add_fingerprint(usr)
if(href_list["release"])
if(!(text2path(href_list["release"]) in machine.stack_list))
return //someone tried to spawn materials by spoofing hrefs
var/obj/item/stack/sheet/inp = machine.stack_list[text2path(href_list["release"])]
var/obj/item/stack/sheet/out = new inp.type(null, inp.amount)
inp.amount = 0
machine.unload_mineral(out)
var/obj/item/multitool/M = I
M.buffer = src
to_chat(user, "<span class='notice'>You store linkage information in [I]'s buffer.</span>")
return TRUE
src.updateUsrDialog()
return
/obj/machinery/mineral/stacking_unit_console/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "StackingConsole", name)
ui.open()
/obj/machinery/mineral/stacking_unit_console/ui_data(mob/user)
var/list/data = list()
data["machine"] = machine ? TRUE : FALSE
data["stacking_amount"] = null
data["contents"] = list()
if(machine)
data["stacking_amount"] = machine.stack_amt
for(var/stack_type in machine.stack_list)
var/obj/item/stack/sheet/stored_sheet = machine.stack_list[stack_type]
if(stored_sheet.amount <= 0)
continue
data["contents"] += list(list(
"type" = stored_sheet.type,
"name" = capitalize(stored_sheet.name),
"amount" = stored_sheet.amount,
))
return data
/obj/machinery/mineral/stacking_unit_console/ui_act(action, list/params)
. = ..()
if(.)
return
switch(action)
if("release")
var/obj/item/stack/sheet/released_type = text2path(params["type"])
if(!released_type || !(initial(released_type.merge_type) in machine.stack_list))
return //someone tried to spawn materials by spoofing hrefs
var/obj/item/stack/sheet/inp = machine.stack_list[initial(released_type.merge_type)]
var/obj/item/stack/sheet/out = new inp.type(null, inp.amount)
inp.amount = 0
machine.unload_mineral(out)
return TRUE
/**********************Mineral stacking unit**************************/
@@ -108,6 +113,17 @@
/obj/machinery/mineral/stacking_machine/proc/process_sheet(obj/item/stack/sheet/inp)
if(QDELETED(inp))
return
// Dump the sheets to the silo if attached
if(materials.silo && !materials.on_hold())
var/matlist = inp.custom_materials & materials.mat_container.materials
if (length(matlist))
var/inserted = materials.mat_container.insert_item(inp)
materials.silo_log(src, "collected", inserted, "sheets", matlist)
qdel(inp)
return
// No silo attached process to internal storage
var/key = inp.merge_type
var/obj/item/stack/sheet/storage = stack_list[key]
if(!storage) //It's the first of this sheet added
@@ -115,15 +131,6 @@
storage.amount += inp.amount //Stack the sheets
qdel(inp)
if(materials.silo && !materials.on_hold()) //Dump the sheets to the silo
var/matlist = storage.custom_materials & materials.mat_container.materials
if (length(matlist))
var/inserted = materials.mat_container.insert_item(storage)
materials.silo_log(src, "collected", inserted, "sheets", matlist)
if (QDELETED(storage))
stack_list -= key
return
while(storage.amount >= stack_amt) //Get rid of excessive stackage
var/obj/item/stack/sheet/out = new inp.type(null, stack_amt)
unload_mineral(out)

View File

@@ -0,0 +1,78 @@
//Portrait picker! It's a tgui window that lets you look through all the portraits, and choose one as your AI.
//very similar to centcom_podlauncher in terms of how this is coded, so i kept a lot of comments from it
//^ wow! it's the second time i've said this! i'm a real coder now, copying my statement of copying other people's stuff.
#define TAB_LIBRARY 1
#define TAB_SECURE 2
#define TAB_PRIVATE 3
/datum/portrait_picker
var/client/holder //client of whoever is using this datum
/datum/portrait_picker/New(user)//user can either be a client or a mob due to byondcode(tm)
if (istype(user, /client))
var/client/user_client = user
holder = user_client //if its a client, assign it to holder
else
var/mob/user_mob = user
holder = user_mob.client //if its a mob, assign the mob's client to holder
/datum/portrait_picker/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PortraitPicker")
ui.open()
/datum/portrait_picker/ui_close()
qdel(src)
/datum/portrait_picker/ui_state(mob/user)
return GLOB.conscious_state
/datum/portrait_picker/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/simple/portraits/library),
get_asset_datum(/datum/asset/simple/portraits/library_secure),
get_asset_datum(/datum/asset/simple/portraits/library_private)
)
/datum/portrait_picker/ui_data(mob/user)
var/list/data = list()
data["library"] = SSpersistence.paintings["library"] ? SSpersistence.paintings["library"] : 0
data["library_secure"] = SSpersistence.paintings["library_secure"] ? SSpersistence.paintings["library_secure"] : 0
data["library_private"] = SSpersistence.paintings["library_private"] ? SSpersistence.paintings["library_private"] : 0 //i'm gonna regret this, won't i?
return data
/datum/portrait_picker/ui_act(action, params)
. = ..()
if(.)
return
switch(action)
if("select")
var/list/tab2key = list(TAB_LIBRARY = "library", TAB_SECURE = "library_secure", TAB_PRIVATE = "library_private")
var/folder = tab2key[params["tab"]]
var/list/current_list = SSpersistence.paintings[folder]
var/list/chosen_portrait = current_list[params["selected"]]
var/png = "data/paintings/[folder]/[chosen_portrait["md5"]].png"
var/icon/portrait_icon = new(png)
var/mob/living/ai = holder.mob
var/w = portrait_icon.Width()
var/h = portrait_icon.Height()
var/mutable_appearance/MA = mutable_appearance(portrait_icon)
if(w == 23 || h == 23)
to_chat(ai, "<span class='notice'>Small note: 23x23 Portraits are accepted, but they do not fit perfectly inside the display frame.</span>")
MA.pixel_x = 5
MA.pixel_y = 5
else if(w == 24 || h == 24)
to_chat(ai, "<span class='notice'>Portrait Accepted. Enjoy!</span>")
MA.pixel_x = 4
MA.pixel_y = 4
else
to_chat(ai, "<span class='warning'>Sorry, only 23x23 and 24x24 Portraits are accepted.</span>")
return
ai.cut_overlays() //so people can't keep repeatedly select portraits to add stacking overlays
ai.icon_state = "ai-portrait-active"//background
ai.add_overlay(MA)

View File

@@ -11,7 +11,6 @@
#define MODE_WRITING 1
#define MODE_STAMPING 2
/**
* Paper is now using markdown (like in github pull notes) for ALL rendering
* so we do loose a bit of functionality but we gain in easy of use of
@@ -23,9 +22,6 @@
icon = 'icons/obj/bureaucracy.dmi'
icon_state = "paper"
item_state = "paper"
// inhand_icon_state = "paper"
// worn_icon_state = "paper"
// custom_fire_overlay = "paper_onfire_overlay"
throwforce = 0
w_class = WEIGHT_CLASS_TINY
throw_range = 1
@@ -102,8 +98,8 @@
/obj/item/paper/Initialize()
. = ..()
pixel_y = rand(-8, 8)
pixel_x = rand(-9, 9)
pixel_x = base_pixel_x + rand(-9, 9)
pixel_y = base_pixel_y + rand(-8, 8)
update_icon()
/obj/item/paper/update_icon_state()
@@ -193,13 +189,6 @@
user.visible_message(ignition_message)
add_fingerprint(user)
fire_act(I.get_temperature())
//I would have it become a paper plane before the throw, but that would risk runtimes
/obj/item/paper/DoRevenantThrowEffects(atom/target)
sleep(10)
if(HAS_TRAIT(src, TRAIT_SPOOKY_THROW))
return
new /obj/item/paperplane(get_turf(src))
qdel(src)
/obj/item/paper/attackby(obj/item/P, mob/living/user, params)
if(burn_paper_product_attackby_check(P, user))
@@ -253,6 +242,8 @@
/obj/item/paper/ui_data(mob/user)
var/list/data = list()
data["edit_usr"] = "[user]"
var/obj/O = user.get_active_held_item()
if(istype(O, /obj/item/toy/crayon))
var/obj/item/toy/crayon/PEN = O
@@ -291,7 +282,8 @@
return data
/obj/item/paper/ui_act(action, params,datum/tgui/ui)
if(..())
. = ..()
if(.)
return
switch(action)
if("stamp")
@@ -317,7 +309,8 @@
LAZYADD(stamped, stamp_icon_state)
update_static_data(usr,ui)
ui.user.visible_message("<span class='notice'>[ui.user] stamps [src] with [stamp_class]!</span>", "<span class='notice'>You stamp [src] with [stamp_class]!</span>")
var/obj/O = ui.user.get_active_held_item()
ui.user.visible_message("<span class='notice'>[ui.user] stamps [src] with \the [O.name]!</span>", "<span class='notice'>You stamp [src] with \the [O.name]!</span>")
else
to_chat(usr, pick("You try to stamp but you miss!", "There is no where else you can stamp!"))
. = TRUE

View File

@@ -2635,6 +2635,7 @@
#include "code\modules\mob\living\silicon\silicon_movement.dm"
#include "code\modules\mob\living\silicon\ai\ai.dm"
#include "code\modules\mob\living\silicon\ai\ai_defense.dm"
#include "code\modules\mob\living\silicon\ai\ai_portrait_picker.dm"
#include "code\modules\mob\living\silicon\ai\death.dm"
#include "code\modules\mob\living\silicon\ai\examine.dm"
#include "code\modules\mob\living\silicon\ai\laws.dm"

View File

@@ -36,13 +36,13 @@ export const ThermoMachine = (props, context) => {
onClick={() => act('power')} />
)}>
<LabeledList>
<LabeledList.Item label="Setting">
{/* <LabeledList.Item label="Setting">
<Button
icon={data.cooling ? 'cooling' : 'heating'}
content={data.cooling ? 'Cooling' : 'Heating'}
selected={data.cooling}
onClick={() => act('cooling')} />
</LabeledList.Item>
</LabeledList.Item> */}
<LabeledList.Item label="Target Temperature">
<NumberInput
animated

File diff suppressed because one or more lines are too long