Merge branch 'master' into upstream-merge-36442

This commit is contained in:
LetterJay
2018-04-01 19:50:37 -05:00
committed by GitHub
132 changed files with 1560 additions and 536 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
+4 -2
View File
@@ -10,9 +10,11 @@
*/
#define LAZYINITLIST(L) if (!L) L = list()
#define UNSETEMPTY(L) if (L && !L.len) L = null
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!L.len) { L = null; } }
#define UNSETEMPTY(L) if (L && !length(L)) L = null
#define LAZYREMOVE(L, I) if(L) { L -= I; if(!length(L)) { L = null; } }
#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 <= 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)
-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)
-98
View File
@@ -1,98 +0,0 @@
/client
var/list/atom/selected_target[2]
var/obj/item/active_mousedown_item = null
var/mouseParams = ""
var/mouseLocation = null
var/mouseObject = null
var/mouseControlObject = null
/client/MouseDown(object, location, control, params)
var/delay = mob.CanMobAutoclick(object, location, params)
if(delay)
selected_target[1] = object
selected_target[2] = params
while(selected_target[1])
Click(selected_target[1], location, control, selected_target[2])
sleep(delay)
active_mousedown_item = mob.canMobMousedown(object, location, params)
if(active_mousedown_item)
active_mousedown_item.onMouseDown(object, location, params, mob)
/client/MouseUp(object, location, control, params)
selected_target[1] = null
if(active_mousedown_item)
active_mousedown_item.onMouseUp(object, location, params, mob)
active_mousedown_item = null
/mob/proc/CanMobAutoclick(object, location, params)
/mob/living/carbon/CanMobAutoclick(atom/object, location, params)
if(!object.IsAutoclickable())
return
var/obj/item/h = get_active_held_item()
if(h)
. = h.CanItemAutoclick(object, location, params)
/mob/proc/canMobMousedown(object, location, params)
/mob/living/carbon/canMobMousedown(atom/object, location, params)
var/obj/item/H = get_active_held_item()
if(H)
. = H.canItemMouseDown(object, location, params)
/obj/item/proc/CanItemAutoclick(object, location, params)
/obj/item/proc/canItemMouseDown(object, location, params)
if(canMouseDown)
return src
/obj/item/proc/onMouseDown(object, location, params, mob)
return
/obj/item/proc/onMouseUp(object, location, params, mob)
return
/obj/item
var/canMouseDown = FALSE
/obj/item/gun
var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds
/obj/item/gun/CanItemAutoclick(object, location, params)
. = automatic
/atom/proc/IsAutoclickable()
. = 1
/obj/screen/IsAutoclickable()
. = 0
/obj/screen/click_catcher/IsAutoclickable()
. = 1
//Please don't roast me too hard
/client/MouseMove(object,location,control,params)
mouseParams = params
mouseLocation = location
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)
/obj/item/proc/onMouseMove(object, location, control, params)
return
/client/MouseDrag(src_object,atom/over_object,src_location,over_location,src_control,over_control,params)
mouseParams = params
mouseLocation = over_location
mouseObject = over_object
mouseControlObject = over_control
if(selected_target[1] && over_object && over_object.IsAutoclickable())
selected_target[1] = over_object
selected_target[2] = params
if(active_mousedown_item)
active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
return
+117
View File
@@ -19,3 +19,120 @@
// recieve a mousedrop
/atom/proc/MouseDrop_T(atom/dropping, mob/user)
return
/client
var/list/atom/selected_target[2]
var/obj/item/active_mousedown_item = null
var/mouseParams = ""
var/mouseLocation = null
var/mouseObject = null
var/mouseControlObject = null
var/middragtime = 0
var/atom/middragatom
/client/MouseDown(object, location, control, params)
var/delay = mob.CanMobAutoclick(object, location, params)
if(delay)
selected_target[1] = object
selected_target[2] = params
while(selected_target[1])
Click(selected_target[1], location, control, selected_target[2])
sleep(delay)
active_mousedown_item = mob.canMobMousedown(object, location, params)
if(active_mousedown_item)
active_mousedown_item.onMouseDown(object, location, params, mob)
/client/MouseUp(object, location, control, params)
selected_target[1] = null
if(active_mousedown_item)
active_mousedown_item.onMouseUp(object, location, params, mob)
active_mousedown_item = null
/mob/proc/CanMobAutoclick(object, location, params)
/mob/living/carbon/CanMobAutoclick(atom/object, location, params)
if(!object.IsAutoclickable())
return
var/obj/item/h = get_active_held_item()
if(h)
. = h.CanItemAutoclick(object, location, params)
/mob/proc/canMobMousedown(object, location, params)
/mob/living/carbon/canMobMousedown(atom/object, location, params)
var/obj/item/H = get_active_held_item()
if(H)
. = H.canItemMouseDown(object, location, params)
/obj/item/proc/CanItemAutoclick(object, location, params)
/obj/item/proc/canItemMouseDown(object, location, params)
if(canMouseDown)
return src
/obj/item/proc/onMouseDown(object, location, params, mob)
return
/obj/item/proc/onMouseUp(object, location, params, mob)
return
/obj/item
var/canMouseDown = FALSE
/obj/item/gun
var/automatic = 0 //can gun use it, 0 is no, anything above 0 is the delay between clicks in ds
/obj/item/gun/CanItemAutoclick(object, location, params)
. = automatic
/atom/proc/IsAutoclickable()
. = 1
/obj/screen/IsAutoclickable()
. = 0
/obj/screen/click_catcher/IsAutoclickable()
. = 1
//Please don't roast me too hard
/client/MouseMove(object,location,control,params)
mouseParams = params
mouseLocation = location
mouseObject = object
mouseControlObject = control
if(mob && LAZYLEN(mob.mousemove_intercept_objects))
for(var/datum/D in mob.mousemove_intercept_objects)
D.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)
var/list/L = params2list(params)
if (L["middle"])
if (src_object && src_location != over_location)
middragtime = world.time
middragatom = src_object
else
middragtime = 0
middragatom = null
mouseParams = params
mouseLocation = over_location
mouseObject = over_object
mouseControlObject = over_control
if(selected_target[1] && over_object && over_object.IsAutoclickable())
selected_target[1] = over_object
selected_target[2] = params
if(active_mousedown_item)
active_mousedown_item.onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
/obj/item/proc/onMouseDrag(src_object, over_object, src_location, over_location, params, mob)
return
/client/MouseDrop(src_object, over_object, src_location, over_location, src_control, over_control, params)
if (middragatom == src_object)
middragtime = 0
middragatom = null
..()
@@ -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
@@ -341,6 +344,14 @@
config_entry_value = null
min_val = 0
/datum/config_entry/number/minute_click_limit
config_entry_value = 400
min_val = 0
/datum/config_entry/number/second_click_limit
config_entry_value = 15
min_val = 0
/datum/config_entry/number/error_cooldown // The "cooldown" time for each occurrence of a unique error
config_entry_value = 600
min_val = 0
+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(..())
+16
View File
@@ -64,6 +64,22 @@
material = WOOD
var/drop_amount = 3
/obj/structure/barricade/wooden/attackby(obj/item/I, mob/user)
if(istype(I,/obj/item/stack/sheet/mineral/wood))
var/obj/item/stack/sheet/mineral/wood/W = I
if(W.amount < 5)
to_chat(user, "<span class='warning'>You need at least five wooden planks to make a wall!</span>")
return
else
to_chat(user, "<span class='notice'>You start adding [I] to [src]...</span>")
if(do_after(user, 50, target=src))
W.use(5)
new /turf/closed/wall/mineral/wood/nonmetal(get_turf(src))
qdel(src)
return
return ..()
/obj/structure/barricade/wooden/crude
name = "crude plank barricade"
desc = "This space is blocked off by a crude assortment of planks."
+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
@@ -61,6 +61,7 @@
icon = 'icons/obj/telescience.dmi'
icon_state = "polycrystal"
item_state = "sheet-polycrystal"
singular_name = "bluespace polycrystal"
desc = "A stable polycrystal, made of fused-together bluespace crystals. You could probably break one off."
materials = list(MAT_BLUESPACE=MINERAL_MATERIAL_AMOUNT)
attack_verb = list("bluespace polybashed", "bluespace polybattered", "bluespace polybludgeoned", "bluespace polythrashed", "bluespace polysmashed")
@@ -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)
@@ -130,7 +130,23 @@
sheet_type = /obj/item/stack/sheet/mineral/wood
hardness = 70
explosion_block = 0
canSmoothWith = list(/turf/closed/wall/mineral/wood, /obj/structure/falsewall/wood)
canSmoothWith = list(/turf/closed/wall/mineral/wood, /obj/structure/falsewall/wood, /turf/closed/wall/mineral/wood/nonmetal)
/turf/closed/wall/mineral/wood/attackby(obj/item/W, mob/user)
var/duration = (48/W.force) * 2 //In seconds, for now.
if(W.sharpness)
if(istype(W, /obj/item/hatchet) || istype(W, /obj/item/twohanded/fireaxe))
duration /= 4 //Much better with hatchets and axes.
if(do_after(user, duration*10, target=src)) //Into deciseconds.
dismantle_wall(FALSE,FALSE)
return
return ..()
/turf/closed/wall/mineral/wood/nonmetal
desc = "A solidly wooden wall. It's a bit weaker than a wall made with metal."
girder_type = /obj/structure/barricade/wooden
hardness = 50
canSmoothWith = list(/turf/closed/wall/mineral/wood, /obj/structure/falsewall/wood, /turf/closed/wall/mineral/wood/nonmetal)
/turf/closed/wall/mineral/iron
name = "rough metal wall"
@@ -250,7 +266,7 @@
/turf/closed/wall/mineral/plastitanium/overspace
icon_state = "map-overspace"
fixed_underlay = list("space"=1)
/turf/closed/wall/mineral/plastitanium/explosive/ex_act(severity)
var/datum/explosion/acted_explosion = null
for(var/datum/explosion/E in GLOB.explosions)
+1 -1
View File
@@ -2,7 +2,7 @@
name = "water"
desc = "Shallow water."
icon = 'icons/turf/floors.dmi'
icon_state = "riverwater"
icon_state = "riverwater_motion"
baseturfs = /turf/open/chasm/lavaland
initial_gas_mix = LAVALAND_DEFAULT_ATMOS
planetary_atmos = TRUE
+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)
@@ -692,7 +692,6 @@ Congratulations! You are now trained for invasive xenobiology research!"}
icon_state = "alien_frame"
framestack = /obj/item/stack/sheet/mineral/abductor
framestackamount = 1
density = TRUE
/obj/structure/table_frame/abductor/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/wrench))
@@ -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
..()
@@ -58,6 +58,7 @@
to_chat(src, "<span class='revenminor'>Something's wrong! [target] seems to be resisting the siphoning, leaving you vulnerable!</span>")
target.visible_message("<span class='warning'>[target] slumps onto the ground.</span>", \
"<span class='revenwarning'>Violets lights, dancing in your vision, receding--</span>")
draining = FALSE
return
var/datum/beam/B = Beam(target,icon_state="drain_life",time=INFINITY)
if(do_after(src, 46, 0, target)) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
@@ -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
@@ -106,6 +106,10 @@
name = "Fireball"
spell_type = /obj/effect/proc_holder/spell/aimed/fireball
/datum/spellbook_entry/spell_cards
name = "Spell Cards"
spell_type = /obj/effect/proc_holder/spell/aimed/spell_cards
/datum/spellbook_entry/rod_form
name = "Rod Form"
spell_type = /obj/effect/proc_holder/spell/targeted/rod_form
+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!
+1
View File
@@ -62,6 +62,7 @@
var/inprefs = FALSE
var/list/topiclimiter
var/list/clicklimiter
var/datum/chatOutput/chatOutput
+43
View File
@@ -650,6 +650,49 @@ GLOBAL_LIST_EMPTY(external_rsc_urls)
message_admins("<span class='adminnotice'>Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.</span>")
ip_intel = res.intel
/client/Click(atom/object, atom/location, control, params)
var/ab = FALSE
var/list/L = params2list(params)
if (object && object == middragatom && L["left"])
ab = max(0, 5 SECONDS-(world.time-middragtime)*0.1)
var/mcl = CONFIG_GET(number/minute_click_limit)
if (!holder && mcl)
var/minute = round(world.time, 600)
if (!clicklimiter)
clicklimiter = new(LIMITER_SIZE)
if (minute != clicklimiter[CURRENT_MINUTE])
clicklimiter[CURRENT_MINUTE] = minute
clicklimiter[MINUTE_COUNT] = 0
clicklimiter[MINUTE_COUNT] += 1+(ab)
if (clicklimiter[MINUTE_COUNT] > mcl)
var/msg = "Your previous click was ignored because you've done too many in a minute."
if (minute != clicklimiter[ADMINSWARNED_AT]) //only one admin message per-minute. (if they spam the admins can just boot/ban them)
clicklimiter[ADMINSWARNED_AT] = minute
msg += " Administrators have been informed."
if (ab)
log_game("[key_name(src)] is using the middle click aimbot exploit")
message_admins("[key_name_admin(src)] [ADMIN_FLW(usr)] [ADMIN_KICK(usr)] is using the middle click aimbot exploit</span>")
add_system_note("aimbot", "Is using the middle click aimbot exploit")
log_game("[key_name(src)] Has hit the per-minute click limit of [mcl] clicks in a given game minute")
message_admins("[key_name_admin(src)] [ADMIN_FLW(usr)] [ADMIN_KICK(usr)] Has hit the per-minute click limit of [mcl] clicks in a given game minute")
to_chat(src, "<span class='danger'>[msg]</span>")
return
var/scl = CONFIG_GET(number/second_click_limit)
if (!holder && scl)
var/second = round(world.time, 10)
if (!clicklimiter)
clicklimiter = new(LIMITER_SIZE)
if (second != clicklimiter[CURRENT_SECOND])
clicklimiter[CURRENT_SECOND] = second
clicklimiter[SECOND_COUNT] = 0
clicklimiter[SECOND_COUNT] += 1+(!!ab)
if (clicklimiter[SECOND_COUNT] > scl)
to_chat(src, "<span class='danger'>Your previous click was ignored because you've done too many in a second</span>")
return
..()
/client/proc/add_verbs_from_config()
if(CONFIG_GET(flag/see_own_notes))
+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)
@@ -345,15 +345,15 @@
activate_pin(3)
/obj/item/integrated_circuit/input/turfpoint
name = "Tile pointer"
desc = "This circuit will get tile ref with given relative coorinates."
extended_desc = "If the machine cannot see the target, it will not be able to calculate the correct direction.\
This circuit is working only in assembly."
name = "tile pointer"
desc = "This circuit will get tile ref with given absolute coorinates."
extended_desc = "If the machine cannot see the target, it will not be able to scan it.\
This circuit will only work in an assembly."
icon_state = "numberpad"
complexity = 5
inputs = list("X" = IC_PINTYPE_NUMBER,"Y" = IC_PINTYPE_NUMBER)
outputs = list("tile" = IC_PINTYPE_REF)
activators = list("calculate dir" = IC_PINTYPE_PULSE_IN, "on calculated" = IC_PINTYPE_PULSE_OUT,"not calculated" = IC_PINTYPE_PULSE_OUT)
activators = list("scan" = IC_PINTYPE_PULSE_IN, "on scanned" = IC_PINTYPE_PULSE_OUT,"not scanned" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 40
@@ -362,11 +362,11 @@
activate_pin(3)
return
var/turf/T = get_turf(assembly)
var/target_x = CLAMP(get_pin_data(IC_INPUT, 1) - assembly.x, 0, world.maxx)
var/target_y = CLAMP(get_pin_data(IC_INPUT, 2) - assembly.y, 0, world.maxy)
var/target_x = CLAMP(get_pin_data(IC_INPUT, 1), 0, world.maxx)
var/target_y = CLAMP(get_pin_data(IC_INPUT, 2), 0, world.maxy)
var/turf/A = locate(target_x, target_y, T.z)
set_pin_data(IC_OUTPUT, 1, null)
if(!A||!(A in view(T)))
if(!A || !(A in view(T)))
activate_pin(3)
return
else
@@ -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
+1 -1
View File
@@ -104,7 +104,7 @@ Roboticist
faction = "Station"
total_positions = 2
spawn_positions = 2
supervisors = "research director"
supervisors = "the research director"
selection_color = "#ffeeff"
exp_requirements = 60
exp_type = EXP_TYPE_CREW
+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()
@@ -63,7 +63,7 @@ Doesn't work on other aliens/AI.*/
var/obj/machinery/atmospherics/components/unary/atmos_thing = locate() in user.loc
if(atmos_thing)
var/rusure = alert(user, "Laying eggs and shaping resin here would block access to [atmos_thing]. Do you want to continue?", "Blocking Atmospheric Component", "Yes", "No")
if(rusure != "No")
if(rusure != "Yes")
return FALSE
return TRUE
+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 = "ai-techdemon"
/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

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