Merge branch 'master' into upstream-merge-36722

This commit is contained in:
LetterJay
2018-04-01 17:44:41 -05:00
committed by GitHub
111 changed files with 2229 additions and 1329 deletions
+8 -1
View File
@@ -40,4 +40,11 @@
#define CHAPLAIN (1<<10)
#define CLOWN (1<<11)
#define MIME (1<<12)
#define ASSISTANT (1<<13)
#define ASSISTANT (1<<13)
#define JOB_AVAILABLE 0
#define JOB_UNAVAILABLE_GENERIC 1
#define JOB_UNAVAILABLE_BANNED 2
#define JOB_UNAVAILABLE_PLAYTIME 3
#define JOB_UNAVAILABLE_ACCOUNTAGE 4
#define JOB_UNAVAILABLE_SLOTFULL 5
+14 -16
View File
@@ -116,6 +116,20 @@
#define GET_ANGLE_OF_INCIDENCE(face, input) (MODULUS((face) - (input), 360))
//Finds the shortest angle that angle A has to change to get to angle B. Aka, whether to move clock or counterclockwise.
/proc/closer_angle_difference(a, b)
if(!isnum(a) || !isnum(b))
return
a = SIMPLIFY_DEGREES(a)
b = SIMPLIFY_DEGREES(b)
var/inc = b - a
if(inc < 0)
inc += 360
var/dec = a - b
if(dec < 0)
dec += 360
. = inc > dec? -dec : inc
//A logarithm that converts an integer to a number scaled between 0 and 1.
//Currently, this is used for hydroponics-produce sprite transforming, but could be useful for other transform functions.
#define TRANSFORM_USING_VARIABLE(input, max) ( sin((90*(input))/(max))**2 )
@@ -146,22 +160,6 @@
return (mean + stddev * R1)
#undef ACCURACY
/proc/mouse_angle_from_client(client/client)
var/list/mouse_control = params2list(client.mouseParams)
if(mouse_control["screen-loc"] && client)
var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",")
var/list/screen_loc_X = splittext(screen_loc_params[1],":")
var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
var/x = (text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32)
var/y = (text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32)
var/list/screenview = getviewsize(client.view)
var/screenviewX = screenview[1] * world.icon_size
var/screenviewY = screenview[2] * world.icon_size
var/ox = round(screenviewX/2) - client.pixel_x //"origin" x
var/oy = round(screenviewY/2) - client.pixel_y //"origin" y
var/angle = SIMPLIFY_DEGREES(ATAN2(y - oy, x - ox))
return angle
/proc/get_turf_in_angle(angle, turf/starting, increments)
var/pixel_x = 0
var/pixel_y = 0
+1 -1
View File
@@ -15,7 +15,7 @@
#define LAZYADD(L, I) if(!L) { L = list(); } L += I;
#define LAZYOR(L, I) if(!L) { L = list(); } L |= I;
#define LAZYFIND(L, V) L ? L.Find(V) : 0
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= length(L) ? L[I] : null) : L[I]) : null)
#define LAZYACCESS(L, I) (L ? (isnum(I) ? (I > 0 && I <= L.len ? L[I] : null) : L[I]) : null)
#define LAZYSET(L, K, V) if(!L) { L = list(); } L[K] = V;
#define LAZYLEN(L) length(L)
#define LAZYCLEARLIST(L) if(L) L.Cut()
-44
View File
@@ -20,12 +20,6 @@
return null
return format_text ? format_text(A.name) : A.name
/proc/get_area_by_name(N) //get area by its name
for(var/area/A in world)
if(A.name == N)
return A
return 0
/proc/get_areas_in_range(dist=0, atom/center=usr)
if(!dist)
var/turf/T = get_turf(center)
@@ -387,44 +381,6 @@
active_players++
return active_players
/datum/projectile_data
var/src_x
var/src_y
var/time
var/distance
var/power_x
var/power_y
var/dest_x
var/dest_y
/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \
var/power_x, var/power_y, var/dest_x, var/dest_y)
src.src_x = src_x
src.src_y = src_y
src.time = time
src.distance = distance
src.power_x = power_x
src.power_y = power_y
src.dest_x = dest_x
src.dest_y = dest_y
/proc/projectile_trajectory(src_x, src_y, rotation, angle, power)
// returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle],
// rotated at [rotation] and with the power of [power]
// Thanks to VistaPOWA for this function
var/power_x = power * cos(angle)
var/power_y = power * sin(angle)
var/time = 2* power_y / 10 //10 = g
var/distance = time * power_x
var/dest_x = src_x + distance*sin(rotation);
var/dest_y = src_y + distance*cos(rotation);
return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)
/proc/showCandidatePollWindow(mob/M, poll_time, Question, list/candidates, ignore_category, time_passed, flashwindow = TRUE)
set waitfor = 0
+6 -21
View File
@@ -1041,7 +1041,7 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
return 0
//For creating consistent icons for human looking simple animals
/proc/get_flat_human_icon(icon_id, datum/job/J, datum/preferences/prefs, dummy_key)
/proc/get_flat_human_icon(icon_id, datum/job/J, datum/preferences/prefs, dummy_key, showDirs = GLOB.cardinals)
var/static/list/humanoid_icon_cache = list()
if(!icon_id || !humanoid_icon_cache[icon_id])
var/mob/living/carbon/human/dummy/body = generate_or_wait_for_human_dummy(dummy_key)
@@ -1053,26 +1053,11 @@ GLOBAL_LIST_EMPTY(friendly_animal_types)
var/icon/out_icon = icon('icons/effects/effects.dmi', "nothing")
body.setDir(NORTH)
COMPILE_OVERLAYS(body)
var/icon/partial = getFlatIcon(body)
out_icon.Insert(partial,dir=NORTH)
body.setDir(SOUTH)
COMPILE_OVERLAYS(body)
partial = getFlatIcon(body)
out_icon.Insert(partial,dir=SOUTH)
body.setDir(WEST)
COMPILE_OVERLAYS(body)
partial = getFlatIcon(body)
out_icon.Insert(partial,dir=WEST)
body.setDir(EAST)
COMPILE_OVERLAYS(body)
partial = getFlatIcon(body)
out_icon.Insert(partial,dir=EAST)
for(var/D in showDirs)
body.setDir(D)
COMPILE_OVERLAYS(body)
var/icon/partial = getFlatIcon(body)
out_icon.Insert(partial,dir=D)
humanoid_icon_cache[icon_id] = out_icon
dummy_key? unset_busy_human_dummy(dummy_key) : qdel(body)
+6 -4
View File
@@ -288,7 +288,7 @@ Proc for attack log creation, because really why not
var/hp =" "
if(living_target)
hp = "(NEWHP: [living_target.health])"
hp = " (NEWHP: [living_target.health]) "
var/starget = "NON-EXISTENT SUBJECT"
if(target)
@@ -307,20 +307,22 @@ Proc for attack log creation, because really why not
var/sobject = ""
if(object)
sobject = "[object]"
if(addition)
addition = " [addition]"
var/sattackloc = ""
if(attack_location)
sattackloc = "([attack_location.x],[attack_location.y],[attack_location.z])"
if(is_mob_user)
var/message = "<font color='red'>has [what_done] [starget] with [sobject][addition] [hp] [sattackloc]</font>"
var/message = "<font color='red'>has [what_done] [starget][(sobject||addition) ? " with ":""][sobject][addition][hp][sattackloc]</font>"
user.log_message(message, INDIVIDUAL_ATTACK_LOG)
if(is_mob_target)
var/message = "<font color='orange'>has been [what_done] by [ssource] with [sobject][addition] [hp] [sattackloc]</font>"
var/message = "<font color='orange'>has been [what_done] by [ssource][(sobject||addition) ? " with ":""][sobject][addition][hp][sattackloc]</font>"
target.log_message(message, INDIVIDUAL_ATTACK_LOG)
log_attack("[ssource] [what_done] [starget] with [sobject][addition] [hp] [sattackloc]")
log_attack("[ssource] [what_done] [starget][(sobject||addition) ? " with ":""][sobject][addition][hp][sattackloc]")
/proc/do_mob(mob/user , mob/target, time = 30, uninterruptible = 0, progress = 1, datum/callback/extra_checks = null)
+50
View File
@@ -0,0 +1,50 @@
/proc/mouse_angle_from_client(client/client)
var/list/mouse_control = params2list(client.mouseParams)
if(mouse_control["screen-loc"] && client)
var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",")
var/list/screen_loc_X = splittext(screen_loc_params[1],":")
var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
var/x = (text2num(screen_loc_X[1]) * 32 + text2num(screen_loc_X[2]) - 32)
var/y = (text2num(screen_loc_Y[1]) * 32 + text2num(screen_loc_Y[2]) - 32)
var/list/screenview = getviewsize(client.view)
var/screenviewX = screenview[1] * world.icon_size
var/screenviewY = screenview[2] * world.icon_size
var/ox = round(screenviewX/2) - client.pixel_x //"origin" x
var/oy = round(screenviewY/2) - client.pixel_y //"origin" y
var/angle = SIMPLIFY_DEGREES(ATAN2(y - oy, x - ox))
return angle
//Wow, specific name!
/proc/mouse_absolute_datum_map_position_from_client(client/client)
if(!isloc(client.mob.loc))
return
var/list/mouse_control = params2list(client.mouseParams)
var/cx = client.mob.x
var/cy = client.mob.y
var/cz = client.mob.z
if(mouse_control["screen-loc"])
var/x = 0
var/y = 0
var/z = 0
var/p_x = 0
var/p_y = 0
//Split screen-loc up into X+Pixel_X and Y+Pixel_Y
var/list/screen_loc_params = splittext(mouse_control["screen-loc"], ",")
//Split X+Pixel_X up into list(X, Pixel_X)
var/list/screen_loc_X = splittext(screen_loc_params[1],":")
//Split Y+Pixel_Y up into list(Y, Pixel_Y)
var/list/screen_loc_Y = splittext(screen_loc_params[2],":")
var/sx = text2num(screen_loc_X[1])
var/sy = text2num(screen_loc_Y[1])
//Get the resolution of the client's current screen size.
var/list/screenview = getviewsize(client.view)
var/svx = screenview[1]
var/svy = screenview[2]
var/cox = round((svx - 1) / 2)
var/coy = round((svy - 1) / 2)
x = cx + (sx - 1 - cox)
y = cy + (sy - 1 - coy)
z = cz
p_x = text2num(screen_loc_X[2])
p_y = text2num(screen_loc_Y[2])
return new /datum/position(x, y, z, p_x, p_y)
+10 -6
View File
@@ -20,7 +20,7 @@
if(isnewplayer(m))
continue
if(m.mind)
if(m.stat != DEAD && !isbrain(m))
if(m.stat != DEAD && !isbrain(m) && !iscameramob(m))
num_survivors++
mob_data += list("name" = m.name, "ckey" = ckey(m.mind.key))
if(isobserver(m))
@@ -41,9 +41,9 @@
if(iscyborg(L))
var/mob/living/silicon/robot/R = L
mob_data += list("module" = R.module)
else
category = "others"
mob_data += list("typepath" = L.type)
else
category = "others"
mob_data += list("typepath" = m.type)
if(!escaped)
if(EMERGENCY_ESCAPED_OR_ENDGAMED && (m.onCentCom() || m.onSyndieBase()))
escaped = "escapees"
@@ -60,8 +60,12 @@
file_data["[escaped]"]["npcs"]["[initial(m.name)]"] = 1
else
if(isobserver(m))
file_data["[escaped]"] = mob_data
var/pos = length(file_data["[escaped]"]) + 1
file_data["[escaped]"]["[pos]"] = mob_data
else
if(!category)
category = "others"
mob_data += list("name" = m.name, "typepath" = m.type)
var/pos = length(file_data["[escaped]"]["[category]"]) + 1
file_data["[escaped]"]["[category]"]["[pos]"] = mob_data
var/datum/station_state/end_state = new /datum/station_state()
@@ -504,7 +508,7 @@
/datum/controller/subsystem/ticker/proc/save_admin_data()
if(CONFIG_GET(flag/admin_legacy_system)) //we're already using legacy system so there's nothing to save
return
else if(load_admins()) //returns true if there was a database failure and the backup was loaded from
else if(load_admins(TRUE)) //returns true if there was a database failure and the backup was loaded from
return
var/datum/DBQuery/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
query_admin_rank_update.Execute()
+33
View File
@@ -734,3 +734,36 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
. = base
if(rest)
. += .(rest)
//Replacement for the \th macro when you want the whole word output as text (first instead of 1st)
/proc/thtotext(number)
if(!isnum(number))
return
switch(number)
if(1)
return "first"
if(2)
return "second"
if(3)
return "third"
if(4)
return "fourth"
if(5)
return "fifth"
if(6)
return "sixth"
if(7)
return "seventh"
if(8)
return "eighth"
if(9)
return "ninth"
if(10)
return "tenth"
if(11)
return "eleventh"
if(12)
return "twelfth"
else
return "[number]\th"
+56 -1
View File
@@ -1301,8 +1301,12 @@ GLOBAL_REAL_VAR(list/stack_trace_storage)
if(!istype(C))
return
var/animate_color = initial(C.color)
var/datum/client_colour/CC = C.mob.client_colours[1]
if(CC)
animate_color = CC.colour
C.color = flash_color
animate(C, color = initial(C.color), time = flash_time)
animate(C, color = animate_color, time = flash_time)
#define RANDOM_COLOUR (rgb(rand(0,255),rand(0,255),rand(0,255)))
@@ -1572,3 +1576,54 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
/proc/get_random_drink()
return pick(subtypesof(/obj/item/reagent_containers/food/drinks))
//For these two procs refs MUST be ref = TRUE format like typecaches!
/proc/weakref_filter_list(list/things, list/refs)
if(!islist(things) || !islist(refs))
return
if(!refs.len)
return things
if(things.len > refs.len)
var/list/f = list()
for(var/i in refs)
var/datum/weakref/r = i
var/datum/d = r.resolve()
if(d)
f |= d
return things & f
else
. = list()
for(var/i in things)
if(!refs[WEAKREF(i)])
continue
. |= i
/proc/weakref_filter_list_reverse(list/things, list/refs)
if(!islist(things) || !islist(refs))
return
if(!refs.len)
return things
if(things.len > refs.len)
var/list/f = list()
for(var/i in refs)
var/datum/weakref/r = i
var/datum/d = r.resolve()
if(d)
f |= d
return things - f
else
. = list()
for(var/i in things)
if(refs[WEAKREF(i)])
continue
. |= i
/proc/special_list_filter(list/L, datum/callback/condition)
if(!islist(L) || !length(L) || !istype(condition))
return list()
. = list()
for(var/i in L)
if(condition.Invoke(i))
. |= i
+39
View File
@@ -0,0 +1,39 @@
/datum/projectile_data
var/src_x
var/src_y
var/time
var/distance
var/power_x
var/power_y
var/dest_x
var/dest_y
/datum/projectile_data/New(var/src_x, var/src_y, var/time, var/distance, \
var/power_x, var/power_y, var/dest_x, var/dest_y)
src.src_x = src_x
src.src_y = src_y
src.time = time
src.distance = distance
src.power_x = power_x
src.power_y = power_y
src.dest_x = dest_x
src.dest_y = dest_y
/proc/projectile_trajectory(src_x, src_y, rotation, angle, power)
// returns the destination (Vx,y) that a projectile shot at [src_x], [src_y], with an angle of [angle],
// rotated at [rotation] and with the power of [power]
// Thanks to VistaPOWA for this function
var/power_x = power * cos(angle)
var/power_y = power * sin(angle)
var/time = 2* power_y / 10 //10 = g
var/distance = time * power_x
var/dest_x = src_x + distance*sin(rotation);
var/dest_y = src_y + distance*cos(rotation);
return new /datum/projectile_data(src_x, src_y, time, distance, power_x, power_y, dest_x, dest_y)
+3 -3
View File
@@ -77,10 +77,10 @@
mouseObject = object
mouseControlObject = control
if(mob && LAZYLEN(mob.mousemove_intercept_objects))
for(var/obj/item/I in mob.mousemove_intercept_objects)
I.onMouseMove(object, location, control, params)
for(var/datum/D in mob.mousemove_intercept_objects)
D.onMouseMove(object, location, control, params)
/obj/item/proc/onMouseMove(object, location, control, params)
/datum/proc/onMouseMove(object, location, control, params)
return
/client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params)
@@ -129,6 +129,9 @@
/datum/config_entry/flag/enable_localhost_rank //Gives the !localhost! rank to any client connecting from 127.0.0.1 or ::1
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/flag/load_legacy_ranks_only //Loads admin ranks only from legacy admin_ranks.txt, while enabled ranks are mirrored to the database
protection = CONFIG_ENTRY_LOCKED
/datum/config_entry/string/hostedby
/datum/config_entry/flag/norespawn
+2 -1
View File
@@ -62,7 +62,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
var/static/current_ticklimit = TICK_LIMIT_RUNNING
/datum/controller/master/New()
config = new
if(!config)
config = new
// Highlander-style: there can only be one! Kill off the old and replace it with the new.
if(!random_seed)
+1 -1
View File
@@ -7,7 +7,7 @@ SUBSYSTEM_DEF(mobs)
var/list/currentrun = list()
var/static/list/clients_by_zlevel[][]
var/static/list/cubemonkeys = list()
var/cubemonkeycap = 20
var/cubemonkeycap = 64
/datum/controller/subsystem/mobs/stat_entry()
..("P:[GLOB.mob_living_list.len]")
+2 -2
View File
@@ -1,5 +1,5 @@
SUBSYSTEM_DEF(pathfinder)
name = "pathfinder"
name = "Pathfinder"
init_order = INIT_ORDER_PATH
flags = SS_NO_FIRE
var/datum/flowcache/mobs
@@ -19,7 +19,7 @@ SUBSYSTEM_DEF(pathfinder)
var/list/flow
/datum/flowcache/New(var/n)
.=..()
. = ..()
lcount = n
run = 0
free = 1
+2 -2
View File
@@ -1,4 +1,4 @@
#define MAX_THROWING_DIST 512 // 2 z-levels on default width
#define MAX_THROWING_DIST 1280 // 5 z-levels on default width
#define MAX_TICKS_TO_MAKE_UP 3 //how many missed ticks will we attempt to make up for this run.
SUBSYSTEM_DEF(throwing)
@@ -107,7 +107,7 @@ SUBSYSTEM_DEF(throwing)
return
dist_travelled++
if (dist_travelled > MAX_THROWING_DIST)
finalize()
return
+5 -3
View File
@@ -71,14 +71,16 @@
if(!owner.can_hear() || world.time < next_scare) //words can't trigger you if you can't hear them *taps head*
return message
for(var/word in trigger_words)
if(findtext(raw_message, word))
var/reg = regex(@"[^\l][REGEX_QUOTE(word)]*s[^\l]")
if(findtext(raw_message, reg))
addtimer(CALLBACK(src, .proc/freak_out, null, word), 10) //to react AFTER the chat message
break
return message
/datum/brain_trauma/mild/phobia/on_say(message)
for(var/word in trigger_words)
if(findtext(message, word))
var/reg = regex(@"[^\l][REGEX_QUOTE(word)]*s[^\l]")
if(findtext(message, reg))
to_chat(owner, "<span class='warning'>You can't bring yourself to say the word \"[word]\"!</span>")
return ""
return message
@@ -114,4 +116,4 @@
owner.dizziness += 10
owner.confused += 10
owner.Jitter(10)
owner.stuttering += 10
owner.stuttering += 10
+1 -1
View File
@@ -173,7 +173,7 @@
var/datum/callback_select/CS = new(count, savereturns)
for (var/i in 1 to count)
CS.invoke_callback(i, callbacks[i], callback_args[i], savereturns)
while(CS.pendingcount)
sleep(resolution*world.tick_lag)
return CS.finished
+243
View File
@@ -0,0 +1,243 @@
#define LOCKON_AIMING_MAX_CURSOR_RADIUS 7
#define LOCKON_IGNORE_RESULT "ignore_my_result"
#define LOCKON_RANGING_BREAK_CHECK if(current_ranging_id != this_id){return LOCKON_IGNORE_RESULT}
/datum/component/lockon_aiming
dupe_mode = COMPONENT_DUPE_ALLOWED
var/lock_icon = 'icons/mob/blob.dmi'
var/lock_icon_state = "marker"
var/mutable_appearance/lock_appearance
var/list/image/lock_images
var/list/target_typecache
var/list/immune_weakrefs //list(weakref = TRUE)
var/mob_stat_check = TRUE //if a potential target is a mob make sure it's conscious!
var/lock_amount = 1
var/lock_cursor_range = 5
var/list/locked_weakrefs
var/update_disabled = FALSE
var/current_ranging_id = 0
var/list/last_location
var/datum/callback/on_lock
var/datum/callback/can_target_callback
/datum/component/lockon_aiming/Initialize(range, list/typecache, amount, list/immune, datum/callback/when_locked, icon, icon_state, datum/callback/target_callback)
if(!ismob(parent))
. = COMPONENT_INCOMPATIBLE
CRASH("Lockon aiming component attempted to be added to a non mob!")
if(target_callback)
can_target_callback = target_callback
else
can_target_callback = CALLBACK(src, .proc/can_target)
if(range)
lock_cursor_range = range
if(typecache)
target_typecache = typecache
if(amount)
lock_amount = amount
immune_weakrefs = list(WEAKREF(parent) = TRUE) //Manually take this out if you want..
if(immune)
for(var/i in immune)
if(isweakref(i))
immune_weakrefs[i] = TRUE
else if(isatom(i))
immune_weakrefs[WEAKREF(i)] = TRUE
if(when_locked)
on_lock = when_locked
if(icon)
lock_icon = icon
if(icon_state)
lock_icon_state = icon_state
generate_lock_visuals()
var/mob/M = parent
LAZYOR(M.mousemove_intercept_objects, src)
START_PROCESSING(SSfastprocess, src)
/datum/component/lockon_aiming/Destroy()
var/mob/M = parent
clear_visuals()
LAZYREMOVE(M.mousemove_intercept_objects, src)
STOP_PROCESSING(SSfastprocess, src)
return ..()
/datum/component/lockon_aiming/proc/show_visuals()
LAZYINITLIST(lock_images)
var/mob/M = parent
if(!M.client)
return
for(var/i in locked_weakrefs)
var/datum/weakref/R = i
var/atom/A = R.resolve()
if(!A)
continue //It'll be cleared by processing.
var/image/I = new
I.appearance = lock_appearance
I.loc = A
M.client.images |= I
lock_images |= I
/datum/component/lockon_aiming/proc/clear_visuals()
var/mob/M = parent
if(!M.client)
return
if(!lock_images)
return
for(var/i in lock_images)
M.client.images -= i
qdel(i)
lock_images.Cut()
/datum/component/lockon_aiming/proc/refresh_visuals()
clear_visuals()
show_visuals()
/datum/component/lockon_aiming/proc/generate_lock_visuals()
lock_appearance = mutable_appearance(icon = lock_icon, icon_state = lock_icon_state, layer = FLOAT_LAYER)
/datum/component/lockon_aiming/proc/unlock_all(refresh_vis = TRUE)
LAZYCLEARLIST(locked_weakrefs)
if(refresh_vis)
refresh_visuals()
/datum/component/lockon_aiming/proc/unlock(atom/A, refresh_vis = TRUE)
if(!A.weak_reference)
return
LAZYREMOVE(locked_weakrefs, A.weak_reference)
if(refresh_vis)
refresh_visuals()
/datum/component/lockon_aiming/proc/lock(atom/A, refresh_vis = TRUE)
LAZYOR(locked_weakrefs, WEAKREF(A))
if(refresh_vis)
refresh_visuals()
/datum/component/lockon_aiming/proc/add_immune_atom(atom/A)
var/datum/weakref/R = WEAKREF(A)
if(immune_weakrefs && (immune_weakrefs[R]))
return
LAZYSET(immune_weakrefs, R, TRUE)
/datum/component/lockon_aiming/proc/remove_immune_atom(atom/A)
if(!A.weak_reference || !immune_weakrefs) //if A doesn't have a weakref how did it get on the immunity list?
return
LAZYREMOVE(immune_weakrefs, A.weak_reference)
/datum/component/lockon_aiming/onMouseMove(object,location,control,params)
var/mob/M = parent
if(!istype(M) || !M.client)
return
var/datum/position/P = mouse_absolute_datum_map_position_from_client(M.client)
if(!P)
return
var/turf/T = P.return_turf()
LAZYINITLIST(last_location)
if(length(last_location) == 3 && last_location[1] == T.x && last_location[2] == T.y && last_location[3] == T.z)
return //Same turf, don't bother.
if(last_location)
last_location.Cut()
else
last_location = list()
last_location.len = 3
last_location[1] = T.x
last_location[2] = T.y
last_location[3] = T.z
autolock()
/datum/component/lockon_aiming/process()
if(update_disabled)
return
if(!last_location)
return
var/changed = FALSE
for(var/i in locked_weakrefs)
var/datum/weakref/R = i
if(istype(R))
var/atom/thing = R.resolve()
if(!istype(thing) || (get_dist(thing, locate(last_location[1], last_location[2], last_location[3])) > lock_cursor_range))
unlock(R)
changed = TRUE
else
unlock(R)
changed = TRUE
if(changed)
autolock()
/datum/component/lockon_aiming/proc/autolock()
var/mob/M = parent
if(!M.client)
return FALSE
var/datum/position/current = mouse_absolute_datum_map_position_from_client(M.client)
var/turf/target = current.return_turf()
var/list/atom/targets = get_nearest(target, target_typecache, lock_amount, lock_cursor_range)
if(targets == LOCKON_IGNORE_RESULT)
return
unlock_all(FALSE)
for(var/i in targets)
if(immune_weakrefs[WEAKREF(i)])
continue
lock(i, FALSE)
refresh_visuals()
on_lock.Invoke(locked_weakrefs)
/datum/component/lockon_aiming/proc/can_target(atom/A)
var/mob/M = A
return is_type_in_typecache(A, target_typecache) && !(ismob(A) && mob_stat_check && M.stat != CONSCIOUS) && !immune_weakrefs[WEAKREF(A)]
/datum/component/lockon_aiming/proc/get_nearest(turf/T, list/typecache, amount, range)
current_ranging_id++
var/this_id = current_ranging_id
var/list/L = list()
var/turf/center = get_turf(T)
if(amount < 1 || range < 0 || !istype(center) || !islist(typecache))
return
if(range == 0)
return typecache_filter_list(T.contents + T, typecache)
var/x = 0
var/y = 0
var/cd = 0
while(cd <= range)
x = center.x - cd + 1
y = center.y + cd
LOCKON_RANGING_BREAK_CHECK
for(x in x to center.x + cd)
T = locate(x, y, center.z)
if(T)
L |= special_list_filter(T.contents, can_target_callback)
if(L.len >= amount)
L.Cut(amount+1)
return L
LOCKON_RANGING_BREAK_CHECK
y = center.y + cd - 1
x = center.x + cd
for(y in center.y - cd to y)
T = locate(x, y, center.z)
if(T)
L |= special_list_filter(T.contents, can_target_callback)
if(L.len >= amount)
L.Cut(amount+1)
return L
LOCKON_RANGING_BREAK_CHECK
y = center.y - cd
x = center.x + cd - 1
for(x in center.x - cd to x)
T = locate(x, y, center.z)
if(T)
L |= special_list_filter(T.contents, can_target_callback)
if(L.len >= amount)
L.Cut(amount+1)
return L
LOCKON_RANGING_BREAK_CHECK
y = center.y - cd + 1
x = center.x - cd
for(y in y to center.y + cd)
T = locate(x, y, center.z)
if(T)
L |= special_list_filter(T.contents, can_target_callback)
if(L.len >= amount)
L.Cut(amount+1)
return L
LOCKON_RANGING_BREAK_CHECK
cd++
CHECK_TICK
/datum/component/lockon_aiming/OnTransfer(datum/new_parent)
CRASH("Warning: Lockon aiming component transfer attempted, but transfer behavior is not implemented!")
+1 -1
View File
@@ -68,7 +68,7 @@
var/datum/mood_event/event = mood_events[i]
msg += event.description
else
msg += "<span class='nicegreen'>Nothing special has happend to me lately!<span>\n"
msg += "<span class='nicegreen'>Nothing special has happened to me lately!<span>\n"
to_chat(owner, msg)
/datum/component/mood/proc/update_mood() //Called whenever a mood event is added or removed
+9 -5
View File
@@ -199,6 +199,7 @@
/datum/datacore/proc/manifest_inject(mob/living/carbon/human/H, client/C)
set waitfor = FALSE
var/static/list/show_directions = list(SOUTH, WEST)
if(H.mind && (H.mind.assigned_role != H.mind.special_role))
var/assignment
if(H.mind.assigned_role)
@@ -212,11 +213,14 @@
var/id = num2hex(record_id_num++,6)
if(!C)
C = H.client
var/image = get_id_photo(H, C)
var/image = get_id_photo(H, C, show_directions)
var/obj/item/photo/photo_front = new()
var/obj/item/photo/photo_side = new()
photo_front.photocreate(null, icon(image, dir = SOUTH))
photo_side.photocreate(null, icon(image, dir = WEST))
for(var/D in show_directions)
if(D == SOUTH)
photo_front.photocreate(null, icon(image, dir = D))
if(D == WEST || D == EAST)
photo_side.photocreate(null, icon(image, dir = D))
//These records should ~really~ be merged or something
//General Record
@@ -279,11 +283,11 @@
locked += L
return
/datum/datacore/proc/get_id_photo(mob/living/carbon/human/H, client/C)
/datum/datacore/proc/get_id_photo(mob/living/carbon/human/H, client/C, show_directions = list(SOUTH))
var/datum/job/J = SSjob.GetJob(H.mind.assigned_role)
var/datum/preferences/P
if(!C)
C = H.client
if(C)
P = C.prefs
return get_flat_human_icon(null, J, P, DUMMY_HUMAN_SLOT_MANIFEST)
return get_flat_human_icon(null, J, P, DUMMY_HUMAN_SLOT_MANIFEST, show_directions)
@@ -2,10 +2,10 @@
//////////////////////////////////////
Facial Hypertrichosis
Very very Noticable.
Decreases resistance slightly.
Decreases stage speed.
Reduced transmittability
No change to stealth.
Increases resistance.
Increases speed.
Slighlty increases transmittability
Intense Level.
BONUS
@@ -18,10 +18,10 @@ BONUS
name = "Facial Hypertrichosis"
desc = "The virus increases hair production significantly, causing rapid beard growth."
stealth = -3
resistance = -1
stage_speed = -3
transmittable = -1
stealth = 0
resistance = 3
stage_speed = 2
transmittable = 1
level = 4
severity = 1
symptom_delay_min = 18
@@ -48,4 +48,4 @@ BONUS
to_chat(H, "<span class='warning'>You feel manly!</span>")
if(!(H.facial_hair_style == "Dwarf Beard") && !(H.facial_hair_style == "Very Long Beard"))
H.facial_hair_style = pick("Dwarf Beard", "Very Long Beard")
H.update_hair()
H.update_hair()
@@ -4,7 +4,7 @@ Alopecia
Not Noticeable.
Increases resistance slightly.
Reduces stage speed slightly.
Increases stage speed.
Transmittable.
Intense Level.
@@ -19,8 +19,8 @@ BONUS
desc = "The virus causes rapid shedding of head and body hair."
stealth = 0
resistance = 1
stage_speed = -1
transmittable = 3
stage_speed = 2
transmittable = 2
level = 4
severity = 1
base_message_chance = 50
@@ -52,4 +52,4 @@ BONUS
H.hair_style = "Bald"
else
H.hair_style = "Balding Hair"
H.update_hair()
H.update_hair()
+16 -16
View File
@@ -2,10 +2,10 @@
//////////////////////////////////////
Vitiligo
Extremely Noticable.
Decreases resistance slightly.
Reduces stage speed slightly.
Reduces transmission.
Hidden.
No change to resistance.
Increases stage speed.
Slightly increases transmittability.
Critical Level.
BONUS
@@ -18,10 +18,10 @@ BONUS
name = "Vitiligo"
desc = "The virus destroys skin pigment cells, causing rapid loss of pigmentation in the host."
stealth = -3
resistance = -1
stage_speed = -1
transmittable = -2
stealth = 2
resistance = 0
stage_speed = 3
transmittable = 1
level = 5
severity = 1
symptom_delay_min = 25
@@ -47,10 +47,10 @@ BONUS
//////////////////////////////////////
Revitiligo
Extremely Noticable.
Decreases resistance slightly.
Reduces stage speed slightly.
Reduces transmission.
Slightly noticable.
Increases resistance.
Increases stage speed slightly.
Increases transmission.
Critical Level.
BONUS
@@ -63,10 +63,10 @@ BONUS
name = "Revitiligo"
desc = "The virus causes increased production of skin pigment cells, making the host's skin grow darker over time."
stealth = -3
resistance = -1
stage_speed = -1
transmittable = -2
stealth = -1
resistance = 3
stage_speed = 1
transmittable = 2
level = 5
severity = 1
symptom_delay_min = 7
@@ -105,6 +105,14 @@
mood_change = -3
timeout = 3000
/datum/mood_event/nyctophobia
description = "<span class='warning'>It sure is dark around here...</span>\n"
mood_change = -3
/datum/mood_event/family_heirloom_missing
description = "<span class='warning'>I'm missing my family heirloom...</span>\n"
mood_change = -4
//These are unused so far but I want to remember them to use them later
/datum/mood_event/cloned_corpse
description = "<span class='boldwarning'>I recently saw my own corpse...</span>\n"
@@ -56,3 +56,7 @@
description = "<span class='nicegreen'>I have seen the truth, praise the almighty one!</span>\n"
mood_change = 40 //maybe being a cultist isnt that bad after all
hidden = TRUE
/datum/mood_event/family_heirloom
description = "<span class='nicegreen'>My family heirloom is safe with me.</span>\n"
mood_change = 1
+13 -13
View File
@@ -9,6 +9,19 @@
#define RETURN_POINT_VECTOR(ATOM, ANGLE, SPEED) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED)}
#define RETURN_POINT_VECTOR_INCREMENT(ATOM, ANGLE, SPEED, AMT) {new /datum/point/vector(ATOM, null, null, null, null, ANGLE, SPEED, AMT)}
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
var/datum/point/P = new
P.x = a.x + (b.x - a.x) / 2
P.y = a.y + (b.y - a.y) / 2
P.z = a.z
return P
/proc/pixel_length_between_points(datum/point/a, datum/point/b)
return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
/proc/angle_between_points(datum/point/a, datum/point/b)
return ATAN2((b.y - a.y), (b.x - a.x))
/datum/position //For positions with map x/y/z and pixel x/y so you don't have to return lists. Could use addition/subtraction in the future I guess.
var/x = 0
var/y = 0
@@ -53,19 +66,6 @@
/datum/position/proc/return_point()
return new /datum/point(src)
/proc/point_midpoint_points(datum/point/a, datum/point/b) //Obviously will not support multiZ calculations! Same for the two below.
var/datum/point/P = new
P.x = a.x + (b.x - a.x) / 2
P.y = a.y + (b.y - a.y) / 2
P.z = a.z
return P
/proc/pixel_length_between_points(datum/point/a, datum/point/b)
return sqrt(((b.x - a.x) ** 2) + ((b.y - a.y) ** 2))
/proc/angle_between_points(datum/point/a, datum/point/b)
return ATAN2((b.y - a.y), (b.x - a.x))
/datum/point //A precise point on the map in absolute pixel locations based on world.icon_size. Pixels are FROM THE EDGE OF THE MAP!
var/x = 0
var/y = 0
+25 -23
View File
@@ -11,6 +11,23 @@
/datum/trait/apathetic
name = "Apathetic"
desc = "You just don't care as much as other people. That's nice to have in a place like this, I guess."
value = 1
/datum/trait/apathetic/add()
GET_COMPONENT_FROM(mood, /datum/component/mood, trait_holder)
if(mood)
mood.mood_modifier = 0.8
/datum/trait/apathetic/remove()
GET_COMPONENT_FROM(mood, /datum/component/mood, trait_holder)
if(mood)
mood.mood_modifier = 1 //Change this once/if species get their own mood modifiers.
/datum/trait/freerunning
name = "Freerunning"
desc = "You're great at quick moves! You can climb tables more quickly."
@@ -21,6 +38,14 @@
/datum/trait/jolly
name = "Jolly"
desc = "You sometimes just feel happy, for no reason at all."
value = 1
mob_trait = TRAIT_JOLLY
/datum/trait/light_step
name = "Light Step"
desc = "You walk with a gentle step, making stepping on sharp objects quieter and less painful."
@@ -81,26 +106,3 @@
mob_trait = TRAIT_VORACIOUS
gain_text = "<span class='notice'>You feel HONGRY.</span>"
lose_text = "<span class='danger'>You no longer feel HONGRY.</span>"
/datum/trait/jolly
name = "Jolly"
desc = "You sometimes just feel happy, for no reason at all."
value = 1
mob_trait = TRAIT_JOLLY
/datum/trait/apathetic
name = "Apathetic"
desc = "You just don't care as much as other people, that's nice to have in a place like this, I guess."
value = 1
/datum/trait/apathetic/add()
GET_COMPONENT_FROM(mood, /datum/component/mood, trait_holder)
if(mood)
mood.mood_modifier = 0.8
/datum/trait/apathetic/remove()
GET_COMPONENT_FROM(mood, /datum/component/mood, trait_holder)
if(mood)
mood.mood_modifier = 1 //Change this once/if species get their own mood modifiers.
+107 -12
View File
@@ -1,5 +1,82 @@
//predominantly negative traits
/datum/trait/depression
name = "Depression"
desc = "You sometimes just hate life."
mob_trait = TRAIT_DEPRESSION
value = -1
gain_text = "<span class='danger'>You start feeling depressed.</span>"
lose_text = "<span class='notice'>You no longer feel depressed.</span>" //if only it were that easy!
medical_record_text = "Patient has a severe mood disorder causing them to experience sudden moments of sadness."
/datum/trait/family_heirloom
name = "Family Heirloom"
desc = "You are the current owner of an heirloom. passed down for generations. You have to keep it safe!"
value = -1
var/obj/item/heirloom
var/where_text
/datum/trait/family_heirloom/on_spawn()
var/mob/living/carbon/human/H = trait_holder
var/obj/item/heirloom_type
if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]) //on april fools, pick from any item in the game
var/list/heirlooms = subtypesof(/obj/item)
for(var/V in heirlooms)
var/obj/item/I = V
if((!initial(I.icon_state)) || (!initial(I.item_state)) || (initial(I.flags_1) & ABSTRACT_1))
heirlooms -= V
heirloom_type = pick(heirlooms)
else
switch(trait_holder.mind.assigned_role)
if("Clown")
heirloom_type = /obj/item/bikehorn/golden
if("Mime")
heirloom_type = /obj/item/reagent_containers/food/snacks/baguette
if("Lawyer")
heirloom_type = /obj/item/gavelhammer
if("Janitor")
heirloom_type = /obj/item/mop
if("Security Officer")
heirloom_type = /obj/item/book/manual/wiki/security_space_law
if("Scientist")
heirloom_type = /obj/item/toy/plush/slimeplushie
if("Assistant")
heirloom_type = /obj/item/storage/toolbox/mechanical/old/heirloom
if(!heirloom_type)
heirloom_type = pick(
/obj/item/toy/cards/deck,
/obj/item/lighter,
/obj/item/dice/d20)
heirloom = new heirloom_type(get_turf(trait_holder))
var/list/slots = list(
"in your backpack" = slot_in_backpack,
"in your left pocket" = slot_l_store,
"in your right pocket" = slot_r_store
)
var/where = H.equip_in_one_of_slots(heirloom, slots)
if(!where)
where = "at your feet"
if(where == "in your backpack")
var/obj/item/storage/B = H.back
B.orient2hud(trait_holder)
B.show_to(trait_holder)
where_text = "<span class='boldnotice'>There is a precious family [heirloom.name] [where], passed down from generation to generation. Keep it safe!</span>"
/datum/trait/family_heirloom/post_add()
to_chat(trait_holder, where_text)
var/list/family_name = splittext(trait_holder.real_name, " ")
heirloom.name = "\improper [family_name[family_name.len]] family [heirloom.name]"
/datum/trait/family_heirloom/process()
if(heirloom in trait_holder.GetAllContents())
trait_holder.SendSignal(COMSIG_CLEAR_MOOD_EVENT, "family_heirloom_missing")
trait_holder.SendSignal(COMSIG_ADD_MOOD_EVENT, "family_heirloom", /datum/mood_event/family_heirloom)
else
trait_holder.SendSignal(COMSIG_CLEAR_MOOD_EVENT, "family_heirloom")
trait_holder.SendSignal(COMSIG_ADD_MOOD_EVENT, "family_heirloom_missing", /datum/mood_event/family_heirloom_missing)
/datum/trait/heavy_sleeper
@@ -44,6 +121,27 @@
/datum/trait/nyctophobia
name = "Nyctophobia"
desc = "As far as you can remember, you've always been afraid of the dark. While in the dark without a light source, you instinctually act careful, and constantly feel a sense of dread."
value = -1
/datum/trait/nyctophobia/on_process()
var/mob/living/carbon/human/H = trait_holder
if(H.dna.species.id in list("shadow", "nightmare"))
return //we're tied with the dark, so we don't get scared of it; don't cleanse outright to avoid cheese
var/turf/T = get_turf(trait_holder)
var/lums = T.get_lumcount()
if(lums <= 0.2)
if(trait_holder.m_intent == MOVE_INTENT_RUN)
to_chat(trait_holder, "<span class='warning'>Easy, easy, take it slow... you're in the dark...</span>")
trait_holder.toggle_move_intent()
trait_holder.SendSignal(COMSIG_ADD_MOOD_EVENT, "nyctophobia", /datum/mood_event/nyctophobia)
else
trait_holder.SendSignal(COMSIG_CLEAR_MOOD_EVENT, "nyctophobia")
/datum/trait/nonviolent
name = "Pacifist"
desc = "The thought of violence makes you sick. So much so, in fact, that you can't hurt anyone."
@@ -156,21 +254,18 @@
var/dumb_thing = TRUE
/datum/trait/social_anxiety/on_process()
var/nearby_people = 0
for(var/mob/living/carbon/human/H in view(5, trait_holder))
if(H.client)
nearby_people++
var/mob/living/carbon/human/H = trait_holder
if(prob(5))
if(prob(2 + nearby_people))
H.stuttering = max(3, H.stuttering)
else if(prob(1) && !H.silent)
else if(prob(min(3, nearby_people)) && !H.silent)
to_chat(H, "<span class='danger'>You retreat into yourself. You <i>really</i> don't feel up to talking.</span>")
H.silent = max(10, H.silent)
else if(prob(0.5) && dumb_thing)
to_chat(H, "<span class='danger'>You think of a dumb thing you said a long time ago and scream internally.</span>")
to_chat(H, "<span class='userdanger'>You think of a dumb thing you said a long time ago and scream internally.</span>")
dumb_thing = FALSE //only once per life
/datum/trait/depression
name = "Depression"
desc = "You sometimes just hate life."
mob_trait = TRAIT_DEPRESSION
value = -1
gain_text = "<span class='danger'>You start feeling depressed.</span>"
lose_text = "<span class='notice'>You no longer feel depressed.</span>" //if only it were that easy!
medical_record_text = "Patient has a severe mood disorder causing them to experience sudden moments of sadness."
if(prob(1))
new/obj/item/reagent_containers/food/snacks/pastatomato(get_turf(H)) //now that's what I call spaghetti code
+19
View File
@@ -31,3 +31,22 @@
var/datum/species/species = H.dna.species
species.liked_food = initial(species.liked_food)
species.disliked_food = initial(species.disliked_food)
/datum/trait/monochromatic
name = "Monochromacy"
desc = "You suffer from full colorblindness, and perceive nearly the entire world in blacks and whites."
value = 0
medical_record_text = "Patient is afflicted with almost complete color blindness."
/datum/trait/monochromatic/add()
trait_holder.add_client_colour(/datum/client_colour/monochrome)
/datum/trait/monochromatic/post_add()
if(trait_holder.mind.assigned_role == "Detective")
to_chat(trait_holder, "<span class='boldannounce'>Mmm. Nothing's ever clear on this station. It's all shades of gray...</span>")
trait_holder.playsound_local(trait_holder, 'sound/ambience/ambidet1.ogg', 50, FALSE)
/datum/trait/monochromatic/remove()
trait_holder.remove_client_colour(/datum/client_colour/monochrome)
+3
View File
@@ -6,6 +6,9 @@
input.weak_reference = new /datum/weakref(input)
return input.weak_reference
/datum/proc/create_weakref() //Forced creation for admin proccalls
return WEAKREF(src)
/datum/weakref
var/reference
+1 -1
View File
@@ -45,7 +45,7 @@ Credit where due:
///////////
/proc/is_servant_of_ratvar(mob/M)
return istype(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/clockcult)
return isliving(M) && M.mind && M.mind.has_antag_datum(/datum/antagonist/clockcult)
/proc/is_eligible_servant(mob/M)
if(!istype(M))
+2 -2
View File
@@ -675,7 +675,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
//Picks as many people as it can from a department (Security,Engineer,Medical,Science)
//and tasks the lings with killing and replacing them
/datum/objective/changeling_team_objective/impersonate_department
explanation_text = "Ensure X derpartment are killed, impersonated, and replaced by Changelings"
explanation_text = "Ensure X department are killed, impersonated, and replaced by Changelings"
var/command_staff_only = FALSE //if this is true, it picks command staff instead
var/list/department_minds = list()
var/list/department_real_names = list()
@@ -823,7 +823,7 @@ GLOBAL_LIST_EMPTY(possible_items_special)
//A subtype of impersonate_derpartment
//A subtype of impersonate_department
//This subtype always picks as many command staff as it can (HoS,HoP,Cap,CE,CMO,RD)
//and tasks the lings with killing and replacing them
/datum/objective/changeling_team_objective/impersonate_department/impersonate_heads
+6 -1
View File
@@ -58,8 +58,13 @@
"<span class='notice'>You climb out of [src]!</span>")
open_machine()
/obj/machinery/sleeper/Exited(atom/movable/user)
if (!state_open && user == occupant)
container_resist(user)
/obj/machinery/sleeper/relaymove(mob/user)
container_resist(user)
if (!state_open)
container_resist(user)
/obj/machinery/sleeper/open_machine()
if(!state_open && !panel_open)
+3 -2
View File
@@ -84,7 +84,6 @@
occupier.adjustToxLoss(-1, 0)
occupier.adjustBruteLoss(-1, 0)
occupier.updatehealth()
occupier.updatehealth()
if(occupier.health >= 0 && occupier.stat == DEAD)
occupier.revive()
return occupier.health < 100
@@ -92,9 +91,11 @@
/obj/machinery/computer/aifixer/process()
if(..())
if(active)
var/oldstat = occupier.stat
active = Fix()
if(oldstat != occupier.stat)
update_icon()
updateDialog()
update_icon()
/obj/machinery/computer/aifixer/Topic(href, href_list)
if(..())
+13 -3
View File
@@ -9,6 +9,7 @@
icon_state = "blueprints"
attack_verb = list("attacked", "bapped", "hit")
var/fluffnotice = "Nobody's gonna read this stuff!"
var/in_use = FALSE
/obj/item/areaeditor/attack_self(mob/user)
add_fingerprint(user)
@@ -25,12 +26,16 @@
/obj/item/areaeditor/Topic(href, href_list)
if(..())
return
return TRUE
if(!usr.canUseTopic(src))
usr << browse(null, "window=blueprints")
return
return TRUE
if(href_list["create_area"])
if(in_use)
return
in_use = TRUE
create_area(usr)
in_use = FALSE
updateUsrDialog()
//Station blueprints!!!
@@ -79,11 +84,16 @@
/obj/item/areaeditor/blueprints/Topic(href, href_list)
..()
if(..())
return
if(href_list["edit_area"])
if(get_area_type()!=AREA_STATION)
return
if(in_use)
return
in_use = TRUE
edit_area()
in_use = FALSE
if(href_list["exit_legend"])
legend = FALSE;
if(href_list["view_legend"])
@@ -28,7 +28,10 @@
disrupt()
/obj/item/device/chameleon/attack_self(mob/user)
toggle(user)
if (isturf(user.loc) || istype(user.loc, /obj/structure) || active_dummy)
toggle(user)
else
to_chat(user, "<span class='userwarning'>You can't use [src] while inside something.</span>")
/obj/item/device/chameleon/afterattack(atom/target, mob/user , proximity)
if(!proximity)
@@ -27,6 +27,18 @@
/obj/item/grenade/chem_grenade/examine(mob/user)
display_timer = (stage == READY && !nadeassembly) //show/hide the timer based on assembly state
..()
if(user.can_see_reagents())
var/count = 0
if(beakers.len)
to_chat(user, "<span class='notice'>You scan the grenade and detect the following reagents:</span>")
for(var/obj/item/reagent_containers/glass/G in beakers)
var/textcount = thtotext(++count)
for(var/datum/reagent/R in G.reagents.reagent_list)
to_chat(user, "<span class='notice'>[R.volume] units of [R.name] in the [textcount] beaker.</span>")
if(beakers.len == 1)
to_chat(user, "<span class='notice'>You detect no second beaker in the grenade.</span>")
else
to_chat(user, "<span class='notice'>You scan the grenade, but detect nothing.</span>")
/obj/item/grenade/chem_grenade/attack_self(mob/user)
+33 -14
View File
@@ -23,8 +23,31 @@
var/detonate_flash_range = 5
var/detonate_fire_range = 5
var/active = FALSE
var/color_val = FALSE
var/datum/weakref/current
/obj/item/hot_potato/Destroy()
if(active)
deactivate()
return ..()
/obj/item/hot_potato/proc/colorize(mob/target)
//Clear color from old target
if(current)
var/mob/M = current.resolve()
if(istype(M))
M.remove_atom_colour(FIXED_COLOUR_PRIORITY)
//Give to new target
current = null
//Swap colors
color_val = !color_val
if(istype(target))
current = WEAKREF(target)
target.add_atom_colour(color_val? "#ffff00" : "#00ffff", FIXED_COLOUR_PRIORITY)
/obj/item/hot_potato/proc/detonate()
var/atom/location = loc
location.visible_message("<span class='userdanger'>[src] [detonate_explosion? "explodes" : "activates"]!</span>", "<span class='userdanger'>[src] activates! You've ran out of time!</span>")
@@ -37,9 +60,6 @@
M.dropItemToGround(src, TRUE)
qdel(src)
/obj/item/hot_potato/proc/is_active()
return isnull(detonation_timerid)? FALSE : TRUE
/obj/item/hot_potato/attack_self(mob/user)
if(activate(timer, user))
user.visible_message("<span class='boldwarning'>[user] squeezes [src], which promptly starts to flash red-hot colors!</span>", "<span class='boldwarning'>You squeeze [src], activating its countdown and attachment mechanism!</span>",
@@ -56,19 +76,18 @@
L.SetSleeping(0)
L.SetUnconscious(0)
L.reagents.add_reagent("muscle_stimulant", CLAMP(5 - L.reagents.get_reagent_amount("muscle_stimulant"), 0, 5)) //If you don't have legs or get bola'd, tough luck!
color_val = !color_val
L.add_atom_colour(color_val? "#ffff00" : "#00ffff", FIXED_COLOUR_PRIORITY)
colorize(L)
/obj/item/hot_potato/examine(mob/user)
. = ..()
if(is_active())
if(active)
to_chat(user, "<span class='warning'>[src] is flashing red-hot! You should probably get rid of it!</span>")
if(show_timer)
to_chat(user, "<span class='warning'>[src]'s timer looks to be at [DisplayTimeText(activation_time - world.time)]!</span>")
/obj/item/hot_potato/equipped(mob/user)
. = ..()
if(is_active())
if(active)
to_chat(user, "<span class='userdanger'>You have a really bad feeling about [src]!</span>")
/obj/item/hot_potato/afterattack(atom/target, mob/user, adjacent, params)
@@ -99,7 +118,7 @@
if(.)
add_logs(user, victim, "forced a hot potato with explosive variables ([detonate_explosion]-[detonate_dev_range]/[detonate_heavy_range]/[detonate_light_range]/[detonate_flash_range]/[detonate_fire_range]) onto")
user.visible_message("<span class='userdanger'>[user] forces [src] onto [victim]!</span>", "<span class='userdanger'>You force [src] onto [victim]!</span>", "<span class='boldwarning'>You hear a mechanical click and a beep.</span>")
user.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
colorize(null)
else
add_logs(user, victim, "tried to force a hot potato with explosive variables ([detonate_explosion]-[detonate_dev_range]/[detonate_heavy_range]/[detonate_light_range]/[detonate_flash_range]/[detonate_fire_range]) onto")
user.visible_message("<span class='boldwarning'>[user] tried to force [src] onto [victim], but it could not attach!</span>", "<span class='boldwarning'>You try to force [src] onto [victim], but it is unable to attach!</span>", "<span class='boldwarning'>You hear a mechanical click and two buzzes.</span>")
@@ -107,10 +126,10 @@
/obj/item/hot_potato/dropped(mob/user)
. = ..()
user.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
colorize(null)
/obj/item/hot_potato/proc/activate(delay, mob/user)
if(is_active())
if(active)
return
update_icon()
if(sticky)
@@ -122,6 +141,7 @@
var/turf/T = get_turf(src)
message_admins("[user? "[ADMIN_LOOKUPFLW(user)] has primed [src]" : "A [src] has been primed"] (Timer:[delay],Explosive:[detonate_explosion],Range:[detonate_dev_range]/[detonate_heavy_range]/[detonate_light_range]/[detonate_fire_range]) for detonation at [COORD(T)]([T.loc])")
log_game("[user? "[user] has primed [src]" : "A [src] has been primed"] ([detonate_dev_range]/[detonate_heavy_range]/[detonate_light_range]/[detonate_fire_range]) for detonation at [COORD(T)]([T.loc])")
active = TRUE
/obj/item/hot_potato/proc/deactivate()
update_icon()
@@ -130,12 +150,11 @@
deltimer(detonation_timerid)
STOP_PROCESSING(SSfastprocess, src)
detonation_timerid = null
if(ismob(loc))
var/mob/user = loc
user.remove_atom_colour(TEMPORARY_COLOUR_PRIORITY)
colorize(null)
active = FALSE
/obj/item/hot_potato/update_icon()
icon_state = is_active()? icon_on : icon_off
icon_state = active? icon_on : icon_off
/obj/item/hot_potato/syndicate
detonate_light_range = 4
+1 -1
View File
@@ -70,7 +70,7 @@
H.sec_hud_set_implants()
if(user)
add_logs(user, target, "implanted", object="[name]")
add_logs(user, target, "implanted", "\a [name]")
return 1
@@ -78,6 +78,15 @@
icon_state = "toolbox_blue_old"
has_latches = FALSE
/obj/item/storage/toolbox/mechanical/old/heirloom
name = "toolbox" //this will be named "X family toolbox"
desc = "It's seen better days."
force = 5
w_class = WEIGHT_CLASS_NORMAL
/obj/item/storage/toolbox/mechanical/old/heirloom/PopulateContents()
return
/obj/item/storage/toolbox/electrical
name = "electrical toolbox"
icon_state = "yellow"
+1 -1
View File
@@ -415,7 +415,7 @@
icon_state = "plasmawindow"
reinf = FALSE
heat_resistance = 25000
armor = list("melee" = 75, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 45, "bio" = 100, "rad" = 100, "fire" = 00, "acid" = 100)
armor = list("melee" = 75, "bullet" = 5, "laser" = 0, "energy" = 0, "bomb" = 45, "bio" = 100, "rad" = 100, "fire" = 99, "acid" = 100)
max_integrity = 150
explosion_block = 1
glass_type = /obj/item/stack/sheet/plasmaglass
-2
View File
@@ -199,8 +199,6 @@
return 0
if(!(lube&SLIDE_ICE))
to_chat(C, "<span class='notice'>You slipped[ O ? " on the [O.name]" : ""]!</span>")
C.log_message("<font color='orange'>Slipped[O ? " on the [O.name]" : ""][(lube&SLIDE)? " (LUBE)" : ""]!</font>", INDIVIDUAL_ATTACK_LOG)
if(!(lube&SLIDE_ICE))
playsound(C.loc, 'sound/misc/slip.ogg', 50, 1, -3)
C.SendSignal(COMSIG_ADD_MOOD_EVENT, "slipped", /datum/mood_event/slipped)
+1 -2
View File
@@ -19,8 +19,7 @@
if(!check_rights())
return
if(!isobserver(usr))
log_game("[key_name(usr)] checked the player panel while in game.")
log_admin("[key_name(usr)] checked the individual player panel for [key_name(M)][isobserver(usr)?"":" while in game"].")
if(!M)
to_chat(usr, "You seem to be selecting a mob that doesn't exist anymore.")
+34 -23
View File
@@ -113,7 +113,7 @@ GLOBAL_PROTECT(protected_ranks)
return ((rank.rights & flag) == flag) //true only if right has everything in flag
//load our rank - > rights associations
/proc/load_admin_ranks(dbfail)
/proc/load_admin_ranks(dbfail, no_update)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>")
return
@@ -137,27 +137,38 @@ GLOBAL_PROTECT(protected_ranks)
prev = next
previous_rights = R.rights
if(!CONFIG_GET(flag/admin_legacy_system) || dbfail)
var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
if(!query_load_admin_ranks.Execute())
message_admins("Error loading admin ranks from database. Loading from backup.")
log_sql("Error loading admin ranks from database. Loading from backup.")
dbfail = 1
else
while(query_load_admin_ranks.NextRow())
var/skip
var/rank_name = query_load_admin_ranks.item[1]
if(CONFIG_GET(flag/load_legacy_ranks_only))
if(!no_update)
var/list/sql_ranks = list()
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == rank_name) //this rank was already loaded from txt override
skip = 1
break
if(!skip)
var/rank_flags = text2num(query_load_admin_ranks.item[2])
var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3])
var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4])
var/datum/admin_rank/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags)
if(!R)
continue
GLOB.admin_ranks += R
var/sql_rank = sanitizeSQL(R.name)
var/sql_flags = sanitizeSQL(R.include_rights)
var/sql_exclude_flags = sanitizeSQL(R.exclude_rights)
var/sql_can_edit_flags = sanitizeSQL(R.can_edit_rights)
sql_ranks += list(list("rank" = "'[sql_rank]'", "flags" = "[sql_flags]", "exclude_flags" = "[sql_exclude_flags]", "can_edit_flags" = "[sql_can_edit_flags]"))
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
else
var/datum/DBQuery/query_load_admin_ranks = SSdbcore.NewQuery("SELECT rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
if(!query_load_admin_ranks.Execute())
message_admins("Error loading admin ranks from database. Loading from backup.")
log_sql("Error loading admin ranks from database. Loading from backup.")
dbfail = 1
else
while(query_load_admin_ranks.NextRow())
var/skip
var/rank_name = query_load_admin_ranks.item[1]
for(var/datum/admin_rank/R in GLOB.admin_ranks)
if(R.name == rank_name) //this rank was already loaded from txt override
skip = 1
break
if(!skip)
var/rank_flags = text2num(query_load_admin_ranks.item[2])
var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3])
var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4])
var/datum/admin_rank/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags)
if(!R)
continue
GLOB.admin_ranks += R
//load ranks from backup file
if(dbfail)
var/backup_file = file("data/admins_backup.json")
@@ -184,7 +195,7 @@ GLOBAL_PROTECT(protected_ranks)
testing(msg)
#endif
/proc/load_admins()
/proc/load_admins(no_update)
var/dbfail
if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect())
message_admins("Failed to connect to database while loading admins. Loading from backup.")
@@ -198,7 +209,7 @@ GLOBAL_PROTECT(protected_ranks)
GLOB.admins.Cut()
GLOB.protected_admins.Cut()
GLOB.deadmins.Cut()
dbfail = load_admin_ranks(dbfail)
dbfail = load_admin_ranks(dbfail, no_update)
//Clear profile access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
+4
View File
@@ -82,6 +82,10 @@
if(D.rank in GLOB.protected_ranks)
to_chat(usr, "<span class='admin prefix'>Editing the flags of this rank is blocked by server configuration.</span>")
return
if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "rank" || task == "permissions"))
to_chat(usr, "<span class='admin prefix'>Database rank loading is disabled, only temporary changes can be made to an admin's rank or permissions.</span>")
use_db = FALSE
skip = TRUE
if(check_rights(R_DBRANKS, FALSE))
if(!skip)
if(!SSdbcore.Connect())
+2 -1
View File
@@ -1,6 +1,7 @@
/datum/admins/proc/player_panel_new()//The new one
if(!check_rights())
return
log_admin("[key_name(usr)] checked the player panel.")
var/dat = "<html><head><title>Player Panel</title></head>"
//javascript, the part that does most of the work~
@@ -307,4 +308,4 @@
</body></html>
"}
usr << browse(dat, "window=players;size=600x480")
usr << browse(dat, "window=players;size=600x480")
+11 -2
View File
@@ -17,6 +17,11 @@
..()
src.bd = bd
/obj/screen/buildmode/Destroy()
bd.buttons -= src
bd = null
return ..()
/obj/screen/buildmode/mode
icon_state = "buildmode1"
name = "Toggle Mode"
@@ -102,8 +107,12 @@
/datum/buildmode/Destroy()
stored = null
for(var/button in buttons)
qdel(button)
QDEL_LIST(buttons)
throw_atom = null
holder = null
preview.Cut()
cornerA = null
cornerB = null
return ..()
/datum/buildmode/proc/create_buttons()
+1 -1
View File
@@ -553,7 +553,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
set category = "Admin"
set name = "Delete"
if(!check_rights(R_ADMIN))
if(!check_rights(R_SPAWN|R_DEBUG))
return
admin_delete(A)
@@ -40,7 +40,7 @@
else if(L.stat)
to_chat(ranged_ability_user, "<span class='neovgre'>\"There is use in shackling the dead, but for examples.\"</span>")
return TRUE
else if(L.handcuffed)
else if (istype(L.handcuffed,/obj/item/restraints/handcuffs/clockwork))
to_chat(ranged_ability_user, "<span class='neovgre'>\"They are already helpless, no?\"</span>")
return TRUE
@@ -49,7 +49,7 @@
"<span class='neovgre_small'>You begin shaping replicant alloy into manacles around [L]'s wrists...</span>")
to_chat(L, "<span class='userdanger'>[ranged_ability_user] begins forming manacles around your wrists!</span>")
if(do_mob(ranged_ability_user, L, 30))
if(!L.handcuffed)
if(!(istype(L.handcuffed,/obj/item/restraints/handcuffs/clockwork)))
L.handcuffed = new/obj/item/restraints/handcuffs/clockwork(L)
L.update_handcuffed()
to_chat(ranged_ability_user, "<span class='neovgre_small'>You shackle [L].</span>")
@@ -184,6 +184,7 @@
important = TRUE
quickbind = TRUE
quickbind_desc = "Returns you to Reebe."
var/client_color
/datum/clockwork_scripture/abscond/check_special_requirements()
if(is_reebe(invoker.z))
@@ -192,6 +193,7 @@
return TRUE
/datum/clockwork_scripture/abscond/recital()
client_color = invoker.client.color
animate(invoker.client, color = "#AF0AAF", time = 50)
. = ..()
@@ -214,11 +216,11 @@
invoker.pulling.forceMove(T)
invoker.forceMove(T)
if(invoker.client)
animate(invoker.client, color = initial(invoker.client.color), time = 25)
animate(invoker.client, color = client_color, time = 25)
/datum/clockwork_scripture/abscond/scripture_fail()
if(invoker && invoker.client)
animate(invoker.client, color = initial(invoker.client.color), time = 10)
animate(invoker.client, color = client_color, time = 10)
//Replicant: Creates a new clockwork slab.
@@ -112,8 +112,12 @@
continue
if(is_servant_of_ratvar(L) || (L.has_trait(TRAIT_BLIND)) || L.anti_magic_check(TRUE, TRUE))
continue
if(L.stat || L.restrained() || L.buckled || L.lying)
if(L.stat || L.lying)
continue
if (iscarbon(L))
var/mob/living/carbon/c = L
if (istype(c.handcuffed,/obj/item/restraints/handcuffs/clockwork))
continue
if(ishostile(L))
var/mob/living/simple_animal/hostile/H = L
if(("ratvar" in H.faction) || (!H.mind && "neutral" in H.faction))
@@ -589,6 +589,7 @@
SEND_SOUND(user, sound('sound/effects/magic.ogg',0,1,25))
else
to_chat(user, "<span class='warning'>The spell will not work on [target]!</span>")
return
..()
//Armor: Gives the target a basic cultist combat loadout
+1 -1
View File
@@ -241,7 +241,7 @@
var/datum/job/sacjob = SSjob.GetJob(sac_objective.target.assigned_role)
var/datum/preferences/sacface = sac_objective.target.current.client.prefs
var/icon/reshape = get_flat_human_icon(null, sacjob, sacface)
var/icon/reshape = get_flat_human_icon(null, sacjob, sacface, list(SOUTH))
reshape.Shift(SOUTH, 4)
reshape.Shift(EAST, 1)
reshape.Crop(7,4,26,31)
@@ -776,7 +776,6 @@
impact_effect_type = /obj/effect/temp_visual/dir_setting/bloodsplatter
/obj/item/projectile/magic/arcane_barrage/blood/Collide(atom/target)
colliding = TRUE
var/turf/T = get_turf(target)
playsound(T, 'sound/effects/splat.ogg', 50, TRUE)
if(iscultist(target))
@@ -790,7 +789,6 @@
M.adjustHealth(-5)
new /obj/effect/temp_visual/cult/sparks(T)
qdel(src)
colliding = FALSE
else
..()
@@ -53,7 +53,7 @@
var/mob/living/simple_animal/revenant/revvie = new(pick(spawn_locs))
revvie.key = selected.key
message_admins("[key_name_admin(revvie)] has been made into a revenant by an event.")
message_admins("[key_name_admin(revvie)] [ADMIN_FLW(revvie)] has been made into a revenant by an event.")
log_game("[key_name(revvie)] was spawned as a revenant by an event.")
spawned_mobs += revvie
return SUCCESSFUL_SPAWN
File diff suppressed because it is too large Load Diff
+4 -4
View File
@@ -266,22 +266,22 @@
return
crit_fail = FALSE
times_used = 0
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
playsound(src, 'sound/items/deconstruct.ogg', 50, 1)
update_icon()
flash.crit_fail = TRUE
flash.update_icon()
return
..()
/obj/item/device/assembly/flash/shield/update_icon(flash = 0)
item_state = "flashshield"
/obj/item/device/assembly/flash/shield/update_icon(flash = FALSE)
icon_state = "flashshield"
item_state = "flashshield"
if(crit_fail)
icon_state = "riot"
item_state = "riot"
else if(flash)
item_state = "flashshield_flash"
icon_state = "flashshield_flash"
item_state = "flashshield_flash"
addtimer(CALLBACK(src, .proc/update_icon), 5)
@@ -8,6 +8,7 @@ What are the archived variables for?
once gases got hot enough, most procedures wouldnt occur due to the fact that the mole counts would get rounded away. Thus, we lowered it a few orders of magnititude */
GLOBAL_LIST_INIT(meta_gas_info, meta_gas_list()) //see ATMOSPHERICS/gas_types.dm
GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(/datum/gas/oxygen, /datum/gas/nitrogen, /datum/gas/carbon_dioxide))) // These gasses cannot react amongst themselves
/proc/init_gaslist_cache()
. = list()
@@ -411,10 +412,18 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
/datum/gas_mixture/react(turf/open/dump_location)
. = NO_REACTION
reaction_results = new
var/list/cached_gases = gases
if(!cached_gases.len)
return
var/possible
for(var/I in cached_gases)
if(GLOB.nonreactive_gases[I])
continue
possible = TRUE
break
if(!possible)
return
reaction_results = new
var/temp = temperature
var/ener = THERMAL_ENERGY(src)
@@ -31,9 +31,6 @@
if(frequency)
radio_connection = SSradio.add_object(src, frequency, RADIO_ATMOSIA)
/obj/machinery/atmospherics/components/trinary/filter/New()
..()
/obj/machinery/atmospherics/components/trinary/filter/Destroy()
SSradio.remove_object(src,frequency)
return ..()
@@ -105,6 +105,8 @@
addMachineryMember(A)
/datum/pipeline/proc/merge(datum/pipeline/E)
if(E == src)
return
air.volume += E.air.volume
members.Add(E.members)
for(var/obj/machinery/atmospherics/pipe/S in E.members)
@@ -79,9 +79,10 @@
parent = P
/obj/machinery/atmospherics/pipe/Destroy()
QDEL_NULL(parent)
releaseAirToTurf()
qdel(air_temporary)
air_temporary = null
QDEL_NULL(air_temporary)
var/turf/T = loc
for(var/obj/machinery/meter/meter in T)
@@ -91,8 +92,6 @@
qdel(meter)
. = ..()
QDEL_NULL(parent)
/obj/machinery/atmospherics/pipe/proc/update_node_icon()
for(var/i in 1 to device_type)
if(nodes[i])
+2 -3
View File
@@ -2,7 +2,7 @@
/obj/machinery/computer/cargo/express
name = "express supply console"
desc = "This console allows the user to purchase a package for double the price,\
desc = "This console allows the user to purchase a package \
with 1/40th of the delivery time: made possible by NanoTrasen's new \"Drop Pod Railgun\".\
All sales are near instantaneous - please choose carefully"
icon_screen = "supply_express"
@@ -75,8 +75,7 @@
data["siliconUser"] = user.has_unlimited_silicon_privilege
data["points"] = SSshuttle.points
data["supplies"] = list()
message = "For normally priced items, please use the standard Supply or Request Console. \
Sales are near-instantaneous - please choose carefully."
message = "Sales are near-instantaneous - please choose carefully."
if(SSshuttle.supplyBlocked)
message = blockade_warning
if(obj_flags & EMAGGED)
+6 -1
View File
@@ -105,4 +105,9 @@
colour = "#ff99ff"
/datum/client_colour/glass_colour/gray
colour = "#cccccc"
colour = "#cccccc"
/datum/client_colour/monochrome
colour = list(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
priority = INFINITY //we can't see colors anyway!
+4 -4
View File
@@ -19,9 +19,9 @@
if(prefs.muted & MUTE_OOC)
to_chat(src, "<span class='danger'>You cannot use OOC (muted).</span>")
return
if(jobban_isbanned(src.mob, "OOC"))
to_chat(src, "<span class='danger'>You have been banned from OOC.</span>")
return
if(jobban_isbanned(src.mob, "OOC"))
to_chat(src, "<span class='danger'>You have been banned from OOC.</span>")
return
msg = copytext(sanitize(msg), 1, MAX_MESSAGE_LEN)
var/raw_msg = msg
@@ -277,7 +277,7 @@ GLOBAL_VAR_INIT(normal_ooc_colour, OOC_COLOR)
set category = "OOC"
set desc ="Ignore a player's messages on the OOC channel"
var/see_ghost_names = isobserver(mob)
var/list/choices = list()
for(var/client/C in GLOB.clients)
@@ -109,7 +109,6 @@
icon_state = "night"
item_state = "glasses"
darkness_view = 8
vision_flags = SEE_BLACKNESS
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
glass_colour_type = /datum/client_colour/glass_colour/green
@@ -468,8 +468,8 @@
/obj/item/device/flightpack/proc/losecontrol(knockdown = FALSE, move = TRUE)
usermessage("Warning: Control system not responding. Deactivating!", "boldwarning")
wearer.visible_message("<span class='warning'>[wearer]'s flight suit abruptly shuts off and they lose control!</span>")
if(wearer)
wearer.visible_message("<span class='warning'>[wearer]'s flight suit abruptly shuts off and they lose control!</span>")
if(move)
while(momentum_x != 0 || momentum_y != 0)
sleep(2)
+10 -4
View File
@@ -52,6 +52,7 @@
/datum/personal_crafting/proc/check_contents(datum/crafting_recipe/R, list/contents)
contents = contents["other"]
main_loop:
for(var/A in R.reqs)
var/needed_amount = R.reqs[A]
@@ -89,25 +90,30 @@
/datum/personal_crafting/proc/get_surroundings(mob/user)
. = list()
.["tool_behaviour"] = list()
.["other"] = list()
for(var/obj/item/I in get_environment(user))
if(I.flags_2 & HOLOGRAM_2)
continue
if(istype(I, /obj/item/stack))
var/obj/item/stack/S = I
.[I.type] += S.amount
.["other"][I.type] += S.amount
else if(I.tool_behaviour)
.["tool_behaviour"] += I.tool_behaviour
else
if(istype(I, /obj/item/reagent_containers))
var/obj/item/reagent_containers/RC = I
if(RC.is_drainable())
for(var/datum/reagent/A in RC.reagents.reagent_list)
.[A.type] += A.volume
.[I.type] += 1
.["other"][A.type] += A.volume
.["other"][I.type] += 1
/datum/personal_crafting/proc/check_tools(mob/user, datum/crafting_recipe/R, list/contents)
if(!R.tools.len)
return TRUE
var/list/possible_tools = list()
var/list/present_qualities = list()
present_qualities |= contents["tool_behaviour"]
for(var/obj/item/I in user.contents)
if(istype(I, /obj/item/storage))
for(var/obj/item/SI in I.contents)
@@ -120,7 +126,7 @@
if(I.tool_behaviour)
present_qualities.Add(I.tool_behaviour)
possible_tools += contents
possible_tools |= contents["other"]
main_loop:
for(var/A in R.tools)
@@ -110,6 +110,8 @@
/obj/item/reagent_containers/food/drinks/proc/smash(atom/target, mob/thrower, ranged = FALSE)
if(!isGlass)
return
if(QDELING(src) || !target) //Invalid loc
return
if(bartender_check(target) && ranged)
return
var/obj/item/broken_bottle/B = new (loc)
@@ -500,6 +500,8 @@
for(var/I in assembly_components)
var/obj/item/integrated_circuit/IC = I
IC.ext_moved(oldLoc, dir)
if(light) //Update lighting objects (From light circuits).
update_light()
/obj/item/device/electronic_assembly/stop_pulling()
for(var/I in assembly_components)
@@ -90,7 +90,7 @@
if(do_tick && !is_running)
is_running = TRUE
tick()
else if(is_running)
else if(!do_tick && is_running)
is_running = FALSE
+31 -15
View File
@@ -278,31 +278,47 @@
qdel(src)
return TRUE
/mob/dead/new_player/proc/IsJobAvailable(rank)
/proc/get_job_unavailable_error_message(retval, jobtitle)
switch(retval)
if(JOB_AVAILABLE)
return "[jobtitle] is available."
if(JOB_UNAVAILABLE_GENERIC)
return "[jobtitle] is unavailable."
if(JOB_UNAVAILABLE_BANNED)
return "You are currently banned from [jobtitle]."
if(JOB_UNAVAILABLE_PLAYTIME)
return "You do not have enough relevant playtime for [jobtitle]."
if(JOB_UNAVAILABLE_ACCOUNTAGE)
return "Your account is not old enough for [jobtitle]."
if(JOB_UNAVAILABLE_SLOTFULL)
return "[jobtitle] is already filled to capacity."
return "Error: Unknown job availability."
/mob/dead/new_player/proc/IsJobUnavailable(rank)
var/datum/job/job = SSjob.GetJob(rank)
if(!job)
return 0
return JOB_UNAVAILABLE_GENERIC
if((job.current_positions >= job.total_positions) && job.total_positions != -1)
if(job.title == "Assistant")
if(isnum(client.player_age) && client.player_age <= 14) //Newbies can always be assistants
return 1
return JOB_AVAILABLE
for(var/datum/job/J in SSjob.occupations)
if(J && J.current_positions < J.total_positions && J.title != job.title)
return 0
return JOB_UNAVAILABLE_SLOTFULL
else
return 0
return JOB_UNAVAILABLE_SLOTFULL
if(jobban_isbanned(src,rank))
return 0
if(!job.player_old_enough(src.client))
return 0
return JOB_UNAVAILABLE_BANNED
if(!job.player_old_enough(client))
return JOB_UNAVAILABLE_ACCOUNTAGE
if(job.required_playtime_remaining(client))
return 0
return 1
return JOB_UNAVAILABLE_PLAYTIME
return JOB_AVAILABLE
/mob/dead/new_player/proc/AttemptLateSpawn(rank)
if(!IsJobAvailable(rank))
alert(src, "[rank] is not available. Please try another.")
var/error = IsJobUnavailable(rank)
if(error != JOB_AVAILABLE)
alert(src, get_job_unavailable_error_message(error, rank))
return FALSE
if(SSticker.late_join_disabled)
@@ -405,7 +421,7 @@
var/available_job_count = 0
for(var/datum/job/job in SSjob.occupations)
if(job && IsJobAvailable(job.title))
if(job && IsJobUnavailable(job.title) == JOB_AVAILABLE)
available_job_count++;
@@ -428,7 +444,7 @@
dat += "<div class='jobs'><div class='jobsColumn'>"
var/job_count = 0
for(var/datum/job/job in SSjob.occupations)
if(job && IsJobAvailable(job.title))
if(job && IsJobUnavailable(job.title) == JOB_AVAILABLE)
job_count++;
if (job_count > round(available_job_count / 2))
dat += "</div><div class='jobsColumn'>"
+2
View File
@@ -20,6 +20,8 @@
//Blood regeneration if there is some space
if(blood_volume < BLOOD_VOLUME_NORMAL)
blood_volume += 0.1 // regenerate blood VERY slowly
if(blood_volume < BLOOD_VOLUME_OKAY)
adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
// Takes care blood loss and regeneration
/mob/living/carbon/human/handle_blood()
+4 -4
View File
@@ -169,9 +169,9 @@
var/turf/start_T = get_turf(loc) //Get the start and target tile for the descriptors
var/turf/end_T = get_turf(target)
if(start_T && end_T)
var/start_T_descriptor = "<font color='#6b5d00'>tile at [start_T.x], [start_T.y], [start_T.z] in area [get_area(start_T)]</font>"
var/end_T_descriptor = "<font color='#6b4400'>tile at [end_T.x], [end_T.y], [end_T.z] in area [get_area(end_T)]</font>"
add_logs(src, throwable_mob, "thrown", addition="from [start_T_descriptor] with the target [end_T_descriptor]")
var/start_T_descriptor = "tile in [get_area_name(start_T, TRUE)] ([start_T.x],[start_T.y],[start_T.z])"
var/end_T_descriptor = "tile at [get_area_name(end_T, TRUE)] ([end_T.x],[end_T.y],[end_T.z])"
add_logs(src, throwable_mob, "thrown", addition="grab from [start_T_descriptor] towards [end_T_descriptor]")
else if(!(I.flags_1 & (NODROP_1|ABSTRACT_1)))
thrown_thing = I
@@ -185,7 +185,7 @@
if(thrown_thing)
visible_message("<span class='danger'>[src] has thrown [thrown_thing].</span>")
add_logs(src, thrown_thing, "has thrown")
add_logs(src, thrown_thing, "thrown")
newtonian_move(get_dir(target, src))
thrown_thing.throw_at(target, thrown_thing.throw_range, thrown_thing.throw_speed, src)
@@ -24,7 +24,7 @@
if(movement_type & FLYING)
return 0
if(!(lube&SLIDE_ICE))
add_logs(src,, "slipped",, "on [O ? O.name : "floor"]")
add_logs(src, (O ? O : get_turf(src)), "slipped on the", null, ((lube & SLIDE) ? "(LUBE)" : null))
return loc.handle_slip(src, knockdown_amount, O, lube)
/mob/living/carbon/Process_Spacemove(movement_dir = 0)
@@ -647,6 +647,9 @@
for(var/X in bodyparts)
var/obj/item/bodypart/LB = X
missing -= LB.body_zone
if(LB.is_pseudopart) //don't show injury text for fake bodyparts; ie chainsaw arms or synthetic armblades
continue
var/limb_max_damage = LB.max_damage
var/status = ""
var/brutedamage = LB.brute_dam
var/burndamage = LB.burn_dam
@@ -663,20 +666,21 @@
else
if(brutedamage > 0)
status = "bruised"
if(brutedamage > 20)
status = "battered"
if(brutedamage > 40)
status = "mangled"
status = LB.light_brute_msg
if(brutedamage > (limb_max_damage*0.4))
status = LB.medium_brute_msg
if(brutedamage > (limb_max_damage*0.8))
status = LB.heavy_brute_msg
if(brutedamage > 0 && burndamage > 0)
status += " and "
if(burndamage > 40)
status += "peeling away"
else if(burndamage > 10)
status += "blistered"
if(burndamage > (limb_max_damage*0.8))
status += LB.heavy_burn_msg
else if(burndamage > (limb_max_damage*0.2))
status += LB.medium_burn_msg
else if(burndamage > 0)
status += "numb"
status += LB.light_burn_msg
if(status == "")
status = "OK"
var/no_damage
+9 -7
View File
@@ -618,23 +618,25 @@
return pick("trails_1", "trails_2")
/mob/living/experience_pressure_difference(pressure_difference, direction, pressure_resistance_prob_delta = 0)
if (client && client.move_delay >= world.time + world.tick_lag*2)
if(buckled)
return
if(client && client.move_delay >= world.time + world.tick_lag*2)
pressure_resistance_prob_delta -= 30
var/list/turfs_to_check = list()
if (has_limbs)
if(has_limbs)
var/turf/T = get_step(src, angle2dir(dir2angle(direction)+90))
if (T)
turfs_to_check += T
T = get_step(src, angle2dir(dir2angle(direction)-90))
if (T)
if(T)
turfs_to_check += T
for (var/t in turfs_to_check)
for(var/t in turfs_to_check)
T = t
if (T.density)
if(T.density)
pressure_resistance_prob_delta -= 20
continue
for (var/atom/movable/AM in T)
@@ -760,10 +762,10 @@
var/list/L = where
if(what == who.get_item_for_held_index(L[2]))
if(who.dropItemToGround(what))
add_logs(src, who, "stripped", addition="of [what]")
add_logs(src, who, "stripped [what] off")
if(what == who.get_item_by_slot(where))
if(who.dropItemToGround(what))
add_logs(src, who, "stripped", addition="of [what]")
add_logs(src, who, "stripped [what] off")
// The src mob is trying to place an item on someone
// Override if a certain mob should be behave differently when placing items (can't, for example)
+3 -2
View File
@@ -171,7 +171,7 @@
if(incapacitated())
return
var/icontype = input("Please, select a display!", "AI", null/*, null*/) in list("Clown", "Monochrome", "Blue", "Inverted", "Firewall", "Green", "Red", "Static", "Red October", "House", "Heartline", "Hades", "Helios", "President", "Syndicat Meow", "Alien", "Too Deep", "Triumvirate", "Triumvirate-M", "Text", "Matrix", "Dorf", "Bliss", "Not Malf", "Fuzzy", "Goon", "Database", "Glitchman", "Murica", "Nanotrasen", "Gentoo", "Angel")
var/icontype = input("Please, select a display!", "AI", null/*, null*/) in list("Clown", "Monochrome", "Blue", "Inverted", "Firewall", "Green", "Red", "Static", "Red October", "House", "Heartline", "Hades", "Helios", "President", "Syndicat Meow", "Alien", "Too Deep", "Triumvirate", "Triumvirate-M", "Text", "Matrix", "Dorf", "Bliss", "Not Malf", "Fuzzy", "Goon", "Database", "Glitchman", "Murica", "Nanotrasen", "Gentoo", "Angel", "TechDemon") //CIT CHANGE - adds 'TechDemon
if(icontype == "Clown")
icon_state = "ai-clown2"
else if(icontype == "Monochrome")
@@ -236,7 +236,8 @@
icon_state = "ai-gentoo"
else if(icontype == "Angel")
icon_state = "ai-angel"
else if(icontype == "TechDemon") //CIT CHANGE - adds 'TechDemon
icon_state = "tech_demon"
/mob/living/silicon/ai/Stat()
..()
if(statpanel("Status"))
@@ -159,6 +159,8 @@
toner = tonermax
diag_hud_set_borgcell()
verbs += /mob/living/proc/lay_down //CITADEL EDIT gimmie rest verb kthx
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
/mob/living/silicon/robot/Destroy()
if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside.
@@ -143,6 +143,7 @@ Difficulty: Very Hard
INVOKE_ASYNC(src, .proc/spiral_shoot, TRUE)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/spiral_shoot(negative = FALSE, counter_start = 8)
var/turf/start_turf = get_step(src, pick(GLOB.alldirs))
var/counter = counter_start
for(var/i in 1 to 80)
if(negative)
@@ -153,7 +154,7 @@ Difficulty: Very Hard
counter = 1
if(counter < 1)
counter = 16
shoot_projectile(null, counter * 22.5)
shoot_projectile(start_turf, counter * 22.5)
playsound(get_turf(src), 'sound/magic/clockwork/invoke_general.ogg', 20, 1)
sleep(1)
@@ -184,7 +185,7 @@ Difficulty: Very Hard
angle_to_target = set_angle
var/static/list/colossus_shotgun_shot_angles = list(12.5, 7.5, 2.5, -2.5, -7.5, -12.5)
for(var/i in colossus_shotgun_shot_angles)
shoot_projectile(null, angle_to_target + i)
shoot_projectile(target_turf, angle_to_target + i)
/mob/living/simple_animal/hostile/megafauna/colossus/proc/dir_shots(list/dirs)
if(!islist(dirs))
@@ -1,3 +1,7 @@
/obj/item/device/modular_computer/attack_self(mob/user)
. = ..()
ui_interact(user)
// Operates TGUI
/obj/item/device/modular_computer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
if(!enabled)
@@ -114,7 +114,7 @@
/obj/singularity/process()
if(current_size >= STAGE_TWO)
move()
radiation_pulse(src, min(5000, (energy*3)+1000), RAD_DISTANCE_COEFFICIENT*0.5)
radiation_pulse(src, min(5000, (energy*4.5)+1000), RAD_DISTANCE_COEFFICIENT*0.5)
if(prob(event_chance))//Chance for it to run a special event TODO:Come up with one or two more that fit
event()
eat()
@@ -254,9 +254,7 @@
current_user.setDir(SOUTH)
if(226 to 315)
current_user.setDir(WEST)
var/difference = abs(lastangle - angle)
if(difference > 350) //Too lazy to properly math, detects 360 --> 0 changes.
difference = (lastangle > 350? ((360 - lastangle) + angle) : ((360 - angle) + lastangle))
var/difference = abs(closer_angle_difference(lastangle, angle))
delay_penalty(difference * aiming_time_increase_angle_multiplier)
lastangle = angle
@@ -292,7 +290,7 @@
current_user = null
if(istype(user))
current_user = user
LAZYADD(current_user.mousemove_intercept_objects, src)
LAZYOR(current_user.mousemove_intercept_objects, src)
mobhook = user.AddComponent(/datum/component/redirect, list(COMSIG_MOVABLE_MOVED), CALLBACK(src, .proc/on_mob_move))
/obj/item/gun/energy/beam_rifle/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
+38 -16
View File
@@ -46,8 +46,6 @@
var/ricochets_max = 2
var/ricochet_chance = 30
var/colliding = FALSE //pause processing..
//Hitscan
var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored.
var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation.
@@ -57,6 +55,15 @@
var/muzzle_type
var/impact_type
//Homing
var/homing = FALSE
var/atom/homing_target
var/homing_turn_speed = 10 //Angle per tick.
var/homing_inaccuracy_min = 0 //in pixels for these. offsets are set once when setting target.
var/homing_inaccuracy_max = 0
var/homing_offset_x = 0
var/homing_offset_y = 0
var/ignore_source_check = FALSE
var/damage = 10
@@ -192,7 +199,6 @@
beam_segments[beam_index] = null
/obj/item/projectile/Collide(atom/A)
colliding = TRUE
var/datum/point/pcache = trajectory.copy_to()
if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max)
ricochets++
@@ -208,7 +214,6 @@
trajectory_ignore_forcemove = TRUE
forceMove(get_turf(A))
trajectory_ignore_forcemove = FALSE
colliding = FALSE
return FALSE
var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
@@ -227,7 +232,6 @@
trajectory_ignore_forcemove = TRUE
forceMove(target_turf)
trajectory_ignore_forcemove = FALSE
colliding = FALSE
return FALSE
var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null
@@ -237,17 +241,14 @@
trajectory_ignore_forcemove = FALSE
if(A)
permutated.Add(A)
colliding = FALSE
return FALSE
else
var/atom/alt = select_target(A)
if(alt)
if(!prehit(alt))
colliding = FALSE
return FALSE
alt.bullet_act(src, def_zone)
qdel(src)
colliding = FALSE
return TRUE
/obj/item/projectile/proc/select_target(atom/A) //Selects another target from a wall if we hit a wall.
@@ -324,7 +325,7 @@
/obj/item/projectile/proc/fire(angle, atom/direct_target)
//If no angle needs to resolve it from xo/yo!
if(!log_override && firer && original)
add_logs(firer, original, "fired at", src, " [get_area(src)]")
add_logs(firer, original, "fired at", src, "from [get_area_name(src, TRUE)]")
if(direct_target)
if(prehit(direct_target))
direct_target.bullet_act(src, def_zone)
@@ -350,7 +351,7 @@
trajectory_ignore_forcemove = TRUE
forceMove(starting)
trajectory_ignore_forcemove = FALSE
trajectory = new(starting.x, starting.y, starting.z, 0, 0, Angle, pixel_speed)
trajectory = new(starting.x, starting.y, starting.z, pixel_x, pixel_y, Angle, pixel_speed)
last_projectile_move = world.time
fired = TRUE
if(hitscan)
@@ -417,6 +418,8 @@
var/matrix/M = new
M.Turn(Angle)
transform = M
if(homing)
process_homing()
trajectory.increment(trajectory_multiplier)
var/turf/T = trajectory.return_turf()
if(!istype(T))
@@ -445,10 +448,32 @@
Collide(original)
Range()
/obj/item/projectile/proc/process_homing() //may need speeding up in the future performance wise.
if(!homing_target)
return FALSE
var/datum/point/PT = RETURN_PRECISE_POINT(homing_target)
PT.x += CLAMP(homing_offset_x, 1, world.maxx)
PT.y += CLAMP(homing_offset_y, 1, world.maxy)
var/angle = closer_angle_difference(Angle, angle_between_points(RETURN_PRECISE_POINT(src), PT))
setAngle(Angle + CLAMP(angle, -homing_turn_speed, homing_turn_speed))
/obj/item/projectile/proc/set_homing_target(atom/A)
if(!A || (!isturf(A) && !isturf(A.loc)))
return FALSE
homing = TRUE
homing_target = A
homing_offset_x = rand(homing_inaccuracy_min, homing_inaccuracy_max)
homing_offset_y = rand(homing_inaccuracy_min, homing_inaccuracy_max)
if(prob(50))
homing_offset_x = -homing_offset_x
if(prob(50))
homing_offset_y = -homing_offset_y
//Returns true if the target atom is on our current turf and above the right layer
/obj/item/projectile/proc/can_hit_target(atom/target, var/list/passthrough)
return (target && ((target.layer >= PROJECTILE_HIT_THRESHHOLD_LAYER) || ismob(target)) && (loc == get_turf(target)) && (!(target in passthrough)))
//Spread is FORCED!
/obj/item/projectile/proc/preparePixelProjectile(atom/target, atom/source, params, spread = 0)
var/turf/curloc = get_turf(source)
var/turf/targloc = get_turf(target)
@@ -460,7 +485,7 @@
if(targloc || !params)
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc))
setAngle(Get_Angle(src, targloc) + spread)
//CIT CHANGES START HERE - makes it so laying down makes you unable to shoot through most objects
if(iscarbon(source))
@@ -474,14 +499,11 @@
p_x = calculated[2]
p_y = calculated[3]
if(spread)
setAngle(calculated[1] + spread)
else
setAngle(calculated[1])
setAngle(calculated[1] + spread)
else if(targloc)
yo = targloc.y - curloc.y
xo = targloc.x - curloc.x
setAngle(Get_Angle(src, targloc))
setAngle(Get_Angle(src, targloc) + spread)
else
stack_trace("WARNING: Projectile [type] fired without either mouse parameters, or a target atom to aim at!")
qdel(src)
+4 -3
View File
@@ -159,11 +159,12 @@
new_mob.invisibility = 0
new_mob.job = "Cyborg"
var/mob/living/silicon/robot/Robot = new_mob
Robot.lawupdate = FALSE
Robot.connected_ai = null
Robot.mmi.transfer_identity(M) //Does not transfer key/client.
Robot.clear_inherent_laws(0)
Robot.clear_zeroth_law(0, 0)
Robot.connected_ai = null
Robot.clear_zeroth_law(0)
if("slime")
new_mob = new /mob/living/simple_animal/slime/random(M.loc)
@@ -0,0 +1,6 @@
/obj/item/projectile/spellcard
name = "enchanted card"
desc = "A piece of paper enchanted to give it extreme durability and stiffness, along with a very hot burn to anyone unfortunate enough to get hit by a charged one."
icon_state = "spellcard"
damage_type = BURN
damage = 2
@@ -14,11 +14,12 @@
var/obj/item/stock_parts/cell/cell
var/powerefficiency = 0.1
var/amount = 30
var/recharged = 0
var/recharge_delay = 5
var/recharge_amount = 10
var/recharge_counter = 0
var/mutable_appearance/beaker_overlay
var/working_state = "dispenser_working"
var/nopower_state = "dispenser_nopower"
var/has_panel_overlay = TRUE
var/macrotier = 1
var/obj/item/reagent_containers/beaker = null
var/list/dispensable_reagents = list(
@@ -62,7 +63,6 @@
/obj/machinery/chem_dispenser/Initialize()
. = ..()
cell = new cell_type
recharge()
dispensable_reagents = sortList(dispensable_reagents)
update_icon()
@@ -71,12 +71,21 @@
QDEL_NULL(cell)
return ..()
/obj/machinery/chem_dispenser/examine(mob/user)
..()
if(panel_open)
to_chat(user, "<span class='notice'>[src]'s maintenance hatch is open!</span>")
/obj/machinery/chem_dispenser/process()
if(recharged < 0)
recharge()
recharged = recharge_delay
else
recharged -= 1
if (recharge_counter >= 4)
if(!is_operational())
return
var/usedpower = cell.give(recharge_amount)
if(usedpower)
use_power(250*recharge_amount)
recharge_counter = 0
return
recharge_counter++
/obj/machinery/chem_dispenser/proc/display_beaker()
..()
@@ -91,23 +100,18 @@ obj/machinery/chem_dispenser/proc/work_animation()
/obj/machinery/chem_dispenser/power_change()
..()
if(!powered() && nopower_state)
icon_state = nopower_state
else
icon_state = initial(icon_state)
icon_state = "[(nopower_state && !powered()) ? nopower_state : initial(icon_state)]"
obj/machinery/chem_dispenser/update_icon()
/obj/machinery/chem_dispenser/update_icon()
cut_overlays()
if(has_panel_overlay && panel_open)
add_overlay(mutable_appearance(icon, "[initial(icon_state)]_panel-o"))
if(beaker)
beaker_overlay = display_beaker()
add_overlay(beaker_overlay)
/obj/machinery/chem_dispenser/proc/recharge()
if(stat & (BROKEN|NOPOWER))
return
var/usedpower = cell.give( 1 / powerefficiency) //Should always be a gain of one on the UI.
if(usedpower)
use_power(2500)
/obj/machinery/chem_dispenser/emag_act(mob/user)
if(obj_flags & EMAGGED)
@@ -188,23 +192,32 @@ obj/machinery/chem_dispenser/update_icon()
return
switch(action)
if("amount")
if(!is_operational())
return
var/target = text2num(params["target"])
if(target in beaker.possible_transfer_amounts)
amount = target
work_animation()
. = TRUE
if("dispense")
if(!is_operational() || QDELETED(cell))
return
var/reagent = params["reagent"]
if(beaker && dispensable_reagents.Find(reagent))
var/datum/reagents/R = beaker.reagents
var/free = R.maximum_volume - R.total_volume
var/actual = min(amount, (cell.charge * powerefficiency)*10, free)
if(!cell.use(actual / powerefficiency))
say("Not enough energy to complete operation!")
return
R.add_reagent(reagent, actual)
cell.use(actual / powerefficiency)
work_animation()
. = TRUE
if("remove")
if(!is_operational())
return
var/amount = text2num(params["amount"])
if(beaker && amount in beaker.possible_transfer_amounts)
beaker.reagents.remove_all(amount)
@@ -219,6 +232,8 @@ obj/machinery/chem_dispenser/update_icon()
update_icon()
. = TRUE
if("dispense_recipe")
if(!is_operational() || QDELETED(cell))
return
var/recipe_to_use = params["recipe"]
var/list/chemicals_to_dispense = process_recipe_list(recipe_to_use)
var/res = get_macro_resolution()
@@ -230,16 +245,24 @@ obj/machinery/chem_dispenser/update_icon()
var/free = R.maximum_volume - R.total_volume
var/actual = min(round(chemicals_to_dispense[key], res), (cell.charge * powerefficiency)*10, free)
if(actual)
if(!cell.use(actual / powerefficiency))
say("Not enough energy to complete operation!")
return
R.add_reagent(r_id, actual)
cell.use(actual / powerefficiency)
work_animation()
if("clear_recipes")
if(!is_operational())
return
var/yesno = alert("Clear all recipes?",, "Yes","No")
if(yesno == "Yes")
saved_recipes = list()
if("add_recipe")
if(!is_operational())
return
var/name = stripped_input(usr,"Name","What do you want to name this recipe?", "Recipe", MAX_NAME_LEN)
var/recipe = stripped_input(usr,"Recipe","Insert recipe with chem IDs")
if(!usr.canUseTopic(src, !issilicon(usr)))
return
if(name && recipe)
var/list/first_process = splittext(recipe, ";")
if(!LAZYLEN(first_process))
@@ -265,7 +288,8 @@ obj/machinery/chem_dispenser/update_icon()
/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
if(default_unfasten_wrench(user, I))
return
if(default_deconstruction_screwdriver(user, "dispenser-o", "dispenser", I))
if(default_deconstruction_screwdriver(user, icon_state, icon_state, I))
update_icon()
return
if(exchange_parts(user, I))
@@ -313,15 +337,14 @@ obj/machinery/chem_dispenser/update_icon()
/obj/machinery/chem_dispenser/RefreshParts()
var/time = 0
recharge_amount = initial(recharge_amount)
var/newpowereff = 0.0666666
for(var/obj/item/stock_parts/cell/P in component_parts)
cell = P
for(var/obj/item/stock_parts/matter_bin/M in component_parts)
newpowereff += 0.0166666666*M.rating
for(var/obj/item/stock_parts/capacitor/C in component_parts)
time += C.rating
recharge_delay = 30/(time/2) //delay between recharges, double the usual time on lowest 50% less than usual on highest
recharge_amount *= C.rating
for(var/obj/item/stock_parts/manipulator/M in component_parts)
if (M.rating > macrotier)
macrotier = M.rating
@@ -386,6 +409,7 @@ obj/machinery/chem_dispenser/update_icon()
anchored = TRUE
icon = 'icons/obj/chemical.dmi'
icon_state = "soda_dispenser"
has_panel_overlay = FALSE
amount = 10
pixel_y = 6
layer = WALL_OBJ_LAYER
@@ -73,12 +73,6 @@
if (prob(50))
qdel(src)
/obj/machinery/chem_master/power_change()
if(powered())
stat &= ~NOPOWER
else
stat |= NOPOWER
/obj/machinery/chem_master/attackby(obj/item/I, mob/user, params)
if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", I))
return
@@ -223,7 +217,7 @@
return
vol_each = min(reagents.total_volume / amount, 50)
var/name = stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/pill/P
@@ -237,7 +231,7 @@
reagents.trans_to(P,vol_each)
else
var/name = stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/food/condiment/pack/P = new/obj/item/reagent_containers/food/condiment/pack(drop_location())
@@ -259,7 +253,7 @@
return
vol_each = min(reagents.total_volume / amount, 40)
var/name = stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/pill/P
@@ -277,7 +271,7 @@
if(condi)
var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/food/condiment/P = new(drop_location())
P.originalname = name
@@ -290,7 +284,7 @@
amount_full = round(reagents.total_volume / 30)
vol_part = reagents.total_volume % 30
var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/glass/bottle/P
@@ -615,7 +615,19 @@
to_chat(M, "<span class = 'warning'>Your eyes sting!</span>")
M.blind_eyes(2)
/datum/reagent/consumable/nutriment/stabilized
name = "Stabilized Nutriment"
id = "stabilizednutriment"
description = "A bioengineered protien-nutrient structure designed to decompose in high saturation. In layman's terms, it won't get you fat."
reagent_state = SOLID
nutriment_factor = 15 * REAGENTS_METABOLISM
color = "#664330" // rgb: 102, 67, 48
/datum/reagent/consumable/nutriment/stabilized/on_mob_life(mob/living/M)
if(M.nutrition > NUTRITION_LEVEL_FULL - 25)
M.nutrition -= 3*nutriment_factor
..()
////Lavaland Flora Reagents////
@@ -673,4 +685,4 @@
description = "The sorrow and melancholy of a thousand bereaved clowns, forever denied their Honkmechs."
nutriment_factor = 5 * REAGENTS_METABOLISM
color = "#eef442" // rgb: 238, 244, 66
taste_description = "mournful honking"
taste_description = "mournful honking"
+7 -3
View File
@@ -17,13 +17,17 @@ If you create T5+ please take a pass at gene_modder.dm [L40]. Max_values MUST fi
display_contents_with_number = 1
max_w_class = WEIGHT_CLASS_NORMAL
max_combined_w_class = 100
can_hold = list(
/obj/item/reagent_containers/glass/beaker,
/obj/item/device/assembly/igniter,
/obj/item/stock_parts,
/obj/item/stack/ore/bluespace_crystal)
var/works_from_distance = 0
var/pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/rped.ogg'
var/alt_sound = null
/obj/item/storage/part_replacer/can_be_inserted(obj/item/W, stop_messages = 0, mob/user)
return ..() && W.get_part_rating()
/obj/item/storage/part_replacer/afterattack(obj/machinery/T, mob/living/carbon/human/user, flag, params)
if(flag)
return
@@ -82,15 +82,13 @@
var/mob/living/carbon/human/H = mover
if(H.nutrition >= NUTRITION_LEVEL_FAT)
H.visible_message("<span class='warning'>[H] pushes through [src]!</span>", "<span class='notice'>You've seen and eaten worse than this.</span>")
return 1
return TRUE
else
to_chat(H, "<span class='warning'>You're repulsed by even looking at [src]. Only a pig could force themselves to go through it.</span>")
if(istype(mover, /mob/living/simple_animal/hostile/morph))
return 1
return TRUE
else
return 0
return FALSE
/obj/structure/mirror/magic/pride //Pride's mirror: Used in the Pride ruin.
name = "pride's mirror"
+77 -7
View File
@@ -9,6 +9,7 @@
var/list/projectile_var_overrides = list()
var/projectile_amount = 1 //Projectiles per cast.
var/current_amount = 0 //How many projectiles left.
var/projectiles_per_fire = 1 //Projectiles per fire. Probably not a good thing to use unless you override ready_projectile().
/obj/effect/proc_holder/spell/aimed/Click()
var/mob/living/user = usr
@@ -26,10 +27,18 @@
charge_counter = charge_max * refund_percent
start_recharge()
remove_ranged_ability(msg)
on_deactivation(user)
else
msg = "<span class='notice'>[active_msg]<B>Left-click to shoot it at a target!</B></span>"
current_amount = projectile_amount
add_ranged_ability(user, msg, TRUE)
on_activation(user)
/obj/effect/proc_holder/spell/aimed/proc/on_activation(mob/user)
return
/obj/effect/proc_holder/spell/aimed/proc/on_deactivation(mob/user)
return
/obj/effect/proc_holder/spell/aimed/update_icon()
if(!action)
@@ -60,19 +69,25 @@
remove_ranged_ability() //Auto-disable the ability once you run out of bullets.
charge_counter = 0
start_recharge()
on_deactivation(user)
return TRUE
/obj/effect/proc_holder/spell/aimed/proc/fire_projectile(mob/living/user, atom/target)
current_amount--
var/obj/item/projectile/P = new projectile_type(user.loc)
P.firer = user
P.preparePixelProjectile(target, user)
for(var/V in projectile_var_overrides)
if(P.vars[V])
P.vv_edit_var(V, projectile_var_overrides[V])
P.fire()
for(var/i in 1 to projectiles_per_fire)
var/obj/item/projectile/P = new projectile_type(user.loc)
P.firer = user
P.preparePixelProjectile(target, user)
for(var/V in projectile_var_overrides)
if(P.vars[V])
P.vv_edit_var(V, projectile_var_overrides[V])
ready_projectile(P, target, user, i)
P.fire()
return TRUE
/obj/effect/proc_holder/spell/aimed/proc/ready_projectile(obj/item/projectile/P, atom/target, mob/user, iteration)
return
/obj/effect/proc_holder/spell/aimed/lightningbolt
name = "Lightning Bolt"
desc = "Fire a high powered lightning bolt at your foes!"
@@ -108,3 +123,58 @@
active_msg = "You prepare to cast your fireball spell!"
deactive_msg = "You extinguish your fireball... for now."
active = FALSE
/obj/effect/proc_holder/spell/aimed/spell_cards
name = "Spell Cards"
desc = "Blazing hot rapid-fire homing cards. Banish your foes with its mystical power!"
school = "evocation"
charge_max = 50
clothes_req = 0
invocation = "Sigi'lu M'Fan 'Tasia"
invocation_type = "shout"
range = 40
cooldown_min = 10
projectile_amount = 5
projectiles_per_fire = 7
projectile_type = /obj/item/projectile/spellcard
var/datum/weakref/current_target_weakref
var/projectile_turnrate = 10
var/projectile_pixel_homing_spread = 32
var/projectile_initial_spread_amount = 30
var/projectile_location_spread_amount = 12
var/datum/component/lockon_aiming/lockon_component
ranged_clickcd_override = 1
/obj/effect/proc_holder/spell/aimed/spell_cards/on_activation(mob/M)
QDEL_NULL(lockon_component)
lockon_component = M.AddComponent(/datum/component/lockon_aiming, 5, typecacheof(list(/mob/living)), 1, null, CALLBACK(src, .proc/on_lockon_component))
/obj/effect/proc_holder/spell/aimed/spell_cards/proc/on_lockon_component(list/locked_weakrefs)
if(!length(locked_weakrefs))
current_target_weakref = null
return
current_target_weakref = locked_weakrefs[1]
var/atom/A = current_target_weakref.resolve()
if(A)
var/mob/M = lockon_component.parent
M.face_atom(A)
/obj/effect/proc_holder/spell/aimed/spell_cards/on_deactivation(mob/M)
QDEL_NULL(lockon_component)
/obj/effect/proc_holder/spell/aimed/spell_cards/ready_projectile(obj/item/projectile/P, atom/target, mob/user, iteration)
if(current_target_weakref)
var/atom/A = current_target_weakref.resolve()
if(A && get_dist(A, user) < 7)
P.homing_turn_speed = projectile_turnrate
P.homing_inaccuracy_min = projectile_pixel_homing_spread
P.homing_inaccuracy_max = projectile_pixel_homing_spread
P.set_homing_target(current_target_weakref.resolve())
var/rand_spr = rand()
var/total_angle = projectile_initial_spread_amount * 2
var/adjusted_angle = total_angle - ((projectile_initial_spread_amount / projectiles_per_fire) * 0.5)
var/one_fire_angle = adjusted_angle / projectiles_per_fire
var/current_angle = iteration * one_fire_angle * rand_spr - (projectile_initial_spread_amount / 2)
P.pixel_x = rand(-projectile_location_spread_amount, projectile_location_spread_amount)
P.pixel_y = rand(-projectile_location_spread_amount, projectile_location_spread_amount)
P.preparePixelProjectile(target, user, null, current_angle)
@@ -43,6 +43,15 @@
var/species_flags_list = list()
var/dmg_overlay_type //the type of damage overlay (if any) to use when this bodypart is bruised/burned.
//Damage messages used by help_shake_act()
var/light_brute_msg = "bruised"
var/medium_brute_msg = "battered"
var/heavy_brute_msg = "mangled"
var/light_burn_msg = "numb"
var/medium_burn_msg = "blistered"
var/heavy_burn_msg = "peeling away"
/obj/item/bodypart/examine(mob/user)
..()
if(brute_dam > 0)
@@ -1,4 +1,10 @@
#define ROBOTIC_LIGHT_BRUTE_MSG "marred"
#define ROBOTIC_MEDIUM_BRUTE_MSG "dented"
#define ROBOTIC_HEAVY_BRUTE_MSG "falling apart"
#define ROBOTIC_LIGHT_BURN_MSG "scorched"
#define ROBOTIC_MEDIUM_BURN_MSG "charred"
#define ROBOTIC_HEAVY_BURN_MSG "smoldering"
/obj/item/bodypart/l_arm/robot
name = "cyborg left arm"
@@ -10,6 +16,13 @@
icon_state = "borg_l_arm"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
/obj/item/bodypart/r_arm/robot
name = "cyborg right arm"
@@ -21,6 +34,13 @@
icon_state = "borg_r_arm"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
/obj/item/bodypart/l_leg/robot
name = "cyborg left leg"
@@ -32,6 +52,13 @@
icon_state = "borg_l_leg"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
/obj/item/bodypart/r_leg/robot
name = "cyborg right leg"
@@ -43,6 +70,13 @@
icon_state = "borg_r_leg"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
/obj/item/bodypart/chest/robot
name = "cyborg torso"
@@ -52,6 +86,15 @@
flags_1 = CONDUCT_1
icon_state = "borg_chest"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
var/wired = 0
var/obj/item/stock_parts/cell/cell = null
@@ -102,6 +145,15 @@
flags_1 = CONDUCT_1
icon_state = "borg_head"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
var/obj/item/device/assembly/flash/handheld/flash1 = null
var/obj/item/device/assembly/flash/handheld/flash2 = null
@@ -189,3 +241,12 @@
icon = 'icons/mob/augmentation/surplus_augments.dmi'
icon_state = "r_leg"
max_damage = 20
#undef ROBOTIC_LIGHT_BRUTE_MSG
#undef ROBOTIC_MEDIUM_BRUTE_MSG
#undef ROBOTIC_HEAVY_BRUTE_MSG
#undef ROBOTIC_LIGHT_BURN_MSG
#undef ROBOTIC_MEDIUM_BURN_MSG
#undef ROBOTIC_HEAVY_BURN_MSG
+1 -1
View File
@@ -68,7 +68,7 @@
user.visible_message("[user] drapes [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].", \
"<span class='notice'>You drape [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].</span>")
add_logs(user, M, "operated", addition="Operation type: [procedure.name], location: [selected_zone]")
add_logs(user, M, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
else
to_chat(user, "<span class='warning'>You need to expose [M]'s [parse_zone(selected_zone)] first!</span>")
-1
View File
@@ -46,7 +46,6 @@
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
actions_types = list(/datum/action/item_action/organ_action/use)
sight_flags = SEE_BLACKNESS
var/night_vision = TRUE
/obj/item/organ/eyes/night_vision/ui_action_click()