diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm
index 59860116889..ea6a0086c4f 100644
--- a/code/_onclick/cyborg.dm
+++ b/code/_onclick/cyborg.dm
@@ -124,7 +124,15 @@
A.BorgCtrlClick(src)
/mob/living/silicon/robot/AltClickOn(var/atom/A)
- A.BorgAltClick(src)
+ var/doClickAction = 1
+ if (istype(module_active, /obj/item/weapon))
+ var/obj/item/weapon/W = module_active
+ doClickAction = W.alt_attack(A,src)
+
+ if (doClickAction)
+ A.BorgAltClick(src)
+
+
/atom/proc/BorgCtrlShiftClick(var/mob/living/silicon/robot/user) //forward to human click if not overriden
CtrlShiftClick(user)
diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm
index 9f5fe9ea7c7..292e1726af0 100644
--- a/code/game/objects/items/weapons/storage/storage.dm
+++ b/code/game/objects/items/weapons/storage/storage.dm
@@ -346,16 +346,10 @@
if(istype(W, /obj/item/weapon/tray))
var/obj/item/weapon/tray/T = W
- if(T.calc_carry() > 0)
- if(prob(85))
- user << "\red The tray won't fit in [src]."
- return
- else
- W.loc = user.loc
- if ((user.client && user.s_active != src))
- user.client.screen -= W
- W.dropped(user)
- user << "\red God damnit!"
+ if(T.current_weight > 0)
+ T.spill(user)
+ user << "\red Trying to place a loaded tray into [src] was a bad idea."
+ return
W.add_fingerprint(user)
return handle_item_insertion(W)
diff --git a/code/game/objects/items/weapons/trays.dm b/code/game/objects/items/weapons/trays.dm
index c52961eda5a..6a3b720e336 100644
--- a/code/game/objects/items/weapons/trays.dm
+++ b/code/game/objects/items/weapons/trays.dm
@@ -1,6 +1,10 @@
/*
- * Trays - Agouri
+ * Trays - Nanako
*/
+ //Use tray on an item to load it, alt+click on anything to attempt to load all the stuff on the tile
+ //To unload, place on a table, then rightclic > Unload tray. Alternatively, alt+click on the tray to unload it
+ //Tray will spill if thrown, dropped on the floor, or used to hit someone with. Spilling scatters contents
+
/obj/item/weapon/tray
name = "tray"
icon = 'icons/obj/food.dmi'
@@ -14,26 +18,29 @@
flags = CONDUCT
matter = list(DEFAULT_WALL_MATERIAL = 3000)
var/list/carrying = list() // List of things on the tray. - Doohl
- var/max_carry = 10
+ var/max_carry = 20
+ var/current_weight = 0
+
+ var/safedrop = 0//Used to tell when we should or shouldn't spill if the tray is dropped.
+ //Safedrop is set true when throwing, because it will spill on impact. And when placing on a table
+ var/list/valid = list( /obj/item/weapon/reagent_containers,
+ /obj/item/weapon/material/kitchen/utensil,
+ /obj/item/weapon/storage/fancy/cigarettes,
+ /obj/item/clothing/mask/smokable,
+ /obj/item/weapon/storage/box/matches,
+ /obj/item/weapon/flame/match,
+ /obj/item/weapon/material/ashtray)
/obj/item/weapon/tray/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
// Drop all the things. All of them.
- overlays.Cut()
- for(var/obj/item/I in carrying)
- I.loc = M.loc
- carrying.Remove(I)
- if(isturf(I.loc))
- spawn()
- for(var/i = 1, i <= rand(1,2), i++)
- if(I)
- step(I, pick(NORTH,SOUTH,EAST,WEST))
- sleep(rand(2,4))
+ spill(user, M.loc)
+ //Note: Added a robot check to all stun/weaken procs, beccause weakening a robot causes its active modules to bug out
if((CLUMSY in user.mutations) && prob(50)) //What if he's a clown?
M << "\red You accidentally slam yourself with the [src]!"
- M.Weaken(1)
+ if(!istype(M,/mob/living/silicon))M.Weaken(1)
user.take_organ_damage(2)
if(prob(50))
playsound(M, 'sound/items/trayhit1.ogg', 50, 1)
@@ -46,7 +53,7 @@
if(!(user.zone_sel.selecting == ("eyes" || "head"))) //////////////hitting anything else other than the eyes
- if(prob(33))
+ if(prob(33) && !istype(M,/mob/living/silicon))//robots dont bleed
src.add_blood(H)
var/turf/location = H.loc
if (istype(location, /turf/simulated))
@@ -57,7 +64,7 @@
msg_admin_attack("[user.name] ([user.ckey]) used the [src.name] to attack [M.name] ([M.ckey]) (JMP)")
if(prob(15))
- M.Weaken(3)
+ if(!istype(M,/mob/living/silicon)) M.Weaken(3)
M.take_organ_damage(3)
else
M.take_organ_damage(5)
@@ -75,7 +82,7 @@
if(istype(M, /mob/living/carbon/human) && ((H.head && H.head.flags & HEADCOVERSEYES) || (H.wear_mask && H.wear_mask.flags & MASKCOVERSEYES) || (H.glasses && H.glasses.flags & GLASSESCOVERSEYES)))
M << "\red You get slammed in the face with the tray, against your mask!"
- if(prob(33))
+ if(prob(33) && !istype(M,/mob/living/silicon))
src.add_blood(H)
if (H.wear_mask)
H.wear_mask.add_blood(H)
@@ -96,14 +103,14 @@
for(var/mob/O in viewers(M, null))
O.show_message(text("\red [] slams [] with the tray!", user, M), 1)
if(prob(10))
- M.Stun(rand(1,3))
+ if(!istype(M,/mob/living/silicon))M.Stun(rand(1,3))
M.take_organ_damage(3)
return
else
M.take_organ_damage(5)
return
- else //No eye or head protection, tough luck!
+ else if (!istype(M,/mob/living/silicon))//No eye or head protection, tough luck!
M << "\red You get slammed in the face with the tray!"
if(prob(33))
src.add_blood(M)
@@ -133,76 +140,179 @@
/obj/item/weapon/tray/var/cooldown = 0 //shield bash cooldown. based on world.time
/obj/item/weapon/tray/attackby(obj/item/weapon/W as obj, mob/user as mob)
+ if (istype(user,/mob/living/silicon/robot))//safety to stop robots losing their items
+ return
+
+ if (istype(W, /obj/item/weapon/tray))//safety to prevent tray stacking
+ return
+
if(istype(W, /obj/item/weapon/material/kitchen/rollingpin))
if(cooldown < world.time - 25)
user.visible_message("[user] bashes [src] with [W]!")
playsound(user.loc, 'sound/effects/shieldbash.ogg', 50, 1)
cooldown = world.time
+
else
- ..()
+ var/obj/item/I = W
+ attempt_load_item(I, user)
+ //..()
/*
-===============~~~~~================================~~~~~====================
-= =
-= Code for trays carrying things. By Doohl for Doohl erryday Doohl Doohl~ =
-= =
-===============~~~~~================================~~~~~====================
+============~~~~~==============~~~~~=============
+= =
+= Code for trays carrying things. By Nanako.
+= =
+============~~~~~============~~~~~===============
*/
-/obj/item/weapon/tray/proc/calc_carry()
- // calculate the weight of the items on the tray
- var/val = 0 // value to return
- for(var/obj/item/I in carrying)
- if(I.w_class == 1.0)
- val ++
- else if(I.w_class == 2.0)
- val += 3
+//Clicking an item individually loads it. clicking a table places the tray on it safely
+/obj/item/weapon/tray/afterattack(atom/target, mob/user as mob, proximity)
+ if (proximity)
+ if (istype(target, /obj/item))
+ var/obj/item/I = target
+ attempt_load_item(I,user,1)
+
+ if (istype(target,/obj/structure/table))
+ safedrop = 1
+
+
+//Alt+click with the tray in hand attempts to grab everything on the tile
+/obj/item/weapon/tray/alt_attack(var/atom/A, var/mob/user)
+ var/dist
+ var/tile
+ if (istype(A,/turf))
+ dist = get_dist(A,user.loc)
+ tile = A
+ else
+ dist = get_dist(A.loc,user.loc)
+ tile = A.loc
+
+ if (dist == 1)//checking that we're adjacent
+ var/addedSomething = 0
+ for(var/obj/item/I in tile)
+ if (attempt_load_item(I, usr,0))
+ addedSomething++
+ if ( addedSomething == 1)
+ usr.visible_message("\blue [user] loads an item onto their service tray.")
+ else if ( addedSomething )
+ usr.visible_message("\blue [user] loads [addedSomething] items onto their service tray.")
else
- val += 5
+ user << "The tray is full or there's nothing valid here"
+ return 1
+ return 0//This prevents the alt-click from doing any farther actions
+ return 1
- return val
+/obj/item/weapon/tray/AltClick(var/mob/user)
+ unload(user)
-/obj/item/weapon/tray/pickup(mob/user)
- if(!isturf(loc))
+/obj/item/weapon/tray/proc/attempt_load_item(var/obj/item/I, var/mob/user, var/messages = 1)
+ if( I != src && !I.anchored && !istype(I, /obj/item/projectile) )
+ var/match = 0
+ for (var/T in valid)
+ if (istype(I,T))
+ match = 1
+ var/remaining = max_carry - current_weight
+ if (remaining >= I.w_class)
+ load_item(I,user)
+ if (messages)user << "You place [I] on the tray"
+ return 1
+ else
+ if (messages)
+ user << "The tray can't take that much weight"
+ if (!match && messages)user << "That item isn't suitable for a tray"
+ return 0
+
+
+/obj/item/weapon/tray/proc/load_item(var/obj/item/I, var/mob/user)
+ user.remove_from_mob(I)
+ I.loc = src
+ current_weight += I.w_class
+ carrying.Add(I)
+ overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = 30 + I.layer, "pixel_x" = I.pixel_x, "pixel_y" = I.pixel_y)
+ //rand(0, (max_offset_y*2)-3)-(max_offset_y)-3
+
+/obj/item/weapon/tray/verb/unload()
+ set name = "Unload Tray"
+ set category = "Object"
+ set src in view(1)
+
+ if (!istype(loc,/turf))//check that we're not being held by a mob
+ usr << "Place the tray down first!"
return
+ else
+ var/turf/dropspot = loc
- for(var/obj/item/I in loc)
- if( I != src && !I.anchored && !istype(I, /obj/item/clothing/under) && !istype(I, /obj/item/clothing/suit) && !istype(I, /obj/item/projectile) )
- var/add = 0
- if(I.w_class == 1.0)
- add = 1
- else if(I.w_class == 2.0)
- add = 3
+ for(var/obj/item/I in carrying)
+ I.loc = dropspot
+ carrying.Remove(I)
+ overlays.Cut()
+ current_weight = 0
+ usr.visible_message("[usr] unloads the tray.", "You unload the tray.")
+
+/obj/item/weapon/tray/proc/unload_at_loc(var/turf/dropspot = null, var/mob/user)
+ if (!istype(loc,/turf) && !dropspot)//check that we're not being held by a mob
+ usr << "Place the tray down first!"
+ return
+ else
+ if (!dropspot)
+ dropspot = loc
+
+ for(var/obj/item/I in carrying)
+ I.loc = dropspot
+ carrying.Remove(I)
+ overlays.Cut()
+ current_weight = 0
+ usr.visible_message("[usr] unloads the tray.", "You unload the tray.")
+
+
+/obj/item/weapon/tray/proc/spill(var/mob/user = null, var/turf/dropspot = null)
+ //This proc is called when a tray is thrown or dropped on the floor
+ //its also called when a cyborg uses its tray on the floor
+ if (current_weight > 0)//can't spill a tray with nothing on it
+
+ overlays.Cut()
+
+ //First we have to find where the items are being dropped, unless a location has been passed in
+ if (!dropspot)
+ if (istype(src.loc, /mob))//If the tray is still held by a mob
+ dropspot = src.loc.loc
else
- add = 5
- if(calc_carry() + add >= max_carry)
- break
+ dropspot = src.loc
- I.loc = src
- carrying.Add(I)
- overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = 30 + I.layer)
-/obj/item/weapon/tray/dropped(mob/user)
-
- var/mob/living/M
- for(M in src.loc) //to handle hand switching
- return
-
- var/foundtable = 0
- for(var/obj/structure/table/T in loc)
- foundtable = 1
- break
-
- overlays.Cut()
-
- for(var/obj/item/I in carrying)
- I.loc = loc
- carrying.Remove(I)
- if(!foundtable && isturf(loc))
- // if no table, presume that the person just shittily dropped the tray on the ground and made a mess everywhere!
+ for(var/obj/item/I in carrying)
+ I.loc = dropspot
+ carrying.Remove(I)
spawn()
for(var/i = 1, i <= rand(1,2), i++)
if(I)
step(I, pick(NORTH,SOUTH,EAST,WEST))
sleep(rand(2,4))
+ if (user)
+ user.visible_message("\blue [user] spills their tray all over the floor.")
+ else
+ src.visible_message("\blue The tray scatters its contents all over the area.")
+ current_weight = 0
+ if(prob(50))
+ playsound(dropspot, 'sound/items/trayhit1.ogg', 50, 1)
+ else
+ playsound(dropspot, 'sound/items/trayhit2.ogg', 50, 1)
+
+/obj/item/weapon/tray/throw_impact(atom/hit_atom)
+ spill(null, src.loc)
+
+/obj/item/weapon/tray/throw_at(/var/atom/target, var/throw_range, var/throw_speed, /var/mob/user)
+ safedrop = 1//we dont want the tray to spill when thrown, it will spill on impact instead
+ ..()
+
+/obj/item/weapon/tray/dropped(mob/user)
+ spawn(1)//A hack to avoid race conditions. Dropped procs too quickly
+ if (istype(src.loc, /mob))
+ //If this is true, then the tray has just switched hands and is still held by a mob
+ return
+
+ if (!safedrop)
+ spill(user, src.loc)
+
+ safedrop = 0
\ No newline at end of file
diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm
index b62efa1c049..b04c81d7940 100644
--- a/code/game/objects/weapons.dm
+++ b/code/game/objects/weapons.dm
@@ -6,4 +6,11 @@
/obj/item/weapon/Bump(mob/M as mob)
spawn(0)
..()
- return
\ No newline at end of file
+ return
+
+//Called when the user alt-clicks on something with this item in their active hand
+//this function is designed to be overridden by individual weapons
+/obj/item/weapon/proc/alt_attack(var/atom/target, var/mob/user)
+ return 1
+ //A return value of 1 continues on to do the normal alt-click action.
+ //A return value of 0 does not continue, and will not do the alt-click
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index c7b2da7ec3c..d51b2233746 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1444,3 +1444,13 @@
return 0
return 1
+
+/mob/living/carbon/human/AltClickOn(var/atom/A)
+ var/doClickAction = 1
+ if (istype(get_active_hand(), /obj/item/weapon))
+ var/obj/item/weapon/W = get_active_hand()
+ doClickAction = W.alt_attack(A,src)
+
+ if (doClickAction)
+ ..()
+
diff --git a/code/modules/mob/living/silicon/robot/robot_items.dm b/code/modules/mob/living/silicon/robot/robot_items.dm
index 9346dbe5d4c..fbfd6261893 100644
--- a/code/modules/mob/living/silicon/robot/robot_items.dm
+++ b/code/modules/mob/living/silicon/robot/robot_items.dm
@@ -139,54 +139,14 @@
user << "Harvesting \a [target] is not the purpose of this tool. The [src] is for plants being grown."
// A special tray for the service droid. Allow droid to pick up and drop items as if they were using the tray normally
-// Click on table to unload, click on item to load. Otherwise works identically to a tray.
-// Unlike the base item "tray", robotrays ONLY pick up food, drinks and condiments.
+// Click on table to unload, click on item to load. Alt+click to load everything on tile
/obj/item/weapon/tray/robotray
name = "RoboTray"
desc = "An autoloading tray specialized for carrying refreshments."
/obj/item/weapon/tray/robotray/afterattack(atom/target, mob/user as mob, proximity)
- if(!proximity)
- return
- if ( !target )
- return
- // pick up items, mostly copied from base tray pickup proc
- // see code\game\objects\items\weapons\kitchen.dm line 241
- if ( istype(target,/obj/item))
- if ( !isturf(target.loc) ) // Don't load up stuff if it's inside a container or mob!
- return
- var turf/pickup = target.loc
-
- var addedSomething = 0
-
- for(var/obj/item/weapon/reagent_containers/food/I in pickup)
-
-
- if( I != src && !I.anchored && !istype(I, /obj/item/clothing/under) && !istype(I, /obj/item/clothing/suit) && !istype(I, /obj/item/projectile) )
- var/add = 0
- if(I.w_class == 1.0)
- add = 1
- else if(I.w_class == 2.0)
- add = 3
- else
- add = 5
- if(calc_carry() + add >= max_carry)
- break
-
- I.loc = src
- carrying.Add(I)
- overlays += image("icon" = I.icon, "icon_state" = I.icon_state, "layer" = 30 + I.layer)
- addedSomething = 1
- if ( addedSomething )
- user.visible_message("\blue [user] load some items onto their service tray.")
-
- return
-
- // Unloads the tray, copied from base item's proc dropped() and altered
- // see code\game\objects\items\weapons\kitchen.dm line 263
-
- if ( isturf(target) || istype(target,/obj/structure/table) )
+ if (isturf(target) || istype(target,/obj/structure/table) )
var foundtable = istype(target,/obj/structure/table/)
if ( !foundtable ) //it must be a turf!
for(var/obj/structure/table/T in target)
@@ -201,27 +161,12 @@
else // they clicked on a table
dropspot = target.loc
+ if (foundtable)
+ unload_at_loc(dropspot, src)
+ else
+ spill(user,dropspot)
- overlays = null
-
- var droppedSomething = 0
-
- for(var/obj/item/I in carrying)
- I.loc = dropspot
- carrying.Remove(I)
- droppedSomething = 1
- if(!foundtable && isturf(dropspot))
- // if no table, presume that the person just shittily dropped the tray on the ground and made a mess everywhere!
- spawn()
- for(var/i = 1, i <= rand(1,2), i++)
- if(I)
- step(I, pick(NORTH,SOUTH,EAST,WEST))
- sleep(rand(2,4))
- if ( droppedSomething )
- if ( foundtable )
- user.visible_message("\blue [user] unloads their service tray.")
- else
- user.visible_message("\blue [user] drops all the items on their tray.")
+ current_weight = 0
return ..()
diff --git a/html/changelogs/Nanako-PR-394.yml b/html/changelogs/Nanako-PR-394.yml
new file mode 100644
index 00000000000..21b3b11523f
--- /dev/null
+++ b/html/changelogs/Nanako-PR-394.yml
@@ -0,0 +1,40 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Nanako
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Fixed trays. Trays can now be unloaded by placing them down on a table, then either alt+clicking themn, or rightclicking and selecting Unload Tray"
+ - rscadd: "Trays can now load individual items by using it on them, or using the item on the tray, or alt+click to attempt to load everything on the tile"
+ - rscadd: "Trays will now spill their contents when dropped, thrown, or when you try to place it into a container"
+ - tweak: "Trays now only hold specific things: Food/drinks, reagent containers, utensils, and smoking supplies"
+ - tweak: "Tray capacity increased"
\ No newline at end of file