mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-29 07:59:01 +01:00
Merge pull request #1960 from Lohikar/map-dev-master
Update map-development
This commit is contained in:
Binary file not shown.
+6
-4
@@ -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>", \
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>", \
|
||||
|
||||
@@ -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>", \
|
||||
|
||||
@@ -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);}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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")
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
..()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
..()
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
name = "Shuttle"
|
||||
desc = "For shuttle control."
|
||||
|
||||
is_holographic = FALSE
|
||||
icon_screen = "shuttle"
|
||||
light_color = "#00ffff"
|
||||
var/auth_need = 3.0
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user