diff --git a/baystation12.dme b/baystation12.dme
index 828007b0de8..e3488cda375 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -1099,6 +1099,7 @@
#include "code\modules\projectiles\guns\energy\temperature.dm"
#include "code\modules\projectiles\guns\projectile\automatic.dm"
#include "code\modules\projectiles\guns\projectile\pistol.dm"
+#include "code\modules\projectiles\guns\projectile\pneumatic.dm"
#include "code\modules\projectiles\guns\projectile\revolver.dm"
#include "code\modules\projectiles\guns\projectile\shotgun.dm"
#include "code\modules\projectiles\projectile\animate.dm"
diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm
index 13249887f87..e144c194e02 100644
--- a/code/defines/obj/weapon.dm
+++ b/code/defines/obj/weapon.dm
@@ -313,7 +313,7 @@
desc = "Could probably be used as ... a throwing weapon?"
w_class = 1.0
force = 5.0
- throwforce = 15.0
+ throwforce = 8.0
item_state = "shard-glass"
g_amt = 3750
attack_verb = list("stabbed", "slashed", "sliced", "cut")
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 1d7d4bce3a6..59db6464fe0 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -21,10 +21,10 @@
//Detective Work, used for the duplicate data points kept in the scanners
var/list/original_atom
-/atom/proc/throw_impact(atom/hit_atom)
+/atom/proc/throw_impact(atom/hit_atom, var/speed)
if(istype(hit_atom,/mob/living))
var/mob/living/M = hit_atom
- M.hitby(src)
+ M.hitby(src,speed)
log_attack("[hit_atom] ([M.ckey]) was hit by [src] thrown by ([src.fingerprintslast])")
@@ -32,7 +32,7 @@
var/obj/O = hit_atom
if(!O.anchored)
step(O, src.dir)
- O.hitby(src)
+ O.hitby(src,speed)
else if(isturf(hit_atom))
var/turf/T = hit_atom
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 087d6fa6795..8f4e6dba669 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -44,18 +44,18 @@
return 1
return 0
-/atom/movable/proc/hit_check()
+/atom/movable/proc/hit_check(var/speed)
if(src.throwing)
for(var/atom/A in get_turf(src))
if(A == src) continue
if(istype(A,/mob/living))
if(A:lying) continue
- src.throw_impact(A)
+ src.throw_impact(A,speed)
if(src.throwing == 1)
src.throwing = 0
if(isobj(A))
if(A.density && !A.throwpass) // **TODO: Better behaviour for windows which are dense, but shouldn't always stop movement
- src.throw_impact(A)
+ src.throw_impact(A,speed)
src.throwing = 0
/atom/movable/proc/throw_at(atom/target, range, speed)
@@ -97,7 +97,7 @@
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
src.Move(step)
- hit_check()
+ hit_check(speed)
error += dist_x
dist_travelled++
dist_since_sleep++
@@ -109,7 +109,7 @@
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
src.Move(step)
- hit_check()
+ hit_check(speed)
error -= dist_y
dist_travelled++
dist_since_sleep++
@@ -126,7 +126,7 @@
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
src.Move(step)
- hit_check()
+ hit_check(speed)
error += dist_y
dist_travelled++
dist_since_sleep++
@@ -138,7 +138,7 @@
if(!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
break
src.Move(step)
- hit_check()
+ hit_check(speed)
error -= dist_x
dist_travelled++
dist_since_sleep++
@@ -150,7 +150,7 @@
//done throwing, either because it hit something or it finished moving
src.throwing = 0
- if(isobj(src)) src:throw_impact(get_turf(src))
+ if(isobj(src)) src:throw_impact(get_turf(src),speed)
//Overlays
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index 62ed84d34f6..da6f8f90c89 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -84,7 +84,7 @@
if(istype(src.beaker, /obj/item/weapon/reagent_containers/blood))
// speed up transfer on blood packs
transfer_amount = 4
- attached.inject_blood(beaker,transfer_amount)
+ src.beaker.reagents.trans_to(src.attached, transfer_amount)
update_icon()
// Take blood
diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools.dm b/code/game/objects/structures/stool_bed_chair_nest/stools.dm
index a2f18e8d813..ab7a55e1da8 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/stools.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/stools.dm
@@ -37,11 +37,12 @@
/obj/structure/stool/MouseDrop(atom/over_object)
if (istype(over_object, /mob/living/carbon/human))
var/mob/living/carbon/human/H = over_object
- var/obj/item/weapon/stool/S = new/obj/item/weapon/stool()
- S.origin = src
- src.loc = S
- H.put_in_hands(S)
- H.visible_message("\red [H] grabs [src] from the floor!", "\red You grab [src] from the floor!")
+ if (!H.restrained() && !H.stat && in_range(src, over_object))
+ var/obj/item/weapon/stool/S = new/obj/item/weapon/stool()
+ S.origin = src
+ src.loc = S
+ H.put_in_hands(S)
+ H.visible_message("\red [H] grabs [src] from the floor!", "\red You grab [src] from the floor!")
/obj/item/weapon/stool
name = "stool"
diff --git a/code/modules/clothing/spacesuits/alien.dm b/code/modules/clothing/spacesuits/alien.dm
index 7a74d855162..3a8824e644e 100644
--- a/code/modules/clothing/spacesuits/alien.dm
+++ b/code/modules/clothing/spacesuits/alien.dm
@@ -56,6 +56,8 @@
allowed = list(/obj/item/weapon/gun,/obj/item/ammo_magazine,/obj/item/ammo_casing,/obj/item/weapon/melee/baton,/obj/item/weapon/melee/energy/sword,/obj/item/weapon/handcuffs,/obj/item/weapon/tank)
slowdown = 2
armor = list(melee = 60, bullet = 50, laser = 30,energy = 15, bomb = 30, bio = 30, rad = 30)
+ heat_protection = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
+ max_heat_protection_temperature = SPACE_SUIT_MAX_HEAT_PROTECITON_TEMPERATURE
/obj/item/clothing/head/helmet/space/vox/carapace
name = "alien visor"
@@ -63,6 +65,7 @@
item_state = "vox-carapace"
desc = "A glowing visor, perhaps stolen from a depressed Cylon."
armor = list(melee = 60, bullet = 50, laser = 30, energy = 15, bomb = 30, bio = 30, rad = 30)
+ flags = HEADCOVERSEYES|STOPSPRESSUREDMAGE
/obj/item/clothing/suit/space/vox/carapace
name = "alien carapace armour"
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index d0f61484c66..86d251cdf45 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -61,7 +61,7 @@
P.on_hit(src, absorb)
return absorb
-/mob/living/hitby(atom/movable/AM as mob|obj)//Standardization and logging -Sieve
+/mob/living/hitby(atom/movable/AM as mob|obj,var/speed)//Standardization and logging -Sieve
if(istype(AM,/obj/))
var/obj/O = AM
var/zone = ran_zone("chest",75)//Hits a random part of the body, geared towards the chest
@@ -72,7 +72,7 @@
src.visible_message("\red [src] has been hit by [O].")
var/armor = run_armor_check(zone, "melee", "Your armor has protected your [zone].", "Your armor has softened hit to your [zone].")
if(armor < 2)
- apply_damage(O.throwforce, dtype, zone, armor, O)
+ apply_damage(O.throwforce*(speed/5), dtype, zone, armor, O)
if(!O.fingerprintslast)
return
var/client/assailant = directory[ckey(O.fingerprintslast)]
diff --git a/code/modules/projectiles/guns/projectile/pneumatic.dm b/code/modules/projectiles/guns/projectile/pneumatic.dm
new file mode 100644
index 00000000000..7d2f0c3b498
--- /dev/null
+++ b/code/modules/projectiles/guns/projectile/pneumatic.dm
@@ -0,0 +1,140 @@
+/obj/item/weapon/storage/pneumatic
+ name = "pneumatic cannon"
+ desc = "A large gas-powered cannon."
+ icon = 'icons/obj/gun.dmi'
+ icon_state = "pneumatic"
+ item_state = "pneumatic"
+ w_class = 4.0
+ flags = FPRINT | TABLEPASS | CONDUCT | USEDELAY
+ slot_flags = SLOT_BELT
+ max_w_class = 3
+ max_combined_w_class = 20
+
+ var/obj/item/weapon/tank/tank = null // Tank of gas for use in firing the cannon.
+ var/obj/item/weapon/storage/tank_container = new() // Something to hold the tank item so we don't accidentally fire it.
+ var/pressure_setting = 10 // Percentage of the gas in the tank used to fire the projectile.
+ var/possible_pressure_amounts = list(5,10,20,25,50) // Possible pressure settings.
+ var/minimum_tank_pressure = 10 // Minimum pressure to fire the gun.
+ var/cooldown = 0 // Whether or not we're cooling down.
+ var/cooldown_time = 50 // Time between shots.
+ var/force_divisor = 400 // Force equates to speed. Speed/5 equates to a damage multiplier for whoever you hit.
+ // For reference, a fully pressurized oxy tank at 50% gas release firing a health
+ // analyzer with a force_divisor of 10 hit with a damage multiplier of 3000+.
+
+/obj/item/weapon/storage/pneumatic/verb/set_pressure() //set amount of tank pressure.
+
+ set name = "Set valve pressure"
+ set category = "Object"
+ set src in range(0)
+ var/N = input("Percentage of tank used per shot:","[src]") as null|anything in possible_pressure_amounts
+ if (N)
+ pressure_setting = N
+ usr << "You dial the pressure valve to [pressure_setting]%."
+
+/obj/item/weapon/storage/pneumatic/verb/eject_tank() //Remove the tank.
+
+ set name = "Eject tank"
+ set category = "Object"
+ set src in range(0)
+
+ if(tank)
+ usr << "You twist the valve and pop the tank out of [src]."
+ tank.loc = usr.loc
+ tank = null
+ icon_state = "pneumatic"
+ item_state = "pneumatic"
+ usr.update_icons()
+ else
+ usr << "There's no tank in [src]."
+
+/obj/item/weapon/storage/pneumatic/attackby(obj/item/W as obj, mob/user as mob)
+ if(!tank && istype(W,/obj/item/weapon/tank))
+ user.drop_item()
+ tank = W
+ tank.loc = src.tank_container
+ user.visible_message("[user] jams [W] into [src]'s valve and twists it closed.","You jam [W] into [src]'s valve and twist it closed.")
+ icon_state = "pneumatic-tank"
+ item_state = "pneumatic-tank"
+ user.update_icons()
+ else
+ ..()
+
+/obj/item/weapon/storage/pneumatic/examine()
+ set src in view()
+ ..()
+ if (!(usr in view(2)) && usr!=src.loc) return
+ usr << "The valve is dialed to [pressure_setting]%."
+ if(tank)
+ usr << "The tank dial reads [tank.air_contents.return_pressure()] kPa."
+ else
+ usr << "Nothing is attached to the tank valve!"
+
+/obj/item/weapon/storage/pneumatic/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag, params)
+ if (istype(target, /obj/item/weapon/storage/backpack ))
+ return
+
+ else if (target.loc == user.loc)
+ return
+
+ else if (locate (/obj/structure/table, src.loc))
+ return
+
+ else if(target == user)
+ return
+
+ if (length(contents) == 0)
+ user << "There's nothing in [src] to fire!"
+ return 0
+ else
+ spawn(0) Fire(target,user,params)
+
+/obj/item/weapon/storage/pneumatic/attack(mob/living/M as mob, mob/living/user as mob, def_zone)
+ if (length(contents) > 0)
+ if(user.a_intent == "hurt")
+ user.visible_message("\red \The [user] fires \the [src] point blank at [M]!")
+ Fire(M,user)
+ return
+ else
+ Fire(M,user)
+ return
+
+/obj/item/weapon/storage/pneumatic/proc/Fire(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, params, reflex = 0)
+
+ if (!tank)
+ user << "There is no gas tank in [src]!"
+ return 0
+
+ if (cooldown)
+ user << "The chamber hasn't built up enough pressure yet!"
+ return 0
+
+ add_fingerprint(user)
+
+ var/turf/curloc = get_turf(user)
+ var/turf/targloc = get_turf(target)
+ if (!istype(targloc) || !istype(curloc))
+ return
+
+ var/fire_pressure = (tank.air_contents.return_pressure()/100)*pressure_setting
+
+ if (fire_pressure < minimum_tank_pressure)
+ user << "There isn't enough gas in the tank to fire [src]."
+ return 0
+
+ var/obj/item/object = contents[1]
+ var/speed = ((fire_pressure*tank.volume)/object.w_class)/force_divisor //projectile speed.
+ if(speed>80) speed = 80 //damage cap.
+
+ user.visible_message("[user] fires [src] and launches [object] at [target]!","You fire [src] and launch [object] at [target]!")
+
+ src.remove_from_storage(object,user.loc)
+ object.throw_at(target,10,speed)
+
+ var/lost_gas_amount = tank.air_contents.total_moles*(pressure_setting/100)
+ var/datum/gas_mixture/removed = tank.air_contents.remove(lost_gas_amount)
+ user.loc.assume_air(removed)
+
+ cooldown = 1
+ spawn(cooldown_time)
+ cooldown = 0
+ user << "[src]'s gauge informs you it's ready to be fired again."
diff --git a/code/modules/projectiles/projectile/force.dm b/code/modules/projectiles/projectile/force.dm
index 68bf6456a03..364ad53370b 100644
--- a/code/modules/projectiles/projectile/force.dm
+++ b/code/modules/projectiles/projectile/force.dm
@@ -12,7 +12,7 @@
var/obj/T = target
var/throwdir = get_dir(firer,target)
- T.throw_at(get_edge_target_turf(target, throwdir),10,1)
+ T.throw_at(get_edge_target_turf(target, throwdir),10,10)
return 1
/*
diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm
index 68cb8c4e2ab..2fb6d0c27fb 100644
--- a/code/modules/reagents/Chemistry-Holder.dm
+++ b/code/modules/reagents/Chemistry-Holder.dm
@@ -104,6 +104,10 @@ datum
for (var/datum/reagent/current_reagent in src.reagent_list)
if (!current_reagent)
continue
+ if (current_reagent.id == "blood" && ishuman(target))
+ var/mob/living/carbon/human/H = target
+ H.inject_blood(my_atom, amount)
+ continue
var/current_reagent_transfer = current_reagent.volume * part
if(preserve_data)
trans_data = current_reagent.data
diff --git a/icons/mob/human.dmi b/icons/mob/human.dmi
index dc9ca30eadc..9b4acf4d0d9 100644
Binary files a/icons/mob/human.dmi and b/icons/mob/human.dmi differ
diff --git a/icons/mob/items_lefthand.dmi b/icons/mob/items_lefthand.dmi
index c0e1013f9d3..c9ab6654416 100644
Binary files a/icons/mob/items_lefthand.dmi and b/icons/mob/items_lefthand.dmi differ
diff --git a/icons/mob/items_righthand.dmi b/icons/mob/items_righthand.dmi
index 1911107430d..b8ac94ee3a5 100644
Binary files a/icons/mob/items_righthand.dmi and b/icons/mob/items_righthand.dmi differ
diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi
index e65fc8825dd..41e428f3c21 100644
Binary files a/icons/mob/suit.dmi and b/icons/mob/suit.dmi differ
diff --git a/icons/obj/gun.dmi b/icons/obj/gun.dmi
index e9d1940dd33..5a0eab2d2a6 100644
Binary files a/icons/obj/gun.dmi and b/icons/obj/gun.dmi differ