Merge branch 'master' of https://github.com/PolarisSS13/Polaris into parachute

# Conflicts:
#	icons/mob/back.dmi
#	icons/obj/storage.dmi
This commit is contained in:
Anewbe
2017-12-01 18:47:06 -06:00
171 changed files with 13871 additions and 11240 deletions
+6
View File
@@ -2,3 +2,9 @@
name = mapmerge driver
driver = ./mapmerge.sh %O %A %B
recursive = text
[merge "merge-dmi"]
name = iconfile merge driver
driver = ./tools/dmitool/dmimerge.sh %O %A %B
[merge "merge-dmm"]
name = mapmerge driver
driver = ./tools/mapmerge/mapmerge.sh %O %A %B
+15 -10
View File
@@ -76,7 +76,8 @@ obj/var/phoronproof = 0
suit_contamination()
if(!pl_head_protected())
if(prob(1)) suit_contamination() //Phoron can sometimes get through such an open suit.
if(prob(1))
suit_contamination() //Phoron can sometimes get through such an open suit.
//Cannot wash backpacks currently.
// if(istype(back,/obj/item/weapon/storage/backpack))
@@ -88,7 +89,8 @@ obj/var/phoronproof = 0
//Handles all the bad things phoron can do.
//Contamination
if(vsc.plc.CLOTH_CONTAMINATION) contaminate()
if(vsc.plc.CLOTH_CONTAMINATION)
contaminate()
//Anything else requires them to not be dead.
if(stat >= 2)
@@ -139,22 +141,22 @@ obj/var/phoronproof = 0
Blind(20)
/mob/living/carbon/human/proc/pl_head_protected()
//Checks if the head is adequately sealed.
//Checks if the head is adequately sealed. //This is just odd. TODO: Make this respect the body_parts_covered stuff like thermal gear does.
if(head)
if(vsc.plc.PHORONGUARD_ONLY)
if(head.flags & PHORONGUARD)
if(head.flags & PHORONGUARD || head.phoronproof)
return 1
else if(head.body_parts_covered & EYES)
return 1
return 0
/mob/living/carbon/human/proc/pl_suit_protected()
//Checks if the suit is adequately sealed.
//Checks if the suit is adequately sealed. //This is just odd. TODO: Make this respect the body_parts_covered stuff like thermal gear does.
var/coverage = 0
for(var/obj/item/protection in list(wear_suit, gloves, shoes))
for(var/obj/item/protection in list(wear_suit, gloves, shoes)) //This is why it's odd. If I'm in a full suit, but my shoes and gloves aren't phoron proof, damage.
if(!protection)
continue
if(vsc.plc.PHORONGUARD_ONLY && !(protection.flags & PHORONGUARD))
if(vsc.plc.PHORONGUARD_ONLY && !(protection.flags & PHORONGUARD) && !protection.phoronproof)
return 0
coverage |= protection.body_parts_covered
@@ -165,9 +167,12 @@ obj/var/phoronproof = 0
/mob/living/carbon/human/proc/suit_contamination()
//Runs over the things that can be contaminated and does so.
if(w_uniform) w_uniform.contaminate()
if(shoes) shoes.contaminate()
if(gloves) gloves.contaminate()
if(w_uniform)
w_uniform.contaminate()
if(shoes)
shoes.contaminate()
if(gloves)
gloves.contaminate()
turf/Entered(obj/item/I)
+1 -1
View File
@@ -28,7 +28,7 @@ Notes for people who used ZAS before:
*/
//#define ZASDBG
//#define MULTIZAS
#define MULTIZAS
#define AIR_BLOCKED 1
#define ZONE_BLOCKED 2
#define BLOCKED 3
+1
View File
@@ -57,6 +57,7 @@
#define PASSTABLE 0x1
#define PASSGLASS 0x2
#define PASSGRILLE 0x4
#define PASSBLOB 0x8
// Bitmasks for the flags_inv variable. These determine when a piece of clothing hides another, i.e. a helmet hiding glasses.
// WARNING: The following flags apply only to the external suit!
+8
View File
@@ -164,6 +164,14 @@ proc/listclearnulls(list/list)
L.Swap(i, rand(i,L.len))
return L
//same, but returns nothing and acts on list in place
/proc/shuffle_inplace(list/L)
if(!L)
return
for(var/i=1, i<L.len, ++i)
L.Swap(i,rand(i,L.len))
//Return a list with no duplicate entries
/proc/uniquelist(var/list/L)
. = list()
+30
View File
@@ -7,6 +7,13 @@
//Checks if all high bits in req_mask are set in bitfield
#define BIT_TEST_ALL(bitfield, req_mask) ((~(bitfield) & (req_mask)) == 0)
//supposedly the fastest way to do this according to https://gist.github.com/Giacom/be635398926bb463b42a
#define RANGE_TURFS(RADIUS, CENTER) \
block( \
locate(max(CENTER.x-(RADIUS),1), max(CENTER.y-(RADIUS),1), CENTER.z), \
locate(min(CENTER.x+(RADIUS),world.maxx), min(CENTER.y+(RADIUS),world.maxy), CENTER.z) \
)
//Inverts the colour of an HTML string
/proc/invertHTML(HTMLstring)
@@ -476,6 +483,8 @@ Turf and target are seperate in case you want to teleport some distance from a t
var/list/sortmob = sortAtom(mob_list)
for(var/mob/observer/eye/M in sortmob)
moblist.Add(M)
for(var/mob/observer/blob/M in sortmob)
moblist.Add(M)
for(var/mob/living/silicon/ai/M in sortmob)
moblist.Add(M)
for(var/mob/living/silicon/pai/M in sortmob)
@@ -1327,3 +1336,24 @@ var/mob/dview/dview_mob = new
return "[round(number / 1e9, 0.1)] G[symbol]" // giga
if(1e12 to 1e15-1)
return "[round(number / 1e12, 0.1)] T[symbol]" // tera
//ultra range (no limitations on distance, faster than range for distances > 8); including areas drastically decreases performance
/proc/urange(dist=0, atom/center=usr, orange=0, areas=0)
if(!dist)
if(!orange)
return list(center)
else
return list()
var/list/turfs = RANGE_TURFS(dist, center)
if(orange)
turfs -= get_turf(center)
. = list()
for(var/V in turfs)
var/turf/T = V
. += T
. += T.contents
if(areas)
. |= T.loc
+2 -2
View File
@@ -108,7 +108,7 @@
W.afterattack(A, src, 1, params) // 1 indicates adjacency
else
if(ismob(A)) // No instant mob attacking
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
setClickCooldown(get_attack_speed())
UnarmedAttack(A, 1)
trigger_aiming(TARGET_CAN_CLICK)
@@ -129,7 +129,7 @@
W.afterattack(A, src, 1, params) // 1: clicking something Adjacent
else
if(ismob(A)) // No instant mob attacking
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
setClickCooldown(get_attack_speed())
UnarmedAttack(A, 1)
trigger_aiming(TARGET_CAN_CLICK)
return
+22 -1
View File
@@ -23,8 +23,13 @@ avoid code duplication. This includes items that may sometimes act as a standard
/obj/item/proc/attack_self(mob/user)
return
// Called at the start of resolve_attackby(), before the actual attack.
/obj/item/proc/pre_attack(atom/a, mob/user)
return
//I would prefer to rename this to attack(), but that would involve touching hundreds of files.
/obj/item/proc/resolve_attackby(atom/A, mob/user)
pre_attack(A, user)
add_fingerprint(user)
return A.attackby(src, user)
@@ -46,6 +51,22 @@ avoid code duplication. This includes items that may sometimes act as a standard
return 0
return I.attack(src, user, user.zone_sel.selecting)
// Used to get how fast a mob should attack, and influences click delay.
// This is just for inheritence.
/mob/proc/get_attack_speed()
return DEFAULT_ATTACK_COOLDOWN
// Same as above but actually does useful things.
// W is the item being used in the attack, if any. modifier is if the attack should be longer or shorter than usual, for whatever reason.
/mob/living/get_attack_speed(var/obj/item/W)
var/speed = DEFAULT_ATTACK_COOLDOWN
if(W && istype(W))
speed = W.attackspeed
for(var/datum/modifier/M in modifiers)
if(!isnull(M.attack_speed_percent))
speed *= M.attack_speed_percent
return speed
// Proximity_flag is 1 if this afterattack was called on something adjacent, in your square, or on your person.
// Click parameters is the params string from byond Click() code, see that documentation.
/obj/item/proc/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
@@ -68,7 +89,7 @@ avoid code duplication. This includes items that may sometimes act as a standard
msg_admin_attack("[key_name(user)] attacked [key_name(M)] with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYE: [uppertext(damtype)])" )
/////////////////////////
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(M)
var/hit_zone = M.resolve_item_attack(src, user, target_zone)
+3 -3
View File
@@ -59,7 +59,7 @@
if(!..())
return 0
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
setClickCooldown(get_attack_speed())
A.attack_generic(src,rand(5,6),"bitten")
/*
@@ -87,7 +87,7 @@
custom_emote(1,"[friendly] [A]!")
return
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
setClickCooldown(get_attack_speed())
if(isliving(A))
target_mob = A
PunchTarget()
@@ -96,7 +96,7 @@
A.attack_generic(src, rand(melee_damage_lower, melee_damage_upper), attacktext)
/mob/living/simple_animal/RangedAttack(var/atom/A)
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
setClickCooldown(get_attack_speed())
var/distance = get_dist(src, A)
if(prob(spattack_prob) && (distance >= spattack_min_range) && (distance <= spattack_max_range))
+1 -1
View File
@@ -74,7 +74,7 @@
return 0
rig.selected_module.engage(A, alert_ai)
if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns
setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
setClickCooldown(get_attack_speed())
return 1
return 0
+7
View File
@@ -92,6 +92,13 @@
question = "An Alien has just been created on the facility. Would you like to play as them?"
be_special_flag = BE_ALIEN
/datum/ghost_query/blob
role_name = "Blob"
question = "A rapidly expanding Blob has just appeared on the facility. Would you like to play as it?"
be_special_flag = BE_ALIEN
cutoff_number = 1
wait_time = 10 SECONDS
/datum/ghost_query/syndicate_drone
role_name = "Mercenary Drone"
question = "A team of dubious mercenaries have purchased a powerful drone, and they are attempting to activate it. Would you like to play as the drone?"
+18
View File
@@ -0,0 +1,18 @@
// Mutable appearances are an inbuilt byond datastructure. Read the documentation on them by hitting F1 in DM.
// Basically use them instead of images for overlays/underlays and when changing an object's appearance if you're doing so with any regularity.
// Unless you need the overlay/underlay to have a different direction than the base object. Then you have to use an image due to a bug.
// Mutable appearances are children of images, just so you know.
/mutable_appearance/New()
..()
plane = FLOAT_PLANE // No clue why this is 0 by default yet images are on FLOAT_PLANE
// And yes this does have to be in the constructor, BYOND ignores it if you set it as a normal var
// Helper similar to image()
/proc/mutable_appearance(icon, icon_state = "", layer = FLOAT_LAYER)
var/mutable_appearance/MA = new()
MA.icon = icon
MA.icon_state = icon_state
MA.layer = layer
return MA
+1 -1
View File
@@ -82,7 +82,7 @@
contains = list(
/obj/item/weapon/storage/firstaid/clotting
)
cost = 40
cost = 100
containertype = "/obj/structure/closet/crate/secure"
containername = "Clotting Medicine crate"
access = access_medical
+4 -2
View File
@@ -55,12 +55,14 @@
if(APC_WIRE_MAIN_POWER1, APC_WIRE_MAIN_POWER2)
if(!mended)
A.shock(usr, 50)
if(istype(usr, /mob/living))
A.shock(usr, 50)
A.shorted = 1
else if(!IsIndexCut(APC_WIRE_MAIN_POWER1) && !IsIndexCut(APC_WIRE_MAIN_POWER2))
A.shorted = 0
A.shock(usr, 50)
if(istype(usr, /mob/living))
A.shock(usr, 50)
if(APC_WIRE_AI_CONTROL)
+4
View File
@@ -84,6 +84,10 @@
P.on_hit(src, 0, def_zone)
. = 0
// Called when a blob expands onto the tile the atom occupies.
/atom/proc/blob_act()
return
/atom/proc/in_contents_of(container)//can take class or object instance as argument
if(ispath(container))
if(istype(src.loc, container))
+2
View File
@@ -16,6 +16,8 @@
var/mob/pulledby = null
var/item_state = null // Used to specify the item state for the on-mob overlays.
var/icon_scale = 1 // Used to scale icons up or down in update_transform().
var/old_x = 0
var/old_y = 0
var/auto_init = 1
/atom/movable/New()
+10 -4
View File
@@ -82,7 +82,7 @@
while(!accepted)
if(!B) break // prevents possible runtime errors
new_book_style = input(H,"Which bible style would you like?") in list("Bible", "Koran", "Scrapbook", "Creeper", "White Bible", "Holy Light", "Athiest", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "the bible melts", "Necronomicon")
new_book_style = input(H,"Which bible style would you like?") in list("Bible", "Koran", "Scrapbook", "Pagan", "White Bible", "Holy Light", "Athiest", "Tome", "The King in Yellow", "Ithaqua", "Scientology", "the bible melts", "Necronomicon","Orthodox","Torah")
switch(new_book_style)
if("Koran")
B.icon_state = "koran"
@@ -90,9 +90,6 @@
if("Scrapbook")
B.icon_state = "scrapbook"
B.item_state = "scrapbook"
if("Creeper")
B.icon_state = "creeper"
B.item_state = "syringe_kit"
if("White Bible")
B.icon_state = "white"
B.item_state = "syringe_kit"
@@ -120,6 +117,15 @@
if("Necronomicon")
B.icon_state = "necronomicon"
B.item_state = "necronomicon"
if("Pagan")
B.icon_state = "shadows"
B.item_state = "syringe_kit"
if("Orthodox")
B.icon_state = "orthodoxy"
B.item_state = "bible"
if("Torah")
B.icon_state = "torah"
B.item_state = "clipboard"
else
B.icon_state = "bible"
B.item_state = "bible"
@@ -40,6 +40,9 @@
else
update_icon()
/obj/machinery/portable_atmospherics/blob_act()
qdel(src)
/obj/machinery/portable_atmospherics/proc/StandardAirMix()
return list(
"oxygen" = O2STANDARD * MolesForPressure(),
+7 -2
View File
@@ -106,6 +106,11 @@
..() //and give it the regular chance of being deleted outright
/obj/machinery/camera/blob_act()
if((stat & BROKEN) || invuln)
return
destroy()
/obj/machinery/camera/hitby(AM as mob|obj)
..()
if (istype(AM, /obj))
@@ -125,7 +130,7 @@
if(user.species.can_shred(user))
set_status(0)
user.do_attack_animation(src)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
visible_message("<span class='warning'>\The [user] slashes at [src]!</span>")
playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
add_hiddenprint(user)
@@ -205,7 +210,7 @@
src.bugged = 1
else if(W.damtype == BRUTE || W.damtype == BURN) //bashing cameras
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
if (W.force >= src.toughness)
user.do_attack_animation(src)
visible_message("<span class='warning'><b>[src] has been [W.attack_verb.len? pick(W.attack_verb) : "attacked"] with [W] by [user]!</b></span>")
+3
View File
@@ -59,6 +59,9 @@
set_broken()
..()
/obj/machinery/computer/blob_act()
ex_act(2)
/obj/machinery/computer/update_icon()
overlays.Cut()
if(stat & NOPOWER)
+2 -2
View File
@@ -96,7 +96,7 @@ for reference:
return
return
else
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
switch(W.damtype)
if("fire")
health -= W.force * 1
@@ -214,7 +214,7 @@ for reference:
if(health <= 0)
explode()
return
/obj/machinery/deployable/barrier/emp_act(severity)
if(stat & (BROKEN|NOPOWER))
return
+6
View File
@@ -182,7 +182,13 @@
/obj/machinery/door/airlock/centcom
name = "Airlock"
icon = 'icons/obj/doors/Doorele.dmi'
opacity = 1
/obj/machinery/door/airlock/glass_centcom
name = "Airlock"
icon = 'icons/obj/doors/Dooreleglass.dmi'
opacity = 0
glass = 1
/obj/machinery/door/airlock/vault
name = "Vault"
+2 -2
View File
@@ -132,7 +132,7 @@
else if(src.density && (user.a_intent == I_HURT)) //If we can't pry it open and it's a weapon, let's hit it.
var/obj/item/weapon/W = C
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
if(W.damtype == BRUTE || W.damtype == BURN)
user.do_attack_animation(src)
if(W.force < min_force)
@@ -162,7 +162,7 @@
else if(src.density && (user.a_intent == I_HURT)) //If we can't pry it open and it's not a weapon.... Eh, let's attack it anyway.
var/obj/item/weapon/W = C
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
if(W.damtype == BRUTE || W.damtype == BURN)
user.do_attack_animation(src)
if(W.force < min_force) //No actual non-weapon item shouls have a force greater than the min_force, but let's include this just in case.
+8 -1
View File
@@ -260,7 +260,7 @@
//psa to whoever coded this, there are plenty of objects that need to call attack() on doors without bludgeoning them.
if(src.density && istype(I, /obj/item/weapon) && user.a_intent == I_HURT && !istype(I, /obj/item/weapon/card))
var/obj/item/weapon/W = I
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
if(W.damtype == BRUTE || W.damtype == BURN)
user.do_attack_animation(src)
if(W.force < min_force)
@@ -355,6 +355,13 @@
take_damage(150)
return
/obj/machinery/door/blob_act()
if(density) // If it's closed.
if(stat & BROKEN)
spawn(0)
open(1)
else
take_damage(100)
/obj/machinery/door/update_icon()
if(density)
+3 -2
View File
@@ -391,8 +391,9 @@
else
use_power(360)
else
log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.")
message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.")
if(usr && usr.ckey)
log_admin("[usr]([usr.ckey]) has forced open an emergency shutter.")
message_admins("[usr]([usr.ckey]) has forced open an emergency shutter.")
latetoggle()
return ..()
+2 -2
View File
@@ -161,7 +161,7 @@
playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1)
visible_message("<span class='danger'>[user] smashes against the [src.name].</span>", 1)
user.do_attack_animation(src)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
take_damage(25)
return
return src.attackby(user, user)
@@ -246,7 +246,7 @@
//If it's a weapon, smash windoor. Unless it's an id card, agent card, ect.. then ignore it (Cards really shouldnt damage a door anyway)
if(src.density && istype(I, /obj/item/weapon) && !istype(I, /obj/item/weapon/card))
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(I))
var/aforce = I.force
playsound(src.loc, 'sound/effects/Glasshit.ogg', 75, 1)
visible_message("<span class='danger'>[src] was hit by [I].</span>")
+5
View File
@@ -36,6 +36,11 @@
name = "surgery holosign"
desc = "Small wall-mounted holographic projector. This one reads SURGERY."
on_icon = "surgery"
/obj/machinery/holosign/exit
name = "exit holosign"
desc = "Small wall-mounted holographic projector. This one reads EXIT."
on_icon = "exit"
////////////////////SWITCH///////////////////////////////////////
/obj/machinery/button/holosign
+1 -1
View File
@@ -381,7 +381,7 @@ var/list/turret_icons
else
//if the turret was attacked with the intention of harming it:
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(I))
take_damage(I.force * 0.5)
if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off
if(!attacked && !emagged)
+19 -13
View File
@@ -103,13 +103,6 @@
R.adjustFireLoss(-wire_rate)
else if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
if(!isnull(H.internal_organs_by_name["cell"]) && H.nutrition < 450)
H.nutrition = min(H.nutrition+10, 450)
cell.use(7000/450*10)
else if(istype(occupant, /mob/living/carbon/human))
var/mob/living/carbon/human/H = occupant
// In case they somehow end up with positive values for otherwise unobtainable damage...
if(H.getToxLoss()>0) H.adjustToxLoss(-(rand(1,3)))
@@ -153,9 +146,21 @@
return
if(default_part_replacement(user, O))
return
if (istype(O, /obj/item/weapon/grab) && get_dist(src,user)<2)
var/obj/item/weapon/grab/G = O
if(istype(G.affecting,/mob/living))
var/mob/living/M = G.affecting
qdel(O)
go_in(M)
..()
/obj/machinery/recharge_station/MouseDrop_T(var/mob/target, var/mob/user)
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user))
return
go_in(target)
/obj/machinery/recharge_station/RefreshParts()
..()
var/man_rating = 0
@@ -214,15 +219,16 @@
if(icon_update_tick == 0)
build_overlays()
/obj/machinery/recharge_station/Bumped(var/mob/living/silicon/robot/R)
go_in(R)
/obj/machinery/recharge_station/Bumped(var/mob/living/L)
go_in(L)
/obj/machinery/recharge_station/proc/go_in(var/mob/living/silicon/robot/R)
/obj/machinery/recharge_station/proc/go_in(var/mob/living/L)
if(occupant)
return
if(istype(R, /mob/living/silicon/robot))
if(istype(L, /mob/living/silicon/robot))
var/mob/living/silicon/robot/R = L
if(R.incapacitated())
return
@@ -237,8 +243,8 @@
update_icon()
return 1
else if(istype(R, /mob/living/carbon/human))
var/mob/living/carbon/human/H = R
else if(istype(L, /mob/living/carbon/human))
var/mob/living/carbon/human/H = L
if(!isnull(H.internal_organs_by_name["cell"]))
add_fingerprint(H)
H.reset_view(src)
+3 -4
View File
@@ -505,7 +505,7 @@
return
/obj/mecha/attack_hand(mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
src.log_message("Attack by hand/paw. Attacker - [user].",1)
if(istype(user,/mob/living/carbon/human))
@@ -513,7 +513,6 @@
if(H.species.can_shred(user))
if(!prob(src.deflect_chance))
src.take_damage(15)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
src.check_for_internal_damage(list(MECHA_INT_TEMP_CONTROL,MECHA_INT_TANK_BREACH,MECHA_INT_CONTROL_LOST))
playsound(src.loc, 'sound/weapons/slash.ogg', 50, 1, -1)
user << "<span class='danger'>You slash at the armored suit!</span>"
@@ -666,7 +665,7 @@
return
/obj/mecha/proc/dynattackby(obj/item/weapon/W as obj, mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
src.log_message("Attacked by [W]. Attacker - [user]")
if(prob(src.deflect_chance))
user << "<span class='danger'>\The [W] bounces off [src.name].</span>"
@@ -1763,7 +1762,7 @@
/obj/mecha/attack_generic(var/mob/user, var/damage, var/attack_message)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
if(!damage)
return 0
+2 -2
View File
@@ -120,7 +120,7 @@
/obj/effect/alien/resin/attackby(obj/item/weapon/W as obj, mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
var/aforce = W.force
health = max(0, health - aforce)
playsound(loc, 'sound/effects/attackblob.ogg', 100, 1)
@@ -227,7 +227,7 @@ Alien plants should do something if theres a lot of poison
return
/obj/effect/alien/weeds/attackby(var/obj/item/weapon/W, var/mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
if(W.attack_verb.len)
visible_message("<span class='danger'>\The [src] have been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]</span>")
else
+2 -2
View File
@@ -3,7 +3,7 @@
icon = 'icons/effects/effects.dmi'
icon_state = "extinguish"
mouse_opacity = 0
pass_flags = PASSTABLE | PASSGRILLE
pass_flags = PASSTABLE | PASSGRILLE | PASSBLOB
/obj/effect/effect/water/New(loc)
..()
@@ -27,7 +27,7 @@
var/mob/M
for(var/atom/A in T)
if(!ismob(A) && A.simulated) // Mobs are handled differently
reagents.touch(A)
reagents.touch(A, reagents.total_volume)
else if(ismob(A) && !M)
M = A
if(M)
@@ -67,4 +67,10 @@
spawn(5)
var/datum/effect/effect/system/smoke_spread/S = new/datum/effect/effect/system/smoke_spread()
S.set_up(5,0,location,null)
S.start()
S.start()
/datum/effect/system/explosion/smokeless/start()
new/obj/effect/explosion(location)
var/datum/effect/system/expl_particles/P = new/datum/effect/system/expl_particles()
P.set_up(10,location)
P.start()
+1 -1
View File
@@ -21,7 +21,7 @@
return
/obj/effect/spider/attackby(var/obj/item/weapon/W, var/mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
if(W.attack_verb.len)
visible_message("<span class='warning'>\The [src] have been [pick(W.attack_verb)] with \the [W][(user ? " by [user]." : ".")]</span>")
+2 -1
View File
@@ -80,6 +80,7 @@
var/list/sprite_sheets_obj = list()
var/toolspeed = 1.0 // This is a multipler on how 'fast' a tool works. e.g. setting this to 0.5 will make the tool work twice as fast.
var/attackspeed = DEFAULT_ATTACK_COOLDOWN // How long click delay will be when using this, in 1/10ths of a second. Checked in the user's get_attack_speed().
var/addblends // Icon overlay for ADD highlights when applicable.
/obj/item/New()
@@ -457,7 +458,7 @@ var/list/global/slot_flags_enumeration = list(
M.attack_log += "\[[time_stamp()]\]<font color='orange'> Attacked by [user.name] ([user.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)])</font>"
msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)") //BS12 EDIT ALG
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
user.do_attack_animation(M)
src.add_fingerprint(user)
@@ -429,6 +429,13 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
exonet.send_message(their_address, "text", text)
im_list += list(list("address" = exonet.address, "to_address" = their_address, "im" = text))
log_pda("[usr] (COMM: [src]) sent \"[text]\" to [exonet.get_atom_from_address(their_address)]")
for(var/mob/M in player_list)
if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat)
continue
if(exonet.get_atom_from_address(their_address) == M)
continue
M.show_message("Comm IM - [src] -> [exonet.get_atom_from_address(their_address)]: [text]")
if(href_list["disconnect"])
var/name_to_disconnect = href_list["disconnect"]
@@ -988,6 +995,14 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
src << "<span class='notice'>You have sent '[text_message]' to [chosen_communicator].</span>"
exonet_messages.Add("<b>To [chosen_communicator]:</b><br>[text_message]")
log_pda("[usr] (COMM: [src]) sent \"[text_message]\" to [chosen_communicator]")
for(var/mob/M in player_list)
if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat)
continue
if(M == src)
continue
M.show_message("Comm IM - [src] -> [chosen_communicator]: [text_message]")
// Verb: show_text_messages()
+11 -2
View File
@@ -284,7 +284,12 @@
return "buzzes, \"Resuscitation failed - Excessive neural degeneration. Further attempts futile.\""
H.updatehealth()
if(H.health + H.getOxyLoss() <= config.health_threshold_dead || (HUSK in H.mutations) || (!H.isSynthetic() && !H.can_defib))
if(H.isSynthetic())
if(H.health + H.getOxyLoss() + H.getToxLoss() <= config.health_threshold_dead)
return "buzzes, \"Resuscitation failed - Severe damage detected. Begin manual repair before further attempts futile.\""
else if(H.health + H.getOxyLoss() <= config.health_threshold_dead || (HUSK in H.mutations) || !H.can_defib)
return "buzzes, \"Resuscitation failed - Severe tissue damage makes recovery of patient impossible via defibrillator. Further attempts futile.\""
var/bad_vital_organ = check_vital_organs(H)
@@ -423,6 +428,9 @@
var/adjust_health = barely_in_crit - H.health //need to increase health by this much
H.adjustOxyLoss(-adjust_health)
if(H.isSynthetic())
H.adjustToxLoss(-H.getToxLoss())
make_announcement("pings, \"Resuscitation successful.\"", "notice")
playsound(get_turf(src), 'sound/machines/defib_success.ogg', 50, 0)
@@ -645,7 +653,8 @@
name = "jumper cable kit"
desc = "A device that delivers powerful shocks to detachable jumper cables that are capable of reviving full body prosthetics."
icon_state = "jumperunit"
item_state = "jumperunit"
item_state = "defibunit"
// item_state = "jumperunit"
paddles = /obj/item/weapon/shockpaddles/linked/jumper
/obj/item/device/defib_kit/jumper_kit/loaded
+2 -5
View File
@@ -62,7 +62,7 @@
user.attack_log += text("\[[time_stamp()]\] <font color='red'>Used the [src.name] to flash [M.name] ([M.ckey])</font>")
msg_admin_attack("[user.name] ([user.ckey]) Used the [src.name] to flash [M.name] ([M.ckey]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(M)
if(!clown_check(user)) return
@@ -75,9 +75,6 @@
if(!check_capacitor(user))
return
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.do_attack_animation(M)
playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
var/flashfail = 0
@@ -147,7 +144,7 @@
/obj/item/device/flash/attack_self(mob/living/carbon/user as mob, flag = 0, emp = 0)
if(!user || !clown_check(user)) return
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(src))
if(broken)
user.show_message("<span class='warning'>The [src.name] is broken</span>", 2)
@@ -163,7 +163,7 @@
else
user << "<span class='notice'>\The [M]'s pupils narrow.</span>"
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) //can be used offensively
user.setClickCooldown(user.get_attack_speed(src)) //can be used offensively
M.flash_eyes()
else
return ..()
+183 -72
View File
@@ -1,115 +1,196 @@
/obj/item/device/spy_bug
name = "bug"
desc = "" // Nothing to see here
/obj/item/device/camerabug
name = "mobile camera pod"
desc = "A camera pod used by tactical operators. Must be linked to a camera scanner unit."
icon = 'icons/obj/grenade.dmi'
icon_state = "camgrenade"
item_state = "empgrenade"
flags = CONDUCT
w_class = ITEMSIZE_SMALL
force = 0
throwforce = 5.0
throw_range = 15
throw_speed = 3
origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1)
var/obj/item/device/bug_monitor/linkedmonitor
var/brokentype = /obj/item/brokenbug
// var/obj/item/device/radio/bug/radio
var/obj/machinery/camera/bug/camera
/obj/item/device/camerabug/New()
..()
// radio = new(src)
camera = new(src)
/obj/item/device/camerabug/attack_self(mob/user)
if(user.a_intent == I_HURT)
to_chat(user, "<span class='notice'>You crush the [src] under your foot, breaking it.</span>")
visible_message("[user.name] crushes the [src] under their foot, breaking it!</span>")
new brokentype(get_turf(src))
spawn(0)
qdel(src)
/* else
user.set_machine(radio)
radio.interact(user)
*/
/obj/item/device/camerabug/verb/reset()
set name = "Reset camera bug"
set category = "Object"
if(linkedmonitor)
linkedmonitor.unpair(src)
linkedmonitor = null
qdel(camera)
camera = new(src)
to_chat(usr, "<span class='notice'>You turn the [src] off and on again, delinking it from any monitors.")
/obj/item/brokenbug
name = "broken mobile camera pod"
desc = "A camera pod formerly used by tactical operators. The lens is smashed, and the circuits are damaged beyond repair."
icon = 'icons/obj/grenade.dmi'
icon_state = "camgrenadebroken"
item_state = "empgrenade"
flags = CONDUCT
force = 5.0
w_class = ITEMSIZE_SMALL
throwforce = 5.0
throw_range = 15
throw_speed = 3
origin_tech = list(TECH_ENGINEERING = 1)
/obj/item/brokenbug/spy
name = "broken bug"
desc = "" //Even when it's broken it's inconspicuous
icon = 'icons/obj/weapons.dmi'
icon_state = "eshield0"
item_state = "nothing"
layer = TURF_LAYER+0.2
flags = CONDUCT
force = 5.0
w_class = ITEMSIZE_TINY
slot_flags = SLOT_EARS
origin_tech = list(TECH_ENGINEERING = 1, TECH_ILLEGAL = 3) //crush it and you lose the data
flags = CONDUCT
force = 0
throwforce = 5.0
throw_range = 15
throw_speed = 3
/obj/item/device/camerabug/spy
name = "bug"
desc = "" //Nothing to see here
icon = 'icons/obj/weapons.dmi'
icon_state = "eshield0"
item_state = "nothing"
layer = TURF_LAYER+0.2
w_class = ITEMSIZE_TINY
slot_flags = SLOT_EARS
origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3)
var/obj/item/device/radio/spy/radio
var/obj/machinery/camera/spy/camera
/obj/item/device/spy_bug/New()
..()
radio = new(src)
camera = new(src)
/obj/item/device/spy_bug/examine(mob/user)
/obj/item/device/camerabug/examine(mob/user)
. = ..(user, 0)
if(.)
user << "It's a tiny camera, microphone, and transmission device in a happy union."
user << "Needs to be both configured and brought in contact with monitor device to be fully functional."
to_chat(user, "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/spy_bug/attack_self(mob/user)
radio.attack_self(user)
/obj/item/device/spy_bug/attackby(obj/W as obj, mob/living/user as mob)
if(istype(W, /obj/item/device/spy_monitor))
var/obj/item/device/spy_monitor/SM = W
SM.pair(src, user)
/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
if(!linkedmonitor)
to_chat(user, "<span class='notice'>\The [src] has been paired with \the [SM].</span>")
SM.pair(src)
linkedmonitor = SM
else if (linkedmonitor == SM)
to_chat(user, "<span class='notice'>\The [src] has been unpaired from \the [SM].</span>")
linkedmonitor.unpair(src)
linkedmonitor = null
else
to_chat(user, "Error: The device is linked to another monitor.")
else
if(W.force >= 5)
visible_message("\The [src] lens shatters!")
new brokentype(get_turf(src))
if(linkedmonitor)
linkedmonitor.unpair(src)
linkedmonitor = null
spawn(0)
qdel(src)
..()
/obj/item/device/spy_bug/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
/obj/item/device/camerabug/bullet_act()
visible_message("The [src] lens shatters!")
new brokentype(get_turf(src))
if(linkedmonitor)
linkedmonitor.unpair(src)
linkedmonitor = null
spawn(0)
qdel(src)
/obj/item/device/camerabug/Destroy()
if(linkedmonitor)
linkedmonitor.unpair(src)
linkedmonitor = null
..()
/*
/obj/item/device/camerabug/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
radio.hear_talk(M, msg, speaking)
/obj/item/device/spy_monitor
name = "\improper PDA"
desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge."
icon = 'icons/obj/pda.dmi'
icon_state = "pda"
*/
/obj/item/device/bug_monitor
name = "mobile camera pod monitor"
desc = "A portable camera console designed to work with mobile camera pods."
icon = 'icons/obj/device.dmi'
icon_state = "forensic0"
item_state = "electronic"
w_class = ITEMSIZE_SMALL
origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3)
w_class = ITEMSIZE_SMALL
origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1)
var/operating = 0
var/obj/item/device/radio/spy/radio
var/obj/machinery/camera/spy/selected_camera
var/list/obj/machinery/camera/spy/cameras = new()
/obj/item/device/spy_monitor/New()
// var/obj/item/device/radio/bug/radio
var/obj/machinery/camera/bug/selected_camera
var/list/obj/machinery/camera/bug/cameras = new()
/*
/obj/item/device/bug_monitor/New()
radio = new(src)
/obj/item/device/spy_monitor/examine(mob/user)
. = ..(user, 1)
if(.)
user << "The time '12:00' is blinking in the corner of the screen and \the [src] looks very cheaply made."
/obj/item/device/spy_monitor/attack_self(mob/user)
*/
/obj/item/device/bug_monitor/attack_self(mob/user)
if(operating)
return
radio.attack_self(user)
// radio.attack_self(user)
view_cameras(user)
/obj/item/device/spy_monitor/attackby(obj/W as obj, mob/living/user as mob)
if(istype(W, /obj/item/device/spy_bug))
pair(W, user)
/obj/item/device/bug_monitor/attackby(obj/item/W as obj, mob/living/user as mob)
if(istype(W, /obj/item/device/camerabug))
W.attackby(src, user)
else
return ..()
/obj/item/device/spy_monitor/proc/pair(var/obj/item/device/spy_bug/SB, var/mob/living/user)
/obj/item/device/bug_monitor/proc/unpair(var/obj/item/device/camerabug/SB)
if(SB.camera in cameras)
user << "<span class='notice'>\The [SB] has been unpaired from \the [src].</span>"
cameras -= SB.camera
else
user << "<span class='notice'>\The [SB] has been paired with \the [src].</span>"
cameras += SB.camera
/obj/item/device/spy_monitor/proc/view_cameras(mob/user)
/obj/item/device/bug_monitor/proc/pair(var/obj/item/device/camerabug/SB)
cameras += SB.camera
/obj/item/device/bug_monitor/proc/view_cameras(mob/user)
if(!can_use_cam(user))
return
selected_camera = cameras[1]
user.reset_view(selected_camera)
view_camera(user)
operating = 1
while(selected_camera && Adjacent(user))
selected_camera = input("Select camera bug to view.") as null|anything in cameras
selected_camera = input("Select camera to view.") as null|anything in cameras
selected_camera = null
operating = 0
/obj/item/device/spy_monitor/proc/view_camera(mob/user)
/obj/item/device/bug_monitor/proc/view_camera(mob/user)
spawn(0)
while(selected_camera && Adjacent(user))
var/turf/T = get_turf(selected_camera)
if(!T || !is_on_same_plane_or_station(T.z, user.z) || !selected_camera.can_use())
user.unset_machine()
user.reset_view(null)
user << "<span class='notice'>[selected_camera] unavailable.</span>"
to_chat(user, "<span class='notice'>Link to [selected_camera] has been lost.</span>")
src.unpair(selected_camera.loc)
sleep(90)
else
user.set_machine(selected_camera)
@@ -118,37 +199,67 @@
user.unset_machine()
user.reset_view(null)
/obj/item/device/spy_monitor/proc/can_use_cam(mob/user)
/obj/item/device/bug_monitor/proc/can_use_cam(mob/user)
if(operating)
return
if(!cameras.len)
user << "<span class='warning'>No paired cameras detected!</span>"
user << "<span class='warning'>Bring a bug in contact with this device to pair the camera.</span>"
to_chat(user, "<span class='warning'>No paired cameras detected!</span>")
to_chat(user, "<span class='warning'>Bring a camera in contact with this device to pair the camera.</span>")
return
return 1
/obj/item/device/spy_monitor/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
/*
/obj/item/device/bug_monitor/hear_talk(mob/M, var/msg, verb, datum/language/speaking)
return radio.hear_talk(M, msg, speaking)
*/
/obj/item/device/bug_monitor/spy
name = "\improper PDA"
desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge."
icon = 'icons/obj/pda.dmi'
icon_state = "pda"
item_state = "electronic"
origin_tech = list(TECH_DATA = 1, TECH_ENGINEERING = 1, TECH_ILLEGAL = 3)
/obj/item/device/bug_monitor/spy/examine(mob/user)
. = ..(user, 1)
if(.)
to_chat(user, "The time '12:00' is blinking in the corner of the screen and \the [src] looks very cheaply made.")
/obj/machinery/camera/spy
// These cheap toys are accessible from the mercenary camera console as well
/obj/machinery/camera/bug/check_eye(var/mob/user as mob)
return 0
/obj/machinery/camera/bug
network = list(NETWORK_SECURITY)
/obj/machinery/camera/bug/New()
..()
name = "Camera #[rand(1000,9999)]"
c_tag = name
/obj/machinery/camera/bug/spy
// These cheap toys are accessible from the mercenary camera console as well - only the antag ones though!
network = list(NETWORK_MERCENARY)
/obj/machinery/camera/spy/New()
/obj/machinery/camera/bug/spy/New()
..()
name = "DV-136ZB #[rand(1000,9999)]"
c_tag = name
/obj/machinery/camera/spy/check_eye(var/mob/user as mob)
return 0
/* //These were originally supposed to have radios in them. Doesn't work.
/obj/item/device/radio/bug
listening = 0 //turn it on first
frequency = 1359 //sec comms
broadcasting = 0
canhear_range = 1
name = "camera bug device"
icon_state = "syn_cypherkey"
/obj/item/device/radio/spy
/obj/item/device/radio/bug/spy
listening = 0
frequency = 1473
broadcasting = 0
canhear_range = 1
name = "spy device"
icon_state = "syn_cypherkey"
*/
+1 -1
View File
@@ -34,7 +34,7 @@
if(!S.get_damage())
user << "<span class='notice'>Nothing to fix here.</span>"
else if(can_use(1))
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(src))
if(S.open >= 2)
if(do_after(user,5 * toolspeed))
S.heal_damage(20, 20, robo_repair = 1)
@@ -16,10 +16,11 @@
for(var/mob/living/carbon/M in hear(7, get_turf(src)))
bang(get_turf(src), M)
for(var/obj/effect/blob/B in hear(8,get_turf(src))) //Blob damage here
for(var/obj/structure/blob/B in hear(8,get_turf(src))) //Blob damage here
var/damage = round(30/(get_dist(B,get_turf(src))+1))
B.health -= damage
B.update_icon()
if(B.overmind)
damage *= B.overmind.blob_type.burn_multiplier
B.adjust_integrity(-damage)
new/obj/effect/effect/sparks(src.loc)
new/obj/effect/effect/smoke/illumination(src.loc, 5, range=30, power=30, color="#FFFFFF")
+2 -2
View File
@@ -81,7 +81,7 @@
msg_admin_attack("[key_name(user)] attempted to handcuff [key_name(H)]")
feedback_add_details("handcuffs","H")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(H)
user.visible_message("<span class='danger'>\The [user] has put [cuff_type] on \the [H]!</span>")
@@ -286,7 +286,7 @@ var/last_chew = 0
msg_admin_attack("[key_name(user)] attempted to legcuff [key_name(H)]")
feedback_add_details("legcuffs","H")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(H)
user.visible_message("<span class='danger'>\The [user] has put [cuff_type] on \the [H]!</span>")
@@ -0,0 +1,112 @@
obj/item/weapon/chainsaw
name = "Chainsaw"
desc = "Vroom vroom."
icon_state = "chainsaw0"
var/on = 0
var/max_fuel = 20
w_class = ITEMSIZE_LARGE
slot_flags = SLOT_BACK
w_class = ITEMSIZE_LARGE
slot_flags = SLOT_BACK
obj/item/weapon/chainsaw/New()
var/datum/reagents/R = new/datum/reagents(max_fuel)
reagents = R
R.my_atom = src
R.add_reagent("fuel", max_fuel)
..()
obj/item/weapon/chainsaw/proc/turnOn()
if(on) return
visible_message("You start pulling the string on \the [src].", "[usr] starts pulling the string on the [src].")
if(max_fuel <= 0)
if(do_after(usr, 15))
to_chat(usr, "\The [src] won't start!")
else
to_chat(usr, "You fumble with the string.")
else
if(do_after(usr, 15))
visible_message("You start \the [src] up with a loud grinding!", "[usr] starts \the [src] up with a loud grinding!")
attack_verb = list("shreds", "rips", "tears")
playsound(src, 'sound/weapons/chainsaw_startup.ogg',40,1)
force = 55
edge = 1
sharp = 1
on = 1
update_icon()
else
to_chat(usr, "You fumble with the string.")
obj/item/weapon/chainsaw/proc/turnOff()
if(!on) return
to_chat(usr, "You switch the gas nozzle on the chainsaw, turning it off.")
attack_verb = list("bluntly hit", "beat", "knocked")
playsound(src, 'sound/weapons/chainsaw_turnoff.ogg',40,1)
force = 10
edge = 0
sharp = 0
on = 0
update_icon()
obj/item/weapon/chainsaw/attack_self(mob/user as mob)
if(!on)
turnOn()
else
turnOff()
obj/item/weapon/chainsaw/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity)
if(!proximity) return
..()
if(on)
playsound(src, 'sound/weapons/chainsaw_attack.ogg',40,1)
if(A && on)
if(istype(A,/obj/structure/window))
var/obj/structure/window/W = A
W.shatter()
else if(istype(A,/obj/structure/grille))
new /obj/structure/grille/broken(A.loc)
new /obj/item/stack/rods(A.loc)
qdel(A)
else if(istype(A,/obj/effect/plant))
var/obj/effect/plant/P = A
qdel(P) //Plant isn't surviving that. At all
if (istype(A, /obj/structure/reagent_dispensers/fueltank) && get_dist(src,A) <= 1)
to_chat(user, "<span class='notice'>You begin filling the tank on the chainsaw.</span>")
if(do_after(usr, 15))
A.reagents.trans_to_obj(src, max_fuel)
playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6)
to_chat(user, "<span class='notice'>Chainsaw succesfully refueled.</span>")
else
to_chat(user, "<span class='notice'>Don't move while you're refilling the chainsaw.</span>")
obj/item/weapon/chainsaw/process()
if(!on) return
if(on)
if(get_fuel() > 0)
reagents.remove_reagent("fuel", 1)
if(get_fuel() <= 0)
to_chat(usr, "\The [src] sputters to a stop!")
on = !on
obj/item/weapon/chainsaw/proc/get_fuel()
reagents.get_reagent_amount("fuel")
obj/item/weapon/chainsaw/examine(mob/user)
if(..(user,0))
if(max_fuel)
to_chat(usr, "<span class = 'notice'>The [src] feels like it contains roughtly [get_fuel()] units of fuel left.</span>")
obj/item/weapon/chainsaw/suicide_act(mob/user)
to_chat(viewers(user), "<span class='danger'>[user] is lying down and pulling the chainsaw into \him, it looks like \he's trying to commit suicide!</span>")
return(BRUTELOSS)
obj/item/weapon/chainsaw/update_icon()
if(on)
icon_state = "chainsaw1"
item_state = "chainsaw1"
else
icon_state = "chainsaw0"
icon_state = "chainsaw0"
@@ -83,6 +83,7 @@
base_icon = "fireaxe"
name = "fire axe"
desc = "Truly, the weapon of a madman. Who would think to fight fire with an axe?"
description_info = "This weapon can cleave, striking nearby lesser, hostile enemies close to the primary target. It must be held in both hands to do this."
unwielded_force_divisor = 0.25
force_divisor = 0.7 // 10/42 with hardness 60 (steel) and 0.25 unwielded divisor
dulled_divisor = 0.75 //Still metal on a stick
@@ -123,6 +124,12 @@
var/obj/effect/plant/P = A
P.die_off()
// This cannot go into afterattack since some mobs delete themselves upon dying.
/obj/item/weapon/material/twohanded/fireaxe/pre_attack(var/mob/living/target, var/mob/living/user)
if(istype(target))
cleave(user, target)
..()
/obj/item/weapon/material/twohanded/fireaxe/scythe
icon_state = "scythe0"
base_icon = "scythe"
@@ -217,7 +217,7 @@
var/obj/O = AM
O.emp_act(3) // A weaker severity is used because this has infinite uses.
playsound(get_turf(O), 'sound/effects/EMPulse.ogg', 100, 1)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) // A lot of objects don't set click delay.
user.setClickCooldown(user.get_attack_speed(src)) // A lot of objects don't set click delay.
return ..()
/obj/item/weapon/melee/energy/sword/ionic_rapier/apply_hit_effect(mob/living/target, mob/living/user, var/hit_zone)
@@ -379,6 +379,17 @@
new /obj/item/weapon/implanter(src)
new /obj/item/weapon/implantpad(src)
/obj/item/weapon/storage/box/camerabug
name = "mobile camera pod box"
desc = "A box containing some mobile camera pods."
icon_state = "pda"
/obj/item/weapon/storage/box/camerabug/New()
..()
for(var/i = 1 to 6)
new /obj/item/device/camerabug(src)
new /obj/item/device/bug_monitor(src)
/obj/item/weapon/storage/box/rxglasses
name = "box of prescription glasses"
desc = "This box contains nerd glasses."
@@ -159,13 +159,13 @@
/obj/item/weapon/storage/box/syndie_kit/spy/New()
..()
new /obj/item/device/spy_bug(src)
new /obj/item/device/spy_bug(src)
new /obj/item/device/spy_bug(src)
new /obj/item/device/spy_bug(src)
new /obj/item/device/spy_bug(src)
new /obj/item/device/spy_bug(src)
new /obj/item/device/spy_monitor(src)
new /obj/item/device/camerabug/spy(src)
new /obj/item/device/camerabug/spy(src)
new /obj/item/device/camerabug/spy(src)
new /obj/item/device/camerabug/spy(src)
new /obj/item/device/camerabug/spy(src)
new /obj/item/device/camerabug/spy(src)
new /obj/item/device/bug_monitor/spy(src)
/obj/item/weapon/storage/box/syndie_kit/g9mm
name = "\improper Smooth operator"
+1 -1
View File
@@ -17,7 +17,7 @@
var/max_carry = 10
/obj/item/weapon/tray/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(src))
// Drop all the things. All of them.
overlays.Cut()
for(var/obj/item/I in carrying)
+2 -2
View File
@@ -21,7 +21,7 @@
msg_admin_attack("[user.name] ([user.ckey]) attacked [M.name] ([M.ckey]) with [src.name] (INTENT: [uppertext(user.a_intent)]) (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[user.x];Y=[user.y];Z=[user.z]'>JMP</a>)")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(src))
user.do_attack_animation(M)
if (!(istype(user, /mob/living/carbon/human) || ticker) && ticker.mode.name != "monkey")
@@ -134,7 +134,7 @@
qdel(src)
/obj/effect/energy_net/user_unbuckle_mob(mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
visible_message("<span class='danger'>[user] begins to tear at \the [src]!</span>")
if(do_after(usr, escape_time, src, incapacitation_flags = INCAPACITATION_DEFAULT & ~(INCAPACITATION_RESTRAINED | INCAPACITATION_BUCKLED_FULLY)))
if(!buckled_mob)
+1 -1
View File
@@ -81,7 +81,7 @@
health = maxhealth
else
take_damage(C.force)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(C))
return ..()
/obj/structure/catwalk/Crossed()
@@ -185,6 +185,9 @@
A.forceMove(src.loc)
qdel(src)
/obj/structure/closet/blob_act()
damage(100)
/obj/structure/closet/proc/damage(var/damage)
health -= damage
if(health <= 0)
+1 -1
View File
@@ -55,7 +55,7 @@
/obj/structure/displaycase/attackby(obj/item/weapon/W as obj, mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
user.do_attack_animation(src)
playsound(loc, 'sound/effects/Glasshit.ogg', 50, 1)
src.health -= W.force
+1 -1
View File
@@ -18,7 +18,7 @@
to_chat(user, "<span class='warning'>You need more energy to use the punching bag. Go eat something.</span>")
else
if(user.a_intent == I_HURT)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
flick("[icon_state]_hit", src)
playsound(src.loc, 'sound/effects/woodhit.ogg', 25, 1, -1)
user.do_attack_animation(src)
+1 -1
View File
@@ -36,7 +36,7 @@
to_chat(user, "<span class='warning'>\The [W] is ineffective at harming \the [src].</span>")
hit_animation()
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
user.do_attack_animation(src)
// Shakes the tree slightly, more or less stolen from lockers.
+3
View File
@@ -43,6 +43,9 @@
return
/obj/structure/girder/blob_act()
dismantle()
/obj/structure/girder/proc/reset_girder()
anchored = 1
cover = initial(cover)
+2 -2
View File
@@ -27,7 +27,7 @@
/obj/structure/grille/attack_hand(mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
playsound(loc, 'sound/effects/grillehit.ogg', 80, 1)
user.do_attack_animation(src)
@@ -151,7 +151,7 @@
//window placing end
else if(!(W.flags & CONDUCT) || !shock(user, 70))
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
user.do_attack_animation(src)
playsound(loc, 'sound/effects/grillehit.ogg', 80, 1)
switch(W.damtype)
@@ -66,6 +66,9 @@
deflate(1)
return
/obj/structure/inflatable/blob_act()
deflate(1)
/obj/structure/inflatable/attack_hand(mob/user as mob)
add_fingerprint(user)
return
+1 -1
View File
@@ -258,7 +258,7 @@
else
playsound(loc, 'sound/effects/grillehit.ogg', 50, 1)
take_damage(W.force)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
return ..()
+58 -5
View File
@@ -156,9 +156,11 @@
/obj/structure/sign/warning/docking_area
name = "\improper KEEP CLEAR: DOCKING AREA"
icon_state = "evac"
/obj/structure/sign/warning/engineering_access
name = "\improper ENGINEERING ACCESS"
icon_state = "engine"
/obj/structure/sign/warning/fire
name = "\improper DANGER: FIRE"
@@ -185,9 +187,11 @@
/obj/structure/sign/warning/mail_delivery
name = "\improper MAIL DELIVERY"
icon_state = "mail"
/obj/structure/sign/warning/moving_parts
name = "\improper MOVING PARTS"
icon_state = "movingparts"
/obj/structure/sign/warning/nosmoking_1
name = "\improper NO SMOKING"
@@ -207,12 +211,15 @@
/obj/structure/sign/warning/secure_area
name = "\improper SECURE AREA"
icon_state = "securearea2"
/obj/structure/sign/warning/secure_area/armory
name = "\improper ARMORY"
icon_state = "armory"
/obj/structure/sign/warning/server_room
name = "\improper SERVER ROOM"
icon_state = "server"
/obj/structure/sign/warning/siphon_valve
name = "\improper SIPHON VALVE"
@@ -224,16 +231,19 @@
/obj/structure/sign/warning/vent_port
name = "\improper EJECTION/VENTING PORT"
/obj/structure/sign/warning/emergence
name = "\improper EMERGENT INTELLIGENCE DETAILS"
icon_state = "rogueai"
/obj/structure/sign/redcross
name = "medbay"
desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here."
icon_state = "redcross"
icon_state = "bluecross"
/obj/structure/sign/greencross
name = "medbay"
desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here."
icon_state = "greencross"
icon_state = "bluecross2"
/obj/structure/sign/goldenplaque
name = "The Most Robust Men Award for Robustness"
@@ -250,6 +260,11 @@
desc = "This plaque commemorates the fall of the Atmos FEA division. For all the charred, dizzy, and brittle men who have died in its hands."
icon_state = "atmosplaque"
/obj/structure/sign/periodic
name = "periodic table"
desc = "A sign reminding those visiting of the elements of the periodic table- though, they should have memorized them by now."
icon_state = "periodic"
/obj/structure/sign/double/maltesefalcon //The sign is 64x32, so it needs two tiles. ;3
name = "The Maltese Falcon"
desc = "The Maltese Falcon, Space Bar and Grill."
@@ -262,7 +277,7 @@
/obj/structure/sign/science //These 3 have multiple types, just var-edit the icon_state to whatever one you want on the map
name = "\improper SCIENCE!"
desc = "A warning sign which reads 'SCIENCE!'."
desc = "A warning sign which reads 'SCIENCE'."
icon_state = "science1"
/obj/structure/sign/chemistry
@@ -278,13 +293,18 @@
/obj/structure/sign/hydro
name = "\improper HYDROPONICS"
desc = "A sign labelling an area as a place where plants are grown."
icon_state = "hydro1"
icon_state = "hydro2"
/obj/structure/sign/hydrostorage
name = "\improper HYDROPONICS STORAGE"
desc = "A sign labelling an area as a place where plant growing supplies are kept."
icon_state = "hydro3"
/obj/structure/sign/xenobio
name = "\improper XENOBIOLOGY"
desc = "A warning sign which reads XENOBIOLOGY."
icon_state = "xenobio3"
/obj/structure/sign/directions
name = "direction sign"
desc = "A direction sign, claiming to know the way."
@@ -328,6 +348,11 @@
desc = "A direction sign, pointing out which way the Cargo department is."
icon_state = "direction_crg"
/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/christmas/lights
name = "Christmas lights"
desc = "Flashy and pretty."
@@ -366,4 +391,32 @@
/obj/structure/sign/hangar/three
name = "\improper Hangar Three"
icon_state = "hangar-3"
icon_state = "hangar-3"
/obj/structure/sign/atmos
name = "\improper WASTE"
icon_state = "atmos_waste"
/obj/structure/sign/atmos/o2
name = "\improper OXYGEN"
icon_state = "atmos_o2"
/obj/structure/sign/atmos/co2
name = "\improper CARBON DIOXIDE"
icon_state = "atmos_co2"
/obj/structure/sign/atmos/phoron
name = "\improper PHORON"
icon_state = "atmos_phoron"
/obj/structure/sign/atmos/n2o
name = "\improper NITROUS OXIDE"
icon_state = "atmos_n2o"
/obj/structure/sign/atmos/n2
name = "\improper NITROGEN"
icon_state = "atmos_n2"
/obj/structure/sign/atmos/air
name = "\improper AIR"
icon_state = "atmos_air"
@@ -197,7 +197,7 @@
name = "roller bed"
desc = "A portable bed-on-wheels made for transporting medical patients."
icon = 'icons/obj/rollerbed.dmi'
icon_state = "rollerbed_down"
icon_state = "rollerbed"
anchored = 0
surgery_odds = 75
var/bedtype = /obj/structure/bed/roller
@@ -205,7 +205,7 @@
/obj/structure/bed/roller/adv
name = "advanced roller bed"
icon_state = "rollerbedadv_down"
icon_state = "rollerbedadv"
bedtype = /obj/structure/bed/roller/adv
rollertype = /obj/item/roller/adv
@@ -230,7 +230,7 @@
name = "roller bed"
desc = "A collapsed roller bed that can be carried around."
icon = 'icons/obj/rollerbed.dmi'
icon_state = "rollerbed"
icon_state = "folded_rollerbed"
slot_flags = SLOT_BACK
w_class = ITEMSIZE_LARGE
var/rollertype = /obj/item/roller
@@ -256,7 +256,7 @@
/obj/item/roller/adv
name = "advanced roller bed"
desc = "A high-tech, compact version of the regular roller bed."
icon_state = "rollerbedadv"
icon_state = "folded_rollerbedadv"
w_class = ITEMSIZE_NORMAL
rollertype = /obj/item/roller/adv
bedtype = /obj/structure/bed/roller/adv
@@ -303,7 +303,7 @@
M.pixel_y = 0
M.old_y = 0
density = 0
icon_state = "[initial(icon_state)]_down"
icon_state = "[initial(icon_state)]"
update_icon()
return ..()
@@ -72,7 +72,7 @@ var/global/list/stool_cache = list() //haha stool
/obj/item/weapon/stool/attack(mob/M as mob, mob/user as mob)
if (prob(5) && istype(M,/mob/living))
user.visible_message("<span class='danger'>[user] breaks [src] over [M]'s back!</span>")
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
user.do_attack_animation(M)
user.drop_from_inventory(src)
+2 -2
View File
@@ -18,7 +18,7 @@
/obj/structure/toilet/attack_hand(mob/living/user as mob)
if(swirlie)
usr.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
usr.setClickCooldown(user.get_attack_speed())
usr.visible_message("<span class='danger'>[user] slams the toilet seat onto [swirlie.name]'s head!</span>", "<span class='notice'>You slam the toilet seat onto [swirlie.name]'s head!</span>", "You hear reverberating porcelain.")
swirlie.adjustBruteLoss(5)
return
@@ -54,7 +54,7 @@
return
if(istype(I, /obj/item/weapon/grab))
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(I))
var/obj/item/weapon/grab/G = I
if(isliving(G.affecting))
+6 -3
View File
@@ -125,6 +125,9 @@
shatter(0)
return
/obj/structure/window/blob_act()
take_damage(50)
//TODO: Make full windows a separate type of window.
//Once a full window, it will always be a full window, so there's no point
//having the same type for both.
@@ -172,7 +175,7 @@
playsound(loc, 'sound/effects/Glasshit.ogg', 50, 1)
/obj/structure/window/attack_hand(mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
if(HULK in user.mutations)
user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!"))
user.visible_message("<span class='danger'>[user] smashes through [src]!</span>")
@@ -200,7 +203,7 @@
return
/obj/structure/window/attack_generic(var/mob/user, var/damage)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
if(!damage)
return
if(damage >= 10)
@@ -295,7 +298,7 @@
var/obj/item/frame/F = W
F.try_build(src)
else
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
if(W.damtype == BRUTE || W.damtype == BURN)
user.do_attack_animation(src)
hit(W.force)
+26 -1
View File
@@ -2,6 +2,7 @@
name = "weapon"
icon = 'icons/obj/weapons.dmi'
hitsound = "swing_hit"
var/cleaving = FALSE // Used to avoid infinite cleaving.
/obj/item/weapon/Bump(mob/M as mob)
spawn(0)
@@ -12,4 +13,28 @@
item_icons = list(
slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi',
slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi',
)
)
// Attacks mobs (atm only simple ones due to friendly fire issues) that are adjacent to the target and user.
/obj/item/weapon/proc/cleave(var/mob/living/user, var/mob/living/target)
if(cleaving)
return // We're busy.
cleaving = TRUE
var/hit_mobs = 0
for(var/mob/living/simple_animal/SA in orange(get_turf(target), 1))
if(SA.stat == DEAD) // Don't beat a dead horse.
continue
if(SA == user) // Don't hit ourselves. Simple mobs shouldn't be able to do this but that might change later to be able to hit all mob/living-s.
continue
if(SA == target) // We (presumably) already hit the target before cleave() was called. orange() should prevent this but just to be safe...
continue
if(user.faction == SA.faction) // Avoid friendly fire.
continue
if(!SA.Adjacent(user) || !SA.Adjacent(target)) // Cleaving only hits mobs near the target mob and user.
continue
if(resolve_attackby(SA, user)) // Hit them with the weapon. This won't cause recursive cleaving due to the cleaving variable being set to true.
hit_mobs++
if(hit_mobs)
to_chat(user, "<span class='danger'>You used \the [src] to attack [hit_mobs] other thing\s!</span>")
cleaving = FALSE // We're done now.
+3 -3
View File
@@ -90,7 +90,7 @@
radiate()
add_fingerprint(user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
var/rotting = (locate(/obj/effect/overlay/wallrot) in src)
if (HULK in user.mutations)
if (rotting || !prob(material.hardness))
@@ -104,7 +104,7 @@
/turf/simulated/wall/attack_generic(var/mob/user, var/damage, var/attack_message, var/wallbreaker)
radiate()
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed())
var/rotting = (locate(/obj/effect/overlay/wallrot) in src)
if(!damage || !wallbreaker)
try_touch(user, rotting)
@@ -122,7 +122,7 @@
/turf/simulated/wall/attackby(obj/item/weapon/W as obj, mob/user as mob)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
user.setClickCooldown(user.get_attack_speed(W))
if (!user.)
user << "<span class='warning'>You don't have the dexterity to do this!</span>"
return
+11 -2
View File
@@ -6,11 +6,13 @@ var/list/admin_verbs_default = list(
/client/proc/hide_verbs, //hides all our adminverbs,
/client/proc/hide_most_verbs, //hides all our hideable adminverbs,
/client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify,
/client/proc/cmd_check_new_players, //allows us to see every new player
// /client/proc/check_antagonists, //shows all antags,
// /client/proc/cmd_mod_say,
// /client/proc/deadchat //toggles deadchat on/off,
// /client/proc/toggle_ahelp_sound,
)
var/list/admin_verbs_admin = list(
/client/proc/player_panel_new, //shows an interface for all players, with links to various panels,
/datum/admins/proc/set_tcrystals,
@@ -99,16 +101,19 @@ var/list/admin_verbs_admin = list(
/datum/admins/proc/paralyze_mob,
/client/proc/fixatmos,
/datum/admins/proc/sendFax
)
)
var/list/admin_verbs_ban = list(
/client/proc/unban_panel,
/client/proc/jobbans
)
var/list/admin_verbs_sounds = list(
/client/proc/play_local_sound,
/client/proc/play_sound,
/client/proc/play_server_sound
)
var/list/admin_verbs_fun = list(
/client/proc/object_talk,
/datum/admins/proc/cmd_admin_dress,
@@ -141,6 +146,7 @@ var/list/admin_verbs_spawn = list(
/client/proc/map_template_upload,
/client/proc/map_template_load_on_new_z
)
var/list/admin_verbs_server = list(
/datum/admins/proc/capture_map,
/client/proc/Set_Holiday,
@@ -168,6 +174,7 @@ var/list/admin_verbs_server = list(
/client/proc/recipe_dump,
/client/proc/panicbunker
)
var/list/admin_verbs_debug = list(
/client/proc/getruntimelog, //allows us to access runtime logs to somebody,
/client/proc/cmd_admin_list_open_jobs,
@@ -209,7 +216,8 @@ var/list/admin_verbs_debug = list(
/client/proc/show_gm_status,
/datum/admins/proc/change_weather,
/datum/admins/proc/change_time,
/client/proc/admin_give_modifier
/client/proc/admin_give_modifier,
/client/proc/simple_DPS
)
var/list/admin_verbs_paranoid_debug = list(
@@ -290,6 +298,7 @@ var/list/admin_verbs_hideable = list(
/client/proc/kill_airgroup,
/client/proc/debug_controller,
/client/proc/startSinglo,
/client/proc/simple_DPS,
/client/proc/cmd_debug_mob_lists,
/client/proc/cmd_debug_using_map,
/client/proc/cmd_debug_del_all,
+50
View File
@@ -16,6 +16,56 @@
// callproc moved to code/modules/admin/callproc
/client/proc/simple_DPS()
set name = "Simple DPS"
set category = "Debug"
set desc = "Gives a really basic idea of how much hurt something in-hand does."
var/obj/item/I = null
var/mob/living/user = null
if(isliving(usr))
user = usr
I = user.get_active_hand()
if(!I || !istype(I))
to_chat(user, "<span class='warning'>You need to have something in your active hand, to use this verb.</span>")
return
var/weapon_attack_speed = user.get_attack_speed(I) / 10
var/weapon_damage = I.force
if(istype(I, /obj/item/weapon/gun))
var/obj/item/weapon/gun/G = I
var/obj/item/projectile/P
if(istype(I, /obj/item/weapon/gun/energy))
var/obj/item/weapon/gun/energy/energy_gun = G
P = new energy_gun.projectile_type()
else if(istype(I, /obj/item/weapon/gun/projectile))
var/obj/item/weapon/gun/projectile/projectile_gun = G
var/obj/item/ammo_casing/ammo = projectile_gun.chambered
P = ammo.BB
else
to_chat(user, "<span class='warning'>DPS calculation by this verb is not supported for \the [G]'s type. Energy or Ballistic only, sorry.</span>")
weapon_damage = P.damage
weapon_attack_speed = G.fire_delay / 10
qdel(P)
var/DPS = weapon_damage / weapon_attack_speed
to_chat(user, "<span class='notice'>Damage: [weapon_damage]</span>")
to_chat(user, "<span class='notice'>Attack Speed: [weapon_attack_speed]/s</span>")
to_chat(user, "<span class='notice'>\The [I] does <b>[DPS]</b> damage per second.</span>")
if(DPS > 0)
to_chat(user, "<span class='notice'>At your maximum health ([user.getMaxHealth()]), it would take approximately;</span>")
to_chat(user, "<span class='notice'>[(user.getMaxHealth() - config.health_threshold_softcrit) / DPS] seconds to softcrit you. ([config.health_threshold_softcrit] health)</span>")
to_chat(user, "<span class='notice'>[(user.getMaxHealth() - config.health_threshold_crit) / DPS] seconds to hardcrit you. ([config.health_threshold_crit] health)</span>")
to_chat(user, "<span class='notice'>[(user.getMaxHealth() - config.health_threshold_dead) / DPS] seconds to kill you. ([config.health_threshold_dead] health)</span>")
else
to_chat(user, "<span class='warning'>You need to be a living mob, with hands, and for an object to be in your active hand, to use this verb.</span>")
return
/client/proc/Cell()
set category = "Debug"
set name = "Cell"
+34
View File
@@ -43,6 +43,40 @@
message_admins("<font color='blue'>[key_name_admin(usr)] sent [key_name_admin(M)] to the prison station.</font>", 1)
feedback_add_details("admin_verb","PRISON") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//Allows staff to determine who the newer players are.
/client/proc/cmd_check_new_players()
set category = "Admin"
set name = "Check new Players"
if(!holder)
src << "Only staff members may use this command."
var/age = alert(src, "Age check", "Show accounts yonger then _____ days","7", "30" , "All")
if(age == "All")
age = 9999999
else
age = text2num(age)
var/missing_ages = 0
var/msg = ""
var/highlight_special_characters = 1
for(var/client/C in clients)
if(C.player_age == "Requires database")
missing_ages = 1
continue
if(C.player_age < age)
msg += "[key_name(C, 1, 1, highlight_special_characters)]: account is [C.player_age] days old<br>"
if(missing_ages)
src << "Some accounts did not have proper ages set in their clients. This function requires database to be present."
if(msg != "")
src << browse(msg, "window=Player_age_check")
else
src << "No matches for that age range found."
/client/proc/cmd_admin_subtle_message(mob/M as mob in mob_list)
set category = "Special Verbs"
set name = "Subtle Message"
+2
View File
@@ -112,6 +112,8 @@
/obj/effect/blob/proc/pulse(var/forceLeft, var/list/dirs)
regen()
animate(src, color = "#FF0000", time=1)
animate(color = "#FFFFFF", time=4, easing=ELASTIC_EASING)
sleep(5)
var/pushDir = pick(dirs)
var/turf/T = get_step(src, pushDir)
+10
View File
@@ -0,0 +1,10 @@
#define BLOB_CORE_PULSE_RANGE 6
#define BLOB_NODE_PULSE_RANGE 4
#define BLOB_CORE_EXPAND_RANGE 8
#define BLOB_NODE_EXPAND_RANGE 6
#define BLOB_DIFFICULTY_EASY 0
#define BLOB_DIFFICULTY_MEDIUM 1
#define BLOB_DIFFICULTY_HARD 2
#define BLOB_DIFFICULTY_SUPERHARD 3
+18
View File
@@ -0,0 +1,18 @@
/proc/level_seven_blob_announcement(var/obj/structure/blob/core/B)
if(!B || !B.overmind)
return
var/datum/blob_type/blob = B.overmind.blob_type // Shortcut so we don't need to delve into three variables every time.
var/list/lines = list()
lines += "Confirmed outbreak of level [7 + blob.difficulty] biohazard aboard [station_name()]. All personnel must contain the outbreak."
if(blob.difficulty >= BLOB_DIFFICULTY_MEDIUM) // Tell them what kind of blob it is if it's tough.
lines += "The biohazard has been identified as a '[blob.name]'."
if(blob.difficulty >= BLOB_DIFFICULTY_HARD) // If it's really hard then tell them where it is so the response occurs faster.
lines += "It is suspected to have originated from \the [get_area(B)]."
if(blob.difficulty >= BLOB_DIFFICULTY_SUPERHARD)
lines += "Extreme caution is advised."
command_announcement.Announce(lines.Join("\n"), "Biohazard Alert", new_sound = 'sound/AI/outbreak7.ogg')
+298
View File
@@ -0,0 +1,298 @@
var/list/blobs = list()
/obj/structure/blob
name = "blob"
icon = 'icons/mob/blob.dmi'
desc = "A thick wall of writhing tendrils."
light_range = 2
density = FALSE // This is false because blob mob AI's walk_to() proc appears to never attempt to move onto dense objects even if allowed by CanPass().
opacity = FALSE
anchored = TRUE
layer = MOB_LAYER + 0.1
var/integrity = 0
var/point_return = 0 //How many points the blob gets back when it removes a blob of that type. If less than 0, blob cannot be removed.
var/max_integrity = 30
var/health_regen = 2 //how much health this blob regens when pulsed
var/pulse_timestamp = 0 //we got pulsed when?
var/heal_timestamp = 0 //we got healed when?
var/mob/observer/blob/overmind = null
var/base_name = "blob" // The name that gets appended along with the blob_type's name.
/obj/structure/blob/New(var/newloc, var/new_overmind)
..(newloc)
if(new_overmind)
overmind = new_overmind
update_icon()
if(!integrity)
integrity = max_integrity
set_dir(pick(cardinal))
blobs += src
consume_tile()
/obj/structure/blob/Destroy()
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Expand() is no longer broken, no check necessary.
blobs -= src
overmind = null
return ..()
/obj/structure/blob/update_icon() //Updates color based on overmind color if we have an overmind.
if(overmind)
name = "[overmind.blob_type.name] [base_name]" // This is in update_icon() because inert blobs can turn into other blobs with magic if another blob core claims it with pulsing.
color = overmind.blob_type.color
set_light(3, 3, color)
else
name = "inert [base_name]"
color = null
set_light(0)
/obj/structure/blob/CanPass(atom/movable/mover, turf/target, height=0, air_group=0)
if(air_group || (height==0))
return TRUE
if(istype(mover) && mover.checkpass(PASSBLOB))
return TRUE
else
return FALSE
// return ..()
/obj/structure/blob/examine(mob/user)
..()
if(!overmind)
to_chat(user, "It seems inert.") // Dead blob.
else
to_chat(user, overmind.blob_type.desc)
/obj/structure/blob/get_description_info()
if(overmind)
return overmind.blob_type.effect_desc
return ..()
/obj/structure/blob/emp_act(severity)
if(overmind)
overmind.blob_type.on_emp(src, severity)
/obj/structure/blob/proc/pulsed()
if(pulse_timestamp <= world.time)
consume_tile()
if(heal_timestamp <= world.time)
adjust_integrity(health_regen)
heal_timestamp = world.time + 2 SECONDS
update_icon()
pulse_timestamp = world.time + 1 SECOND
if(overmind)
overmind.blob_type.on_pulse(src)
return TRUE //we did it, we were pulsed!
return FALSE //oh no we failed
/obj/structure/blob/proc/pulse_area(pulsing_overmind = overmind, claim_range = 10, pulse_range = 3, expand_range = 2)
src.pulsed()
var/expanded = FALSE
if(prob(70) && expand())
expanded = TRUE
var/list/blobs_to_affect = list()
for(var/obj/structure/blob/B in urange(claim_range, src, 1))
blobs_to_affect += B
shuffle_inplace(blobs_to_affect)
for(var/L in blobs_to_affect)
var/obj/structure/blob/B = L
if(!B.overmind && !istype(B, /obj/structure/blob/core) && prob(30))
B.overmind = pulsing_overmind //reclaim unclaimed, non-core blobs.
B.update_icon()
var/distance = get_dist(get_turf(src), get_turf(B))
var/expand_probablity = max(50 / (max(distance, 1)), 1)
if(overmind)
expand_probablity *= overmind.blob_type.spread_modifier
if(overmind.blob_type.slow_spread_with_size)
expand_probablity /= (blobs.len / 10)
if(distance <= expand_range)
var/can_expand = TRUE
if(blobs_to_affect.len >= 120 && B.heal_timestamp > world.time)
can_expand = FALSE
if(!expanded && can_expand && B.pulse_timestamp <= world.time && prob(expand_probablity))
var/obj/structure/blob/newB = B.expand(null, null, !expanded) //expansion falls off with range but is faster near the blob causing the expansion
if(newB)
if(expanded)
qdel(newB)
expanded = TRUE
if(distance <= pulse_range)
B.pulsed()
/obj/structure/blob/proc/expand(turf/T = null, controller = null, expand_reaction = 1)
if(!T)
var/list/dirs = cardinal.Copy()
for(var/i = 1 to 4)
var/dirn = pick(dirs)
dirs.Remove(dirn)
T = get_step(src, dirn)
if(!(locate(/obj/structure/blob) in T))
break
else
T = null
if(!T)
return FALSE
var/make_blob = TRUE //can we make a blob?
if(istype(T, /turf/space) && !(locate(/obj/structure/lattice) in T) && prob(80))
make_blob = FALSE
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) //Let's give some feedback that we DID try to spawn in space, since players are used to it
consume_tile() //hit the tile we're in, making sure there are no border objects blocking us
if(!T.CanPass(src, T)) //is the target turf impassable
make_blob = FALSE
T.blob_act(src) //hit the turf if it is
for(var/atom/A in T)
if(!A.CanPass(src, T)) //is anything in the turf impassable
make_blob = FALSE
A.blob_act(src) //also hit everything in the turf
if(make_blob) //well, can we?
var/obj/structure/blob/B = new /obj/structure/blob/normal(src.loc)
if(controller)
B.overmind = controller
else
B.overmind = overmind
B.density = TRUE
if(T.Enter(B,src)) //NOW we can attempt to move into the tile
sleep(1) // To have the slide animation work.
B.density = initial(B.density)
B.forceMove(T)
B.update_icon()
if(B.overmind && expand_reaction)
B.overmind.blob_type.on_expand(src, B, T, B.overmind)
return B
else
blob_attack_animation(T, controller)
T.blob_act(src) //if we can't move in hit the turf again
qdel(B) //we should never get to this point, since we checked before moving in. destroy the blob so we don't have two blobs on one tile
return null
else
blob_attack_animation(T, controller) //if we can't, animate that we attacked
return null
/obj/structure/blob/proc/consume_tile()
for(var/atom/A in loc)
A.blob_act(src)
if(loc && loc.density)
loc.blob_act(src) //don't ask how a wall got on top of the core, just eat it
/obj/structure/blob/proc/blob_glow_animation()
flick("[icon_state]_glow", src)
/obj/structure/blob/proc/blob_attack_animation(atom/A = null, controller) //visually attacks an atom
var/obj/effect/temporary_effect/blob_attack/O = new /obj/effect/temporary_effect/blob_attack(src.loc)
O.set_dir(dir)
if(controller)
var/mob/observer/blob/BO = controller
O.color = BO.blob_type.color
O.alpha = 200
else if(overmind)
O.color = overmind.blob_type.color
if(A)
O.do_attack_animation(A) //visually attack the whatever
return O //just in case you want to do something to the animation.
/obj/structure/blob/proc/change_to(type, controller)
if(!ispath(type))
throw EXCEPTION("change_to(): invalid type for blob")
return
var/obj/structure/blob/B = new type(src.loc, controller)
if(controller)
B.overmind = controller
B.update_icon()
B.set_dir(dir)
qdel(src)
return B
/obj/structure/blob/attackby(var/obj/item/weapon/W, var/mob/user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
playsound(loc, 'sound/effects/attackblob.ogg', 50, 1)
visible_message("<span class='danger'>\The [src] has been attacked with \the [W][(user ? " by [user]." : ".")]</span>")
var/damage = W.force
switch(W.damtype)
if(BURN)
if(overmind)
damage *= overmind.blob_type.burn_multiplier
else
damage *= 2
if(damage > 0)
playsound(src.loc, 'sound/items/welder.ogg', 100, 1)
else
playsound(src, 'sound/weapons/tap.ogg', 50, 1)
if(BRUTE)
if(overmind)
damage *= overmind.blob_type.brute_multiplier
else
damage *= 2
if(damage > 0)
playsound(src.loc, 'sound/effects/attackblob.ogg', 50, 1)
else
playsound(src, 'sound/weapons/tap.ogg', 50, 1)
if(overmind)
damage = overmind.blob_type.on_received_damage(src, damage, W.damtype, user)
adjust_integrity(-damage)
return
/obj/structure/blob/bullet_act(var/obj/item/projectile/P)
if(!P)
return
var/damage = P.damage
switch(P.damage_type)
if(BRUTE)
if(overmind)
damage *= overmind.blob_type.brute_multiplier
if(BURN)
if(overmind)
damage *= overmind.blob_type.burn_multiplier
if(overmind)
damage = overmind.blob_type.on_received_damage(src, damage, P.damage_type, P.firer)
adjust_integrity(-damage)
return ..()
/obj/structure/blob/water_act(amount)
if(overmind)
overmind.blob_type.on_water(src, amount)
/obj/structure/blob/proc/adjust_integrity(amount)
integrity = between(0, integrity + amount, max_integrity)
if(integrity == 0)
playsound(loc, 'sound/effects/splat.ogg', 50, 1)
if(overmind)
overmind.blob_type.on_death(src)
qdel(src)
else
update_icon()
/obj/effect/temporary_effect/blob_attack
name = "blob"
desc = "The blob lashing out at something."
icon = 'icons/effects/effects.dmi'
icon_state = "blob_attack"
layer = 5.2
time_to_die = 6
alpha = 140
invisibility = 0
mouse_opacity = 0
new_light_range = 0
new_light_power = 0
/obj/structure/grille/blob_act()
qdel(src)
/turf/simulated/wall/blob_act()
take_damage(100)
+179
View File
@@ -0,0 +1,179 @@
var/list/blob_cores = list()
/obj/structure/blob/core
name = "blob core"
base_name = "core"
icon = 'icons/mob/blob.dmi'
icon_state = "blank_blob"
desc = "A huge, pulsating yellow mass."
max_integrity = 150
point_return = -1
health_regen = 0 //we regen in Life() instead of when pulsed
var/datum/blob_type/desired_blob_type = null // If this is set, the core always creates an overmind possessing this blob type.
var/difficulty_threshold = null // Otherwise if this is set, it picks a random blob_type that is equal or lower in difficulty.
var/core_regen = 2
var/overmind_get_delay = 0 //we don't want to constantly try to find an overmind, this var tracks when we'll try to get an overmind again
var/resource_delay = 0
var/point_rate = 2
var/ai_controlled = TRUE
// Spawn this if you want a ghost to be able to play as the blob.
/obj/structure/blob/core/player
ai_controlled = FALSE
// Spawn these if you want a semi-random blob.
/obj/structure/blob/core/random_easy
difficulty_threshold = BLOB_DIFFICULTY_EASY
/obj/structure/blob/core/random_medium
difficulty_threshold = BLOB_DIFFICULTY_MEDIUM
/obj/structure/blob/core/random_hard
difficulty_threshold = BLOB_DIFFICULTY_HARD
// Spawn these if you want a specific blob.
/obj/structure/blob/core/blazing_oil
desired_blob_type = /datum/blob_type/blazing_oil
/obj/structure/blob/core/grey_goo
desired_blob_type = /datum/blob_type/grey_goo
/obj/structure/blob/core/electromagnetic_web
desired_blob_type = /datum/blob_type/electromagnetic_web
/obj/structure/blob/core/fungal_bloom
desired_blob_type = /datum/blob_type/fungal_bloom
/obj/structure/blob/core/fulminant_organism
desired_blob_type = /datum/blob_type/fulminant_organism
/obj/structure/blob/core/reactive_spines
desired_blob_type = /datum/blob_type/reactive_spines
/obj/structure/blob/core/synchronous_mesh
desired_blob_type = /datum/blob_type/synchronous_mesh
/obj/structure/blob/core/shifting_fragments
desired_blob_type = /datum/blob_type/shifting_fragments
/obj/structure/blob/core/cryogenic_goo
desired_blob_type = /datum/blob_type/cryogenic_goo
/obj/structure/blob/core/energized_jelly
desired_blob_type = /datum/blob_type/energized_jelly
/obj/structure/blob/core/explosive_lattice
desired_blob_type = /datum/blob_type/explosive_lattice
/obj/structure/blob/core/pressurized_slime
desired_blob_type = /datum/blob_type/pressurized_slime
/obj/structure/blob/core/radioactive_ooze
desired_blob_type = /datum/blob_type/radioactive_ooze
/obj/structure/blob/core/classic
desired_blob_type = /datum/blob_type/classic
/obj/structure/blob/core/New(var/newloc, var/client/new_overmind = null, new_rate = 2, placed = 0)
..(newloc)
blob_cores += src
processing_objects += src
update_icon() //so it atleast appears
if(!placed && !overmind)
create_overmind(new_overmind)
if(overmind)
update_icon()
point_rate = new_rate
/obj/structure/blob/core/Destroy()
blob_cores -= src
if(overmind)
overmind.blob_core = null
qdel(overmind)
overmind = null
processing_objects -= src
return ..()
/obj/structure/blob/core/update_icon()
overlays.Cut()
color = null
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
if(overmind)
blob_overlay.color = overmind.blob_type.color
name = "[overmind.blob_type.name] [base_name]"
overlays += blob_overlay
overlays += mutable_appearance('icons/mob/blob.dmi', "blob_core_overlay")
/obj/structure/blob/core/process()
set waitfor = FALSE
if(QDELETED(src))
return
if(!overmind)
spawn(0)
create_overmind()
else
if(resource_delay <= world.time)
resource_delay = world.time + 1 SECOND
overmind.add_points(point_rate)
integrity = min(max_integrity, integrity + core_regen)
// if(overmind)
// overmind.update_health_hud()
pulse_area(overmind, 15, BLOB_CORE_PULSE_RANGE, BLOB_CORE_EXPAND_RANGE)
for(var/obj/structure/blob/normal/B in range(1, src))
if(prob(5))
B.change_to(/obj/structure/blob/shield/core, overmind)
/obj/structure/blob/core/proc/create_overmind(client/new_overmind, override_delay)
if(overmind_get_delay > world.time && !override_delay)
return
if(!ai_controlled) // Do we want a bona fide player blob?
overmind_get_delay = world.time + 15 SECONDS //if this fails, we'll try again in 15 seconds
if(overmind)
qdel(overmind)
var/client/C = null
if(!new_overmind)
var/datum/ghost_query/Q = new /datum/ghost_query/blob()
var/list/winner = Q.query()
if(winner.len)
var/mob/observer/dead/D = winner[1]
C = D.client
else
C = new_overmind
if(C)
if(!desired_blob_type && !isnull(difficulty_threshold))
desired_blob_type = get_random_blob_type()
var/mob/observer/blob/B = new(loc, TRUE, 60, desired_blob_type)
B.key = C.key
B.blob_core = src
src.overmind = B
update_icon()
if(B.mind && !B.mind.special_role)
B.mind.special_role = "Blob Overmind"
return TRUE
return FALSE
else // An AI opponent.
if(!desired_blob_type && !isnull(difficulty_threshold))
desired_blob_type = get_random_blob_type()
var/mob/observer/blob/B = new(loc, TRUE, 60, desired_blob_type)
overmind = B
B.blob_core = src
B.ai_controlled = TRUE
update_icon()
return TRUE
/obj/structure/blob/core/proc/get_random_blob_type()
if(!difficulty_threshold)
return
var/list/valid_types = list()
for(var/thing in subtypesof(/datum/blob_type))
var/datum/blob_type/BT = thing
if(initial(BT.difficulty) > difficulty_threshold)
continue
valid_types += BT
return pick(valid_types)
+37
View File
@@ -0,0 +1,37 @@
/obj/structure/blob/factory
name = "factory blob"
base_name = "factory"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_factory"
desc = "A thick spire of tendrils."
description_info = "A section of the blob that creates numerous hostile entities to attack enemies of the blob. \
It requires a 'node' blob be nearby, or it will cease functioning."
max_integrity = 40
health_regen = 1
point_return = 25
var/list/spores = list()
var/max_spores = 3
var/spore_delay = 0
var/spore_cooldown = 8 SECONDS
/obj/structure/blob/factory/Destroy()
for(var/mob/living/simple_animal/hostile/blob/spore/spore in spores)
if(spore.factory == src)
spore.factory = null
spores = null
return ..()
/obj/structure/blob/factory/pulsed()
. = ..()
if(spores.len >= max_spores)
return
if(spore_delay > world.time)
return
flick("blob_factory_glow", src)
spore_delay = world.time + spore_cooldown
var/mob/living/simple_animal/hostile/blob/spore/S = null
if(overmind)
S = new overmind.blob_type.spore_type(src.loc, src)
S.overmind = overmind
S.update_icons()
overmind.blob_mobs.Add(S)
+36
View File
@@ -0,0 +1,36 @@
var/list/blob_nodes = list()
/obj/structure/blob/node
name = "blob node"
base_name = "node"
icon_state = "blank_blob"
desc = "A large, pulsating yellow mass."
max_integrity = 50
health_regen = 3
point_return = 50
/obj/structure/blob/node/New(var/newloc)
..()
blob_nodes += src
processing_objects += src
update_icon()
/obj/structure/blob/node/Destroy()
blob_nodes -= src
processing_objects -= src
return ..()
/obj/structure/blob/node/update_icon()
overlays.Cut()
color = null
var/mutable_appearance/blob_overlay = mutable_appearance('icons/mob/blob.dmi', "blob")
if(overmind)
name = "[overmind.blob_type.name] [base_name]"
blob_overlay.color = overmind.blob_type.color
overlays += blob_overlay
overlays += mutable_appearance('icons/mob/blob.dmi', "blob_node_overlay")
/obj/structure/blob/node/process()
set waitfor = FALSE
if(overmind) // This check is so that if the core is killed, the nodes stop.
pulse_area(overmind, 10, BLOB_NODE_PULSE_RANGE, BLOB_NODE_EXPAND_RANGE)
+22
View File
@@ -0,0 +1,22 @@
/obj/structure/blob/normal
name = "normal blob"
base_name = "blob"
icon_state = "blob"
light_range = 0
integrity = 21 //doesn't start at full health
max_integrity = 25
health_regen = 1
/obj/structure/blob/normal/update_icon()
..()
if(integrity <= 15)
icon_state = "blob_damaged"
desc = "A thin lattice of slightly twitching tendrils."
else
icon_state = "blob"
desc = "A thick wall of writhing tendrils."
if(overmind)
name = "[overmind.blob_type.name]"
else
name = "inert [base_name]"
+30
View File
@@ -0,0 +1,30 @@
/obj/structure/blob/resource
name = "resource blob"
base_name = "resource blob"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_resource"
desc = "A thin spire of slightly swaying tendrils."
max_integrity = 40
point_return = 15
var/resource_delay = 0
/obj/structure/blob/resource/New(var/newloc, var/new_overmind)
..(newloc, new_overmind)
if(overmind)
overmind.resource_blobs += src
/obj/structure/blob/resource/Destroy()
if(overmind)
overmind.resource_blobs -= src
return ..()
/obj/structure/blob/resource/pulsed()
. = ..()
if(resource_delay > world.time)
return
flick("blob_resource_glow", src)
if(overmind)
overmind.add_points(1)
resource_delay = world.time + 4 SECONDS + (overmind.resource_blobs.len * 2.5) //4 seconds plus a quarter second for each resource blob the overmind has
else
resource_delay = world.time + 4 SECONDS
+25
View File
@@ -0,0 +1,25 @@
/obj/structure/blob/shield
name = "thick blob"
base_name = "thick"
icon = 'icons/mob/blob.dmi'
icon_state = "blob_shield"
desc = "A solid wall of slightly twitching tendrils."
max_integrity = 100
point_return = 4
/obj/structure/blob/shield/core
point_return = 0
/obj/structure/blob/shield/update_icon()
..()
if(integrity <= 75)
icon_state = "blob_shield_damaged"
desc = "A wall of twitching tendrils."
else
icon_state = initial(icon_state)
desc = initial(desc)
if(overmind)
name = "[base_name] [overmind.blob_type.name]"
else
name = "inert [base_name] blob"
+57
View File
@@ -0,0 +1,57 @@
////////////////
// BASE TYPE //
////////////////
//Do not spawn
/mob/living/simple_animal/hostile/blob
icon = 'icons/mob/blob.dmi'
pass_flags = PASSBLOB | PASSTABLE
faction = "blob"
// bubble_icon = "blob"
// speak_emote = null //so we use verb_yell/verb_say/etc
// atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
// minbodytemp = 0
// maxbodytemp = 360
// unique_name = 1
// a_intent = INTENT_HARM
cooperative = TRUE
heat_damage_per_tick = 0
cold_damage_per_tick = 0
min_oxy = 0
max_tox = 0
max_co2 = 0
var/mob/observer/blob/overmind = null
var/obj/structure/blob/factory/factory = null
/mob/living/simple_animal/hostile/blob/speech_bubble_appearance()
return "slime"
/mob/living/simple_animal/hostile/blob/update_icons()
if(overmind)
color = overmind.blob_type.complementary_color
else
color = null
/mob/living/simple_animal/hostile/blob/Destroy()
if(overmind)
overmind.blob_mobs -= src
return ..()
/mob/living/simple_animal/hostile/blob/blob_act(obj/structure/blob/B)
if(!overmind && B.overmind)
overmind = B.overmind
update_icon()
if(stat != DEAD && health < maxHealth)
adjustBruteLoss(-maxHealth*0.0125)
adjustFireLoss(-maxHealth*0.0125)
/mob/living/simple_animal/hostile/blob/CanPass(atom/movable/mover, turf/target)
if(istype(mover, /obj/structure/blob)) // Don't block blobs from expanding onto a tile occupied by a blob mob.
return TRUE
return ..()
/mob/living/simple_animal/hostile/blob/Process_Spacemove()
for(var/obj/structure/blob/B in range(1, src))
return TRUE
return ..()
+110
View File
@@ -0,0 +1,110 @@
////////////////
// BLOB SPORE //
////////////////
/mob/living/simple_animal/hostile/blob/spore
name = "blob spore"
desc = "A floating, fragile spore."
icon_state = "blobpod"
icon_living = "blobpod"
health = 30
maxHealth = 30
melee_damage_lower = 2
melee_damage_upper = 4
layer = MOB_LAYER + 0.2 // Over the blob.
attacktext = "slams into"
attack_sound = 'sound/effects/slime_squish.ogg'
emote_see = list("sways", "inflates briefly")
var/mob/living/carbon/human/infested = null // The human this thing is totally not making into a zombie.
var/can_infest = FALSE
var/is_infesting = FALSE
/mob/living/simple_animal/hostile/blob/spore/infesting
name = "infesting blob spore"
can_infest = TRUE
/mob/living/simple_animal/hostile/blob/spore/weak
name = "fragile blob spore"
health = 15
maxHealth = 15
melee_damage_lower = 1
melee_damage_upper = 2
/mob/living/simple_animal/hostile/blob/spore/New(var/newloc, var/obj/structure/blob/factory/my_factory)
if(istype(my_factory))
factory = my_factory
factory.spores += src
..(newloc)
/mob/living/simple_animal/hostile/blob/spore/Destroy()
if(factory)
factory.spores -= src
factory = null
if(infested)
infested.forceMove(get_turf(src))
visible_message("<span class='warning'>\The [infested] falls to the ground as the blob spore bursts.</span>")
infested = null
return ..()
/mob/living/simple_animal/hostile/blob/spore/death(gibbed, deathmessage = "bursts!")
if(overmind)
overmind.blob_type.on_spore_death(src)
..(gibbed, deathmessage)
qdel(src)
/mob/living/simple_animal/hostile/blob/spore/update_icons()
if(overmind)
color = overmind.blob_type.complementary_color
set_light(3, 5, color)
else
color = null
set_light(0)
if(is_infesting)
overlays.Cut()
icon = infested.icon
overlays = infested.overlays
var/mutable_appearance/blob_head_overlay = mutable_appearance('icons/mob/blob.dmi', "blob_head")
if(overmind)
blob_head_overlay.color = overmind.blob_type.complementary_color
color = initial(color)//looks better.
overlays += blob_head_overlay
/mob/living/simple_animal/hostile/blob/spore/Life()
if(can_infest && !is_infesting && isturf(src.loc))
for(var/mob/living/carbon/human/H in view(src,1))
if(H.stat != DEAD) // We want zombies.
continue
if(H.isSynthetic()) // Not philosophical zombies.
continue
infest(H)
break
if(factory && z != factory.z) // This is to prevent spores getting lost in space and making the factory useless.
qdel(src)
..()
/mob/living/simple_animal/hostile/blob/spore/proc/infest(mob/living/carbon/human/H)
is_infesting = TRUE
if(H.wear_suit)
var/obj/item/clothing/suit/A = H.wear_suit
if(A.armor && A.armor["melee"])
maxHealth += A.armor["melee"] //That zombie's got armor, I want armor!
maxHealth += 40
health = maxHealth
name = "Infested [H.real_name]" // Not using the Z word.
desc = "A parasitic organism attached to a deceased body, controlling it directly as if it were a puppet."
melee_damage_lower += 8 // 10 total.
melee_damage_upper += 11 // 15 total.
emote_see = list("shambles around", "twitches", "stares")
attacktext = "claws"
H.forceMove(src)
infested = H
update_icons()
visible_message("<span class='warning'>The corpse of [H.name] suddenly rises!</span>")
/mob/living/simple_animal/hostile/blob/spore/GetIdCard()
if(infested) // If we've infested someone, use their ID.
return infested.GetIdCard()
+97
View File
@@ -0,0 +1,97 @@
var/list/overminds = list()
/mob/observer/blob
name = "Blob Overmind"
real_name = "Blob Overmind"
desc = "The overmind. It controls the blob."
icon = 'icons/mob/blob.dmi'
icon_state = "marker"
mouse_opacity = 1
see_in_dark = 8
invisibility = INVISIBILITY_OBSERVER
layer = FLY_LAYER + 0.1
faction = "blob"
var/obj/structure/blob/core/blob_core = null // The blob overmind's core
var/blob_points = 0
var/max_blob_points = 200
var/last_attack = 0
var/datum/blob_type/blob_type = null
var/list/blob_mobs = list()
var/list/resource_blobs = list()
var/placed = 0
var/base_point_rate = 2 //for blob core placement
var/ai_controlled = TRUE
var/auto_pilot = FALSE // If true, and if a client is attached, the AI routine will continue running.
/mob/observer/blob/New(var/newloc, pre_placed = 0, starting_points = 60, desired_blob_type = null)
blob_points = starting_points
if(pre_placed) //we already have a core!
placed = 1
overminds += src
var/new_name = "[initial(name)] ([rand(1, 999)])"
name = new_name
real_name = new_name
if(desired_blob_type)
blob_type = new desired_blob_type()
else
var/datum/blob_type/BT = pick(subtypesof(/datum/blob_type))
blob_type = new BT()
color = blob_type.complementary_color
if(blob_core)
blob_core.update_icon()
level_seven_blob_announcement(blob_core)
..(newloc)
/mob/observer/blob/Destroy()
for(var/BL in blobs)
var/obj/structure/blob/B = BL
if(B && B.overmind == src)
B.overmind = null
B.update_icon() //reset anything that was ours
for(var/BLO in blob_mobs)
var/mob/living/simple_animal/hostile/blob/BM = BLO
if(BM)
BM.overmind = null
BM.update_icons()
overminds -= src
return ..()
/mob/observer/blob/Stat()
..()
if(statpanel("Status"))
if(blob_core)
stat(null, "Core Health: [blob_core.integrity]")
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
stat(null, "Total Blobs: [blobs.len]")
/mob/observer/blob/Move(NewLoc, Dir = 0)
if(placed)
var/obj/structure/blob/B = locate() in range("3x3", NewLoc)
if(B)
forceMove(NewLoc)
return TRUE
else
return FALSE
else
var/area/A = get_area(NewLoc)
if(istype(NewLoc, /turf/space) || istype(A, /area/shuttle)) //if unplaced, can't go on shuttles or space tiles
return FALSE
forceMove(NewLoc)
return TRUE
/mob/observer/blob/proc/add_points(points)
blob_points = between(0, blob_points + points, max_blob_points)
/mob/observer/blob/Life()
if(ai_controlled && (!client || auto_pilot))
if(prob(blob_type.ai_aggressiveness))
auto_attack()
if(blob_points >= 100)
if(!auto_factory() && !auto_resource())
auto_node()
+229
View File
@@ -0,0 +1,229 @@
/mob/observer/blob/proc/can_buy(cost = 15)
if(blob_points < cost)
to_chat(src, "<span class='warning'>You cannot afford this, you need at least [cost] resources!</span>")
return FALSE
add_points(-cost)
return TRUE
/mob/observer/blob/verb/transport_core()
set category = "Blob"
set name = "Jump to Core"
set desc = "Move your camera to your core."
if(blob_core)
forceMove(blob_core.loc)
/mob/observer/blob/proc/createSpecial(price, blobType, nearEquals, needsNode, turf/T)
if(!T)
T = get_turf(src)
var/obj/structure/blob/B = (locate(/obj/structure/blob) in T)
if(!B)
to_chat(src, "<span class='warning'>There is no blob here!</span>")
return
if(!istype(B, /obj/structure/blob/normal))
to_chat(src, "<span class='warning'>Unable to use this blob, find a normal one.</span>")
return
if(nearEquals)
for(var/obj/structure/blob/L in orange(nearEquals, T))
if(L.type == blobType)
to_chat(src, "<span class='warning'>There is a similar blob nearby, move more than [nearEquals] tiles away from it!</span>")
return
if(!can_buy(price))
return
var/obj/structure/blob/N = B.change_to(blobType, src)
return N
/mob/observer/blob/verb/create_shield_power()
set category = "Blob"
set name = "Create Shield Blob (15)"
set desc = "Create a shield blob, which is hard to kill."
create_shield()
/mob/observer/blob/proc/create_shield(turf/T)
createSpecial(15, /obj/structure/blob/shield, 0, 0, T)
/mob/observer/blob/verb/create_resource()
set category = "Blob"
set name = "Create Resource Blob (40)"
set desc = "Create a resource tower which will generate resources for you."
if(!blob_type.can_build_resources)
return FALSE
createSpecial(40, /obj/structure/blob/resource, 4, 1)
/mob/observer/blob/verb/auto_resource()
set category = "Blob"
set name = "Auto Resource Blob (40)"
set desc = "Automatically places a resource tower near a node or your core, at a sufficent distance."
if(!blob_type.can_build_resources)
return FALSE
var/obj/structure/blob/B = null
var/list/potential_blobs = blobs.Copy()
while(potential_blobs.len)
var/obj/structure/blob/temp = pick(potential_blobs)
if(!(locate(/obj/structure/blob/node) in range(temp, BLOB_NODE_PULSE_RANGE) ) && !(locate(/obj/structure/blob/core) in range(temp, BLOB_CORE_PULSE_RANGE) ))
potential_blobs -= temp // Can't be pulsed.
else if(locate(/obj/structure/blob/resource) in range(temp, 4) )
potential_blobs -= temp // Too close to another resource blob.
else if(locate(/obj/structure/blob/core) in range(temp, 1) )
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
B = temp
break
CHECK_TICK // Iterating over a list containing hundreds of blobs can get taxing.
if(B)
forceMove(B.loc)
return createSpecial(40, /obj/structure/blob/resource, 4, 1, B.loc)
/mob/observer/blob/verb/create_factory()
set category = "Blob"
set name = "Create Factory Blob (60)"
set desc = "Create a spore tower that will spawn spores to harass your enemies."
if(!blob_type.can_build_factories)
return FALSE
createSpecial(60, /obj/structure/blob/factory, 7, 1)
/mob/observer/blob/verb/auto_factory()
set category = "Blob"
set name = "Auto Factory Blob (60)"
set desc = "Automatically places a resource tower near a node or your core, at a sufficent distance."
if(!blob_type.can_build_factories)
return FALSE
var/obj/structure/blob/B = null
var/list/potential_blobs = blobs.Copy()
while(potential_blobs.len)
var/obj/structure/blob/temp = pick(potential_blobs)
if(!(locate(/obj/structure/blob/node) in range(temp, BLOB_NODE_PULSE_RANGE) ) && !(locate(/obj/structure/blob/core) in range(temp, BLOB_CORE_PULSE_RANGE) ))
potential_blobs -= temp // Can't be pulsed.
else if(locate(/obj/structure/blob/factory) in range(temp, 7) )
potential_blobs -= temp // Too close to another factory blob.
else if(locate(/obj/structure/blob/core) in range(temp, 1) )
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
B = temp
break
CHECK_TICK
if(B)
forceMove(B.loc)
return createSpecial(60, /obj/structure/blob/factory, 7, 1, B.loc)
/mob/observer/blob/verb/create_node()
set category = "Blob"
set name = "Create Node Blob (100)"
set desc = "Create a node, which will expand blobs around it, and power nearby factory and resource blobs."
if(!blob_type.can_build_nodes)
return FALSE
createSpecial(100, /obj/structure/blob/node, 5, 0)
/mob/observer/blob/verb/auto_node()
set category = "Blob"
set name = "Auto Node Blob (100)"
set desc = "Automatically places a node blob at a sufficent distance."
if(!blob_type.can_build_nodes)
return FALSE
var/obj/structure/blob/B = null
var/list/potential_blobs = blobs.Copy()
while(potential_blobs.len)
var/obj/structure/blob/temp = pick(potential_blobs)
if(locate(/obj/structure/blob/node) in range(temp, 5) )
potential_blobs -= temp
else if(locate(/obj/structure/blob/core) in range(temp, 5) )
potential_blobs -= temp
else if(!istype(temp, /obj/structure/blob/normal))
potential_blobs -= temp
else
B = temp
break
CHECK_TICK
if(B)
forceMove(B.loc)
return createSpecial(100, /obj/structure/blob/node, 5, 0, B.loc)
/mob/observer/blob/verb/expand_blob_power()
set category = "Blob"
set name = "Expand/Attack Blob (4)"
set desc = "Attempts to create a new blob in this tile. If the tile isn't clear, instead attacks it, damaging mobs and objects."
var/turf/T = get_turf(src)
expand_blob(T)
/mob/observer/blob/proc/expand_blob(turf/T)
var/obj/structure/blob/B = null
var/turf/other_T = null
for(var/direction in cardinal)
other_T = get_step(T, direction)
if(other_T)
B = locate(/obj/structure/blob) in other_T
if(B)
break
if(!B)
to_chat(src, "<span class='warning'>There is no blob cardinally adjacent to the target tile!</span>")
return
if(!can_buy(4))
return
B.expand(T)
/mob/observer/blob/verb/auto_attack()
set category = "Blob"
set name = "Auto Attack (4)"
set desc = "Automatically tries to kill whatever's attacking you."
transport_core() // In-case the overmind wandered off somewhere else.
var/list/potential_targets = list()
for(var/mob/living/L in view(src))
if(L.stat == DEAD)
continue // Already dying or dead.
if(L.faction == "blob")
continue // No friendly fire.
if(locate(/obj/structure/blob) in L.loc)
continue // Already has a blob over them.
var/obj/structure/blob/B = null
for(var/direction in cardinal)
var/turf/T = get_step(L, direction)
B = locate(/obj/structure/blob) in T
if(B)
break
if(!B)
continue
potential_targets += L
if(potential_targets.len)
var/mob/living/victim = pick(potential_targets)
var/turf/T = get_turf(victim)
expand_blob(T)
+545
View File
@@ -0,0 +1,545 @@
// There are different kinds of blobs, with different colors, properties, weaknesses, etc. This datum tells the blob objects what kind they are, without a million typepaths.
/datum/blob_type
var/name = "base blob"
var/desc = "This shouldn't exist." // Shown on examine.
var/effect_desc = "This does nothing special." // For examine panel.
var/ai_desc = "default" // Shown when examining the overmind.
var/difficulty = BLOB_DIFFICULTY_EASY // A rough guess on how hard a blob is to kill.
// When a harder blob spawns by event, the crew is given more information than usual from the announcement.
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/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().
var/attack_verb = "attacks" // Used for the visible_message(), as the above is shown to the mob getting hit directly.
// Format is '\The [blob name] [attack_verb] [victim]!' E.g. 'The explosive lattice blasts John Doe!'
var/damage_type = BRUTE // What kind of damage to do to living mobs via blob_act()
var/armor_check = "melee" // What armor to check for when blob_act()-ing living mobs.
var/armor_pen = 0 // How much armor to penetrate(ignore) when attacking via blob_act().
var/damage_lower = 30 // Lower bound for amount of damage to do for attacks.
var/damage_upper = 40 // Upper bound.
var/brute_multiplier = 0.5 // Adjust to make blobs stonger or weaker against brute damage.
var/burn_multiplier = 1.0 // Ditto, for burns.
var/spread_modifier = 0.5 // A multipler on how fast the blob should naturally spread from the core and nodes.
var/slow_spread_with_size = TRUE // Blobs that get really huge will slow down in expansion.
var/ai_aggressiveness = 10 // Probability of the blob AI attempting to attack someone next to the blob, independant of the attacks from node/core pulsing.
var/can_build_factories = FALSE // Forbids this blob type from building factories. Set to true to enable.
var/can_build_resources = FALSE // Ditto, for resource blobs.
var/can_build_nodes = TRUE // Ditto, for nodes.
var/spore_type = /mob/living/simple_animal/hostile/blob/spore
// Called when a blob receives damage. This needs to return the final damage or blobs will be immortal.
/datum/blob_type/proc/on_received_damage(var/obj/structure/blob/B, damage, damage_type)
return damage
// Called when a blob dies due to integrity depletion. Not called if deleted by other means.
/datum/blob_type/proc/on_death(var/obj/structure/blob/B)
return
// Called when a blob expands onto another tile.
/datum/blob_type/proc/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O)
return
// Called when blob_act() is called on a living mob.
/datum/blob_type/proc/on_attack(var/obj/structure/blob/B, var/mob/living/victim, var/def_zone)
return
// Called when the blob is pulsed by a node or the core.
/datum/blob_type/proc/on_pulse(var/obj/structure/blob/B)
return
// Called when hit by EMP.
/datum/blob_type/proc/on_emp(obj/structure/blob/B, severity)
return
// Called when hit by water.
/datum/blob_type/proc/on_water(obj/structure/blob/B, amount)
return
// Spore things
/datum/blob_type/proc/on_spore_death(mob/living/simple_animal/hostile/blob/spore/S)
return
// Subtypes
// Super fast spreading, but weak to EMP.
/datum/blob_type/grey_goo
name = "grey tide"
desc = "A swarm of self replicating nanomachines. Extremely illegal and dangerous, the EIO was meant to prevent this from showing up a second time."
effect_desc = "Spreads much faster than average, but is harmed greatly by electromagnetic pulses."
ai_desc = "genocidal"
difficulty = BLOB_DIFFICULTY_SUPERHARD // Fastest spread of them all and has snowballing capabilities.
color = "#888888"
complementary_color = "#CCCCCC"
spread_modifier = 1.0
slow_spread_with_size = FALSE
ai_aggressiveness = 80
can_build_resources = TRUE
attack_message = "The tide tries to shallow you"
attack_message_living = ", and you feel your skin dissolve"
attack_message_synth = ", and your external plating dissolves"
/datum/blob_type/grey_goo/on_emp(obj/structure/blob/B, severity)
B.adjust_integrity(-(20 / severity))
// A blob meant to be fought like a fire.
/datum/blob_type/blazing_oil
name = "blazing oil"
desc = "A strange, extremely vicious liquid that seems to burn endlessly."
ai_desc = "aggressive"
effect_desc = "Cannot be harmed by burning weapons, and ignites entities it attacks. It will also gradually heat up the area it is in. Water harms it greatly."
difficulty = BLOB_DIFFICULTY_MEDIUM // Emitters don't work but extinguishers are fairly common. Might need fire/atmos suits.
color = "#B68D00"
complementary_color = "#BE5532"
spread_modifier = 0.5
ai_aggressiveness = 50
damage_type = BURN
burn_multiplier = 0 // Fire immunity
attack_message = "The blazing oil splashes you with its burning oil"
attack_message_living = ", and you feel your skin char and melt"
attack_message_synth = ", and your external plating melts"
attack_verb = "splashes"
/datum/blob_type/blazing_oil/on_attack(obj/structure/blob/B, mob/living/victim)
victim.fire_act() // Burn them.
/datum/blob_type/blazing_oil/on_water(obj/structure/blob/B, amount)
spawn(1)
B.adjust_integrity(-(amount * 5))
/datum/blob_type/blazing_oil/on_pulse(var/obj/structure/blob/B)
var/turf/T = get_turf(B)
if(!T)
return
var/datum/gas_mixture/env = T.return_air()
if(env)
env.add_thermal_energy(10 * 1000)
// Mostly a classic blob. No nodes, no other blob types.
/datum/blob_type/classic
name = "lethargic blob"
desc = "A mass that seems bound to its core."
ai_desc = "unambitious"
effect_desc = "Will not create any nodes. Has average strength and resistances."
difficulty = BLOB_DIFFICULTY_EASY // Behaves almost like oldblob, and as such is about as easy as oldblob.
color = "#AAFF00"
complementary_color = "#57787B"
can_build_nodes = FALSE
spread_modifier = 1.0
ai_aggressiveness = 0
// Makes robots cry. Really weak to brute damage.
/datum/blob_type/electromagnetic_web
name = "electromagnetic web"
desc = "A gooy mesh that generates an electromagnetic field. Electronics will likely be ruined if nearby."
ai_desc = "balanced"
effect_desc = "Causes an EMP on attack, and will EMP upon death. It is also more fragile than average, especially to brute force."
difficulty = BLOB_DIFFICULTY_MEDIUM // Rough for robots but otherwise fragile and can be fought at range like most blobs anyways.
color = "#83ECEC"
complementary_color = "#EC8383"
damage_type = BURN
damage_lower = 10
damage_upper = 20
brute_multiplier = 3
burn_multiplier = 2
ai_aggressiveness = 60
attack_message = "The web lashes you"
attack_message_living = ", and you hear a faint buzzing"
attack_message_synth = ", and your electronics get badly damaged"
attack_verb = "lashes"
/datum/blob_type/electromagnetic_web/on_death(obj/structure/blob/B)
empulse(B.loc, 0, 1, 2)
/datum/blob_type/electromagnetic_web/on_attack(obj/structure/blob/B, mob/living/victim)
victim.emp_act(2)
// Makes spores that spread the blob and infest dead people.
/datum/blob_type/fungal_bloom
name = "fungal bloom"
desc = "A massive network of rapidly expanding mycelium. Large spore-like particles can be seen spreading from it."
ai_desc = "swarming"
effect_desc = "Creates floating spores that attack enemies from specialized blobs, and will spread the blob if killed. The spores can also \
infest deceased biological humanoids. It is vulnerable to fire."
difficulty = BLOB_DIFFICULTY_MEDIUM // The spores are more of an annoyance but can be difficult to contain.
color = "#AAAAAA"
complementary_color = "#FFFFFF"
damage_type = TOX
damage_lower = 15
damage_upper = 25
spread_modifier = 0.3 // Lower, since spores will do a lot of the spreading.
burn_multiplier = 3
ai_aggressiveness = 40
can_build_factories = TRUE
spore_type = /mob/living/simple_animal/hostile/blob/spore/infesting
/datum/blob_type/fungal_bloom/on_spore_death(mob/living/simple_animal/hostile/blob/spore/S)
if(S.is_infesting)
return // Don't make blobs if they were on someone's head.
var/turf/T = get_turf(S)
var/obj/structure/blob/B = locate(/obj/structure/blob) in T
if(B) // Is there already a blob here? If so, just heal it.
B.adjust_integrity(10)
else
B = new /obj/structure/blob/normal(T, S.overmind) // Otherwise spread it.
B.visible_message("<span class='danger'>\A [B] forms on \the [T] as \the [S] bursts!</span>")
// Makes tons of weak spores whenever it spreads.
/datum/blob_type/fulminant_organism
name = "fulminant organism"
desc = "A self expanding mass of living biomaterial, that appears to produce entities to defend it, much like a living organism's immune system."
ai_desc = "swarming"
effect_desc = "Creates weak floating spores that attack enemies from specialized blobs, has a chance to also create a spore when \
it spreads onto a new tile, and has a chance to create a spore when a blob tile is destroyed. It is more fragile than average to all types of damage."
difficulty = BLOB_DIFFICULTY_HARD // Loads of spores that can overwhelm, and spreads quickly.
color = "#FF0000" // Red
complementary_color = "#FFCC00" // Orange-ish
damage_type = TOX
damage_lower = 10
damage_upper = 20
spread_modifier = 0.7
burn_multiplier = 1.5
brute_multiplier = 1.5
ai_aggressiveness = 30 // The spores do most of the fighting.
can_build_factories = TRUE
spore_type = /mob/living/simple_animal/hostile/blob/spore/weak
/datum/blob_type/fulminant_organism/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O)
if(prob(10)) // 10% chance to make a weak spore when expanding.
var/mob/living/simple_animal/hostile/blob/S = new spore_type(T)
S.overmind = O
S.update_icons()
O.blob_mobs.Add(S)
/datum/blob_type/fulminant_organism/on_death(obj/structure/blob/B)
if(prob(33)) // 33% chance to make a spore when dying.
var/mob/living/simple_animal/hostile/blob/S = new spore_type(get_turf(B))
B.visible_message("<span class='danger'>A spore floats free from the [name]!</span>")
S.overmind = B.overmind
S.update_icons()
B.overmind.blob_mobs.Add(S)
// Auto-retaliates against melee attacks. Weak to projectiles.
/datum/blob_type/reactive_spines
name = "reactive spines"
desc = "An ever-growing lifeform with a large amount of sharp, powerful looking spines. They look like they could pierce most armor."
ai_desc = "defensive"
effect_desc = "When attacked by a melee weapon, it will automatically retaliate, striking the attacker with an armor piercing attack. \
The blob itself is rather weak to all forms of attacks regardless, and lacks automatic realitation from ranged attacks."
difficulty = BLOB_DIFFICULTY_EASY // Potentially deadly to people not knowing the mechanics, but otherwise fairly tame, due to its slow spread and weakness.
color = "#9ACD32"
complementary_color = "#FFA500"
damage_type = BRUTE
damage_lower = 30
damage_upper = 40
armor_pen = 50 // Even with riot armor and tactical jumpsuit, you'd have 90 armor, reduced by 50, totaling 40. Getting hit for around 21 damage is still rough.
burn_multiplier = 2.0
brute_multiplier = 2.0
spread_modifier = 0.35 // Ranged projectiles tend to have a higher material cost, so ease up on the spreading.
ai_aggressiveness = 40
attack_message = "The blob stabs you"
attack_message_living = ", and you feel sharp spines pierce your flesh"
attack_message_synth = ", and your external plating is pierced by sharp spines"
attack_verb = "stabs"
// Even if the melee attack is enough to one-shot this blob, it gets to retaliate at least once.
/datum/blob_type/reactive_spines/on_received_damage(var/obj/structure/blob/B, damage, damage_type, mob/living/attacker)
if(damage > 0 && attacker && get_dist(B, attacker) <= 1)
B.visible_message("<span class='danger'>The [name] retaliates, lashing out at \the [attacker]!</span>")
B.blob_attack_animation(attacker, B.overmind)
attacker.blob_act(B)
..()
// Spreads damage to nearby blobs, and attacks with the force of all nearby blobs.
/datum/blob_type/synchronous_mesh
name = "synchronous mesh"
desc = "A mesh that seems strongly interconnected to itself. It moves slowly, but with purpose."
ai_desc = "defensive"
effect_desc = "When damaged, spreads the damage to nearby blobs. When attacking, damage is increased based on how many blobs are near the target. It is resistant to burn damage."
difficulty = BLOB_DIFFICULTY_EASY // Mostly a tank and spank.
color = "#65ADA2"
complementary_color = "#AD6570"
damage_type = BRUTE
damage_lower = 10
damage_upper = 15
brute_multiplier = 0.5
burn_multiplier = 0.2 // Emitters do so much damage that this will likely not matter too much.
spread_modifier = 0.3 // Since the blob spreads damage, it takes awhile to actually kill, so spread is reduced.
ai_aggressiveness = 60
attack_message = "The mesh synchronously strikes you"
attack_verb = "synchronously strikes"
var/synchronously_attacking = FALSE
/datum/blob_type/synchronous_mesh/on_attack(obj/structure/blob/B, mob/living/victim)
if(synchronously_attacking)
return
synchronously_attacking = TRUE // To avoid infinite loops.
for(var/obj/structure/blob/C in orange(1, victim))
if(victim) // Some things delete themselves when dead...
C.blob_attack_animation(victim)
victim.blob_act(C)
synchronously_attacking = FALSE
/datum/blob_type/synchronous_mesh/on_received_damage(var/obj/structure/blob/B, damage, damage_type)
var/list/blobs_to_hurt = list() // Maximum split is 9, reducing the damage each blob takes to 11.1% but doing that damage to 9 blobs.
for(var/obj/structure/blob/C in range(1, B))
if(!istype(C, /obj/structure/blob/core) && !istype(C, /obj/structure/blob/node) && C.overmind && (C.overmind == B.overmind) ) //if it doesn't have the same 'ownership' or is a core or node, don't split damage to it
blobs_to_hurt += C
for(var/thing in blobs_to_hurt)
var/obj/structure/blob/C = thing
if(C == B)
continue // We'll damage this later.
C.adjust_integrity(-(damage / blobs_to_hurt.len))
return damage / max(blobs_to_hurt.len, 1) // To hurt the blob that got hit.
/datum/blob_type/shifting_fragments
name = "shifting fragments"
desc = "A collection of fragments that seem to shuffle around constantly."
ai_desc = "evasive"
effect_desc = "Swaps places with nearby blobs when hit or when expanding."
difficulty = BLOB_DIFFICULTY_EASY
color = "#C8963C"
complementary_color = "#3C6EC8"
damage_type = BRUTE
damage_lower = 20
damage_upper = 30
brute_multiplier = 0.5
burn_multiplier = 0.5
spread_modifier = 0.5
ai_aggressiveness = 30
attack_message = "A fragment strikes you"
attack_verb = "strikes"
/datum/blob_type/shifting_fragments/on_received_damage(var/obj/structure/blob/B, damage, damage_type)
if(damage > 0 && prob(60))
var/list/available_blobs = list()
for(var/obj/structure/blob/OB in orange(1, B))
if((istype(OB, /obj/structure/blob/normal) || (istype(OB, /obj/structure/blob/shield) && prob(25))) && OB.overmind && OB.overmind == B.overmind)
available_blobs += OB
if(available_blobs.len)
var/obj/structure/blob/targeted = pick(available_blobs)
var/turf/T = get_turf(targeted)
targeted.forceMove(get_turf(B))
B.forceMove(T) // Swap places.
return ..()
/datum/blob_type/shifting_fragments/on_expand(var/obj/structure/blob/B, var/obj/structure/blob/new_B, var/turf/T, var/mob/observer/blob/O)
if(istype(B, /obj/structure/blob/normal) || (istype(B, /obj/structure/blob/shield) && prob(25)))
new_B.forceMove(get_turf(B))
B.forceMove(T)
// A very cool blob, literally.
/datum/blob_type/cryogenic_goo
name = "cryogenic goo"
desc = "A mass of goo that freezes anything it touches."
ai_desc = "balanced"
effect_desc = "Lowers the temperature of the room passively, and will also greatly lower the temperature of anything it attacks."
difficulty = BLOB_DIFFICULTY_MEDIUM
color = "#8BA6E9"
complementary_color = "#7D6EB4"
damage_type = BURN
damage_lower = 15
damage_upper = 25
brute_multiplier = 0.25
burn_multiplier = 1.2
spread_modifier = 0.5
ai_aggressiveness = 50
attack_message = "The goo stabs you"
attack_message_living = ", and you feel an intense chill from within"
attack_message_synth = ", and your system reports lower internal temperatures"
attack_verb = "stabs"
/datum/blob_type/cryogenic_goo/on_attack(obj/structure/blob/B, mob/living/victim)
if(ishuman(victim))
var/mob/living/carbon/human/H = victim
var/protection = H.get_cold_protection(50)
if(protection < 1)
var/temp_change = 80 // Each hit can reduce temperature by up to 80 kelvin.
var/datum/species/baseline = all_species["Human"]
var/temp_cap = baseline.cold_level_3 - 5 // Can't go lower than this.
var/cold_factor = abs(protection - 1)
temp_change *= cold_factor // If protection was at 0.5, then they only lose 40 kelvin.
H.bodytemperature = max(H.bodytemperature - temp_change, temp_cap)
else // Just do some extra burn for mobs who don't process bodytemp
victim.adjustFireLoss(20)
/datum/blob_type/cryogenic_goo/on_pulse(var/obj/structure/blob/B)
var/turf/simulated/T = get_turf(B)
if(!istype(T))
return
T.freeze_floor()
var/datum/gas_mixture/env = T.return_air()
if(env)
env.add_thermal_energy(-10 * 1000)
// Electric blob that stuns.
/datum/blob_type/energized_jelly
name = "energized jelly"
desc = "A substance that seems to generate electricity."
ai_desc = "suppressive"
effect_desc = "When attacking an entity, it will shock them with a strong electric shock. Repeated attacks can stun the target."
difficulty = BLOB_DIFFICULTY_MEDIUM
color = "#EFD65A"
complementary_color = "#00E5B1"
damage_type = BURN
damage_lower = 5
damage_upper = 10
brute_multiplier = 0.5
burn_multiplier = 0.5
spread_modifier = 0.35
ai_aggressiveness = 80
attack_message = "The jelly prods you"
attack_message_living = ", and your flesh burns as electricity arcs into you"
attack_message_synth = ", and your internal circuity is overloaded as electricity arcs into you"
attack_verb = "prods"
/datum/blob_type/energized_jelly/on_attack(obj/structure/blob/B, mob/living/victim, def_zone)
victim.electrocute_act(10, src, 1, def_zone)
victim.stun_effect_act(0, 40, BP_TORSO, src)
// A blob with area of effect attacks.
/datum/blob_type/explosive_lattice
name = "explosive lattice"
desc = "A very unstable lattice that looks quite explosive."
ai_desc = "aggressive"
effect_desc = "When attacking an entity, it will cause a small explosion, hitting things near the target. It is somewhat resilient, but weaker to brute damage."
difficulty = BLOB_DIFFICULTY_MEDIUM
color = "#8B2500"
complementary_color = "#00668B"
damage_type = BURN
damage_lower = 25
damage_upper = 35
armor_check = "bomb"
armor_pen = 5 // This is so blob hits still hurt just slightly when wearing a bomb suit (100 bomb resist).
brute_multiplier = 0.75
burn_multiplier = 0.5
spread_modifier = 0.4
ai_aggressiveness = 75
attack_message = "The lattice blasts you"
attack_message_living = ", and your flesh burns from the blast wave"
attack_message_synth = ", and your plating burns from the blast wave"
attack_verb = "blasts"
var/exploding = FALSE
/datum/blob_type/explosive_lattice/on_attack(obj/structure/blob/B, mob/living/victim, def_zone) // This doesn't use actual bombs since they're too strong and it would hurt the blob.
if(exploding) // We're busy, don't infinite loop us.
return
exploding = TRUE
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
continue
L.blob_act()
// Visual effect.
var/datum/effect/system/explosion/E = new/datum/effect/system/explosion/smokeless()
var/turf/T = get_turf(victim)
E.set_up(T)
E.start()
// Now for sounds.
playsound(T, "explosion", 75, 1) // Local sound.
for(var/mob/M in player_list) // For everyone else.
if(M.z == T.z && get_dist(M, T) > world.view && !M.ear_deaf && !istype(M.loc,/turf/space))
M << 'sound/effects/explosionfar.ogg'
exploding = FALSE
// A blob that slips and drowns you.
/datum/blob_type/pressurized_slime
name = "pressurized slime"
desc = "A large mass that seems to leak slippery fluid everywhere."
ai_desc = "drowning"
effect_desc = "Wets the floor when expanding and when hit. Tries to drown its enemies when attacking. It forces itself past internals. Resistant to burn damage."
difficulty = BLOB_DIFFICULTY_HARD
color = "#AAAABB"
complementary_color = "#BBBBAA"
damage_type = OXY
damage_lower = 5
damage_upper = 15
armor_check = null
brute_multiplier = 0.6
burn_multiplier = 0.2
spread_modifier = 0.4
ai_aggressiveness = 75
attack_message = "The slime splashes into you"
attack_message_living = ", and you gasp for breath"
attack_message_synth = ", and the fluid wears down on your components"
attack_verb = "splashes"
/datum/blob_type/pressurized_slime/on_attack(obj/structure/blob/B, mob/living/victim, def_zone)
victim.water_act(5)
var/turf/simulated/T = get_turf(victim)
if(T)
T.wet_floor()
/datum/blob_type/pressurized_slime/on_received_damage(var/obj/structure/blob/B, damage, damage_type)
wet_surroundings(B, damage)
return ..()
/datum/blob_type/pressurized_slime/on_pulse(var/obj/structure/blob/B)
var/turf/simulated/T = get_turf(B)
if(!istype(T))
return
T.wet_floor()
/datum/blob_type/pressurized_slime/on_death(obj/structure/blob/B)
B.visible_message("<span class='danger'>The blob ruptures, spraying the area with liquid!</span>")
wet_surroundings(B, 50)
/datum/blob_type/pressurized_slime/proc/wet_surroundings(var/obj/structure/blob/B, var/probability = 50)
for(var/turf/simulated/T in range(1, B))
if(prob(probability))
T.wet_floor()
for(var/atom/movable/AM in T)
AM.water_act(2)
// A blob that irradiates everything.
/datum/blob_type/radioactive_ooze
name = "radioactive ooze"
desc = "A goopy mess that glows with an unhealthy aura."
ai_desc = "radical"
effect_desc = "Irradiates the surrounding area, and inflicts toxic attacks. Weak to brute damage."
difficulty = BLOB_DIFFICULTY_MEDIUM
color = "#33CC33"
complementary_color = "#99FF66"
damage_type = TOX
damage_lower = 20
damage_upper = 30
armor_check = "rad"
brute_multiplier = 0.75
burn_multiplier = 0.2
spread_modifier = 0.8
ai_aggressiveness = 50
attack_message = "The ooze splashes you"
attack_message_living = ", and you feel warm"
attack_message_synth = ", and your internal systems are bombarded by ionizing radiation"
attack_verb = "splashes"
/datum/blob_type/radioactive_ooze/on_pulse(var/obj/structure/blob/B)
radiation_repository.radiate(B, 200)
@@ -199,7 +199,7 @@
/datum/gear/accessory/sweater
display_name = "Sweater Selection"
display_name = "sweater selection"
path = /obj/item/clothing/accessory/sweater
/datum/gear/accessory/sweater/New()
@@ -231,4 +231,13 @@
/datum/gear/accessory/bracelet/friendship
display_name = "friendship bracelet"
path = /obj/item/clothing/accessory/bracelet/friendship
path = /obj/item/clothing/accessory/bracelet/friendship
/datum/gear/accessory/stethoscope
display_name = "stethoscope"
path = /obj/item/clothing/accessory/stethoscope
allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic")
/datum/gear/accessory/locket
display_name = "locket"
path = /obj/item/clothing/accessory/locket
@@ -1,16 +1,20 @@
/datum/gear/lipstick
/datum/gear/cosmetic/lipstick/black
display_name = "lipstick, black"
path = /obj/item/weapon/lipstick/black
sort_category = "Cosmetics"
/datum/gear/lipstick/jade
/datum/gear/cosmetic/lipstick/jade
display_name = "lipstick, jade"
path = /obj/item/weapon/lipstick/jade
/datum/gear/lipstick/purple
/datum/gear/cosmetic/lipstick/purple
display_name = "lipstick, purple"
path = /obj/item/weapon/lipstick/purple
/datum/gear/lipstick/red
/datum/gear/cosmetic/lipstick
display_name = "lipstick, red"
path = /obj/item/weapon/lipstick
/datum/gear/cosmetic
display_name = "purple comb"
path = /obj/item/weapon/haircomb
sort_category = "Cosmetics"
@@ -18,14 +18,18 @@
path = /obj/item/clothing/glasses/regular/hipster
/datum/gear/eyes/glasses/monocle
display_name = "Monocle"
display_name = "monocle"
path = /obj/item/clothing/glasses/monocle
/datum/gear/eyes/scanning_goggles
/datum/gear/eyes/goggles
display_name = "plain goggles"
path = /obj/item/clothing/glasses/goggles
/datum/gear/eyes/goggles/scanning
display_name = "scanning goggles"
path = /obj/item/clothing/glasses/regular/scanners
/datum/gear/eyes/sciencegoggles
/datum/gear/eyes/goggles/science
display_name = "Science Goggles"
path = /obj/item/clothing/glasses/science
@@ -73,7 +77,7 @@
allowed_roles = list("Station Engineer","Chief Engineer","Atmospheric Technician", "Scientist", "Research Director")
/datum/gear/eyes/meson/prescription
display_name = "Optical Meson Scanners, prescription (Engineering)"
display_name = "Optical Meson Scanners, prescription (Engineering, Science)"
path = /obj/item/clothing/glasses/meson/prescription
/datum/gear/eyes/material
@@ -86,11 +90,11 @@
path = /obj/item/clothing/glasses/material/prescription
/datum/gear/eyes/meson/aviator
display_name = "Optical Meson Aviators, (Engineering)"
display_name = "Optical Meson Aviators, (Engineering, Science)"
path = /obj/item/clothing/glasses/meson/aviator
/datum/gear/eyes/meson/aviator/prescription
display_name = "Optical Meson Aviators, prescription (Engineering)"
display_name = "Optical Meson Aviators, prescription (Engineering, Science)"
path = /obj/item/clothing/glasses/meson/aviator/prescription
/datum/gear/eyes/glasses/fakesun
@@ -2,6 +2,10 @@
display_name = "cane"
path = /obj/item/weapon/cane
/datum/gear/cane/white
display_name = "white cane"
path = /obj/item/weapon/cane/whitecane
/datum/gear/dice
display_name = "dice pack"
path = /obj/item/weapon/storage/pill_bottle/dice
@@ -62,10 +66,6 @@
..()
gear_tweaks += new/datum/gear_tweak/reagents(lunchables_drink_reagents())
/datum/gear/comb
display_name = "purple comb"
path = /obj/item/weapon/haircomb
/datum/gear/lunchbox
display_name = "lunchbox"
description = "A little lunchbox."
@@ -114,9 +114,13 @@
path = /obj/item/clothing/head/soft/yellow
/datum/gear/head/cap/white
display_name = "cap, white"
display_name = "cap (colorable)"
path = /obj/item/clothing/head/soft/mime
/datum/gear/head/cap/white/New()
..()
gear_tweaks = list(gear_tweak_free_color_choice)
/datum/gear/head/cap/mbill
display_name = "cap, bill"
path = /obj/item/clothing/head/soft/mbill
@@ -154,7 +158,7 @@
path = /obj/item/clothing/head/fedora/grey
/datum/gear/head/hairflower
display_name = "hair flower pin"
display_name = "hair flower pin (colorable)"
path = /obj/item/clothing/head/hairflower/white
/datum/gear/head/hairflower/New()
@@ -162,21 +166,17 @@
gear_tweaks = list(gear_tweak_free_color_choice)
/datum/gear/head/hardhat
display_name = "hardhat, yellow"
display_name = "hardhat selection"
path = /obj/item/clothing/head/hardhat
cost = 2
/datum/gear/head/hardhat/blue
display_name = "hardhat, blue"
path = /obj/item/clothing/head/hardhat/dblue
/datum/gear/head/hardhat/orange
display_name = "hardhat, orange"
path = /obj/item/clothing/head/hardhat/orange
/datum/gear/head/hardhat/red
display_name = "hardhat, red"
path = /obj/item/clothing/head/hardhat/red
/datum/gear/head/hardhat/New()
..()
var/list/hardhats = list()
for(var/hardhat in typesof(/obj/item/clothing/head/hardhat))
var/obj/item/clothing/head/hardhat/hardhat_type = hardhat
hardhats[initial(hardhat_type.name)] = hardhat_type
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(hardhats))
/datum/gear/head/boater
display_name = "hat, boatsman"
@@ -194,22 +194,30 @@
display_name = "hat, tophat"
path = /obj/item/clothing/head/that
/datum/gear/head/philosopher_wig
/datum/gear/head/wig/philosopher
display_name = "natural philosopher's wig"
path = /obj/item/clothing/head/philosopher_wig
/datum/gear/head/wig
display_name = "powdered wig"
path = /obj/item/clothing/head/powdered_wig
/datum/gear/head/ushanka
display_name = "ushanka"
path = /obj/item/clothing/head/ushanka
/datum/gear/head/santahat
display_name = "santa hat, red (holiday)"
display_name = "santa hat"
path = /obj/item/clothing/head/santa
cost = 2
/datum/gear/head/santahat/green
display_name = "santa hat, green (holiday)"
path = /obj/item/clothing/head/santa/green
/datum/gear/head/santahat/New()
..()
var/list/santahats = list()
for(var/santahat in typesof(/obj/item/clothing/head/santa))
var/obj/item/clothing/head/santa/santahat_type = santahat
santahats[initial(santahat_type.name)] = santahat_type
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(santahats))
/datum/gear/head/hijab
display_name = "hijab"
@@ -243,11 +251,14 @@
..()
gear_tweaks = list(gear_tweak_free_color_choice)
/datum/gear/head/kitty
display_name = "kitty ears"
path = /obj/item/clothing/head/kitty
/datum/gear/head/rabbit
display_name = "rabbit ears"
path = /obj/item/clothing/head/rabbitears
/datum/gear/head/beanie
display_name = "beanie"
path = /obj/item/clothing/head/beanie
@@ -1,20 +1,29 @@
/datum/gear/smokingpipe
display_name = "pipe, smoking"
/datum/gear/pipe
display_name = "pipe"
path = /obj/item/clothing/mask/smokable/pipe
/datum/gear/cornpipe
display_name = "pipe, corn"
path = /obj/item/clothing/mask/smokable/pipe/cobpipe
/datum/gear/pipe/New()
..()
var/list/pipes = list()
for(var/pipe_style in typesof(/obj/item/clothing/mask/smokable/pipe))
var/obj/item/clothing/mask/smokable/pipe/pipe = pipe_style
pipes[initial(pipe.name)] = pipe
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(pipes))
/datum/gear/matchbook
display_name = "matchbook"
path = /obj/item/weapon/storage/box/matches
/datum/gear/zippo
display_name = "Zippo Selection"
/datum/gear/lighter
display_name = "cheap lighter"
path = /obj/item/weapon/flame/lighter
/datum/gear/lighter/zippo
display_name = "Zippo selection"
path = /obj/item/weapon/flame/lighter/zippo
/datum/gear/zippo/New()
/datum/gear/lighter/zippo/New()
..()
var/list/zippos = list()
for(var/zippo in typesof(/obj/item/weapon/flame/lighter/zippo))
@@ -40,4 +49,4 @@
for(var/cigarette in (typesof(/obj/item/weapon/storage/fancy/cigarettes) - typesof(/obj/item/weapon/storage/fancy/cigarettes/killthroat)))
var/obj/item/weapon/storage/fancy/cigarettes/cigarette_brand = cigarette
cigarettes[initial(cigarette_brand.name)] = cigarette_brand
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(cigarettes))
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(cigarettes))
@@ -6,6 +6,10 @@
sort_category = "Suits and Overwear"
cost = 2
/datum/gear/suit/greatcoat
display_name = "greatcoat"
path = /obj/item/clothing/suit/greatcoat
/datum/gear/suit/leather_coat
display_name = "leather coat"
path = /obj/item/clothing/suit/leathercoat
@@ -349,7 +349,7 @@
/datum/gear/uniform/whitewedding
display_name= "white wedding dress"
path = /obj/item/clothing/under/dress/white
path = /obj/item/clothing/under/wedding/bride_white
/datum/gear/uniform/skirts
display_name = "executive skirt"
@@ -13,6 +13,10 @@
path = /obj/item/device/communicator
cost = 0
/datum/gear/utility/camera
display_name = "camera"
path = /obj/item/device/camera
/datum/gear/utility/codex
display_name = "the traveler's guide to vir"
path = /obj/item/weapon/book/codex/lore/vir
@@ -43,25 +43,33 @@
bandtypes[initial(band.name)] = band
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(bandtypes))
/datum/gear/ears/skrell/cloth/male
display_name = "male headtail cloth (Skrell)"
/datum/gear/ears/skrell/cloth/short
display_name = "short headtail cloth (Skrell)"
path = /obj/item/clothing/ears/skrell/cloth_male/black
sort_category = "Xenowear"
whitelisted = "Skrell"
/datum/gear/ears/skrell/cloth/male/New()
/datum/gear/ears/skrell/cloth/short/New()
..()
gear_tweaks = list(gear_tweak_free_color_choice)
var/list/shorttypes = list()
for(var/short_style in typesof(/obj/item/clothing/ears/skrell/cloth_male))
var/obj/item/clothing/ears/skrell/cloth_male/short = short_style
shorttypes[initial(short.name)] = short
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(shorttypes))
/datum/gear/ears/skrell/cloth/female
display_name = "female headtail cloth (Skrell)"
/datum/gear/ears/skrell/cloth/long
display_name = "long headtail cloth (Skrell)"
path = /obj/item/clothing/ears/skrell/cloth_female/black
sort_category = "Xenowear"
whitelisted = "Skrell"
/datum/gear/ears/skrell/cloth/female/New()
/datum/gear/ears/skrell/cloth/long/New()
..()
gear_tweaks = list(gear_tweak_free_color_choice)
var/list/longtypes = list()
for(var/long_style in typesof(/obj/item/clothing/ears/skrell/cloth_female))
var/obj/item/clothing/ears/skrell/cloth_female/long = long_style
longtypes[initial(long.name)] = long
gear_tweaks += new/datum/gear_tweak/path(sortAssoc(longtypes))
/datum/gear/ears/skrell/colored/band
display_name = "Colored bands (Skrell)"
+2 -2
View File
@@ -43,9 +43,9 @@
name = "winter boots"
desc = "Boots lined with 'synthetic' animal fur."
icon_state = "winterboots"
cold_protection = FEET|LEGS
cold_protection = FEET
min_cold_protection_temperature = SHOE_MIN_COLD_PROTECTION_TEMPERATURE
heat_protection = FEET|LEGS
heat_protection = FEET
max_heat_protection_temperature = SHOE_MAX_HEAT_PROTECTION_TEMPERATURE
snow_speed = -1
step_volume_mod = 0.8
@@ -16,6 +16,7 @@
siemens_coefficient = 0.9
species_restricted = list("exclude","Diona")
preserve_item = 1
phoronproof = 1
var/obj/machinery/camera/camera
var/list/camera_networks
@@ -61,6 +62,7 @@
siemens_coefficient = 0.9
species_restricted = list("exclude","Diona")
preserve_item = 1
phoronproof = 1
var/list/supporting_limbs //If not-null, automatically splints breaks. Checked when removing the suit.
+5 -1
View File
@@ -8,6 +8,8 @@
flags_inv = HIDEMASK|HIDEEARS|HIDEEYES|BLOCKHAIR
body_parts_covered = HEAD|FACE|EYES
siemens_coefficient = 0.9
item_flags = THICKMATERIAL
phoronproof = 1
/obj/item/clothing/suit/bio_suit
name = "bio suit"
@@ -16,12 +18,14 @@
w_class = ITEMSIZE_LARGE//bulky item
gas_transfer_coefficient = 0.01
permeability_coefficient = 0.01
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|FEET|ARMS|HANDS
body_parts_covered = UPPER_TORSO|LOWER_TORSO|LEGS|ARMS|HANDS|FEET
slowdown = 1.0
allowed = list(/obj/item/weapon/tank/emergency/oxygen,/obj/item/weapon/pen,/obj/item/device/flashlight/pen)
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 20)
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
siemens_coefficient = 0.9
item_flags = THICKMATERIAL
phoronproof = 1
//Standard biosuit, orange stripe
/obj/item/clothing/head/bio_hood/general
+5 -2
View File
@@ -659,9 +659,9 @@ obj/item/clothing/suit/storage/toggle/peacoat
desc = "A heavy jacket made from 'synthetic' animal furs."
icon_state = "coatwinter"
item_state_slots = list(slot_r_hand_str = "coatwinter", slot_l_hand_str = "coatwinter")
body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS
body_parts_covered = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS
flags_inv = HIDEHOLSTER
cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS
cold_protection = UPPER_TORSO|LOWER_TORSO|ARMS|LEGS
min_cold_protection_temperature = SPACE_SUIT_MIN_COLD_PROTECTION_TEMPERATURE
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 10, rad = 0)
hooded = 1
@@ -798,6 +798,9 @@ obj/item/clothing/suit/storage/toggle/peacoat
name = "explorer hood"
desc = "An armoured hood for exploring harsh environments."
icon_state = "explorer"
brightness_on = 3
light_overlay = "hood_light"
action_button_name = "Toggle Head-light"
body_parts_covered = HEAD
cold_protection = HEAD
flags = THICKMATERIAL
+1 -1
View File
@@ -36,7 +36,7 @@
var/mob/living/carbon/H = hood.loc
H.unEquip(hood, 1)
H.update_inv_wear_suit()
hood.loc = src
hood.forceMove(src)
/obj/item/clothing/suit/storage/hooded/dropped()
RemoveHood()
+1 -1
View File
@@ -95,4 +95,4 @@
slowdown = 1.5
armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 60, rad = 100)
flags_inv = HIDEJUMPSUIT|HIDETAIL|HIDETIE|HIDEHOLSTER
item_flags = THICKMATERIAL
item_flags = THICKMATERIAL

Some files were not shown because too many files have changed in this diff Show More