Merge pull request #1960 from Lohikar/map-dev-master

Update map-development
This commit is contained in:
LordFowl
2017-03-22 19:17:52 -04:00
committed by GitHub
312 changed files with 5817 additions and 3112 deletions
BIN
View File
Binary file not shown.
+6 -4
View File
@@ -53,6 +53,7 @@
#include "code\_helpers\matrices.dm"
#include "code\_helpers\mobs.dm"
#include "code\_helpers\names.dm"
#include "code\_helpers\overlay.dm"
#include "code\_helpers\sanitize_values.dm"
#include "code\_helpers\spawn_sync.dm"
#include "code\_helpers\text.dm"
@@ -172,6 +173,7 @@
#include "code\datums\mind.dm"
#include "code\datums\mixed.dm"
#include "code\datums\modules.dm"
#include "code\datums\scheduled_task.dm"
#include "code\datums\server_greeting.dm"
#include "code\datums\sun.dm"
#include "code\datums\supplypacks.dm"
@@ -336,6 +338,7 @@
#include "code\game\gamemodes\setupgame.dm"
#include "code\game\gamemodes\calamity\calamity.dm"
#include "code\game\gamemodes\changeling\changeling.dm"
#include "code\game\gamemodes\changeling\changeling_items.dm"
#include "code\game\gamemodes\changeling\changeling_powers.dm"
#include "code\game\gamemodes\changeling\modularchangling.dm"
#include "code\game\gamemodes\cult\cult.dm"
@@ -467,6 +470,8 @@
#include "code\game\machinery\wall_frames.dm"
#include "code\game\machinery\washing_machine.dm"
#include "code\game\machinery\wishgranter.dm"
#include "code\game\machinery\abstract\abstract.dm"
#include "code\game\machinery\abstract\intercom_listener.dm"
#include "code\game\machinery\atmoalter\area_atmos_computer.dm"
#include "code\game\machinery\atmoalter\canister.dm"
#include "code\game\machinery\atmoalter\meter.dm"
@@ -999,10 +1004,7 @@
#include "code\modules\alarm\fire_alarm.dm"
#include "code\modules\alarm\motion_alarm.dm"
#include "code\modules\alarm\power_alarm.dm"
#include "code\modules\antag_contest\contest_defines.dm"
#include "code\modules\antag_contest\contest_helpers.dm"
#include "code\modules\antag_contest\contest_objective.dm"
#include "code\modules\antag_contest\contest_verbs.dm"
#include "code\modules\antag_contest\sol_items.dm"
#include "code\modules\assembly\assembly.dm"
#include "code\modules\assembly\bomb.dm"
#include "code\modules\assembly\helpers.dm"
@@ -250,7 +250,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -229,7 +229,7 @@ Thus, the two variables affect pump operation are set in New():
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -92,7 +92,7 @@
return 1
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
if(do_after(user, 40))
if(do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -146,7 +146,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -139,7 +139,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -114,7 +114,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
+1 -1
View File
@@ -358,7 +358,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -80,7 +80,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -275,7 +275,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
@@ -290,7 +290,7 @@
user << "<span class='danger'>\The [WT] must be turned on!</span>"
else if (WT.remove_fuel(0,user))
user << "<span class='notice'>Now welding \the [src].</span>"
if(do_after(user, 20))
if(do_after(user, 20, act_target = src))
if(!src || !WT.isOn()) return
playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
if(!welded)
+1 -1
View File
@@ -315,7 +315,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
+1 -1
View File
@@ -93,7 +93,7 @@
return 1
playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
user << "<span class='notice'>You begin to unfasten \the [src]...</span>"
if (do_after(user, 40))
if (do_after(user, 40, act_target = src))
user.visible_message( \
"<span class='notice'>\The [user] unfastens \the [src].</span>", \
"<span class='notice'>You have unfastened \the [src].</span>", \
+9 -4
View File
@@ -73,15 +73,20 @@
#define LIGHT_NARROW 45
// Night lighting controller times
// The time (in ticks based on worldtime2ticks()) that various actions trigger
#define MORNING_LIGHT_RESET 252000 // 7am or 07:00 - lighting restores to normal in morning
#define NIGHT_LIGHT_ACTIVE 648000 // 6pm or 18:00 - night lighting mode activates
// The time (in hours based on worldtime2hours()) that various actions trigger
#define MORNING_LIGHT_RESET 7 // 7am or 07:00 - lighting restores to normal in morning
#define NIGHT_LIGHT_ACTIVE 18 // 6pm or 18:00 - night lighting mode activates
// Some brightness/range defines for objects.
#define L_WALLMOUNT_POWER 0.4
#define L_WALLMOUNT_RANGE 2
#define L_WALLMOUNT_HI_POWER 2 // For red/delta alert on fire alarms.
#define L_WALLMOUNT_HI_POWER 1 // For red/delta alert on fire alarms.
#define L_WALLMOUNT_HI_RANGE 4
// This controls by how much console sprites are dimmed before being overlayed.
#define HOLOSCREEN_ADDITION_FACTOR 1
#define HOLOSCREEN_MULTIPLICATION_FACTOR 0.5
#define HOLOSCREEN_ADDITION_OPACITY 0.8
#define HOLOSCREEN_MULTIPLICATION_OPACITY 1
// Just so we can avoid unneeded proc calls when profiling is disabled.
#define L_PROF(O,T) if (lighting_profiling) {lprof_write(O,T);}
+5
View File
@@ -105,3 +105,8 @@ var/list/restricted_camera_networks = list(NETWORK_ERT,NETWORK_MERCENARY,"Secret
// If this is returned from a machine's process() proc, the machine will stop processing but
// will continue to have power calculations done.
#define M_NO_PROCESS 27
// This controls how much power the AME generates per unit of fuel.
// Assuming 100% efficency, use this equation to figure out power output.
// power_generated = (fuel**2) * AM_POWER_FACTOR
#define AM_POWER_FACTOR 50000
+5 -1
View File
@@ -49,7 +49,8 @@
#define CHAT_NOICONS 0x8000
#define PARALLAX_SPACE 0x1
#define PARALLAX_DUST 0x2
#define PARALLAX_DUST 0x2
#define PROGRESS_BARS 0x4
#define TOGGLES_DEFAULT (SOUND_ADMINHELP|SOUND_MIDI|SOUND_AMBIENCE|SOUND_LOBBY|CHAT_OOC|CHAT_DEAD|CHAT_GHOSTEARS|CHAT_GHOSTSIGHT|CHAT_PRAYER|CHAT_RADIO|CHAT_ATTACKLOGS|CHAT_LOOC)
@@ -286,6 +287,9 @@
#define get_turf(A) (get_step(A, 0))
#define QDELETED(TARGET) (!TARGET || TARGET.gcDestroyed)
#define QDEL_NULL(item) qdel(item); item = null
// Shim until addtimer is merged or I figure out if it is safe to use scheduler for this.
#define QDEL_IN(OBJ, TIME) spawn(TIME) qdel(OBJ)
//Recipe type defines. Used to determine what machine makes them
#define MICROWAVE 0x1
+2
View File
@@ -30,3 +30,5 @@
last_slept = TimeOfHour; \
tick_start = world.tick_usage; \
}
#define PSCHED_CHECK_TICK (world.tick_usage > 100 || (world.tick_usage - tick_start) > tick_allowance)
+33
View File
@@ -0,0 +1,33 @@
// Factor/Opacity values are defined in __defines\lighting.dm
/proc/holographic_overlay(obj/target, icon, icon_state)
var/image/multiply = make_screen_overlay(icon, icon_state)
multiply.blend_mode = BLEND_MULTIPLY
multiply.color = list(
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_FACTOR, HOLOSCREEN_MULTIPLICATION_OPACITY
)
target.overlays += multiply
var/image/overlay = make_screen_overlay(icon, icon_state)
overlay.blend_mode = BLEND_ADD
overlay.color = list(
HOLOSCREEN_ADDITION_FACTOR, 0, 0, 0,
0, HOLOSCREEN_ADDITION_FACTOR, 0, 0,
0, 0, HOLOSCREEN_ADDITION_FACTOR, 0,
0, 0, 0, HOLOSCREEN_ADDITION_OPACITY
)
target.overlays += overlay
/proc/make_screen_overlay(icon, icon_state, brightness_factor = null)
var/image/overlay = image(icon, icon_state)
overlay.layer = LIGHTING_LAYER + 0.1
if (brightness_factor)
overlay.color = list(
brightness_factor, 0, 0, 0,
0, brightness_factor, 0, 0,
0, 0, brightness_factor, 0,
0, 0, 0, 1
)
return overlay
+5 -3
View File
@@ -10,10 +10,12 @@ proc/worldtime2text(time = world.time, timeshift = 1)
if(!roundstart_hour) roundstart_hour = rand(0, 23)
return timeshift ? time2text(time+(36000*roundstart_hour), "hh:mm") : time2text(time, "hh:mm")
proc/worldtime2ticks(time = world.time)
if(!roundstart_hour)
/proc/worldtime2hours()
if (!roundstart_hour)
worldtime2text()
return ((roundstart_hour * 60 MINUTES) + time) % TICKS_IN_DAY
. = (world.timeofday / (60 MINUTES)) + roundstart_hour
if (. > 24)
. -= 24
proc/worlddate2text()
return num2text(game_year) + "-" + time2text(world.timeofday, "MM-DD")
+1 -1
View File
@@ -222,7 +222,7 @@ proc/tg_list2text(list/list, glue=",")
// Converts an angle (degrees) into an ss13 direction
/proc/angle2dir(var/degree)
degree = (degree + 22.5) % 365 // 22.5 = 45 / 2
degree = (degree + 22.5) % 360 // 22.5 = 45 / 2
if (degree < 45) return NORTH
if (degree < 90) return NORTHEAST
if (degree < 135) return EAST
+86 -30
View File
@@ -646,45 +646,109 @@ proc/GaussRandRound(var/sigma,var/roundto)
else return get_step(ref, base_dir)
/proc/do_mob(var/mob/user, var/mob/target, var/delay = 30, var/numticks = 5, var/needhand = 1) //This is quite an ugly solution but i refuse to use the old request system.
if(!user || !target) return 0
if(numticks == 0) return 0
var/delayfraction = round(delay/numticks)
var/original_user_loc = user.loc
var/original_target_loc = target.loc
/proc/do_mob(mob/user , mob/target, delay = 30, numticks = 10, needhand = TRUE, display_progress = TRUE) //This is quite an ugly solution but i refuse to use the old request system.
if(!user || !target)
return 0
var/user_loc = user.loc
var/target_loc = target.loc
var/holding = user.get_active_hand()
for(var/i = 0, i<numticks, i++)
var/delayfraction = round(delay/numticks)
var/image/progbar
if(user && user.client && (user.client.prefs.parallax_togs & PROGRESS_BARS) && display_progress)
if(!progbar)
progbar = image(icon = 'icons/effects/doafter_icon.dmi', loc = target, icon_state = "prog_bar_0")
progbar.layer = 21
progbar.pixel_z = WORLD_ICON_SIZE
progbar.appearance_flags = RESET_TRANSFORM
for (var/i = 1 to numticks)
if(user && user.client && (user.client.prefs.parallax_togs & PROGRESS_BARS) && progbar && display_progress)
progbar.icon_state = "prog_bar_[round(((i / numticks) * 100), 10)]"
user.client.images |= progbar
sleep(delayfraction)
if(!user || user.stat || user.weakened || user.stunned || user.loc != original_user_loc)
if(!user || !target)
if(progbar)
progbar.icon_state = "prog_bar_stopped"
spawn(2)
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 0
if(!target || target.loc != original_target_loc)
if (user.loc != user_loc || target.loc != target_loc || (needhand && user.get_active_hand() != holding) || user.stat || user.weakened || user.stunned)
if(progbar)
progbar.icon_state = "prog_bar_stopped"
spawn(2)
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 0
if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand
return 0
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 1
/proc/do_after(var/mob/user as mob, delay as num, var/numticks = 5, var/needhand = 1)
/proc/do_after(mob/user as mob, delay as num, numticks = 10, needhand = TRUE, atom/movable/act_target = null, use_user_turf = FALSE, display_progress = TRUE)
if(!user || isnull(user))
return 0
if(numticks == 0)
return 0
if (!act_target)
act_target = user
var/delayfraction = round(delay/numticks)
var/original_loc = user.loc
var/Location
if(use_user_turf) //When this is true, do_after() will check whether the user's turf has changed, rather than the user's loc.
Location = get_turf(user)
else
Location = user.loc
var/holding = user.get_active_hand()
for(var/i = 0, i<numticks, i++)
var/image/progbar
if(user && user.client && (user.client.prefs.parallax_togs & PROGRESS_BARS) && act_target && display_progress)
if(!progbar)
progbar = image(icon = 'icons/effects/doafter_icon.dmi', loc = act_target, icon_state = "prog_bar_0")
progbar.pixel_z = WORLD_ICON_SIZE
progbar.layer = 21
progbar.appearance_flags = RESET_COLOR | RESET_TRANSFORM
for (var/i = 1 to numticks)
if(user && user.client && (user.client.prefs.parallax_togs & PROGRESS_BARS) && act_target && display_progress)
if(!progbar)
progbar = image(icon = 'icons/effects/doafter_icon.dmi', loc = act_target, icon_state = "prog_bar_0")
progbar.pixel_z = WORLD_ICON_SIZE
progbar.layer = 21
progbar.appearance_flags = RESET_COLOR | RESET_TRANSFORM
progbar.icon_state = "prog_bar_[round(((i / numticks) * 100), 10)]"
user.client.images |= progbar
sleep(delayfraction)
if(!user || user.stat || user.weakened || user.stunned || user.loc != original_loc)
var/user_loc_to_check
if(use_user_turf)
user_loc_to_check = get_turf(user)
else
user_loc_to_check = user.loc
if (!user || user.stat || user.weakened || user.stunned || !(user_loc_to_check == Location))
if(progbar)
progbar.icon_state = "prog_bar_stopped"
spawn(2)
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 0
if(needhand && !(user.get_active_hand() == holding)) //Sometimes you don't want the user to have to keep their active hand
if(progbar)
progbar.icon_state = "prog_bar_stopped"
spawn(2)
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 0
if(user && user.client)
user.client.images -= progbar
if(progbar)
progbar.loc = null
return 1
//Takes: Anything that could possibly have variables and a varname to check.
@@ -794,8 +858,6 @@ proc/GaussRandRound(var/sigma,var/roundto)
var/old_icon1 = T.icon
var/old_overlays = T.overlays.Copy()
var/old_underlays = T.underlays.Copy()
if (T.dynamic_lighting && T.lighting_overlay)
T.lighting_overlay.forceMove(B, harderforce = TRUE)
var/turf/X = B.ChangeTurf(T.type)
X.set_dir(old_dir1)
@@ -851,12 +913,6 @@ proc/GaussRandRound(var/sigma,var/roundto)
if(!istype(M,/mob) || istype(M, /mob/eye)) continue // If we need to check for more mobs, I'll add a variable
M.loc = X
// var/area/AR = X.loc
// if(AR.lighting_use_dynamic) //TODO: rewrite this code so it's not messed by lighting ~Carn
// X.opacity = !X.opacity
// X.SetOpacity(!X.opacity)
toupdate += X
if(turftoleave)
+8 -10
View File
@@ -27,14 +27,12 @@ var/list/parallax_icon[(GRID_WIDTH**2)*3]
blend_mode = BLEND_ADD
layer = AREA_LAYER
plane = PLANE_SPACE_PARALLAX
globalscreen = 1
var/parallax_speed = 0
/obj/screen/plane_master
appearance_flags = PLANE_MASTER
screen_loc = "CENTER,CENTER"
globalscreen = 1
/obj/screen/plane_master/parallax_master
plane = PLANE_SPACE_PARALLAX
blend_mode = BLEND_MULTIPLY
@@ -74,15 +72,15 @@ var/list/parallax_icon[(GRID_WIDTH**2)*3]
var/client/C = mymob.client
if(!C.parallax_master)
C.parallax_master = getFromPool(/obj/screen/plane_master/parallax_master)
C.parallax_master = new /obj/screen/plane_master/parallax_master
if(!C.parallax_spacemaster)
C.parallax_spacemaster = getFromPool(/obj/screen/plane_master/parallax_spacemaster)
C.parallax_spacemaster = new /obj/screen/plane_master/parallax_spacemaster
if(!C.parallax_dustmaster)
C.parallax_dustmaster = getFromPool(/obj/screen/plane_master/parallax_dustmaster)
C.parallax_dustmaster = new /obj/screen/plane_master/parallax_dustmaster
if(!C.parallax.len)
for(var/obj/screen/parallax/bgobj in parallax_icon)
var/obj/screen/parallax/parallax_layer = getFromPool(/obj/screen/parallax)
var/obj/screen/parallax/parallax_layer = new /obj/screen/parallax
parallax_layer.appearance = bgobj.appearance
parallax_layer.base_offset_x = bgobj.base_offset_x
parallax_layer.base_offset_y = bgobj.base_offset_y
@@ -182,7 +180,7 @@ var/list/parallax_icon[(GRID_WIDTH**2)*3]
pixel_y += WORLD_ICON_SIZE * round(i/PARALLAX_IMAGE_WIDTH)
for(var/i in 0 to ((GRID_WIDTH**2)-1))
var/obj/screen/parallax/parallax_layer = getFromPool(/obj/screen/parallax)
var/obj/screen/parallax/parallax_layer = new /obj/screen/parallax
var/list/L = list()
for(var/j in 1 to PARALLAX_IMAGE_TILES)
@@ -199,7 +197,7 @@ var/list/parallax_icon[(GRID_WIDTH**2)*3]
index++
for(var/i in 0 to ((GRID_WIDTH**2)-1))
var/obj/screen/parallax/parallax_layer = getFromPool(/obj/screen/parallax)
var/obj/screen/parallax/parallax_layer = new /obj/screen/parallax
var/list/L = list()
for(var/j in 1 to PARALLAX_IMAGE_TILES)
@@ -216,7 +214,7 @@ var/list/parallax_icon[(GRID_WIDTH**2)*3]
index++
for(var/i in 0 to ((GRID_WIDTH**2)-1))
var/obj/screen/parallax/parallax_layer = getFromPool(/obj/screen/parallax)
var/obj/screen/parallax/parallax_layer = new /obj/screen/parallax
var/list/L = list()
for(var/j in 1 to PARALLAX_IMAGE_TILES)
if(plane3[j+i*PARALLAX_IMAGE_TILES] <= PARALLAX2_ICON_NUMBER)
+1 -1
View File
@@ -12,10 +12,10 @@
layer = 20.0
unacidable = 1
var/obj/master = null //A reference to the object in the slot. Grabs or items, generally.
var/globalscreen = 0 // TODO: Screen pooling.
/obj/screen/Destroy()
master = null
screen_loc = null
return ..()
/obj/screen/text
+10 -6
View File
@@ -74,13 +74,19 @@ var/const/tk_maxrange = 15
var/atom/movable/focus = null
var/mob/living/host = null
/obj/item/tk_grab/Destroy()
if (host)
host.remove_from_mob(src)
host = null
focus = null
return ..()
/obj/item/tk_grab/dropped(mob/user as mob)
if(focus && user && loc != user && loc != user.loc) // drop_item() gets called when you tk-attack a table/closet with an item
if(focus.Adjacent(loc))
focus.loc = loc
loc = null
spawn(1)
qdel(src)
QDEL_IN(src, 1)
return
//stops TK grabs being equipped anywhere but into hands
@@ -157,7 +163,7 @@ var/const/tk_maxrange = 15
/obj/item/tk_grab/proc/apply_focus_overlay()
if(!focus) return
var/obj/effect/overlay/O = getFromPool(/obj/effect/overlay, locate(focus.x,focus.y,focus.z))
var/obj/effect/overlay/O = new(locate(focus.x,focus.y,focus.z))
O.name = "sparkles"
O.anchored = 1
O.density = 0
@@ -166,9 +172,7 @@ var/const/tk_maxrange = 15
O.icon = 'icons/effects/effects.dmi'
O.icon_state = "nothing"
flick("empdisable",O)
spawn(5)
qdel(O)
return
QDEL_IN(O, 5)
/obj/item/tk_grab/update_icon()
overlays.Cut()
+2 -2
View File
@@ -37,7 +37,7 @@ var/datum/controller/process/effects/effect_master
effects_objects += E
if (EFFECT_DESTROY)
qdel(E)
returnToPool(E)
F_SCHECK
@@ -61,7 +61,7 @@ var/datum/controller/process/effects/effect_master
if (EFFECT_DESTROY)
effects_visuals -= V
V.end()
qdel(V)
returnToPool(V)
F_SCHECK
+15 -23
View File
@@ -10,7 +10,7 @@ var/datum/controller/process/night_lighting/nl_ctrl
/datum/controller/process/night_lighting/setup()
name = "night lighting controller"
schedule_interval = 3600 // Every 5 minutes.
schedule_interval = 5 MINUTES
nl_ctrl = src
@@ -18,34 +18,26 @@ var/datum/controller/process/night_lighting/nl_ctrl
// Stop trying to delete processes. Not how it goes.
disabled = 1
/datum/controller/process/night_lighting/preStart()
switch (worldtime2ticks())
if (0 to config.nl_finish)
deactivate()
if (config.nl_start to TICKS_IN_DAY)
activate()
var/time = worldtime2hours()
if (time <= config.nl_finish || time >= config.nl_start)
activate()
else
deactivate()
/datum/controller/process/night_lighting/doWork()
if (manual_override) // don't automatically change lighting if it was manually changed in-game
return
switch (worldtime2ticks())
if (0 to config.nl_finish)
if (isactive)
command_announcement.Announce("Good morning. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now return the public hallway lighting levels to normal.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
deactivate()
if (config.nl_start to TICKS_IN_DAY)
if (!isactive)
command_announcement.Announce("Good evening. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now dim lighting in the public hallways in order to accommodate the circadian rhythm of some species.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
activate()
else
if (isactive)
deactivate()
var/time = worldtime2hours()
if (time <= config.nl_finish || time >= config.nl_start)
if (!isactive)
command_announcement.Announce("Good evening. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now dim lighting in the public hallways in order to accommodate the circadian rhythm of some species.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
activate()
else
if (isactive)
command_announcement.Announce("Good morning. The time is [worldtime2text()]. \n\nThe automated systems aboard the [station_name()] will now return the public hallway lighting levels to normal.", "Automated Lighting System", new_sound = 'sound/misc/bosuns_whistle.ogg')
deactivate()
// 'whitelisted' areas are areas that have nightmode explicitly enabled
+112 -90
View File
@@ -4,54 +4,141 @@
* Scheduler *
************/
/datum/controller/process/scheduler
var/list/scheduled_tasks
var/tick_completed = TRUE
var/list/queued_tasks
var/list/datum/scheduled_task/tasks = list()
var/datum/scheduled_task/head
/datum/controller/process/scheduler/setup()
name = "scheduler"
schedule_interval = 2 SECONDS
scheduled_tasks = list()
schedule_interval = 2
scheduler = src
/datum/controller/process/scheduler/doWork()
if (tick_completed)
queued_tasks = scheduled_tasks.Copy()
tick_completed = FALSE
while (head && !PSCHED_CHECK_TICK)
if (head.destroyed)
head.kill()
head = head.next
tasks -= head
continue
if (head.trigger_time >= world.time)
return // Nothing after this will be ready to fire.
while (queued_tasks.len)
var/datum/scheduled_task/task = queued_tasks[queued_tasks.len]
queued_tasks.len--
// This one's ready to fire, process it.
var/datum/scheduled_task/task = head
head = task.next
task.pre_process()
task.process()
task.post_process()
if (task.destroyed) // post_process probably destroyed it.
tasks -= task
task.kill()
/datum/controller/process/scheduler/proc/queue(datum/scheduled_task/task)
if (!task || !task.trigger_time)
warning("scheduler: Invalid task queued! Ignoring.")
return
// Reset this in-case we're doing a rebuild.
task.next = null
if (!head && !tasks.len)
head = task
tasks += task
return
if (!head) // Head's missing but we still have tasks, rebuild.
tasks += task
rebuild_queue()
return
var/datum/scheduled_task/curr = head
while (curr.next && curr.trigger_time < task.trigger_time)
curr = curr.next
if (!curr.next)
// We're at the end of the queue, just append.
curr.next = task
tasks += task
return
// Inserting midway into the list.
var/old_next = curr.next
curr.next = task
task.next = old_next
tasks += task
// Rebuilds the queue linked-list, removing invalid or destroyed entries.
/datum/controller/process/scheduler/proc/rebuild_queue()
log_debug("scheduler: Rebuilding queue.")
var/list/old_tasks = tasks
tasks = list()
if (!old_tasks.len)
log_debug("scheduler: rebuild was called on empty queue! Aborting.")
return
// Find the head.
for (var/thing in old_tasks)
var/datum/scheduled_task/task = thing
if (QDELETED(task))
scheduled_tasks -= task
old_tasks -= task
continue
if (world.time > task.trigger_time)
unschedule(task)
task.pre_process()
task.process()
task.post_process()
F_SCHECK
if (task.destroyed)
old_tasks -= task
task.kill()
continue
tick_completed = TRUE
if (!head || task.trigger_time < head.trigger_time)
head = task
if (!head)
log_debug("scheduler: unable to find head! Purging task queue.")
for (var/thing in old_tasks)
var/datum/scheduled_task/task = thing
if (QDELETED(task))
continue
task.kill()
head = null
return
// Don't queue the head.
tasks += head
old_tasks -= head
// Now rebuild the queue.
for (var/thing in old_tasks)
var/datum/scheduled_task/task = thing
queue(task)
log_debug("scheduler: Queue diff is [old_tasks.len - tasks.len].")
/datum/controller/process/scheduler/statProcess()
..()
stat(null, "[scheduled_tasks.len] tasks, [queued_tasks.len] queued")
stat(null, "[tasks.len] task\s")
/datum/controller/process/scheduler/proc/schedule(var/datum/scheduled_task/st)
scheduled_tasks += st
destroyed_event.register(st, src, /datum/controller/process/scheduler/proc/unschedule)
queue(st)
/datum/controller/process/scheduler/proc/unschedule(var/datum/scheduled_task/st)
if(st in scheduled_tasks)
scheduled_tasks -= st
destroyed_event.unregister(st, src)
st.destroyed = TRUE
/**********
* Helpers *
**********/
/proc/schedule(source, the_proc, time, ...)
if (time < 0)
return
time += world.time
var/list/the_args
if (length(args) > 3)
the_args = args.Copy(4)
else
the_args = list()
if (source)
return schedule_task_with_source(time, the_proc, the_args)
else
return schedule_task(time, the_proc, the_args)
/proc/schedule_task_in(var/in_time, var/procedure, var/list/arguments = list())
return schedule_task(world.time + in_time, procedure, arguments)
@@ -77,68 +164,3 @@
var/datum/scheduled_task/st = new/datum/scheduled_task/source(trigger_time, source, procedure, arguments, /proc/repeat_scheduled_task, list(repeat_interval))
scheduler.schedule(st)
return st
/*************
* Task Datum *
*************/
/datum/scheduled_task
var/trigger_time
var/procedure
var/list/arguments
var/task_after_process
var/list/task_after_process_args
/datum/scheduled_task/New(var/trigger_time, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
..()
src.trigger_time = trigger_time
src.procedure = procedure
src.arguments = arguments ? arguments : list()
src.task_after_process = task_after_process ? task_after_process : /proc/destroy_scheduled_task
src.task_after_process_args = istype(task_after_process_args) ? task_after_process_args : list()
task_after_process_args += src
/datum/scheduled_task/Destroy()
procedure = null
arguments.Cut()
task_after_process = null
task_after_process_args.Cut()
return ..()
/datum/scheduled_task/proc/pre_process()
task_triggered_event.raise_event(list(src))
/datum/scheduled_task/proc/process()
if(procedure)
call(procedure)(arglist(arguments))
/datum/scheduled_task/proc/post_process()
call(task_after_process)(arglist(task_after_process_args))
// Resets the trigger time, has no effect if the task has already triggered
/datum/scheduled_task/proc/trigger_task_in(var/trigger_in)
src.trigger_time = world.time + trigger_in
/datum/scheduled_task/source
var/datum/source
/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
src.source = source
destroyed_event.register(src.source, src, /datum/scheduled_task/source/proc/source_destroyed)
..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
/datum/scheduled_task/source/Destroy()
source = null
return ..()
/datum/scheduled_task/source/process()
call(source, procedure)(arglist(arguments))
/datum/scheduled_task/source/proc/source_destroyed()
qdel(src)
/proc/destroy_scheduled_task(var/datum/scheduled_task/st)
qdel(st)
/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
st.trigger_time = world.time + trigger_delay
scheduler.schedule(st)
+10 -4
View File
@@ -177,8 +177,8 @@ var/list/gamemode_cache = list()
var/ghost_interaction = 0
var/night_lighting = 0
var/nl_start = 19 * TICKS_IN_HOUR
var/nl_finish = 8 * TICKS_IN_HOUR
var/nl_start = 19
var/nl_finish = 8
var/comms_password = ""
@@ -187,6 +187,7 @@ var/list/gamemode_cache = list()
var/use_discord_bot = 0
var/discord_bot_host = "localhost"
var/discord_bot_port = 0
var/use_discord_pins = 0
var/python_path = "python" //Path to the python executable. Defaults to "python" on windows and "/usr/bin/env python2" on unix
var/use_lib_nudge = 0 //Use the C library nudge instead of the python nudge.
var/use_overmap = 0
@@ -629,10 +630,10 @@ var/list/gamemode_cache = list()
config.night_lighting = 1
if("nl_start_hour")
config.nl_start = text2num(value) * TICKS_IN_HOUR
config.nl_start = text2num(value)
if("nl_finish_hour")
config.nl_finish = text2num(value) * TICKS_IN_HOUR
config.nl_finish = text2num(value)
if("disable_player_mice")
config.disable_player_mice = 1
@@ -652,6 +653,9 @@ var/list/gamemode_cache = list()
if("discord_bot_port")
config.discord_bot_port = value
if("use_discord_pins")
config.use_discord_pins = 1
if("python_path")
if(value)
config.python_path = value
@@ -878,6 +882,8 @@ var/list/gamemode_cache = list()
discord_bot.active = 1
if ("robust_debug")
discord_bot.robust_debug = 1
if ("subscriber")
discord_bot.subscriber_role = value
else
log_misc("Unknown setting in discord configuration: '[name]'")
+29 -11
View File
@@ -194,8 +194,16 @@ datum/controller/vote
proc/submit_vote(var/ckey, var/vote)
if(mode)
if(config.vote_no_dead && usr.stat == DEAD && !usr.client.holder)
return 0
if (mode == "crew_transfer")
if(config.vote_no_dead && usr && !usr.client.holder)
if (isnewplayer(usr))
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
return 0
else if (isobserver(usr))
var/mob/dead/observer/O = usr
if (O.started_as_observer)
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
return 0
if(vote && vote >= 1 && vote <= choices.len)
if(current_votes[ckey])
choices[choices[current_votes[ckey]]]--
@@ -211,6 +219,16 @@ datum/controller/vote
// Transfer votes are their own little special snowflake
var/next_allowed_time = 0
if (vote_type == "crew_transfer")
if (config.vote_no_dead && !usr.client.holder)
if (isnewplayer(usr))
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
return 0
else if (isobserver(usr))
var/mob/dead/observer/O = usr
if (O.started_as_observer)
usr << "<span class='warning'>You must be playing or have been playing to start a vote.</span>"
return 0
if (last_transfer_vote)
next_allowed_time = (last_transfer_vote + config.vote_delay)
else
@@ -275,16 +293,16 @@ datum/controller/vote
log_vote(text)
world << "<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=\ref[src]'>here</a> to place your votes.\nYou have [config.vote_period/10] seconds to vote.</font>"
for(var/client/C in clients)
for(var/cc in clients)
var/client/C = cc
if(C.prefs.asfx_togs & ASFX_VOTE) //Personal mute
C << sound('sound/effects/vote.ogg')
switch(vote_type)
if("crew_transfer")
world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
if("gamemode")
world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
if("custom")
world << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
switch(vote_type)
if("crew_transfer")
C << sound('sound/effects/vote.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
if("gamemode")
C << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
if("custom")
C << sound('sound/ambience/alarm4.ogg', repeat = 0, wait = 0, volume = 50, channel = 3)
if(mode == "gamemode" && round_progressing)
round_progressing = 0
world << "<font color='red'><b>Round start has been delayed.</b></font>"
+335 -48
View File
@@ -1,6 +1,13 @@
#define CHAN_ADMIN "channel_admin"
#define CHAN_CCIAA "channel_cciaa"
#define CHAN_ANNOUNCE "channel_announce"
#define CHAN_INVITE "channel_invite"
#define SEND_OK 0
#define SEND_TIMEOUT 1
#define ERROR_PROC 2
#define ERROR_CURL 3
#define ERROR_HTTP 4
var/datum/discord_bot/discord_bot = null
@@ -15,21 +22,34 @@ var/datum/discord_bot/discord_bot = null
discord_bot.update_channels()
if (config.use_discord_pins && server_greeting)
server_greeting.update_pins()
return 1
/datum/discord_bot
var/list/channels = list()
var/list/channels_to_group = list() // Group flag -> list of channel datums map.
var/list/channels = list() // Channel ID -> channel datum map. Will ensure that only one datum per channel ID exists.
var/datum/discord_channel/invite = null // The channel datum where the ingame Join Channel button will link to.
var/active = 0
var/auth_token = ""
var/subscriber_role = ""
var/robust_debug = 0
// Lazy man's rate limiting vars
var/rate_limited_since = 0
var/queue_being_pushed = 0
var/datum/scheduled_task/push_task
var/list/queue = list()
/*
* Proc update_channels
* Used to load channels from the database and construct them with the discord API.
* Wipes all current channels and channel maps.
*
* @return num - Error code. 0 upon success.
*/
/datum/discord_bot/proc/update_channels()
if (!active)
return 1
@@ -38,27 +58,56 @@ var/datum/discord_bot/discord_bot = null
log_debug("BOREALIS: Failed to update channels due to missing database.")
return 2
channels = list()
// Reset the channel lists.
channels_to_group.Cut()
channels.Cut()
var/DBQuery/channel_query = dbcon.NewQuery("SELECT channel_group, channel_id FROM discord_channels")
var/DBQuery/channel_query = dbcon.NewQuery("SELECT channel_group, channel_id, pin_flag, server_id FROM discord_channels")
channel_query.Execute()
var/list/A
while (channel_query.NextRow())
if (isnull(channels[channel_query.item[1]]))
channels[channel_query.item[1]] = list()
// Create the channel map.
if (isnull(channels_to_group[channel_query.item[1]]))
channels_to_group[channel_query.item[1]] = list()
A = channels[channel_query.item[1]]
A += channel_query.item[2]
var/datum/discord_channel/B = channels[channel_query.item[2]]
// We don't have this channel datum yet.
if (isnull(B))
B = new(channel_query.item[2], channel_query.item[4], text2num(channel_query.item[3]))
if (!B)
log_debug("BOREALIS: Bad channel data during update channels. [jointext(channel_query.item, ", ")].")
continue
channels[channel_query.item[2]] = B
if (text2num(channel_query.item[3]))
B.pin_flag |= text2num(channel_query.item[3])
// Add the channel to the required lists.
channels_to_group[channel_query.item[1]] += B
if (!isnull(channels_to_group[CHAN_INVITE]))
invite = channels_to_group[CHAN_INVITE][1]
else if (robust_debug)
log_debug("BOREALIS: No invite channel designated.")
log_debug("BOREALIS: Channels updated successfully.")
return 0
/*
* Proc send_message
* Used to send a message to a specific channel group.
*
* @param text channel_group - The name of the channel group which to target.
* @param text message - The message to send.
*/
/datum/discord_bot/proc/send_message(var/channel_group, var/message)
if (!active || !auth_token)
return
if (!channel_group || !channels.len || isnull(channels[channel_group]))
if (!channel_group || !channels.len || isnull(channels_to_group[channel_group]))
return
if (!message)
@@ -70,18 +119,18 @@ var/datum/discord_bot/discord_bot = null
// Let's run it through the proper JSON encoder, just in case of special characters.
message = json_encode(list("content" = message))
var/list/A = channels[channel_group]
var/list/A = channels_to_group[channel_group]
var/list/sent = list()
for (var/channel in A)
if (send_post_request("https://discordapp.com/api/channels/[channel]/messages", message, "Authorization: Bot [auth_token]", "Content-Type: application/json") == 429)
for (var/B in A)
var/datum/discord_channel/channel = B
if (channel.send_message_to(auth_token, message) == SEND_TIMEOUT)
// Whoopsies, rate limited.
// Set up the queue.
rate_limited_since = world.time
queue.Add(list(message, A - sent))
queue.Add(list(list(message, A - sent)))
// Schedule a push.
spawn (100)
push_queue()
if (!push_task)
push_task = schedule_task_with_source_in(10 SECONDS, src, /datum/discord_bot/proc/push_queue)
// And exit.
return
@@ -89,43 +138,87 @@ var/datum/discord_bot/discord_bot = null
sent += channel
if (robust_debug)
log_debug("BOEALIS: Message sent to [channel_group]. JSON body: '[message]'")
log_debug("BOREALIS: Message sent to [channel_group]. JSON body: '[message]'")
/*
* Proc retreive_pins
* Used to fetch a list of all pins from the designated channels.
*
* @return list - A multilayered list of flags associated with pins. Structure looks like this:
* list("pin_flag" = list(list("author" = author name, "content" = content),
* list("author" = author name, "content" = content)))
*/
/datum/discord_bot/proc/retreive_pins()
if (!active || !auth_token)
return list()
if (!channels.len || isnull(channels_to_group["channel_pins"]))
testing("No group.")
return list()
var/list/output = list()
for (var/A in channels_to_group["channel_pins"])
var/datum/discord_channel/channel = A
if (isnull(output["[channel.pin_flag]"]))
output["[channel.pin_flag]"] = list()
output["[channel.pin_flag]"] += channel.get_pins(auth_token)
return output
/*
* Proc retreive_invite
* Used to retreive the invite to the invite channel.
* One will be created if none exist.
*
* @return text - The invite URL to the designated invite channel.
*/
/datum/discord_bot/proc/retreive_invite()
if (!active || !auth_token)
return ""
if (!invite)
return ""
var/res = invite.get_invite(auth_token)
return isnum(res) ? "" : res
/*
* Proc send_to_admin
* Forwards a message to the admin channels.
*/
/datum/discord_bot/proc/send_to_admins(message)
send_message(CHAN_ADMIN, message)
/*
* Proc send_to_cciaa
* Forwards a message to the CCIAA channels.
*/
/datum/discord_bot/proc/send_to_cciaa(message)
send_message(CHAN_CCIAA, message)
/datum/discord_bot/proc/send_to_announce(message)
/*
* Proc send_to_announce
* Forwards a message to the announcements channels.
*/
/datum/discord_bot/proc/send_to_announce(message, prepend_role = 0)
if (prepend_role && subscriber_role)
message = "<@&[subscriber_role]> " + message
send_message(CHAN_ANNOUNCE, message)
/*
* Proc push_queue
* Handles the queue pushing for the bot. If there is no need to reschedule (all messages get successfully
* pushed), then it deletes push_task and sets it back to null. Otherwise, it simply reschedules it.
*/
/datum/discord_bot/proc/push_queue()
// What facking queue.
if (!queue.len)
if (!queue || !queue.len)
if (robust_debug)
log_debug("BOREALIS: Attempted to push a null length queue.")
if (queue_being_pushed)
queue_being_pushed = 0
return
if (queue_being_pushed)
if (robust_debug)
log_debug("BOREALIS: Attempted to initialize a second queue driver.")
return
if ((world.time - rate_limited_since) < 100)
// Something broke the limit again. Ideally, this wouldn't happen. But sure.
// Use a longer timeout, just in case.
spawn (200)
push_queue()
queue_being_pushed = 0
return
// Async process lock var. No touchy.
queue_being_pushed = 1
// A[1] - message body.
// A[2] - list of channels to send to.
var/message
@@ -134,22 +227,216 @@ var/datum/discord_bot/discord_bot = null
message = A[1]
destinations = A[2]
for (var/channel in destinations)
if (send_post_request("https://discordapp.com/api/channels/[channel]/messages", message, "Authorization: Bot [auth_token]", "Content-Type: application/json") == 429)
// Limited again. Reschedule.
rate_limited_since = world.time
spawn (100)
push_queue()
for (var/B in destinations)
var/datum/discord_channel/channel = B
if (channel.send_message_to(auth_token, message) == SEND_TIMEOUT)
// Tasks nuke themselves after use. So just make a new one! What could possibly go wrong!
push_task = schedule_task_with_source_in(10 SECONDS, src, /datum/discord_bot/proc/push_queue)
queue_being_pushed = 0
return
else
destinations.Remove(channel)
queue.Remove(A)
queue_being_pushed = 0
// A holder class for channels.
/datum/discord_channel
var/id = ""
var/server_id = ""
var/pin_flag = 0
var/invite_url = ""
/*
* Constructor
*
* @param text _id - the discord API id of the channel, as a string.
* @param text _sid - the discord API server id for the channel, as a string.
* @param num _pin - the bitflags of admin permissions which have access to the pins from this channel.
*/
/datum/discord_channel/New(var/_id, var/_sid, var/_pin)
id = _id
server_id = _sid
if (_pin)
pin_flag = _pin
/*
* Proc send_message_to
* Sends a message to the channel.
*
* @param text token - the authorization token to be used for the requests.
* @param text message - the sanitized message content to be sent.
* @return num - a specific return code for the discord_bot to handle.
*/
/datum/discord_channel/proc/send_message_to(var/token, var/message)
if (!token || !message)
return ERROR_PROC
var/res = send_post_request("https://discordapp.com/api/channels/[id]/messages", message, "Authorization: Bot [token]", "Content-Type: application/json")
switch (res)
if (-1)
return ERROR_PROC
if (200)
return SEND_OK
if (429)
return SEND_TIMEOUT
if (0 to 90)
log_debug("BOREALIS: cURL error while forwarding message to Discord API: [res]. Message body: [message].")
return ERROR_CURL
else
log_debug("BOREALIS: HTTP error while forwarding message to Discord API: [res]. Channel: [id]. Message body: [message].")
return ERROR_HTTP
/*
* Proc get_pins
* Retreives a list of pinned messages from the channel.
*
* @param text token - the authorization token to be used for the requests.
* @return mixed - 2d array of content upon success, in format: list(list("author" = text, "content" = text)).
* Num upon failure.
*/
/datum/discord_channel/proc/get_pins(var/token)
var/res = send_get_request("https://discordapp.com/api/channels/[id]/pins", "Authorization: Bot [token]")
// Is a number, so an error code.
if (isnum(res))
switch (res)
if (-1)
return ERROR_PROC
if (0 to 90)
log_debug("BOREALIS: cURL error while fetching pins from the Discord API: [res].")
return ERROR_CURL
if (100 to 600)
log_debug("BOREALIS: HTTP error while fetching pins from the Discord API: [res].")
return ERROR_HTTP
else
log_debug("BOREALIS: Unknown response code while fetching pins from the Discord API: [res].")
return ERROR_PROC
else
var/list/A = res
var/list/pinned_messages = list()
// Begin processing the list structure delivered back to us from send_get_request.
for (var/list/B in A)
var/content = B["content"]
// We have mentions to take care of.
if (!isnull(B["mentions"]))
var/list/mentions = B["mentions"]
for (var/list/C in mentions)
content = replacetextEx(content, "<@[C["id"]]>", C["username"])
// Role mentions are up next.
if (!isnull(B["mention_roles"]))
var/list/mentions = B["mention_roles"]
for (var/C in mentions)
content = replacetextEx(content, "<@&[C]>", "@SomeRole")
pinned_messages += list(list("author" = B["author"]["username"], "content" = content))
return pinned_messages
/*
* Proc get_invite
* Retreives (or creates if none found) an invite URL to the specific channel.
*
* @param text token - the authorization token to be used for the requests.
* @return mixed - text upon success, the link to the invite; num upon failure.
*/
/datum/discord_channel/proc/get_invite(var/token)
if (invite_url)
return invite_url
var/res = send_get_request("https://discordapp.com/api/channels/[id]/invites", "Authorization: Bot [token]")
// Is a number, so an error code.
if (isnum(res))
switch (res)
if (-1)
return ERROR_PROC
if (0 to 90)
log_debug("BOREALIS: cURL error while fetching pins from the Discord API: [res].")
return ERROR_CURL
if (100 to 600)
log_debug("BOREALIS: HTTP error while fetching pins from the Discord API: [res].")
return ERROR_HTTP
else
log_debug("BOREALIS: Unknown response code while fetching pins from the Discord API: [res].")
return ERROR_PROC
else
var/list/A = res
// No length to return data, but a valid 200 return header.
// So we simply have no invites active. Make one!
if (!A || !A.len)
return create_invite(token)
var/best_age = A[1]["max_age"]
var/code = A[1]["code"]
// Find the best invite.
// Why? Because round time expires are lame, I guess.
if (best_age != 0)
for (var/i = 2, i < A.len, i++)
if (A[i]["max_age"] == 0)
code = A[i]["code"]
break
if (best_age < A[i]["max_age"])
best_age = A[i]["max_age"]
code = A[i]["code"]
// Sanity check for debug I guess.
if (!code)
log_debug("BOREALIS: Retreived an empty invite. This should not happen. Response object: [json_encode(A)]")
// Save the URL for later retreival.
invite_url = "https://discord.gg/[code]"
return invite_url
/*
* Proc create_invite
* Creates a permanent invite to a channel and returns it, under the assumption that
* there are no other invites for this channel.
*
* @param text token - the authorization token to be used for the requests.
* @return mixed - String upon success - the URL of the newly generated invite.
* Num upon failure.
*/
/datum/discord_channel/proc/create_invite(var/token)
var/data = list("max_age" = 0, "max_uses" = 0)
var/res = send_post_request("https://discordapp.com/api/channels/[id]/invites", json_encode(data), "Authorization: Bot [token]", "Content-Type: application/json")
if (res == 200)
var/list/get_req = send_get_request("https://discordapp.com/api/channels/[id]/invites", "Authorization: Bot [token]")
if (!istype(get_req) || !get_req.len)
return ERROR_PROC
// The first index should now exist. So we just use that!
invite_url = "https://discord.gg/[get_req[1]["code"]]"
return invite_url
#undef CHAN_ADMIN
#undef CHAN_CCIAA
#undef CHAN_ANNOUNCE
#undef CHAN_INVITE
#undef SEND_OK
#undef SEND_TIMEOUT
#undef ERROR_PROC
#undef ERROR_CURL
#undef ERROR_HTTP
+79
View File
@@ -0,0 +1,79 @@
/*************
* Task Datum *
*************/
/datum/scheduled_task
var/trigger_time
var/procedure
var/list/arguments
var/task_after_process
var/list/task_after_process_args
var/datum/scheduled_task/next
var/destroyed = FALSE
/datum/scheduled_task/New(var/trigger_time, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
..()
src.trigger_time = trigger_time
src.procedure = procedure
src.arguments = arguments ? arguments : list()
src.task_after_process = task_after_process ? task_after_process : /proc/destroy_scheduled_task
src.task_after_process_args = istype(task_after_process_args) ? task_after_process_args : list()
task_after_process_args += src
/datum/scheduled_task/Destroy()
if (!destroyed)
kill(no_del = TRUE)
procedure = null
arguments.Cut()
task_after_process = null
task_after_process_args.Cut()
return ..()
/datum/scheduled_task/proc/pre_process()
return
/datum/scheduled_task/proc/process()
if(procedure)
call(procedure)(arglist(arguments))
/datum/scheduled_task/proc/post_process()
call(task_after_process)(arglist(task_after_process_args))
// Resets the trigger time, has no effect if the task has already triggered
/datum/scheduled_task/proc/trigger_task_in(var/trigger_in)
src.trigger_time = world.time + trigger_in
/datum/scheduled_task/proc/kill(no_del = FALSE)
if (!destroyed)
warning("scheduler: Non-destroyed task was killed!")
destroyed = TRUE
if (src in scheduler.tasks)
warning("scheduler: Task was not cleaned up correctly, rebuilding scheduler queue!")
scheduler.rebuild_queue()
if (!no_del)
qdel(src)
/datum/scheduled_task/source
var/datum/source
/datum/scheduled_task/source/New(var/trigger_time, var/datum/source, var/procedure, var/list/arguments, var/proc/task_after_process, var/list/task_after_process_args)
src.source = source
..(trigger_time, procedure, arguments, task_after_process, task_after_process_args)
/datum/scheduled_task/source/Destroy()
source = null
return ..()
/datum/scheduled_task/source/process()
call(source, procedure)(arglist(arguments))
/datum/scheduled_task/source/proc/source_destroyed()
destroyed = TRUE
/proc/destroy_scheduled_task(var/datum/scheduled_task/st)
st.destroyed = TRUE
/proc/repeat_scheduled_task(var/trigger_delay, var/datum/scheduled_task/st)
st.trigger_time = world.time + trigger_delay
scheduler.schedule(st)
+111 -15
View File
@@ -46,8 +46,9 @@
if (F["motd"])
F["motd"] >> motd
if (F["memo"])
F["memo"] >> memo_list
if (!config.use_discord_pins)
if (F["memo"])
F["memo"] >> memo_list
update_data()
@@ -63,18 +64,32 @@
motd = initial(motd)
motd_hash = ""
if (memo_list.len)
memo = ""
for (var/ckey in memo_list)
var/data = {"<p><b>[ckey]</b> wrote on [memo_list[ckey]["date"]]:<br>
[memo_list[ckey]["content"]]</p>"}
if (!config.use_discord_pins)
// The initialization of memos in case use_discord_pins == 1 is done in discord_bot.dm
// Primary reason is to avoid null references when the bot isn't created yet.
if (memo_list.len)
memo = ""
for (var/ckey in memo_list)
var/data = {"<p><b>[ckey]</b> wrote on [memo_list[ckey]["date"]]:<br>
[memo_list[ckey]["content"]]</p>"}
memo += data
memo += data
memo_hash = md5(memo)
else
memo = initial(memo)
memo_hash = ""
memo_hash = md5(memo)
else
memo = initial(memo)
memo_hash = ""
/datum/server_greeting/proc/update_pins()
var/list/temp_list = discord_bot.retreive_pins()
// A is a number in a string form
// temp_list[A] is a list of lists.
for (var/A in temp_list)
var/list/memos = temp_list[A]
var/flag = text2num(A)
memo_list += new /datum/memo_datum(memos, flag)
/*
* Helper to update the MoTD or memo contents.
@@ -94,9 +109,15 @@
motd = new_value
if ("memo_write")
if (config.use_discord_pins)
return 0
memo_list[new_value[1]] = list("date" = time2text(world.realtime, "DD-MMM-YYYY"), "content" = new_value[2])
if ("memo_delete")
if (config.use_discord_pins)
return 0
if (memo_list[new_value])
memo_list -= new_value
else
@@ -132,7 +153,7 @@
if (motd_hash && user.prefs.motd_hash != motd_hash)
outdated_info |= OUTDATED_MOTD
if (user.holder && memo_hash && user.prefs.memo_hash != memo_hash)
if (user.holder && user.prefs.memo_hash != get_memo_hash(user))
outdated_info |= OUTDATED_MEMO
if (user.prefs.notifications.len)
@@ -185,13 +206,13 @@
else
if (outdated_info & OUTDATED_MEMO)
data["update"] = 1
data["changeHash"] = memo_hash
data["changeHash"] = get_memo_hash(user)
else
data["update"] = 0
data["changeHash"] = null
data["div"] = "#memo"
data["content"] = memo
data["content"] = get_memo_content(user)
user << output(JS_SANITIZE(data), "greeting.browser:AddContent")
if (outdated_info & OUTDATED_MOTD)
@@ -216,6 +237,81 @@
if ("request_data")
send_to_javascript(C)
/*
* Gets the appropriate memo hash for the memo system in use.
* Args:
* - var/C client
* Returns:
* - string
*/
/datum/server_greeting/proc/get_memo_hash(var/client/C)
if (!C || !C.holder)
return ""
if (!config.use_discord_pins)
return memo_hash
var/joint_checksum = ""
for (var/A in memo_list)
var/datum/memo_datum/memo = A
if (C.holder.rights & memo.flag)
joint_checksum += memo.hash
return md5(joint_checksum)
/*
* Gets the appropriate memo content for the memo system in use.
* Args:
* - var/C client
* Returns:
* - string if old memo system is used (config.use_discord_pins = 0)
* - list of strings if new memo system is used
*/
/datum/server_greeting/proc/get_memo_content(var/client/C)
if (!C || !C.holder)
return ""
if (!config.use_discord_pins)
return memo
var/list/content = list()
for (var/A in memo_list)
var/datum/memo_datum/memo = A
if (C.holder.rights & memo.flag)
content += memo.contents
return content
/datum/memo_datum
var/contents
var/hash
var/flag
/datum/memo_datum/New(var/list/input = list(), var/_flag)
flag = _flag
// Yes. This is an unfortunately acceptable way of doing it.
// Why? Because you cannot use numbers as indexes in an assoc list without fucking DM.
var/static/list/flags_to_divs = list("[R_ADMIN]" = "danger",
"[R_MOD]" = "warning",
"[(R_MOD|R_ADMIN)]" = "warning",
"[R_CCIAA]" = "info",
"[R_DEV]" = "info")
if (input.len)
contents = "<div class='alert alert-[flags_to_divs["[flag]"]]'>"
for (var/i = 1, i <= input.len, i++)
contents += "<b>[input[i]["author"]]</b> wrote:<br>[nl2br(input[i]["content"])]"
if (i < input.len)
contents += "<hr></hr>"
contents += "</div>"
else
contents = ""
hash = md5(contents)
#undef OUTDATED_NOTE
#undef OUTDATED_MEMO
#undef OUTDATED_MOTD
+41
View File
@@ -1733,3 +1733,44 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
containertype = /obj/structure/closet/crate
containername = "IPC/Shell tag implanters"
group = "Security"
/datum/supply_packs/ame_ctrl
name = "Antimatter Reactor Control Unit"
contains = list(
/obj/machinery/power/am_control_unit
)
cost = 40
containertype = /obj/structure/closet/crate/secure/large
containername = "antimatter reactor control unit kit"
group = "Engineering"
access = access_ce
/datum/supply_packs/ame_section
name = "Antimatter Reactor Shielding Kit"
contains = list(
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container,
/obj/item/device/am_shielding_container
)
cost = 20
containertype = /obj/structure/closet/crate/secure/phoron
containername = "antimatter reactor shielding (9x) crate"
group = "Engineering"
access = access_ce
/datum/supply_packs/ame_fuel
name = "Antimatter Reactor Fuel"
contains = list(
/obj/item/weapon/am_containment
)
cost = 20
containertype = /obj/structure/closet/crate/secure/bin
containername = "antimatter fuel container"
group = "Engineering"
access = access_engine
+4 -3
View File
@@ -45,7 +45,7 @@
update_icons_added(player)
return 1
/datum/antagonist/proc/remove_antagonist(var/datum/mind/player, var/show_message, var/implanted)
/datum/antagonist/proc/remove_antagonist(var/datum/mind/player, var/show_message = TRUE, var/implanted)
if(!istype(player))
return 0
@@ -53,14 +53,15 @@
player.current.verbs -= faction_verb
if(player in current_antagonists)
player.current << "<span class='danger'><font size = 3>You are no longer a [role_text]!</font></span>"
if (show_message)
player.current << "<span class='danger'><font size = 3>You are no longer a [role_text]!</font></span>"
current_antagonists -= player
faction_members -= player
player.special_role = null
update_icons_removed(player)
BITSET(player.current.hud_updateflag, SPECIALROLE_HUD)
if (!is_special_character(player))
if (!is_special_character(player) && !player.current.client.holder)
player.current.client.verbs -= /client/proc/aooc
return 1
+7 -1
View File
@@ -6,6 +6,12 @@ var/datum/antagonist/cultist/cult
if(player.mind in cult.current_antagonists)
return 1
/proc/iscult(var/mob/test)
if (test.faction == "cult")
return 1
else return iscultist(test)
/datum/antagonist/cultist
id = MODE_CULTIST
role_text = "Cultist"
@@ -113,7 +119,7 @@ var/datum/antagonist/cultist/cult
player << "You catch a glimpse of the Realm of Nar-Sie, the Geometer of Blood. You now see how flimsy the world is, you see that it should be open to the knowledge of That Which Waits. Assist your new compatriots in their dark dealings. Their goals are yours, and yours are theirs. You serve the Dark One above all else. Bring It back."
if(player.current && !istype(player.current, /mob/living/simple_animal/construct))
player.current.add_language(LANGUAGE_CULT)
/datum/antagonist/cultist/can_become_antag(var/datum/mind/player, ignore_role = 1)
if(!..())
return 0
+18 -5
View File
@@ -611,6 +611,13 @@ area/space/atmosalert()
flags = RAD_SHIELDED
sound_env = TUNNEL_ENCLOSED
turf_initializer = new /datum/turf_initializer/maintenance()
ambience = list(
'sound/ambience/ambimaint1.ogg',
'sound/ambience/ambimaint2.ogg',
'sound/ambience/ambimaint3.ogg',
'sound/ambience/ambimaint4.ogg',
'sound/ambience/ambimaint5.ogg'
)
station_area = 1
/area/maintenance/civ
@@ -1130,14 +1137,20 @@ area/space/atmosalert()
/area/engineering
name = "\improper Engineering"
icon_state = "engineering"
ambience = list('sound/ambience/ambisin1.ogg','sound/ambience/ambisin2.ogg','sound/ambience/ambisin3.ogg','sound/ambience/ambisin4.ogg')
ambience = list(
'sound/ambience/ambisin1.ogg',
'sound/ambience/ambisin2.ogg',
'sound/ambience/ambisin3.ogg',
'sound/ambience/ambisin4.ogg'
)
station_area = 1
/area/engineering/atmos
name = "\improper Atmospherics"
icon_state = "atmos"
sound_env = LARGE_ENCLOSED
no_light_control = 1
name = "\improper Atmospherics"
icon_state = "atmos"
sound_env = LARGE_ENCLOSED
no_light_control = 1
ambience = list('sound/ambience/ambiatm1.ogg')
/area/engineering/atmos/monitoring
name = "\improper Atmospherics Monitoring Room"
+31 -12
View File
@@ -9,7 +9,7 @@
blend_mode = BLEND_MULTIPLY
/area/New()
icon_state = ""
icon_state = "white"
layer = 10
uid = ++global_uid
all_areas += src
@@ -154,21 +154,40 @@
D.open()
return
#define DO_PARTY(COLOR) animate(color = COLOR, time = 0.5 SECONDS, easing = QUAD_EASING)
/area/proc/updateicon()
if ((fire || eject || party) && (!requires_power||power_environ) && !istype(src, /area/space))//If it doesn't require power, can still activate this proc.
if(fire && !eject && !party)
icon_state = "blue"
/*else if(atmosalm && !fire && !eject && !party)
icon_state = "bluenew"*/
else if(!fire && eject && !party)
icon_state = "red"
else if(party && !fire && !eject)
icon_state = "party"
if(fire && !eject && !party) // FIRE
color = "#ff9292"
animate(src) // stop any current animations.
animate(src, color = "#ffa5b2", time = 1 SECOND, loop = -1, easing = SINE_EASING)
animate(color = "#ff9292", time = 1 SECOND, easing = SINE_EASING)
else if(!fire && eject && !party) // EJECT
color = "#ff9292"
animate(src)
animate(src, color = "#bc8a81", time = 1 SECOND, loop = -1, easing = EASE_IN|CUBIC_EASING)
animate(color = "#ff9292", time = 0.5 SECOND, easing = EASE_OUT|CUBIC_EASING)
else if(party && !fire && !eject) // PARTY
color = "#ff728e"
animate(src)
animate(src, color = "#7272ff", time = 0.5 SECONDS, loop = -1, easing = QUAD_EASING)
DO_PARTY("#72aaff")
DO_PARTY("#ffc68e")
DO_PARTY("#72c6ff")
DO_PARTY("#ff72e2")
DO_PARTY("#72ff8e")
DO_PARTY("#ffff8e")
DO_PARTY("#ff728e")
else
icon_state = "blue-red"
color = "#ffb2b2"
animate(src)
animate(src, color = "#B3DFFF", time = 0.5 SECOND, loop = -1, easing = SINE_EASING)
animate(color = "#ffb2b2", time = 0.5 SECOND, loop = -1, easing = SINE_EASING)
else
// new lighting behaviour with obj lights
icon_state = null
animate(src, color = "#FFFFFF", time = 0.5 SECONDS, easing = QUAD_EASING) // Stop the animation.
#undef DO_PARTY
/*
+1 -10
View File
@@ -223,15 +223,6 @@ its easier to just keep the beam vertical.
/atom/proc/set_dir(new_dir)
. = new_dir != dir
dir = new_dir
/*for(var/datum/light_source/L in light_sources)
if (L.source_atom.offset_light)
L.force_update = 1
if (world.tick_usage < 80)
L.instant_update()//Instant update skips the normal controller process and updates the light now.
//This makes things more responsive, but probably has a performance cost for people spinning rapidly
//Ergo, it checks tick usage first
else
L.source_atom.update_light()*/
/atom/proc/ex_act()
return
@@ -510,4 +501,4 @@ its easier to just keep the beam vertical.
change_area_name(oldarea.name, newarea.name)
/atom/proc/change_area_name(var/oldname, var/newname)
name = replacetext(name,oldname,newname)
name = replacetext(name,oldname,newname)
@@ -0,0 +1,91 @@
/obj/item/weapon/melee/arm_blade
name = "arm blade"
desc = "A grotesque blade made out of bone and flesh that cleaves through people as a hot knife through butter."
icon = 'icons/obj/changeling.dmi'
icon_state = "arm_blade"
item_state = "arm_blade"
contained_sprite = 1
w_class = 4
force = 30
sharp = 1
edge = 1
anchored = 1
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
can_embed = 0
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
var/mob/living/creator
/obj/item/weapon/melee/arm_blade/New()
processing_objects |= src
/obj/item/weapon/melee/arm_blade/Destroy()
processing_objects -= src
..()
/obj/item/weapon/melee/arm_blade/dropped()
visible_message("<span class='danger'>With a sickening crunch, [user] reforms their arm blade into an arm!</span>",
"<span class='notice'>We assimilate the weapon back into our body.</span>",
"<span class='warning'>You hear organic matter ripping and tearing!</span>")
playsound(loc, 'sound/effects/blobattack.ogg', 30, 1)
spawn(1) if(src) qdel(src)
/obj/item/weapon/melee/arm_blade/process()
if(!creator || loc != creator || (creator.l_hand != src && creator.r_hand != src))
// Tidy up a bit.
if(istype(loc,/mob/living))
var/mob/living/carbon/human/host = loc
if(istype(host))
for(var/obj/item/organ/external/organ in host.organs)
for(var/obj/item/O in organ.implants)
if(O == src)
organ.implants -= src
host.pinned -= src
host.embedded -= src
host.drop_from_inventory(src)
spawn(1) if(src) qdel(src)
/obj/item/weapon/shield/riot/changeling
name = "shield-like mass"
desc = "A mass of tough, boney tissue. You can still see the fingers as a twisted pattern in the shield."
icon = 'icons/obj/changeling.dmi'
icon_state = "ling_shield"
item_state = "ling_shield"
contained_sprite = 1
slot_flags = null
anchored = 1
throwforce = 0 //Just to be on the safe side
throw_range = 0
throw_speed = 0
can_embed = 0
var/mob/living/creator
/obj/item/weapon/shield/riot/changeling/New()
processing_objects |= src
/obj/item/weapon/shield/riot/changeling/Destroy()
processing_objects -= src
..()
/obj/item/weapon/shield/riot/changeling/dropped()
visible_message("<span class='danger'>With a sickening crunch, [user] reforms their arm blade into an arm!</span>",
"<span class='notice'>We assimilate the weapon back into our body.</span>",
"<span class='warning'>You hear organic matter ripping and tearing!</span>")
playsound(loc, 'sound/effects/blobattack.ogg', 30, 1)
spawn(1) if(src) qdel(src)
/obj/item/weapon/shield/riot/changeling/process()
if(!creator || loc != creator || (creator.l_hand != src && creator.r_hand != src))
// Tidy up a bit.
if(istype(loc,/mob/living))
var/mob/living/carbon/human/host = loc
if(istype(host))
for(var/obj/item/organ/external/organ in host.organs)
for(var/obj/item/O in organ.implants)
if(O == src)
organ.implants -= src
host.pinned -= src
host.embedded -= src
host.drop_from_inventory(src)
spawn(1) if(src) qdel(src)
@@ -1,8 +1,7 @@
var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","Epsilon","Zeta","Eta","Theta","Iota","Kappa","Lambda","Mu","Nu","Xi","Omicron","Pi","Rho","Sigma","Tau","Upsilon","Phi","Chi","Psi","Omega")
/datum/changeling //stores changeling powers, changeling recharge thingie, changeling absorbed DNA and changeling ID (for changeling hivemind)
var/list/absorbed_dna = list()
var/list/absorbed_species = list()
var/list/datum/absorbed_dna/absorbed_dna = list()
var/list/absorbed_languages = list()
var/absorbedcount = 0
var/chem_charges = 20
@@ -31,12 +30,24 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
geneticdamage = max(0, geneticdamage-1)
/datum/changeling/proc/GetDNA(var/dna_owner)
var/datum/dna/chosen_dna
for(var/datum/dna/DNA in absorbed_dna)
if(dna_owner == DNA.real_name)
chosen_dna = DNA
break
return chosen_dna
for(var/datum/absorbed_dna/DNA in absorbed_dna)
if(dna_owner == DNA.name)
return DNA
/mob/proc/absorbDNA(var/datum/absorbed_dna/newDNA)
var/datum/changeling/changeling = null
if(src.mind && src.mind.changeling)
changeling = src.mind.changeling
if(!changeling)
return
for(var/language in newDNA.languages)
changeling.absorbed_languages |= language
changeling_update_languages(changeling.absorbed_languages)
if(!changeling.GetDNA(newDNA.name)) // Don't duplicate - I wonder if it's possible for it to still be a different DNA? DNA code could use a rewrite
changeling.absorbed_dna += newDNA
//Restores our verbs. It will only restore verbs allowed during lesser (monkey) form if we are not human
/mob/proc/make_changeling()
@@ -65,14 +76,13 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
if(!(P in src.verbs))
src.verbs += P.verbpath
mind.changeling.absorbed_dna |= dna
for(var/language in languages)
mind.changeling.absorbed_languages |= language
var/mob/living/carbon/human/H = src
if(istype(H))
mind.changeling.absorbed_species += H.species.name
for(var/language in languages)
mind.changeling.absorbed_languages |= language
var/datum/absorbed_dna/newDNA = new(H.real_name, H.dna, H.species.name, H.languages)
absorbDNA(newDNA)
return 1
@@ -83,13 +93,6 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
if(P.isVerb)
verbs -= P.verbpath
//removes changeling powers but keeps those with allowduringlesserform
/mob/proc/remove_changeling_powers_monkey()
if(!mind || !mind.changeling) return
for(var/datum/power/changeling/P in mind.changeling.purchasedpowers)
if(P.isVerb && (P.allowduringlesserform == 0))
verbs -= P.verbpath
//Helper proc. Does all the checks and stuff for us to avoid copypasta
/mob/proc/changeling_power(var/required_chems=0, var/required_dna=0, var/max_genetic_damage=100, var/max_stat=0)
@@ -133,46 +136,6 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
return
//Used to switch species based on the changeling datum.
/mob/proc/changeling_change_species()
set category = "Changeling"
set name = "Change Species (5)"
var/mob/living/carbon/human/H = src
if(!istype(H))
src << "<span class='warning'>We may only use this power while in humanoid form.</span>"
return
var/datum/changeling/changeling = changeling_power(5,1,0)
if(!changeling) return
if(changeling.absorbed_species.len < 2)
src << "<span class='warning'>We do not know of any other species genomes to use.</span>"
return
var/S = input("Select the target species: ", "Target Species", null) as null|anything in changeling.absorbed_species
if(!S) return
domutcheck(src, null)
changeling.chem_charges -= 5
changeling.geneticdamage = 30
src.visible_message("<span class='warning'>[src] transforms!</span>")
src.verbs -= /mob/proc/changeling_change_species
H.set_species(S,1) //Until someone moves body colour into DNA, they're going to have to use the default.
spawn(10)
src.verbs += /mob/proc/changeling_change_species
src.regenerate_icons()
changeling_update_languages(changeling.absorbed_languages)
feedback_add_details("changeling_powers","TR")
return 1
//Absorbs the victim's DNA making them uncloneable. Requires a strong grip on the victim.
//Doesn't cost anything as it's the most basic ability.
/mob/proc/changeling_absorb_dna()
@@ -196,6 +159,10 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
src << "<span class='warning'>We do not know how to parse this creature's DNA!</span>"
return
if(islesserform(T))
src << "<span class='warning'>This creature DNA is not compatible with our form!</span>"
return
if(HUSK in T.mutations)
src << "<span class='warning'>This creature's DNA is ruined beyond useability!</span>"
return
@@ -213,6 +180,7 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
switch(stage)
if(1)
src << "<span class='notice'>This creature is compatible. We must hold still...</span>"
src.visible_message("<span class='warning'>[src]'s skin begins to shift and squirm!</span>")
if(2)
src << "<span class='notice'>We extend a proboscis.</span>"
src.visible_message("<span class='warning'>[src] extends a proboscis!</span>")
@@ -234,9 +202,6 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
src.visible_message("<span class='danger'>[src] sucks the fluids from [T]!</span>")
T << "<span class='danger'>You have been absorbed by the changeling!</span>"
T.dna.real_name = T.real_name //Set this again, just to be sure that it's properly set.
changeling.absorbed_dna |= T.dna
if(src.nutrition < max_nutrition) src.nutrition = min((nutrition + T.nutrition), max_nutrition)
changeling.chem_charges += 10
changeling.geneticpoints += 2
@@ -247,16 +212,15 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
changeling_update_languages(changeling.absorbed_languages)
//Steal their species!
if(T.species && !(T.species.name in changeling.absorbed_species))
changeling.absorbed_species += T.species.name
var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages)
absorbDNA(newDNA)
if(T.mind && T.mind.changeling)
if(T.mind.changeling.absorbed_dna)
for(var/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot
if(dna_data in changeling.absorbed_dna)
for(var/datum/absorbed_dna/dna_data in T.mind.changeling.absorbed_dna) //steal all their loot
if(changeling.GetDNA(dna_data.name))
continue
changeling.absorbed_dna += dna_data
absorbDNA(dna_data)
changeling.absorbedcount++
T.mind.changeling.absorbed_dna.len = 1
@@ -295,31 +259,44 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
if(!changeling) return
var/list/names = list()
for(var/datum/dna/DNA in changeling.absorbed_dna)
names += "[DNA.real_name]"
for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
names += "[DNA.name]"
var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
if(!S) return
var/datum/dna/chosen_dna = changeling.GetDNA(S)
var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
if(!chosen_dna)
return
changeling.chem_charges -= 5
src.visible_message("<span class='warning'>[src] transforms!</span>")
changeling.geneticdamage = 30
src.dna = chosen_dna.Clone()
src.real_name = chosen_dna.real_name
src.flavor_text = ""
src.UpdateAppearance()
domutcheck(src, null)
handle_changeling_transform(chosen_dna)
src.verbs -= /mob/proc/changeling_transform
spawn(10) src.verbs += /mob/proc/changeling_transform
spawn(10)
src.verbs += /mob/proc/changeling_transform
changeling_update_languages(changeling.absorbed_languages)
feedback_add_details("changeling_powers","TR")
return 1
/mob/proc/handle_changeling_transform(var/datum/absorbed_dna/chosen_dna)
src.visible_message("<span class='warning'>[src] transforms!</span>")
if(ishuman(src))
var/mob/living/carbon/human/H = src
var/newSpecies = chosen_dna.speciesName
H.set_species(newSpecies,1)
src.dna = chosen_dna.dna
src.real_name = chosen_dna.name
src.flavor_text = ""
domutcheck(src, null)
src.UpdateAppearance()
//Transform into a monkey.
/mob/proc/changeling_lesser_form()
@@ -340,15 +317,10 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
return
changeling.chem_charges--
H.remove_changeling_powers_monkey() //we weren't making us of allowduringlesserform before for some reason? -Ryan784
H.visible_message("<span class='warning'>[H] transforms!</span>")
changeling.geneticdamage = 30
H << "<span class='warning'>Our genes cry out!</span>"
var/list/implants = list() //Try to preserve implants.
for(var/obj/item/weapon/implant/W in H)
implants += W
H.monkeyize()
verbs += /mob/proc/changeling_lesser_transform //allow us to actually transform BACK into a human - Ryan784
H = H.monkeyize()
feedback_add_details("changeling_powers","LF")
return 1
@@ -457,25 +429,29 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
// charge the changeling chemical cost for stasis
changeling.chem_charges -= 20
// restore us to health
C.revive()
// remove our fake death flag
C.status_flags &= ~(FAKEDEATH)
// let us move again
C.update_canmove()
// re-add out changeling powers
C.make_changeling()
// sending display messages
C << "<span class='notice'>We have regenerated.</span>"
C << "<span class='notice'><font size='5'>We are ready to rise. Use the <b>Revive</b> verb when you are ready.</font></span>"
C.verbs += /mob/proc/changeling_revive
feedback_add_details("changeling_powers","FD")
return 1
/mob/proc/changeling_revive()
set category = "Changeling"
set name = "Revive"
var/mob/living/carbon/C = src
// restore us to health
C.revive()
// remove our fake death flag
C.status_flags &= ~(FAKEDEATH)
// let us move again
C.update_canmove()
// re-add out changeling powers
C.make_changeling()
// sending display messages
C << "<span class='notice'>We have regenerated.</span>"
C.verbs -= /mob/proc/changeling_revive
//Boosts the range of your next sting attack by 1
/mob/proc/changeling_boost_range()
@@ -581,7 +557,7 @@ var/global/list/possible_changeling_IDs = list("Alpha","Beta","Gamma","Delta","E
// HIVE MIND UPLOAD/DOWNLOAD DNA
var/list/datum/dna/hivemind_bank = list()
var/list/datum/absorbed_dna/hivemind_bank = list()
/mob/proc/changeling_hiveupload()
set category = "Changeling"
@@ -592,9 +568,14 @@ var/list/datum/dna/hivemind_bank = list()
if(!changeling) return
var/list/names = list()
for(var/datum/dna/DNA in changeling.absorbed_dna)
if(!(DNA in hivemind_bank))
names += DNA.real_name
for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
var/valid = 1
for(var/datum/absorbed_dna/DNB in hivemind_bank)
if(DNA.name == DNB.name)
valid = 0
break
if(valid)
names += DNA.name
if(names.len <= 0)
src << "<span class='notice'>The airwaves already have all of our DNA.</span>"
@@ -603,7 +584,7 @@ var/list/datum/dna/hivemind_bank = list()
var/S = input("Select a DNA to channel: ", "Channel DNA", null) as null|anything in names
if(!S) return
var/datum/dna/chosen_dna = changeling.GetDNA(S)
var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
if(!chosen_dna)
return
@@ -622,9 +603,9 @@ var/list/datum/dna/hivemind_bank = list()
if(!changeling) return
var/list/names = list()
for(var/datum/dna/DNA in hivemind_bank)
if(!(DNA in changeling.absorbed_dna))
names[DNA.real_name] = DNA
for(var/datum/absorbed_dna/DNA in hivemind_bank)
if(!(changeling.GetDNA(DNA.name)))
names[DNA.name] = DNA
if(names.len <= 0)
src << "<span class='notice'>There's no new DNA to absorb from the air.</span>"
@@ -637,7 +618,7 @@ var/list/datum/dna/hivemind_bank = list()
return
changeling.chem_charges -= 20
changeling.absorbed_dna += chosen_dna
absorbDNA(chosen_dna)
src << "<span class='notice'>We absorb the DNA of [S] from the air.</span>"
feedback_add_details("changeling_powers","HD")
return 1
@@ -692,7 +673,7 @@ var/list/datum/dna/hivemind_bank = list()
return 1
//Handles the general sting code to reduce on copypasta (seeming as somebody decided to make SO MANY dumb abilities)
/mob/proc/changeling_sting(var/required_chems=0, var/verb_path)
/mob/proc/changeling_sting(var/required_chems=0, var/verb_path, var/stealthy = 0)
var/datum/changeling/changeling = changeling_power(required_chems)
if(!changeling) return
@@ -711,9 +692,20 @@ var/list/datum/dna/hivemind_bank = list()
src.verbs -= verb_path
spawn(10) src.verbs += verb_path
src << "<span class='notice'>We stealthily sting [T].</span>"
if(stealthy == 1)
src << "<span class='notice'>We stealthily sting [T].</span>"
T << "<span class='warning'>You feel a tiny prick.</span>"
else
src.visible_message(pick("<span class='danger'>[src]'s eyes balloon and burst out in a welter of blood, burrowing into [T]!</span>",
"<span class='danger'>[src]'s arm rapidly shifts into a giant scorpion-stinger and stabs into [T]!</span>",
"<span class='danger'>[src]'s throat lengthens and twists before vomitting a chunky red spew all over [T]!</span>",
"<span class='danger'>[src]'s tongue stretches an impossible length and stabs into [T]!</span>",
"<span class='danger'>[src] sneezes a cloud of shrieking spiders at [T]!</span>",
"<span class='danger'>[src] erupts a grotesque tail and impales [T]!</span>",
"<span class='danger'>[src]'s chin skin bulges and tears, launching a bone-dart at [T]!</span>"))
if(!T.mind || !T.mind.changeling) return T //T will be affected by the sting
T << "<span class='warning'>You feel a tiny prick.</span>"
return
@@ -722,7 +714,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Hallucination Sting (15)"
set desc = "Causes terror in the target."
var/mob/living/carbon/T = changeling_sting(15,/mob/proc/changeling_lsdsting)
var/mob/living/carbon/T = changeling_sting(15,/mob/proc/changeling_lsdsting,stealthy = 1)
if(!T) return 0
spawn(rand(300,600))
if(T) T.hallucination += 400
@@ -734,7 +726,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Silence sting (10)"
set desc="Sting target"
var/mob/living/carbon/T = changeling_sting(10,/mob/proc/changeling_silence_sting)
var/mob/living/carbon/T = changeling_sting(10,/mob/proc/changeling_silence_sting,stealthy = 1)
if(!T) return 0
T.silent += 30
feedback_add_details("changeling_powers","SS")
@@ -745,7 +737,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Blind sting (20)"
set desc="Sting target"
var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_blind_sting)
var/mob/living/carbon/T = changeling_sting(20,/mob/proc/changeling_blind_sting,stealthy = 0)
if(!T) return 0
T << "<span class='danger'>Your eyes burn horrificly!</span>"
T.disabilities |= NEARSIGHTED
@@ -760,7 +752,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Deaf sting (5)"
set desc="Sting target:"
var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_deaf_sting)
var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_deaf_sting,stealthy = 0)
if(!T) return 0
T << "<span class='danger'>Your ears pop and begin ringing loudly!</span>"
T.sdisabilities |= DEAF
@@ -773,7 +765,7 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Paralysis sting (30)"
set desc="Sting target"
var/mob/living/carbon/T = changeling_sting(30,/mob/proc/changeling_paralysis_sting)
var/mob/living/carbon/T = changeling_sting(30,/mob/proc/changeling_paralysis_sting,stealthy = 0)
if(!T) return 0
T << "<span class='danger'>Your muscles begin to painfully tighten.</span>"
T.Weaken(20)
@@ -788,29 +780,25 @@ var/list/datum/dna/hivemind_bank = list()
var/datum/changeling/changeling = changeling_power(40)
if(!changeling) return 0
var/list/names = list()
for(var/datum/dna/DNA in changeling.absorbed_dna)
names += "[DNA.real_name]"
for(var/datum/absorbed_dna/DNA in changeling.absorbed_dna)
names += "[DNA.name]"
var/S = input("Select the target DNA: ", "Target DNA", null) as null|anything in names
if(!S) return
var/datum/dna/chosen_dna = changeling.GetDNA(S)
var/datum/absorbed_dna/chosen_dna = changeling.GetDNA(S)
if(!chosen_dna)
return
var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_transformation_sting)
var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_transformation_sting,stealthy = 1)
if(!T) return 0
if((HUSK in T.mutations) || (!ishuman(T) && !issmall(T)))
src << "<span class='warning'>Our sting appears ineffective against its DNA.</span>"
return 0
T.visible_message("<span class='warning'>[T] transforms!</span>")
T.dna = chosen_dna.Clone()
T.real_name = chosen_dna.real_name
T.UpdateAppearance()
domutcheck(T, null)
T.handle_changeling_transform(chosen_dna)
feedback_add_details("changeling_powers","TS")
return 1
@@ -819,10 +807,9 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Unfat sting (5)"
set desc = "Sting target"
var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_unfat_sting)
var/mob/living/carbon/T = changeling_sting(5,/mob/proc/changeling_unfat_sting,stealthy = 1)
if(!T) return 0
T << "<span class='danger'>you feel a small prick as stomach churns violently and you become to feel skinnier.</span>"
T.overeatduration = 0
T.nutrition -= 100
feedback_add_details("changeling_powers","US")
return 1
@@ -832,13 +819,13 @@ var/list/datum/dna/hivemind_bank = list()
set name = "Death Sting (40)"
set desc = "Causes spasms onto death."
var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_DEATHsting)
var/mob/living/carbon/T = changeling_sting(40,/mob/proc/changeling_DEATHsting,stealthy = 0)
if(!T) return 0
T << "<span class='danger'>You feel a small prick and your chest becomes tight.</span>"
T.silent = 10
T.Paralyse(10)
T.make_jittery(1000)
if(T.reagents) T.reagents.add_reagent("lexorin", 40)
if(T.reagents) T.reagents.add_reagent("cyanide", 5)
feedback_add_details("changeling_powers","DTHS")
return 1
@@ -853,13 +840,66 @@ var/list/datum/dna/hivemind_bank = list()
if(!changeling)
return 0
var/mob/living/carbon/human/T = changeling_sting(40, /mob/proc/changeling_extract_dna_sting)
var/mob/living/carbon/human/T = changeling_sting(40, /mob/proc/changeling_extract_dna_sting,stealthy = 1)
if(!T) return 0
T.dna.real_name = T.real_name
changeling.absorbed_dna |= T.dna
if(T.species && !(T.species.name in changeling.absorbed_species))
changeling.absorbed_species += T.species.name
var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages)
absorbDNA(newDNA)
feedback_add_details("changeling_powers","ED")
return 1
/mob/proc/armblades()
set category = "Changeling"
set name = "Form Blades"
set desc="Rupture the flesh and mend the bone of your hand into a deadly blade."
var/mob/living/M = src
if(M.l_hand && M.r_hand)
M << "<span class='danger'>Your hands are full.</span>"
return
var/obj/item/weapon/melee/arm_blade/blade = new(M)
blade.creator = M
M.put_in_hands(blade)
playsound(loc, 'sound/effects/blobattack.ogg', 30, 1)
src.visible_message("<span class='danger'>A grotesque blade forms around [M]\'s arm!</span>",
"<span class='danger'>Our arm twists and mutates, transforming it into a deadly blade.</span>",
"<span class='danger'>You hear organic matter ripping and tearing!</span>")
gibs(src.loc)
/mob/proc/changeling_shield()
set category = "Changeling"
set name = "Form Shield"
set desc="Bend the flesh and bone of your hand into a grotesque shield."
var/mob/living/M = src
if(M.l_hand && M.r_hand)
M << "<span class='danger'>Your hands are full.</span>"
return
var/obj/item/weapon/shield/riot/changeling/shield = new(M)
shield.creator = M
M.put_in_hands(shield)
playsound(loc, 'sound/effects/blobattack.ogg', 30, 1)
src.visible_message("<span class='warning'>The end of [M]\'s hand inflates rapidly, forming a huge shield-like mass!</span>",
"<span class='warning'>We inflate our hand into a robust shield.</span>",
"<span class='warning'>You hear organic matter ripping and tearing!</span>")
gibs(src.loc)
//dna related datum
/datum/absorbed_dna
var/name
var/datum/dna/dna
var/speciesName
var/list/languages
/datum/absorbed_dna/New(var/newName, var/newDNA, var/newSpecies, var/newLanguages)
..()
name = newName
dna = newDNA
speciesName = newSpecies
languages = newLanguages
@@ -26,12 +26,6 @@ var/list/datum/power/changeling/powerinstances = list()
genomecost = 0
verbpath = /mob/proc/changeling_transform
/datum/power/changeling/change_species
name = "Change Species"
desc = "We take on the apperance of a species that we have absorbed."
genomecost = 0
verbpath = /mob/proc/changeling_change_species
/datum/power/changeling/fakedeath
name = "Regenerative Stasis"
desc = "We become weakened to a death-like state, where we will rise again from death."
@@ -64,14 +58,14 @@ var/list/datum/power/changeling/powerinstances = list()
/datum/power/changeling/deaf_sting
name = "Deaf Sting"
desc = "We silently sting a human, completely deafening them for a short time."
desc = "We sting a human, completely deafening them for a short time."
genomecost = 1
allowduringlesserform = 1
verbpath = /mob/proc/changeling_deaf_sting
/datum/power/changeling/blind_sting
name = "Blind Sting"
desc = "We silently sting a human, completely blinding them for a short time."
desc = "We sting a human, completely blinding them for a short time."
genomecost = 2
allowduringlesserform = 1
verbpath = /mob/proc/changeling_blind_sting
@@ -108,7 +102,7 @@ var/list/datum/power/changeling/powerinstances = list()
/datum/power/changeling/paralysis_sting
name = "Paralysis Sting"
desc = "We silently sting a human, paralyzing them for a short time."
desc = "We sting a human, paralyzing them for a short time."
genomecost = 8
verbpath = /mob/proc/changeling_paralysis_sting
@@ -121,7 +115,7 @@ var/list/datum/power/changeling/powerinstances = list()
/datum/power/changeling/DeathSting
name = "Death Sting"
desc = "We silently sting a human, filling them with potent chemicals. Their rapid death is all but assured."
desc = "We sting a human, filling them with potent chemicals. Their rapid death is all but assured."
genomecost = 10
verbpath = /mob/proc/changeling_DEATHsting
@@ -184,7 +178,19 @@ var/list/datum/power/changeling/powerinstances = list()
genomecost = 7
verbpath = /mob/proc/changeling_rapidregen
//weapon and armor like powers
/datum/power/changeling/armblades
name = "Mutate Armblades"
desc = "Permits us to reshape our arms into a deadly blade."
genomecost = 2
verbpath = /mob/proc/armblades
/datum/power/changeling/shield
name = "Mutate Shield"
desc = "Permits us to bloat our hands into a robust shield."
genomecost = 3
verbpath = /mob/proc/changeling_shield
// Modularchangling, totally stolen from the new player panel. YAYY
/datum/changeling/proc/EvolutionMenu()//The new one
+541 -26
View File
@@ -17,59 +17,565 @@
desc = "A forge used in crafting the unholy weapons used by the armies of Nar-Sie"
icon_state = "forge"
/*
Cult pylons can be used as arcane defensive turrets.
First of all they must be empowered with a sacrifice. Any small organic creature that can be picked up is valid
Hit the pylon with the creature - while still alive - to present it as a sacrifice, and then end its life to complete the process.
Once empowered, pylons will fire rapid, weak magical beams at non cult mobs.
As crystals, pylons are immune to energy weaponry. More than immune, they will absorb lasers and become
supercharged for some time, boosting their damage. Pylons can only be harmed by ballistics and physical weapons
Building reinforced windows around them is highly recommended.
Pylons are fairly fragile and will quickly break under direct hits.
A damaged pylon will self repair, if it contains a soul.
Pylons are largely intended as a stalling, early warning, fire support and area denial system. They are unlikely to actually
kill anyone alone but they can keep pesky civilians away for long enough to learn runes and prepare rituals.
If you can get ahold of a laser rifle to supercharge them, they can even give security some pause
Recommended tools for combatting pylons:
Heavy melee (fireaxe, baseball bat, etc)
Sniper rifles
Exosuits
Explosives
Ablative armour
*/
/obj/structure/cult/pylon
name = "Pylon"
desc = "A floating crystal that hums with an unearthly energy"
icon_state = "pylon"
description_antag = "A pylon can be upgraded into a magical defensive turret that shoots anyone opposing the cult\
</br>Upgrading a pylon requires a sacrifice. Bring it a small organic creature, like a monkey or mouse. Use the creature on the pylon, or drag and drop to present it.\
</br>Once the sacrifice is accepted, kill it to complete the process. This will gib its body and make a very visible mess. After this point the pylon is fixed to the floor and cant be moved\
</br>The pylon will fire weak beams that are harmless to the cult. In addition it can be upgraded even more by shooting it with a laser, which will give it a limited number of extra-power shots."
icon_state = "pylonbase"
var/isbroken = 0
light_range = 5
light_color = "#3e0000"
var/obj/item/wepon = null
var/pylonmode = 0
//Pylon has three states:
//0 = Idle, the pylon is an inert crystal
//1 = Awaiting sacrifice. The pylon is actively tracking a creature to be sacrificed to it
//2 = Turret. The pylon has been empowered by a sacrifice and is now a turret permanantly
var/damagetaken = 0
var/empowered = 0 //Number of empowered, higher-damage shots remaining
var/processing = 0
var/mob/living/sacrifice //Holds a reference to a mob that is a pending sacrifice
var/mob/living/sacrificer //A reference to the last mob that attempted to sacrifice something. So we can message them
//Sacrifier is also used in target handling. the pylon will not bite the hand that feeds it. Noncultist colleagues are fair game though
var/next_shot = 0 //Absolute world time when we're allowed to fire again
var/shot_delay = 5 //Minimum delay between shots, in deciseconds
var/mob/living/target
var/notarget //Number of times handle_firing has been called without finding anything to shoot at
//This is used for switching to lower-intensity processing
var/datum/language/cultcommon/lang
//An instance of the cult language datum. Used to scramble speech when speaking to noncultists
var/turf/last_target_loc
var/process_interval = 1
var/ticks
anchored = 0
//Just a subtype that starts off in turret mode. For adminbus and debugging. Maybe a future wizard spell
//Spawn them next to ERPers
/obj/structure/cult/pylon/turret
pylonmode = 2
/obj/structure/cult/pylon/turret/New()
..()
start_process()
//Another subtype which starts with infinite empower shots. For empowered adminbus
/obj/structure/cult/pylon/turret/empowered
empowered = 99999999
/obj/structure/cult/pylon/New()
..()
lang = new /datum/language/cultcommon()
update_icon()
/obj/structure/cult/pylon/examine(var/mob/user)
..()
if (damagetaken)
switch (damagetaken)
if (1 to 8)
user << "It has very faint hairline fractures."
if (8 to 20)
user << "It has several cracks across its surface."
if (20 to 30)
user << "It is chipped and deeply cracked, it may shatter with much more pressure."
if (30 to INFINITY)
user << "It is almost cleaved in two, the pylon looks like it will fall to shards under its own weight."
/obj/structure/cult/pylon/Move()
..()
last_target_loc = null
/obj/structure/cult/pylon/proc/start_process()
process_interval = 1
if (!processing)
processing_objects += src
processing = 1
//If the pylon goes a long time without shooting anything, it will consider slowing down processing
/obj/structure/cult/pylon/proc/reconsider_interval()
var/mindist = INFINITY
for (var/m in player_list)
var/mob/living/L = m
if (L.z != z)
continue
if (L.stat == DEAD)
continue
if (iscult(L))
continue
if (L == sacrificer)
continue
var/d = get_dist(src, L)
if (d < mindist)
mindist = d
mindist = min(150, mindist)
process_interval = Ceiling(mindist*0.1)
process_interval = max(1, process_interval)
//Run each process loop, this function checks if we can stop processing yet.
//Returns 0 if its time to stop. Returns 1 if we should keep going.
/obj/structure/cult/pylon/proc/check_process()
if (isbroken)
return 0
if (pylonmode == 2)
return 1
if (pylonmode == 1)
if (sacrifice)
return 1
if (empowered > 0)
return 1
return 0
/obj/structure/cult/pylon/process()
ticks++
if(!check_process())
processing_objects.Remove(src)
processing = 0
return
switch (pylonmode)
if (1)
handle_sacrifice()
if (2)
if ((ticks % process_interval) == 0)
handle_firing()
if (damagetaken && prob(50))
damagetaken = max(0, damagetaken-1) //An empowered pylon slowly self repairs
if (prob(10))
visible_message("Cracks in the [src] gradually seal as new crystalline matter grows to fill them.")
if (empowered && prob(2))
empowered = max(0, empowered - 1) //Overcharging gradually wears off over time
if (empowered <= 0)
update_icon()
//If user is a cultist, speaks message to them with a prefix
//If user is not cultist, then speaks cult-y gibberish
/obj/structure/cult/pylon/proc/speak_to(var/mob/user, var/message)
if (iscult(user) || (/datum/language/cultcommon in user.languages))
user << "A voice speaks into your mind, [span("cult","\"<I>[message]</I>\"")]"
else
user << "A voice speaks into your mind, [span("cult","\"<I>[lang.scramble(message)]</I>\"")]"
//Todo: Replace the messages here with better ones. Should display a proper message to cultists
//And nonsensical arcane gibberish to non cultists
/obj/structure/cult/pylon/proc/present_sacrifice(var/mob/living/user, var/mob/living/victim)
if (!user || !victim)
return 0
if (isbroken)
user << "The pylon lies silent."
return 0
if (pylonmode != 0)
return 0
if (!(victim.mob_size) || victim.mob_size > 6)
//toolarge sacrifice, display a message and return
speak_to(user, "This creature is too large, we only require a lesser sacrifice. A small morsel of life...")
return
if (victim.stat == DEAD)
speak_to(user, "You are too late, the spark of life is already gone from this one. It is naught but an empty husk...")
return
var/types = victim.find_type()
if ((!(types & TYPE_ORGANIC)) || ((types & TYPE_WIERD)))
//Invalid sacrifice. Display a message and return
speak_to(user, "This soulless automaton cannot satisfy our hunger. We yearn for life essence, it must have a soul.")
return
sacrifice = victim
sacrificer = user
//Sacrifice accepted, display message here
speak_to(user, "Your sacrifice has been deemed worthy, and accepted. End its life now, and liberate its soul, to seal our contract...")
sacrifice << span("danger", "You feel an invisible force grip your soul, as you're drawn inexorably towards the pylon. Every part of you screams to flee from here!")
if (istype(sacrifice.loc,/obj/item/weapon/holder))
var/obj/item/weapon/holder/H = sacrifice.loc
H.release_to_floor()
else
sacrifice.forceMove(get_turf(sacrifice))
pylonmode = 1
update_icon()
start_process()
//Runs every proc while there's a pending sacrifice. This checks whether the sacrifice was killed or escaped
/obj/structure/cult/pylon/proc/handle_sacrifice()
.=0 //Return value has 3 states.
//0 = waiting, the sacrifice is still alive
//1 = Finished, the sacrifice was killed, finalize the process and become a turret
//-1 = Escape: The sacrifice left the vicinity of the pylon, return to inert mode.
if (!sacrifice)
//If the sacrifice was deleted somehow, we cant know exactly what happened. We'll assume it escaped
.=-1
else if(get_dist(src, sacrifice) > 5)
//If the sacrifice gets more than 5 tiles away, it has escaped
.=-1
else if (sacrifice.stat == DEAD)
.=1
switch(.)
if (1)
finalize_sacrifice()
if (-1)
speak_to(sacrificer, "Fool! Your offering has escaped. Bring it back, or find us another...")
sacrifice = null
pylonmode = 0
update_icon()
if (sacrifice)
walk_to(sacrifice,0)
else
walk_towards(sacrifice,src, 10)
/obj/structure/cult/pylon/proc/finalize_sacrifice()
sacrifice.visible_message(span("danger","\The [sacrifice]'s physical form unwinds as its soul is extracted from the remains, and drawn into the pylon!"))
if (istype(sacrifice.loc,/obj/item/weapon/holder))
var/obj/item/weapon/holder/H = sacrifice.loc
H.release_to_floor()
else
sacrifice.forceMove(get_turf(sacrifice)) //Make sure its on the floor before we gib it
pylonmode = 2
sacrifice.gib()
update_icon()
//Called every process in turret mode, and also by chaining spawns
/obj/structure/cult/pylon/proc/handle_firing()
if ((world.time < next_shot) || isbroken)
return
//Not ready to fire yet
var/list/stuffcache
//Stuffcache holds a list of things found by a dview call, so we only need to do it once per proc.
if (target && (target.stat != DEAD))
if (target.loc == last_target_loc)
//To minimise expensive DVIEW calls, if the target hasnt moved since the last shot we'll assume they're still in sight
//This could cause problems in some edge cases (like lowering firelocks or shutters without moving)
//But it has a major performance gain that makes it worth it
fire_at(target)
return
else
stuffcache = mobs_in_view(10, src)
//for (var/turf/T in stuffcache)
//new /obj/effect/testtrans(T)
if ((target in stuffcache) && isInSight(src, target))
for (var/mob/m in stuffcache)
fire_at(target)
return
else
target = null
//We may have already populated stuffcache this run, dont repeat work
if (!stuffcache)
stuffcache = mobs_in_view(10, src)
target = null //Either we lost a target or lack one
//for (var/turf/T in stuffcache)
//new /obj/effect/testtrans(T)
for (var/mob/living/L in stuffcache)
if (L == sacrificer)
continue
//A pylon won't shoot the person who created it, regardless of cult status.
//This is mainly for xenoarch antags
if (L.stat == DEAD)
continue
//No point shooting at corpses
if (iscult(L))
continue
//Pylon wont shoot at cultists or constructs
if (!isInSight(src, L))
continue
target = L
break
if (target)
fire_at(target)
else
notarget++
if (notarget > 60 || process_interval > 1)
notarget = 0
reconsider_interval()
/obj/structure/cult/pylon/proc/fire_at(var/atom/target)
//Only store loc if target is on a turf. Otherwise this bugs out with people in exosuits
if (istype(target.loc, /turf))
last_target_loc = target.loc
process_interval = 1 //Instantly wake up if we found a target
var/obj/item/projectile/beam/cult/A
if (empowered > 0)
A = new /obj/item/projectile/beam/cult/heavy(loc)
empowered = max(0, empowered-1)
playsound(loc, 'sound/weapons/laserdeep.ogg', 100, 1)
if (empowered <= 0)
update_icon()
else
A = new /obj/item/projectile/beam/cult(loc)
playsound(loc, 'sound/weapons/laserdeep.ogg', 65, 1)
A.ignore = sacrificer
A.launch(target)
next_shot = world.time + shot_delay
spawn(shot_delay+1)
handle_firing()
/obj/structure/cult/pylon/attack_hand(mob/M as mob)
attackpylon(M, 5)
if (M.a_intent == "help")
M << "The pylon feels warm to the touch...."
else
attackpylon(M, 4, M)
/obj/structure/cult/pylon/attack_generic(var/mob/user, var/damage)
attackpylon(user, damage)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
//Artificiers maintain pylons
if(istype(user, /mob/living/simple_animal/construct))
var/mob/living/simple_animal/construct/C = user
if (C.can_repair)
repair(user)
return
attackpylon(user, damage, user)
/obj/structure/cult/pylon/attackby(obj/item/W as obj, mob/user as mob)
attackpylon(user, W.force)
if (istype(W, /obj/item/weapon/holder))
var/obj/item/weapon/holder/H = W
if (H.contained)
present_sacrifice(user, H.contained)
return
/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if(!isbroken)
if(prob(1+ damage * 5))
user.visible_message(
"<span class='danger'>[user] smashed the pylon!</span>",
"<span class='warning'>You hit the pylon, and its crystal breaks apart!</span>",
"You hear a tinkle of crystal shards"
attackpylon(user, W.force, W)
//Mousedrop so that constructs can drag mice out of maintenance to make turrets
/obj/structure/cult/pylon/MouseDrop_T(var/atom/movable/C, mob/user)
if (istype(C, /mob/living))
present_sacrifice(user, C)
return
return ..()
/obj/structure/cult/pylon/bullet_act(var/obj/item/projectile/Proj)
attackpylon(user, Proj.damage, Proj)
//Explosions will usually cause instant shattering, or heavy damage
//Class 3 or lower blast is sometimes survivable. 2 or higher will always shatter
/obj/structure/cult/pylon/ex_act(var/severity)
if (severity > 0)
attackpylon(null, (rand(200,400)/severity), null)
/obj/structure/cult/pylon/proc/attackpylon(mob/user as mob, var/damage, var/source)
var/ranged = 0
if (user)
user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
if (istype(source, /obj/item))
var/obj/item/I = source
if (I.damtype != BRUTE)
user << "You swing at the pylon to no effect."
return
if (istype(source, /obj/item/projectile))
if (istype(source, /obj/item/projectile/beam/cult))
return //No feedback loops
var/obj/item/projectile/proj = source
if (proj.damage_type == BURN)
if (empowered <= 0)
visible_message(
"<span class='cult'>The beam refracts inside the pylon, splitting into an indistinct violet glow. The crystal takes on a new, more ominous aura!</span>"
)
empowered += damage*0.3
//When shot with a laser, the pylon absorbs the beam, becoming empowered for a while, glowing brighter
// and firing more powerful blasts which have some armor penetration
// Using lasers to empower a defensive pylon yields more total damage than directly shooting your enemies
start_process()
update_icon()
return
else if (proj.damage_type != BRUTE)
return
ranged = 1
if (!damage)
return
damagetaken += damage
if(!isbroken)
if (user)
user.do_attack_animation(src)
playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1)
isbroken = 1
density = 0
icon_state = "pylon-broken"
set_light(0)
if(prob(damagetaken*0.5))
shatter()
else
user << "You hit the pylon!"
if (user && !ranged)
user << "You hit the pylon!"
playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1)
else
if(prob(damage * 2))
user << "You pulverize what was left of the pylon!"
if(prob(damagetaken))
if (user)
user << "You pulverize what was left of the pylon!"
qdel(src)
else
else if (user && !ranged)
user << "You hit the pylon!"
playsound(get_turf(src), 'sound/effects/Glasshit.ogg', 75, 1)
start_process()
/obj/structure/cult/pylon/proc/shatter()
visible_message(
"<span class='danger'>The pylon shatters into shards of crystal!</span>",
"You hear a tinkle of crystal shards"
)
playsound(get_turf(src), 'sound/effects/Glassbr3.ogg', 75, 1)
isbroken = 1
if (pylonmode == 2)
//If the pylon had a soul in it then it plays a creepy evil sound as the soul is released
var/possibles = list('sound/hallucinations/far_noise.ogg',
'sound/hallucinations/growl3.ogg',
'sound/hallucinations/veryfar_noise.ogg',
'sound/hallucinations/wail.ogg',
'sound/voice/hiss5.ogg')
playsound(get_turf(src), pick(possibles), 50, 1)
pylonmode = 0 //A broken pylon loses its soul. Even if repaired it will need a new sacrifice to re-empower it
//Make sure we stop it from firing
target = null
sacrificer = null
notarget = 0
last_target_loc = null
process_interval = 1
//It will stop processing after the next check, start of the next process
empowered = 0
density = 0
update_icon()
/obj/structure/cult/pylon/proc/repair(mob/user as mob)
if(isbroken)
user << "You repair the pylon."
if(user)
user << span("notice","You weave forgotten magic, summoning the shards of the crystal and knitting them anew, until it hovers flawless once more.")
isbroken = 0
density = 1
icon_state = "pylon"
set_light(5)
else if (damagetaken > 0)
user << span("notice","You meld the crystal lattice back into integrity, sealing over the cracks until they never were.")
else
user << span("notice","The crystal lights up at your touch.")
process_interval = 1 //Wake up the crystal
notarget = 0
damagetaken = 0
update_icon()
/obj/structure/cult/pylon/update_icon()
overlays.Cut()
if (pylonmode == 2)
anchored = 1
if (empowered > 0)
overlays += "crystal_overcharge"
set_light(7, 3, l_color = "#a160bf")
else
set_light(6, 3, l_color = "#3e0000")
overlays += "crystal_turret"
else if (!isbroken)
set_light(5, 2, l_color = "#3e0000")
overlays += "crystal_idle"
if (pylonmode == 1)
anchored = 1
else
anchored = 0
else
anchored = 0
set_light(0)
//============================================
/obj/structure/cult/tome
name = "Desk"
desc = "A desk covered in arcane manuscripts and tomes in unknown languages. Looking at the text makes your skin crawl"
@@ -144,7 +650,7 @@
if(M.has_brain_worms())
return //Borer stuff - RR
if(iscultist(M)) return
if(iscult(M)) return
if(!ishuman(M) && !isrobot(M)) return
M.transforming = 1
@@ -174,3 +680,12 @@
new_mob.key = M.key
new_mob << "<B>Your form morphs into that of a corgi.</B>" //Because we don't have cluwnes
/obj/effect/testtrans
icon = 'icons/obj/cult.dmi'
icon_state = "tt"
/obj/effect/testtrans/New()
..()
spawn(10)
qdel(src)
+2 -2
View File
@@ -283,7 +283,7 @@ var/global/list/additional_antag_types = list()
/datum/game_mode/proc/declare_completion()
var/is_antag_mode = (antag_templates && antag_templates.len)
var/discord_text = "@subscribers A round of **[name]** has ended! \[Game ID: [game_id]\]\n\n"
var/discord_text = "A round of **[name]** has ended! \[Game ID: [game_id]\]\n\n"
check_victory()
if(is_antag_mode)
sleep(10)
@@ -298,7 +298,7 @@ var/global/list/additional_antag_types = list()
sleep(10)
print_ownerless_uplinks()
discord_bot.send_to_announce(discord_text)
discord_bot.send_to_announce(discord_text, 1)
discord_text = ""
var/clients = 0
@@ -111,6 +111,13 @@
return
log_ability_use(user, "elite encryption hack (SUCCESS - title: [title])")
command_announcement.Announce(text, title)
for (var/obj/machinery/computer/communications/C in machines)
if(! (C.stat & (BROKEN|NOPOWER) ) )
var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( C.loc )
P.name = "[command_name()] Update"
P.info = replacetext(text, "\n", "<br/>")
P.update_space(P.info)
P.update_icon()
/datum/game_mode/malfunction/verb/elite_encryption_hack()
set category = "Software"
@@ -850,6 +850,10 @@
if (alert(src, choice_text, "Choices", "Yes", "No") == "No")
src << "<span class='notice'>[denial_response]</span>"
return
vampire_thrall.remove_antagonist(T.mind, 0, 0)
qdel(draining_vamp)
draining_vamp = null
else
src << "<span class='warning'>You feel corruption running in [T.name]'s blood. Much like yourself, \he[T] is already a spawn of the Veil, and cannot be Embraced.</span>"
return
+2 -3
View File
@@ -29,13 +29,12 @@ var/datum/announcement/minor/captain_announcement = new(do_newscast = 1)
if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel_cap(H), slot_back)
if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back)
if(5) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/duffel/cap(H), slot_back)
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/captain(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/captain(H), slot_w_uniform)
if(H.age>49)
// Since we can have something other than the default uniform at this
// Since we can have something other than the default uniform at this
// point, check if we can actually attach the medal
var/obj/item/clothing/uniform = H.w_uniform
var/obj/item/clothing/accessory/medal/gold/captain/medal = new()
if(uniform && uniform.can_attach_accessory(medal))
uniform.attach_accessory(null, medal)
else
+8 -3
View File
@@ -80,7 +80,12 @@
H.equip_to_slot_or_del(new /obj/item/device/radio/headset/headset_service(H), slot_l_ear)
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/hydroponics(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/black(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/botanic_leather(H), slot_gloves)
if(istajara(H))
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/botanic_leather/tajara(H), slot_gloves)
if(isunathi(H))
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/botanic_leather/unathi(H), slot_gloves)
else
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/botanic_leather(H), slot_gloves)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/apron(H), slot_wear_suit)
H.equip_to_slot_or_del(new /obj/item/device/analyzer/plant_analyzer(H), slot_s_store)
H.equip_to_slot_or_del(new /obj/item/device/pda/botanist(H), slot_belt)
@@ -285,6 +290,8 @@
if(3) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel_norm(H), slot_back)
if(4) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/satchel(H), slot_back)
if(5) H.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/duffel(H), slot_back)
H.implant_loyalty(H)
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/internalaffairs(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/suit/storage/toggle/internalaffairs(H), slot_wear_suit)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/brown(H), slot_shoes)
@@ -292,7 +299,5 @@
H.equip_to_slot_or_del(new /obj/item/device/pda/lawyer(H), slot_belt)
H.equip_to_slot_or_del(new /obj/item/weapon/storage/briefcase(H), slot_l_hand)
H.implant_loyalty(H)
return 1
+6 -8
View File
@@ -33,15 +33,13 @@
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/head_of_security(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/jackboots(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/device/pda/heads/hos(H), slot_belt)
if(H.species.name == "Tajara")
if(istajara(H))
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves)
if(H.species.name == "Unathi")
if(isunathi(H))
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves)
else
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves)
// H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas(H), slot_wear_mask) //Grab one from the armory you donk
H.equip_to_slot_or_del(new /obj/item/clothing/glasses/sunglasses/sechud(H), slot_glasses)
H.equip_to_slot_or_del(new /obj/item/weapon/gun/energy/gun(H), slot_s_store)
if(H.backbag == 1)
H.equip_to_slot_or_del(new /obj/item/weapon/handcuffs(H), slot_l_store)
else
@@ -77,9 +75,9 @@
H.equip_to_slot_or_del(new /obj/item/clothing/under/rank/warden(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/jackboots(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/device/pda/warden(H), slot_belt)
if(H.species.name == "Tajara")
if(istajara(H))
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves)
if(H.species.name == "Unathi")
if(isunathi(H))
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves)
else
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves)
@@ -120,9 +118,9 @@
H.equip_to_slot_or_del(new /obj/item/clothing/under/det(H), slot_w_uniform)
H.equip_to_slot_or_del(new /obj/item/clothing/shoes/laceup(H), slot_shoes)
H.equip_to_slot_or_del(new /obj/item/device/pda/detective(H), slot_belt)
if(H.species.name == "Tajara")
if(istajara(H))
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/tajara(H), slot_gloves)
if(H.species.name == "Unathi")
if(isunathi(H))
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black/unathi(H), slot_gloves)
else
H.equip_to_slot_or_del(new /obj/item/clothing/gloves/black(H), slot_gloves)
+47
View File
@@ -0,0 +1,47 @@
/*
- Abstract machinery -
Pretty much designed for whenever you want something that is not a machine to interact with the power grid.
Better to use one of these to be notified of power changes than poll with process().
*/
/obj/machinery/abstract
name = "impossible device"
desc = "No matter how hard you look at it, you have no idea what it is. (please inform coders if you see this)"
simulated = FALSE
anchored = 1
var/should_process = FALSE
mouse_opacity = 0
/obj/machinery/abstract/attack_ai(mob/user as mob)
return
/obj/machinery/abstract/attack_hand(mob/user as mob)
return
/obj/machinery/abstract/emp_act(severity)
return // No EMPing these.
/obj/machinery/abstract/ex_act(severity)
return
/obj/machinery/abstract/tesla_act()
return
/obj/machinery/abstract/singularity_act()
return
/obj/machinery/abstract/singularity_pull()
return
/obj/machinery/abstract/singuloCanEat()
return FALSE
/obj/machinery/abstract/operable(additional_flags = 0)
return TRUE
/obj/machinery/abstract/get_process_type()
. = ..()
if (!should_process)
. &= ~M_PROCESSES
@@ -0,0 +1,25 @@
/obj/machinery/abstract/intercom_listener
name = "intercom power interface"
desc = "You shouldn't see this."
power_channel = EQUIP
var/obj/item/device/radio/intercom/master
/obj/machinery/abstract/intercom_listener/New(atom/loc, obj/item/device/radio/intercom/owner)
if (QDELETED(owner))
warning("intercom_listener created with QDELETED intercom!")
qdel(src)
else
master = owner
/obj/machinery/abstract/intercom_listener/Destroy()
master = null
return ..()
/obj/machinery/abstract/intercom_listener/power_change()
if (master)
var/state = powered(power_channel)
master.power_change(state)
else
warning("intercom_listener got power_change but has no master!")
qdel(src)
+1 -1
View File
@@ -40,4 +40,4 @@
/obj/machinery/computer/rcon/update_icon()
..()
if(is_operable())
overlays += image('icons/obj/computer.dmi', "ai-fixer-empty", overlay_layer)
holographic_overlay(src, src.icon, "ai-fixer-empty")
+2
View File
@@ -215,6 +215,7 @@
network = list(NETWORK_THUNDER)
density = 0
circuit = null
is_holographic = FALSE
/obj/machinery/computer/security/telescreen/entertainment
name = "entertainment monitor"
@@ -260,6 +261,7 @@
icon_screen = "syndicam"
network = list(NETWORK_MERCENARY)
circuit = null
is_holographic = FALSE // I mean, it is, but the holo effect looks terrible with the current merc shuttle floor.
/obj/machinery/computer/security/nuclear/New()
..()
+4 -1
View File
@@ -14,6 +14,7 @@
var/light_range_on = 2
var/light_power_on = 1
var/overlay_layer
var/is_holographic = TRUE
/obj/machinery/computer/New()
overlay_layer = layer
@@ -64,8 +65,10 @@
if(stat & BROKEN)
overlays += image(icon,"[icon_state]_broken", overlay_layer)
else if (is_holographic)
holographic_overlay(src, src.icon, icon_screen)
else
overlays += image(icon,icon_screen, overlay_layer)
overlays += image(icon, icon_screen, overlay_layer)
/obj/machinery/computer/power_change()
..()
@@ -47,6 +47,7 @@
light_color = LIGHT_COLOR_BLUE
icon_screen = "pass"
is_holographic = FALSE
density = 0
var/obj/item/weapon/card/id/giver
+1 -1
View File
@@ -16,7 +16,6 @@
var/datum/data/record/active2 = null
var/a_id = null
var/temp = null
//var/printing = null
/obj/machinery/computer/med_data/AltClick(var/mob/user)
eject_id()
@@ -562,3 +561,4 @@
icon_state = "medlaptop0"
icon_screen = "medlaptop"
is_holographic = FALSE
+6
View File
@@ -33,6 +33,12 @@
..()
spark_system = bind_spark(src, 5)
/obj/machinery/computer/message_monitor/Destroy()
QDEL_NULL(spark_system)
linkedServer = null
customrecepient = null
return ..()
/obj/machinery/computer/message_monitor/attackby(obj/item/weapon/O as obj, mob/living/user as mob)
if(stat & (NOPOWER|BROKEN))
..()
+1
View File
@@ -2,6 +2,7 @@
name = "Shuttle"
desc = "For shuttle control."
is_holographic = FALSE
icon_screen = "shuttle"
light_color = "#00ffff"
var/auth_need = 3.0
+1 -1
View File
@@ -16,7 +16,7 @@
var/datum/data/record/active1 = null
var/a_id = null
var/temp = null
//var/printing = null
is_holographic = FALSE
var/can_change_id = 0
var/list/Perp
var/tempname = null
+1 -1
View File
@@ -261,6 +261,6 @@ for reference:
else if (src.emagged == 1)
src.emagged = 2
user << "You short out the anchoring mechanism on \the [src]."
spark(src, 2, 1)
spark(src, 2, alldirs)
visible_message("<span class='warning'>BZZzZZzZZzZT</span>")
return 1
+2 -3
View File
@@ -30,8 +30,7 @@
var/obj/item/stack/material/steel/repairing
var/block_air_zones = 1 //If set, air zones cannot merge across the door even when it is opened.
var/open_duration = 150//How long it stays open
var/datum/scheduled_task/close_task
var/datum/scheduled_task/hatch_task
var/hashatch = 0//If 1, this door has hatches, and certain small creatures can move through them without opening the door
var/hatchstate = 0//0: closed, 1: open
@@ -229,7 +228,7 @@
switch (Proj.damage_type)
if(BRUTE)
new /obj/item/stack/material/steel(src.loc, 2)
getFromPool(/obj/item/stack/rods, list(src.loc, 3))
getFromPool(/obj/item/stack/rods, src.loc, 3)
if(BURN)
new /obj/effect/decal/cleanable/ash(src.loc) // Turn it to ashes!
qdel(src)
+4 -4
View File
@@ -33,10 +33,10 @@
// If the cell is almost empty rarely "flicker" the light. Aesthetic only.
if((cell.percent() < 10) && prob(5))
set_light(brightness_on/2, brightness_on/4)
set_light(brightness_on/2, 0.5)
spawn(20)
if(on)
set_light(brightness_on, brightness_on/2)
set_light(brightness_on, 1)
cell.use(use*CELLRATE)
@@ -49,7 +49,7 @@
return 0
on = 1
set_light(brightness_on, brightness_on / 2)
set_light(brightness_on, 1)
update_icon()
if(loud)
visible_message("\The [src] turns on.")
@@ -57,7 +57,7 @@
/obj/machinery/floodlight/proc/turn_off(var/loud = 0)
on = 0
set_light(0, 0)
set_light(0)
update_icon()
if(loud)
visible_message("\The [src] shuts down.")
+1 -1
View File
@@ -126,7 +126,7 @@
flick("migniter-spark", src)
spark(src, 2, 1)
spark(src, 2, alldirs)
src.last_spark = world.time
use_power(1000)
var/turf/location = src.loc
@@ -389,14 +389,15 @@
CI.result_type = 4//Recipe type, a specific recipe will transform the ingredients into a new food
var/list/results = recipe.make_food(C)
var/atom/temp = new /atom //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes
var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes
for (var/atom/movable/AM in results)
AM.loc = temp
//making multiple copies of a recipe from one container. For example, tons of fries
while (select_recipe(available_recipes,C) == recipe)
var/list/TR = recipe.make_food(C)
var/list/TR = list()
TR.Add(recipe.make_food(C))
for (var/atom/movable/AM in TR) //Move results to buffer
AM.loc = temp
results.Add(TR)
@@ -636,7 +637,7 @@
//var/obj/object
var/max_cookwork
var/cookwork
var/overcook_mult = 2
var/overcook_mult = 3
var/result_type = 0
var/obj/item/weapon/reagent_containers/cooking_container/container = null
var/combine_target = null
@@ -16,7 +16,7 @@
if (.)//no need to duplicate adjacency check
if (!stat)
if (temperature < min_temp)
user << span("warning", "It is heating up.")
user << span("warning", "The [src] is still heating up and is too cold to cook anything yet.")
else
user << span("notice", "It is running at [round(get_efficiency(), 0.1)]% efficiency!")
user << "Temperature: [round(temperature - T0C, 0.1)]C / [round(optimal_temp - T0C, 0.1)]C"
@@ -121,9 +121,10 @@
//Cookers do differently, they use containers
/obj/machinery/appliance/cooker/has_space(var/obj/item/I)
if (istype(I, /obj/item/weapon/reagent_containers/cooking_container) && cooking_objs.len < max_contents)
if (istype(I, /obj/item/weapon/reagent_containers/cooking_container))
//Containers can go into an empty slot
return 1
if (cooking_objs.len < max_contents)
return 1
else
@@ -137,6 +138,6 @@
/obj/machinery/appliance/cooker/add_content(var/obj/item/I, var/mob/user)
var/datum/cooking_item/CI = ..()
if (CI.combine_target)
if (CI && CI.combine_target)
user << "The [I] will be used to make a [selected_option]. Output selection is returned to default for future items."
selected_option = null
@@ -8,6 +8,10 @@
var/max_space = 20//Maximum sum of w-classes of foods in this container at once
var/max_reagents = 80//Maximum units of reagents
var/list/insertable = list(/obj/item/weapon/reagent_containers/food/snacks,
/obj/item/weapon/holder,
/obj/item/weapon/paper)
/obj/item/weapon/reagent_containers/cooking_container/New()
..()
create_reagents(max_reagents)
@@ -26,15 +30,17 @@
/obj/item/weapon/reagent_containers/cooking_container/attackby(var/obj/item/I as obj, var/mob/user as mob)
if (istype(I, /obj/item/weapon/reagent_containers/food/snacks))
if (!can_fit(I))
user << span("warning","There's no more space in the [src] for that!")
return 0
for (var/possible_type in insertable)
if (istype(I, possible_type))
if (!can_fit(I))
user << span("warning","There's no more space in the [src] for that!")
return 0
if(!user.unEquip(I))
if(!user.unEquip(I))
return
I.forceMove(src)
user << span("notice", "You put the [I] into the [src]")
return
I.forceMove(src)
user << span("notice", "You put the [I] into the [src]")
/obj/item/weapon/reagent_containers/cooking_container/verb/empty()
set src in view()
@@ -67,7 +67,7 @@
loss = (active_power_usage / resistance)*0.5
else
open = 1
loss = (active_power_usage / resistance)*10
loss = (active_power_usage / resistance)*6
//When the oven door is opened, heat is lost MUCH faster
playsound(src, 'sound/machines/hatch_open.ogg', 20, 1)
+18 -4
View File
@@ -16,7 +16,7 @@
var/global/list/datum/recipe/available_recipes // List of the recipes you can use
var/global/list/acceptable_items // List of the items you can put in
var/global/list/acceptable_reagents // List of the reagents you can put in
var/global/max_n_of_items = 0
var/global/max_n_of_items = 20
var/appliancetype = MICROWAVE
// see code/modules/food/recipes_microwave.dm for recipes
@@ -44,8 +44,6 @@
acceptable_items |= item
for (var/reagent in recipe.reagents)
acceptable_reagents |= reagent
if (recipe.items)
max_n_of_items = max(max_n_of_items,recipe.items.len)
// This will do until I can think of a fun recipe to use dionaea in -
// will also allow anything using the holder item to be microwaved into
// impure carbon. ~Z
@@ -295,13 +293,28 @@
var/result = recipe.result
var/valid = 1
var/list/cooked_items = list()
var/obj/temp = new /obj(src) //To prevent infinite loops, all results will be moved into a temporary location so they're not considered as inputs for other recipes
while(valid)
cooked_items += recipe.make_food(src)
var/list/things = list()
things.Add(recipe.make_food(src))
cooked_items += things
//Move cooked things to the buffer so they're not considered as ingredients
for (var/atom/movable/AM in things)
AM.loc = temp
valid = 0
recipe = select_recipe(available_recipes,src)
if (recipe && recipe.result == result)
sleep(2)
valid = 1
for (var/r in cooked_items)
var/atom/movable/R = r
R.loc = src //Move everything from the buffer back to the container
qdel(temp)//Delete buffer object
temp = null
//Any leftover reagents are divided amongst the foods
var/total = reagents.total_volume
for (var/obj/item/weapon/reagent_containers/food/snacks/S in cooked_items)
@@ -312,6 +325,7 @@
dispose(0) //clear out anything left
stop()
return
/obj/machinery/microwave/proc/wzhzhzh(var/seconds as num) // Whoever named this proc is fucking literally Satan. ~ Z
+5
View File
@@ -179,6 +179,11 @@
underlays += image('icons/obj/stationobjs.dmi', icon_state = "tele-wires")
spark_system = bind_spark(src, 5, alldirs)
/obj/machinery/teleport/hub/Destroy()
QDEL_NULL(spark_system)
com = null
return ..()
/obj/machinery/teleport/hub/Bumped(M as mob|obj)
spawn()
if (src.icon_state == "tele1")
+13 -11
View File
@@ -237,7 +237,7 @@
anchored = !anchored
return
else
else if (!is_borg_item(W))
for(var/datum/data/vending_product/R in product_records)
if(istype(W, R.product_path))
@@ -245,6 +245,8 @@
qdel(W)
return
..()
else
..()
/**
* Receive payment with cashmoney.
@@ -722,7 +724,7 @@
vend_power_usage = 85000 //85 kJ to heat a 250 mL cup of coffee
products = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 25,/obj/item/weapon/reagent_containers/food/drinks/tea = 25,/obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 25)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/ice = 10)
prices = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 30, /obj/item/weapon/reagent_containers/food/drinks/tea = 30, /obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 32)
prices = list(/obj/item/weapon/reagent_containers/food/drinks/coffee = 20, /obj/item/weapon/reagent_containers/food/drinks/tea = 20, /obj/item/weapon/reagent_containers/food/drinks/h_chocolate = 22)
@@ -738,10 +740,10 @@
/obj/item/weapon/reagent_containers/food/snacks/cheesiehonkers = 6, /obj/item/weapon/reagent_containers/food/snacks/tastybread = 6, /obj/item/weapon/reagent_containers/food/snacks/skrellsnacks = 3,
/obj/item/weapon/reagent_containers/food/snacks/meatsnack = 2, /obj/item/weapon/reagent_containers/food/snacks/maps = 2, /obj/item/weapon/reagent_containers/food/snacks/nathisnack = 2)
contraband = list(/obj/item/weapon/reagent_containers/food/snacks/syndicake = 6)
prices = list(/obj/item/weapon/reagent_containers/food/snacks/candy = 25,/obj/item/weapon/reagent_containers/food/drinks/dry_ramen = 30,/obj/item/weapon/reagent_containers/food/snacks/chips = 27,
/obj/item/weapon/reagent_containers/food/snacks/sosjerky = 30,/obj/item/weapon/reagent_containers/food/snacks/no_raisin = 22,/obj/item/weapon/reagent_containers/food/snacks/spacetwinkie = 25,
/obj/item/weapon/reagent_containers/food/snacks/cheesiehonkers = 25, /obj/item/weapon/reagent_containers/food/snacks/tastybread = 28, /obj/item/weapon/reagent_containers/food/snacks/skrellsnacks = 50,
/obj/item/weapon/reagent_containers/food/snacks/meatsnack = 32, /obj/item/weapon/reagent_containers/food/snacks/maps = 33, /obj/item/weapon/reagent_containers/food/snacks/nathisnack = 34)
prices = list(/obj/item/weapon/reagent_containers/food/snacks/candy = 15,/obj/item/weapon/reagent_containers/food/drinks/dry_ramen = 20,/obj/item/weapon/reagent_containers/food/snacks/chips = 17,
/obj/item/weapon/reagent_containers/food/snacks/sosjerky = 20,/obj/item/weapon/reagent_containers/food/snacks/no_raisin = 12,/obj/item/weapon/reagent_containers/food/snacks/spacetwinkie = 15,
/obj/item/weapon/reagent_containers/food/snacks/cheesiehonkers = 15, /obj/item/weapon/reagent_containers/food/snacks/tastybread = 18, /obj/item/weapon/reagent_containers/food/snacks/skrellsnacks = 40,
/obj/item/weapon/reagent_containers/food/snacks/meatsnack = 22, /obj/item/weapon/reagent_containers/food/snacks/maps = 23, /obj/item/weapon/reagent_containers/food/snacks/nathisnack = 24)
/obj/machinery/vending/cola
@@ -755,10 +757,10 @@
/obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle = 10,/obj/item/weapon/reagent_containers/food/drinks/cans/space_up = 10,
/obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea = 10, /obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice = 10)
contraband = list(/obj/item/weapon/reagent_containers/food/drinks/cans/thirteenloko = 5, /obj/item/weapon/reagent_containers/food/snacks/liquidfood = 6)
prices = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola = 25,/obj/item/weapon/reagent_containers/food/drinks/cans/space_mountain_wind = 21,
/obj/item/weapon/reagent_containers/food/drinks/cans/dr_gibb = 26,/obj/item/weapon/reagent_containers/food/drinks/cans/starkist = 25,
/obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle = 22,/obj/item/weapon/reagent_containers/food/drinks/cans/space_up = 25,
/obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea = 23,/obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice = 26)
prices = list(/obj/item/weapon/reagent_containers/food/drinks/cans/cola = 15,/obj/item/weapon/reagent_containers/food/drinks/cans/space_mountain_wind = 11,
/obj/item/weapon/reagent_containers/food/drinks/cans/dr_gibb = 16,/obj/item/weapon/reagent_containers/food/drinks/cans/starkist = 15,
/obj/item/weapon/reagent_containers/food/drinks/cans/waterbottle = 12,/obj/item/weapon/reagent_containers/food/drinks/cans/space_up = 15,
/obj/item/weapon/reagent_containers/food/drinks/cans/iced_tea = 13,/obj/item/weapon/reagent_containers/food/drinks/cans/grape_juice = 16)
idle_power_usage = 211 //refrigerator - believe it or not, this is actually the average power consumption of a refrigerated vending machine according to NRCan.
//This one's from bay12
@@ -783,7 +785,7 @@
products = list(/obj/item/weapon/storage/fancy/cigarettes = 10,/obj/item/weapon/storage/box/matches = 10,/obj/item/weapon/flame/lighter/random = 4)
contraband = list(/obj/item/weapon/flame/lighter/zippo = 4)
premium = list(/obj/item/weapon/storage/fancy/cigar = 5,/obj/item/weapon/storage/fancy/cigarettes/killthroat = 5 )
prices = list(/obj/item/weapon/storage/fancy/cigarettes = 520,/obj/item/weapon/storage/box/matches = 22,/obj/item/weapon/flame/lighter/random = 22)
prices = list(/obj/item/weapon/storage/fancy/cigarettes = 510,/obj/item/weapon/storage/box/matches = 12,/obj/item/weapon/flame/lighter/random = 12)
/obj/machinery/vending/medical
@@ -12,6 +12,10 @@
if (in_mecha())
exo_beacons.Add(src)//For the sake of exosuits which spawn with a preinstalled tracking beacon
/obj/item/mecha_parts/mecha_tracking/Destroy()
exo_beacons.Remove(src)
return ..()
/obj/item/mecha_parts/mecha_tracking/proc/get_mecha_info()
if(!in_mecha())
return 0
+1 -1
View File
@@ -250,7 +250,7 @@
if(passed_smoke)
smoke = passed_smoke
else
smoke = getFromPool(/obj/effect/effect/smoke/chem, list(location, smoke_duration + rand(smoke_duration*-0.25, smoke_duration*0.25), T, I))
smoke = getFromPool(/obj/effect/effect/smoke/chem, location, smoke_duration + rand(smoke_duration*-0.25, smoke_duration*0.25), T, I)
if(chemholder.reagents.reagent_list.len)
chemholder.reagents.trans_to_obj(smoke, chemholder.reagents.total_volume / dist, copy = 1) //copy reagents to the smoke so mob/breathe() can handle inhaling the reagents
+2 -2
View File
@@ -108,7 +108,7 @@
F.amount += amount
return
F = getFromPool(/obj/effect/effect/foam, list(location, metal))
F = getFromPool(/obj/effect/effect/foam, location, metal)
F.amount = amount
if(!metal) // don't carry other chemicals if a metal foam
@@ -180,4 +180,4 @@
/obj/structure/foamedmetal/CanPass(atom/movable/mover, turf/target, height=1.5, air_group = 0)
if(air_group)
return 0
return !density
return !density
+1 -1
View File
@@ -101,7 +101,7 @@
O = loc
for(var/i=0, i<num, i++)
var/spiderling = getFromPool(/obj/effect/spider/spiderling, list(src.loc, src))
var/spiderling = getFromPool(/obj/effect/spider/spiderling, src.loc, src)
if(O)
O.implants += spiderling
qdel(src)
+1 -1
View File
@@ -558,7 +558,7 @@ var/global/list/obj/item/device/pda/PDAs = list()
data["feed"] = feed
data["manifest"] = list("__json_cache" = ManifestJSON)
data["manifest"] = PDA_Manifest
nanoUI = data
// update the ui if it exists, returns null if no ui is passed/found
+6 -14
View File
@@ -116,7 +116,7 @@
/obj/item/weapon/cartridge/signal/Destroy()
qdel(radio)
..()
return ..()
/obj/item/weapon/cartridge/quartermaster
name = "\improper Space Parts & Space Vendors cartridge"
@@ -436,7 +436,7 @@
else
JaniData["user_loc"] = list("x" = 0, "y" = 0)
var/MopData[0]
for(var/obj/item/weapon/mop/M in world)
for(var/obj/item/weapon/mop/M in global.janitorial_supplies)
var/turf/ml = get_turf(M)
if(ml)
if(ml.z != cl.z)
@@ -447,9 +447,8 @@
if(!MopData.len)
MopData[++MopData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
var/BucketData[0]
for(var/obj/structure/mopbucket/B in world)
for(var/obj/structure/mopbucket/B in global.janitorial_supplies)
var/turf/bl = get_turf(B)
if(bl)
if(bl.z != cl.z)
@@ -461,7 +460,7 @@
BucketData[++BucketData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
var/CbotData[0]
for(var/mob/living/bot/cleanbot/B in world)
for(var/mob/living/bot/cleanbot/B in global.janitorial_supplies)
var/turf/bl = get_turf(B)
if(bl)
if(bl.z != cl.z)
@@ -469,23 +468,19 @@
var/direction = get_dir(src,B)
CbotData[++CbotData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.on ? "Online" : "Offline")
if(!CbotData.len)
CbotData[++CbotData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
var/CartData[0]
for(var/obj/structure/janitorialcart/B in world)
for(var/obj/structure/janitorialcart/B in global.janitorial_supplies)
var/turf/bl = get_turf(B)
if(bl)
if(bl.z != cl.z)
continue
var/direction = get_dir(src,B)
CartData[++CartData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.reagents.total_volume/100)
CartData[++CartData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.get_short_status())
if(!CartData.len)
CartData[++CartData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
JaniData["mops"] = MopData
JaniData["buckets"] = BucketData
JaniData["cleanbots"] = CbotData
@@ -495,9 +490,6 @@
return values
/obj/item/weapon/cartridge/Topic(href, href_list)
..()
@@ -7,7 +7,7 @@
canhear_range = 2
flags = CONDUCT | NOBLOODY
var/number = 0
var/last_tick //used to delay the powercheck
var/obj/machinery/abstract/intercom_listener/power_interface
/obj/item/device/radio/intercom/custom
name = "station intercom (Custom)"
@@ -46,7 +46,7 @@
/obj/item/device/radio/intercom/New()
..()
processing_objects += src
power_interface = new(loc, src)
/obj/item/device/radio/intercom/department/medbay/New()
..()
@@ -78,8 +78,8 @@
internal_channels[num2text(SYND_FREQ)] = list(access_syndicate)
/obj/item/device/radio/intercom/Destroy()
processing_objects -= src
..()
QDEL_NULL(power_interface)
return ..()
/obj/item/device/radio/intercom/attack_ai(mob/user as mob)
src.add_fingerprint(user)
@@ -106,23 +106,23 @@
return canhear_range
/obj/item/device/radio/intercom/process()
if(((world.timeofday - last_tick) > 30) || ((world.timeofday - last_tick) < 0))
last_tick = world.timeofday
/obj/item/device/radio/intercom/proc/power_change(has_power)
if (!src.loc)
on = 0
else
on = has_power
if(!src.loc)
on = 0
else
var/area/A = get_area(src)
if(!A)
on = 0
else
on = A.powered(EQUIP) // set "on" to the power status
update_icon()
if(!on)
icon_state = "intercom-p"
else
icon_state = "intercom"
/obj/item/device/radio/intercom/forceMove(atom/dest)
power_interface.forceMove(dest)
..(dest)
/obj/item/device/radio/intercom/update_icon()
if (on)
icon_state = "intercom"
else
icon_state = "intercom-p"
/obj/item/device/radio/intercom/broadcasting
broadcasting = 1
@@ -121,6 +121,10 @@ var/const/NO_EMAG_ACT = -50
var/rank = null //actual job
var/dorm = 0 // determines if this ID has claimed a dorm already
/obj/item/weapon/card/id/Destroy()
mob = null
return ..()
/obj/item/weapon/card/id/examine(mob/user)
set src in oview(1)
if(in_range(usr, src))
@@ -266,7 +266,7 @@ CIGARETTE PACKETS ARE IN FANCY.DM
ignitermes = "<span class='notice'>USER fiddles with FLAME, and manages to light their NAME with the power of science.</span>"
/obj/item/clothing/mask/smokable/cigarette/cigar/cohiba
name = "\improper Cohiba Robusto cigar"
name = "\improper Cohiba robusto cigar"
desc = "There's little more you could want from a cigar."
icon_state = "cigar2off"
icon_on = "cigar2on"
@@ -5,7 +5,7 @@
/obj/item/weapon/circuitboard/holodeckcontrol
name = T_BOARD("holodeck control console")
build_path = /obj/machinery/computer/HolodeckControl
origin_tech = "programming=2;bluespace=2"
origin_tech = list(TECH_DATA = 2, TECH_BLUESPACE = 2)
var/last_to_emag
var/linkedholodeck_area
var/list/supported_programs
@@ -367,6 +367,17 @@ the implant may become unstable and either pre-maturely inject the subject or si
processing_objects.Add(src)
return 1
/obj/item/weapon/implant/loyalty/ipc
name = "loyalty chip"
desc = "A device that sets directives programmed for loyalty to NanoTrasen on the synthetic subject. Will not work on organics."
/obj/item/weapon/implant/loyalty/ipc/implanted(mob/M)
if (!isipc(M))
return
..()
/obj/item/weapon/implant/adrenalin
name = "adrenalin"
desc = "Removes all stuns and knockdowns."
@@ -58,7 +58,7 @@
edge = 1
force_divisor = 0.15 // 9 when wielded with hardness 60 (steel)
matter = list(DEFAULT_WALL_MATERIAL = 12000)
origin_tech = "materials=1"
origin_tech = list(TECH_MATERIAL = 1)
attack_verb = list("slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
unbreakable = 1
@@ -18,7 +18,7 @@
w_class = 2
sharp = 1
edge = 1
origin_tech = "materials=2;combat=1"
origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 1)
attack_verb = list("chopped", "torn", "cut")
applies_material_colour = 0
@@ -61,5 +61,5 @@
throw_range = 3
w_class = 4
slot_flags = SLOT_BACK
origin_tech = "materials=2;combat=2"
origin_tech = list(TECH_MATERIAL = 2, TECH_COMBAT = 2)
attack_verb = list("chopped", "sliced", "cut", "reaped")
@@ -107,6 +107,7 @@
var/obj/item/weapon/material/twohanded/offhand/O = user.get_inactive_hand()
if(O && istype(O))
user.u_equip(O)
O.unwield()
else //Trying to wield it
@@ -149,9 +150,17 @@
default_material = "placeholder"
/obj/item/weapon/material/twohanded/offhand/unwield()
if (ismob(loc))
var/mob/living/our_mob = loc
our_mob.remove_from_mob(src)
qdel(src)
/obj/item/weapon/material/twohanded/offhand/wield()
if (ismob(loc))
var/mob/living/our_mob = loc
our_mob.remove_from_mob(src)
qdel(src)
/obj/item/weapon/material/twohanded/offhand/update_icon()
+95 -14
View File
@@ -5,15 +5,23 @@
var/active_w_class
sharp = 0
edge = 0
armor_penetration = 50
armor_penetration = 10
flags = NOBLOODY
can_embed = 0//No embedding pls
var/base_reflectchance = 40
var/base_block_chance = 25
var/shield_power = 100
var/can_block_bullets = 0
var/datum/effect_system/sparks/spark_system
/obj/item/weapon/melee/energy/New()
spark_system = bind_spark(src, 5)
..()
/obj/item/weapon/melee/energy/Destroy()
QDEL_NULL(spark_system)
return ..()
/obj/item/weapon/melee/energy/proc/activate(mob/living/user)
anchored = 1
if(active)
@@ -56,6 +64,66 @@
add_fingerprint(user)
return
/obj/item/weapon/melee/energy/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(active && default_parry_check(user, attacker, damage_source) && prob(50))
user.visible_message("<span class='danger'>\The [user] parries [attack_text] with \the [src]!</span>")
spark_system.queue()
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
return 1
else
if(!active)
return 0 //turn it on first!
if(user.incapacitated())
return 0
//block as long as they are not directly behind us
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
if(check_shield_arc(user, bad_arc, damage_source, attacker))
if(prob(base_block_chance))
spark_system.queue()
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
shield_power -= round(damage/4)
if(shield_power <= 0)
visible_message("<span class='danger'>\The [user]'s [src.name] overloads!</span>")
deactivate()
shield_power = initial(shield_power)
return 0
if(istype(damage_source, /obj/item/projectile/energy) || istype(damage_source, /obj/item/projectile/beam))
var/obj/item/projectile/P = damage_source
var/reflectchance = base_reflectchance - round(damage/3)
if(!(def_zone in list("chest", "groin","head")))
reflectchance /= 2
if(P.starting && prob(reflectchance))
visible_message("<span class='danger'>\The [user]'s [src.name] reflects [attack_text]!</span>")
// Find a turf near or on the original location to bounce to
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_turf(user)
// redirect the projectile
P.redirect(new_x, new_y, curloc, user)
return PROJECTILE_CONTINUE // complete projectile permutation
else
user.visible_message("<span class='danger'>\The [user] blocks [attack_text] with \the [src]!</span>")
return 1
else if(istype(damage_source, /obj/item/projectile/bullet) && can_block_bullets)
var/reflectchance = (base_reflectchance) - round(damage/3)
if(!(def_zone in list("chest", "groin","head")))
reflectchance /= 2
if(prob(reflectchance))
user.visible_message("<span class='danger'>\The [user] blocks [attack_text] with \the [src]!</span>")
return 1
/obj/item/weapon/melee/energy/glaive
name = "energy glaive"
desc = "An energized glaive."
@@ -74,6 +142,11 @@
sharp = 1
edge = 1
slot_flags = SLOT_BACK
base_reflectchance = 0
base_block_chance = 0 //cannot be used to block guns
shield_power = 0
can_block_bullets = 0
armor_penetration = 20
/obj/item/weapon/melee/energy/glaive/activate(mob/living/user)
..()
@@ -108,6 +181,11 @@
attack_verb = list("attacked", "chopped", "cleaved", "torn", "cut")
sharp = 1
edge = 1
base_reflectchance = 0
base_block_chance = 0 //cannot be used to block guns
shield_power = 0
can_block_bullets = 0
armor_penetration = 35
/obj/item/weapon/melee/energy/axe/activate(mob/living/user)
..()
@@ -140,6 +218,7 @@
sharp = 1
edge = 1
var/blade_color
shield_power = 75
/obj/item/weapon/melee/energy/sword/dropped(var/mob/user)
..()
@@ -175,19 +254,12 @@
attack_verb = list()
icon_state = initial(icon_state)
/obj/item/weapon/melee/energy/sword/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(active && default_parry_check(user, attacker, damage_source) && prob(50))
user.visible_message("<span class='danger'>\The [user] parries [attack_text] with \the [src]!</span>")
spark_system.queue()
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
return 1
return 0
/obj/item/weapon/melee/energy/sword/pirate
name = "energy cutlass"
desc = "Arrrr matey."
icon_state = "cutlass0"
base_reflectchance = 60
base_block_chance = 60
/obj/item/weapon/melee/energy/sword/pirate/activate(mob/living/user)
..()
@@ -196,13 +268,12 @@
/*
*Energy Blade
*/
//Can't be activated or deactivated, so no reason to be a subtype of energy
/obj/item/weapon/melee/energy/blade
name = "energy blade"
desc = "A concentrated beam of energy in the shape of a blade. Very stylish... and lethal."
icon_state = "blade"
force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe
force = 40
active_force = 40 //Normal attacks deal very high damage - about the same as wielded fire axe
armor_penetration = 100
sharp = 1
edge = 1
@@ -214,6 +285,12 @@
flags = NOBLOODY
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
var/mob/living/creator
base_reflectchance = 140
base_block_chance = 75
shield_power = 150
can_block_bullets = 1
active = 1
armor_penetration = 20
/obj/item/weapon/melee/energy/blade/New()
processing_objects |= src
@@ -223,10 +300,14 @@
processing_objects -= src
..()
/obj/item/weapon/melee/energy/blade/attack_self(mob/user as mob)
/obj/item/weapon/melee/energy/blade/deactivate(mob/living/user)
if(!active)
return
playsound(user, 'sound/weapons/saberoff.ogg', 50, 1)
user.drop_from_inventory(src)
spawn(1) if(src) qdel(src)
/obj/item/weapon/melee/energy/blade/dropped()
spawn(1) if(src) qdel(src)
+6 -1
View File
@@ -12,9 +12,14 @@
var/mopping = 0
var/mopcount = 0
/obj/item/weapon/mop/New()
..()
create_reagents(30)
janitorial_supplies |= src
/obj/item/weapon/mop/Destroy()
janitorial_supplies -= src
return ..()
/obj/item/weapon/mop/afterattack(atom/A, mob/user, proximity)
if(!proximity) return
+47 -2
View File
@@ -103,17 +103,62 @@
w_class = 2
origin_tech = list(TECH_MATERIAL = 4, TECH_MAGNET = 3, TECH_ILLEGAL = 4)
attack_verb = list("shoved", "bashed")
var/shield_power = 150
var/active = 0
/obj/item/weapon/shield/energy/handle_shield(mob/user)
/obj/item/weapon/shield/energy/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack")
if(!active)
return 0 //turn it on first!
. = ..()
if(user.incapacitated())
return 0
if(.)
spark(user.loc, 5)
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
//block as long as they are not directly behind us
var/bad_arc = reverse_direction(user.dir) //arc of directions from which we cannot block
if(check_shield_arc(user, bad_arc, damage_source, attacker))
if(prob(get_block_chance(user, damage, damage_source, attacker)))
spark(user.loc, 5)
playsound(user.loc, 'sound/weapons/blade1.ogg', 50, 1)
shield_power -= round(damage/4)
if(shield_power <= 0)
visible_message("<span class='danger'>\The [user]'s [src.name] overloads!</span>")
active = 0
force = 3
update_icon()
w_class = 1
playsound(user, 'sound/weapons/saberoff.ogg', 50, 1)
shield_power = initial(shield_power)
return 0
if(istype(damage_source, /obj/item/projectile/energy) || istype(damage_source, /obj/item/projectile/beam))
var/obj/item/projectile/P = damage_source
var/reflectchance = 80 - round(damage/3)
if(P.starting && prob(reflectchance))
visible_message("<span class='danger'>\The [user]'s [src.name] reflects [attack_text]!</span>")
// Find a turf near or on the original location to bounce to
var/new_x = P.starting.x + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/new_y = P.starting.y + pick(0, 0, 0, 0, 0, -1, 1, -2, 2)
var/turf/curloc = get_turf(user)
// redirect the projectile
P.redirect(new_x, new_y, curloc, user)
return PROJECTILE_CONTINUE // complete projectile permutation
else
user.visible_message("<span class='danger'>\The [user] blocks [attack_text] with \the [src]!</span>")
return 1
else
user.visible_message("<span class='danger'>\The [user] blocks [attack_text] with \the [src]!</span>")
return 1
/obj/item/weapon/shield/energy/get_block_chance(mob/user, var/damage, atom/damage_source = null, mob/attacker = null)
if(istype(damage_source, /obj/item/projectile))
var/obj/item/projectile/P = damage_source
@@ -329,11 +329,19 @@
name = "duffel bag"
desc = "A spacious duffel bag."
icon_state = "duffel-norm"
item_state_slots = list(
slot_l_hand_str = "duffle",
slot_r_hand_str = "duffle"
)
/obj/item/weapon/storage/backpack/duffel/cap
name = "captain's duffel bag"
desc = "A rare and special duffel bag for only the most air-headed of Nanotrasen personnel."
icon_state = "duffel-captain"
item_state_slots = list(
slot_l_hand_str = "duffle_captain",
slot_r_hand_str = "duffle_captain"
)
/obj/item/weapon/storage/backpack/duffel/hyd
name = "botanist's duffel bag"
@@ -344,16 +352,28 @@
name = "virology duffel bag"
desc = "A sterilized duffel bag suited to those about to unleash pathogenic havoc upon the world."
icon_state = "duffel-virology"
item_state_slots = list(
slot_l_hand_str = "duffle_med",
slot_r_hand_str = "duffle_med"
)
/obj/item/weapon/storage/backpack/duffel/med
name = "medical duffel bag"
desc = "A sterilized duffel bag for the young, upcoming lesbayan."
icon_state = "duffel-medical"
item_state_slots = list(
slot_l_hand_str = "duffle_med",
slot_r_hand_str = "duffle_med"
)
/obj/item/weapon/storage/backpack/duffel/eng
name = "industrial duffel bag"
desc = "A rough and tumble duffel bag for the hard working wrench-monkey of tomorrow."
icon_state = "duffel-engineering"
item_state_slots = list(
slot_l_hand_str = "duffle_eng",
slot_r_hand_str = "duffle_eng"
)
/obj/item/weapon/storage/backpack/duffel/tox
name = "scientist's duffel bag"
@@ -374,11 +394,19 @@
name = "chemistry duffel bag"
desc = "Spice up the love life a little."
icon_state = "duffel-chemistry"
item_state_slots = list(
slot_l_hand_str = "duffle_med",
slot_r_hand_str = "duffle_med"
)
/obj/item/weapon/storage/backpack/duffel/syndie
name = "syndicate duffel bag"
desc = "A snazzy black and red duffel bag, perfect for smuggling C4 and Parapens."
icon_state = "duffel-syndie"
item_state_slots = list(
slot_l_hand_str = "duffle_syndie",
slot_r_hand_str = "duffle_syndie"
)
/obj/item/weapon/storage/backpack/duffel/wizard
name = "wizardly duffel bag"
@@ -71,7 +71,7 @@
item_state = "candlebox5"
throwforce = 2
slot_flags = SLOT_BELT
max_storage_space = 5
/obj/item/weapon/storage/fancy/candle_box/New()
..()
@@ -114,21 +114,15 @@
/obj/item/weapon/storage/lockbox/medal/New()
..()
new /obj/item/clothing/accessory/medal
new /obj/item/clothing/accessory/medal
new /obj/item/clothing/accessory/medal/conduct
new /obj/item/clothing/accessory/medal/conduct
new /obj/item/clothing/accessory/medal/conduct
new /obj/item/clothing/accessory/medal/conduct
new /obj/item/clothing/accessory/medal/bronze_heart
new /obj/item/clothing/accessory/medal/bronze_heart
new /obj/item/clothing/accessory/medal/nobel_science
new /obj/item/clothing/accessory/medal/nobel_science
new /obj/item/clothing/accessory/medal/silver
new /obj/item/clothing/accessory/medal/iron/merit
new /obj/item/clothing/accessory/medal/silver/valor
new /obj/item/clothing/accessory/medal/silver/security
new /obj/item/clothing/accessory/medal/silver/security
new /obj/item/clothing/accessory/medal/gold
new /obj/item/clothing/accessory/medal/conduct(src)
new /obj/item/clothing/accessory/medal/conduct(src)
new /obj/item/clothing/accessory/medal/conduct(src)
new /obj/item/clothing/accessory/medal/bronze_heart(src)
new /obj/item/clothing/accessory/medal/bronze_heart(src)
new /obj/item/clothing/accessory/medal/nobel_science(src)
new /obj/item/clothing/accessory/medal/nobel_science(src)
new /obj/item/clothing/accessory/medal/iron/merit(src)
new /obj/item/clothing/accessory/medal/iron/merit(src)
new /obj/item/clothing/accessory/medal/silver/valor(src)
new /obj/item/clothing/accessory/medal/silver/security(src)
new /obj/item/clothing/accessory/medal/silver/security(src)
@@ -413,7 +413,16 @@
update_icon()
return 1
//This proc is called when you want to place an item into the storage item.
//Its a safe proc for adding things to the storage that does the necessary checks. Object will not be moved if it fails
/obj/item/weapon/storage/proc/insert_into_storage(obj/item/W as obj, var/prevent_messages = 1)
if(!can_be_inserted(W, prevent_messages))
return
return handle_item_insertion(W, prevent_messages)
/obj/item/weapon/storage/attackby(obj/item/W as obj, mob/user as mob)
..()
@@ -4,11 +4,13 @@
storage_slots = 10
icon_state = "wallet"
w_class = 2
max_w_class = 2
can_hold = list(
/obj/item/weapon/spacecash,
/obj/item/weapon/card,
/obj/item/clothing/mask/smokable/cigarette/,
/obj/item/clothing/mask/smokable,
/obj/item/clothing/accessory/badge,
/obj/item/clothing/accessory/locket,
/obj/item/device/flashlight/pen,
/obj/item/seeds,
/obj/item/stack/medical,
@@ -19,11 +21,16 @@
/obj/item/weapon/flame/lighter,
/obj/item/weapon/flame/match,
/obj/item/weapon/paper,
/obj/item/weapon/paper_bundle,
/obj/item/weapon/pen,
/obj/item/weapon/photo,
/obj/item/weapon/reagent_containers/dropper,
/obj/item/weapon/reagent_containers/syringe,
/obj/item/weapon/reagent_containers/pill,
/obj/item/weapon/reagent_containers/hypospray/autoinjector,
/obj/item/weapon/screwdriver,
/obj/item/weapon/stamp)
/obj/item/weapon/stamp,
/obj/item/device/paicard)
slot_flags = SLOT_ID
var/obj/item/weapon/card/id/front_id = null
+1 -1
View File
@@ -208,7 +208,7 @@
item_state = "stunrod"
force = 20
baton_color = "#75ACFF"
origin_tech = "combat=4,illegal=2"
origin_tech = list(TECH_COMBAT = 4, TECH_ILLEGAL = 2)
contained_sprite = 1
/obj/item/weapon/melee/baton/stunrod/New()
+3 -3
View File
@@ -166,8 +166,8 @@
desc = "A welding tool with an extended-capacity built-in fuel tank, standard issue for engineers."
max_fuel = 40
matter = list(DEFAULT_WALL_MATERIAL = 100, "glass" = 60)
origin_tech = "engineering=2"
base_iconstate = "ind_welder_off"
origin_tech = list(TECH_ENGINEERING = 2)
/obj/item/weapon/weldingtool/hugetank
@@ -176,8 +176,8 @@
max_fuel = 80
w_class = 2.0
matter = list(DEFAULT_WALL_MATERIAL = 200, "glass" = 120)
origin_tech = "engineering=3"
base_iconstate = "adv_welder_off"
origin_tech = list(TECH_ENGINEERING = 3)
//The Experimental Welding Tool!
@@ -187,8 +187,8 @@
max_fuel = 40
w_class = 2.0
matter = list(DEFAULT_WALL_MATERIAL = 100, "glass" = 120)
origin_tech = "engineering=4;biotech=4"
base_iconstate = "exp_welder_off"
origin_tech = list(TECH_ENGINEERING = 4, TECH_BIO = 4)
base_itemstate = "exp_welder"
var/last_gen = 0
+1 -1
View File
@@ -8,7 +8,7 @@
desc = "A mechanically activated leg trap. Low-tech, but reliable. Looks like it could really hurt if you set it off."
throwforce = 0
w_class = 3
origin_tech = "materials=1"
origin_tech = list(TECH_MATERIAL = 1)
matter = list(DEFAULT_WALL_MATERIAL = 18750)
var/deployed = 0
@@ -157,7 +157,7 @@
desc = "A lightweight Zo'rane designed Vaurcae softsuit, for extremely extended EVA operations."
slowdown = 0
species_restricted = list("Vaurca Worker", "Vaurca Warrior")
species_restricted = list("Vaurca")
boots = /obj/item/clothing/shoes/magboots/vox/vaurca
helmet = /obj/item/clothing/head/helmet/space/void/vaurca
@@ -170,7 +170,7 @@
icon_state = "helm_void"
item_state = "helm_void"
species_restricted = list("Vaurca Worker", "Vaurca Warrior")
species_restricted = list("Vaurca")
light_overlay = "helmet_light"
@@ -183,7 +183,7 @@
contained_sprite = 1
icon = 'icons/obj/vaurca_items.dmi'
species_restricted = list("Vaurca Worker", "Vaurca Warrior")
species_restricted = list("Vaurca")
action_button_name = "Toggle the magclaws"
+1 -1
View File
@@ -96,7 +96,7 @@
if(iswirecutter(W))
if(!shock(user, 100))
playsound(loc, 'sound/items/Wirecutter.ogg', 100, 1)
getFromPool(/obj/item/stack/rods, list(get_turf(src), destroyed ? 1 : 2))
getFromPool(/obj/item/stack/rods, get_turf(src), destroyed ? 1 : 2)
qdel(src)
else if((isscrewdriver(W)) && (istype(loc, /turf/simulated) || anchored))
if(!shock(user, 90))
+14 -1
View File
@@ -22,8 +22,21 @@
var/has_items = 0//This is set true whenever the cart has anything loaded/mounted on it
var/dismantled = 0//This is set true after the object has been dismantled to avoid an infintie loop
///obj/structure/janitorialcart/New()
/obj/structure/janitorialcart/New()
..()
janitorial_supplies |= src
/obj/structure/janitorialcart/Destroy()
janitorial_supplies -= src
QDEL_NULL(mybag)
QDEL_NULL(mymop)
QDEL_NULL(myspray)
QDEL_NULL(myreplacer)
QDEL_NULL(mybucket)
return ..()
/obj/structure/janitorialcart/proc/get_short_status()
return "Contents: [english_list(contents)]"
/obj/structure/janitorialcart/examine(mob/user)
if(..(user, 1))
+7 -2
View File
@@ -10,12 +10,17 @@
/obj/structure/mopbucket/New()
create_reagents(100)
..()
create_reagents(100)
janitorial_supplies |= src
/obj/structure/mobbucket/Destroy()
janitorial_supplies -= src
return ..()
/obj/structure/mopbucket/examine(mob/user)
if(..(user, 1))
user << "[src] \icon[src] contains [reagents.total_volume] unit\s of water!"
user << "Contains [reagents.total_volume] unit\s of water."
/obj/structure/mopbucket/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/weapon/mop))
+4 -4
View File
@@ -18,6 +18,7 @@
/obj/structure/toilet/attack_hand(mob/living/user as mob)
if(swirlie)
usr.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
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(8)
return
@@ -53,6 +54,7 @@
return
if(istype(I, /obj/item/weapon/grab))
usr.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
var/obj/item/weapon/grab/G = I
if(isliving(G.affecting))
@@ -117,7 +119,7 @@
/obj/machinery/shower
name = "shower"
desc = "The HS-451. Installed in the 2550s by the Hygiene Division."
desc = "The HS-451. Installed in the 2450s by the Hygiene Division."
icon = 'icons/obj/watercloset.dmi'
icon_state = "shower"
density = 0
@@ -453,9 +455,7 @@
R.cell.charge -= 20
else
B.deductcharge(B.hitcost)
user.visible_message( \
"<span class='danger'>[user] was stunned by \his wet [O]!</span>", \
"<span class='userdanger'>[user] was stunned by \his wet [O]!</span>")
user.visible_message("<span class='danger'>[user] was stunned by \the [O]!</span>")
return 1
// Short of a rewrite, this is necessary to stop monkeycubes being washed.
else if(istype(O, /obj/item/weapon/reagent_containers/food/snacks/monkeycube))
@@ -17,6 +17,7 @@ obj/structure/windoor_assembly
density = 0
dir = NORTH
w_class = 3
flags = ON_BORDER
var/obj/item/weapon/airlock_electronics/electronics = null
@@ -79,7 +80,7 @@ obj/structure/windoor_assembly/Destroy()
user << "<span class='notice'>You dissasembled the windoor assembly!</span>"
new /obj/item/stack/material/glass/reinforced(get_turf(src), 5)
if(secure)
getFromPool(/obj/item/stack/rods, list(get_turf(src), 4))
getFromPool(/obj/item/stack/rods, get_turf(src), 4)
qdel(src)
else
user << "<span class='notice'>You need more welding fuel to dissassemble the windoor assembly.</span>"
@@ -261,18 +262,60 @@ obj/structure/windoor_assembly/Destroy()
//Rotates the windoor assembly clockwise
/obj/structure/windoor_assembly/verb/revrotate()
set name = "Rotate Windoor Assembly"
//These directions are fucked up, apparently dm rotates anticlockwise by default
/obj/structure/windoor_assembly/verb/rotate()
set name = "Rotate Windoor Clockwise"
set category = "Object"
set src in oview(1)
var/targetdir = turn(src.dir, 270)
for(var/obj/obstacle in get_turf(src))
if (obstacle == src)
continue
if((obstacle.flags & ON_BORDER) && obstacle.dir == targetdir)
usr << span("danger", "You can't turn the windoor assembly that way, there's already something there!")
return
if (src.anchored)
usr << "It is fastened to the floor; therefore, you can't rotate it!"
return 0
if(src.state != "01")
update_nearby_tiles(need_rebuild=1) //Compel updates before
src.set_dir(turn(src.dir, 270))
src.set_dir(targetdir)
if(src.state != "01")
update_nearby_tiles(need_rebuild=1)
update_icon()
return
//Rotates the windoor assembly anticlockwise
/obj/structure/windoor_assembly/verb/revrotate()
set name = "Rotate Windoor Anticlockwise"
set category = "Object"
set src in oview(1)
var/targetdir = turn(src.dir, 90)
for(var/obj/obstacle in get_turf(src))
if (obstacle == src)
continue
if((obstacle.flags & ON_BORDER) && obstacle.dir == targetdir)
usr << span("danger", "You can't turn the windoor assembly that way, there's already something there!")
return
if (src.anchored)
usr << "It is fastened to the floor; therefore, you can't rotate it!"
return 0
if(src.state != "01")
update_nearby_tiles(need_rebuild=1) //Compel updates before
src.set_dir(targetdir)
if(src.state != "01")
update_nearby_tiles(need_rebuild=1)

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