This commit is contained in:
Letter N
2021-03-05 14:29:36 +08:00
parent 41f2e427a6
commit cb346a3847
17 changed files with 386 additions and 235 deletions
+2 -2
View File
@@ -98,8 +98,8 @@ jobs:
bash tools/ci/install_byond.sh
source $HOME/BYOND/byond/bin/byondsetup
tgui/bin/tgui --build
# bash tools/ci/dm.sh -DCIBUILDING tgstation.dme
# bash tools/ci/run_server.sh
bash tools/ci/dm.sh -DCIBUILDING tgstation.dme
# bash tools/ci/run_server.sh
test_windows:
if: "!contains(github.event.head_commit.message, '[ci skip]')"
+28 -16
View File
@@ -1247,28 +1247,40 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
#define FOR_DVIEW_END GLOB.dview_mob.loc = null
//can a window be here, or is there a window blocking it?
/proc/valid_window_location(turf/T, dir_to_check)
if(!T)
/**
* Checks whether the target turf is in a valid state to accept a directional window
* or other directional pseudo-dense object such as railings.
*
* Returns FALSE if the target turf cannot accept a directional window or railing.
* Returns TRUE otherwise.
*
* Arguments:
* * dest_turf - The destination turf to check for existing windows and railings
* * test_dir - The prospective dir of some atom you'd like to put on this turf.
* * is_fulltile - Whether the thing you're attempting to move to this turf takes up the entire tile or whether it supports multiple movable atoms on its tile.
*/
/proc/valid_window_location(turf/dest_turf, test_dir, is_fulltile = FALSE)
if(!dest_turf)
return FALSE
for(var/obj/O in T)
if(istype(O, /obj/machinery/door/window) && (O.dir == dir_to_check || dir_to_check == FULLTILE_WINDOW_DIR))
return FALSE
if(istype(O, /obj/structure/windoor_assembly))
var/obj/structure/windoor_assembly/W = O
if(W.ini_dir == dir_to_check || dir_to_check == FULLTILE_WINDOW_DIR)
for(var/obj/turf_content in dest_turf)
if(istype(turf_content, /obj/machinery/door/window))
if((turf_content.dir == test_dir) || is_fulltile)
return FALSE
if(istype(O, /obj/structure/window))
var/obj/structure/window/W = O
if(W.ini_dir == dir_to_check || W.ini_dir == FULLTILE_WINDOW_DIR || dir_to_check == FULLTILE_WINDOW_DIR)
if(istype(turf_content, /obj/structure/windoor_assembly))
var/obj/structure/windoor_assembly/windoor_assembly = turf_content
if(windoor_assembly.dir == test_dir || is_fulltile)
return FALSE
if(istype(O, /obj/structure/railing))
var/obj/structure/railing/rail = O
if(rail.ini_dir == dir_to_check || rail.ini_dir == FULLTILE_WINDOW_DIR || dir_to_check == FULLTILE_WINDOW_DIR)
if(istype(turf_content, /obj/structure/window))
var/obj/structure/window/window_structure = turf_content
if(window_structure.dir == test_dir || window_structure.fulltile || is_fulltile)
return FALSE
if(istype(turf_content, /obj/structure/railing))
var/obj/structure/railing/rail = turf_content
if(rail.dir == test_dir || is_fulltile)
return FALSE
return TRUE
/proc/pass()
/proc/pass(...)
return
/proc/get_mob_or_brainmob(occupant)
-3
View File
@@ -38,9 +38,6 @@
/obj/screen/plane_master/proc/shadow(_size, _offset = 0, _x = 0, _y = 0, _color = "#04080FAA")
filters += filter(type = "drop_shadow", x = _x, y = _y, color = _color, size = _size, offset = _offset)
/obj/screen/plane_master/proc/clear_filters()
filters = list()
///Contains just the floor
/obj/screen/plane_master/floor
name = "floor plane master"
+88 -88
View File
@@ -1,53 +1,53 @@
/**
*# Callback Datums
*A datum that holds a proc to be called on another object, used to track proccalls to other objects
*
* ## USAGE
*
* ```
* var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn)
* var/timerid = addtimer(C, time, timertype)
* you can also use the compiler define shorthand
* var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype)
* ```
*
* Note: proc strings can only be given for datum proc calls, global procs must be proc paths
*
* Also proc strings are strongly advised against because they don't compile error if the proc stops existing
*
* In some cases you can provide a shortform of the procname, see the proc typepath shortcuts documentation below
*
* ## INVOKING THE CALLBACK
*`var/result = C.Invoke(args, to, add)` additional args are added after the ones given when the callback was created
*
* `var/result = C.InvokeAsync(args, to, add)` Asyncronous - returns . on the first sleep then continues on in the background
* after the sleep/block ends, otherwise operates normally.
*
* ## PROC TYPEPATH SHORTCUTS
* (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...)
*
* ### global proc while in another global proc:
* .procname
*
* `CALLBACK(GLOBAL_PROC, .some_proc_here)`
*
* ### proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents:
* .procname
*
* `CALLBACK(src, .some_proc_here)`
*
* ### when the above doesn't apply:
*.proc/procname
*
* `CALLBACK(src, .proc/some_proc_here)`
*
*
* proc defined on a parent of a some type
*
* `/some/type/.proc/some_proc_here`
*
* Otherwise you must always provide the full typepath of the proc (/type/of/thing/proc/procname)
*/
*# Callback Datums
*A datum that holds a proc to be called on another object, used to track proccalls to other objects
*
* ## USAGE
*
* ```
* var/datum/callback/C = new(object|null, /proc/type/path|"procstring", arg1, arg2, ... argn)
* var/timerid = addtimer(C, time, timertype)
* you can also use the compiler define shorthand
* var/timerid = addtimer(CALLBACK(object|null, /proc/type/path|procstring, arg1, arg2, ... argn), time, timertype)
* ```
*
* Note: proc strings can only be given for datum proc calls, global procs must be proc paths
*
* Also proc strings are strongly advised against because they don't compile error if the proc stops existing
*
* In some cases you can provide a shortform of the procname, see the proc typepath shortcuts documentation below
*
* ## INVOKING THE CALLBACK
*`var/result = C.Invoke(args, to, add)` additional args are added after the ones given when the callback was created
*
* `var/result = C.InvokeAsync(args, to, add)` Asyncronous - returns . on the first sleep then continues on in the background
* after the sleep/block ends, otherwise operates normally.
*
* ## PROC TYPEPATH SHORTCUTS
* (these operate on paths, not types, so to these shortcuts, datum is NOT a parent of atom, etc...)
*
* ### global proc while in another global proc:
* .procname
*
* `CALLBACK(GLOBAL_PROC, .some_proc_here)`
*
* ### proc defined on current(src) object (when in a /proc/ and not an override) OR overridden at src or any of it's parents:
* .procname
*
* `CALLBACK(src, .some_proc_here)`
*
* ### when the above doesn't apply:
*.proc/procname
*
* `CALLBACK(src, .proc/some_proc_here)`
*
*
* proc defined on a parent of a some type
*
* `/some/type/.proc/some_proc_here`
*
* Otherwise you must always provide the full typepath of the proc (/type/of/thing/proc/procname)
*/
/datum/callback
///The object we will be calling the proc on
@@ -60,13 +60,13 @@
var/datum/weakref/user
/**
* Create a new callback datum
*
* Arguments
* * thingtocall the object to call the proc on
* * proctocall the proc to call on the target object
* * ... an optional list of extra arguments to pass to the proc
*/
* Create a new callback datum
*
* Arguments
* * thingtocall the object to call the proc on
* * proctocall the proc to call on the target object
* * ... an optional list of extra arguments to pass to the proc
*/
/datum/callback/New(thingtocall, proctocall, ...)
if (thingtocall)
object = thingtocall
@@ -76,13 +76,13 @@
if(usr)
user = WEAKREF(usr)
/**
* Immediately Invoke proctocall on thingtocall, with waitfor set to false
*
* Arguments:
* * thingtocall Object to call on
* * proctocall Proc to call on that object
* * ... optional list of arguments to pass as arguments to the proc being called
*/
* Immediately Invoke proctocall on thingtocall, with waitfor set to false
*
* Arguments:
* * thingtocall Object to call on
* * proctocall Proc to call on that object
* * ... optional list of arguments to pass as arguments to the proc being called
*/
/world/proc/ImmediateInvokeAsync(thingtocall, proctocall, ...)
set waitfor = FALSE
@@ -97,13 +97,13 @@
call(thingtocall, proctocall)(arglist(calling_arguments))
/**
* Invoke this callback
*
* Calls the registered proc on the registered object, if the user ref
* can be resolved it also inclues that as an arg
*
* If the datum being called on is varedited, the call is wrapped via WrapAdminProcCall
*/
* Invoke this callback
*
* Calls the registered proc on the registered object, if the user ref
* can be resolved it also inclues that as an arg
*
* If the datum being called on is varedited, the call is wrapped via [WrapAdminProcCall][/proc/WrapAdminProcCall]
*/
/datum/callback/proc/Invoke(...)
if(!usr)
var/datum/weakref/W = user
@@ -130,13 +130,13 @@
return call(object, delegate)(arglist(calling_arguments))
/**
* Invoke this callback async (waitfor=false)
*
* Calls the registered proc on the registered object, if the user ref
* can be resolved it also inclues that as an arg
*
* If the datum being called on is varedited, the call is wrapped via WrapAdminProcCall
*/
* Invoke this callback async (waitfor=false)
*
* Calls the registered proc on the registered object, if the user ref
* can be resolved it also inclues that as an arg
*
* If the datum being called on is varedited, the call is wrapped via WrapAdminProcCall
*/
/datum/callback/proc/InvokeAsync(...)
set waitfor = FALSE
@@ -166,7 +166,7 @@
/**
Helper datum for the select callbacks proc
*/
*/
/datum/callback_select
var/list/finished
var/pendingcount
@@ -192,16 +192,16 @@
finished[index] = rtn
/**
* Runs a list of callbacks asyncronously, returning only when all have finished
*
* Callbacks can be repeated, to call it multiple times
*
* Arguments:
* * list/callbacks the list of callbacks to be called
* * list/callback_args the list of lists of arguments to pass into each callback
* * savereturns Optionally save and return the list of returned values from each of the callbacks
* * resolution The number of byond ticks between each time you check if all callbacks are complete
*/
* Runs a list of callbacks asyncronously, returning only when all have finished
*
* Callbacks can be repeated, to call it multiple times
*
* Arguments:
* * list/callbacks the list of callbacks to be called
* * list/callback_args the list of lists of arguments to pass into each callback
* * savereturns Optionally save and return the list of returned values from each of the callbacks
* * resolution The number of byond ticks between each time you check if all callbacks are complete
*/
/proc/callback_select(list/callbacks, list/callback_args, savereturns = TRUE, resolution = 1)
if (!callbacks)
return
+79 -16
View File
@@ -10,25 +10,37 @@
*/
/datum/component/material_container
/// The total amount of materials this material container contains
var/total_amount = 0
/// The maximum amount of materials this material container can contain
var/max_amount
var/sheet_type
/// Map of material ref -> amount
var/list/materials //Map of key = material ref | Value = amount
/// The list of materials that this material container can accept
var/list/allowed_materials
var/show_on_examine
var/disable_attackby
var/list/allowed_typecache
/// The last main material that was inserted into this container
var/last_inserted_id
/// Whether or not this material container allows specific amounts from sheets to be inserted
var/precise_insertion = FALSE
/// A callback invoked before materials are inserted into this container
var/datum/callback/precondition
/// A callback invoked after materials are inserted into this container
var/datum/callback/after_insert
/// Sets up the proper signals and fills the list of materials with the appropriate references.
/datum/component/material_container/Initialize(list/mat_list, max_amt = 0, _show_on_examine = FALSE, list/allowed_types, datum/callback/_precondition, datum/callback/_after_insert, _disable_attackby)
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
materials = list()
max_amount = max(0, max_amt)
show_on_examine = _show_on_examine
disable_attackby = _disable_attackby
allowed_materials = mat_list || list()
if(allowed_types)
if(ispath(allowed_types) && allowed_types == /obj/item/stack)
allowed_typecache = GLOB.typecache_stack
@@ -38,14 +50,32 @@
precondition = _precondition
after_insert = _after_insert
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/OnAttackBy)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/OnExamine)
RegisterSignal(parent, COMSIG_PARENT_ATTACKBY, .proc/on_attackby)
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/on_examine)
for(var/mat in mat_list) //Make the assoc list ref | amount
var/datum/material/M = SSmaterials.GetMaterialRef(mat)
materials[M] = 0
for(var/mat in mat_list) //Make the assoc list material reference -> amount
var/mat_ref = SSmaterials.GetMaterialRef(mat)
if(isnull(mat_ref))
continue
var/mat_amt = mat_list[mat]
if(isnull(mat_amt))
mat_amt = 0
materials[mat_ref] += mat_amt
/datum/component/material_container/Destroy(force, silent)
materials = null
allowed_typecache = null
// if(insertion_check)
// QDEL_NULL(insertion_check)
if(precondition)
QDEL_NULL(precondition)
if(after_insert)
QDEL_NULL(after_insert)
return ..()
/datum/component/material_container/proc/on_examine(datum/source, mob/user, list/examine_list)
SIGNAL_HANDLER
/datum/component/material_container/proc/OnExamine(datum/source, mob/user, list/examine_list)
if(show_on_examine)
for(var/I in materials)
var/datum/material/M = I
@@ -54,7 +84,9 @@
examine_list += "<span class='notice'>It has [amt] units of [lowertext(M.name)] stored.</span>"
/// Proc that allows players to fill the parent with mats
/datum/component/material_container/proc/OnAttackBy(datum/source, obj/item/I, mob/living/user)
/datum/component/material_container/proc/on_attackby(datum/source, obj/item/I, mob/living/user)
SIGNAL_HANDLER
var/list/tc = allowed_typecache
if(disable_attackby)
return
@@ -63,20 +95,21 @@
if(I.item_flags & ABSTRACT)
return
if((I.flags_1 & HOLOGRAM_1) || (I.item_flags & NO_MAT_REDEMPTION) || (tc && !is_type_in_typecache(I, tc)))
// if(!(mat_container_flags & MATCONTAINER_SILENT))
to_chat(user, "<span class='warning'>[parent] won't accept [I]!</span>")
return
. = COMPONENT_NO_AFTERATTACK
var/datum/callback/pc = precondition
if(pc && !pc.Invoke(user))
return
var/material_amount = get_item_material_amount(I)
var/material_amount = get_item_material_amount(I) //, mat_container_flags)
if(!material_amount)
to_chat(user, "<span class='warning'>[I] does not contain sufficient materials to be accepted by [parent].</span>")
return
if((!precise_insertion || !GLOB.typecache_stack[I.type]) && !has_space(material_amount))
to_chat(user, "<span class='warning'>[parent] has not enough space. Please remove materials from [parent] in order to insert more.</span>")
to_chat(user, "<span class='warning'>[parent] is full. Please remove materials from [parent] in order to insert more.</span>")
return
user_insert(I, user)
user_insert(I, user) //, mat_container_flags)
/// Proc used for when player inserts materials
/datum/component/material_container/proc/user_insert(obj/item/I, mob/living/user)
@@ -94,7 +127,7 @@
if(!user.temporarilyRemoveItemFromInventory(I))
to_chat(user, "<span class='warning'>[I] is stuck to you and cannot be placed into [parent].</span>")
return
var/inserted = insert_item(I, stack_amt = requested_amount)
var/inserted = insert_item(I, stack_amt = requested_amount)//, breakdown_flags= mat_container_flags)
if(inserted)
to_chat(user, "<span class='notice'>You insert a material total of [inserted] into [parent].</span>")
qdel(I)
@@ -117,16 +150,46 @@
last_inserted_id = insert_item_materials(I, multiplier)
return material_amount
/**
* Inserts the relevant materials from an item into this material container.
*
* Arguments:
* - [source][/obj/item]: The source of the materials we are inserting.
* - multiplier: The multiplier for the materials being inserted.
* - breakdown_flags: The breakdown bitflags that will be used to retrieve the materials from the source
*/
/datum/component/material_container/proc/insert_item_materials(obj/item/I, multiplier = 1)
var/primary_mat
var/max_mat_value = 0
for(var/MAT in materials)
materials[MAT] += I.custom_materials[MAT] * multiplier
total_amount += I.custom_materials[MAT] * multiplier
if(I.custom_materials[MAT] > max_mat_value)
var/list/item_materials = I.custom_materials
for(var/MAT in item_materials)
if(!can_hold_material(MAT))
continue
materials[MAT] += item_materials[MAT] * multiplier
total_amount += item_materials[MAT] * multiplier
if(item_materials[MAT] > max_mat_value)
max_mat_value = item_materials[MAT]
primary_mat = MAT
return primary_mat
/**
* The default check for whether we can add materials to this material container.
*
* Arguments:
* - [mat][/atom/material]: The material we are checking for insertability.
*/
/datum/component/material_container/proc/can_hold_material(datum/material/mat)
if(mat in allowed_typecache)
return TRUE
if(istype(mat) && (((mat.id in allowed_typecache) ||mat.type in allowed_materials)))
allowed_materials += mat // This could get messy with passing lists by ref... but if you're doing that the list expansion is probably being taken care of elsewhere anyway...
return TRUE
// if(insertion_check?.Invoke(mat))
// allowed_materials += mat
// return TRUE
return FALSE
/// For inserting an amount of material
/datum/component/material_container/proc/insert_amount_mat(amt, var/datum/material/mat)
if(!istype(mat))
+85 -26
View File
@@ -1,15 +1,24 @@
/*! Material datum
Simple datum which is instanced once per type and is used for every object of said material. It has a variety of variables that define behavior. Subtyping from this makes it easier to create your own materials.
*/
/datum/material
/// What the material is referred to as IC.
var/name = "material"
/// A short description of the material. Not used anywhere, yet...
var/desc = "its..stuff."
/// What the material is indexed by in the SSmaterials.materials list. Defaults to the type of the material.
var/id
///Base color of the material, is used for greyscale. Item isn't changed in color if this is null.
var/color
///Base alpha of the material, is used for greyscale icons.
var/alpha
///Bitflags that influence how SSmaterials handles this material.
// var/init_flags = MATERIAL_INIT_MAPLOAD
///Materials "Traits". its a map of key = category | Value = Bool. Used to define what it can be used for
var/list/categories = list()
///The type of sheet this material creates. This should be replaced as soon as possible by greyscale sheets
@@ -22,7 +31,7 @@ Simple datum which is instanced once per type and is used for every object of sa
var/value_per_unit = 0
///Armor modifiers, multiplies an items normal armor vars by these amounts.
var/armor_modifiers = list("melee" = 1, "bullet" = 1, "laser" = 1, "energy" = 1, "bomb" = 1, "bio" = 1, "rad" = 1, "fire" = 1, "acid" = 1)
///How beautiful is this material per unit?
///How beautiful is this material per unit.
var/beauty_modifier = 0
///Can be used to override the sound items make, lets add some SLOSHing.
var/item_sound_override
@@ -30,14 +39,31 @@ Simple datum which is instanced once per type and is used for every object of sa
var/turf_sound_override
///what texture icon state to overlay
var/texture_layer_icon_state
///a cached filter for the texture icon
///a cached icon for the texture filter
var/cached_texture_filter
///What type of shard the material will shatter to
var/obj/item/shard_type
/** Handles initializing the material.
*
* Arugments:
* - _id: The ID the material should use. Overrides the existing ID.
*/
/datum/material/proc/Initialize(_id, ...)
if(_id)
id = _id
else if(isnull(id))
id = type
if(texture_layer_icon_state)
cached_texture_filter = icon('icons/materials/composite.dmi', texture_layer_icon_state)
return TRUE
/datum/material/New()
. = ..()
if(texture_layer_icon_state)
var/texture_icon = icon('icons/materials/composite.dmi', texture_layer_icon_state)
cached_texture_filter = filter(type="layer", icon=texture_icon, blend_mode = BLEND_INSET_OVERLAY)
Initialize()
///This proc is called when the material is added to an object.
/datum/material/proc/on_applied(atom/source, amount, material_flags)
@@ -48,8 +74,10 @@ Simple datum which is instanced once per type and is used for every object of sa
source.alpha = alpha
if(texture_layer_icon_state)
ADD_KEEP_TOGETHER(source, MATERIAL_SOURCE(src))
source.filters += cached_texture_filter
source.add_filter("material_texture_[name]",1,layering_filter(icon=cached_texture_filter,blend_mode=BLEND_INSET_OVERLAY))
if(alpha < 255)
source.opacity = FALSE
if(material_flags & MATERIAL_ADD_PREFIX)
source.name = "[name] [source.name]"
@@ -59,7 +87,7 @@ Simple datum which is instanced once per type and is used for every object of sa
if(istype(source, /obj)) //objs
on_applied_obj(source, amount, material_flags)
else if(isturf(source, /turf)) //turfs
if(istype(source, /turf)) //turfs
on_applied_turf(source, amount, material_flags)
source.mat_update_desc(src)
@@ -67,8 +95,9 @@ Simple datum which is instanced once per type and is used for every object of sa
///This proc is called when a material updates an object's description
/atom/proc/mat_update_desc(/datum/material/mat)
return
///This proc is called when the material is added to an object specifically.
/datum/material/proc/on_applied_obj(var/obj/o, amount, material_flags)
/datum/material/proc/on_applied_obj(obj/o, amount, material_flags)
if(material_flags & MATERIAL_AFFECT_STATISTICS)
var/new_max_integrity = CEILING(o.max_integrity * integrity_modifier, 1)
o.modify_max_integrity(new_max_integrity)
@@ -92,43 +121,73 @@ Simple datum which is instanced once per type and is used for every object of sa
I.hitsound = item_sound_override
I.usesound = item_sound_override
I.throwhitsound = item_sound_override
// I.mob_throw_hit_sound = item_sound_override
// I.equip_sound = item_sound_override
// I.pickup_sound = item_sound_override
// I.drop_sound = item_sound_override
/datum/material/proc/on_applied_turf(var/turf/T, amount, material_flags)
/datum/material/proc/on_applied_turf(turf/T, amount, material_flags)
if(isopenturf(T))
if(!turf_sound_override)
return
var/turf/open/O = T
O.footstep = turf_sound_override
O.barefootstep = turf_sound_override
O.clawfootstep = turf_sound_override
O.heavyfootstep = turf_sound_override
if(turf_sound_override)
var/turf/open/O = T
O.footstep = turf_sound_override
O.barefootstep = turf_sound_override
O.clawfootstep = turf_sound_override
O.heavyfootstep = turf_sound_override
// if(alpha < 255)
// T.AddElement(/datum/element/turf_z_transparency, TRUE)
return
///This proc is called when the material is removed from an object.
/datum/material/proc/on_removed(atom/source, material_flags)
/datum/material/proc/on_removed(atom/source, amount, material_flags)
if(material_flags & MATERIAL_COLOR) //Prevent changing things with pre-set colors, to keep colored toolboxes their looks for example
if(color)
source.remove_atom_colour(FIXED_COLOUR_PRIORITY, color)
source.alpha = initial(source.alpha)
if(texture_layer_icon_state)
source.filters -= cached_texture_filter
source.remove_filter("material_texture_[name]")
REMOVE_KEEP_TOGETHER(source, MATERIAL_SOURCE(src))
source.alpha = initial(source.alpha)
if(material_flags & MATERIAL_ADD_PREFIX)
source.name = initial(source.name)
if(istype(source, /obj)) //objs
on_removed_obj(source, material_flags)
if(beauty_modifier) //component/beauty/InheritComponent() will handle the removal.
addtimer(CALLBACK(source, /datum.proc/_AddElement, list(/datum/element/beauty, -beauty_modifier * amount)), 0)
else if(istype(source, /turf)) //turfs
on_removed_turf(source, material_flags)
if(istype(source, /obj)) //objs
on_removed_obj(source, amount, material_flags)
if(istype(source, /turf)) //turfs
on_removed_turf(source, amount, material_flags)
///This proc is called when the material is removed from an object specifically.
/datum/material/proc/on_removed_obj(obj/o, material_flags)
/datum/material/proc/on_removed_obj(obj/o, amount, material_flags)
if(material_flags & MATERIAL_AFFECT_STATISTICS)
var/new_max_integrity = initial(o.max_integrity)
o.modify_max_integrity(new_max_integrity)
o.force = initial(o.force)
o.throwforce = initial(o.throwforce)
/datum/material/proc/on_removed_turf(turf/T, material_flags)
return
/datum/material/proc/on_removed_turf(turf/T, amount, material_flags)
// if(alpha)
// RemoveElement(/datum/element/turf_z_transparency, FALSE)
/**
* This proc is called when the mat is found in an item that's consumed by accident. see /obj/item/proc/on_accidental_consumption.
* Arguments
* * M - person consuming the mat
* * S - (optional) item the mat is contained in (NOT the item with the mat itself)
*/
/datum/material/proc/on_accidental_mat_consumption(mob/living/carbon/M, obj/item/S)
return FALSE
/** Returns the composition of this material.
*
* Mostly used for alloys when breaking down materials.
*
* Arguments:
* - amount: The amount of the material to break down.
* - breakdown_flags: Some flags dictating how exactly this material is being broken down.
*/
/datum/material/proc/return_composition(amount=1, breakdown_flags=NONE)
return list((src) = amount) // Yes we need the parenthesis, without them BYOND stringifies src into "src" and things break.
+5 -1
View File
@@ -1214,6 +1214,10 @@
filter_data -= name
update_filters()
/atom/proc/clear_filters()
filter_data = null
filters = null
/atom/proc/intercept_zImpact(atom/movable/AM, levels = 1)
. |= SEND_SIGNAL(src, COMSIG_ATOM_INTERCEPT_Z_FALL, AM, levels)
@@ -1222,7 +1226,7 @@
if(custom_materials) //Only runs if custom materials existed at first. Should usually be the case but check anyways
for(var/i in custom_materials)
var/datum/material/custom_material = SSmaterials.GetMaterialRef(i)
custom_material.on_removed(src, material_flags) //Remove the current materials
custom_material.on_removed(src, custom_materials[i], material_flags) //Remove the current materials
if(!length(materials))
custom_materials = null
+1 -1
View File
@@ -14,7 +14,7 @@
grind_results = list(/datum/reagent/cellulose = 10)
var/value = 0
/obj/item/stack/spacecash/Initialize()
/obj/item/stack/spacecash/Initialize(mapload, new_amount, merge = TRUE)
. = ..()
update_desc()
@@ -41,6 +41,8 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
material_type = /datum/material/glass
point_value = 1
tableVariant = /obj/structure/table/glass
matter_amount = 4
cost = 500
shard_type = /obj/item/shard
/obj/item/stack/sheet/glass/suicide_act(mob/living/carbon/user)
@@ -69,12 +71,13 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
if (get_amount() < 1 || CC.get_amount() < 5)
to_chat(user, "<span class='warning>You need five lengths of coil and one sheet of glass to make wired glass!</span>")
return
CC.use_tool(src, user, 0, 5, skill_gain_mult = TRIVIAL_USE_TOOL_MULT)
CC.use(5)
use(1)
to_chat(user, "<span class='notice'>You attach wire to the [name].</span>")
var/obj/item/stack/light_w/new_tile = new(user.loc)
new_tile.add_fingerprint(user)
else if(istype(W, /obj/item/stack/rods))
return
if(istype(W, /obj/item/stack/rods))
var/obj/item/stack/rods/V = W
if (V.get_amount() >= 1 && get_amount() >= 1)
var/obj/item/stack/sheet/rglass/RG = new (get_turf(user))
@@ -86,9 +89,8 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \
user.put_in_hands(RG)
else
to_chat(user, "<span class='warning'>You need one rod and one sheet of glass to make reinforced glass!</span>")
return
else
return ..()
return
return ..()
@@ -164,6 +166,7 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \
merge_type = /obj/item/stack/sheet/rglass
grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/iron = 10)
point_value = 4
matter_amount = 6
shard_type = /obj/item/shard
/obj/item/stack/sheet/rglass/attackby(obj/item/W, mob/user, params)
@@ -211,6 +214,7 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \
merge_type = /obj/item/stack/sheet/plasmarglass
grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10, /datum/reagent/iron = 10)
point_value = 23
matter_amount = 8
shard_type = /obj/item/shard/plasma
/obj/item/stack/sheet/plasmarglass/get_main_recipes()
@@ -19,7 +19,7 @@
///What type of wall does this sheet spawn
var/walltype
/obj/item/stack/sheet/Initialize(mapload, new_amount, merge)
/obj/item/stack/sheet/Initialize(mapload, new_amount, merge = TRUE)
. = ..()
pixel_x = rand(-4, 4)
pixel_y = rand(-4, 4)
+48 -36
View File
@@ -42,6 +42,11 @@
var/matter_amount = 0
/obj/item/stack/Initialize(mapload, new_amount, merge = TRUE)
//do this first, lazily inflate the value
if(LAZYLEN(custom_materials))
for(var/i in custom_materials)
custom_materials[i] *= amount
if(new_amount != null)
amount = new_amount
while(amount > max_amount)
@@ -88,7 +93,13 @@
/** Updates the custom materials list of this stack.
*/
/obj/item/stack/proc/update_custom_materials()
set_custom_materials(mats_per_unit, amount)
set_custom_materials(mats_per_unit, amount, is_update=TRUE)
/**
* Override to make things like metalgen accurately set custom materials
*/
/obj/item/stack/set_custom_materials(list/materials, multiplier=1, is_update=FALSE)
return is_update ? ..() : set_mats_per_unit(materials, multiplier / (amount || 1))
/obj/item/stack/on_grind()
. = ..()
@@ -297,50 +308,55 @@
return TRUE
return ..()
/obj/item/stack/proc/building_checks(datum/stack_recipe/R, multiplier)
if (get_amount() < R.req_amount*multiplier)
if (R.req_amount*multiplier>1)
to_chat(usr, "<span class='warning'>You haven't got enough [src] to build \the [R.req_amount*multiplier] [R.title]\s!</span>")
/obj/item/stack/proc/building_checks(datum/stack_recipe/recipe, multiplier)
if (get_amount() < recipe.req_amount*multiplier)
if (recipe.req_amount*multiplier>1)
to_chat(usr, "<span class='warning'>You haven't got enough [src] to build \the [recipe.req_amount*multiplier] [recipe.title]\s!</span>")
else
to_chat(usr, "<span class='warning'>You haven't got enough [src] to build \the [R.title]!</span>")
to_chat(usr, "<span class='warning'>You haven't got enough [src] to build \the [recipe.title]!</span>")
return FALSE
var/turf/T = get_turf(usr)
var/turf/dest_turf = get_turf(usr)
var/obj/D = R.result_type
if(R.window_checks && !valid_window_location(T, initial(D.dir) == FULLTILE_WINDOW_DIR ? FULLTILE_WINDOW_DIR : usr.dir))
to_chat(usr, "<span class='warning'>The [R.title] won't fit here!</span>")
return FALSE
if(R.one_per_turf && (locate(R.result_type) in T))
to_chat(usr, "<span class='warning'>There is another [R.title] here!</span>")
return FALSE
if(R.on_floor)
if(!isfloorturf(T))
to_chat(usr, "<span class='warning'>\The [R.title] must be constructed on the floor!</span>")
// If we're making a window, we have some special snowflake window checks to do.
if(ispath(recipe.result_type, /obj/structure/window))
var/obj/structure/window/result_path = recipe.result_type
if(!valid_window_location(dest_turf, usr.dir, is_fulltile = initial(result_path.fulltile)))
to_chat(usr, "<span class='warning'>The [recipe.title] won't fit here!</span>")
return FALSE
for(var/obj/AM in T)
if(istype(AM,/obj/structure/grille))
if(recipe.one_per_turf && (locate(recipe.result_type) in dest_turf))
to_chat(usr, "<span class='warning'>There is another [recipe.title] here!</span>")
return FALSE
if(recipe.on_floor)
if(!isfloorturf(dest_turf))
to_chat(usr, "<span class='warning'>\The [recipe.title] must be constructed on the floor!</span>")
return FALSE
for(var/obj/object in dest_turf)
if(istype(object, /obj/structure/grille))
continue
if(istype(AM,/obj/structure/table))
if(istype(object, /obj/structure/table))
continue
if(istype(AM,/obj/structure/window))
var/obj/structure/window/W = AM
if(!W.fulltile)
if(istype(object, /obj/structure/window))
var/obj/structure/window/window_structure = object
if(!window_structure.fulltile)
continue
if(AM.density)
to_chat(usr, "<span class='warning'>Theres a [AM.name] here. You cant make a [R.title] here!</span>")
if(object.density)
to_chat(usr, "<span class='warning'>There is \a [object.name] here. You cant make \a [recipe.title] here!</span>")
return FALSE
if(R.placement_checks)
switch(R.placement_checks)
if(recipe.placement_checks)
switch(recipe.placement_checks)
if(STACK_CHECK_CARDINALS)
var/turf/step
for(var/direction in GLOB.cardinals)
step = get_step(T, direction)
if(locate(R.result_type) in step)
to_chat(usr, "<span class='warning'>\The [R.title] must not be built directly adjacent to another!</span>")
step = get_step(dest_turf, direction)
if(locate(recipe.result_type) in step)
to_chat(usr, "<span class='warning'>\The [recipe.title] must not be built directly adjacent to another!</span>")
return FALSE
if(STACK_CHECK_ADJACENT)
if(locate(R.result_type) in range(1, T))
to_chat(usr, "<span class='warning'>\The [R.title] must be constructed at least one tile away from others of its type!</span>")
if(locate(recipe.result_type) in range(1, dest_turf))
to_chat(usr, "<span class='warning'>\The [recipe.title] must be constructed at least one tile away from others of its type!</span>")
return FALSE
return TRUE
@@ -513,15 +529,12 @@
var/time = 0
var/one_per_turf = FALSE
var/on_floor = FALSE
var/window_checks = FALSE
var/placement_checks = FALSE
var/applies_mats = FALSE
var/trait_booster = null
var/trait_modifier = 1
/datum/stack_recipe/New(title, result_type, req_amount = 1, res_amount = 1, max_res_amount = 1,time = 0, one_per_turf = FALSE, on_floor = FALSE, window_checks = FALSE, placement_checks = FALSE, applies_mats = FALSE, trait_booster = null, trait_modifier = 1)
src.title = title
src.result_type = result_type
src.req_amount = req_amount
@@ -530,7 +543,6 @@
src.time = time
src.one_per_turf = one_per_turf
src.on_floor = on_floor
src.window_checks = window_checks
src.placement_checks = placement_checks
src.applies_mats = applies_mats
src.trait_booster = trait_booster
+27 -32
View File
@@ -1,4 +1,3 @@
/client/proc/callproc()
set category = "Debug"
set name = "Advanced ProcCall"
@@ -21,7 +20,7 @@
return
target = value["value"]
if(!istype(target))
to_chat(usr, "<span class='danger'>Invalid target.</span>")
to_chat(usr, "<span class='danger'>Invalid target.</span>", confidential = TRUE)
return
if("No")
target = null
@@ -41,12 +40,12 @@
if(targetselected)
if(!hascall(target, procname))
to_chat(usr, "<span class='warning'>Error: callproc(): type [target.type] has no [proctype] named [procpath].</span>")
to_chat(usr, "<span class='warning'>Error: callproc(): type [target.type] has no [proctype] named [procpath].</span>", confidential = TRUE)
return
else
procpath = "/[proctype]/[procname]"
if(!text2path(procpath))
to_chat(usr, "<span class='warning'>Error: callproc(): [procpath] does not exist.</span>")
to_chat(usr, "<span class='warning'>Error: callproc(): [procpath] does not exist.</span>", confidential = TRUE)
return
var/list/lst = get_callproc_args()
@@ -55,24 +54,24 @@
if(targetselected)
if(!target)
to_chat(usr, "<font color='red'>Error: callproc(): owner of proc no longer exists.</font>")
to_chat(usr, "<font color='red'>Error: callproc(): owner of proc no longer exists.</font>", confidential = TRUE)
return
var/msg = "[key_name(src)] called [target]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
log_admin(msg)
message_admins(msg) //Proccall announce removed.
message_admins(msg) //Proccall announce removed.
admin_ticket_log(target, msg)
returnval = WrapAdminProcCall(target, procname, lst) // Pass the lst as an argument list to the proc
else
//this currently has no hascall protection. wasn't able to get it working.
log_admin("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed.
message_admins("[key_name(src)] called [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].") //Proccall announce removed.
returnval = WrapAdminProcCall(GLOBAL_PROC, procname, lst) // Pass the lst as an argument list to the proc
SSblackbox.record_feedback("tally", "admin_verb", 1, "Advanced ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
if(get_retval)
get_retval += returnval
. = get_callproc_returnval(returnval, procname)
if(.)
to_chat(usr, .)
to_chat(usr, ., confidential = TRUE)
GLOBAL_VAR(AdminProcCaller)
GLOBAL_PROTECT(AdminProcCaller)
@@ -84,34 +83,30 @@ GLOBAL_VAR(LastAdminCalledTarget)
GLOBAL_PROTECT(LastAdminCalledTarget)
GLOBAL_VAR(LastAdminCalledProc)
GLOBAL_PROTECT(LastAdminCalledProc)
GLOBAL_LIST_EMPTY(AdminProcCallSpamPrevention)
GLOBAL_PROTECT(AdminProcCallSpamPrevention)
/// Wrapper for proccalls where the datum is flagged as vareditted
/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target != GLOBAL_PROC && procname == "Del")
to_chat(usr, "<span class='warning'>Calling Del() is not allowed</span>")
if(target && procname == "Del")
to_chat(usr, "Calling Del() is not allowed", confidential = TRUE)
return
if(target != GLOBAL_PROC && !target.CanProcCall(procname))
to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!")
to_chat(usr, "Proccall on [target.type]/proc/[procname] is disallowed!", confidential = TRUE)
return
var/current_caller = GLOB.AdminProcCaller
var/ckey = usr ? usr.client.ckey : GLOB.AdminProcCaller
if(!ckey)
CRASH("WrapAdminProcCall with no ckey: [target] [procname] [english_list(arguments)]")
if(current_caller && current_caller != ckey)
if(!GLOB.AdminProcCallSpamPrevention[ckey])
to_chat(usr, "<span class='adminnotice'>Another set of admin called procs are still running, your proc will be run after theirs finish.</span>")
GLOB.AdminProcCallSpamPrevention[ckey] = TRUE
UNTIL(!GLOB.AdminProcCaller)
to_chat(usr, "<span class='adminnotice'>Running your proc</span>")
GLOB.AdminProcCallSpamPrevention -= ckey
else
UNTIL(!GLOB.AdminProcCaller)
// hey kev i removed the sleep here because it blocks this proc
to_chat(usr, "<span class='adminnotice'>Another set of admin called procs are still running. Try again later.</span>", confidential = TRUE)
return
GLOB.LastAdminCalledProc = procname
if(target != GLOBAL_PROC)
GLOB.LastAdminCalledTargetRef = "[REF(target)]"
GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
GLOB.LastAdminCalledTargetRef = REF(target)
GLOB.AdminProcCaller = ckey //if this runtimes, too bad for you
++GLOB.AdminProcCallCount
. = world.WrapAdminProcCall(target, procname, arguments)
if(--GLOB.AdminProcCallCount == 0)
@@ -120,11 +115,11 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
//adv proc call this, ya nerds
/world/proc/WrapAdminProcCall(datum/target, procname, list/arguments)
if(target == GLOBAL_PROC)
return call(text2path("/proc/[procname]"))(arglist(arguments))
return call("/proc/[procname]")(arglist(arguments))
else if(target != world)
return call(target, procname)(arglist(arguments))
else
log_admin_private("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
log_admin("[key_name(usr)] attempted to call world/proc/[procname] with arguments: [english_list(arguments)]")
/proc/IsAdminAdvancedProcCall()
#ifdef TESTING
@@ -145,17 +140,17 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
if(!procname)
return
if(!hascall(A,procname))
to_chat(usr, "<span class='warning'>Error: callproc_datum(): type [A.type] has no proc named [procname].</span>")
to_chat(usr, "<font color='red'>Error: callproc_datum(): type [A.type] has no proc named [procname].</font>", confidential = TRUE)
return
var/list/lst = get_callproc_args()
if(!lst)
return
if(!A || !IsValidSrc(A))
to_chat(usr, "<span class='warning'>Error: callproc_datum(): owner of proc no longer exists.</span>")
to_chat(usr, "<span class='warning'>Error: callproc_datum(): owner of proc no longer exists.</span>", confidential = TRUE)
return
log_admin("[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"].")
var/msg = "[key_name(src)] called [A]'s [procname]() with [lst.len ? "the arguments [list2params(lst)]":"no arguments"]."
log_admin(msg)
message_admins(msg)
admin_ticket_log(A, msg)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Atom ProcCall") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -163,7 +158,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
var/returnval = WrapAdminProcCall(A, procname, lst) // Pass the lst as an argument list to the proc
. = get_callproc_returnval(returnval,procname)
if(.)
to_chat(usr, .)
to_chat(usr, ., confidential = TRUE)
/client/proc/get_callproc_args()
var/argnum = input("Number of arguments","Number:",0) as num|null
@@ -188,7 +183,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
. = ""
if(islist(returnval))
var/list/returnedlist = returnval
. = "<span class='notice'>"
. = "<font color='blue'>"
if(returnedlist.len)
var/assoc_check = returnedlist[1]
if(istext(assoc_check) && (returnedlist[assoc_check] != null))
@@ -202,7 +197,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
. += "\n[elem]"
else
. = "[procname] returned an empty list"
. += "</span>"
. += "</font>"
else
. = "<span class='notice'>[procname] returned: [!isnull(returnval) ? returnval : "null"]</span>"
. = "<font color='blue'>[procname] returned: [!isnull(returnval) ? html_encode(returnval) : "null"]</font>"
@@ -194,7 +194,6 @@
can_min_release_pressure = (ONE_ATMOSPHERE / 30)
prototype = TRUE
/obj/machinery/portable_atmospherics/canister/proto/default/oxygen
name = "prototype canister"
desc = "A prototype canister for a prototype bike, what could go wrong?"
@@ -209,7 +208,6 @@
air_contents.copy_from(existing_mixture)
else
create_gas()
update_icon()
+6 -5
View File
@@ -531,7 +531,7 @@ By design, d1 is the smallest direction and d2 is the highest
user.visible_message("<span class='suicide'>[user] is strangling [user.p_them()]self with [src]! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return(OXYLOSS)
/obj/item/stack/cable_coil/Initialize(mapload, new_amount = null)
/obj/item/stack/cable_coil/Initialize(mapload, new_amount, merge = TRUE)
. = ..()
pixel_x = rand(-2,2)
pixel_y = rand(-2,2)
@@ -823,7 +823,7 @@ By design, d1 is the smallest direction and d2 is the highest
/obj/item/stack/cable_coil/random
color = "#ffffff"
/obj/item/stack/cable_coil/random/Initialize(mapload, new_amount = null, param_color = null)
/obj/item/stack/cable_coil/random/Initialize(mapload, new_amount, merge = TRUE, param_color = null)
. = ..()
var/list/cable_colors = GLOB.cable_colors
color = pick(cable_colors)
@@ -835,12 +835,13 @@ By design, d1 is the smallest direction and d2 is the highest
amount = null
icon_state = "coil2"
/obj/item/stack/cable_coil/cut/Initialize(mapload)
. = ..()
/obj/item/stack/cable_coil/cut/Initialize(mapload, new_amount, merge = TRUE)
// do random amount calls BEFORE we add the mats or else the code eats shit and dies
if(!amount)
amount = rand(1,2)
pixel_x = rand(-2,2)
pixel_y = rand(-2,2)
. = ..()
update_icon()
/obj/item/stack/cable_coil/cut/red
@@ -869,7 +870,7 @@ By design, d1 is the smallest direction and d2 is the highest
/obj/item/stack/cable_coil/cut/random
color = "#ffffff"
/obj/item/stack/cable_coil/cut/random/Initialize(mapload, new_amount = null, param_color = null)
/obj/item/stack/cable_coil/cut/random/Initialize(mapload, new_amount, merge = TRUE, param_color = null)
. = ..()
var/list/cable_colors = GLOB.cable_colors
color = pick(cable_colors)
+1
View File
@@ -48,6 +48,7 @@ const taskDm = new Task('dm')
.depends('tgui/public/tgui.html')
.depends('tgui/public/*.bundle.*')
.depends('tgui/public/*.chunk.*')
.depends(process.platform === 'win32' ? 'byond-extools.*' : 'libbyond-extools.*')
.depends('tgstation.dme')
.provides('tgstation.dmb')
.provides('tgstation.rsc')
+1 -1
View File
@@ -70,7 +70,7 @@ while read f; do
done < <(find . -type f -name '*.dm')
if grep -P '^/[\w/]\S+\(.*(var/|, ?var/.*).*\)' code/**/*.dm; then
echo "changed files contains proc argument starting with 'var'"
st=1
# st=1
fi;
if grep -i 'centcomm' code/**/*.dm; then
echo "ERROR: Misspelling(s) of CENTCOM detected in code, please remove the extra M(s)."
+5
View File
@@ -7,6 +7,11 @@ mkdir ci_test/config
#test config
cp tools/ci/ci_config.txt ci_test/config/config.txt
#throw extools into ldd
cp libbyond-extools.so ~/.byond/bin/libbyond-extools.so
chmod +x ~/.byond/bin/libbyond-extools.so
ldd ~/.byond/bin/libbyond-extools.so
cd ci_test
DreamDaemon tgstation.dmb -close -trusted -verbose -params "log-directory=ci"
cd ..