diff --git a/.github/workflows/autochangelog.yml b/.github/workflows/autochangelog.yml
new file mode 100644
index 0000000000..7732426718
--- /dev/null
+++ b/.github/workflows/autochangelog.yml
@@ -0,0 +1,39 @@
+name: Autochangelog
+on:
+ pull_request:
+ types: closed
+ branches:
+ - master
+
+env:
+ BASENAME: "polaris"
+
+jobs:
+ autochangelog:
+ name: Autochangelog
+ runs-on: ubuntu-16.04
+ if: github.event.pull_request.merged == true
+ steps:
+ - uses: /actions/checkout@v2
+ - name: Ensure +x on CI directory
+ run: |
+ chmod -R +x ./tools/ci
+ - uses: actions/setup-python@v2
+ with:
+ python-version: '3.7'
+ - name: Generate Changelog
+ run: |
+ pip install pyyaml
+ python tools/GenerateChangelog/ss13_autochangelog.py \
+ html/changelogs \
+ ${{ github.event.pull_request.number }} \
+ "${{ github.event.pull_request.user.login }}" \
+ "${{ github.event.pull_request.body }}"
+ python tools/GenerateChangelog/ss13_genchangelog.py \
+ html/changelog.html \
+ html/changelogs
+ - uses: stefanzweifel/git-auto-commit-action@v4
+ with:
+ commit_message: Automatic changelog generation for ${{ github.events.pull_request.number }}
+ branch: ${{ github.events.pull_request.base }}
+ commit_user_name: Autochangelog Bot
diff --git a/code/__defines/belly_modes_vr.dm b/code/__defines/belly_modes_vr.dm
index ccffcf4341..e7853f7059 100644
--- a/code/__defines/belly_modes_vr.dm
+++ b/code/__defines/belly_modes_vr.dm
@@ -9,25 +9,11 @@
#define DM_SIZE_STEAL "Size Steal"
#define DM_HEAL "Heal"
#define DM_EGG "Encase In Egg"
-#define DM_TRANSFORM "Transform"
//#define DM_ITEMWEAK "Digest (Item Friendly)"
//#define DM_STRIPDIGEST "Strip Digest (Items Only)"
//#define DM_DIGEST_NUMB "Digest (Numbing)"
-//TF modes
-#define DM_TRANSFORM_HAIR_AND_EYES "Transform (Hair and eyes)"
-#define DM_TRANSFORM_MALE "Transform (Male)"
-#define DM_TRANSFORM_FEMALE "Transform (Female)"
-#define DM_TRANSFORM_KEEP_GENDER "Transform (Keep Gender)"
-#define DM_TRANSFORM_REPLICA "Transform (Replica Of Self)"
-#define DM_TRANSFORM_CHANGE_SPECIES_AND_TAUR "Transform (Change Species and Taur)"
-#define DM_TRANSFORM_CHANGE_SPECIES_AND_TAUR_EGG "Transform (Change Species and Taur) (EGG)"
-#define DM_TRANSFORM_REPLICA_EGG "Transform (Replica Of Self) (EGG)"
-#define DM_TRANSFORM_KEEP_GENDER_EGG "Transform (Keep Gender) (EGG)"
-#define DM_TRANSFORM_MALE_EGG "Transform (Male) (EGG)"
-#define DM_TRANSFORM_FEMALE_EGG "Transform (Female) (EGG)"
-
//Addon mode flags
#define DM_FLAG_NUMBING 0x1
#define DM_FLAG_STRIPPING 0x2
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index 82b1eabc60..69e949e495 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -157,6 +157,8 @@
#define MAT_GRAPHITE "graphite"
#define MAT_LEATHER "leather"
#define MAT_CHITIN "chitin"
+#define MAT_CLOTH "cloth"
+#define MAT_SYNCLOTH "syncloth"
#define SHARD_SHARD "shard"
#define SHARD_SHRAPNEL "shrapnel"
@@ -480,4 +482,15 @@ GLOBAL_LIST_INIT(all_volume_channels, list(
VOLUME_CHANNEL_ALARMS,
VOLUME_CHANNEL_VORE,
VOLUME_CHANNEL_DOORS,
-))
\ No newline at end of file
+))
+
+#define APPEARANCECHANGER_CHANGED_RACE "Race"
+#define APPEARANCECHANGER_CHANGED_GENDER "Gender"
+#define APPEARANCECHANGER_CHANGED_GENDER_ID "Gender Identity"
+#define APPEARANCECHANGER_CHANGED_SKINTONE "Skin Tone"
+#define APPEARANCECHANGER_CHANGED_SKINCOLOR "Skin Color"
+#define APPEARANCECHANGER_CHANGED_HAIRSTYLE "Hair Style"
+#define APPEARANCECHANGER_CHANGED_HAIRCOLOR "Hair Color"
+#define APPEARANCECHANGER_CHANGED_F_HAIRSTYLE "Facial Hair Style"
+#define APPEARANCECHANGER_CHANGED_F_HAIRCOLOR "Facial Hair Color"
+#define APPEARANCECHANGER_CHANGED_EYES "Eye Color"
diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm
index 34c68ebd08..f6ca0ea565 100644
--- a/code/__defines/species_languages.dm
+++ b/code/__defines/species_languages.dm
@@ -68,7 +68,7 @@
#define LANGUAGE_ALAI "Alai"
#define LANGUAGE_ZADDAT "Vedahq"
#define LANGUAGE_PROMETHEAN "Promethean Biolinguistics"
-#define LANGUAGE_BLOB "Blob"
+#define LANGUAGE_BLOB "Chemosense Transmission"
#define LANGUAGE_GIBBERISH "Babel"
// Language flags.
diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm
index 371f35b051..855decff39 100644
--- a/code/_helpers/global_lists.dm
+++ b/code/_helpers/global_lists.dm
@@ -215,7 +215,7 @@ var/global/list/string_slot_flags = list(
GLOB.closet_appearances[T] = app
// VOREStation Add - Vore Modes!
- paths = typesof(/datum/digest_mode) - /datum/digest_mode/transform
+ paths = typesof(/datum/digest_mode)
for(var/T in paths)
var/datum/digest_mode/DM = new T
GLOB.digest_modes[DM.id] = DM
diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm
index 0406099616..946cd9de0e 100644
--- a/code/_helpers/global_lists_vr.dm
+++ b/code/_helpers/global_lists_vr.dm
@@ -8,6 +8,7 @@ var/global/list/tail_styles_list = list() // Stores /datum/sprite_accessory/tail
var/global/list/wing_styles_list = list() // Stores /datum/sprite_accessory/wing indexed by type
var/global/list/negative_traits = list() // Negative custom species traits, indexed by path
var/global/list/neutral_traits = list() // Neutral custom species traits, indexed by path
+var/global/list/everyone_traits = list() // Neutral traits available to all species, indexed by path
var/global/list/positive_traits = list() // Positive custom species traits, indexed by path
var/global/list/traits_costs = list() // Just path = cost list, saves time in char setup
var/global/list/all_traits = list() // All of 'em at once (same instances)
@@ -457,7 +458,7 @@ var/global/list/remainless_species = list(SPECIES_PROMETHEAN,
for(var/path in paths)
var/datum/sprite_accessory/hair_accessory/instance = new path()
hair_accesories_list[path] = instance
-
+
// Custom species traits
paths = typesof(/datum/trait) - /datum/trait
for(var/path in paths)
@@ -472,6 +473,8 @@ var/global/list/remainless_species = list(SPECIES_PROMETHEAN,
negative_traits[path] = instance
if(0)
neutral_traits[path] = instance
+ if(!(instance.custom_only))
+ everyone_traits[path] = instance
if(0.1 to INFINITY)
positive_traits[path] = instance
diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm
index 6a4579f191..b29a1192d9 100644
--- a/code/_onclick/click.dm
+++ b/code/_onclick/click.dm
@@ -354,19 +354,13 @@
facedir(direction)
/obj/screen/click_catcher
+ name = "Darkness"
icon = 'icons/mob/screen_gen.dmi'
icon_state = "click_catcher"
plane = CLICKCATCHER_PLANE
+ layer = LAYER_HUD_UNDER
mouse_opacity = 2
- screen_loc = "CENTER-7,CENTER-7"
-
-/obj/screen/click_catcher/proc/MakeGreed()
- . = list()
- for(var/i = 0, i<15, i++)
- for(var/j = 0, j<15, j++)
- var/obj/screen/click_catcher/CC = new()
- CC.screen_loc = "NORTH-[i],EAST-[j]"
- . += CC
+ screen_loc = "SOUTHWEST to NORTHEAST"
/obj/screen/click_catcher/Click(location, control, params)
var/list/modifiers = params2list(params)
@@ -374,7 +368,8 @@
var/mob/living/carbon/C = usr
C.swap_hand()
else
- var/turf/T = screen_loc2turf(screen_loc, get_turf(usr))
+ var/list/P = params2list(params)
+ var/turf/T = screen_loc2turf(P["screen-loc"], get_turf(usr))
if(T)
T.Click(location, control, params)
. = 1
diff --git a/code/_onclick/hud/screen_objects_vr.dm b/code/_onclick/hud/screen_objects_vr.dm
index b3388f7bf7..1d8df98a38 100644
--- a/code/_onclick/hud/screen_objects_vr.dm
+++ b/code/_onclick/hud/screen_objects_vr.dm
@@ -11,6 +11,11 @@
var/mob/living/simple_mob/shadekin/SK = usr
if(istype(SK))
to_chat(usr,"Energy: [SK.energy] ([SK.dark_gains])")
+ if("shadekin status")
+ var/turf/T = get_turf(usr)
+ if(T)
+ var/darkness = round(1 - T.get_lumcount(),0.1)
+ to_chat(usr,"Darkness: [darkness]")
var/mob/living/carbon/human/H = usr
if(istype(H) && istype(H.species, /datum/species/shadekin))
to_chat(usr,"Energy: [H.shadekin_get_energy(H)]")
diff --git a/code/datums/autolathe/devices.dm b/code/datums/autolathe/devices.dm
index 9c6ab5bb97..eed4839159 100644
--- a/code/datums/autolathe/devices.dm
+++ b/code/datums/autolathe/devices.dm
@@ -26,6 +26,12 @@
name = "mechanical trap"
path =/obj/item/weapon/beartrap
+/datum/category_item/autolathe/devices/barbedwire
+ name = "barbed wire"
+ path = /obj/item/weapon/material/barbedwire
+ hidden = 1
+ resources = list(DEFAULT_WALL_MATERIAL = 10000)
+
/datum/category_item/autolathe/devices/electropack
name = "electropack"
path =/obj/item/device/radio/electropack
diff --git a/code/datums/autolathe/tools.dm b/code/datums/autolathe/tools.dm
index 9e9a87ea71..9b88a496fa 100644
--- a/code/datums/autolathe/tools.dm
+++ b/code/datums/autolathe/tools.dm
@@ -48,6 +48,11 @@
path = /obj/item/weapon/reagent_containers/spray
resources = list(MAT_PLASTIC = 2000)
+/datum/category_item/autolathe/devices/slowwire
+ name = "snare wire"
+ path = /obj/item/weapon/material/barbedwire/plastic
+ resources = list(MAT_PLASTIC = 10000)
+
/datum/category_item/autolathe/tools/spraynozzle
name = "spray nozzle"
path = /obj/item/weapon/reagent_containers/spray
diff --git a/code/datums/uplink/tools.dm b/code/datums/uplink/tools.dm
index 51d7ec7cd2..eee41e9699 100644
--- a/code/datums/uplink/tools.dm
+++ b/code/datums/uplink/tools.dm
@@ -92,6 +92,12 @@
desc = "A device which is capable of disrupting subspace communications, preventing the use of headsets, PDAs, and communicators within \
a radius of seven meters. It runs off weapon cells, which can be replaced as needed. One cell will last for approximately ten minutes."
+/datum/uplink_item/item/tools/wall_elecrtifier
+ name = "Wall Electrifier"
+ item_cost = 10
+ path = /obj/item/weapon/cell/spike
+ desc = "A modified powercell which will electrify walls and reinforced floors in a 3x3 tile range around it. Always active."
+
/datum/uplink_item/item/tools/emag
name = "Cryptographic Sequencer"
item_cost = 30
diff --git a/code/datums/wires/mines.dm b/code/datums/wires/mines.dm
index 209fcc4c30..5e757ad16c 100644
--- a/code/datums/wires/mines.dm
+++ b/code/datums/wires/mines.dm
@@ -1,5 +1,5 @@
/datum/wires/mines
- wire_count = 6
+ wire_count = 7
randomize = TRUE
holder_type = /obj/effect/mine
proper_name = "Explosive Wires"
@@ -7,6 +7,7 @@
/datum/wires/mines/New(atom/_holder)
wires = list(WIRE_EXPLODE, WIRE_EXPLODE_DELAY, WIRE_DISARM, WIRE_BADDISARM)
return ..()
+#define WIRE_TRAP 64
/datum/wires/mines/get_status()
. = ..()
@@ -29,7 +30,13 @@
if(WIRE_DISARM)
C.visible_message("[bicon(C)] *click!*", "[bicon(C)] *click!*")
- new C.mineitemtype(get_turf(C))
+ var/obj/effect/mine/MI = new C.mineitemtype(get_turf(C))
+
+ if(C.trap)
+ MI.trap = C.trap
+ C.trap = null
+ MI.trap.forceMove(MI)
+
spawn(0)
qdel(C)
@@ -37,6 +44,15 @@
C.visible_message("[bicon(C)] *BEEPBEEPBEEP*", "[bicon(C)] *BEEPBEEPBEEP*")
spawn(20)
C.explode()
+
+ if(WIRE_TRAP)
+ C.visible_message("[bicon(C)] *click!*", "[bicon(C)] *click!*")
+
+ if(mend)
+ C.visible_message("[bicon(C)] - The mine recalibrates[C.camo_net ? ", revealing \the [C.trap] inside." : "."]")
+
+ C.alpha = 255
+
..()
/datum/wires/mines/on_pulse(wire)
@@ -57,6 +73,10 @@
if(WIRE_BADDISARM)
C.visible_message("[bicon(C)] *ping*", "[bicon(C)] *ping*")
+
+ if(WIRE_TRAP)
+ C.visible_message("[bicon(C)] *ping*", "[bicon(C)] *ping*")
+
..()
/datum/wires/mines/interactable(mob/user)
diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm
index d68842fb2c..b65b44eb06 100644
--- a/code/game/dna/dna2.dm
+++ b/code/game/dna/dna2.dm
@@ -206,23 +206,9 @@ var/global/list/datum/dna/gene/dna_genes[0]
// Technically custom_species is not part of the UI, but this place avoids merge problems.
src.custom_species = character.custom_species
- if(istype(character.species,/datum/species/custom))
- var/datum/species/custom/CS = character.species
- src.species_traits = CS.traits.Copy()
- src.base_species = CS.base_species
- src.blood_color = CS.blood_color
-
- if(istype(character.species,/datum/species/xenochimera))
- var/datum/species/xenochimera/CS = character.species
- //src.species_traits = CS.traits.Copy() //No traits
- src.base_species = CS.base_species
- src.blood_color = CS.blood_color
-
- if(istype(character.species,/datum/species/alraune))
- var/datum/species/alraune/CS = character.species
- //src.species_traits = CS.traits.Copy() //No traits
- src.base_species = CS.base_species
- src.blood_color = CS.blood_color
+ src.base_species = character.species.base_species
+ src.blood_color = character.species.blood_color
+ src.species_traits = character.species.traits.Copy()
// +1 to account for the none-of-the-above possibility
SetUIValueRange(DNA_UI_EAR_STYLE, ear_style + 1, ear_styles_list.len + 1, 1)
diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm
index 0edc6bb636..758f74d873 100644
--- a/code/game/dna/dna2_helpers.dm
+++ b/code/game/dna/dna2_helpers.dm
@@ -237,20 +237,9 @@
// Technically custom_species is not part of the UI, but this place avoids merge problems.
H.custom_species = dna.custom_species
- if(istype(H.species,/datum/species/custom))
- var/datum/species/custom/CS = H.species
- var/datum/species/custom/new_CS = CS.produceCopy(dna.base_species,dna.species_traits,src)
- new_CS.blood_color = dna.blood_color
-
- if(istype(H.species,/datum/species/xenochimera))
- var/datum/species/xenochimera/CS = H.species
- var/datum/species/xenochimera/new_CS = CS.produceCopy(dna.base_species,dna.species_traits,src)
- new_CS.blood_color = dna.blood_color
-
- if(istype(H.species,/datum/species/alraune))
- var/datum/species/alraune/CS = H.species
- var/datum/species/alraune/new_CS = CS.produceCopy(dna.base_species,dna.species_traits,src)
- new_CS.blood_color = dna.blood_color
+ H.species.blood_color = dna.blood_color
+ var/datum/species/S = H.species
+ S.produceCopy(dna.base_species,dna.species_traits,src)
// VOREStation Edit End
H.force_update_organs() //VOREStation Add - Gotta do this too
diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm
index db27559796..64a3cc1fc7 100644
--- a/code/game/machinery/deployable.dm
+++ b/code/game/machinery/deployable.dm
@@ -1,111 +1,8 @@
/*
CONTAINS:
Deployable items
-Barricades
*/
-//Barricades!
-/obj/structure/barricade
- name = "barricade"
- desc = "This space is blocked off by a barricade."
- icon = 'icons/obj/structures.dmi'
- icon_state = "barricade"
- anchored = 1.0
- density = 1.0
- var/health = 100
- var/maxhealth = 100
- var/datum/material/material
-
-/obj/structure/barricade/New(var/newloc, var/material_name)
- ..(newloc)
- if(!material_name)
- material_name = "wood"
- material = get_material_by_name("[material_name]")
- if(!material)
- qdel(src)
- return
- name = "[material.display_name] barricade"
- desc = "This space is blocked off by a barricade made of [material.display_name]."
- color = material.icon_colour
- maxhealth = material.integrity
- health = maxhealth
-
-/obj/structure/barricade/get_material()
- return material
-
-/obj/structure/barricade/attackby(obj/item/W as obj, mob/user as mob)
- user.setClickCooldown(user.get_attack_speed(W))
- if(istype(W, /obj/item/stack))
- var/obj/item/stack/D = W
- if(D.get_material_name() != material.name)
- return //hitting things with the wrong type of stack usually doesn't produce messages, and probably doesn't need to.
- if(health < maxhealth)
- if(D.get_amount() < 1)
- to_chat(user, "You need one sheet of [material.display_name] to repair \the [src].")
- return
- visible_message("[user] begins to repair \the [src].")
- if(do_after(user,20) && health < maxhealth)
- if(D.use(1))
- health = maxhealth
- visible_message("[user] repairs \the [src].")
- return
- return
- else
- switch(W.damtype)
- if("fire")
- health -= W.force * 1
- if("brute")
- health -= W.force * 0.75
- if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD)))
- playsound(src, 'sound/effects/woodcutting.ogg', 100, 1)
- else
- playsound(src, 'sound/weapons/smash.ogg', 50, 1)
- CheckHealth()
- ..()
-
-/obj/structure/barricade/proc/CheckHealth()
- if(health <= 0)
- dismantle()
- return
-
-/obj/structure/barricade/take_damage(var/damage)
- health -= damage
- CheckHealth()
- return
-
-/obj/structure/barricade/attack_generic(var/mob/user, var/damage, var/attack_verb)
- visible_message("[user] [attack_verb] the [src]!")
- if(material == get_material_by_name("resin"))
- playsound(src, 'sound/effects/attackblob.ogg', 100, 1)
- else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD)))
- playsound(src, 'sound/effects/woodcutting.ogg', 100, 1)
- else
- playsound(src, 'sound/weapons/smash.ogg', 50, 1)
- user.do_attack_animation(src)
- health -= damage
- CheckHealth()
- return
-
-/obj/structure/barricade/proc/dismantle()
- material.place_dismantled_product(get_turf(src))
- visible_message("\The [src] falls apart!")
- qdel(src)
- return
-
-/obj/structure/barricade/ex_act(severity)
- switch(severity)
- if(1.0)
- dismantle()
- if(2.0)
- health -= 25
- CheckHealth()
-
-/obj/structure/barricade/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff.
- if(istype(mover) && mover.checkpass(PASSTABLE))
- return TRUE
- return FALSE
-
-//Actual Deployable machinery stuff
/obj/machinery/deployable
name = "deployable"
desc = "deployable"
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index c522b4300e..c5e1f26c61 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -7,16 +7,32 @@
icon_state = "uglymine"
var/triggered = 0
var/smoke_strength = 3
- var/mineitemtype = /obj/item/weapon/mine
+ var/obj/item/weapon/mine/mineitemtype = /obj/item/weapon/mine
var/panel_open = 0
var/datum/wires/mines/wires = null
register_as_dangerous_object = TRUE
+ var/camo_net = FALSE // Will the mine 'cloak' on deployment?
+
+ // The trap item will be triggered in some manner when detonating. Default only checks for grenades.
+ var/obj/item/trap = null
+
/obj/effect/mine/New()
icon_state = "uglyminearmed"
wires = new(src)
+ if(ispath(trap))
+ trap = new trap(src)
+
+/obj/effect/mine/Initialize()
+ ..()
+
+ if(camo_net)
+ alpha = 50
+
/obj/effect/mine/Destroy()
+ if(trap)
+ QDEL_NULL(trap)
qdel_null(wires)
return ..()
@@ -25,11 +41,33 @@
triggered = 1
s.set_up(3, 1, src)
s.start()
- explosion(loc, 0, 2, 3, 4) //land mines are dangerous, folks.
- visible_message("\The [src.name] detonates!")
+
+ if(trap)
+ trigger_trap(M)
+ visible_message("\The [src.name] flashes as it is triggered!")
+
+ else
+ explosion(loc, 0, 2, 3, 4) //land mines are dangerous, folks.
+ visible_message("\The [src.name] detonates!")
+
qdel(s)
qdel(src)
+/obj/effect/mine/proc/trigger_trap(var/mob/living/victim)
+ if(istype(trap, /obj/item/weapon/grenade))
+ var/obj/item/weapon/grenade/G = trap
+ trap = null
+ G.forceMove(get_turf(src))
+ if(victim.ckey)
+ msg_admin_attack("[key_name_admin(victim)] stepped on \a [src.name], triggering [trap]")
+ G.activate()
+
+ if(istype(trap, /obj/item/device/transfer_valve))
+ var/obj/item/device/transfer_valve/TV = trap
+ trap = null
+ TV.forceMove(get_turf(src))
+ TV.toggle_valve()
+
/obj/effect/mine/bullet_act()
if(prob(50))
explode()
@@ -63,6 +101,9 @@
"You very carefully screw the mine's panel [panel_open ? "open" : "closed"].")
playsound(src, W.usesound, 50, 1)
+ // Panel open, stay uncloaked, or uncloak if already cloaked. If you don't cloak on place, ignore it and just be normal alpha.
+ alpha = camo_net ? (panel_open ? 255 : 50) : 255
+
else if((W.is_wirecutter() || istype(W, /obj/item/device/multitool)) && panel_open)
interact(user)
else
@@ -74,6 +115,9 @@
user.set_machine(src)
wires.Interact(user)
+/obj/effect/mine/camo
+ camo_net = TRUE
+
/obj/effect/mine/dnascramble
mineitemtype = /obj/item/weapon/mine/dnascramble
@@ -193,6 +237,9 @@
spawn(0)
qdel(src)
+/obj/effect/mine/emp/camo
+ camo_net = TRUE
+
/obj/effect/mine/incendiary
mineitemtype = /obj/item/weapon/mine/incendiary
@@ -208,6 +255,26 @@
spawn(0)
qdel(src)
+/obj/effect/mine/gadget
+ mineitemtype = /obj/item/weapon/mine/gadget
+
+/obj/effect/mine/gadget/explode(var/mob/living/M)
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread()
+ triggered = 1
+ s.set_up(3, 1, src)
+ s.start()
+
+ if(trap)
+ trigger_trap(M)
+ visible_message("\The [src.name] flashes as it is triggered!")
+
+ else
+ explosion(loc, 0, 0, 2, 2)
+ visible_message("\The [src.name] detonates!")
+
+ qdel(s)
+ qdel(src)
+
/////////////////////////////////////////////
// The held item version of the above mines
/////////////////////////////////////////////
@@ -219,6 +286,10 @@
var/countdown = 10
var/minetype = /obj/effect/mine //This MUST be an /obj/effect/mine type, or it'll runtime.
+ var/obj/item/trap = null
+
+ var/list/allowed_gadgets = null
+
/obj/item/weapon/mine/attack_self(mob/user as mob) // You do not want to move or throw a land mine while priming it... Explosives + Sudden Movement = Bad Times
add_fingerprint(user)
msg_admin_attack("[key_name_admin(user)] primed \a [src]")
@@ -231,11 +302,39 @@
prime(user, TRUE)
return
+/obj/item/weapon/mine/attackby(obj/item/W as obj, mob/living/user as mob)
+ if(W.is_screwdriver() && trap)
+ to_chat(user, "You begin removing \the [trap].")
+ if(do_after(user, 10 SECONDS))
+ to_chat(user, "You finish disconnecting the mine's trigger.")
+ trap.forceMove(get_turf(src))
+ trap = null
+ return
+
+ if(LAZYLEN(allowed_gadgets) && !trap)
+ var/allowed = FALSE
+
+ for(var/path in allowed_gadgets)
+ if(istype(W, path))
+ allowed = TRUE
+ break
+
+ if(allowed)
+ user.drop_from_inventory(W)
+ W.forceMove(src)
+ trap = W
+
+ ..()
+
/obj/item/weapon/mine/proc/prime(mob/user as mob, var/explode_now = FALSE)
visible_message("\The [src.name] beeps as the priming sequence completes.")
var/obj/effect/mine/R = new minetype(get_turf(src))
src.transfer_fingerprints_to(R)
R.add_fingerprint(user)
+ if(trap)
+ R.trap = trap
+ trap = null
+ R.trap.forceMove(R)
if(explode_now)
R.explode(user)
spawn(0)
@@ -286,8 +385,14 @@
desc = "A small explosive mine with a fire symbol on the side."
minetype = /obj/effect/mine/incendiary
+/obj/item/weapon/mine/gadget
+ name = "gadget mine"
+ desc = "A small pressure-triggered device. If no component is added, the internal release bolts will detonate in unison when triggered."
+
+ allowed_gadgets = list(/obj/item/weapon/grenade, /obj/item/device/transfer_valve)
+
// This tells AI mobs to not be dumb and step on mines willingly.
/obj/item/weapon/mine/is_safe_to_step(mob/living/L)
if(!L.hovering)
return FALSE
- return ..()
\ No newline at end of file
+ return ..()
diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm
index 8e5fa683c9..f938f0eab1 100644
--- a/code/game/objects/items/devices/defib.dm
+++ b/code/game/objects/items/devices/defib.dm
@@ -509,8 +509,8 @@
return // Still no brain.
// If the brain'd `defib_timer` var gets below this number, brain damage will happen at a linear rate.
- // This is measures in `Life()` ticks. E.g. 10 minute defib timer = 6000 world.time units = 3000 `Life()` ticks.
- var/brain_damage_timer = ((config.defib_timer MINUTES) / 2) - ((config.defib_braindamage_timer MINUTES) / 2)
+ // This is measures in `Life()` ticks. E.g. 10 minute defib timer = 300 `Life()` ticks. // Original math was VERY off. Life() tick occurs every ~2 seconds, not every 2 world.time ticks.
+ var/brain_damage_timer = ((config.defib_timer MINUTES) / 20) - ((config.defib_braindamage_timer MINUTES) / 20)
if(brain.defib_timer > brain_damage_timer)
return // They got revived before brain damage got a chance to set in.
diff --git a/code/game/objects/items/devices/spy_bug.dm b/code/game/objects/items/devices/spy_bug.dm
index 03d0bdd60c..60ca5cc1a2 100644
--- a/code/game/objects/items/devices/spy_bug.dm
+++ b/code/game/objects/items/devices/spy_bug.dm
@@ -88,6 +88,14 @@
if(get_dist(user, src) == 0)
. += "It has a tiny camera inside. Needs to be both configured and brought in contact with monitor device to be fully functional."
+/obj/item/device/camerabug/update_icon()
+ ..()
+
+ if(anchored) // Standard versions are relatively obvious if not hidden in a container. Anchoring them is advised, to disguise them.
+ alpha = 50
+ else
+ alpha = 255
+
/obj/item/device/camerabug/attackby(obj/item/W as obj, mob/living/user as mob)
if(istype(W, /obj/item/device/bug_monitor))
var/obj/item/device/bug_monitor/SM = W
@@ -101,6 +109,15 @@
linkedmonitor = null
else
to_chat(user, "Error: The device is linked to another monitor.")
+
+ else if(W.is_wrench() && user.a_intent != I_HURT)
+ if(isturf(loc))
+ anchored = !anchored
+
+ to_chat(user, "You [anchored ? "" : "un"]secure \the [src].")
+
+ update_icon()
+ return
else
if(W.force >= 5)
visible_message("\The [src] lens shatters!")
diff --git a/code/game/objects/items/stacks/matter_synth.dm b/code/game/objects/items/stacks/matter_synth.dm
index d92cd5f8dd..02c6518374 100644
--- a/code/game/objects/items/stacks/matter_synth.dm
+++ b/code/game/objects/items/stacks/matter_synth.dm
@@ -56,3 +56,6 @@
name = "Bandage Synthesizer"
max_energy = 10
recharge_rate = 1
+
+/datum/matter_synth/cloth
+ name = "Cloth Synthesizer"
diff --git a/code/game/objects/items/stacks/sandbags.dm b/code/game/objects/items/stacks/sandbags.dm
new file mode 100644
index 0000000000..c4e755c485
--- /dev/null
+++ b/code/game/objects/items/stacks/sandbags.dm
@@ -0,0 +1,158 @@
+/obj/item/stack/sandbags
+ name = "sandbag"
+ desc = "Filled sandbags. Fortunately pre-filled."
+ singular_name = "sandbag"
+ icon = 'icons/obj/sandbags.dmi'
+ icon_state = "sandbag"
+ w_class = ITEMSIZE_LARGE
+ force = 10.0
+ throwforce = 20.0
+ throw_speed = 5
+ throw_range = 3
+ drop_sound = 'sound/items/drop/clothing.ogg'
+ pickup_sound = 'sound/items/pickup/clothing.ogg'
+ matter = list(MAT_CLOTH = SHEET_MATERIAL_AMOUNT * 2)
+ max_amount = 30
+ attack_verb = list("hit", "bludgeoned", "pillowed")
+ no_variants = TRUE
+ stacktype = /obj/item/stack/sandbags
+
+ pass_color = TRUE
+
+ var/bag_material = "cloth"
+
+/obj/item/stack/sandbags/cyborg
+ name = "sandbag synthesizer"
+ desc = "A device that makes filled sandbags. Don't ask how."
+ gender = NEUTER
+ matter = null
+ uses_charge = 1
+ charge_costs = list(500)
+ stacktype = /obj/item/stack/sandbags
+
+ bag_material = MAT_SYNCLOTH
+
+/obj/item/stack/sandbags/New(var/newloc, var/amt, var/bag_mat)
+ ..()
+ recipes = sandbag_recipes
+ update_icon()
+
+ if(bag_mat)
+ bag_material = bag_mat
+
+ var/datum/material/M = get_material_by_name("[bag_material]")
+ if(!M)
+ qdel(src)
+ return
+
+ color = M.icon_colour
+
+/obj/item/stack/sandbags/update_icon()
+ var/amount = get_amount()
+
+ slowdown = round(amount / 10, 0.1)
+
+var/global/list/datum/stack_recipe/sandbag_recipes = list( \
+ new/datum/stack_recipe("barricade", /obj/structure/barricade/sandbag, 3, time = 5 SECONDS, one_per_turf = 1, on_floor = 1, pass_stack_color = TRUE))
+
+/obj/item/stack/sandbags/produce_recipe(datum/stack_recipe/recipe, var/quantity, mob/user)
+ var/required = quantity*recipe.req_amount
+ var/produced = min(quantity*recipe.res_amount, recipe.max_res_amount)
+
+ if (!can_use(required))
+ if (produced>1)
+ to_chat(user, "You haven't got enough [src] to build \the [produced] [recipe.title]\s!")
+ else
+ to_chat(user, "You haven't got enough [src] to build \the [recipe.title]!")
+ return
+
+ if (recipe.one_per_turf && (locate(recipe.result_type) in user.loc))
+ to_chat(user, "There is another [recipe.title] here!")
+ return
+
+ if (recipe.on_floor && !isfloor(user.loc))
+ to_chat(user, "\The [recipe.title] must be constructed on the floor!")
+ return
+
+ if (recipe.time)
+ to_chat(user, "Building [recipe.title] ...")
+ if (!do_after(user, recipe.time))
+ return
+
+ if (use(required))
+ var/atom/O = new recipe.result_type(user.loc, bag_material)
+
+ if(istype(O, /obj))
+ var/obj/Ob = O
+
+ if(LAZYLEN(Ob.matter)) // Law of equivalent exchange.
+ Ob.matter.Cut()
+
+ else
+ Ob.matter = list()
+
+ var/mattermult = istype(Ob, /obj/item) ? min(2000, 400 * Ob.w_class) : 2000
+
+ Ob.matter[recipe.use_material] = mattermult / produced * required
+
+ O.set_dir(user.dir)
+ O.add_fingerprint(user)
+
+ if (istype(O, /obj/item/stack))
+ var/obj/item/stack/S = O
+ S.amount = produced
+ S.add_to_stacks(user)
+
+ if (istype(O, /obj/item/weapon/storage)) //BubbleWrap - so newly formed boxes are empty
+ for (var/obj/item/I in O)
+ qdel(I)
+
+ if ((pass_color || recipe.pass_color))
+ if(!color)
+ if(recipe.use_material)
+ var/datum/material/MAT = get_material_by_name(recipe.use_material)
+ if(MAT.icon_colour)
+ O.color = MAT.icon_colour
+ else
+ return
+ else
+ O.color = color
+
+// Empty bags. Yes, you need to fill them.
+
+/obj/item/stack/emptysandbag
+ name = "sandbag"
+ desc = "Empty sandbags. You know what must be done."
+ singular_name = "sandbag"
+ icon = 'icons/obj/sandbags.dmi'
+ icon_state = "sandbag_e"
+ w_class = ITEMSIZE_LARGE
+
+ strict_color_stacking = TRUE
+ max_amount = 30
+ stacktype = /obj/item/stack/emptysandbag
+
+ pass_color = TRUE
+
+ var/bag_material = "cloth"
+
+/obj/item/stack/emptysandbag/New(var/newloc, var/amt, var/bag_mat)
+ ..(newloc, amt)
+
+ if(bag_mat)
+ bag_material = bag_mat
+
+ var/datum/material/M = get_material_by_name("[bag_material]")
+ if(!M)
+ qdel(src)
+ return
+
+ color = M.icon_colour
+
+/obj/item/stack/emptysandbag/attack_self(var/mob/user)
+ while(do_after(user, 1 SECOND) && can_use(1) && istype(get_turf(src), /turf/simulated/floor/outdoors))
+ use(1)
+ var/obj/item/stack/sandbags/SB = new (get_turf(src), 1, bag_material)
+ SB.color = color
+ if(user)
+ to_chat(user, "You fill a sandbag.")
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index 5fa718564f..d42d6c0e56 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -163,7 +163,7 @@
if (recipe.time)
to_chat(user, "Building [recipe.title] ...")
- if (!do_after(user, recipe.time, exclusive = TRUE))
+ if (!do_after(user, recipe.time))
return
if (use(required))
diff --git a/code/game/objects/items/weapons/material/material_weapons.dm b/code/game/objects/items/weapons/material/material_weapons.dm
index 963f1d3683..4948adf425 100644
--- a/code/game/objects/items/weapons/material/material_weapons.dm
+++ b/code/game/objects/items/weapons/material/material_weapons.dm
@@ -99,6 +99,8 @@
/obj/item/weapon/material/proc/check_health(var/consumed)
if(health<=0)
+ health = 0
+
if(fragile)
shatter(consumed)
else if(!dulled && can_dull)
diff --git a/code/game/objects/items/weapons/traps.dm b/code/game/objects/items/weapons/traps.dm
index 0ec9bc7af3..c583ce1eef 100644
--- a/code/game/objects/items/weapons/traps.dm
+++ b/code/game/objects/items/weapons/traps.dm
@@ -1,3 +1,9 @@
+
+/*
+ * Beartraps.
+ * Buckles crossing individuals, doing moderate brute damage.
+ */
+
/obj/item/weapon/beartrap
name = "mechanical trap"
throw_speed = 2
@@ -158,3 +164,238 @@
color = "#C9DCE1"
origin_tech = list(TECH_MATERIAL = 4, TECH_BLUESPACE = 3, TECH_MAGNET = 4, TECH_PHORON = 2, TECH_ARCANE = 1)
+
+/*
+ * Barbed-Wire.
+ * Slows individuals crossing it. Barefoot individuals will be cut. Can be electrified by placing over a cable node.
+ */
+
+/obj/item/weapon/material/barbedwire
+ name = "barbed wire"
+ desc = "A coil of wire."
+ icon = 'icons/obj/trap.dmi'
+ icon_state = "barbedwire"
+ anchored = FALSE
+ layer = TABLE_LAYER
+ w_class = ITEMSIZE_LARGE
+ explosion_resistance = 1
+ can_dull = TRUE
+ fragile = TRUE
+ force_divisor = 0.20
+ thrown_force_divisor = 0.25
+
+ sharp = TRUE
+
+/obj/item/weapon/material/barbedwire/set_material(var/new_material)
+ ..()
+
+ if(!QDELETED(src))
+ health = round(material.integrity / 3)
+ name = (material.get_edge_damage() * force_divisor > 15) ? "[material.display_name] razor wire" : "[material.display_name] [initial(name)]"
+
+/obj/item/weapon/material/barbedwire/proc/can_use(mob/user)
+ return (user.IsAdvancedToolUser() && !issilicon(user) && !user.stat && !user.restrained())
+
+/obj/item/weapon/material/barbedwire/attack_hand(mob/user as mob)
+ if(anchored && can_use(user))
+ user.visible_message(
+ "[user] starts to collect \the [src].",
+ "You begin collecting \the [src]!",
+ "You hear the sound of rustling [material.name]."
+ )
+ playsound(src, 'sound/machines/click.ogg', 50, 1)
+
+ if(do_after(user, health))
+ user.visible_message(
+ "[user] has collected \the [src].",
+ "You have collected \the [src]!"
+ )
+ anchored = 0
+ update_icon()
+ else
+ ..()
+
+/obj/item/weapon/material/barbedwire/attack_self(mob/user as mob)
+ ..()
+ if(!anchored && can_use(user))
+ user.visible_message(
+ "[user] starts to deploy \the [src].",
+ "You begin deploying \the [src]!",
+ "You hear the rustling of [material.name]."
+ )
+
+ if (do_after(user, 60))
+ user.visible_message(
+ "[user] has deployed \the [src].",
+ "You have deployed \the [src]!",
+ "You hear the rustling of [material.name]."
+ )
+ playsound(src, 'sound/items/Wirecutter.ogg',70, 1)
+ spawn(2)
+ playsound(src, 'sound/items/Wirecutter.ogg',40, 1)
+ user.drop_from_inventory(src)
+ forceMove(get_turf(src))
+ anchored = 1
+ update_icon()
+
+/obj/item/weapon/material/barbedwire/attackby(obj/item/W as obj, mob/user as mob)
+ if(!istype(W))
+ return
+
+ if((W.flags & NOCONDUCT) || !shock(user, 70, pick(BP_L_HAND, BP_R_HAND)))
+ user.setClickCooldown(user.get_attack_speed(W))
+ user.do_attack_animation(src)
+ playsound(src, 'sound/effects/grillehit.ogg', 40, 1)
+
+ var/inc_damage = W.force
+
+ if(W.is_wirecutter())
+ if(!shock(user, 100, pick(BP_L_HAND, BP_R_HAND)))
+ playsound(src, W.usesound, 100, 1)
+ inc_damage *= 3
+
+ if(W.damtype != BRUTE)
+ inc_damage *= 0.3
+
+ health -= inc_damage
+
+ check_health()
+
+ ..()
+
+/obj/item/weapon/material/barbedwire/update_icon()
+ ..()
+
+ if(anchored)
+ icon_state = "[initial(icon_state)]-out"
+ else
+ icon_state = "[initial(icon_state)]"
+
+/obj/item/weapon/material/barbedwire/Crossed(atom/movable/AM as mob|obj)
+ if(AM.is_incorporeal())
+ return
+ if(anchored && isliving(AM))
+ var/mob/living/L = AM
+ if(L.m_intent == "run")
+ L.visible_message(
+ "[L] steps in \the [src].",
+ "You step in \the [src]!",
+ "You hear a sharp rustling!"
+ )
+ attack_mob(L)
+ update_icon()
+ ..()
+
+/obj/item/weapon/material/barbedwire/proc/shock(mob/user as mob, prb, var/target_zone = BP_TORSO)
+ if(!anchored || health == 0) // anchored/destroyed grilles are never connected
+ return 0
+ if(material.conductivity <= 0)
+ return 0
+ if(!prob(prb))
+ return 0
+ if(!in_range(src, user))//To prevent TK and mech users from getting shocked
+ return 0
+ var/turf/T = get_turf(src)
+ var/obj/structure/cable/C = T.get_cable_node()
+ if(C)
+ if(C.powernet)
+ var/datum/powernet/PN = C.powernet
+
+ if(PN)
+ PN.trigger_warning()
+
+ var/PN_damage = PN.get_electrocute_damage() * (material.conductivity / 50)
+
+ var/drained_energy = PN_damage * 10 / CELLRATE
+
+ PN.draw_power(drained_energy)
+
+ if(ishuman(user))
+ var/mob/living/carbon/human/H = user
+
+ var/obj/item/organ/external/affected = H.get_organ(check_zone(target_zone))
+
+ H.electrocute_act(PN_damage, src, H.get_siemens_coefficient_organ(affected))
+
+ else
+ if(isliving(user))
+ var/mob/living/L = user
+ L.electrocute_act(PN_damage, src, 0.8)
+
+ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
+ s.set_up(3, 1, src)
+ s.start()
+ if(user.stunned)
+ return 1
+ else
+ return 0
+ return 0
+
+/obj/item/weapon/material/barbedwire/proc/attack_mob(mob/living/L)
+ var/target_zone
+ if(L.lying)
+ target_zone = ran_zone()
+ else
+ target_zone = pick("l_foot", "r_foot", "l_leg", "r_leg")
+
+ //armour
+ var/blocked = L.run_armor_check(target_zone, "melee")
+ var/soaked = L.get_armor_soak(target_zone, "melee")
+
+ if(blocked >= 100)
+ return
+
+ if(soaked >= 30)
+ return
+
+ if(L.buckled) //wheelchairs, office chairs, rollerbeds
+ return
+
+ shock(L, 100, target_zone)
+
+ L.add_modifier(/datum/modifier/entangled, 3 SECONDS)
+
+ if(!L.apply_damage(force * (issilicon(L) ? 0.25 : 1), BRUTE, target_zone, blocked, soaked, src, sharp, edge))
+ return
+
+ playsound(src, 'sound/effects/glass_step.ogg', 50, 1) // not sure how to handle metal shards with sounds
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+
+ if(H.species.siemens_coefficient<0.5) //Thick skin.
+ return
+
+ if( H.shoes || ( H.wear_suit && (H.wear_suit.body_parts_covered & FEET) ) )
+ return
+
+ if(H.species.flags & NO_MINOR_CUT)
+ return
+
+ to_chat(H, "You step directly on \the [src]!")
+
+ var/list/check = list("l_foot", "r_foot")
+ while(check.len)
+ var/picked = pick(check)
+ var/obj/item/organ/external/affecting = H.get_organ(picked)
+ if(affecting)
+ if(affecting.robotic >= ORGAN_ROBOT)
+ return
+ if(affecting.take_damage(force, 0))
+ H.UpdateDamageIcon()
+ H.updatehealth()
+ if(affecting.organ_can_feel_pain())
+ H.Weaken(3)
+ return
+ check -= picked
+
+ if(material.is_brittle() && prob(material.hardness))
+ health = 0
+ else if(!prob(material.hardness))
+ health--
+ check_health()
+
+ return
+
+/obj/item/weapon/material/barbedwire/plastic
+ name = "snare wire"
+ default_material = MAT_PLASTIC
diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm
index 16be9dff94..4d40578c1e 100644
--- a/code/game/objects/random/mapping.dm
+++ b/code/game/objects/random/mapping.dm
@@ -92,6 +92,8 @@
return pick(prob(30);/obj/effect/mine,
prob(25);/obj/effect/mine/frag,
prob(25);/obj/effect/mine/emp,
+ prob(15);/obj/effect/mine/camo,
+ prob(15);/obj/effect/mine/emp/camo,
prob(10);/obj/effect/mine/stun,
prob(10);/obj/effect/mine/incendiary,)
diff --git a/code/game/objects/structures/barricades.dm b/code/game/objects/structures/barricades.dm
new file mode 100644
index 0000000000..34ab6025c7
--- /dev/null
+++ b/code/game/objects/structures/barricades.dm
@@ -0,0 +1,185 @@
+//Barricades!
+/obj/structure/barricade
+ name = "barricade"
+ desc = "This space is blocked off by a barricade."
+ icon = 'icons/obj/structures.dmi'
+ icon_state = "barricade"
+ anchored = 1.0
+ density = 1.0
+ var/health = 100
+ var/maxhealth = 100
+ var/datum/material/material
+
+/obj/structure/barricade/New(var/newloc, var/material_name)
+ ..(newloc)
+ if(!material_name)
+ material_name = "wood"
+ material = get_material_by_name("[material_name]")
+ if(!material)
+ qdel(src)
+ return
+ name = "[material.display_name] barricade"
+ desc = "This space is blocked off by a barricade made of [material.display_name]."
+ color = material.icon_colour
+ maxhealth = material.integrity
+ health = maxhealth
+
+/obj/structure/barricade/get_material()
+ return material
+
+/obj/structure/barricade/attackby(obj/item/W as obj, mob/user as mob)
+ user.setClickCooldown(user.get_attack_speed(W))
+ if(istype(W, /obj/item/stack))
+ var/obj/item/stack/D = W
+ if(D.get_material_name() != material.name)
+ return //hitting things with the wrong type of stack usually doesn't produce messages, and probably doesn't need to.
+ if(health < maxhealth)
+ if(D.get_amount() < 1)
+ to_chat(user, "You need one sheet of [material.display_name] to repair \the [src].")
+ return
+ visible_message("[user] begins to repair \the [src].")
+ if(do_after(user,20) && health < maxhealth)
+ if(D.use(1))
+ health = maxhealth
+ visible_message("[user] repairs \the [src].")
+ return
+ return
+ else
+ switch(W.damtype)
+ if("fire")
+ health -= W.force * 1
+ if("brute")
+ health -= W.force * 0.75
+ if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD)))
+ playsound(src, 'sound/effects/woodcutting.ogg', 100, 1)
+ else
+ playsound(src, 'sound/weapons/smash.ogg', 50, 1)
+ CheckHealth()
+ ..()
+
+/obj/structure/barricade/proc/CheckHealth()
+ if(health <= 0)
+ dismantle()
+
+ health = min(health, maxhealth)
+
+ return
+
+/obj/structure/barricade/take_damage(var/damage)
+ health -= damage
+ CheckHealth()
+ return
+
+/obj/structure/barricade/attack_generic(var/mob/user, var/damage, var/attack_verb)
+ visible_message("[user] [attack_verb] the [src]!")
+ if(material == get_material_by_name("resin"))
+ playsound(src, 'sound/effects/attackblob.ogg', 100, 1)
+ else if(material == (get_material_by_name(MAT_CLOTH) || get_material_by_name(MAT_SYNCLOTH)))
+ playsound(src, 'sound/items/drop/clothing.ogg', 100, 1)
+ else if(material == (get_material_by_name(MAT_WOOD) || get_material_by_name(MAT_SIFWOOD)))
+ playsound(src, 'sound/effects/woodcutting.ogg', 100, 1)
+ else
+ playsound(src, 'sound/weapons/smash.ogg', 50, 1)
+ user.do_attack_animation(src)
+ health -= damage
+ CheckHealth()
+ return
+
+/obj/structure/barricade/proc/dismantle()
+ material.place_dismantled_product(get_turf(src))
+ visible_message("\The [src] falls apart!")
+ qdel(src)
+ return
+
+/obj/structure/barricade/ex_act(severity)
+ switch(severity)
+ if(1.0)
+ dismantle()
+ if(2.0)
+ health -= 25
+ CheckHealth()
+
+/obj/structure/barricade/CanPass(atom/movable/mover, turf/target)//So bullets will fly over and stuff.
+ if(istype(mover) && mover.checkpass(PASSTABLE))
+ return TRUE
+ return FALSE
+
+/obj/structure/barricade/sandbag
+ name = "sandbags"
+ desc = "Bags. Bags of sand. It's rough and coarse and somehow stays in the bag."
+ icon = 'icons/obj/sandbags.dmi'
+ icon_state = "blank"
+
+/obj/structure/barricade/sandbag/New(var/newloc, var/material_name)
+ if(!material_name)
+ material_name = "cloth"
+ ..(newloc, material_name)
+ material = get_material_by_name("[material_name]")
+ if(!material)
+ qdel(src)
+ return
+ name = "[material.display_name] [initial(name)]"
+ desc = "This space is blocked off by a barricade made of [material.display_name]."
+ color = null
+ maxhealth = material.integrity * 2 // These things are, commonly, used to stop bullets where possible.
+ health = maxhealth
+ update_connections(1)
+
+/obj/structure/barricade/sandbag/Destroy()
+ update_connections(1, src)
+ ..()
+
+/obj/structure/barricade/sandbag/dismantle()
+ update_connections(1, src)
+ material.place_dismantled_product(get_turf(src))
+ visible_message("\The [src] falls apart!")
+ qdel(src)
+ return
+
+/obj/structure/barricade/sandbag/update_icon()
+ if(!material)
+ return
+
+ cut_overlays()
+ var/image/I
+
+ for(var/i = 1 to 4)
+ I = image('icons/obj/sandbags.dmi', "sandbags[connections[i]]", dir = 1<<(i-1))
+ I.color = material.icon_colour
+ add_overlay(I)
+
+ return
+
+/obj/structure/barricade/sandbag/update_connections(propagate = 0, var/obj/structure/barricade/sandbag/ignore = null)
+ if(!material)
+ return
+ var/list/dirs = list()
+ for(var/obj/structure/barricade/sandbag/S in orange(src, 1))
+ if(!S.material)
+ continue
+ if(S == ignore)
+ continue
+ if(propagate >= 1)
+ S.update_connections(propagate - 1, ignore)
+ if(can_join_with(S))
+ dirs += get_dir(src, S)
+
+ connections = dirs_to_corner_states(dirs)
+
+ update_icon()
+
+/obj/structure/barricade/sandbag/proc/can_join_with(var/obj/structure/barricade/sandbag/S)
+ if(material == S.material)
+ return 1
+ return 0
+
+/obj/structure/barricade/sandbag/CanPass(atom/movable/mover, turf/target)
+ . = ..()
+
+ if(.)
+ if(istype(mover, /obj/item/projectile))
+ var/obj/item/projectile/P = mover
+
+ if(P.firer && get_dist(P.firer, src) > 1) // If you're firing from adjacent turfs, you are unobstructed.
+ if(P.armor_penetration < (material.protectiveness + material.hardness) || prob(33))
+ return FALSE
diff --git a/code/game/objects/structures/signs.dm b/code/game/objects/structures/signs.dm
index f93ceaf0a1..78384b0285 100644
--- a/code/game/objects/structures/signs.dm
+++ b/code/game/objects/structures/signs.dm
@@ -321,73 +321,344 @@
desc = "A warning sign which reads XENOBIOLOGY."
icon_state = "xenobio3"
+//direction signs presented by the order they appear in the dmi
/obj/structure/sign/directions
name = "direction sign"
- desc = "A direction sign, claiming to know the way."
+ desc = "A direction sign, claiming to know the way to... somewhere?"
icon_state = "direction"
+ icon = 'icons/obj/decals_directions.dmi'
+ //TODO: set up overlay systems, inc. interactions (e.g. vines clear w/ plantbgone or fire, snow can be brushed off or melted, and so on)
+//disabled this proc, it serves no purpose except to overwrite the description that already exists. may have been intended for making your own signs?
+//seems to defeat the point of having a generic directional sign that mappers could edit and use in POIs? left it here in case something breaks.
+/*
/obj/structure/sign/directions/New()
..()
- desc = "A direction sign, pointing out which way \the [src] is."
-
-/obj/structure/sign/directions/science
- name = "\improper Science department"
- desc = "A direction sign, pointing out which way the Science department is."
- icon_state = "direction_sci"
+ desc = "A direction sign, pointing out the way to \the [src]."
+*/
+//engineering signs
/obj/structure/sign/directions/engineering
- name = "\improper Engineering department"
- desc = "A direction sign, pointing out which way the Engineering department is."
+ name = "\improper Engineering Department"
+ desc = "A direction sign, pointing out the way to the Engineering Department."
icon_state = "direction_eng"
+/obj/structure/sign/directions/engineering/reactor
+ name = "\improper Reactor"
+ desc = "A direction sign, pointing out the way to the Reactor."
+ icon_state = "direction_core"
+
+/obj/structure/sign/directions/engineering/solars
+ name = "\improper Solar Array"
+ desc = "A direction sign, pointing out the way to the nearest Solar Array."
+ icon_state = "direction_solar"
+
+/obj/structure/sign/directions/engineering/atmospherics
+ name = "\improper Atmospherics Department"
+ desc = "A direction sign, pointing out the way to the Atmospherics Department."
+ icon_state = "direction_atmos"
+
+/obj/structure/sign/directions/engineering/gravgen
+ name = "\improper Gravity Generator"
+ desc = "A direction sign, pointing out the way to the Artificial Gravity Generator."
+ icon_state = "direction_grav"
+
+/obj/structure/sign/directions/engineering/engeqp
+ name = "\improper Engineering Equipment Storage"
+ desc = "A direction sign, pointing out the way to Engineering Equipment Storage."
+ icon_state = "direction_engeqp"
+
+//security signs
/obj/structure/sign/directions/security
- name = "\improper Security department"
- desc = "A direction sign, pointing out which way the Security department is."
+ name = "\improper Security Department"
+ desc = "A direction sign, pointing out the way to the Security Department."
icon_state = "direction_sec"
+/obj/structure/sign/directions/security/armory
+ name = "\improper Armory"
+ desc = "A direction sign, pointing out the way to the Armory."
+ icon_state = "direction_armory"
+
+/obj/structure/sign/directions/security/brig
+ name = "\improper Brig"
+ desc = "A direction sign, pointing out the way to the Brig."
+ icon_state = "direction_brig"
+
+/obj/structure/sign/directions/security/seceqp
+ name = "\improper Security Equipment Storage"
+ desc = "A direction sign, pointing out the way to Security Equipment Storage."
+ icon_state = "direction_seceqp"
+
+/obj/structure/sign/directions/security/internal_affairs
+ name = "\improper Internal Affairs Office"
+ desc = "A direction sign, pointing out the way to the Internal Affairs Office."
+ icon_state = "direction_intaff"
+
+/obj/structure/sign/directions/security/forensics
+ name = "\improper Forensics Lab"
+ desc = "A direction sign, pointing out the way to the Forensics Lab."
+ icon_state = "direction_forensics"
+
+/obj/structure/sign/directions/security/forensics/alt
+ icon_state = "direction_lab"
+
+/obj/structure/sign/directions/security/interrogation
+ name = "\improper Interrogations"
+ desc = "A direction sign, pointing out the way to Interrogations."
+ icon_state = "direction_interrogation"
+
+//science signs
+/obj/structure/sign/directions/science
+ name = "\improper Science Department"
+ desc = "A direction sign, pointing out the way to the Science Department."
+ icon_state = "direction_sci"
+
+/obj/structure/sign/directions/science/rnd
+ name = "\improper Research & Development"
+ desc = "A direction sign, pointing out the way to Research & Development."
+ icon_state = "direction_rnd"
+
+/obj/structure/sign/directions/science/toxins
+ name = "\improper Toxins Lab"
+ desc = "A direction sign, pointing out the way to the Toxins Lab."
+ icon_state = "direction_sci"
+
+/obj/structure/sign/directions/science/robotics
+ name = "\improper Robotics Workshop"
+ desc = "A direction sign, pointing out the way to the Robotics Workshop."
+ icon_state = "direction_robotics"
+
+/obj/structure/sign/directions/science/xenoarch
+ name = "\improper Xenoarchaeology Lab"
+ desc = "A direction sign, pointing out the way to the Xenoarchaeology Lab."
+ icon_state = "direction_xenoarch"
+
+/obj/structure/sign/directions/science/xenobiology
+ name = "\improper Xenobiology Lab"
+ desc = "A direction sign, pointing out the way to the Xenobiology Lab."
+ icon_state = "direction_xbio"
+
+/obj/structure/sign/directions/science/xenoflora
+ name = "\improper Xenoflora Lab"
+ desc = "A direction sign, pointing out the way to the Xenoflora Lab."
+ icon_state = "direction_xflora"
+
+/obj/structure/sign/directions/science/exploration
+ name = "\improper Exploration Department"
+ desc = "A direction sign, pointing out the way to the Exploration Department."
+ icon_state = "direction_explo"
+
+//medical signs
/obj/structure/sign/directions/medical
name = "\improper Medical Bay"
- desc = "A direction sign, pointing out which way the Medical Bay is."
+ desc = "A direction sign, pointing out the way to the Medical Bay."
icon_state = "direction_med"
+/obj/structure/sign/directions/medical/chemlab
+ name = "\improper Chemistry Lab"
+ desc = "A direction sign, pointing out the way to the Chemistry Lab."
+ icon_state = "direction_med"
+
+/obj/structure/sign/directions/medical/surgery
+ name = "\improper Surgery"
+ desc = "A direction sign, pointing out the way to Surgery."
+ icon_state = "direction_surgery"
+
+/obj/structure/sign/directions/medical/operating_1
+ name = "\improper Operating Theatre 1"
+ desc = "A direction sign, pointing out the way to Operating Theatre 1."
+ icon_state = "direction_op1"
+
+/obj/structure/sign/directions/medical/operating_2
+ name = "\improper Operating Theatre 2"
+ desc = "A direction sign, pointing out the way to Operating Theatre 2."
+ icon_state = "direction_op2"
+
+/obj/structure/sign/directions/medical/virology
+ name = "\improper Virology"
+ desc = "A direction sign, pointing out the way to the Virology Lab."
+ icon_state = "direction_viro"
+
+/obj/structure/sign/directions/medical/medeqp
+ name = "\improper Medical Equipment Storage"
+ desc = "A direction sign, pointing out the way to Medical Equipment Storage."
+ icon_state = "direction_medeqp"
+
+/obj/structure/sign/directions/medical/morgue
+ name = "\improper Morgue"
+ desc = "A direction sign, pointing out the way to the Morgue."
+ icon_state = "direction_morgue"
+
+/obj/structure/sign/directions/medical/cloning
+ name = "\improper Cloning Lab"
+ desc = "A direction sign, pointing out the way to the Cloning Lab."
+ icon_state = "direction_cloning"
+
+/obj/structure/sign/directions/medical/resleeving
+ name = "\improper Resleeving Lab"
+ desc = "A direction sign, pointing out the way to the Resleeving Lab."
+ icon_state = "direction_resleeve"
+
+//special signs
/obj/structure/sign/directions/evac
name = "\improper Evacuation"
- desc = "A direction sign, pointing out which way the Escape Shuttle dock is."
+ desc = "A direction sign, pointing out the way to the Escape Shuttle Dock."
icon_state = "direction_evac"
+/obj/structure/sign/directions/eva
+ name = "\improper Extra-Vehicular Activity"
+ desc = "A direction sign, pointing out the way to the EVA Bay."
+ icon_state = "direction_eva"
+
+//command signs
+/obj/structure/sign/directions/ai_core
+ name = "\improper AI Core"
+ desc = "A direction sign, pointing out the way to the AI Core."
+ icon_state = "direction_ai_core"
+
/obj/structure/sign/directions/bridge
name = "\improper Bridge"
+ desc = "A direction sign, pointing out the way to the Bridge."
icon_state = "direction_bridge"
+/obj/structure/sign/directions/command
+ name = "\improper Command"
+ desc = "A direction sign, pointing out the way to the Command Center."
+ icon_state = "direction_command"
+
+/obj/structure/sign/directions/teleporter
+ name = "\improper Teleporter"
+ desc = "A direction sign, pointing out the way to the Teleporter."
+ icon_state = "direction_teleport"
+
+/obj/structure/sign/directions/telecomms
+ name = "\improper Telecommunications Hub"
+ desc = "A direction sign, pointing out the way to the Telecommunications Hub."
+ icon_state = "direction_tcomms"
+
+//cargonia signs
/obj/structure/sign/directions/cargo
- name = "\improper Cargo department"
- desc = "A direction sign, pointing out which way the Cargo department is."
+ name = "\improper Cargo Department"
+ desc = "A direction sign, pointing out the way to the Cargo Department."
icon_state = "direction_crg"
-// VOREStation Edit - New signs for us
-/obj/structure/sign/directions/command
- icon = 'icons/obj/decals_vr.dmi'
- name = "\improper Command department"
- desc = "A direction sign, pointing out which way the Command department is."
- icon_state = "direction_cmd"
+/obj/structure/sign/directions/cargo/mining
+ name = "\improper Mining Department"
+ desc = "A direction sign, pointing out the way to the Mining Department."
+ icon_state = "direction_mining"
-/obj/structure/sign/directions/elevator
- icon = 'icons/obj/decals_vr.dmi'
- name = "\improper Space Elevator"
- desc = "A direction sign, pointing out which way the Space Elevator is."
- icon_state = "direction_elv"
-// VOREStation Edit End
-
-/obj/structure/sign/directions/cryo
- name = "\improper Cryogenic Storage"
- desc = "A direction sign, pointing out which way cryogenic storage is."
- icon_state = "direction_cry"
+/obj/structure/sign/directions/cargo/refinery
+ name = "\improper Refinery"
+ desc = "A direction sign, pointing out the way to the Refinery."
+ icon_state = "direction_refinery"
+//civilian/misc signs
/obj/structure/sign/directions/roomnum
name = "room number"
desc = "A sign detailing the number of the room beside it."
icon_state = "roomnum"
+/obj/structure/sign/directions/cryo
+ name = "\improper Cryogenic Storage"
+ desc = "A direction sign, pointing out the way to Cryogenic Storage."
+ icon_state = "direction_cry"
+
+/obj/structure/sign/directions/elevator
+ name = "\improper Elevator"
+ desc = "A direction sign, pointing out the way to the nearest elevator."
+ icon_state = "direction_elv"
+
+/obj/structure/sign/directions/bar
+ name = "\improper Bar"
+ desc = "A direction sign, pointing out the way to the nearest watering hole."
+ icon_state = "direction_bar"
+
+/obj/structure/sign/directions/kitchen
+ name = "\improper Kitchen"
+ desc = "A pictographic direction sign with a knife, plate, and fork, pointing out the way to the nearest dining establishment."
+ icon_state = "direction_kitchen"
+
+/obj/structure/sign/directions/tram
+ name = "\improper Public Transit Station"
+ desc = "A direction sign, pointing out the way to the nearest public transit station."
+ icon_state = "direction_tram"
+
+/obj/structure/sign/directions/janitor
+ name = "\improper Custodial Closet"
+ desc = "A direction sign, pointing out the way to the Custodial Closet."
+ icon_state = "direction_janitor"
+
+/obj/structure/sign/directions/chapel
+ name = "\improper Chapel"
+ desc = "A direction sign, pointing out the way to the Chapel."
+ icon_state = "direction_chapel"
+
+/obj/structure/sign/directions/dorms
+ name = "\improper Dormitories"
+ desc = "A direction sign, pointing out the way to the Dormitories."
+ icon_state = "direction_dorms"
+
+/obj/structure/sign/directions/library
+ name = "\improper Library"
+ desc = "A direction sign, pointing out the way to the Library."
+ icon_state = "direction_library"
+
+/obj/structure/sign/directions/dock
+ name = "\improper Dock"
+ desc = "A direction sign, pointing out the way to the nearest docking area."
+ icon_state = "direction_dock"
+
+/obj/structure/sign/directions/gym
+ name = "\improper Gym"
+ desc = "A direction sign, pointing out the way to the Gym."
+ icon_state = "direction_gym"
+
+/obj/structure/sign/directions/pool
+ name = "\improper Pool"
+ desc = "A direction sign, pointing out the way to the Pool."
+ icon_state = "direction_pool"
+
+/obj/structure/sign/directions/recreation
+ name = "\improper Recreation Area"
+ desc = "A direction sign, pointing out the way to the nearest Recreation Area."
+ icon_state = "direction_recreation"
+
+/obj/structure/sign/directions/stairwell
+ name = "\improper Stairwell"
+ desc = "A direction sign with stairs and a door, pointing out the way to the nearest stairwell."
+ icon_state = "stairwell"
+
+/obj/structure/sign/directions/stairs_up
+ name = "\improper Stairs Up"
+ desc = "A direction sign with stairs and an upward-slanted arrow, pointing out the way to the nearest set of stairs that go up."
+ icon_state = "stairs_up"
+
+/obj/structure/sign/directions/stairs_down
+ name = "\improper Stairs Down"
+ desc = "A direction sign with stairs and a downward-slanted arrow, pointing out the way to the nearest set of stairs that go down."
+ icon_state = "stairs_down"
+
+/obj/structure/sign/directions/ladderwell
+ name = "\improper Access Shaft"
+ desc = "A direction sign with a ladder and a door, pointing out the way to the nearest access shaft."
+ icon_state = "ladderwell"
+
+/obj/structure/sign/directions/ladder_up
+ name = "\improper Ladder Up"
+ desc = "A direction sign with a ladder and an upward arrow, pointing out the way to the nearest ladder that goes up."
+ icon_state = "ladder_up"
+
+/obj/structure/sign/directions/ladder_down
+ name = "\improper Ladder Down"
+ desc = "A direction sign with a ladder and a downward arrow, pointing out the way to the nearest ladder that goes down."
+ icon_state = "ladder_down"
+
+/obj/structure/sign/directions/exit
+ name = "\improper Emergency Exit"
+ desc = "A lurid green sign that unmistakably identifies that the door it's next to as an emergency exit route."
+ icon_state = "exit_sign"
+
+//OTHER STUFF
/obj/structure/sign/christmas/lights
name = "Christmas lights"
desc = "Flashy and pretty."
diff --git a/code/game/turfs/unsimulated/beach_vr.dm b/code/game/turfs/unsimulated/beach_vr.dm
new file mode 100644
index 0000000000..be3130a72e
--- /dev/null
+++ b/code/game/turfs/unsimulated/beach_vr.dm
@@ -0,0 +1,5 @@
+/turf/simulated/floor/beach/sand/outdoors
+ outdoors = TRUE
+
+/turf/simulated/floor/beach/sand/desert/outdoors
+ outdoors = TRUE
\ No newline at end of file
diff --git a/code/modules/awaymissions/corpse.dm b/code/modules/awaymissions/corpse.dm
index 7ec68e40f6..6aa06ef4ec 100644
--- a/code/modules/awaymissions/corpse.dm
+++ b/code/modules/awaymissions/corpse.dm
@@ -23,7 +23,11 @@
var/corpseidjob = null // Needs to be in quotes, such as "Clown" or "Chef." This just determines what the ID reads as, not their access
var/corpseidaccess = null //This is for access. See access.dm for which jobs give what access. Again, put in quotes. Use "Captain" if you want it to be all access.
var/corpseidicon = null //For setting it to be a gold, silver, CentCom etc ID
- var/species = SPECIES_HUMAN
+ var/species = SPECIES_HUMAN //defaults to generic-ass humans
+ var/random_species = FALSE //flip to TRUE to randomize species from the list below
+ var/list/random_species_list = list(SPECIES_HUMAN,SPECIES_TAJ,SPECIES_UNATHI,SPECIES_SKRELL) //preset list that can be overriden downstream. only includes common humanoids for voidsuit compatibility's sake.
+// var/random_appearance = FALSE //TODO: make this work
+// var/cause_of_death = null //TODO: set up a cause-of-death system. needs to support both damage types and actual wound types, so a body can have been bitten/stabbed/clawed/shot/burned/lasered/etc. to death
delete_me = TRUE
/obj/effect/landmark/corpse/Initialize()
@@ -33,13 +37,20 @@
/obj/effect/landmark/corpse/proc/createCorpse() //Creates a mob and checks for gear in each slot before attempting to equip it.
var/mob/living/carbon/human/M = new /mob/living/carbon/human (src.loc)
- M.set_species(species)
+ if(random_species)
+ var/random_pick = pick(random_species_list)
+ M.set_species(random_pick)
+ src.species = random_pick
+ else
+ M.set_species(species)
+ //TODO: insert appearance randomization, needs to be species-based
M.real_name = src.name
M.death(1) //Kills the new mob
+ //TODO: insert cause of death handling/wound simulation here
if(src.corpseuniform)
M.equip_to_slot_or_del(new src.corpseuniform(M), slot_w_uniform)
if(src.corpsesuit)
- M.equip_to_slot_or_del(new src.corpsesuit(M), slot_wear_suit)
+ M.equip_voidsuit_to_slot_or_del_with_refit(new src.corpsesuit(M), slot_wear_suit, src.species)
if(src.corpseshoes)
M.equip_to_slot_or_del(new src.corpseshoes(M), slot_shoes)
if(src.corpsegloves)
@@ -51,7 +62,7 @@
if(src.corpsemask)
M.equip_to_slot_or_del(new src.corpsemask(M), slot_wear_mask)
if(src.corpsehelmet)
- M.equip_to_slot_or_del(new src.corpsehelmet(M), slot_head)
+ M.equip_voidhelm_to_slot_or_del_with_refit(new src.corpsehelmet(M), slot_head, src.species)
if(src.corpsebelt)
M.equip_to_slot_or_del(new src.corpsebelt(M), slot_belt)
if(src.corpsepocket1)
@@ -84,10 +95,6 @@
// I'll work on making a list of corpses people request for maps, or that I think will be commonly used. Syndicate operatives for example.
-
-
-
-
/obj/effect/landmark/corpse/syndicatesoldier
name = "Mercenary"
corpseuniform = /obj/item/clothing/under/syndicate
@@ -102,8 +109,6 @@
corpseidjob = "Operative"
corpseidaccess = "Syndicate"
-
-
/obj/effect/landmark/corpse/syndicatecommando
name = "Syndicate Commando"
corpseuniform = /obj/item/clothing/under/syndicate
@@ -119,10 +124,14 @@
corpseidjob = "Operative"
corpseidaccess = "Syndicate"
-
-
///////////Civilians//////////////////////
+/obj/effect/landmark/corpse/random_civ
+ name = "Civilian"
+ corpseuniform = /obj/item/clothing/under/color/grey
+ corpseshoes = /obj/item/clothing/shoes/black
+ random_species = TRUE
+
/obj/effect/landmark/corpse/chef
name = "Chef"
corpseuniform = /obj/item/clothing/under/rank/chef
@@ -165,6 +174,7 @@
corpsesuit = /obj/item/clothing/suit/space/void/engineering
corpsemask = /obj/item/clothing/mask/breath
corpsehelmet = /obj/item/clothing/head/helmet/space/void/engineering
+ corpseback = /obj/item/weapon/tank/oxygen
/obj/effect/landmark/corpse/clown
name = "Clown"
@@ -188,6 +198,39 @@
corpseid = 1
corpseidjob = "Scientist"
corpseidaccess = "Scientist"
+
+/obj/effect/landmark/corpse/security
+ name = "Security Officer"
+ corpseradio = /obj/item/device/radio/headset/headset_sec
+ corpseuniform = /obj/item/clothing/under/rank/security
+ corpsesuit = /obj/item/clothing/suit/armor/vest
+ corpseback = /obj/item/weapon/storage/backpack/security
+ corpseshoes = /obj/item/clothing/shoes/boots/jackboots
+ corpseglasses = /obj/item/clothing/glasses/sunglasses/sechud
+ corpsegloves = /obj/item/clothing/gloves/black
+ corpsehelmet = /obj/item/clothing/head/helmet
+ corpseid = 1
+ corpseidjob = "Security Officer"
+ corpseidaccess = "Security Officer"
+
+/obj/effect/landmark/corpse/security/rig
+ corpsesuit = /obj/item/clothing/suit/space/void/security
+ corpsemask = /obj/item/clothing/mask/breath
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/security
+ corpseback = /obj/item/weapon/tank/jetpack/oxygen
+
+/obj/effect/landmark/corpse/security/rig/eva
+ corpsesuit = /obj/item/clothing/suit/space/void/security/alt
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/security/alt
+ corpseidjob = "Starship Security Officer"
+
+/obj/effect/landmark/corpse/prisoner
+ name = "Unknown Prisoner"
+ corpseuniform = /obj/item/clothing/under/color/prison
+ corpseshoes = /obj/item/clothing/shoes/orange
+ corpseid = 1
+ corpseidjob = "Prisoner"
+ random_species = TRUE
/obj/effect/landmark/corpse/miner
corpseradio = /obj/item/device/radio/headset/headset_cargo
@@ -203,17 +246,69 @@
corpsesuit = /obj/item/clothing/suit/space/void/mining
corpsemask = /obj/item/clothing/mask/breath
corpsehelmet = /obj/item/clothing/head/helmet/space/void/mining
+ corpseback = /obj/item/weapon/tank/oxygen
-//VORESTATION ADD START
+/////////////////Vintage//////////////////////
+
+//define the basic props at this level and only change specifics for variants, e.z.
+/obj/effect/landmark/corpse/vintage
+ name = "Unknown Crewmate"
+ corpseuniform = /obj/item/clothing/under/utility
+ corpsesuit = /obj/item/clothing/suit/space/void/refurb
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb
+ corpsemask = /obj/item/clothing/mask/breath
+ corpseback = /obj/item/weapon/tank/oxygen
+ corpseid = 1
+ corpseidjob = "Crewmate"
+
+/obj/effect/landmark/corpse/vintage/engineering
+ name = "Unknown Engineer"
+ corpsesuit = /obj/item/clothing/suit/space/void/refurb/engineering
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/engineering
+ corpsebelt = /obj/item/weapon/storage/belt/utility/full
+ corpseback = /obj/item/weapon/tank/oxygen/yellow
+ corpseidjob = "Engineer"
+
+/obj/effect/landmark/corpse/vintage/marine
+ name = "Unknown Marine"
+ corpsesuit = /obj/item/clothing/suit/space/void/refurb/marine
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/marine
+ corpsebelt = /obj/item/weapon/storage/belt/security/tactical
+ corpseidjob = "Marine"
+
+/obj/effect/landmark/corpse/vintage/medical
+ name = "Unknown Medic"
+ corpsesuit = /obj/item/clothing/suit/space/void/refurb/medical
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/medical
+ corpsebelt = /obj/item/weapon/storage/belt/medical
+ corpseidjob = "Medic"
+
+/obj/effect/landmark/corpse/vintage/mercenary
+ name = "Unknown Mercenary"
+ corpsesuit = /obj/item/clothing/suit/space/void/refurb/mercenary
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/mercenary
+ corpsebelt = /obj/item/weapon/storage/belt/security/tactical
+ corpseback = /obj/item/weapon/tank/oxygen/red
+ corpseidjob = "Mercenary"
+
+/obj/effect/landmark/corpse/vintage/officer
+ name = "Unknown Captain"
+ corpsesuit = /obj/item/clothing/suit/space/void/refurb/officer
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/officer
+ corpseback = /obj/item/weapon/tank/oxygen/yellow
+ corpseidjob = "Captain"
+
/obj/effect/landmark/corpse/vintage/pilot
- name = "Pilot"
- corpseuniform = /obj/item/clothing/under/color/grey
+ name = "Unknown Pilot"
corpsesuit = /obj/item/clothing/suit/space/void/refurb/pilot
corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/pilot
- corpseshoes = /obj/item/clothing/shoes/black
- corpseback = /obj/item/weapon/tank/oxygen
-//VORESTATION ADD END
+ corpseidjob = "Pilot"
+/obj/effect/landmark/corpse/vintage/research
+ name = "Unknown Researcher"
+ corpsesuit = /obj/item/clothing/suit/space/void/refurb/research
+ corpsehelmet = /obj/item/clothing/head/helmet/space/void/refurb/research
+ corpseidjob = "Researcher"
/////////////////Officers//////////////////////
diff --git a/code/modules/blob2/blobs/base_blob.dm b/code/modules/blob2/blobs/base_blob.dm
index 71d4ad7254..f9c9eb588d 100644
--- a/code/modules/blob2/blobs/base_blob.dm
+++ b/code/modules/blob2/blobs/base_blob.dm
@@ -22,6 +22,7 @@ GLOBAL_LIST_EMPTY(all_blobs)
/obj/structure/blob/Initialize(newloc, new_overmind)
if(new_overmind)
overmind = new_overmind
+ faction = overmind.blob_type.faction
update_icon()
if(!integrity)
integrity = max_integrity
@@ -47,19 +48,24 @@ GLOBAL_LIST_EMPTY(all_blobs)
color = null
set_light(0)
+/obj/structure/blob/update_transform()
+ var/matrix/M = matrix()
+ M.Scale(icon_scale_x, icon_scale_y)
+ animate(src, transform = M, time = 10)
+
// Blob tiles are not actually dense so we need Special Code(tm).
/obj/structure/blob/CanPass(atom/movable/mover, turf/target)
if(istype(mover) && mover.checkpass(PASSBLOB))
return TRUE
else if(istype(mover, /mob/living))
var/mob/living/L = mover
- if(L.faction == "blob")
+ if(L.faction == faction)
return TRUE
else if(istype(mover, /obj/item/projectile))
var/obj/item/projectile/P = mover
if(istype(P.firer, /obj/structure/blob))
return TRUE
- if(istype(P.firer) && P.firer.faction == "blob")
+ if(istype(P.firer) && P.firer.faction == faction)
return TRUE
return FALSE
@@ -88,6 +94,7 @@ GLOBAL_LIST_EMPTY(all_blobs)
update_icon()
pulse_timestamp = world.time + 1 SECOND
if(overmind)
+ faction = overmind.blob_type.faction
overmind.blob_type.on_pulse(src)
return TRUE //we did it, we were pulsed!
return FALSE //oh no we failed
@@ -106,6 +113,9 @@ GLOBAL_LIST_EMPTY(all_blobs)
for(var/L in blobs_to_affect)
var/obj/structure/blob/B = L
+ if(B.faction != faction)
+ continue
+
if(!B.overmind && !istype(B, /obj/structure/blob/core) && prob(30))
B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
B.update_icon()
@@ -138,7 +148,8 @@ GLOBAL_LIST_EMPTY(all_blobs)
var/dirn = pick(dirs)
dirs.Remove(dirn)
T = get_step(src, dirn)
- if(!(locate(/obj/structure/blob) in T))
+ var/obj/structure/blob/B = locate(/obj/structure/blob) in T
+ if(!B || B.faction != faction) // Allow opposing blobs to fight.
break
else
T = null
@@ -164,6 +175,7 @@ GLOBAL_LIST_EMPTY(all_blobs)
if(make_blob) //well, can we?
var/obj/structure/blob/B = new /obj/structure/blob/normal(src.loc)
+ B.faction = faction
if(controller)
B.overmind = controller
else
@@ -340,7 +352,7 @@ GLOBAL_LIST_EMPTY(all_blobs)
if(!P)
return
- if(istype(P.firer) && P.firer.faction == "blob")
+ if(istype(P.firer) && P.firer.faction == faction)
return
var/damage = P.get_structure_damage() // So tasers don't hurt the blob.
@@ -366,6 +378,31 @@ GLOBAL_LIST_EMPTY(all_blobs)
if(overmind)
overmind.blob_type.on_water(src, amount)
+/obj/structure/blob/blob_act(var/obj/structure/blob/B)
+ . = ..()
+
+ if(B)
+
+ if(!B.overmind)
+ return
+
+ if(B.faction != faction)
+ var/damage = rand(B.overmind.blob_type.damage_lower, B.overmind.blob_type.damage_upper)
+ var/inc_damage_type = B.overmind.blob_type.damage_type
+
+ if(overmind)
+ damage = overmind.blob_type.on_received_damage(src, damage, inc_damage_type, B)
+
+ else
+ faction = B.faction
+ overmind = B.overmind
+ update_icon()
+ return
+
+ adjust_integrity(-1 * damage)
+
+ return
+
/obj/structure/blob/proc/adjust_integrity(amount)
integrity = between(0, integrity + amount, max_integrity)
if(integrity == 0)
diff --git a/code/modules/blob2/blobs/factory.dm b/code/modules/blob2/blobs/factory.dm
index f9415a6e66..464f89f760 100644
--- a/code/modules/blob2/blobs/factory.dm
+++ b/code/modules/blob2/blobs/factory.dm
@@ -35,7 +35,7 @@
var/mob/living/simple_mob/blob/spore/S = null
if(overmind)
S = new overmind.blob_type.spore_type(src.loc, src)
- S.faction = "blob"
+ S.faction = overmind.blob_type.faction
if(istype(S))
S.overmind = overmind
overmind.blob_mobs.Add(S)
diff --git a/code/modules/blob2/blobs/normal.dm b/code/modules/blob2/blobs/normal.dm
index a6e841d4dd..cf25ff5c1b 100644
--- a/code/modules/blob2/blobs/normal.dm
+++ b/code/modules/blob2/blobs/normal.dm
@@ -19,4 +19,13 @@
if(overmind)
name = "[overmind.blob_type.name]"
else
- name = "inert [base_name]"
\ No newline at end of file
+ name = "inert [base_name]"
+
+/obj/structure/blob/normal/pulsed()
+ ..()
+
+ if(prob(30))
+ adjust_scale((rand(10, 13) / 10), (rand(10, 13) / 10))
+
+ else
+ adjust_scale(1)
diff --git a/code/modules/blob2/core_chunk.dm b/code/modules/blob2/core_chunk.dm
index 7d8f588568..4abc06843e 100644
--- a/code/modules/blob2/core_chunk.dm
+++ b/code/modules/blob2/core_chunk.dm
@@ -5,6 +5,7 @@
description_info = "Some blob types will have core effects when the chunk is used in-hand, toggled with an alt click, or constantly active."
icon = 'icons/mob/blob.dmi'
icon_state = "blobcore"
+ flags = OPENCONTAINER
var/datum/blob_type/blob_type // The blob type this dropped from.
var/active_ability_cooldown = 20 SECONDS
@@ -15,11 +16,17 @@
var/passive_ability_cooldown = 5 SECONDS
var/last_passive_use = 0
+ var/can_genesis = TRUE // Can the core chunk be used to grow a new blob?
+
drop_sound = 'sound/effects/slime_squish.ogg'
+/obj/item/weapon/blobcore_chunk/is_open_container()
+ return 1
+
/obj/item/weapon/blobcore_chunk/New(var/atom/newloc, var/datum/blob_type/parentblob = null)
..(newloc)
+ create_reagents(120)
setup_blobtype(parentblob)
/obj/item/weapon/blobcore_chunk/Destroy()
@@ -91,10 +98,57 @@
blob_type.on_chunk_tick(src)
/obj/item/weapon/blobcore_chunk/AltClick(mob/living/carbon/user)
- if(blob_type &&blob_type.chunk_active_type == BLOB_CHUNK_TOGGLE)
+ if(blob_type && blob_type.chunk_active_type == BLOB_CHUNK_TOGGLE)
should_tick = !should_tick
if(should_tick)
to_chat(user, "\The [src] shudders with life.")
else
to_chat(user, "\The [src] stills, returning to a death-like state.")
+
+/obj/item/weapon/blobcore_chunk/proc/regen(var/newfaction = null)
+ if(istype(blob_type))
+ if(newfaction)
+ blob_type.faction = newfaction
+
+ var/obj/structure/blob/core/NC = new (get_turf(src))
+ NC.overmind.blob_type = blob_type
+ NC.overmind.blob_core.update_icon()
+ return TRUE
+
+ return FALSE
+
+/datum/chemical_reaction/blob_reconstitution
+ name = "Hostile Blob Revival"
+ id = "blob_revival"
+ result = null
+ required_reagents = list("phoron" = 60)
+ result_amount = 1
+
+/datum/chemical_reaction/blob_reconstitution/can_happen(var/datum/reagents/holder)
+ if(holder.my_atom && istype(holder.my_atom, /obj/item/weapon/blobcore_chunk))
+ return ..()
+ return FALSE
+
+/datum/chemical_reaction/blob_reconstitution/on_reaction(var/datum/reagents/holder)
+ var/obj/item/weapon/blobcore_chunk/chunk = holder.my_atom
+ if(chunk.can_genesis && chunk.regen())
+ chunk.visible_message("[chunk] bubbles, surrounding itself with a rapidly expanding mass of [chunk.blob_type.name]!")
+ chunk.can_genesis = FALSE
+ else
+ chunk.visible_message("[chunk] shifts strangely, but falls still.")
+
+/datum/chemical_reaction/blob_reconstitution/domination
+ name = "Allied Blob Revival"
+ id = "blob_friend"
+ result = null
+ required_reagents = list("hydrophoron" = 40, "peridaxon" = 20, "mutagen" = 20)
+ result_amount = 1
+
+/datum/chemical_reaction/blob_reconstitution/domination/on_reaction(var/datum/reagents/holder)
+ var/obj/item/weapon/blobcore_chunk/chunk = holder.my_atom
+ if(chunk.can_genesis && chunk.regen("neutral"))
+ chunk.visible_message("[chunk] bubbles, surrounding itself with a rapidly expanding mass of [chunk.blob_type.name]!")
+ chunk.can_genesis = FALSE
+ else
+ chunk.visible_message("[chunk] shifts strangely, but falls still.")
diff --git a/code/modules/blob2/overmind/overmind.dm b/code/modules/blob2/overmind/overmind.dm
index ffcd347dd8..6df0cba629 100644
--- a/code/modules/blob2/overmind/overmind.dm
+++ b/code/modules/blob2/overmind/overmind.dm
@@ -23,6 +23,14 @@ var/list/overminds = list()
var/ai_controlled = TRUE
var/auto_pilot = FALSE // If true, and if a client is attached, the AI routine will continue running.
+ universal_understand = TRUE
+
+ var/list/has_langs = list(LANGUAGE_BLOB)
+ var/datum/language/default_language = null
+
+/mob/observer/blob/get_default_language()
+ return default_language
+
/mob/observer/blob/Initialize(newloc, pre_placed = 0, starting_points = 60, desired_blob_type = null)
blob_points = starting_points
if(pre_placed) //we already have a core!
@@ -41,6 +49,11 @@ var/list/overminds = list()
if(blob_core)
blob_core.update_icon()
+ for(var/L in has_langs)
+ languages |= GLOB.all_languages[L]
+ if(languages.len)
+ default_language = languages[1]
+
return ..(newloc)
/mob/observer/blob/Destroy()
@@ -67,9 +80,9 @@ var/list/overminds = list()
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
stat(null, "Total Blobs: [GLOB.all_blobs.len]")
-/mob/observer/blob/Move(NewLoc, Dir = 0)
+/mob/observer/blob/Move(var/atom/NewLoc, Dir = 0)
if(placed)
- var/obj/structure/blob/B = locate() in range("3x3", NewLoc)
+ var/obj/structure/blob/B = (locate() in view("5x5", NewLoc))
if(B)
forceMove(NewLoc)
return TRUE
@@ -93,3 +106,62 @@ var/list/overminds = list()
if(blob_points >= 100)
if(!auto_factory() && !auto_resource())
auto_node()
+
+/mob/observer/blob/say(var/message, var/datum/language/speaking = null, var/whispering = 0)
+ message = sanitize(message)
+
+ if(!message)
+ return
+
+ //If you're muted for IC chat
+ if(client)
+ if(message)
+ client.handle_spam_prevention(MUTE_IC)
+ if((client.prefs.muted & MUTE_IC) || say_disabled)
+ to_chat(src, "You cannot speak in IC (Muted).")
+ return
+
+ //These will contain the main receivers of the message
+ var/list/listening = list()
+ var/list/listening_obj = list()
+
+ var/turf/T = get_turf(src)
+ if(T)
+ //Obtain the mobs and objects in the message range
+ var/list/results = get_mobs_and_objs_in_view_fast(T, world.view, remote_ghosts = client ? TRUE : FALSE)
+ listening = results["mobs"]
+ listening_obj = results["objs"]
+ else
+ return 1 //If we're in nullspace, then forget it.
+
+ var/list/message_pieces = parse_languages(message)
+ if(istype(message_pieces, /datum/multilingual_say_piece)) // Little quirk for dealing with hivemind/signlang languages.
+ var/datum/multilingual_say_piece/S = message_pieces // Yay for BYOND's hilariously broken typecasting for allowing us to do this.
+ S.speaking.broadcast(src, S.message)
+ return 1
+
+ if(!LAZYLEN(message_pieces))
+ log_runtime(EXCEPTION("Message failed to generate pieces. [message] - [json_encode(message_pieces)]"))
+ return 0
+
+ //Handle nonverbal languages here
+ for(var/datum/multilingual_say_piece/S in message_pieces)
+ if(S.speaking.flags & NONVERBAL)
+ custom_emote(1, "[pick(S.speaking.signlang_verb)].")
+
+ for(var/mob/M in listening)
+ spawn()
+ if(M && src)
+ if(get_dist(M, src) <= world.view || (M.stat == DEAD && !forbid_seeing_deadchat))
+ M.hear_say(message_pieces, "conveys", (M.faction == blob_type.faction), src)
+
+ //Object message delivery
+ for(var/obj/O in listening_obj)
+ spawn(0)
+ if(O && src) //If we still exist, when the spawn processes
+ var/dst = get_dist(get_turf(O),get_turf(src))
+ if(dst <= world.view)
+ O.hear_talk(src, message_pieces, "conveys")
+
+ log_say(message, src)
+ return 1
diff --git a/code/modules/blob2/overmind/powers.dm b/code/modules/blob2/overmind/powers.dm
index f5727e94bf..4177bac991 100644
--- a/code/modules/blob2/overmind/powers.dm
+++ b/code/modules/blob2/overmind/powers.dm
@@ -80,8 +80,8 @@
potential_blobs -= temp // Don't take up the core's shield spot.
else if(!istype(temp, /obj/structure/blob/normal))
potential_blobs -= temp // Not a normal blob.
- else if(temp.overmind != src)
- potential_blobs -= temp // Not our blob.
+ else if(temp.overmind != src || temp.overmind.blob_type.faction != blob_type.faction)
+ potential_blobs -= temp // Not our blob, or even an ally.
else
B = temp
break
@@ -123,8 +123,8 @@
potential_blobs -= temp // Don't take up the core's shield spot.
else if(!istype(temp, /obj/structure/blob/normal))
potential_blobs -= temp // Not a normal blob.
- else if(temp.overmind != src)
- potential_blobs -= temp // Not our blob.
+ else if(temp.overmind != src || temp.overmind.blob_type.faction != blob_type.faction)
+ potential_blobs -= temp // Not our blob, or even an ally
else
B = temp
break
@@ -165,8 +165,8 @@
potential_blobs -= temp
else if(!istype(temp, /obj/structure/blob/normal))
potential_blobs -= temp
- else if(temp.overmind != src)
- potential_blobs -= temp // Not our blob.
+ else if(temp.overmind != src || temp.overmind.blob_type.faction != blob_type.faction)
+ potential_blobs -= temp // Not our blob, or even our ally.
else
B = temp
break
@@ -216,7 +216,7 @@
for(var/mob/living/L in view(src))
if(L.stat == DEAD)
continue // Already dying or dead.
- if(L.faction == "blob")
+ if(L.faction == blob_type.faction)
continue // No friendly fire.
if(locate(/obj/structure/blob) in L.loc)
continue // Already has a blob over them.
diff --git a/code/modules/blob2/overmind/types.dm b/code/modules/blob2/overmind/types.dm
index 79df541aae..8fffbcdbca 100644
--- a/code/modules/blob2/overmind/types.dm
+++ b/code/modules/blob2/overmind/types.dm
@@ -9,6 +9,8 @@
var/color = "#FFFFFF" // The actual blob's color.
var/complementary_color = "#000000" //a color that's complementary to the normal blob color. Blob mobs are colored in this.
+ var/faction = "blob" // The blob's faction.
+
var/attack_message = "The blob attacks you" // Base message the mob gets when blob_act() gets called on them by the blob. An exclaimation point is added to the end.
var/attack_message_living = null // Appended to attack_message, if the target fails isSynthetic() check.
var/attack_message_synth = null // Ditto, but if they pass isSynthetic().
diff --git a/code/modules/blob2/overmind/types/blazing_oil.dm b/code/modules/blob2/overmind/types/blazing_oil.dm
index 094083e778..7d8c9f307d 100644
--- a/code/modules/blob2/overmind/types/blazing_oil.dm
+++ b/code/modules/blob2/overmind/types/blazing_oil.dm
@@ -33,6 +33,8 @@
env.add_thermal_energy(10 * 1000)
/datum/blob_type/blazing_oil/on_chunk_tick(obj/item/weapon/blobcore_chunk/B)
+ B.reagents.add_reagent("thermite_v", 0.5)
+
var/turf/T = get_turf(B)
if(!T)
return
diff --git a/code/modules/blob2/overmind/types/cryogenic_goo.dm b/code/modules/blob2/overmind/types/cryogenic_goo.dm
index 32684ed58f..564516a71d 100644
--- a/code/modules/blob2/overmind/types/cryogenic_goo.dm
+++ b/code/modules/blob2/overmind/types/cryogenic_goo.dm
@@ -46,6 +46,8 @@
env.add_thermal_energy(-10 * 1000)
/datum/blob_type/cryogenic_goo/on_chunk_tick(obj/item/weapon/blobcore_chunk/B)
+ B.reagents.add_reagent("cryoslurry", 0.5)
+
var/turf/simulated/T = get_turf(B)
if(!istype(T))
return
diff --git a/code/modules/blob2/overmind/types/ectoplasmic_horror.dm b/code/modules/blob2/overmind/types/ectoplasmic_horror.dm
index f7689847cd..a528ce8388 100644
--- a/code/modules/blob2/overmind/types/ectoplasmic_horror.dm
+++ b/code/modules/blob2/overmind/types/ectoplasmic_horror.dm
@@ -35,7 +35,7 @@
listclearnulls(active_beams)
var/atom/movable/beam_origin = B
for(var/mob/living/L in oview(world.view, B))
- if(L.stat == DEAD || L.faction == "blob")
+ if(L.stat == DEAD || L.faction == faction)
continue
if(prob(5))
var/beamtarget_exists = FALSE
@@ -82,7 +82,7 @@
if(nearby_mobs.len)
listclearnulls(active_beams)
for(var/mob/living/L in nearby_mobs)
- if(L.stat == DEAD || L.faction == "blob")
+ if(L.stat == DEAD || L.faction == faction)
continue
if(prob(5))
var/beamtarget_exists = FALSE
diff --git a/code/modules/blob2/overmind/types/explosive_lattice.dm b/code/modules/blob2/overmind/types/explosive_lattice.dm
index d0484c3725..aec6787eab 100644
--- a/code/modules/blob2/overmind/types/explosive_lattice.dm
+++ b/code/modules/blob2/overmind/types/explosive_lattice.dm
@@ -30,7 +30,7 @@
for(var/mob/living/L in range(get_turf(victim), 1)) // We don't use orange(), in case there is more than one mob on the target tile.
if(L == victim) // Already hit.
continue
- if(L.faction == "blob") // No friendly fire
+ if(L.faction == faction) // No friendly fire
continue
L.blob_act()
diff --git a/code/modules/blob2/overmind/types/fulminant_organism.dm b/code/modules/blob2/overmind/types/fulminant_organism.dm
index 2d940f9359..4f3788b787 100644
--- a/code/modules/blob2/overmind/types/fulminant_organism.dm
+++ b/code/modules/blob2/overmind/types/fulminant_organism.dm
@@ -24,9 +24,10 @@
var/mob/living/simple_mob/blob/spore/S = new spore_type(T)
if(istype(S))
S.overmind = O
+ S.faction = faction
O.blob_mobs.Add(S)
else
- S.faction = "blob"
+ S.faction = faction
S.update_icons()
/datum/blob_type/fulminant_organism/on_death(obj/structure/blob/B)
@@ -35,9 +36,10 @@
B.visible_message("\The [S] floats free from the [name]!")
if(istype(S))
S.overmind = B.overmind
+ S.faction = faction
B.overmind.blob_mobs.Add(S)
else
- S.faction = "blob"
+ S.faction = faction
S.update_icons()
/datum/blob_type/fulminant_organism/on_chunk_use(obj/item/weapon/blobcore_chunk/B, mob/living/user)
diff --git a/code/modules/blob2/overmind/types/grey_goo.dm b/code/modules/blob2/overmind/types/grey_goo.dm
index 5c93fb46c8..558f624e96 100644
--- a/code/modules/blob2/overmind/types/grey_goo.dm
+++ b/code/modules/blob2/overmind/types/grey_goo.dm
@@ -14,6 +14,7 @@
attack_message = "The tide tries to swallow you"
attack_message_living = ", and you feel your skin dissolve"
attack_message_synth = ", and your external plating dissolves"
+ faction = "nanomachines"
/datum/blob_type/grey_goo/on_emp(obj/structure/blob/B, severity)
B.adjust_integrity(-(20 / severity))
diff --git a/code/modules/blob2/overmind/types/roiling_mold.dm b/code/modules/blob2/overmind/types/roiling_mold.dm
index e909d06086..4594ad1c9f 100644
--- a/code/modules/blob2/overmind/types/roiling_mold.dm
+++ b/code/modules/blob2/overmind/types/roiling_mold.dm
@@ -44,7 +44,7 @@
if(!istype(L))
return
- if(istype(B, /obj/structure/blob/factory) && L.stat != DEAD && prob(ai_aggressiveness) && L.faction != "blob")
+ if(istype(B, /obj/structure/blob/factory) && L.stat != DEAD && prob(ai_aggressiveness) && L.faction != faction)
var/obj/item/projectile/arc/spore/P = new(get_turf(B))
P.launch_projectile(L, BP_TORSO, B)
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index 548ceaa74d..f6ef8a5c28 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -228,7 +228,6 @@
if(!void)
void = new()
- void.MakeGreed()
screen += void
if((prefs.lastchangelog != changelog_hash) && isnewplayer(src.mob)) //bolds the changelog button on the interface so we know there are updates.
diff --git a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm
index ce6ada61ab..2aebfd668f 100644
--- a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm
@@ -18,4 +18,28 @@
/datum/gear/head/detective_alt
display_name = "cyberscope headgear, detective"
path = /obj/item/clothing/head/helmet/detective_alt
- allowed_roles = list("Head of Security", "Detective")
\ No newline at end of file
+ allowed_roles = list("Head of Security", "Detective")
+
+/datum/gear/head/bearpelt
+ display_name = "brown bear pelt"
+ path = /obj/item/clothing/head/pelt
+
+/datum/gear/head/wolfpelt
+ display_name = "brown wolf pelt"
+ path = /obj/item/clothing/head/pelt/wolfpelt
+
+/datum/gear/head/wolfpeltblack
+ display_name = "black wolf pelt"
+ path = /obj/item/clothing/head/pelt/wolfpeltblack
+
+/datum/gear/head/tigerpelt
+ display_name = "shiny tiger pelt"
+ path = /obj/item/clothing/head/pelt/tigerpelt
+
+/datum/gear/head/tigerpeltsnow
+ display_name = "snow tiger pelt"
+ path = /obj/item/clothing/head/pelt/tigerpeltsnow
+
+/datum/gear/head/tigerpeltpink
+ display_name = "pink tiger pelt"
+ path = /obj/item/clothing/head/pelt/tigerpeltpink
\ No newline at end of file
diff --git a/code/modules/client/preference_setup/vore/07_traits.dm b/code/modules/client/preference_setup/vore/07_traits.dm
index e653166b7a..ecaaa840cf 100644
--- a/code/modules/client/preference_setup/vore/07_traits.dm
+++ b/code/modules/client/preference_setup/vore/07_traits.dm
@@ -58,21 +58,21 @@
if(pref.species != SPECIES_CUSTOM)
pref.pos_traits.Cut()
- pref.neu_traits.Cut()
pref.neg_traits.Cut()
- else
- // Clean up positive traits
- for(var/path in pref.pos_traits)
- if(!(path in positive_traits))
- pref.pos_traits -= path
- //Neutral traits
- for(var/path in pref.neu_traits)
- if(!(path in neutral_traits))
- pref.neu_traits -= path
- //Negative traits
- for(var/path in pref.neg_traits)
- if(!(path in negative_traits))
- pref.neg_traits -= path
+ // Clean up positive traits
+ for(var/path in pref.pos_traits)
+ if(!(path in positive_traits))
+ pref.pos_traits -= path
+ //Neutral traits
+ for(var/path in pref.neu_traits)
+ if(!(path in neutral_traits))
+ pref.neu_traits -= path
+ if(!(pref.species == SPECIES_CUSTOM) && !(path in everyone_traits))
+ pref.neu_traits -= path
+ //Negative traits
+ for(var/path in pref.neg_traits)
+ if(!(path in negative_traits))
+ pref.neg_traits -= path
var/datum/species/selected_species = GLOB.all_species[pref.species]
if(selected_species.selects_bodytype)
@@ -82,23 +82,27 @@
/datum/category_item/player_setup_item/vore/traits/copy_to_mob(var/mob/living/carbon/human/character)
character.custom_species = pref.custom_species
- var/datum/species/selected_species = GLOB.all_species[pref.species]
if(character.isSynthetic()) //Checking if we have a synth on our hands, boys.
pref.dirty_synth = 1
- if(selected_species.selects_bodytype)
- var/datum/species/custom/CS = character.species
- var/S = pref.custom_base ? pref.custom_base : "Human"
- var/datum/species/custom/new_CS = CS.produceCopy(S, pref.pos_traits + pref.neu_traits + pref.neg_traits, character)
+ var/datum/species/S = character.species
+ var/SB
+ if(S.selects_bodytype)
+ SB = pref.custom_base ? pref.custom_base : "Human"
+ else
+ SB = S.name
+ var/datum/species/new_S = S.produceCopy(SB, pref.pos_traits + pref.neu_traits + pref.neg_traits, character)
- //Any additional non-trait settings can be applied here
- new_CS.blood_color = pref.blood_color
+ //Any additional non-trait settings can be applied here
+ new_S.blood_color = pref.blood_color
+
+ if(pref.species == SPECIES_CUSTOM)
+ //Statistics for this would be nice
+ var/english_traits = english_list(new_S.traits, and_text = ";", comma_text = ";")
+ log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet
+ else
- if(pref.species == SPECIES_CUSTOM)
- //Statistics for this would be nice
- var/english_traits = english_list(new_CS.traits, and_text = ";", comma_text = ";")
- log_game("TRAITS [pref.client_ckey]/([character]) with: [english_traits]") //Terrible 'fake' key_name()... but they aren't in the same entity yet
/datum/category_item/player_setup_item/vore/traits/content(var/mob/user)
. += "Custom Species Name: "
@@ -109,15 +113,16 @@
. += "Icon Base: "
. += "[pref.custom_base ? pref.custom_base : "Human"]
"
+ var/traits_left = pref.max_traits
+ . += "Traits Left: [traits_left]
"
if(pref.species == SPECIES_CUSTOM)
var/points_left = pref.starting_trait_points
- var/traits_left = pref.max_traits
+
for(var/T in pref.pos_traits + pref.neg_traits)
points_left -= traits_costs[T]
traits_left--
. += "Points Left: [points_left]
"
- . += "Traits Left: [traits_left]
"
if(points_left < 0 || traits_left < 0 || !pref.custom_species)
. += "^ Fix things! ^
"
@@ -128,19 +133,18 @@
. += "