Files
Paradise/code/game/objects/items.dm
Tigercat2000 bbca8405ef -tg- Move Refactor
This commit ports /tg/'s move refactor.

The throwing system has been replaced entirely, removing the necessity
of throw_at_fast and resolving multiple outstanding issues, such as
crossbows being unusable.

Spacedrifting has also been upgraded to function with the new throwing
system. It is now it's own process.

Tickcomp has been killed, and the config values have been adjusted to
more or less match live Paradise.

All mobs now share a common Bump() proc. There are only four mobtypes
which do not, including humans and simple animals. With the exception
of mob types that do not ever want to Bump() or be Bumped(), they should
call the parent proc.

Human movement slowdown has been moderately tweaked in how it stacks effects;
It shouldn't be significantly different from a player perspective.

Mobs will now spread fire if they bump into another mob. I don't want to set
the world on fiiiire, I just want start a flame in your heart~

For player facing changes: Input delay has been reduced by roughly ~50ms for
any direction keys, by advantage of a previously unknown flag on byond verbs
which allow them to operate independently from the tick rate of the server.
You may need to clear your interface.dmf file if you have a custom skin for
this change to function.
2017-05-25 06:35:01 -07:00

562 lines
20 KiB
Plaintext

var/global/image/fire_overlay = image("icon" = 'icons/goonstation/effects/fire.dmi', "icon_state" = "fire")
/obj/item
name = "item"
icon = 'icons/obj/items.dmi'
var/discrete = 0 // used in item_attack.dm to make an item not show an attack message to viewers
var/no_embed = 0 // For use in item_attack.dm
var/image/blood_overlay = null //this saves our blood splatter overlay, which will be processed not to go over the edges of the sprite
var/blood_overlay_color = null
var/item_state = null
var/lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
var/righthand_file = 'icons/mob/inhands/items_righthand.dmi'
//Dimensions of the lefthand_file and righthand_file vars
//eg: 32x32 sprite, 64x64 sprite, etc.
var/inhand_x_dimension = 32
var/inhand_y_dimension = 32
var/r_speed = 1.0
var/health = null
var/hitsound = null
var/usesound = null
var/w_class = 3
var/slot_flags = 0 //This is used to determine on which slots an item can fit.
pass_flags = PASSTABLE
pressure_resistance = 3
// causeerrorheresoifixthis
var/obj/item/master = null
var/heat_protection = 0 //flags which determine which body parts are protected from heat. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm
var/cold_protection = 0 //flags which determine which body parts are protected from cold. Use the HEAD, UPPER_TORSO, LOWER_TORSO, etc. flags. See setup.dm
var/max_heat_protection_temperature //Set this variable to determine up to which temperature (IN KELVIN) the item protects against heat damage. Keep at null to disable protection. Only protects areas set by heat_protection flags
var/min_cold_protection_temperature //Set this variable to determine down to which temperature (IN KELVIN) the item protects against cold damage. 0 is NOT an acceptable number due to if(varname) tests!! Keep at null to disable protection. Only protects areas set by cold_protection flags
var/list/actions = list() //list of /datum/action's that this item has.
var/list/actions_types = list() //list of paths of action datums to give to the item on New().
var/list/materials = list()
//Since any item can now be a piece of clothing, this has to be put here so all items share it.
var/flags_inv //This flag is used to determine when items in someone's inventory cover others. IE helmets making it so you can't see glasses, etc.
var/item_color = null
var/body_parts_covered = 0 //see setup.dm for appropriate bit flags
//var/heat_transfer_coefficient = 1 //0 prevents all transfers, 1 is invisible
var/gas_transfer_coefficient = 1 // for leaking gas from turf to mask and vice-versa (for masks right now, but at some point, i'd like to include space helmets)
var/permeability_coefficient = 1 // for chemicals/diseases
var/siemens_coefficient = 1 // for electrical admittance/conductance (electrocution checks and shit)
var/slowdown = 0 // How much clothing is slowing you down. Negative values speeds you up
var/armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 0, rad = 0)
var/armour_penetration = 0 //percentage of armour effectiveness to remove
var/list/allowed = null //suit storage stuff.
var/obj/item/device/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers.
var/needs_permit = 0 //Used by security bots to determine if this item is safe for public use.
var/strip_delay = DEFAULT_ITEM_STRIP_DELAY
var/put_on_delay = DEFAULT_ITEM_PUTON_DELAY
var/breakouttime = 0
var/flags_cover = 0 //for flags such as GLASSESCOVERSEYES
var/flags_size = 0 //flag, primarily used for clothing to determine if a fatty can wear something or not.
var/block_chance = 0
var/hit_reaction_chance = 0 //If you want to have something unrelated to blocking/armour piercing etc. Maybe not needed, but trying to think ahead/allow more freedom
var/mob/thrownby = null
var/toolspeed = 1 // If this item is a tool, the speed multiplier
/* Species-specific sprites, concept stolen from Paradise//vg/.
ex:
sprite_sheets = list(
"Tajaran" = 'icons/cat/are/bad'
)
If index term exists and icon_override is not set, this sprite sheet will be used.
*/
var/list/sprite_sheets = null
var/icon_override = null //Used to override hardcoded clothing dmis in human clothing proc.
var/sprite_sheets_obj = null //Used to override hardcoded clothing inventory object dmis in human clothing proc.
var/list/species_fit = null //This object has a different appearance when worn by these species
/obj/item/New()
..()
for(var/path in actions_types)
new path(src)
/obj/item/Destroy()
QDEL_NULL(hidden_uplink)
if(ismob(loc))
var/mob/m = loc
m.unEquip(src, 1)
QDEL_LIST(actions)
master = null
return ..()
/obj/item/proc/check_allowed_items(atom/target, not_inside, target_self)
if(((src in target) && !target_self) || ((!istype(target.loc, /turf)) && (!istype(target, /turf)) && (not_inside)))
return 0
else
return 1
/obj/item/device
icon = 'icons/obj/device.dmi'
/obj/item/ex_act(severity)
switch(severity)
if(1.0)
qdel(src)
return
if(2.0)
if(prob(50))
qdel(src)
return
if(3.0)
if(prob(5))
qdel(src)
return
else
return
/obj/item/blob_act()
qdel(src)
//user: The mob that is suiciding
//damagetype: The type of damage the item will inflict on the user
//BRUTELOSS = 1
//FIRELOSS = 2
//TOXLOSS = 4
//OXYLOSS = 8
//Output a creative message and then return the damagetype done
/obj/item/proc/suicide_act(mob/user)
return
/obj/item/verb/move_to_top()
set name = "Move To Top"
set category = null
set src in oview(1)
if(!istype(src.loc, /turf) || usr.stat || usr.restrained() )
return
var/turf/T = src.loc
src.loc = null
src.loc = T
/obj/item/examine(mob/user, var/distance = -1)
var/size
switch(src.w_class)
if(1.0)
size = "tiny"
if(2.0)
size = "small"
if(3.0)
size = "normal-sized"
if(4.0)
size = "bulky"
if(5.0)
size = "huge"
. = ..(user, distance, "", "It is a [size] item.")
if(user.research_scanner) //Mob has a research scanner active.
var/msg = "*--------* <BR>"
if(origin_tech)
msg += "<span class='notice'>Testing potentials:</span><BR>"
var/list/techlvls = params2list(origin_tech)
for(var/T in techlvls) //This needs to use the better names.
msg += "Tech: [CallTechName(T)] | Magnitude: [techlvls[T]] <BR>"
msg += "Research reliability: [reliability]% <BR>"
if(crit_fail)
msg += "<span class='danger'>Critical failure detected in subject!</span><BR>"
else
msg += "<span class='danger'>No tech origins detected.</span><BR>"
if(materials.len)
msg += "<span class='notice'>Extractable materials:<BR>"
for(var/mat in materials)
msg += "[CallMaterialName(mat)]<BR>" //Capitize first word, remove the "$"
else
msg += "<span class='danger'>No extractable materials detected.</span><BR>"
msg += "*--------*"
to_chat(user, msg)
/obj/item/attack_hand(mob/user as mob)
if(!user) return 0
if(hasorgans(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/external/temp = H.bodyparts_by_name["r_hand"]
if(user.hand)
temp = H.bodyparts_by_name["l_hand"]
if(!temp)
to_chat(user, "<span class='warning'>You try to use your hand, but it's missing!</span>")
return 0
if(temp && !temp.is_usable())
to_chat(user, "<span class='warning'>You try to move your [temp.name], but cannot!</span>")
return 0
if(burn_state == ON_FIRE)
var/mob/living/carbon/human/H = user
if(istype(H))
if(H.gloves && (H.gloves.max_heat_protection_temperature > 360))
extinguish()
to_chat(user, "<span class='notice'>You put out the fire on [src].</span>")
else
to_chat(user, "<span class='warning'>You burn your hand on [src]!</span>")
var/obj/item/organ/external/affecting = H.get_organ("[user.hand ? "l" : "r" ]_arm")
if(affecting && affecting.take_damage(0, 5)) // 5 burn damage
H.UpdateDamageIcon()
H.updatehealth()
return
else
extinguish()
if(istype(src.loc, /obj/item/weapon/storage))
//If the item is in a storage item, take it out
var/obj/item/weapon/storage/S = src.loc
S.remove_from_storage(src)
if(throwing)
throwing.finalize(FALSE)
if(loc == user)
if(!user.unEquip(src))
return 0
else
if(isliving(loc))
return 0
pickup(user)
add_fingerprint(user)
user.put_in_active_hand(src)
return 1
/obj/item/attack_alien(mob/user as mob)
var/mob/living/carbon/alien/A = user
if(!A.has_fine_manipulation)
if(src in A.contents) // To stop Aliens having items stuck in their pockets
A.unEquip(src)
to_chat(user, "Your claws aren't capable of such fine manipulation.")
return
attack_hand(A)
/obj/item/attack_ai(mob/user as mob)
if(istype(src.loc, /obj/item/weapon/robot_module))
//If the item is part of a cyborg module, equip it
if(!isrobot(user))
return
var/mob/living/silicon/robot/R = user
if(!R.low_power_mode) //can't equip modules with an empty cell.
R.activate_module(src)
R.hud_used.update_robot_modules_display()
// Due to storage type consolidation this should get used more now.
// I have cleaned it up a little, but it could probably use more. -Sayu
/obj/item/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W,/obj/item/weapon/storage))
var/obj/item/weapon/storage/S = W
if(S.use_to_pickup)
if(S.collection_mode) //Mode is set to collect all items on a tile and we clicked on a valid one.
if(isturf(src.loc))
var/list/rejections = list()
var/success = 0
var/failure = 0
for(var/obj/item/I in src.loc)
if(I.type in rejections) // To limit bag spamming: any given type only complains once
continue
if(!S.can_be_inserted(I)) // Note can_be_inserted still makes noise when the answer is no
rejections += I.type // therefore full bags are still a little spammy
failure = 1
continue
success = 1
S.handle_item_insertion(I, 1) //The 1 stops the "You put the [src] into [S]" insertion message from being displayed.
if(success && !failure)
to_chat(user, "<span class='notice'>You put everything in [S].</span>")
else if(success)
to_chat(user, "<span class='notice'>You put some things in [S].</span>")
else
to_chat(user, "<span class='notice'>You fail to pick anything up with [S].</span>")
else if(S.can_be_inserted(src))
S.handle_item_insertion(src)
return
/obj/item/proc/hit_reaction(mob/living/carbon/human/owner, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = MELEE_ATTACK)
if(prob(final_block_chance))
owner.visible_message("<span class='danger'>[owner] blocks [attack_text] with [src]!</span>")
return 1
return 0
/obj/item/proc/talk_into(mob/M, var/text, var/channel=null)
return
/obj/item/proc/dropped(mob/user)
for(var/X in actions)
var/datum/action/A = X
A.Remove(user)
// called just as an item is picked up (loc is not yet changed)
/obj/item/proc/pickup(mob/user)
return
// called when this item is removed from a storage item, which is passed on as S. The loc variable is already set to the new destination before this is called.
/obj/item/proc/on_exit_storage(obj/item/weapon/storage/S as obj)
return
// called when this item is added into a storage item, which is passed on as S. The loc variable is already set to the storage item.
/obj/item/proc/on_enter_storage(obj/item/weapon/storage/S as obj)
return
// called when "found" in pockets and storage items. Returns 1 if the search should end.
/obj/item/proc/on_found(mob/finder as mob)
return
// called after an item is placed in an equipment slot
// user is mob that equipped it
// slot uses the slot_X defines found in setup.dm
// for items that can be placed in multiple slots
// note this isn't called during the initial dressing of a player
/obj/item/proc/equipped(var/mob/user, var/slot)
for(var/X in actions)
var/datum/action/A = X
if(item_action_slot_check(slot, user)) //some items only give their actions buttons when in a specific slot.
A.Grant(user)
/obj/item/proc/item_action_slot_check(slot, mob/user)
return 1
//returns 1 if the item is equipped by a mob, 0 otherwise.
//This might need some error trapping, not sure if get_equipped_items() is safe for non-human mobs.
/obj/item/proc/is_equipped()
if(!ismob(loc))
return 0
var/mob/M = loc
if(src in M.get_equipped_items())
return 1
else
return 0
//the mob M is attempting to equip this item into the slot passed through as 'slot'. Return 1 if it can do this and 0 if it can't.
//If you are making custom procs but would like to retain partial or complete functionality of this one, include a 'return ..()' to where you want this to happen.
//Set disable_warning to 1 if you wish it to not give you outputs.
/obj/item/proc/mob_can_equip(mob/M, slot, disable_warning = 0)
if(!M)
return 0
return M.can_equip(src, slot, disable_warning)
/obj/item/verb/verb_pickup()
set src in oview(1)
set category = null
set name = "Pick up"
if(!(usr)) //BS12 EDIT
return
if(!usr.canmove || usr.stat || usr.restrained() || !Adjacent(usr))
return
if((!istype(usr, /mob/living/carbon)) || (istype(usr, /mob/living/carbon/brain)))//Is humanoid, and is not a brain
to_chat(usr, "<span class='warning'>You can't pick things up!</span>")
return
if( usr.stat || usr.restrained() )//Is not asleep/dead and is not restrained
to_chat(usr, "<span class='warning'>You can't pick things up!</span>")
return
if(src.anchored) //Object isn't anchored
to_chat(usr, "<span class='warning'>You can't pick that up!</span>")
return
if(!usr.hand && usr.r_hand) //Right hand is not full
to_chat(usr, "<span class='warning'>Your right hand is full.</span>")
return
if(usr.hand && usr.l_hand) //Left hand is not full
to_chat(usr, "<span class='warning'>Your left hand is full.</span>")
return
if(!istype(src.loc, /turf)) //Object is on a turf
to_chat(usr, "<span class='warning'>You can't pick that up!</span>")
return
//All checks are done, time to pick it up!
usr.UnarmedAttack(src)
return
//This proc is executed when someone clicks the on-screen UI button.
//The default action is attack_self().
//Checks before we get to here are: mob is alive, mob is not restrained, paralyzed, asleep, resting, laying, item is on the mob.
/obj/item/proc/ui_action_click(mob/user, actiontype)
attack_self(user)
/obj/item/proc/IsReflect(var/def_zone) //This proc determines if and at what% an object will reflect energy projectiles if it's in l_hand,r_hand or wear_suit
return 0
/obj/item/proc/get_loc_turf()
var/atom/L = loc
while(L && !istype(L, /turf/))
L = L.loc
return loc
/obj/item/proc/eyestab(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
var/mob/living/carbon/human/H = M
if(istype(H) && ( \
(H.head && H.head.flags_cover & HEADCOVERSEYES) || \
(H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSEYES) || \
(H.glasses && H.glasses.flags_cover & GLASSESCOVERSEYES) \
))
// you can't stab someone in the eyes wearing a mask!
to_chat(user, "<span class='danger'>You're going to need to remove that mask/helmet/glasses first!</span>")
return
if(istype(M, /mob/living/carbon/alien) || istype(M, /mob/living/carbon/slime))//Aliens don't have eyes./N slimes also don't have eyes!
to_chat(user, "<span class='warning'>You cannot locate any eyes on this creature!</span>")
return
if(!iscarbon(user))
M.LAssailant = null
else
M.LAssailant = user
src.add_fingerprint(user)
playsound(loc, src.hitsound, 30, 1, -1)
if(M != user)
M.visible_message("<span class='danger'>[user] has stabbed [M] in the eye with [src]!</span>", \
"<span class='userdanger'>[user] stabs you in the eye with [src]!</span>")
user.do_attack_animation(M)
else
user.visible_message( \
"<span class='danger'>[user] has stabbed themself in the eyes with [src]!</span>", \
"<span class='userdanger'>You stab yourself in the eyes with [src]!</span>" \
)
add_logs(user, M, "attacked", "[name]", "(INTENT: [uppertext(user.a_intent)])")
if(istype(H))
var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes)
if(!eyes) // should still get stabbed in the head
var/obj/item/organ/external/head/head = H.bodyparts_by_name["head"]
head.take_damage(rand(10,14), 1)
return
eyes.take_damage(rand(3,4), 1)
if(eyes.damage >= eyes.min_bruised_damage)
if(M.stat != 2)
if(!(eyes.status & ORGAN_ROBOT) || !(eyes.status & ORGAN_ASSISTED)) //robot eyes bleeding might be a bit silly
to_chat(M, "<span class='danger'>Your eyes start to bleed profusely!</span>")
if(prob(50))
if(M.stat != DEAD)
to_chat(M, "<span class='danger'>You drop what you're holding and clutch at your eyes!</span>")
M.drop_item()
M.AdjustEyeBlurry(10)
M.Paralyse(1)
M.Weaken(2)
if(eyes.damage >= eyes.min_broken_damage)
if(M.stat != 2)
to_chat(M, "<span class='danger'>You go blind!</span>")
var/obj/item/organ/external/affecting = H.get_organ("head")
if(affecting.take_damage(7))
H.UpdateDamageIcon()
else
M.take_organ_damage(7)
M.AdjustEyeBlurry(rand(3,4))
return
/obj/item/clean_blood()
. = ..()
if(blood_overlay)
overlays.Remove(blood_overlay)
if(istype(src, /obj/item/clothing/gloves))
var/obj/item/clothing/gloves/G = src
G.transfer_blood = 0
/obj/item/add_blood(mob/living/carbon/human/M as mob)
if(!..())
return 0
if(istype(src, /obj/item/weapon/melee/energy))
return
//if we haven't made our blood_overlay already
if( !blood_overlay )
generate_blood_overlay()
//apply the blood-splatter overlay if it isn't already in there
if(!blood_DNA.len)
blood_overlay.color = blood_color
overlays += blood_overlay
//if this blood isn't already in the list, add it
if(istype(M))
if(blood_DNA[M.dna.unique_enzymes])
return 0 //already bloodied with this blood. Cannot add more.
blood_DNA[M.dna.unique_enzymes] = M.dna.b_type
return 1 //we applied blood to the item
/obj/item/proc/generate_blood_overlay()
if(blood_overlay)
return
var/icon/I = new /icon(icon, icon_state)
I.Blend(new /icon('icons/effects/blood.dmi', rgb(255,255,255)),ICON_ADD) //fills the icon_state with white (except where it's transparent)
I.Blend(new /icon('icons/effects/blood.dmi', "itemblood"),ICON_MULTIPLY) //adds blood and the remaining white areas become transparant
//not sure if this is worth it. It attaches the blood_overlay to every item of the same type if they don't have one already made.
for(var/obj/item/A in world)
if(A.type == type && !A.blood_overlay)
A.blood_overlay = image(I)
/obj/item/singularity_pull(S, current_size)
spawn(0) //this is needed or multiple items will be thrown sequentially and not simultaneously
if(current_size >= STAGE_FOUR)
throw_at(S,14,3)
else ..()
/obj/item/throw_impact(atom/A)
if(A && !qdeleted(A))
var/itempush = 1
if(w_class < 4)
itempush = 0 // too light to push anything
return A.hitby(src, 0, itempush)
/obj/item/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback)
thrownby = thrower
callback = CALLBACK(src, .proc/after_throw, callback) //replace their callback with our own
. = ..(target, range, speed, thrower, spin, diagonals_first, callback)
/obj/item/proc/after_throw(datum/callback/callback)
if(callback) //call the original callback
. = callback.Invoke()
throw_speed = initial(throw_speed) //explosions change this.
/obj/item/proc/pwr_drain()
return 0 // Process Kill
/obj/item/proc/remove_item_from_storage(atom/newLoc) //please use this if you're going to snowflake an item out of a obj/item/weapon/storage
if(!newLoc)
return 0
if(istype(loc,/obj/item/weapon/storage))
var/obj/item/weapon/storage/S = loc
S.remove_from_storage(src,newLoc)
return 1
return 0
/obj/item/proc/wash(mob/user, atom/source)
if(flags & ABSTRACT) //Abstract items like grabs won't wash. No-drop items will though because it's still technically an item in your hand.
return
to_chat(user, "<span class='notice'>You start washing [src]...</span>")
if(!do_after(user, 40, target = source))
return
clean_blood()
user.visible_message("<span class='notice'>[user] washes [src] using [source].</span>", \
"<span class='notice'>You wash [src] using [source].</span>")
return 1
/obj/item/proc/is_crutch() //Does an item prop up a human mob and allow them to stand if they are missing a leg/foot?
return 0