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
+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
@@ -73,12 +73,6 @@
if (prob(50))
qdel(src)
/obj/machinery/chem_master/power_change()
if(powered())
stat &= ~NOPOWER
else
stat |= NOPOWER
/obj/machinery/chem_master/attackby(obj/item/I, mob/user, params)
if(default_deconstruction_screwdriver(user, "mixer0_nopower", "mixer0", I))
return
@@ -223,7 +217,7 @@
return
vol_each = min(reagents.total_volume / amount, 50)
var/name = stripped_input(usr,"Name:","Name your pill!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/pill/P
@@ -237,7 +231,7 @@
reagents.trans_to(P,vol_each)
else
var/name = stripped_input(usr, "Name:", "Name your pack!", reagents.get_master_reagent_name(), MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/food/condiment/pack/P = new/obj/item/reagent_containers/food/condiment/pack(drop_location())
@@ -259,7 +253,7 @@
return
vol_each = min(reagents.total_volume / amount, 40)
var/name = stripped_input(usr,"Name:","Name your patch!", "[reagents.get_master_reagent_name()] ([vol_each]u)", MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/pill/P
@@ -277,7 +271,7 @@
if(condi)
var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/food/condiment/P = new(drop_location())
P.originalname = name
@@ -290,7 +284,7 @@
amount_full = round(reagents.total_volume / 30)
vol_part = reagents.total_volume % 30
var/name = stripped_input(usr, "Name:","Name your bottle!", (reagents.total_volume ? reagents.get_master_reagent_name() : " "), MAX_NAME_LEN)
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, BE_CLOSE))
if(!name || !reagents.total_volume || !src || QDELETED(src) || !usr.canUseTopic(src, !issilicon(usr)))
return
var/obj/item/reagent_containers/glass/bottle/P
@@ -615,7 +615,19 @@
to_chat(M, "<span class = 'warning'>Your eyes sting!</span>")
M.blind_eyes(2)
/datum/reagent/consumable/nutriment/stabilized
name = "Stabilized Nutriment"
id = "stabilizednutriment"
description = "A bioengineered protien-nutrient structure designed to decompose in high saturation. In layman's terms, it won't get you fat."
reagent_state = SOLID
nutriment_factor = 15 * REAGENTS_METABOLISM
color = "#664330" // rgb: 102, 67, 48
/datum/reagent/consumable/nutriment/stabilized/on_mob_life(mob/living/M)
if(M.nutrition > NUTRITION_LEVEL_FULL - 25)
M.nutrition -= 3*nutriment_factor
..()
////Lavaland Flora Reagents////
@@ -673,4 +685,4 @@
description = "The sorrow and melancholy of a thousand bereaved clowns, forever denied their Honkmechs."
nutriment_factor = 5 * REAGENTS_METABOLISM
color = "#eef442" // rgb: 238, 244, 66
taste_description = "mournful honking"
taste_description = "mournful honking"
+7 -3
View File
@@ -17,13 +17,17 @@ If you create T5+ please take a pass at gene_modder.dm [L40]. Max_values MUST fi
display_contents_with_number = 1
max_w_class = WEIGHT_CLASS_NORMAL
max_combined_w_class = 100
can_hold = list(
/obj/item/reagent_containers/glass/beaker,
/obj/item/device/assembly/igniter,
/obj/item/stock_parts,
/obj/item/stack/ore/bluespace_crystal)
var/works_from_distance = 0
var/pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/rped.ogg'
var/alt_sound = null
/obj/item/storage/part_replacer/can_be_inserted(obj/item/W, stop_messages = 0, mob/user)
return ..() && W.get_part_rating()
/obj/item/storage/part_replacer/afterattack(obj/machinery/T, mob/living/carbon/human/user, flag, params)
if(flag)
return
@@ -82,15 +82,13 @@
var/mob/living/carbon/human/H = mover
if(H.nutrition >= NUTRITION_LEVEL_FAT)
H.visible_message("<span class='warning'>[H] pushes through [src]!</span>", "<span class='notice'>You've seen and eaten worse than this.</span>")
return 1
return TRUE
else
to_chat(H, "<span class='warning'>You're repulsed by even looking at [src]. Only a pig could force themselves to go through it.</span>")
if(istype(mover, /mob/living/simple_animal/hostile/morph))
return 1
return TRUE
else
return 0
return FALSE
/obj/structure/mirror/magic/pride //Pride's mirror: Used in the Pride ruin.
name = "pride's mirror"
+77 -7
View File
@@ -9,6 +9,7 @@
var/list/projectile_var_overrides = list()
var/projectile_amount = 1 //Projectiles per cast.
var/current_amount = 0 //How many projectiles left.
var/projectiles_per_fire = 1 //Projectiles per fire. Probably not a good thing to use unless you override ready_projectile().
/obj/effect/proc_holder/spell/aimed/Click()
var/mob/living/user = usr
@@ -26,10 +27,18 @@
charge_counter = charge_max * refund_percent
start_recharge()
remove_ranged_ability(msg)
on_deactivation(user)
else
msg = "<span class='notice'>[active_msg]<B>Left-click to shoot it at a target!</B></span>"
current_amount = projectile_amount
add_ranged_ability(user, msg, TRUE)
on_activation(user)
/obj/effect/proc_holder/spell/aimed/proc/on_activation(mob/user)
return
/obj/effect/proc_holder/spell/aimed/proc/on_deactivation(mob/user)
return
/obj/effect/proc_holder/spell/aimed/update_icon()
if(!action)
@@ -60,19 +69,25 @@
remove_ranged_ability() //Auto-disable the ability once you run out of bullets.
charge_counter = 0
start_recharge()
on_deactivation(user)
return TRUE
/obj/effect/proc_holder/spell/aimed/proc/fire_projectile(mob/living/user, atom/target)
current_amount--
var/obj/item/projectile/P = new projectile_type(user.loc)
P.firer = user
P.preparePixelProjectile(target, user)
for(var/V in projectile_var_overrides)
if(P.vars[V])
P.vv_edit_var(V, projectile_var_overrides[V])
P.fire()
for(var/i in 1 to projectiles_per_fire)
var/obj/item/projectile/P = new projectile_type(user.loc)
P.firer = user
P.preparePixelProjectile(target, user)
for(var/V in projectile_var_overrides)
if(P.vars[V])
P.vv_edit_var(V, projectile_var_overrides[V])
ready_projectile(P, target, user, i)
P.fire()
return TRUE
/obj/effect/proc_holder/spell/aimed/proc/ready_projectile(obj/item/projectile/P, atom/target, mob/user, iteration)
return
/obj/effect/proc_holder/spell/aimed/lightningbolt
name = "Lightning Bolt"
desc = "Fire a high powered lightning bolt at your foes!"
@@ -108,3 +123,58 @@
active_msg = "You prepare to cast your fireball spell!"
deactive_msg = "You extinguish your fireball... for now."
active = FALSE
/obj/effect/proc_holder/spell/aimed/spell_cards
name = "Spell Cards"
desc = "Blazing hot rapid-fire homing cards. Banish your foes with its mystical power!"
school = "evocation"
charge_max = 50
clothes_req = 0
invocation = "Sigi'lu M'Fan 'Tasia"
invocation_type = "shout"
range = 40
cooldown_min = 10
projectile_amount = 5
projectiles_per_fire = 7
projectile_type = /obj/item/projectile/spellcard
var/datum/weakref/current_target_weakref
var/projectile_turnrate = 10
var/projectile_pixel_homing_spread = 32
var/projectile_initial_spread_amount = 30
var/projectile_location_spread_amount = 12
var/datum/component/lockon_aiming/lockon_component
ranged_clickcd_override = 1
/obj/effect/proc_holder/spell/aimed/spell_cards/on_activation(mob/M)
QDEL_NULL(lockon_component)
lockon_component = M.AddComponent(/datum/component/lockon_aiming, 5, typecacheof(list(/mob/living)), 1, null, CALLBACK(src, .proc/on_lockon_component))
/obj/effect/proc_holder/spell/aimed/spell_cards/proc/on_lockon_component(list/locked_weakrefs)
if(!length(locked_weakrefs))
current_target_weakref = null
return
current_target_weakref = locked_weakrefs[1]
var/atom/A = current_target_weakref.resolve()
if(A)
var/mob/M = lockon_component.parent
M.face_atom(A)
/obj/effect/proc_holder/spell/aimed/spell_cards/on_deactivation(mob/M)
QDEL_NULL(lockon_component)
/obj/effect/proc_holder/spell/aimed/spell_cards/ready_projectile(obj/item/projectile/P, atom/target, mob/user, iteration)
if(current_target_weakref)
var/atom/A = current_target_weakref.resolve()
if(A && get_dist(A, user) < 7)
P.homing_turn_speed = projectile_turnrate
P.homing_inaccuracy_min = projectile_pixel_homing_spread
P.homing_inaccuracy_max = projectile_pixel_homing_spread
P.set_homing_target(current_target_weakref.resolve())
var/rand_spr = rand()
var/total_angle = projectile_initial_spread_amount * 2
var/adjusted_angle = total_angle - ((projectile_initial_spread_amount / projectiles_per_fire) * 0.5)
var/one_fire_angle = adjusted_angle / projectiles_per_fire
var/current_angle = iteration * one_fire_angle * rand_spr - (projectile_initial_spread_amount / 2)
P.pixel_x = rand(-projectile_location_spread_amount, projectile_location_spread_amount)
P.pixel_y = rand(-projectile_location_spread_amount, projectile_location_spread_amount)
P.preparePixelProjectile(target, user, null, current_angle)
@@ -43,6 +43,15 @@
var/species_flags_list = list()
var/dmg_overlay_type //the type of damage overlay (if any) to use when this bodypart is bruised/burned.
//Damage messages used by help_shake_act()
var/light_brute_msg = "bruised"
var/medium_brute_msg = "battered"
var/heavy_brute_msg = "mangled"
var/light_burn_msg = "numb"
var/medium_burn_msg = "blistered"
var/heavy_burn_msg = "peeling away"
/obj/item/bodypart/examine(mob/user)
..()
if(brute_dam > 0)
@@ -1,4 +1,10 @@
#define ROBOTIC_LIGHT_BRUTE_MSG "marred"
#define ROBOTIC_MEDIUM_BRUTE_MSG "dented"
#define ROBOTIC_HEAVY_BRUTE_MSG "falling apart"
#define ROBOTIC_LIGHT_BURN_MSG "scorched"
#define ROBOTIC_MEDIUM_BURN_MSG "charred"
#define ROBOTIC_HEAVY_BURN_MSG "smoldering"
/obj/item/bodypart/l_arm/robot
name = "cyborg left arm"
@@ -10,6 +16,13 @@
icon_state = "borg_l_arm"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
/obj/item/bodypart/r_arm/robot
name = "cyborg right arm"
@@ -21,6 +34,13 @@
icon_state = "borg_r_arm"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
/obj/item/bodypart/l_leg/robot
name = "cyborg left leg"
@@ -32,6 +52,13 @@
icon_state = "borg_l_leg"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
/obj/item/bodypart/r_leg/robot
name = "cyborg right leg"
@@ -43,6 +70,13 @@
icon_state = "borg_r_leg"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
/obj/item/bodypart/chest/robot
name = "cyborg torso"
@@ -52,6 +86,15 @@
flags_1 = CONDUCT_1
icon_state = "borg_chest"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
var/wired = 0
var/obj/item/stock_parts/cell/cell = null
@@ -102,6 +145,15 @@
flags_1 = CONDUCT_1
icon_state = "borg_head"
status = BODYPART_ROBOTIC
light_brute_msg = ROBOTIC_LIGHT_BRUTE_MSG
medium_brute_msg = ROBOTIC_MEDIUM_BRUTE_MSG
heavy_brute_msg = ROBOTIC_HEAVY_BRUTE_MSG
light_burn_msg = ROBOTIC_LIGHT_BURN_MSG
medium_burn_msg = ROBOTIC_MEDIUM_BURN_MSG
heavy_burn_msg = ROBOTIC_HEAVY_BURN_MSG
var/obj/item/device/assembly/flash/handheld/flash1 = null
var/obj/item/device/assembly/flash/handheld/flash2 = null
@@ -189,3 +241,12 @@
icon = 'icons/mob/augmentation/surplus_augments.dmi'
icon_state = "r_leg"
max_damage = 20
#undef ROBOTIC_LIGHT_BRUTE_MSG
#undef ROBOTIC_MEDIUM_BRUTE_MSG
#undef ROBOTIC_HEAVY_BRUTE_MSG
#undef ROBOTIC_LIGHT_BURN_MSG
#undef ROBOTIC_MEDIUM_BURN_MSG
#undef ROBOTIC_HEAVY_BURN_MSG
+1 -1
View File
@@ -68,7 +68,7 @@
user.visible_message("[user] drapes [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].", \
"<span class='notice'>You drape [I] over [M]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name].</span>")
add_logs(user, M, "operated", addition="Operation type: [procedure.name], location: [selected_zone]")
add_logs(user, M, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
else
to_chat(user, "<span class='warning'>You need to expose [M]'s [parse_zone(selected_zone)] first!</span>")
-1
View File
@@ -46,7 +46,6 @@
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_VISIBLE
actions_types = list(/datum/action/item_action/organ_action/use)
sight_flags = SEE_BLACKNESS
var/night_vision = TRUE
/obj/item/organ/eyes/night_vision/ui_action_click()