diff --git a/code/__defines/items_clothing.dm b/code/__defines/items_clothing.dm index 13af3139cbf..12bb94dfbbe 100644 --- a/code/__defines/items_clothing.dm +++ b/code/__defines/items_clothing.dm @@ -147,7 +147,7 @@ #define FIRE_MAX_STACKS 25 #define FIRE_MAX_FIRESUIT_STACKS 20 // If the number of stacks goes above this firesuits won't protect you anymore. If not, you can walk around while on fire like a badass. -#define THROWFORCE_GIBS 10 // Throw speed for gibbed or dismembered organs. +#define THROWFORCE_GIBS 3 // Throw speed for gibbed or dismembered organs. #define THROWFORCE_SPEED_DIVISOR 12 // The throwing speed value at which the throwforce multiplier is exactly 1. #define THROWNOBJ_KNOCKBACK_SPEED 15 // The minumum speed of a w_class 2 thrown object that will cause living mobs it hits to be knocked back. Heavier objects can cause knockback at lower speeds. #define THROWNOBJ_KNOCKBACK_DIVISOR 2 // Affects how much speed the mob is knocked back with. diff --git a/code/__defines/unit_tests.dm b/code/__defines/unit_tests.dm new file mode 100644 index 00000000000..dbd67aab94d --- /dev/null +++ b/code/__defines/unit_tests.dm @@ -0,0 +1,8 @@ + +#define UT_NORMAL 1 // Standard one atmosphere 20celsius +#define UT_VACUUM 2 // Vacume on simulated turfs +#define UT_NORMAL_COLD 3 // Cold but standard atmosphere. + +#define FAILURE 0 +#define SUCCESS 1 +#define SKIP 2 diff --git a/code/_helpers/lists.dm b/code/_helpers/lists.dm index b2c5bcd3556..a8bea9fa45d 100644 --- a/code/_helpers/lists.dm +++ b/code/_helpers/lists.dm @@ -767,12 +767,15 @@ proc/dd_sortedObjectList(list/incoming) var/global/list/json_cache = list() /proc/cached_json_decode(var/json_to_decode) - if(!json_to_decode || !length(json_to_decode)) - return list() - try - if(isnull(global.json_cache[json_to_decode])) - global.json_cache[json_to_decode] = json_decode(json_to_decode) - . = global.json_cache[json_to_decode] - catch(var/exception/e) - log_error("Exception during JSON decoding ([json_to_decode]): [e]") - return list() + if(length(json_to_decode)) + try + if(isnull(global.json_cache[json_to_decode])) + global.json_cache[json_to_decode] = json_decode(json_to_decode) + var/list/decoded = global.json_cache[json_to_decode] + if(islist(decoded)) // To prevent cache mutation. + return deepCopyList(decoded) + else if(decoded) + return decoded + catch(var/exception/e) + log_error("Exception during JSON decoding ([json_to_decode]): [e]") + return list() diff --git a/code/_helpers/turfs.dm b/code/_helpers/turfs.dm index 4bf084380f1..b3712ee4711 100644 --- a/code/_helpers/turfs.dm +++ b/code/_helpers/turfs.dm @@ -41,24 +41,10 @@ if(turfs.len) return pick(turfs) -/proc/screen_loc2turf(text, turf/origin) - if(!origin) - return null - var/tZ = splittext(text, ",") - var/tX = splittext(tZ[1], "-") - var/tY = text2num(tX[2]) - tX = splittext(tZ[2], "-") - tX = text2num(tX[2]) - tZ = origin.z - tX = max(1, min(origin.x + 7 - tX, world.maxx)) - tY = max(1, min(origin.y + 7 - tY, world.maxy)) - return locate(tX, tY, tZ) - /* Predicate helpers */ - /proc/is_holy_turf(var/turf/T) return T && T.holy diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 4a601b141ed..030e54c9650 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -369,7 +369,18 @@ var/global/list/click_catchers /proc/get_click_catchers() if(!global.click_catchers) - global.click_catchers = create_click_catcher() + global.click_catchers = list() + var/ox = -(round(config.max_client_view_x*0.5)) + for(var/i = 0 to config.max_client_view_x) + var/oy = -(round(config.max_client_view_y*0.5)) + var/tx = ox + i + for(var/j = 0 to config.max_client_view_y) + var/ty = oy + j + var/obj/screen/click_catcher/CC = new + CC.screen_loc = "CENTER[tx < 0 ? tx : "+[tx]"],CENTER[ty < 0 ? ty : "+[ty]"]" + CC.x_offset = tx + CC.y_offset = ty + global.click_catchers += CC return global.click_catchers /obj/screen/click_catcher @@ -378,26 +389,22 @@ var/global/list/click_catchers plane = CLICKCATCHER_PLANE mouse_opacity = 2 screen_loc = "CENTER-7,CENTER-7" + var/x_offset = 0 + var/y_offset = 0 /obj/screen/click_catcher/Destroy() SHOULD_CALL_PARENT(FALSE) return QDEL_HINT_LETMELIVE -/proc/create_click_catcher() - . = list() - for(var/i = 0, i<15, i++) - for(var/j = 0, j<15, j++) - var/obj/screen/click_catcher/CC = new() - CC.screen_loc = "TOP-[i],RIGHT-[j]" - . += CC - /obj/screen/click_catcher/Click(location, control, params) var/list/modifiers = params2list(params) if(modifiers["middle"] && istype(usr, /mob/living/carbon)) var/mob/living/carbon/C = usr C.swap_hand() else - var/turf/T = screen_loc2turf(screen_loc, get_turf(usr)) - if(T) - T.Click(location, control, params) + var/turf/origin = get_turf(usr) + if(isturf(origin)) + var/turf/clicked = locate(origin.x + x_offset, origin.y + y_offset, origin.z) + if(clicked) + clicked.Click(location, control, params) . = 1 diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index b2e6cc8efd9..f28a472232d 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -124,8 +124,7 @@ #define ui_iarrowleft "BOTTOM-1,RIGHT-4" #define ui_iarrowright "BOTTOM-1,RIGHT-2" -#define ui_spell_master "RIGHT-1:16,TOP-1:16" -#define ui_genetic_master "RIGHT-1:16,TOP-3:16" +#define ui_ability_master "RIGHT-2:16,TOP-1:16" // AI #define ui_ai_core "LEFT:6,BOTTOM:5" diff --git a/code/_onclick/hud/ability_screen_objects.dm b/code/_onclick/hud/ability_screen_objects.dm index c4fc5aa2474..7887e0dd43c 100644 --- a/code/_onclick/hud/ability_screen_objects.dm +++ b/code/_onclick/hud/ability_screen_objects.dm @@ -1,4 +1,4 @@ -/obj/screen/movable/ability_master +/obj/screen/ability_master name = "Abilities" icon = 'icons/mob/screen_spells.dmi' icon_state = "grey_spell_ready" @@ -6,14 +6,15 @@ var/list/obj/screen/ability/spell_objects = list() var/showing = 0 // If we're 'open' or not. + var/const/abilities_per_row = 7 var/open_state = "master_open" // What the button looks like when it's 'open', showing the other buttons. var/closed_state = "master_closed" // Button when it's 'closed', hiding everything else. - screen_loc = ui_spell_master // TODO: Rename + screen_loc = ui_ability_master var/mob/my_mob = null // The mob that possesses this hud object. -/obj/screen/movable/ability_master/Initialize(mapload, owner) +/obj/screen/ability_master/Initialize(mapload, owner) . = ..() if(owner) my_mob = owner @@ -22,7 +23,7 @@ . = INITIALIZE_HINT_QDEL CRASH("ERROR: ability_master's Initialize() was not given an owner argument. This is a bug.") -/obj/screen/movable/ability_master/Destroy() +/obj/screen/ability_master/Destroy() . = ..() remove_all_abilities() //Get rid of the ability objects. ability_objects.Cut() @@ -32,18 +33,18 @@ my_mob.client.screen -= src my_mob = null -/obj/screen/movable/ability_master/handle_mouse_drop(var/atom/over, var/mob/user) +/obj/screen/ability_master/handle_mouse_drop(var/atom/over, var/mob/user) if(showing) return FALSE . = ..() -/obj/screen/movable/ability_master/Click() +/obj/screen/ability_master/Click() if(!ability_objects.len) // If we're empty for some reason. return toggle_open() -/obj/screen/movable/ability_master/proc/toggle_open(var/forced_state = 0) +/obj/screen/ability_master/proc/toggle_open(var/forced_state = 0) if(showing && (forced_state != 2)) // We are closing the ability master, hide the abilities. for(var/obj/screen/ability/O in ability_objects) if(my_mob && my_mob.client) @@ -60,9 +61,15 @@ overlays.Add(open_state) update_icon() -/obj/screen/movable/ability_master/proc/open_ability_master() +/obj/screen/ability_master/proc/open_ability_master() + for(var/i = 1 to length(ability_objects)) + var/obj/screen/ability/A = ability_objects[i] + var/row = round(i/abilities_per_row) + A.screen_loc = "RIGHT-[(i-(row*abilities_per_row))+2]:16,TOP-[row+1]:16" + if(my_mob && my_mob.client) + my_mob.client.screen += A -/obj/screen/movable/ability_master/proc/update_abilities(forced = 0, mob/user) +/obj/screen/ability_master/proc/update_abilities(forced = 0, mob/user) update_icon() if(user && user.client) if(!(src in user.client.screen)) @@ -73,13 +80,13 @@ ability.maptext = "[i]" // Slot number i++ -/obj/screen/movable/ability_master/on_update_icon() +/obj/screen/ability_master/on_update_icon() if(ability_objects.len) set_invisibility(0) else set_invisibility(101) -/obj/screen/movable/ability_master/proc/add_ability(var/name_given) +/obj/screen/ability_master/proc/add_ability(var/name_given) if(!name) return var/obj/screen/ability/new_button = new /obj/screen/ability new_button.ability_master = src @@ -90,7 +97,7 @@ if(my_mob.client) toggle_open(2) //forces the icons to refresh on screen -/obj/screen/movable/ability_master/proc/remove_ability(var/obj/screen/ability/ability) +/obj/screen/ability_master/proc/remove_ability(var/obj/screen/ability/ability) if(!ability) return ability_objects.Remove(ability) @@ -105,36 +112,36 @@ // else // qdel(src) -/obj/screen/movable/ability_master/proc/remove_all_abilities() +/obj/screen/ability_master/proc/remove_all_abilities() for(var/obj/screen/ability/A in ability_objects) remove_ability(A) -/obj/screen/movable/ability_master/proc/get_ability_by_name(name_to_search) +/obj/screen/ability_master/proc/get_ability_by_name(name_to_search) for(var/obj/screen/ability/A in ability_objects) if(A.name == name_to_search) return A return null -/obj/screen/movable/ability_master/proc/get_ability_by_proc_ref(proc_ref) +/obj/screen/ability_master/proc/get_ability_by_proc_ref(proc_ref) for(var/obj/screen/ability/verb_based/V in ability_objects) if(V.verb_to_call == proc_ref) return V return null -/obj/screen/movable/ability_master/proc/get_ability_by_instance(var/obj/instance/) +/obj/screen/ability_master/proc/get_ability_by_instance(var/obj/instance/) for(var/obj/screen/ability/obj_based/O in ability_objects) if(O.object == instance) return O return null -/obj/screen/movable/ability_master/proc/get_ability_by_spell(var/spell/s) +/obj/screen/ability_master/proc/get_ability_by_spell(var/spell/s) for(var/screen in spell_objects) var/obj/screen/ability/spell/S = screen if(S.spell == s) return S return null -/obj/screen/movable/ability_master/proc/synch_spells_to_mind(var/datum/mind/M) +/obj/screen/ability_master/proc/synch_spells_to_mind(var/datum/mind/M) if(!M) return LAZYINITLIST(M.learned_spells) @@ -144,7 +151,7 @@ /mob/Initialize() . = ..() - ability_master = new /obj/screen/movable/ability_master(null,src) + ability_master = new /obj/screen/ability_master(null,src) ///////////ACTUAL ABILITIES//////////// //This is what you click to do things// @@ -155,7 +162,7 @@ maptext_x = 3 var/background_base_state = "grey" var/ability_icon_state = null - var/obj/screen/movable/ability_master/ability_master + var/obj/screen/ability_master/ability_master /obj/screen/ability/Destroy() if(ability_master) @@ -217,7 +224,7 @@ if(object_used && verb_to_call) call(object_used,verb_to_call)(arguments_to_use) -/obj/screen/movable/ability_master/proc/add_verb_ability(var/object_given, var/verb_given, var/name_given, var/ability_icon_given, var/arguments) +/obj/screen/ability_master/proc/add_verb_ability(var/object_given, var/verb_given, var/name_given, var/ability_icon_given, var/arguments) if(!object_given) message_admins("ERROR: add_verb_ability() was not given an object in its arguments.") if(!verb_given) @@ -241,7 +248,7 @@ icon_state = "ling_spell_base" background_base_state = "ling" -/obj/screen/movable/ability_master/proc/add_ling_ability(var/object_given, var/verb_given, var/name_given, var/ability_icon_given, var/arguments) +/obj/screen/ability_master/proc/add_ling_ability(var/object_given, var/verb_given, var/name_given, var/ability_icon_given, var/arguments) if(!object_given) message_admins("ERROR: add_ling_ability() was not given an object in its arguments.") if(!verb_given) @@ -277,7 +284,7 @@ icon_state = "wiz_spell_base" background_base_state = "wiz" -/obj/screen/movable/ability_master/proc/add_technomancer_ability(var/obj/object_given, var/ability_icon_given) +/obj/screen/ability_master/proc/add_technomancer_ability(var/obj/object_given, var/ability_icon_given) if(!object_given) message_admins("ERROR: add_technomancer_ability() was not given an object in its arguments.") if(get_ability_by_instance(object_given)) @@ -304,7 +311,7 @@ spell = null return ..() -/obj/screen/movable/ability_master/proc/add_spell(var/spell/spell) +/obj/screen/ability_master/proc/add_spell(var/spell/spell) if(!spell) return if(spell.spell_flags & NO_BUTTON) //no button to add if we don't get one @@ -331,7 +338,7 @@ if(my_mob.client) toggle_open(2) //forces the icons to refresh on screen -/obj/screen/movable/ability_master/proc/update_spells(var/forced = 0) +/obj/screen/ability_master/proc/update_spells(var/forced = 0) for(var/obj/screen/ability/spell/spell in spell_objects) spell.update_charge(forced) @@ -380,7 +387,7 @@ /obj/screen/ability/spell/activate() spell.perform(usr) -/obj/screen/movable/ability_master/proc/silence_spells(var/amount) +/obj/screen/ability_master/proc/silence_spells(var/amount) for(var/obj/screen/ability/spell/spell in spell_objects) spell.spell.silenced = amount spell.spell.process() diff --git a/code/_onclick/hud/action.dm b/code/_onclick/hud/action.dm index 5ca2a24e9a9..6d30353ae04 100644 --- a/code/_onclick/hud/action.dm +++ b/code/_onclick/hud/action.dm @@ -20,7 +20,7 @@ var/check_flags = 0 var/processing = 0 var/active = 0 - var/obj/screen/movable/action_button/button = null + var/obj/screen/action_button/button = null var/button_icon = 'icons/obj/action_buttons/actions.dmi' var/button_icon_state = "default" var/background_icon_state = "bg_default" @@ -120,21 +120,17 @@ /datum/action/proc/UpdateDesc() return desc -/obj/screen/movable/action_button +/obj/screen/action_button var/datum/action/owner screen_loc = "LEFT,TOP" -/obj/screen/movable/action_button/Click(location,control,params) - var/list/modifiers = params2list(params) - if(modifiers["shift"]) - moved = 0 - return 1 - if(usr.next_move >= world.time) // Is this needed ? - return - owner.Trigger() - return 1 +/obj/screen/action_button/Click(location,control,params) + if(owner && usr && usr.next_move < world.time) + owner.Trigger() + return TRUE + return FALSE -/obj/screen/movable/action_button/proc/UpdateIcon() +/obj/screen/action_button/proc/UpdateIcon() if(!owner) return icon = owner.button_icon @@ -156,26 +152,26 @@ else color = rgb(255,255,255,255) -/obj/screen/movable/action_button/MouseEntered(location, control, params) +/obj/screen/action_button/MouseEntered(location, control, params) openToolTip(user = usr, tip_src = src, params = params, title = name, content = desc) ..() -/obj/screen/movable/action_button/MouseDown() +/obj/screen/action_button/MouseDown() closeToolTip(usr) ..() -/obj/screen/movable/action_button/MouseExited() +/obj/screen/action_button/MouseExited() closeToolTip(usr) ..() //Hide/Show Action Buttons ... Button -/obj/screen/movable/action_button/hide_toggle +/obj/screen/action_button/hide_toggle name = "Hide Buttons" icon = 'icons/obj/action_buttons/actions.dmi' icon_state = "bg_default" var/hidden = 0 -/obj/screen/movable/action_button/hide_toggle/Click() +/obj/screen/action_button/hide_toggle/Click() usr.hud_used.action_buttons_hidden = !usr.hud_used.action_buttons_hidden hidden = usr.hud_used.action_buttons_hidden @@ -187,7 +183,7 @@ usr.update_action_buttons() -/obj/screen/movable/action_button/hide_toggle/proc/InitialiseIcon(var/mob/living/user) +/obj/screen/action_button/hide_toggle/proc/InitialiseIcon(var/mob/living/user) if(isalien(user)) icon_state = "bg_alien" else @@ -195,7 +191,7 @@ UpdateIcon() return -/obj/screen/movable/action_button/hide_toggle/UpdateIcon() +/obj/screen/action_button/hide_toggle/UpdateIcon() overlays.Cut() var/image/img = image(icon,src,hidden?"show":"hide") overlays += img diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm index 158729f5557..1ed3eb3a259 100644 --- a/code/_onclick/hud/hud.dm +++ b/code/_onclick/hud/hud.dm @@ -34,7 +34,7 @@ var/list/other var/list/obj/screen/hotkeybuttons - var/obj/screen/movable/action_button/hide_toggle/hide_actions_toggle + var/obj/screen/action_button/hide_toggle/hide_actions_toggle var/action_buttons_hidden = FALSE var/static/list/hidden_inventory_slots = list( @@ -333,8 +333,22 @@ hud_used.persistant_inventory_update() update_action_buttons() +/client/proc/reset_click_catchers() + + var/xmin = -(round(last_view_x_dim*0.5)) + var/xmax = last_view_x_dim - abs(xmin) + var/ymin = -(round(last_view_y_dim*0.5)) + var/ymax = last_view_y_dim - abs(ymin) + + var/list/click_catchers = get_click_catchers() + for(var/obj/screen/click_catcher/catcher in click_catchers) + if(catcher.x_offset <= xmin || catcher.x_offset >= xmax || catcher.y_offset <= ymin || catcher.y_offset >= ymax) + screen -= catcher + else + screen |= catcher + /mob/proc/add_click_catcher() - client.screen |= get_click_catchers() + client.reset_click_catchers() /mob/new_player/add_click_catcher() return diff --git a/code/_onclick/hud/movable_screen_objects.dm b/code/_onclick/hud/movable_screen_objects.dm deleted file mode 100644 index 8d024420c4e..00000000000 --- a/code/_onclick/hud/movable_screen_objects.dm +++ /dev/null @@ -1,11 +0,0 @@ -/obj/screen/movable - var/moved = FALSE - -/obj/screen/movable/MouseDrop(over_object, src_location, over_location, src_control, over_control, params) - SHOULD_CALL_PARENT(FALSE) - var/list/PM = params2list(params) - if(LAZYLEN(PM) && PM["screen-loc"]) - var/list/screen_loc_params = splittext(PM["screen-loc"], ",") - var/list/x_data = splittext(screen_loc_params[1], ":") - var/list/y_data = splittext(screen_loc_params[2], ":") - screen_loc = "LEFT+[x_data[1]]:[text2num(x_data[2])-(1.5 * world.icon_size)],BOTTOM+[y_data[1]]:[text2num(y_data[2])-(1.5 * world.icon_size)]" diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index 36c233a69dd..615b83773f6 100644 --- a/code/controllers/subsystems/jobs.dm +++ b/code/controllers/subsystems/jobs.dm @@ -128,9 +128,6 @@ SUBSYSTEM_DEF(jobs) syndicate_code_phrase = generate_code_phrase() syndicate_code_response = generate_code_phrase() - // Set up AI spawn locations - spawn_empty_ai() - . = ..() /datum/controller/subsystem/jobs/proc/guest_jobbans(var/job) diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index 15af273f225..ae918117a62 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -167,7 +167,7 @@ //Base skin and blend for(var/obj/item/organ/external/E in H.get_external_organs()) - E.set_dna(E.dna) + E.set_dna(H.dna) //Hair var/list/hair_subtypes = subtypesof(/decl/sprite_accessory/hair) diff --git a/code/game/machinery/doors/_door.dm b/code/game/machinery/doors/_door.dm index 60cd8eb5f93..5ac9006545b 100644 --- a/code/game/machinery/doors/_door.dm +++ b/code/game/machinery/doors/_door.dm @@ -393,52 +393,57 @@ flick("door_deny", src) playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) -/obj/machinery/door/proc/open(var/forced = 0) +/obj/machinery/door/proc/open(forced = FALSE) if(!can_open(forced)) return + operating = 1 do_animate("opening") icon_state = "door0" - set_opacity(0) - sleep(3) - src.set_density(0) + set_opacity(FALSE) + + sleep(0.5 SECONDS) + src.set_density(FALSE) update_nearby_tiles() - sleep(7) + + sleep(0.5 SECONDS) src.layer = open_layer update_icon() - set_opacity(0) + set_opacity(FALSE) operating = 0 if(autoclose) close_door_at = next_close_time() - return 1 + return TRUE /obj/machinery/door/proc/next_close_time() return world.time + (normalspeed ? 150 : 5) -/obj/machinery/door/proc/close(var/forced = 0) +/obj/machinery/door/proc/close(forced = FALSE) if(!can_close(forced)) return + operating = 1 close_door_at = 0 do_animate("closing") - sleep(3) - src.set_density(1) - src.layer = closed_layer + + sleep(0.5 SECONDS) + src.set_density(TRUE) update_nearby_tiles() - sleep(7) + src.layer = closed_layer + + sleep(0.5 SECONDS) update_icon() if(visible && !glass) - set_opacity(1) //caaaaarn! + set_opacity(TRUE) operating = 0 //I shall not add a check every x ticks if a door has closed over some fire. var/obj/fire/fire = locate() in loc - if(fire) - qdel(fire) + qdel(fire) /obj/machinery/door/proc/toggle(to_open = density) if(to_open) diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index f1f87e17a39..f7431bd00e1 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -110,11 +110,12 @@ operating = 1 playsound(src.loc, open_sound, 100, 1) flick(icon_state_opening, src) - set_density(0) + + sleep(0.6 SECONDS) + set_density(FALSE) update_nearby_tiles() update_icon() - set_opacity(0) - sleep(15) + set_opacity(FALSE) layer = open_layer operating = 0 @@ -126,11 +127,12 @@ playsound(src.loc, close_sound, 100, 1) layer = closed_layer flick(icon_state_closing, src) - set_density(1) + + sleep(0.6 SECONDS) + set_density(TRUE) update_nearby_tiles() update_icon() - set_opacity(1) - sleep(15) + set_opacity(TRUE) operating = 0 // Proc: force_toggle() @@ -186,20 +188,23 @@ // Parameters: None // Description: Opens the door. Does necessary checks. Automatically closes if autoclose is true /obj/machinery/door/blast/open() - if (operating || (stat & BROKEN || stat & NOPOWER)) + if (!can_open() || (stat & BROKEN || stat & NOPOWER)) return + force_open() + if(autoclose) - spawn(150) - close() - return 1 + addtimer(CALLBACK(src, .proc/close), 15 SECONDS, TIMER_UNIQUE|TIMER_OVERRIDE) + + return TRUE // Proc: close() // Parameters: None // Description: Closes the door. Does necessary checks. /obj/machinery/door/blast/close() - if (operating || (stat & BROKEN || stat & NOPOWER)) + if (!can_close() || (stat & BROKEN || stat & NOPOWER)) return + force_close() /obj/machinery/door/blast/toggle(to_open = density) diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm index 3bccb37eef1..765dd1f6720 100644 --- a/code/game/machinery/doors/windowdoor.dm +++ b/code/game/machinery/doors/windowdoor.dm @@ -103,11 +103,8 @@ icon_state = "[src.base_state]open" flick("[src.base_state]opening", src) playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1) - addtimer(CALLBACK(src, .proc/open_final), 10, TIMER_UNIQUE | TIMER_OVERRIDE) - return 1 - -/obj/machinery/door/window/proc/open_final() + sleep(0.9 SECONDS) explosion_resistance = 0 set_density(FALSE) update_icon() @@ -116,23 +113,25 @@ if(operating == 1) //emag again operating = 0 + return TRUE + /obj/machinery/door/window/close() if (src.operating) return 0 + operating = 1 flick(text("[]closing", src.base_state), src) playsound(src.loc, 'sound/machines/windowdoor.ogg', 100, 1) - set_density(1) + set_density(TRUE) update_icon() explosion_resistance = initial(explosion_resistance) update_nearby_tiles() - addtimer(CALLBACK(src, .proc/close_final), 10, TIMER_UNIQUE | TIMER_OVERRIDE) - return 1 - -/obj/machinery/door/window/proc/close_final() + sleep(0.9 SECONDS) operating = 0 + return TRUE + /obj/machinery/door/window/take_damage(var/damage) src.health = max(0, src.health - damage) if (src.health <= 0) diff --git a/code/game/machinery/suit_cycler.dm b/code/game/machinery/suit_cycler.dm index 100c05195a8..e1365ce4dae 100644 --- a/code/game/machinery/suit_cycler.dm +++ b/code/game/machinery/suit_cycler.dm @@ -463,8 +463,15 @@ if(suit) suit.refit_for_bodytype(target_flags) if(boots) boots.refit_for_bodytype(target_flags) - target_modification.RefitItem(helmet) - target_modification.RefitItem(suit) + + if(helmet) + target_modification.RefitItem(helmet) + helmet.refit_for_bodytype(target_bodytype) + if(suit) + suit.refit_for_bodytype(target_bodytype) + target_modification.RefitItem(suit) + if(boots) + boots.refit_for_bodytype(target_bodytype) if(helmet) helmet.SetName("refitted [helmet.name]") if(suit) suit.SetName("refitted [suit.name]") diff --git a/code/game/objects/items/books/skill_book.dm b/code/game/objects/items/books/skill_book.dm index 3908408902e..4923de11526 100644 --- a/code/game/objects/items/books/skill_book.dm +++ b/code/game/objects/items/books/skill_book.dm @@ -38,16 +38,16 @@ Skill books that increase your skills while you activate and hold them author = "The Oracle of Bakersroof" icon_state = "book2" force = 4 - w_class = ITEM_SIZE_LARGE //Skill books are THICC with knowledge. Up one level from regular books to prevent library-in-a-bag silliness. + w_class = ITEM_SIZE_LARGE // Skill books are THICC with knowledge. Up one level from regular books to prevent library-in-a-bag silliness. unique = TRUE material = /decl/material/solid/plastic matter = list(/decl/material/solid/wood = MATTER_AMOUNT_REINFORCEMENT) - var/decl/hierarchy/skill/skill // e.g. SKILL_LITERACY - var/skill_req = SKILL_NONE //The level the user needs in the skill to benefit from the book, e.g. SKILL_PROF - var/reading = FALSE //Tto check if the book is actively being used - var/custom = FALSE //To bypass init stuff, for player made textbooks and weird books. If true must have details manually set - var/ez_read = FALSE //Set to TRUE if you can read it without basic literacy skills + var/decl/hierarchy/skill/skill // e.g. SKILL_LITERACY + var/skill_req = SKILL_NONE // The level the user needs in the skill to benefit from the book, e.g. SKILL_PROF + var/weakref/reading // To check if the book is actively being used + var/custom = FALSE // To bypass init stuff, for player made textbooks and weird books. If true must have details manually set + var/ez_read = FALSE // Set to TRUE if you can read it without basic literacy skills var/skill_name = "missing skill name" var/progress = SKILLBOOK_PROG_FINISH // used to track the progress of making a custom book. defaults as finished so, you know, you can read the damn thing @@ -56,6 +56,8 @@ Skill books that increase your skills while you activate and hold them . = ..() + global.events_repository.register(/decl/observ/moved, src, src, .proc/check_buff) + if(!custom && skill && skill_req)// custom books should already have all they need skill_name = initial(skill.name) title = RANDOM_BOOK_TITLE(capitalize(skill_name)) @@ -82,44 +84,95 @@ Skill books that increase your skills while you activate and hold them /datum/skill_buff/skill_book limit = 1 // you can only read one book at a time nerd, therefore you can only get one buff at a time -/obj/item/book/skill/attack_self(mob/user) +/obj/item/book/skill/get_single_monetary_worth() + . = max(..(), 200) + (100 * skill_req) + +/obj/item/book/skill/proc/check_can_read(mob/user) + if(QDELETED(user)) + return FALSE + var/effective_title = length(title) ? title : "the textbook" + if(!CanPhysicallyInteract(user)) + to_chat(user, SPAN_WARNING("You can't reach [effective_title]!")) + return FALSE if(!skill || (custom && progress == SKILLBOOK_PROG_NONE)) - to_chat(user, SPAN_WARNING("The textbook is blank!")) - return + to_chat(user, SPAN_WARNING("[capitalize(effective_title)] is blank!")) + return FALSE if(custom && progress < SKILLBOOK_PROG_FINISH) - to_chat(user, SPAN_WARNING("The textbook is unfinished! You can't learn from it in this state!")) - return + to_chat(user, SPAN_WARNING("[capitalize(effective_title)] is unfinished! You can't learn from it in this state!")) + return FALSE if(!ez_read &&!user.skill_check(SKILL_LITERACY, SKILL_BASIC)) - to_chat(user, SPAN_WARNING(pick("Haha, you know you can't read. Good joke. Put [title] back.","You open up [title], but there aren't any pictures, so you close it again.","You don't know how to read! What good is this [name] to you?!"))) - return + to_chat(user, SPAN_WARNING(pick(list( + "Haha, you know you can't read. Good joke. Put [effective_title] back.", + "You open up [effective_title], but there aren't any pictures, so you close it again.", + "You don't know how to read! What good is [effective_title] to you?!" + )))) + return FALSE - if(reading) //Close book, get rid of buffs - src.unlearn(user) - to_chat(user, SPAN_NOTICE("You close the [name]. That's enough learning for now.")) - reading = FALSE - return + if(reading) + if(reading.resolve() != user) + to_chat(user, SPAN_WARNING("\The [reading.resolve()] is already reading [effective_title]!")) + else + to_chat(user, SPAN_WARNING("You are already reading [effective_title]!")) + return FALSE if(user.too_many_buffs(/datum/skill_buff/skill_book)) to_chat(user, SPAN_WARNING("You can't read two books at once!")) - return + return FALSE if(!user.skill_check(skill, skill_req)) - to_chat(user, SPAN_WARNING("[title] is too advanced for you! Try something easier, perhaps the \"For Idiots\" edition?")) - return + to_chat(user, SPAN_WARNING("[capitalize(title)] is too advanced for you! Try something easier, perhaps the \"For Idiots\" edition?")) + return FALSE + if(user.get_skill_value(skill) > skill_req) - to_chat(user, SPAN_WARNING("You already know everything [title] has to teach you!")) - return + to_chat(user, SPAN_WARNING("You already know everything [effective_title] has to teach you!")) + return FALSE - to_chat(user, SPAN_NOTICE("You open up the [name] and start reading...")) - if(user.do_skilled(4 SECONDS, SKILL_LITERACY, src, 0.75)) - var/list/buff = list() - buff[skill] = 1 - user.buff_skill(buff, buff_type = /datum/skill_buff/skill_book) - reading = TRUE - to_chat(user, SPAN_NOTICE("You find the information you need! Better keep the page open to reference it.")) - else - to_chat(user, SPAN_DANGER("Your perusal of the [name] was interrupted!")) - return + return TRUE + +/obj/item/book/skill/attack_self(mob/user) + return try_to_read(user) || ..() + +/obj/item/book/skill/verb/read_book() + set name = "Read Book" + set category = "Object" + set src in view(1) + try_to_read(usr) + +/obj/item/book/skill/proc/try_to_read(mob/user) + + if(istype(user, /mob/observer)) + to_chat(user, SPAN_WARNING("Ghosts can't read! Go away!")) + return TRUE + + if(isturf(loc)) + user.face_atom(src) + + if(user && user == reading?.resolve()) + //Close book, get rid of buffs + unlearn(user) + to_chat(user, SPAN_NOTICE("You close [title]. That's enough learning for now.")) + reading = null + STOP_PROCESSING(SSprocessing, src) + return TRUE + + if(!check_can_read(user)) + return FALSE + + to_chat(user, SPAN_NOTICE("You open up [title] and start reading...")) + if(!user.do_skilled(4 SECONDS, SKILL_LITERACY, src, 0.75)) + to_chat(user, SPAN_DANGER("Your perusal of [title] was interrupted!")) + return TRUE + + if(!check_can_read(user)) + return TRUE + + var/list/buff = list() + buff[skill] = 1 + user.buff_skill(buff, buff_type = /datum/skill_buff/skill_book) + reading = weakref(user) + to_chat(user, SPAN_NOTICE("You find the information you need! Better keep the page open to reference it.")) + START_PROCESSING(SSprocessing, src) + return TRUE // buff removal /obj/item/book/skill/proc/unlearn(var/mob/user) @@ -127,19 +180,30 @@ Skill books that increase your skills while you activate and hold them for(var/datum/skill_buff/skill_book/S in F) S.remove() -// Remove buffs when book goes away -/obj/item/book/skill/dropped(mob/user) - if(reading) - to_chat(user, SPAN_DANGER("You lose the page you were on! You can't cross-reference using the [name] like this!")) - var/mob/M = user - if(istype(M) && M.fetch_buffs_of_type(/datum/skill_buff/skill_book, 0)) - src.unlearn(user) - reading = FALSE - . = ..() +/obj/item/book/skill/Process() + if(!reading) + return PROCESS_KILL + check_buff() + +/obj/item/book/skill/proc/check_buff() + if(!reading) + return + var/mob/R = reading.resolve() + if(!istype(R) || !CanPhysicallyInteract(R)) + remove_buff() + +/obj/item/book/skill/proc/remove_buff() + var/mob/R = reading?.resolve() + reading = null + if(istype(R)) + to_chat(R, SPAN_DANGER("You lose the page you were on! You can't cross-reference using [title] like this!")) + if(R.fetch_buffs_of_type(/datum/skill_buff/skill_book, 0)) + unlearn(R) + STOP_PROCESSING(SSprocessing, src) + /obj/item/book/skill/Destroy() - var/mob/M = loc - if(istype(M) && M.fetch_buffs_of_type(/datum/skill_buff/skill_book, 0)) - src.unlearn(M) + global.events_repository.unregister(/decl/observ/moved, src, src) + remove_buff() . = ..() /obj/item/book/skill/get_codex_value() diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm index 286b60618a8..6fe932be188 100644 --- a/code/game/objects/items/weapons/defib.dm +++ b/code/game/objects/items/weapons/defib.dm @@ -334,10 +334,10 @@ if(check_blood_level(H)) make_announcement("buzzes, \"Warning - Patient is in hypovolemic shock and may require a blood transfusion.\"", "warning") //also includes heart damage - if(H.get_organ(BP_HEART)) //People may need more direct instruction - var/obj/item/organ/internal/heart/heart = H.get_organ(BP_HEART) - if(heart.is_bruised()) - make_announcement("buzzes, \"Danger! The patient has sustained a cardiac contusion and will require surgical treatment for full recovery!\"", "danger") + //People may need more direct instruction + var/obj/item/organ/internal/heart/heart = H.get_organ(BP_HEART) + if(heart?.is_bruised()) + make_announcement("buzzes, \"Danger! The patient has sustained a cardiac contusion and will require surgical treatment for full recovery!\"", "danger") //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total if(!do_after(user, chargetime, H)) diff --git a/code/game/objects/items/weapons/storage/med_pouch.dm b/code/game/objects/items/weapons/storage/med_pouch.dm index 5f214f52e7c..fd48e10d392 100644 --- a/code/game/objects/items/weapons/storage/med_pouch.dm +++ b/code/game/objects/items/weapons/storage/med_pouch.dm @@ -69,8 +69,9 @@ Single Use Emergency Pouches startswith = list( /obj/item/chems/hypospray/autoinjector/pouch_auto/stabilizer, + /obj/item/chems/hypospray/autoinjector/pouch_auto/painkillers, /obj/item/chems/pill/pouch_pill/stabilizer, - /obj/item/chems/pill/pouch_pill/painkillers, + /obj/item/chems/pill/pouch_pill/brute_meds, /obj/item/stack/medical/bruise_pack = 2, ) instructions = {" @@ -89,10 +90,10 @@ Single Use Emergency Pouches color = COLOR_SEDONA startswith = list( - /obj/item/chems/hypospray/autoinjector/pouch_auto/stabilizer, + /obj/item/chems/hypospray/autoinjector/pouch_auto/nanoblood, /obj/item/chems/hypospray/autoinjector/pouch_auto/painkillers, /obj/item/chems/hypospray/autoinjector/pouch_auto/adrenaline, - /obj/item/chems/pill/pouch_pill/painkillers, + /obj/item/chems/pill/pouch_pill/burn_meds, /obj/item/stack/medical/ointment = 2, ) instructions = {" @@ -183,6 +184,12 @@ Single Use Emergency Pouches /obj/item/chems/pill/pouch_pill/painkillers chem_type = /decl/material/liquid/painkillers +/obj/item/chems/pill/pouch_pill/brute_meds + chem_type = /decl/material/liquid/brute_meds + +/obj/item/chems/pill/pouch_pill/burn_meds + chem_type = /decl/material/liquid/burn_meds + /obj/item/chems/pill/pouch_pill/initialize_reagents() reagents.add_reagent(chem_type, chem_amount) var/decl/material/reagent = GET_DECL(chem_type) @@ -212,3 +219,7 @@ Single Use Emergency Pouches name = "emergency adrenaline autoinjector" amount_per_transfer_from_this = 8 starts_with = list(/decl/material/liquid/adrenaline = 8) + +/obj/item/chems/hypospray/autoinjector/pouch_auto/nanoblood + name = "emergency nanoblood autoinjector" + starts_with = list(/decl/material/liquid/nanoblood = 5) diff --git a/code/game/objects/items/weapons/storage/toolbox.dm b/code/game/objects/items/weapons/storage/toolbox.dm index c0e9bf9069d..1d54ceff8e2 100644 --- a/code/game/objects/items/weapons/storage/toolbox.dm +++ b/code/game/objects/items/weapons/storage/toolbox.dm @@ -67,7 +67,7 @@ startswith = list(/obj/item/clothing/gloves/insulated, /obj/item/screwdriver, /obj/item/wrench, /obj/item/weldingtool, /obj/item/crowbar, /obj/item/wirecutters, /obj/item/multitool) /obj/item/storage/toolbox/repairs - name = "electrician toolbox" + name = "electronics toolbox" desc = "A box full of boxes, with electrical machinery parts and tools needed to get them where they're needed." icon_state = "yellow_striped" item_state = "toolbox_yellow" diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 37bc4b3f616..02dc41df788 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -173,6 +173,9 @@ if(istype(W, /obj/item/grab)) var/obj/item/grab/G = W + if (G.affecting == G.assailant) + return TRUE + step(G.affecting, get_dir(G.affecting.loc, src)) return TRUE @@ -438,7 +441,7 @@ var/global/const/enterloopsanity = 100 if(flooded) LAZYADD(., global.flood_object) -/**Whether we can place a cable here +/**Whether we can place a cable here * If you cannot build a cable will return an error code explaining why you cannot. */ /turf/proc/cannot_build_cable() diff --git a/code/modules/atmospherics/components/unary/tank.dm b/code/modules/atmospherics/components/unary/tank.dm index abdc8d52454..e727497c43f 100644 --- a/code/modules/atmospherics/components/unary/tank.dm +++ b/code/modules/atmospherics/components/unary/tank.dm @@ -90,7 +90,8 @@ desc = "A large vessel containing pressurized gas." color = PIPE_COLOR_WHITE connect_types = CONNECT_TYPE_REGULAR|CONNECT_TYPE_REGULAR|CONNECT_TYPE_SCRUBBER|CONNECT_TYPE_FUEL - w_class = ITEM_SIZE_HUGE + w_class = ITEM_SIZE_STRUCTURE + density = 1 level = 1 dir = SOUTH constructed_path = /obj/machinery/atmospherics/unary/tank diff --git a/code/modules/atmospherics/pipes.dm b/code/modules/atmospherics/pipes.dm index ccf082dfd3e..9279dd15297 100644 --- a/code/modules/atmospherics/pipes.dm +++ b/code/modules/atmospherics/pipes.dm @@ -79,7 +79,7 @@ qdel(parent) ..() var/turf/T = loc - if(level == 1 && !T.is_plating()) + if(level == 1 && isturf(T) && !T.is_plating()) hide(1) /obj/machinery/atmospherics/pipe/return_air() diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 4fad2e08c3a..ca8e3897008 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -462,8 +462,8 @@ var/global/list/localhost_addresses = list( OnResize() /client - var/last_view_x_dim = 7 - var/last_view_y_dim = 7 + var/last_view_x_dim = 15 + var/last_view_y_dim = 15 /client/verb/force_onresize_view_update() set name = "Force Client View Update" @@ -514,8 +514,9 @@ var/global/const/MAX_VIEW = 41 winset(src, "menu.icon[divisor]", "is-checked=true") view = "[last_view_x_dim]x[last_view_y_dim]" - + // Reset eye/perspective + reset_click_catchers() var/last_perspective = perspective perspective = MOB_PERSPECTIVE if(perspective != last_perspective) diff --git a/code/modules/client/preference_setup/occupation/occupation.dm b/code/modules/client/preference_setup/occupation/occupation.dm index f2af0b7acbc..2216f00cae8 100644 --- a/code/modules/client/preference_setup/occupation/occupation.dm +++ b/code/modules/client/preference_setup/occupation/occupation.dm @@ -185,6 +185,9 @@ skill_link = "View Skills" skill_link = "[skill_link]" + if(!user.skillset.skills_transferable) + skill_link = "" + // Begin assembling the actual HTML. index += 1 if((index >= limit) || (job.title in splitJobs)) diff --git a/code/modules/clothing/_clothing.dm b/code/modules/clothing/_clothing.dm index d0466f49ed0..1495276e615 100644 --- a/code/modules/clothing/_clothing.dm +++ b/code/modules/clothing/_clothing.dm @@ -143,6 +143,8 @@ /obj/item/clothing/proc/refit_for_bodytype(var/target_bodytype) bodytype_equip_flags = target_bodytype + if(sprite_sheets[target_bodytype]) + icon = sprite_sheets[target_bodytype] /obj/item/clothing/get_examine_line() . = ..() diff --git a/code/modules/clothing/spacesuits/void/void.dm b/code/modules/clothing/spacesuits/void/void.dm index b92849eb22c..cf71c18f486 100644 --- a/code/modules/clothing/spacesuits/void/void.dm +++ b/code/modules/clothing/spacesuits/void/void.dm @@ -286,4 +286,13 @@ else if(##equipment_var) {\ /obj/item/clothing/suit/space/void/adjust_mob_overlay(var/mob/living/user_mob, var/bodytype, var/image/overlay, var/slot, var/bodypart) if(overlay && tank && slot == slot_back_str) overlay.overlays += tank.get_mob_overlay(user_mob, slot_back_str) - . = ..() \ No newline at end of file + . = ..() +/obj/item/clothing/suit/space/void/refit_for_bodytype(target_bodytype) + ..() + icon = get_icon_for_bodytype(target_bodytype) + queue_icon_update() + +/obj/item/clothing/head/helmet/space/void/refit_for_bodytype(target_bodytype) + ..() + icon = get_icon_for_bodytype(target_bodytype) + queue_icon_update() \ No newline at end of file diff --git a/code/modules/detectivework/tools/sample_kits/_sample_kit.dm b/code/modules/detectivework/tools/sample_kits/_sample_kit.dm index 2a2ebcb0fa8..78a0277c77b 100644 --- a/code/modules/detectivework/tools/sample_kits/_sample_kit.dm +++ b/code/modules/detectivework/tools/sample_kits/_sample_kit.dm @@ -32,7 +32,7 @@ /obj/item/forensics/sample_kit/afterattack(var/atom/A, var/mob/user, var/proximity) if(!proximity) return - if(user.skill_check(SKILL_FORENSICS, SKILL_ADEPT) && can_take_sample(user, A)) + if(user.skill_check(SKILL_FORENSICS, SKILL_BASIC) && can_take_sample(user, A)) take_sample(user,A) . = 1 else diff --git a/code/modules/materials/definitions/solids/materials_solid_metal.dm b/code/modules/materials/definitions/solids/materials_solid_metal.dm index c22ebb9d2d2..acd7844f7f1 100644 --- a/code/modules/materials/definitions/solids/materials_solid_metal.dm +++ b/code/modules/materials/definitions/solids/materials_solid_metal.dm @@ -193,7 +193,7 @@ . += new/datum/stack_recipe/furniture/canister(src) . += new/datum/stack_recipe/furniture/tank(src) . += new/datum/stack_recipe/cannon(src) - . += create_recipe_list(/datum/stack_recipe/tile/metal) + . += new/datum/stack_recipe_list("tiling", create_recipe_list(/datum/stack_recipe/tile/metal)) . += new/datum/stack_recipe/furniture/computerframe(src) . += new/datum/stack_recipe/furniture/machine(src) . += new/datum/stack_recipe/furniture/turret(src) diff --git a/code/modules/mob/language/alien/monkey.dm b/code/modules/mob/language/alien/monkey.dm index a96393b5dbd..a2a3bfcf473 100644 --- a/code/modules/mob/language/alien/monkey.dm +++ b/code/modules/mob/language/alien/monkey.dm @@ -5,6 +5,7 @@ ask_verb = "chimpers" exclaim_verb = "screeches" key = "" + flags = RESTRICTED syllables = list("ook", "eek", "hiss", "gronk") shorthand = "Ook" hidden_from_codex = 1 diff --git a/code/modules/mob/language/human/human.dm b/code/modules/mob/language/human/human.dm index cff1216db6b..767a0b7e1ff 100644 --- a/code/modules/mob/language/human/human.dm +++ b/code/modules/mob/language/human/human.dm @@ -5,7 +5,7 @@ speech_verb = "says" whisper_verb = "whispers" colour = "solcom" - flags = WHITELISTED + flags = WHITELISTED | RESTRICTED shorthand = "???" space_chance = 40 abstract_type = /decl/language/human diff --git a/code/modules/mob/living/carbon/human/appearance.dm b/code/modules/mob/living/carbon/human/appearance.dm index 0bb076ccc61..c0b481c8dac 100644 --- a/code/modules/mob/living/carbon/human/appearance.dm +++ b/code/modules/mob/living/carbon/human/appearance.dm @@ -14,6 +14,7 @@ return set_species(new_species) + dna.ready_dna(src) //Handle spawning stuff species.handle_pre_spawn(src) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index d1d89f91d15..845a45ab0e3 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1027,11 +1027,7 @@ default behaviour is: if(!hud_used.hide_actions_toggle) hud_used.hide_actions_toggle = new(hud_used) hud_used.hide_actions_toggle.UpdateIcon() - - if(!hud_used.hide_actions_toggle.moved) - hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(1) - //hud_used.SetButtonCoords(hud_used.hide_actions_toggle,1) - + hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(1) client.screen += hud_used.hide_actions_toggle return @@ -1039,11 +1035,11 @@ default behaviour is: for(var/datum/action/A in actions) button_number++ if(A.button == null) - var/obj/screen/movable/action_button/N = new(hud_used) + var/obj/screen/action_button/N = new(hud_used) N.owner = A A.button = N - var/obj/screen/movable/action_button/B = A.button + var/obj/screen/action_button/B = A.button B.UpdateIcon() @@ -1051,18 +1047,13 @@ default behaviour is: B.desc = A.UpdateDesc() client.screen += B - - if(!B.moved) - B.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number) - //hud_used.SetButtonCoords(B,button_number) + B.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number) if(button_number > 0) if(!hud_used.hide_actions_toggle) hud_used.hide_actions_toggle = new(hud_used) hud_used.hide_actions_toggle.InitialiseIcon(src) - if(!hud_used.hide_actions_toggle.moved) - hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number+1) - //hud_used.SetButtonCoords(hud_used.hide_actions_toggle,button_number+1) + hud_used.hide_actions_toggle.screen_loc = hud_used.ButtonNumberToScreenCoords(button_number+1) client.screen += hud_used.hide_actions_toggle /mob/living/handle_fall_effect(var/turf/landing) diff --git a/code/modules/mob/living/silicon/ai/latejoin.dm b/code/modules/mob/living/silicon/ai/latejoin.dm index 8a14cecbe0d..76b2a4ce312 100644 --- a/code/modules/mob/living/silicon/ai/latejoin.dm +++ b/code/modules/mob/living/silicon/ai/latejoin.dm @@ -26,3 +26,11 @@ //Handle job slot/tater cleanup. clear_client() + +/obj/effect/landmark/start/ai + name = "AI" + +/obj/effect/landmark/start/ai/Initialize() + . = ..() + //The job subsystem does its thing before we can, so we've got to handle this + empty_playable_ai_cores += new /obj/structure/aicore/deactivated(get_turf(loc)) \ No newline at end of file diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 99c28849792..fd2cb343be7 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -58,7 +58,7 @@ var/obj/screen/gun/move/gun_move_icon = null var/obj/screen/gun/mode/gun_setting_icon = null - var/obj/screen/movable/ability_master/ability_master = null + var/obj/screen/ability_master/ability_master = null /*A bunch of this stuff really needs to go under their own defines instead of being globally attached to mob. A variable should only be globally attached to turfs/objects/whatever, when it is in fact needed as such. diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 5637bbf6b28..54280d78b16 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -244,13 +244,10 @@ var/global/list/global/organ_rel_size = list( if(lowertext(newletter)=="a") newletter="ah" if(lowertext(newletter)=="c") newletter="k" switch(rand(1,15)) - if(1,3,5,8) newletter="[lowertext(newletter)]" - if(2,4,6,15) newletter="[uppertext(newletter)]" - if(7) newletter+="'" - if(9 to 14) break - //if(9,10) newletter="[newletter]" - //if(11,12) newletter="[newletter]" - //if(13) newletter="[newletter]" + if(1 to 4) newletter="[lowertext(newletter)]" + if(5 to 8) newletter="[uppertext(newletter)]" + if(9) newletter+="'" + else newletter = newletter newphrase+="[newletter]";counter-=1 return newphrase diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index 442263d481c..b4c25bf7001 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -663,7 +663,7 @@ This function completely restores a damaged organ to perfect condition. var/wound_type = get_wound_type(type, damage) if(wound_type) - var/datum/wound/W = new wound_type(damage, src) + var/datum/wound/W = new wound_type(damage, src, surgical) //Check whether we can add the wound to an existing wound if(surgical) @@ -1074,17 +1074,17 @@ Note that amputating the affected organ does in fact remove the infection from t G.basecolor = use_blood_color G.update_icon() - gore.throw_at(get_edge_target_turf(src,pick(global.alldirs)),rand(1,3),30) + gore.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) for(var/obj/item/organ/I in internal_organs) I.do_uninstall() //No owner so run uninstall directly I.dropInto(get_turf(loc)) if(!QDELETED(I) && isturf(loc)) - I.throw_at(get_edge_target_turf(src,pick(global.alldirs)),rand(1,3),30) + I.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) for(var/obj/item/I in src) I.dropInto(loc) - I.throw_at(get_edge_target_turf(src,pick(global.alldirs)),rand(1,3),30) + I.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) qdel(src) @@ -1484,8 +1484,9 @@ Note that amputating the affected organ does in fact remove the infection from t if(encased && (status & ORGAN_BROKEN)) . = SURGERY_ENCASED else - var/smol_threshold = min_broken_damage * 0.4 - var/beeg_threshold = min_broken_damage * 0.6 + var/total_health_coefficient = scale_max_damage_to_species_health ? (species.total_health / DEFAULT_SPECIES_HEALTH) : 1 + var/smol_threshold = max(1, FLOOR(min_broken_damage * 0.4 * total_health_coefficient)) + var/beeg_threshold = max(1, FLOOR(min_broken_damage * 0.6 * total_health_coefficient)) if(!incision.autoheal_cutoff == 0) //not clean incision smol_threshold *= 1.5 beeg_threshold = max(beeg_threshold, min(beeg_threshold * 1.5, incision.damage_list[1])) //wounds can't achieve bigger @@ -1568,7 +1569,9 @@ Note that amputating the affected organ does in fact remove the infection from t if(!BP_IS_PROSTHETIC(src) && !BP_IS_CRYSTAL(src)) var/decay_rate = damage/(max_damage*2) germ_level += round(rand(decay_rate,decay_rate*1.5)) //So instead, we're going to say the damage is so severe its functions are slowly failing due to the extensive damage - else + else //TODO: more advanced system for synths + if(istype(src,/obj/item/organ/external/chest) || istype(src,/obj/item/organ/external/groin)) + return status |= ORGAN_DEAD if(status & ORGAN_DEAD) //The organic dying part is covered in germ handling STOP_PROCESSING(SSobj, src) diff --git a/code/modules/organs/external/wounds/wound.dm b/code/modules/organs/external/wounds/wound.dm index 8f8cc9f0546..47e239bc8d8 100644 --- a/code/modules/organs/external/wounds/wound.dm +++ b/code/modules/organs/external/wounds/wound.dm @@ -29,7 +29,7 @@ var/tmp/list/desc_list = list() var/tmp/list/damage_list = list() -/datum/wound/New(var/damage, var/obj/item/organ/external/organ = null) +/datum/wound/New(var/damage, var/obj/item/organ/external/organ = null, var/surgical) created = world.time @@ -39,6 +39,10 @@ desc_list += V damage_list += stages[V] + // Surgical wounds need to be at minimum big enough to be considered open, which is max_bleeding_stage. + if(surgical) + damage = max(damage, damage_list[Clamp(max_bleeding_stage, 1, length(damage_list))]+1) + src.damage = damage // initialize with the appropriate stage diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index 3cc24e38039..ec288a3cd09 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -37,7 +37,7 @@ owner.remove_organ(src, FALSE, FALSE, TRUE, TRUE, FALSE) qdel(src) if(tmp_owner) - var/obj/item/organ/org = new replace_path(tmp_owner, given_dna = dna) + var/obj/item/organ/org = new replace_path(tmp_owner, null, dna) tmp_owner.add_organ(org, tmp_owner.get_organ(org.parent_organ), TRUE, TRUE) tmp_owner = null diff --git a/code/modules/organs/internal/posibrain.dm b/code/modules/organs/internal/posibrain.dm index 71dbe79f829..a529eb8fc8a 100644 --- a/code/modules/organs/internal/posibrain.dm +++ b/code/modules/organs/internal/posibrain.dm @@ -380,4 +380,5 @@ //Since the mmi_holder is an horrible hacky pos we turn it into a mmi on drop, since it shouldn't exist outside a mob /obj/item/organ/internal/mmi_holder/dropInto(atom/destination) . = ..() - transfer_and_delete() + if (!QDELETED(src)) + transfer_and_delete() diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 7c3b13d4277..076243b47a2 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -70,18 +70,19 @@ if(!given_dna) if(dna) given_dna = dna //Use existing if possible - else if(owner) - if(owner.dna) + else if(owner) + if(owner.dna) given_dna = owner.dna //Grab our owner's dna if we don't have any, and they have else //The owner having no DNA can be a valid reason to keep our dna null in some cases - dna = null + log_debug("obj/item/organ/setup_as_organic(): [src] had null dna, with a owner with null dna!") + dna = null //#TODO: Not sure that's really legal return else //If we have NO OWNER and given_dna, just make one up for consistency given_dna = new/datum/dna() given_dna.check_integrity() //Defaults everything - + set_dna(given_dna) setup_reagents() return TRUE @@ -97,7 +98,7 @@ if(istype(material)) robotize(forced_model, apply_material = material.type) - else + else robotize(forced_model) return TRUE @@ -109,8 +110,7 @@ reagents.add_reagent(/decl/material/liquid/nutriment/protein, reagents.maximum_volume) /obj/item/organ/proc/set_dna(var/datum/dna/new_dna) - if(!new_dna) - return + QDEL_NULL(dna) dna = new_dna.Clone() if(!blood_DNA) blood_DNA = list() @@ -122,7 +122,7 @@ if(istext(specie_name)) species = get_species_by_key(specie_name) else - species = specie_name + species = specie_name if(!species) species = get_species_by_key(global.using_map.default_species) PRINT_STACK_TRACE("Invalid species. Expected a valid species name as string, was: [log_info_line(specie_name)]") @@ -132,7 +132,12 @@ // Adjust limb health proportinate to total species health. var/total_health_coefficient = scale_max_damage_to_species_health ? (species.total_health / DEFAULT_SPECIES_HEALTH) : 1 + + //Use initial value to prevent scaling down each times we change the species during init absolute_max_damage = initial(absolute_max_damage) + min_broken_damage = initial(min_broken_damage) + max_damage = initial(max_damage) + if(absolute_max_damage) absolute_max_damage = max(1, FLOOR(absolute_max_damage * total_health_coefficient)) min_broken_damage = max(1, FLOOR(absolute_max_damage * 0.5)) @@ -492,7 +497,7 @@ var/global/list/ailment_reference_cache = list() /obj/item/organ/proc/do_install(var/mob/living/carbon/human/target, var/obj/item/organ/external/affected, var/in_place = FALSE, var/update_icon = TRUE, var/detached = FALSE) //Make sure to force the flag accordingly set_detached(detached) - + owner = target action_button_name = initial(action_button_name) if(owner) @@ -506,10 +511,10 @@ var/global/list/ailment_reference_cache = list() //Handles uninstalling the organ from its owner and parent limb, without triggering effects or deep updates //CASES: -// 1. Before deletion to clear our references. +// 1. Before deletion to clear our references. // 2. Called through removal on surgery or dismemberement // 3. Called when we're changing a mob's species. -//detach: If detach is true, we're going to set the organ to detached, and add it to the detached organs list, and remove it from processing lists. +//detach: If detach is true, we're going to set the organ to detached, and add it to the detached organs list, and remove it from processing lists. // If its false, we just remove the organ from all lists /obj/item/organ/proc/do_uninstall(var/in_place = FALSE, var/detach = FALSE, var/ignore_children = FALSE, var/update_icon = TRUE) action_button_name = null @@ -520,7 +525,7 @@ var/global/list/ailment_reference_cache = list() if(ailment.timer_id) deltimer(ailment.timer_id) ailment.timer_id = null - + //When we detach, we set the ORGAN_CUT_AWAY flag on, depending on whether the organ supports it or not if(detach) set_detached(TRUE) diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/power/fusion/core/core_field.dm index efb09516afd..c053090aac8 100644 --- a/code/modules/power/fusion/core/core_field.dm +++ b/code/modules/power/fusion/core/core_field.dm @@ -35,6 +35,7 @@ /obj/effect, /obj/structure/cable, /obj/machinery/atmospherics, + /obj/machinery/air_sensor, /obj/machinery/power/terminal ) diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index 3a5df59e9c7..c06d54c6aec 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -20,7 +20,7 @@ /obj/machinery/power/terminal/Initialize() . = ..() var/turf/T = src.loc - if(level == 1) + if(level == 1 && isturf(T)) hide(!T.is_plating()) /obj/machinery/power/terminal/Destroy() @@ -36,7 +36,7 @@ to_chat(user, SPAN_WARNING("You must remove the floor plating in front of \the [machine] first!")) return - // If this is a terminal that's somehow been left behind, let it be removed freely. + // If this is a terminal that's somehow been left behind, let it be removed freely. if(machine && !machine.components_are_accessible(/obj/item/stock_parts/power/terminal)) to_chat(user, SPAN_WARNING("You must open the panel on \the [machine] first!")) return @@ -88,7 +88,7 @@ . = ..() if(master) var/obj/machinery/machine = master_machine() - + // Wall frames and SMES have directional terminals. if(!master.terminal_dir && !ispath(machine.frame_type, /obj/item/frame)) icon_state = "term-omni" diff --git a/code/modules/reagents/dispenser/dispenser2.dm b/code/modules/reagents/dispenser/dispenser2.dm index 89a6c815769..1f0867b44f8 100644 --- a/code/modules/reagents/dispenser/dispenser2.dm +++ b/code/modules/reagents/dispenser/dispenser2.dm @@ -33,6 +33,9 @@ /obj/item/chems/drinks ) + var/beaker_offset = 0 + var/beaker_positions = list(0,1) + /obj/machinery/chemical_dispenser/Initialize(mapload, d=0, populate_parts = TRUE) . = ..() if(spawn_cartridges && populate_parts) @@ -196,5 +199,6 @@ if(container) var/mutable_appearance/beaker_overlay beaker_overlay = image(src, src, "lil_beaker") - beaker_overlay.pixel_x = rand(-10, 5) + beaker_overlay.pixel_y = beaker_offset + beaker_overlay.pixel_x = pick(beaker_positions) overlays += beaker_overlay diff --git a/code/modules/reagents/dispenser/dispenser_presets.dm b/code/modules/reagents/dispenser/dispenser_presets.dm index d0f2b5beeb7..a42e49a4ae4 100644 --- a/code/modules/reagents/dispenser/dispenser_presets.dm +++ b/code/modules/reagents/dispenser/dispenser_presets.dm @@ -57,6 +57,8 @@ core_skill = SKILL_COOKING can_contaminate = FALSE //It's not a complex panel, and I'm fairly sure that most people don't haymaker the control panel on a soft drinks machine. -- Chaoko99 base_type = /obj/machinery/chemical_dispenser/bar_soft + beaker_offset = -2 + beaker_positions = list(-1,3,7,11,15) /obj/machinery/chemical_dispenser/bar_soft/full spawn_cartridges = list( @@ -93,6 +95,8 @@ core_skill = SKILL_COOKING can_contaminate = FALSE //See above. base_type = /obj/machinery/chemical_dispenser/bar_alc + beaker_offset = -2 + beaker_positions = list(-3,2,7,12,17) /obj/machinery/chemical_dispenser/bar_alc/full @@ -128,6 +132,8 @@ core_skill = SKILL_COOKING can_contaminate = FALSE //See above. base_type = /obj/machinery/chemical_dispenser/bar_coffee + beaker_offset = -2 + beaker_positions = list(0,14) /obj/machinery/chemical_dispenser/bar_coffee/full diff --git a/code/modules/reagents/storage/pill_bottle_subtypes.dm b/code/modules/reagents/storage/pill_bottle_subtypes.dm index fa1ef8da0e7..f795da595b7 100644 --- a/code/modules/reagents/storage/pill_bottle_subtypes.dm +++ b/code/modules/reagents/storage/pill_bottle_subtypes.dm @@ -74,7 +74,7 @@ desc = "Commonly found on paramedics, these assorted pill bottles contain all the basics." startswith = list( - /obj/item/chems/pill/adrenaline = 6, + /obj/item/chems/pill/stabilizer = 6, /obj/item/chems/pill/antitoxins = 6, /obj/item/chems/pill/sugariron = 2, /obj/item/chems/pill/painkillers = 2, diff --git a/code/modules/surgery/generic.dm b/code/modules/surgery/generic.dm index 6988384b0fe..86cd9dceffd 100644 --- a/code/modules/surgery/generic.dm +++ b/code/modules/surgery/generic.dm @@ -49,7 +49,7 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) user.visible_message("[user] has made [access_string] on [target]'s [affected.name] with \the [tool].", \ "You have made [access_string] on [target]'s [affected.name] with \the [tool].",) - affected.createwound(CUT, affected.min_broken_damage/2, 1) + affected.createwound(CUT, CEILING(affected.min_broken_damage/2), TRUE) playsound(target.loc, 'sound/weapons/bladeslice.ogg', 15, 1) if(tool.damtype == BURN) affected.clamp_organ() diff --git a/code/modules/surgery/necrotic.dm b/code/modules/surgery/necrotic.dm index 811876ac329..ef49c939e6e 100644 --- a/code/modules/surgery/necrotic.dm +++ b/code/modules/surgery/necrotic.dm @@ -53,7 +53,7 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) var/target_organ = LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone) user.visible_message("\The [user] slowly starts removing necrotic tissue from \the [target]'s [target_organ] with \the [tool].", \ - "You slowly start removing necrotic tissue from \the [target]'s [target_organ)] with \the [tool].") + "You slowly start removing necrotic tissue from \the [target]'s [target_organ] with \the [tool].") target.custom_pain("You feel sporadic spikes of pain from points around your [affected.name]!",20, affecting = affected) ..() diff --git a/code/unit_tests/equipment_tests.dm b/code/unit_tests/equipment_tests.dm index ef4ba4edffd..2ca191ed995 100644 --- a/code/unit_tests/equipment_tests.dm +++ b/code/unit_tests/equipment_tests.dm @@ -1,7 +1,3 @@ -#define SUCCESS 1 -#define FAILURE 0 - - /datum/unit_test/vision_glasses name = "EQUIPMENT: Vision Template" template = /datum/unit_test/vision_glasses @@ -101,7 +97,3 @@ bad_tests++ return bad_tests - -#undef SUCCESS -#undef FAILURE - diff --git a/code/unit_tests/map_tests.dm b/code/unit_tests/map_tests.dm index 6bd35d9318e..93a950869fd 100644 --- a/code/unit_tests/map_tests.dm +++ b/code/unit_tests/map_tests.dm @@ -5,11 +5,6 @@ * * */ - -#define FAILURE 0 -#define SUCCESS 1 - - /datum/unit_test/apc_area_test name = "MAP: Area Test APC / Scrubbers / Vents" @@ -864,6 +859,3 @@ else pass("All doors are on appropriate turfs") return TRUE - -#undef SUCCESS -#undef FAILURE diff --git a/code/unit_tests/mob_tests.dm b/code/unit_tests/mob_tests.dm index 3d8b5b0b827..28ce29a6ed6 100644 --- a/code/unit_tests/mob_tests.dm +++ b/code/unit_tests/mob_tests.dm @@ -8,9 +8,6 @@ * */ -#define SUCCESS 1 -#define FAILURE 0 - // // Tests Life() and mob breathing in space. // @@ -293,8 +290,6 @@ var/global/default_mobloc = null return 1 #undef IMMUNE -#undef SUCCESS -#undef FAILURE /datum/unit_test/mob_nullspace name = "MOB: Mob in nullspace shall not cause runtimes" diff --git a/code/unit_tests/zas_tests.dm b/code/unit_tests/zas_tests.dm index c3ef6698b4d..819d310f357 100644 --- a/code/unit_tests/zas_tests.dm +++ b/code/unit_tests/zas_tests.dm @@ -6,14 +6,6 @@ * */ -#define UT_NORMAL 1 // Standard one atmosphere 20celsius -#define UT_VACUUM 2 // Vacume on simulated turfs -#define UT_NORMAL_COLD 3 // Cold but standard atmosphere. - -#define FAILURE 0 -#define SUCCESS 1 -#define SKIP 2 - // // Generic check for an area. // @@ -153,7 +145,7 @@ if(world.time < testtime) return 0 for(var/area/A in shuttle.shuttle_area) - var/list/test = test_air_in_area(A.type) + var/list/test = test_air_in_area(A.type, global.using_map.shuttle_atmos_expectation) if(isnull(test)) fail("Check Runtimed") return 1 @@ -163,9 +155,3 @@ if(SKIP) skip(test["msg"]) else fail(test["msg"]) return 1 - -#undef UT_NORMAL -#undef UT_VACUUM -#undef UT_NORMAL_COLD -#undef SUCCESS -#undef FAILURE diff --git a/maps/~mapsystem/maps_unit_testing.dm b/maps/~mapsystem/maps_unit_testing.dm index 5a30577988e..35696dde5c0 100644 --- a/maps/~mapsystem/maps_unit_testing.dm +++ b/maps/~mapsystem/maps_unit_testing.dm @@ -3,6 +3,9 @@ var/const/NO_VENT = 2 var/const/NO_SCRUBBER = 4 + /// Defines the expected result of the atmospherics shuttle unit test for atmosphere. + var/shuttle_atmos_expectation = UT_NORMAL + // Unit test vars var/list/apc_test_exempt_areas = list( /area/space = NO_SCRUBBER|NO_VENT|NO_APC, diff --git a/mods/species/neoavians/datum/language.dm b/mods/species/neoavians/datum/language.dm index 02531011b2d..eb549c3426f 100644 --- a/mods/species/neoavians/datum/language.dm +++ b/mods/species/neoavians/datum/language.dm @@ -49,6 +49,7 @@ name = "Neo-Avian Milieu" description = "Neo-avians form a loose coalition of family and flock groupings, and are usually in an extreme minority in human settlements. \ They tend to cope poorly with confined, crowded spaces like human habs, and often make their homes in hab domes or other spacious facilities." + language = /decl/language/neoavian secondary_langs = list( /decl/language/corvid, /decl/language/neoavian, diff --git a/nebula.dme b/nebula.dme index a53cdaad774..aed9a1fe2c2 100644 --- a/nebula.dme +++ b/nebula.dme @@ -85,6 +85,7 @@ #include "code\__defines\tools.dm" #include "code\__defines\topic.dm" #include "code\__defines\turfs.dm" +#include "code\__defines\unit_tests.dm" #include "code\__defines\webhooks.dm" #include "code\__defines\xenoarcheaology.dm" #include "code\__defines\ZAS.dm" @@ -163,7 +164,6 @@ #include "code\_onclick\hud\gun_mode.dm" #include "code\_onclick\hud\hud.dm" #include "code\_onclick\hud\human.dm" -#include "code\_onclick\hud\movable_screen_objects.dm" #include "code\_onclick\hud\other_mobs.dm" #include "code\_onclick\hud\pai.dm" #include "code\_onclick\hud\radial.dm"