mirror of
https://github.com/vgstation-coders/vgstation13.git
synced 2025-12-10 10:21:11 +00:00
606 lines
17 KiB
Plaintext
606 lines
17 KiB
Plaintext
/*
|
|
* NOTE FROM SOMEBODY WHO DID SOME HOUSEKEEPING AROUND THIS ASS-CODE
|
|
* There's absolutely no documentation on most of this so I'd like to write this down to help you get a grasp of how this works and how redundant it all is.
|
|
* Let's assume you live in a horrible horrible universe where nobody coded inventory datums yet. Damn. Now let's say you want your new mob to be able to wear a jumpsuit.
|
|
*
|
|
* First thing the mob needs is a variable for the inventory slot, like var/obj/item/w_uniform = null. Start by the most awful part and modify /obj/item/proc/mob_can_equip()
|
|
* to account for your mob. It has a switch for every slot, and jumpsuits go in under slot_w_uniform, so probably just copypaste how monkeys do it.
|
|
*
|
|
* Good, now the item thinks the mob can equip it. Now the mob has to be ACTUALLY be able to equip it. Go and modify or create mob/your_mob/equip_to_slot(). Again, it's a switch of
|
|
* all slots. You'll see that that proc also handles the icon refreshing, so you have to create mob/your_mob/update_inv_w_uniform() as well. Good luck there because iconcode is also ass.
|
|
*
|
|
* You can't do anything with the jumpsuit yet though, you need to add it to mob/your_mob/get_item_by_slot() for most things to work.
|
|
* Small detail: Your mob cannot unequip their jumpsuit yet. You need to add it to mob/your_mob/u_equip()
|
|
* Oh yeah, you need to add the new variable for the jumpsuit to mob/your_mob/get_all_slots(). I know, I know, this is why we need inventory datums.
|
|
*
|
|
* Okay, so your mob still has no HUD element for their inventory slot. That takes a /obj/abstract/screen/inventory... beats me how most of it works, but you do need to have it point to
|
|
* slot_id = slot_w_uniform and define a new screen_loc for it. I would advise you to just look at code/_onclick/hud/monkey.dm and do the ol' monkey see, monkey do.
|
|
* When you click on that obj/abstract/screen/inventory, it calls attack_ui() which by default will just call equip_to_slot_if_possible(item, slot_id)
|
|
* At this point I think you need to handle some UI shitcode in mob/your_mob/update_inv_w_uniform(), something like w_uniform.screen_loc = the_screen_loc_you_defined_earlier
|
|
*
|
|
* For other players to be able to strip and forcibly put on the uniform on your mob, you need to give it a fancy mob/your_mob/show_inv(), and put HREF links that you'll pick up
|
|
* in mob/your_mob/Topic() that finally point to handle_strip_slot(). If you search for (href_list["item"]) I'm sure you'll be able to figure this out.
|
|
*
|
|
* Good luck.
|
|
*
|
|
* So, a list of all methods used:
|
|
* /obj/item/proc/mob_can_equip() -> mob/equip_to_slot() -> update_inv_[slot]()
|
|
* get_item_by_slot(), u_equip(), get_all_slots()
|
|
* obj/abstract/screen/inventory -> ??? -> attack_ui()? -> equip_to_slot_if_possible() -> ??????? -> update_inv_[slot]()?
|
|
* show_inv() -> Topic() -> handle_strip_slot()
|
|
*/
|
|
|
|
//These procs handle putting stuff in your hand. It's probably best to use these rather than setting l_hand = ...etc
|
|
//as they handle all relevant stuff like adding it to the player's screen and updating their overlays.
|
|
|
|
//Returns the thing in our active hand
|
|
/mob/proc/get_held_item_by_index(index)
|
|
if(!is_valid_hand_index(index))
|
|
return null
|
|
|
|
return held_items[index]
|
|
|
|
/mob/proc/find_held_item_by_type(type) //Returns the list index
|
|
if(!held_items.len)
|
|
return 0
|
|
|
|
for(var/i = 1 to held_items.len)
|
|
if(istype(held_items[i], type))
|
|
return i
|
|
|
|
return 0
|
|
|
|
/mob/proc/is_holding_item(item)
|
|
return held_items.Find(item)
|
|
|
|
/mob/proc/find_empty_hand_index()
|
|
for(var/i = 1 to held_items.len)
|
|
if(!held_items[i])
|
|
return i
|
|
|
|
return 0
|
|
|
|
/mob/proc/empty_hand_indexes_amount()
|
|
. = 0
|
|
|
|
for(var/i = 1 to held_items.len) //Go through all hand slots, increase return value by 1 for each empty slot
|
|
if(!held_items[i])
|
|
.++
|
|
|
|
/mob/proc/get_active_hand()
|
|
return get_held_item_by_index(active_hand)
|
|
|
|
/mob/proc/get_held_item_ui_location(index,var/obj/item/W=null)
|
|
if(!is_valid_hand_index(index))
|
|
return
|
|
|
|
var/x_offset = -(index % 2) //Index is 1 -> one unit to the left
|
|
var/y_offset = round((index-1) / 2) //Two slots per row, then go higher. Rounded down
|
|
|
|
var/x_pixel_offset = 0
|
|
var/y_pixel_offset = 0
|
|
if (W)
|
|
x_pixel_offset = initial(W.pixel_x)
|
|
y_pixel_offset = initial(W.pixel_y)
|
|
|
|
return "CENTER[x_offset ? x_offset : ""]:[WORLD_ICON_SIZE/2+x_pixel_offset],SOUTH[y_offset ? "+[y_offset]" : ""]:[5*PIXEL_MULTIPLIER+y_pixel_offset]"
|
|
|
|
/*
|
|
switch(index)
|
|
if(1)
|
|
return "CENTER-1:16,SOUTH:5"
|
|
if(2)
|
|
return "CENTER:16,SOUTH:5"
|
|
if(3)
|
|
return "CENTER-1:16,SOUTH+1:5"
|
|
if(4)
|
|
return "CENTER:16,SOUTH+1:5"
|
|
*/
|
|
|
|
/mob/proc/get_direction_by_index(index)
|
|
if(index % 2 == GRASP_RIGHT_HAND)
|
|
return "right_hand"
|
|
else
|
|
return "left_hand"
|
|
|
|
/mob/proc/get_index_limb_name(var/index)
|
|
if(!index)
|
|
index = active_hand
|
|
|
|
switch(index)
|
|
if(GRASP_LEFT_HAND)
|
|
return "left hand"
|
|
if(GRASP_RIGHT_HAND)
|
|
return "right hand"
|
|
else
|
|
return "hand"
|
|
|
|
/mob/proc/get_item_offset_by_index(index) //Return a list with x and y offsets depending on index. Example: list("x"=5, "y"=4)
|
|
return list()
|
|
|
|
// Get the organ of the active hand
|
|
/mob/proc/get_active_hand_organ()
|
|
if(!istype(src, /mob/living/carbon))
|
|
return
|
|
if (hasorgans(src))
|
|
var/datum/organ/external/temp = find_organ_by_grasp_index(active_hand)
|
|
return temp
|
|
|
|
/mob/proc/find_organ_by_grasp_index(index)
|
|
return
|
|
|
|
//Returns the thing in our inactive hand
|
|
/mob/proc/get_inactive_hand()
|
|
return get_held_item_by_index(get_inactive_hand_index())
|
|
|
|
// Because there's several different places it's stored.
|
|
/mob/proc/get_multitool(var/if_active=0)
|
|
return null
|
|
|
|
/mob/proc/get_inactive_hand_index()
|
|
var/new_index = active_hand - 1
|
|
|
|
if(new_index < 1)
|
|
new_index = held_items.len
|
|
|
|
return new_index
|
|
|
|
/mob/proc/swap_hand()
|
|
if(++active_hand > held_items.len)
|
|
active_hand = 1
|
|
|
|
if(!hud_used)
|
|
return
|
|
|
|
for(var/obj/abstract/screen/inventory/hand_hud_object in hud_used.hand_hud_objects)
|
|
if(active_hand == hand_hud_object.hand_index)
|
|
hand_hud_object.icon_state = "hand_active"
|
|
else
|
|
hand_hud_object.icon_state = "hand_inactive"
|
|
|
|
return
|
|
|
|
/mob/proc/activate_hand(var/selhand)
|
|
active_hand = selhand
|
|
|
|
if(!hud_used)
|
|
return
|
|
|
|
for(var/obj/abstract/screen/inventory/hand_hud_object in hud_used.hand_hud_objects)
|
|
if(active_hand == hand_hud_object.hand_index)
|
|
hand_hud_object.icon_state = "hand_active"
|
|
else
|
|
hand_hud_object.icon_state = "hand_inactive"
|
|
|
|
/mob/proc/put_in_hand(index, obj/item/W)
|
|
if(!is_valid_hand_index(index) || !is_valid_hand_index(active_hand))
|
|
return 0
|
|
|
|
if(!put_in_hand_check(W, index))
|
|
return 0
|
|
|
|
if(W.prepickup(src))
|
|
return 0
|
|
|
|
W.forceMove(src)
|
|
held_items[index] = W
|
|
W.hud_layerise()
|
|
W.pixel_x = initial(W.pixel_x)
|
|
W.pixel_y = initial(W.pixel_y)
|
|
W.equipped(src,null,index)
|
|
|
|
if(client)
|
|
client.screen |= W
|
|
if(pulling == W)
|
|
stop_pulling()
|
|
|
|
update_inv_hand(index)
|
|
W.pickup(src)
|
|
return 1
|
|
|
|
//Puts the item into your left hand if possible and calls all necessary triggers/updates. returns 1 on success.
|
|
/mob/proc/put_in_l_hand(var/obj/item/W)
|
|
return put_in_hand(GRASP_LEFT_HAND, W)
|
|
|
|
//Puts the item into your right hand if possible and calls all necessary triggers/updates. returns 1 on success.
|
|
/mob/proc/put_in_r_hand(var/obj/item/W)
|
|
return put_in_hand(GRASP_RIGHT_HAND, W)
|
|
|
|
/mob/proc/put_in_hand_check(var/obj/item/W, index)
|
|
if(lying) //&& !(W.flags & ABSTRACT))
|
|
return 0
|
|
if(!isitem(W))
|
|
return 0
|
|
|
|
if(held_items[index])
|
|
return 0
|
|
|
|
if(W.flags & MUSTTWOHAND)
|
|
if(!W.wield(src, 1))
|
|
to_chat(src, "You need both hands to pick up \the [W].")
|
|
return 0
|
|
|
|
if(W.cant_drop) //if the item can't be dropped
|
|
var/I = is_holding_item(W) //AND the item is currently being held in one of the mob's hands
|
|
if(I)
|
|
to_chat(src, "You can't pry \the [W] out of your [get_index_limb_name(I)]!")
|
|
return 0
|
|
|
|
return 1
|
|
|
|
//Puts the item into our active hand if possible. returns 1 on success.
|
|
/mob/proc/put_in_active_hand(var/obj/item/W)
|
|
return put_in_hand(active_hand, W)
|
|
|
|
//Puts the item into our inactive hand if possible. returns 1 on success.
|
|
/mob/proc/put_in_inactive_hand(var/obj/item/W) // The technology has advanced, we can now handle lifeforms with more than 2 hands.
|
|
for (var/i = held_items.len; i > 0; i--) // held_items.len returns us to the last available hand. We iterate until we come to 0.
|
|
if (i != active_hand && put_in_hand(i, W))
|
|
return 1
|
|
return 0
|
|
|
|
//Puts the item our active hand if possible. Failing that it tries our inactive hand. Returns 1 on success.
|
|
//If both fail it drops it on the floor and returns 0.
|
|
//This is probably the main one you need to know :)
|
|
/mob/proc/put_in_hands(var/obj/item/W)
|
|
if(!W)
|
|
return 0
|
|
for (var/i = 1 to held_items.len)
|
|
if (held_items[i] == W)
|
|
return 0 // If it's already in your hands and you move it, it's in a superposition and breaks everything.
|
|
if(put_in_active_hand(W))
|
|
return 1
|
|
else if(put_in_inactive_hand(W))
|
|
return 1
|
|
else
|
|
W.forceMove(get_turf(src))
|
|
W.reset_plane_and_layer()
|
|
W.dropped()
|
|
return 0
|
|
|
|
/mob/proc/set_hand_amount(new_amount)
|
|
if(new_amount < held_items.len) //Decrease hand amount - drop items held in hands which will no longer exist!
|
|
for(var/i = (new_amount+1) to held_items.len)
|
|
var/obj/item/I = held_items[i]
|
|
|
|
if(I)
|
|
drop_item(I, force_drop = 1)
|
|
if(new_amount < active_hand)
|
|
active_hand = new_amount //Don't update the HUD - it'll be redrawn anyways
|
|
|
|
held_items.len = new_amount
|
|
|
|
if(hud_used)
|
|
hud_used.update_hand_icons()
|
|
|
|
/mob/proc/drop_item_v() //this is dumb.
|
|
if(stat == CONSCIOUS && isturf(loc))
|
|
return drop_item()
|
|
return 0
|
|
|
|
|
|
/mob/proc/drop_from_inventory(var/obj/item/W) //I'm fairly sure the entirety of this proc is redundant and can be replaced by just u_equip(W,1)
|
|
if(W)
|
|
if(client)
|
|
client.screen -= W
|
|
u_equip(W,1)
|
|
if(!W)
|
|
return 1 // self destroying objects (tk, grabs)
|
|
W.reset_plane_and_layer()
|
|
W.forceMove(loc)
|
|
|
|
//W.dropped(src)
|
|
//update_icons() // Redundant as u_equip will handle updating the specific overlay
|
|
return 1
|
|
|
|
|
|
// Drops all and only equipped items, including items in hand
|
|
/mob/proc/drop_all()
|
|
for (var/obj/item/I in get_all_slots())
|
|
drop_from_inventory(I)
|
|
drop_hands()
|
|
|
|
// deletes all and only equipped items, including items in hand
|
|
/mob/proc/delete_all_equipped_items()
|
|
for (var/obj/item/I in get_all_slots())
|
|
qdel(I)
|
|
delete_held_items()
|
|
|
|
//Drops the item in our hand - you can specify an item and a location to drop to
|
|
|
|
/mob/proc/drop_item(var/obj/item/to_drop, var/atom/Target, force_drop = 0) //Set force_drop to 1 to force the item to drop (even if it can't be dropped normally)
|
|
|
|
if(!candrop) //can't drop items while etheral
|
|
return 0
|
|
|
|
if(!to_drop) //if we're not told to drop something specific
|
|
to_drop = get_active_hand() //drop what we're currently holding
|
|
|
|
if(!istype(to_drop)) //still nothing to drop?
|
|
return 0 //bail
|
|
|
|
if((to_drop.cant_drop > 0) && !force_drop)
|
|
return 0
|
|
|
|
if(!Target)
|
|
Target = src.loc
|
|
|
|
remove_from_mob(to_drop) //clean out any refs
|
|
|
|
if(!to_drop)
|
|
return 0
|
|
|
|
to_drop.forceMove(Target) //calls the Entered procs
|
|
if(ismob(Target))
|
|
var/mob/M = Target
|
|
if(iscarbon(M))
|
|
var/mob/living/carbon/C = M
|
|
C.stomach_contents.Add(to_drop)
|
|
|
|
to_drop.dropped(src)
|
|
|
|
if(to_drop && to_drop.loc)
|
|
return 1
|
|
return 0
|
|
|
|
/mob/proc/drop_hands(var/atom/Target, force_drop = 0) //drops both items
|
|
for(var/obj/item/I in held_items)
|
|
drop_item(I, Target, force_drop = force_drop)
|
|
|
|
/mob/proc/delete_held_items(var/atom/Target) //deletes both items
|
|
for(var/obj/item/I in held_items)
|
|
qdel(I)
|
|
|
|
//TODO: phase out this proc
|
|
/mob/proc/before_take_item(var/obj/item/W) //TODO: what is this?
|
|
W.forceMove(null)
|
|
W.reset_plane_and_layer()
|
|
u_equip(W,0)
|
|
update_icons()
|
|
|
|
|
|
/mob/proc/u_equip(var/obj/item/W as obj, dropped = 1, var/slot = null)
|
|
if(!W)
|
|
return 0
|
|
var/success = 0
|
|
|
|
var/index = is_holding_item(W)
|
|
if(index)
|
|
held_items[index] = null
|
|
success = 1
|
|
update_inv_hand(index)
|
|
else if (W == back)
|
|
back = null
|
|
success = 1
|
|
update_inv_back()
|
|
else if (W == wear_mask)
|
|
wear_mask = null
|
|
success = 1
|
|
update_inv_wear_mask()
|
|
else
|
|
return 0
|
|
|
|
if(success)
|
|
if(client)
|
|
client.screen -= W
|
|
if(dropped)
|
|
W.forceMove(loc)
|
|
W.dropped(src)
|
|
if(W)
|
|
W.reset_plane_and_layer()
|
|
return 1
|
|
|
|
|
|
//Attemps to remove an object on a mob. Will not move it to another area or such, just removes from the mob.
|
|
/mob/proc/remove_from_mob(var/obj/O)
|
|
src.u_equip(O,1)
|
|
if (src.client)
|
|
src.client.screen -= O
|
|
if(!O)
|
|
return
|
|
O.reset_plane_and_layer()
|
|
O.screen_loc = null
|
|
return 1
|
|
|
|
/mob/proc/get_all_slots()
|
|
return list(wear_mask, back) + held_items
|
|
|
|
//everything on the mob that it isn't holding
|
|
/mob/proc/get_equipped_items()
|
|
var/list/equipped = get_all_slots()
|
|
equipped -= list(get_active_hand(), get_inactive_hand())
|
|
return equipped
|
|
|
|
//everything on the mob that is not in its pockets, hands and belt.
|
|
/mob/proc/get_clothing_items()
|
|
var/list/equipped = get_all_slots()
|
|
equipped -= list(get_active_hand(), get_inactive_hand())
|
|
return equipped
|
|
|
|
//inventory slots which would be hidden by other clothing items. currently only used for humans because fuck you
|
|
/mob/proc/check_obscured_slots()
|
|
return null
|
|
|
|
//currently only used for humans because fuck you
|
|
/mob/proc/has_organ(name)
|
|
return TRUE
|
|
|
|
//currently only used for humans because fuck you
|
|
/mob/proc/has_organ_for_slot(slot)
|
|
return TRUE
|
|
|
|
//Returns true if mob is wearing the item in any of the inventory slots (NOT hands). If slot is specified, only checks if the item is in that slot
|
|
//Item can be either an object or a path. In the second case, the proc checks if there is a worn item of that type (in that slot), and returns the item
|
|
/mob/proc/is_wearing_item(obj/item/I, slot = null)
|
|
if(slot)
|
|
if(ispath(I))
|
|
var/obj/item/item = get_item_by_slot(slot)
|
|
if(istype(item, I))
|
|
return item
|
|
return (get_item_by_slot(slot) == I)
|
|
else
|
|
if(ispath(I))
|
|
return (locate(I) in get_equipped_items())
|
|
return (I in get_equipped_items())
|
|
|
|
//Same as above, but checks for any item type in the list. Try to use a slot with large lists or it could end up fairly costly.
|
|
/mob/proc/is_wearing_any(list/item_types, slot = null)
|
|
if(slot)
|
|
for(var/element in item_types)
|
|
if(ispath(element))
|
|
var/obj/item/item = get_item_by_slot(slot)
|
|
if(istype(item, element))
|
|
return item
|
|
else
|
|
for(var/element in item_types)
|
|
if(ispath(element))
|
|
var/obj/item/I = locate(element) in get_equipped_items()
|
|
if(I)
|
|
return I
|
|
|
|
/mob/living/carbon/human/proc/equip_if_possible(obj/item/W, slot, act_on_fail = EQUIP_FAILACTION_DELETE) // since byond doesn't seem to have pointers, this seems like the best way to do this :/
|
|
//warning: icky code
|
|
var/equipped = 0
|
|
switch(slot)
|
|
if(slot_back)
|
|
if(!src.back)
|
|
src.back = W
|
|
equipped = 1
|
|
if(slot_wear_mask)
|
|
if(!src.wear_mask)
|
|
src.wear_mask = W
|
|
equipped = 1
|
|
if(slot_handcuffed)
|
|
if(!src.handcuffed)
|
|
src.handcuffed = W
|
|
equipped = 1
|
|
if (!src.mutual_handcuffs)
|
|
src.mutual_handcuffs = W
|
|
equipped = 1
|
|
if(slot_belt)
|
|
if(!src.belt && src.w_uniform)
|
|
src.belt = W
|
|
equipped = 1
|
|
if(slot_wear_id)
|
|
if(!src.wear_id && src.w_uniform)
|
|
src.wear_id = W
|
|
equipped = 1
|
|
if(slot_ears)
|
|
if(!src.ears)
|
|
src.ears = W
|
|
equipped = 1
|
|
if(slot_glasses)
|
|
if(!src.glasses)
|
|
src.glasses = W
|
|
equipped = 1
|
|
if(slot_gloves)
|
|
if(!src.gloves)
|
|
src.gloves = W
|
|
equipped = 1
|
|
if(slot_head)
|
|
if(!src.head)
|
|
src.head = W
|
|
equipped = 1
|
|
if(slot_shoes)
|
|
if(!src.shoes)
|
|
src.shoes = W
|
|
equipped = 1
|
|
if(slot_wear_suit)
|
|
if(!src.wear_suit)
|
|
src.wear_suit = W
|
|
equipped = 1
|
|
if(slot_w_uniform)
|
|
if(!src.w_uniform)
|
|
src.w_uniform = W
|
|
equipped = 1
|
|
if(slot_l_store)
|
|
if(!src.l_store && src.w_uniform)
|
|
src.l_store = W
|
|
equipped = 1
|
|
if(slot_r_store)
|
|
if(!src.r_store && src.w_uniform)
|
|
src.r_store = W
|
|
equipped = 1
|
|
if(slot_s_store)
|
|
if(!src.s_store && src.wear_suit)
|
|
src.s_store = W
|
|
equipped = 1
|
|
if(slot_in_backpack)
|
|
if (src.back && istype(src.back, /obj/item/weapon/storage/backpack))
|
|
var/obj/item/weapon/storage/backpack/B = src.back
|
|
if(B.contents.len < B.storage_slots && W.w_class <= B.fits_max_w_class)
|
|
W.forceMove(B)
|
|
equipped = 1
|
|
|
|
if(equipped)
|
|
W.hud_layerise()
|
|
if(src.back && W.loc != src.back)
|
|
W.forceMove(src)
|
|
else
|
|
switch(act_on_fail)
|
|
if(EQUIP_FAILACTION_DELETE)
|
|
qdel(W)
|
|
W = null
|
|
if(EQUIP_FAILACTION_DROP)
|
|
W.forceMove(get_turf(src)) // I think.
|
|
return equipped
|
|
|
|
/mob/proc/get_id_card()
|
|
for(var/obj/item/I in src.get_all_slots() + held_items)
|
|
. = I.GetID()
|
|
if(.)
|
|
break
|
|
|
|
/mob/proc/get_card()
|
|
// Try to find a debit card first.
|
|
var/list/all_slots = held_items + src.get_all_slots()
|
|
var/obj/item/weapon/card/debit/debit_card = locate(/obj/item/weapon/card/debit/) in all_slots
|
|
if(debit_card)
|
|
return debit_card
|
|
else
|
|
return get_id_card()
|
|
|
|
/mob/proc/slotID2slotname(slot_id)
|
|
switch (slot_id)
|
|
if (slot_back)
|
|
return "back"
|
|
if (slot_wear_mask)
|
|
return "face"
|
|
if (slot_handcuffed)
|
|
return "hands"
|
|
if (slot_belt)
|
|
return "belt"
|
|
if (slot_wear_id)
|
|
return "jumpsuit"
|
|
if (slot_ears)
|
|
return "ears"
|
|
if (slot_glasses)
|
|
return "face"
|
|
if (slot_gloves)
|
|
return "hands"
|
|
if (slot_head)
|
|
return "head"
|
|
if (slot_shoes)
|
|
return "feet"
|
|
if (slot_wear_suit)
|
|
return "suit"
|
|
if (slot_w_uniform)
|
|
return "uniform"
|
|
if (slot_l_store)
|
|
return "left pocket"
|
|
if (slot_r_store)
|
|
return "right pocket"
|
|
if (slot_s_store)
|
|
return "suit storage"
|
|
if (slot_in_backpack)
|
|
return "backpack"
|
|
if (slot_legcuffed)
|
|
return "ankles"
|
|
else
|
|
return ""
|
|
|
|
//Returns 1 if the item is part of the mob's built-in modules, and thus shouldn't be dropped or recycled or whatever.
|
|
//Currently only used for silicons, but I guess you could use this for robot arms with built-in tools or something
|
|
/mob/proc/is_in_modules(var/obj/item/W)
|
|
return 0
|