diff --git a/.github/workflows/generate_documentation.yml b/.github/workflows/generate_documentation.yml new file mode 100644 index 00000000000..ea7386f69a2 --- /dev/null +++ b/.github/workflows/generate_documentation.yml @@ -0,0 +1,36 @@ +name: Generate Documentation + +on: + push: + branches: + - dev + workflow_dispatch: + +env: + SPACEMAN_DMM_VERSION: suite-1.7.2 + +jobs: + generate_documentation: + if: "!contains(github.event.head_commit.message, '[ci skip]')" + runs-on: ubuntu-latest + concurrency: gen-docs + steps: + - uses: actions/checkout@5a4ac9002d0be2fb38bd78e4b4dbde5606d7042f + - name: Setup Cache + uses: actions/cache@26968a09c0ea4f3e233fdddbafd1166051a095f6 + with: + path: $HOME/spaceman_dmm/$SPACEMAN_DMM_VERSION + key: ${{ runner.os }}-spacemandmm-${{ env.SPACEMAN_DMM_VERSION }} + - name: Install dmdoc + run: scripts/install-spaceman-dmm.sh dmdoc + - name: Generate documentation + run: | + ~/dmdoc + touch dmdoc/.nojekyll + - name: Deploy + uses: JamesIves/github-pages-deploy-action@da91e735be5cabb471a4b8afe367d10606da4683 + with: + token: ${{ secrets.GITHUB_TOKEN }} + branch: gh-pages-dmdoc + folder: dmdoc + force: false diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cfab69d15d9..70fca081971 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,8 +15,8 @@ on: env: BYOND_MAJOR: "514" - BYOND_MINOR: "1572" - SPACEMAN_DMM_VERSION: suite-1.7.1 + BYOND_MINOR: "1575" + SPACEMAN_DMM_VERSION: suite-1.7.2 jobs: DreamChecker: diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 60d9e9ad8fc..ad43ea70311 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,5 +1,6 @@ { "recommendations": [ - "ss13.byond" + "ss13.byond", + "anturk.dmi-editor" ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index ecfc95d317b..04e29897ec5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,4 @@ { - // Lets you preview .dmi files in the editor - "workbench.editorAssociations": { - "*.dmi": "imagePreview.previewEditor" - }, "files.associations": { "*.tmpl": "html" }, diff --git a/SpacemanDMM.toml b/SpacemanDMM.toml index 7f74da78240..1dcd669c09f 100644 --- a/SpacemanDMM.toml +++ b/SpacemanDMM.toml @@ -1,6 +1,9 @@ [langserver] dreamchecker = true +[dmdoc] +use_typepath_names = true + [code_standards] disallow_relative_type_definitions = true disallow_relative_proc_definitions = true diff --git a/code/__defines/_tick.dm b/code/__defines/_tick.dm index 734ab37f03a..63f8b443420 100644 --- a/code/__defines/_tick.dm +++ b/code/__defines/_tick.dm @@ -1,19 +1,30 @@ -#define TICK_LIMIT_RUNNING 80 -#define TICK_LIMIT_TO_RUN 78 +/// Percentage of tick to leave for master controller to run +#define MAPTICK_MC_MIN_RESERVE 70 +#define MAPTICK_LAST_INTERNAL_TICK_USAGE (world.map_cpu) + +/// Tick limit while running normally +#define TICK_BYOND_RESERVE 2 +#define TICK_LIMIT_RUNNING (max(100 - TICK_BYOND_RESERVE - MAPTICK_LAST_INTERNAL_TICK_USAGE, MAPTICK_MC_MIN_RESERVE)) +/// Tick limit used to resume things in stoplag +#define TICK_LIMIT_TO_RUN 70 +/// Tick limit for MC while running #define TICK_LIMIT_MC 70 -#define TICK_LIMIT_MC_INIT_DEFAULT 98 +/// Tick limit while initializing +#define TICK_LIMIT_MC_INIT_DEFAULT (100 - TICK_BYOND_RESERVE) -#define TICK_USAGE world.tick_usage //for general usage -#define TICK_USAGE_REAL world.tick_usage //to be used where the result isn't checked +/// for general usage of tick_usage +#define TICK_USAGE world.tick_usage +/// Returns true if tick_usage is above the limit #define TICK_CHECK ( TICK_USAGE > Master.current_ticklimit ) -#define CHECK_TICK if TICK_CHECK stoplag() +/// runs stoplag if tick_usage is above the limit +#define CHECK_TICK ( TICK_CHECK ? stoplag() : 0 ) //"fancy" math for calculating time in ms from tick_usage percentage and the length of ticks //percent_of_tick_used * (ticklag * 100(to convert to ms)) / 100(percent ratio) //collapsed to percent_of_tick_used * tick_lag #define TICK_DELTA_TO_MS(percent_of_tick_used) ((percent_of_tick_used) * world.tick_lag) -#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(TICK_USAGE_REAL - starting_tickusage)) +#define TICK_USAGE_TO_MS(starting_tickusage) (TICK_DELTA_TO_MS(TICK_USAGE - starting_tickusage)) //time of day but automatically adjusts to the server going into the next day within the same round. //for when you need a reliable time number that doesn't depend on byond time. diff --git a/code/__defines/chemistry.dm b/code/__defines/chemistry.dm index 79638b0aaf0..86b875b0fce 100644 --- a/code/__defines/chemistry.dm +++ b/code/__defines/chemistry.dm @@ -47,7 +47,7 @@ #define IGNORE_MOB_SIZE BITFLAG(0) #define AFFECTS_DEAD BITFLAG(1) -#define HANDLE_REACTIONS(_reagents) SSmaterials.active_holders[_reagents] = TRUE +#define HANDLE_REACTIONS(_reagents) if(!QDELETED(_reagents)) { SSmaterials.active_holders[_reagents] = TRUE; } #define UNQUEUE_REACTIONS(_reagents) SSmaterials.active_holders -= _reagents #define REAGENT_LIST(R) (R.reagents?.get_reagents() || "No reagent holder") diff --git a/code/__defines/computers.dm b/code/__defines/computers.dm index 4ba26584d5f..ab31c52e45e 100644 --- a/code/__defines/computers.dm +++ b/code/__defines/computers.dm @@ -11,10 +11,10 @@ // Network mainframe roles #define MF_ROLE_FILESERVER "FILE SERVER" -#define MF_ROLE_EMAIL_SERVER "EMAIL SERVER" #define MF_ROLE_LOG_SERVER "LOG SERVER" #define MF_ROLE_CREW_RECORDS "RECORDS SERVER" #define MF_ROLE_SOFTWARE "SOFTWARE REPOSITORY" +#define MF_ROLE_ACCOUNT_SERVER "ACCOUNT SERVER" // Program bitflags #define PROGRAM_CONSOLE BITFLAG(0) diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm index 79a9888f945..01333652cd6 100644 --- a/code/__defines/damage_organs.dm +++ b/code/__defines/damage_organs.dm @@ -49,6 +49,7 @@ #define ORGAN_PROSTHETIC BITFLAG(11) // The organ is prosthetic. Changes numerous behaviors, search BP_IS_PROSTHETIC for checks. #define ORGAN_BRITTLE BITFLAG(12) // The organ takes additional blunt damage. If robotic, cannot be repaired through normal means. #define ORGAN_CRYSTAL BITFLAG(13) // The organ does not suffer laser damage, but shatters on droplimb. +#define ORGAN_DISLOCATED BITFLAG(14) // Organ flag defines. #define ORGAN_FLAG_CAN_AMPUTATE BITFLAG(0) // The organ can be amputated. @@ -58,6 +59,7 @@ #define ORGAN_FLAG_FINGERPRINT BITFLAG(4) // The organ has a fingerprint. #define ORGAN_FLAG_HEALS_OVERKILL BITFLAG(5) // The organ heals from overkill damage. #define ORGAN_FLAG_DEFORMED BITFLAG(6) // The organ is permanently disfigured. +#define ORGAN_FLAG_CAN_DISLOCATE BITFLAG(7) // The organ can be dislocated. // Droplimb types. #define DISMEMBER_METHOD_EDGE 0 diff --git a/code/__defines/fluids.dm b/code/__defines/fluids.dm index 9038dde0322..b3314534543 100644 --- a/code/__defines/fluids.dm +++ b/code/__defines/fluids.dm @@ -8,11 +8,11 @@ #define FLUID_PUSH_THRESHOLD 20 // Amount of flow needed to push items. // Expects /turf for T. -#define ADD_ACTIVE_FLUID_SOURCE(T) SSfluids.water_sources[T] = TRUE +#define ADD_ACTIVE_FLUID_SOURCE(T) if(!T.changing_turf) { SSfluids.water_sources[T] = TRUE; } #define REMOVE_ACTIVE_FLUID_SOURCE(T) SSfluids.water_sources -= T // Expects /obj/effect/fluid for F. -#define ADD_ACTIVE_FLUID(F) SSfluids.active_fluids[F] = TRUE +#define ADD_ACTIVE_FLUID(F) if(!QDELETED(F)) { SSfluids.active_fluids[F] = TRUE; } #define REMOVE_ACTIVE_FLUID(F) SSfluids.active_fluids -= F // Expects turf for T, 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/misc.dm b/code/__defines/misc.dm index e753c7766f8..6e7b541e2ff 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -116,11 +116,6 @@ #define PROJECTILE_CONTINUE -1 //if the projectile should continue flying after calling bullet_act() #define PROJECTILE_FORCE_MISS -2 //if the projectile should treat the attack as a miss (suppresses attack and admin logs) - only applies to mobs. -//Camera capture modes -#define CAPTURE_MODE_REGULAR 0 //Regular polaroid camera mode -#define CAPTURE_MODE_ALL 1 //Admin camera mode -#define CAPTURE_MODE_PARTIAL 3 //Simular to regular mode, but does not do dummy check - //objectives #define CONFIG_OBJECTIVE_NONE 2 #define CONFIG_OBJECTIVE_VERB 1 diff --git a/code/__defines/qdel.dm b/code/__defines/qdel.dm index 337afb2ff03..25683bb5c8b 100644 --- a/code/__defines/qdel.dm +++ b/code/__defines/qdel.dm @@ -27,6 +27,7 @@ #define QDEL_IN(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE) #define QDEL_IN_CLIENT_TIME(item, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/qdel, item), time, TIMER_STOPPABLE | TIMER_CLIENT_TIME) #define QDEL_NULL(item) if(item) {qdel(item); item = null} +#define QDEL_NULL_SCREEN(item) if(client) { client.screen -= item; }; QDEL_NULL(item) #define QDEL_NULL_LIST(x) if(x) { for(var/y in x) { qdel(y) }}; if(x) {x.Cut(); x = null } // Second x check to handle items that LAZYREMOVE on qdel. #define QDEL_LIST(L) if(L) { for(var/I in L) qdel(I); L.Cut(); } #define QDEL_LIST_IN(L, time) addtimer(CALLBACK(GLOBAL_PROC, .proc/______qdel_list_wrapper, L), time, TIMER_STOPPABLE) diff --git a/code/__defines/species.dm b/code/__defines/species.dm index fe2e22f6969..75ab5d81503 100644 --- a/code/__defines/species.dm +++ b/code/__defines/species.dm @@ -10,6 +10,8 @@ #define SPECIES_FLAG_NO_BLOCK BITFLAG(8) // Unable to block or defend itself from attackers. #define SPECIES_FLAG_NEED_DIRECT_ABSORB BITFLAG(9) // This species can only have their DNA taken by direct absorption. #define SPECIES_FLAG_LOW_GRAV_ADAPTED BITFLAG(10) // This species is used to lower than standard gravity, affecting stamina in high-grav +#define SPECIES_FLAG_CRYSTALLINE BITFLAG(11) // This species is made of crystalline material. Replaces var/is_crystalline. +#define SPECIES_FLAG_SYNTHETIC BITFLAG(12) // This species is synthetic/robotic and spawns with prosthetic limbs. // Species spawn flags #define SPECIES_IS_WHITELISTED BITFLAG(0) // Must be whitelisted to play. diff --git a/code/__defines/temperature.dm b/code/__defines/temperature.dm index a1280715022..33003c92d12 100644 --- a/code/__defines/temperature.dm +++ b/code/__defines/temperature.dm @@ -1,24 +1,39 @@ -#define ATOM_IS_TEMPERATURE_SENSITIVE(A) (A && !QDELETED(A) && !(A.atom_flags & ATOM_FLAG_NO_TEMP_CHANGE)) +#define ATOM_IS_TEMPERATURE_SENSITIVE(A) (A && !(A.atom_flags & ATOM_FLAG_NO_TEMP_CHANGE)) +#define ATOM_SHOULD_TEMPERATURE_ENQUEUE(A) (ATOM_IS_TEMPERATURE_SENSITIVE(A) && !QDELETED(A)) #define ATOM_TEMPERATURE_EQUILIBRIUM_THRESHOLD 5 #define ATOM_TEMPERATURE_EQUILIBRIUM_CONSTANT 0.25 #define ADJUST_ATOM_TEMPERATURE(_atom, _temp) \ - if(!QDELETED(_atom)) { \ - _atom.temperature = _temp; \ - if(!QDELETED(_atom.reagents)) { \ - HANDLE_REACTIONS(_atom.reagents); \ + _atom.temperature = _temp; \ + HANDLE_REACTIONS(_atom.reagents); \ + QUEUE_TEMPERATURE_ATOMS(_atom); + +#define QUEUE_TEMPERATURE_ATOMS(_atoms) \ + if(islist(_atoms)) { \ + for(var/thing in _atoms) { \ + var/atom/A = thing; \ + QUEUE_TEMPERATURE_ATOM(A); \ } \ - QUEUE_TEMPERATURE_ATOMS(_atom);\ + } else { \ + QUEUE_TEMPERATURE_ATOM(_atoms); \ } -#define QUEUE_TEMPERATURE_ATOMS(_atoms) \ +#define QUEUE_TEMPERATURE_ATOM(_atom) \ + if(ATOM_SHOULD_TEMPERATURE_ENQUEUE(_atom)) { \ + SStemperature.processing[_atom] = TRUE; \ + } + +#define UNQUEUE_TEMPERATURE_ATOMS(_atoms) \ if(islist(_atoms)) { \ for(var/thing in _atoms) { \ var/atom/A = thing; \ - if(ATOM_IS_TEMPERATURE_SENSITIVE(A)) { \ - SStemperature.processing[A] = TRUE; \ - } \ + UNQUEUE_TEMPERATURE_ATOM(A); \ } \ - } else if(ATOM_IS_TEMPERATURE_SENSITIVE(_atoms)) { \ - SStemperature.processing[_atoms] = TRUE; \ + } else { \ + UNQUEUE_TEMPERATURE_ATOM(_atoms); \ } + +#define UNQUEUE_TEMPERATURE_ATOM(_atom) \ + if(ATOM_IS_TEMPERATURE_SENSITIVE(_atom)) { \ + SStemperature.processing -= _atom; \ + } \ No newline at end of file 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/_global_vars/configuration.dm b/code/_global_vars/configuration.dm index aec7a3eb94f..e5f6bc2e132 100644 --- a/code/_global_vars/configuration.dm +++ b/code/_global_vars/configuration.dm @@ -1,7 +1,7 @@ // Bomb cap! var/global/max_explosion_range = 14 var/global/href_logfile = null -var/global/game_version = "Nebula13" +var/global/game_version = "Nebula" var/global/changelog_hash = "" var/global/join_motd = null diff --git a/code/_global_vars/lists/objects.dm b/code/_global_vars/lists/objects.dm index b8baa941fba..1d4285d5f28 100644 --- a/code/_global_vars/lists/objects.dm +++ b/code/_global_vars/lists/objects.dm @@ -15,8 +15,13 @@ var/global/host = null //only here until check @ code\modules\ghosttrap\trap.dm: var/global/datum/sun/sun = new var/global/datum/universal_state/universe = new -var/global/list/vowels = list("a","e","i","o","u") -var/global/list/alphabet_no_vowels = list("b","c","d","f","g","h","j","k","l","m","n","p","q","r","s","t","v","w","x","z") -var/global/list/full_alphabet = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z") +/// Vowels. +var/global/list/vowels = list("a","e","i","o","u") +/// Alphabet a-z. +var/global/list/alphabet = list("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z") +/// Alphabet A-Z. +var/global/list/alphabet_capital = list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") +/// Numbers 0-9. +var/global/list/numbers = list("0","1","2","3","4","5","6","7","8","9") var/global/list/meteor_list = list() diff --git a/code/_helpers/cmp.dm b/code/_helpers/cmp.dm index 8c109261f1e..375e1d5397d 100644 --- a/code/_helpers/cmp.dm +++ b/code/_helpers/cmp.dm @@ -85,8 +85,8 @@ /proc/cmp_program(var/datum/computer_file/program/A, var/datum/computer_file/program/B) return cmp_text_asc(A.filedesc, B.filedesc) -/proc/cmp_emails_asc(var/datum/computer_file/data/email_account/A, var/datum/computer_file/data/email_account/B) - return cmp_text_asc(A.login,B.login) +/proc/cmp_accounts_asc(var/datum/computer_file/data/account/A, var/datum/computer_file/data/account/B) + return cmp_text_asc(A.login, B.login) /proc/cmp_planelayer(atom/A, atom/B) return (B.plane - A.plane) || (B.layer - A.layer) diff --git a/code/_helpers/icons.dm b/code/_helpers/icons.dm index a0f0330a0aa..b06d09cf38e 100644 --- a/code/_helpers/icons.dm +++ b/code/_helpers/icons.dm @@ -872,56 +872,67 @@ The _flatIcons list is a cache for generated icon files. result.Swap(i, gap + i) swapped = 1 return result -/* -generate_image function generates image of specified range and location -arguments tx, ty, tz are target coordinates (requred), range defines render distance to opposite corner (requred) -cap_mode is capturing mode (optional), user is capturing mob (requred only wehen cap_mode = CAPTURE_MODE_REGULAR), -lighting determines lighting capturing (optional), suppress_errors suppreses errors and continues to capture (optional). +/** + * Generate_image function generates image of specified range and location: + * * arguments `target_x`, `target_y`, `target_z` are target coordinates (requred). + * * `range` defines render distance to opposite corner (requred). + * * lighting determines lighting capturing (optional), suppress_errors suppreses errors and continues to capture (optional). + * * `checker` is a person from which side will be perfored capture check, should be `/mob/living` target_ype. */ -/proc/generate_image(var/tx as num, var/ty as num, var/tz as num, var/range as num, var/cap_mode = CAPTURE_MODE_PARTIAL, var/mob/living/user, var/lighting = 1, var/suppress_errors = 1) - var/list/turfstocapture = list() - //Lines below determine what tiles will be rendered - for(var/xoff = 0 to range) - for(var/yoff = 0 to range) - var/turf/T = locate(tx + xoff,ty + yoff,tz) - if(T) - if(cap_mode == CAPTURE_MODE_REGULAR) - if(user.can_capture_turf(T)) - turfstocapture.Add(T) - continue - else - turfstocapture.Add(T) +/proc/create_area_image(target_x, target_y, target_z, range, show_lighting = TRUE, mob/living/checker) + // They're all should be set. + ASSERT(target_x) + ASSERT(target_y) + ASSERT(target_z) + ASSERT(range) + + // - Collecting list of turfs to render - + + var/list/render_turfs = list() + for(var/x_offset = 0 to range) + for(var/y_offset = 0 to range) + var/turf/T = locate(target_x + x_offset, target_y + y_offset, target_z) + if(checker && !checker?.can_capture_turf(T)) + continue else - //Capture includes non-existan turfs - if(!suppress_errors) - return - //Lines below determine what objects will be rendered - var/list/atoms = list() - for(var/turf/T in turfstocapture) - atoms.Add(T) - for(var/atom/A in T) - if(istype(A, /atom/movable/lighting_overlay) && lighting) //Special case for lighting - atoms.Add(A) + render_turfs.Add(T) + + // - Collecting list of atoms to render - + + var/list/render_atoms = list() + for(var/turf/T as anything in render_turfs) + render_atoms.Add(T) + + for(var/atom/A as anything in T) + // In some cases we want to filter lighting overlays. + if(istype(A, /atom/movable/lighting_overlay) && show_lighting) + render_atoms.Add(A) + continue + + if(!A.alpha || A.invisibility) continue - if(A.invisibility) continue - atoms.Add(A) - //Lines below actually render all colected data - atoms = sort_atoms_by_layer(atoms) - var/icon/cap = icon('icons/effects/96x96.dmi', "") - cap.Scale(range*32, range*32) - cap.Blend("#000", ICON_OVERLAY) - for(var/atom/A in atoms) - if(A) - var/icon/img = getFlatIcon(A) - if(istype(img, /icon)) - if(istype(A, /mob/living) && A:lying) - img.BecomeLying() - var/xoff = (A.x - tx) * 32 - var/yoff = (A.y - ty) * 32 - cap.Blend(img, blendMode2iconMode(A.blend_mode), A.pixel_x + xoff, A.pixel_y + yoff) - - return cap + render_atoms.Add(A) + + // - Performing rendering with collected atoms in list - + + render_atoms = sort_atoms_by_layer(render_atoms) + var/icon/capture = icon('icons/effects/96x96.dmi', "") + capture.Scale(range * world.icon_size, range * world.icon_size) + capture.Blend(COLOR_BLACK, ICON_OVERLAY) + for(var/atom/A as anything in render_atoms) + var/icon/atom_icon = getFlatIcon(A) + + if(ismob(A)) + var/mob/M = A + if(M.lying) + atom_icon.BecomeLying() + + var/x_offset = (A.x - target_x) * world.icon_size + var/y_offset = (A.y - target_y) * world.icon_size + capture.Blend(atom_icon, blendMode2iconMode(A.blend_mode), A.pixel_x + x_offset, A.pixel_y + y_offset) + + return capture var/global/list/icon_state_lists = list() /proc/cached_icon_states(var/icon/I) 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/mobs.dm b/code/_helpers/mobs.dm index dfb304f2ee2..8f00e23f49f 100644 --- a/code/_helpers/mobs.dm +++ b/code/_helpers/mobs.dm @@ -71,7 +71,7 @@ /proc/get_exposed_defense_zone(var/atom/movable/target) return pick(BP_HEAD, BP_L_HAND, BP_R_HAND, BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM, BP_L_LEG, BP_R_LEG, BP_CHEST, BP_GROIN) -/proc/do_mob(mob/user , mob/target, time = 30, target_zone = 0, uninterruptible = 0, progress = 1, var/incapacitation_flags = INCAPACITATION_DEFAULT) +/proc/do_mob(mob/user , mob/target, time = 30, target_zone = 0, uninterruptible = 0, progress = 1, incapacitation_flags = INCAPACITATION_DEFAULT, check_holding = TRUE) if(!user || !target) return 0 var/user_loc = user.loc @@ -82,7 +82,7 @@ var/target_loc = target.loc - var/holding = user.get_active_hand() + var/holding = check_holding && user.get_active_hand() var/datum/progressbar/progbar if (progress) progbar = new(user, time, target) @@ -91,7 +91,7 @@ var/starttime = world.time . = 1 while (world.time < endtime) - sleep(1) + stoplag(1) if (progress) progbar.update(world.time - starttime) if(!user || !target) @@ -112,7 +112,7 @@ . = 0 break - if(user.get_active_hand() != holding) + if(check_holding && user.get_active_hand() != holding) . = 0 break @@ -123,7 +123,7 @@ if (progbar) qdel(progbar) -/proc/do_after(mob/user, delay, atom/target = null, needhand = 1, progress = 1, var/incapacitation_flags = INCAPACITATION_DEFAULT, var/same_direction = 0, var/can_move = 0) +/proc/do_after(mob/user, delay, atom/target = null, check_holding = 1, progress = 1, var/incapacitation_flags = INCAPACITATION_DEFAULT, var/same_direction = 0, var/can_move = 0) if(!user) return 0 var/atom/target_loc = null @@ -151,7 +151,7 @@ var/starttime = world.time . = 1 while (world.time < endtime) - sleep(1) + stoplag(1) if (progress) progbar.update(world.time - starttime) @@ -167,7 +167,7 @@ . = 0 break - if(needhand) + if(check_holding) if(user.get_active_hand() != holding) . = 0 break diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index 3b1a5b1cbd2..ea64ab498d9 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -150,30 +150,54 @@ //Used to strip text of everything but letters and numbers, make letters lowercase, and turn spaces into .'s. //Make sure the text hasn't been encoded if using this. -/proc/sanitize_for_email(text) +/proc/sanitize_for_account(text) if(!text) return "" var/list/dat = list() - var/last_was_space = 1 + var/last_was_fullstop = 1 for(var/i=1, i<=length(text), i++) var/ascii_char = text2ascii(text,i) switch(ascii_char) if(65 to 90) //A-Z, make them lowercase dat += ascii2text(ascii_char + 32) + last_was_fullstop = 0 if(97 to 122) //a-z dat += ascii2text(ascii_char) - last_was_space = 0 + last_was_fullstop = 0 if(48 to 57) //0-9 dat += ascii2text(ascii_char) - last_was_space = 0 + last_was_fullstop = 0 if(32) //space - if(last_was_space) + if(last_was_fullstop) continue dat += "." //We turn these into ., but avoid repeats or . at start. - last_was_space = 1 + last_was_fullstop = 1 + if(46) //. + if(last_was_fullstop) + continue + dat += "." + last_was_fullstop = 1 if(dat[length(dat)] == ".") //kill trailing . dat.Cut(length(dat)) return jointext(dat, null) +// Strips text of everything alphanumeric characters, '_' and '-'. Converts spaces to '_' +/proc/sanitize_for_group(text) + if(!text) return "" + var/list/dat = list() + for(var/i=1, i<=length(text), i++) + var/ascii_char = text2ascii(text,i) + switch(ascii_char) + if(65 to 90) //A-Z + dat += ascii2text(ascii_char) + if(97 to 122) //a-z + dat += ascii2text(ascii_char) + if(48 to 57) //0-9 + dat += ascii2text(ascii_char) + if(45, 95) //-, _ + dat += ascii2text(ascii_char) + if(32) //space + dat += "_" + return jointext(dat, null) // UNICODE: Convert to regex? //Returns null if there is any bad text in the string diff --git a/code/_helpers/time.dm b/code/_helpers/time.dm index 70e62bc039b..2d69d3f43b6 100644 --- a/code/_helpers/time.dm +++ b/code/_helpers/time.dm @@ -134,10 +134,11 @@ var/global/rollovercheck_last_timeofday = 0 global.rollovercheck_last_timeofday = world.timeofday return global.midnight_rollovers -//Increases delay as the server gets more overloaded, -//as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful -#define DELTA_CALC max(((max(world.tick_usage, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) +/// Increases delay as the server gets more overloaded +/// as sleeps aren't cheap and sleeping only to wake up and sleep again is wasteful +#define DELTA_CALC max(((max(TICK_USAGE, world.cpu) / 100) * max(Master.sleep_delta-1,1)), 1) +/// returns the number of ticks slept /proc/stoplag(initial_delay) // If we're initializing, our tick limit might be over 100 (testing config), but stoplag() penalizes procs that go over. // Unfortunately, this penalty slows down init a *lot*. So, we disable it during boot and lobby, when relatively few things should be calling this. @@ -152,9 +153,9 @@ var/global/rollovercheck_last_timeofday = 0 var/i = DS2TICKS(initial_delay) do . += NONUNIT_CEILING(i*DELTA_CALC, 1) - sleep(i*world.tick_lag*DELTA_CALC) + sleep(i * world.tick_lag * DELTA_CALC) i *= 2 - while (world.tick_usage > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) + while (TICK_USAGE > min(TICK_LIMIT_TO_RUN, Master.current_ticklimit)) #undef DELTA_CALC 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/drag_drop.dm b/code/_onclick/drag_drop.dm index 956f834c1f3..39be692eb6c 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -26,14 +26,11 @@ /atom/proc/check_mousedrop_adjacency(var/atom/over, var/mob/user) . = (Adjacent(user) && over.Adjacent(user)) -/mob/can_mouse_drop(var/atom/over, var/mob/user = usr, var/incapacitation_flags = INCAPACITATION_DEFAULT) - . = !anchored && ..() - // Receive a mouse drop. // Returns false if the atom is valid for dropping further up the chain, true if the drop has been handled. /atom/proc/receive_mouse_drop(var/atom/dropping, var/mob/user) var/mob/living/H = user - if(istype(H) && can_climb(H) && dropping == user) + if(istype(H) && !H.anchored && can_climb(H) && dropping == user) do_climb(dropping) return TRUE return FALSE diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm index 2d878560e93..f28a472232d 100644 --- a/code/_onclick/hud/_defines.dm +++ b/code/_onclick/hud/_defines.dm @@ -56,7 +56,7 @@ #define ui_movi "RIGHT-3:24,BOTTOM:5" #define ui_attack_selector "RIGHT-3:24,BOTTOM+1:-2" #define ui_zonesel "RIGHT-1:28,BOTTOM:5" -#define ui_acti_alt "RIGHT-1:28,BOTTOM:5" //alternative intent switcher for when the interface is hidden (F12) +#define ui_acti_alt "RIGHT-1:28,BOTTOM:5" //alternative intent switcher for when the interface is hidden #define ui_stamina "RIGHT-3:24,BOTTOM+1:5" #define ui_borg_pull "RIGHT-3:24,BOTTOM+1:7" @@ -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 e9f6876942d..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( @@ -236,10 +236,9 @@ if(!skip_client_update) mymob.client?.screen |= inv_box -//Triggered when F12 is pressed (Unless someone changed something in the DMF) -/mob/verb/button_pressed_F12(var/full = 0 as null) - set name = "F12" - set hidden = 1 +/mob/verb/minimize_hud(full = FALSE as null) + set name = "Minimize Hud" + set hidden = TRUE if(!hud_used) to_chat(usr, "This mob type does not use a HUD.") @@ -297,7 +296,7 @@ hud_used.persistant_inventory_update() update_action_buttons() -//Similar to button_pressed_F12() but keeps zone_sel, gun_setting_icon, and healths. +//Similar to minimize_hud() but keeps zone_sel, gun_setting_icon, and healths. /mob/proc/toggle_zoom_hud() if(!hud_used) return @@ -334,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/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm index e61cf91fddd..d4b4cebe6c7 100644 --- a/code/_onclick/hud/radial.dm +++ b/code/_onclick/hud/radial.dm @@ -342,3 +342,22 @@ var/global/list/radial_menus = list() return answer #define RADIAL_INPUT(user, choices) show_radial_menu(user, user, choices) + +/* + Helper to make a radial menu button with a name and icon for a given atom. +*/ +/proc/make_item_radial_menu_button(var/atom/movable/AM, var/name_prefix = "", var/name_suffix = "") + var/image/radial_button = new + radial_button.appearance = AM + radial_button.plane = FLOAT_PLANE + radial_button.layer = FLOAT_LAYER + radial_button.name = "[name_prefix][AM.name][name_suffix]" + return radial_button + +/* + Helper to make a radial menu button for a set of atoms with their names and icons. +*/ +/proc/make_item_radial_menu_choices(var/list/items, var/name_prefix = "", var/name_suffix = "") + for(var/atom/movable/AM in items) + LAZYSET(., AM, make_item_radial_menu_button(AM, name_prefix, name_suffix)) + diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 16d4de2b5ce..bf00ec77cd3 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -50,7 +50,7 @@ name = "default attack selector" icon_state = "attack_selector" screen_loc = ui_attack_selector - maptext_y = 5 + maptext_y = 12 var/mob/living/carbon/human/owner /obj/screen/default_attack_selector/Click(location, control, params) diff --git a/code/controllers/master.dm b/code/controllers/master.dm index 912deb2e7d5..49a05a04af1 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -587,9 +587,44 @@ var/global/datum/controller/master/Master = new if(!statclick) statclick = new/obj/effect/statclick/debug(null, "Initializing...", src) - stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))") + stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)") stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])")) +/// Colors cpu number before output. +/datum/controller/master/proc/format_color_cpu() + switch(world.cpu) + // 0-80 = green + if(0 to 80) + . = "[world.cpu]" + // 80-90 = orange + if(80 to 90) + . = "[world.cpu]" + // 90-100 = red + if(90 to 100) + . = "[world.cpu]" + // >100 = bold red + if(100 to INFINITY) + . = "[world.cpu]" + +/// Colors map cpu number before output. +/// Same as before, but specially for map cpu. +/// It uses same colors, but need different number range. +/datum/controller/master/proc/format_color_cpu_map() + var/current_map_cpu = MAPTICK_LAST_INTERNAL_TICK_USAGE + switch(current_map_cpu) + // 0-30 = green + if(0 to 30) + . = "[current_map_cpu]" + // 30-60 = orange + if(30 to 60) + . = "[current_map_cpu]" + // 60-80 = red + if(60 to 80) + . = "[current_map_cpu]" + // >100 = bold red + if(80 to INFINITY) + . = "[current_map_cpu]" + /datum/controller/master/StartLoadingMap() //disallow more than one map to load at once, multithreading it will just cause race conditions while(map_loading) diff --git a/code/controllers/subsystems/initialization/codex.dm b/code/controllers/subsystems/initialization/codex.dm index e079ab89b4e..55b713a5969 100644 --- a/code/controllers/subsystems/initialization/codex.dm +++ b/code/controllers/subsystems/initialization/codex.dm @@ -4,15 +4,19 @@ SUBSYSTEM_DEF(codex) init_order = SS_INIT_MISC_CODEX var/regex/linkRegex + var/list/all_entries = list() var/list/entries_by_path = list() var/list/entries_by_string = list() var/list/index_file = list() var/list/search_cache = list() var/list/categories = list() + // HEARTH ADDITION + var/list/name_cache = list() + // END HEARTH ADDITION /datum/controller/subsystem/codex/Initialize() - // Codex link syntax is such: - // keyword when keyword is mentioned verbatim, + // Codex link syntax is such: + // keyword when keyword is mentioned verbatim, // whatever when shit gets tricky linkRegex = regex(@"<(span|l)(\s+codexlink='([^>]*)'|)>([^<]+)","g") @@ -21,39 +25,36 @@ SUBSYSTEM_DEF(codex) var/datum/codex_entry/centry = ctype if(initial(centry.name) || initial(centry.associated_paths) || initial(centry.associated_strings)) centry = new centry() - for(var/associated_path in centry.associated_paths) - entries_by_path[associated_path] = centry - for(var/associated_string in centry.associated_strings) - add_entry_by_string(associated_string, centry) - if(centry.name) - add_entry_by_string(centry.name, centry) // Create categorized entries. var/list/categories = decls_repository.get_decls_of_subtype(/decl/codex_category) for(var/ctype in categories) var/decl/codex_category/cat = categories[ctype] if(cat?.name) - cat.Initialize() + cat.Populate() // Create the index file for later use. - for(var/thing in SScodex.entries_by_path) - var/datum/codex_entry/entry = SScodex.entries_by_path[thing] - index_file[entry.name] = entry - for(var/thing in SScodex.entries_by_string) - var/datum/codex_entry/entry = SScodex.entries_by_string[thing] + for(var/thing in SScodex.all_entries) + var/datum/codex_entry/entry = thing index_file[entry.name] = entry index_file = sortTim(index_file, /proc/cmp_text_asc) . = ..() /datum/controller/subsystem/codex/proc/parse_links(string, viewer) while(linkRegex.Find(string)) - var/key = linkRegex.group[4] + // HEARTH MODIFICATION START + var/list/linked_entries + var/datum/codex_entry/linked_entry if(linkRegex.group[2]) - key = linkRegex.group[3] - key = lowertext(trim(key)) - var/datum/codex_entry/linked_entry = get_entry_by_string(key) + linked_entry = get_entry_by_string(linkRegex.group[3]) + else + linked_entries = retrieve_entries_for_name(linkRegex.group[4]) + linked_entry = LAZYACCESS(linked_entries, 1) var/replacement = linkRegex.group[4] - if(linked_entry) + if (LAZYLEN(linked_entries) > 1) + replacement = "[replacement]" + else if(linked_entry) + // HEARTH MODIFICATION END replacement = "[replacement]" string = replacetextEx(string, linkRegex.match, replacement) return string @@ -69,9 +70,6 @@ SUBSYSTEM_DEF(codex) else if(entries_by_string[lowertext(entry)]) return entries_by_string[lowertext(entry)] -/datum/controller/subsystem/codex/proc/add_entry_by_string(var/string, var/entry) - entries_by_string[lowertext(trim(string))] = entry - /datum/controller/subsystem/codex/proc/get_entry_by_string(var/string) return entries_by_string[lowertext(trim(string))] @@ -109,22 +107,87 @@ SUBSYSTEM_DEF(codex) search_cache[searching] = dd_sortedObjectList(results) return search_cache[searching] +// HEARTH ADDITION START +/// Like retrieve_entries_for_string, but it only searches for matches in names and other associated strings. +/datum/controller/subsystem/codex/proc/retrieve_entries_for_name(var/name, var/ignore_categories = TRUE, var/exact_word = TRUE) + if(!initialized) + return list() + + name = sanitize(lowertext(trim(name))) + if(!name) + return list() + if(!name_cache[name]) + var/list/results + if(entries_by_string[name]) + results = list(entries_by_string[name]) + else + results = list() + for(var/entry_title in entries_by_string) + var/datum/codex_entry/entry = entries_by_string[entry_title] + var/check_string = entry_title + if(ignore_categories) + check_string = replacetext(check_string, regex(@" \([^)]+\)$"), "") + if (check_string == name) + results = list(entry) + break + if (exact_word) + if(findtext(check_string, regex("\\b[name]\\b"))) // take into account word boundaries + results |= entry + else + if(findtext(check_string, name)) + results |= entry + name_cache[name] = dd_sortedObjectList(results) + return name_cache[name] +// HEARTH ADDITION END + +// HEARTH MODIFICATION START /datum/controller/subsystem/codex/Topic(href, href_list) . = ..() - if(!. && href_list["show_examined_info"] && href_list["show_to"]) + if(.) + return . + if (href_list["show_to"]) var/mob/showing_mob = locate(href_list["show_to"]) if(!istype(showing_mob) || !showing_mob.can_use_codex()) - return - - var/atom/showing_atom = locate(href_list["show_examined_info"]) - var/entry - if(istype(showing_atom, /datum/codex_entry)) - entry = showing_atom - else if(istype(showing_atom)) - entry = get_codex_entry(showing_atom.get_codex_value()) - else - entry = get_codex_entry(href_list["show_examined_info"]) - - if(entry) - present_codex_entry(showing_mob, entry) - return TRUE + return + if (href_list["show_examined_info"]) + var/atom/showing_atom = locate(href_list["show_examined_info"]) + var/entry + if(istype(showing_atom, /datum/codex_entry)) + entry = showing_atom + else if(istype(showing_atom)) + entry = get_codex_entry(showing_atom.get_codex_value()) + else + entry = get_codex_entry(href_list["show_examined_info"]) + + if(entry) + present_codex_entry(showing_mob, entry) + return TRUE + else if (href_list["show_disambiguation"]) + var/search_string = href_list["show_disambiguation"] + var/list/all_entries = SScodex.retrieve_entries_for_name(search_string) + if(showing_mob && showing_mob.mind && !player_is_antag(showing_mob.mind)) + all_entries = all_entries.Copy() // So we aren't messing with the codex search cache. + for(var/datum/codex_entry/entry in all_entries) + if(entry.antag_text && !entry.mechanics_text && !entry.lore_text) + all_entries -= entry + switch(LAZYLEN(all_entries)) + if(0) + to_chat(src, SPAN_NOTICE("The codex reports no matches for '[search_string]'. Please report this on Github, along with what link you clicked!")) + if(1) + SScodex.present_codex_entry(showing_mob, all_entries[1]) + else + var/list/codex_data = list() + var/datum/codex_entry/linked_entry = SScodex.get_entry_by_string("nexus") + codex_data += "Home" + codex_data += "Search Codex" + codex_data += "List All Entries" + codex_data += "

[search_string] (Disambiguation)

" + codex_data += "" + for(var/i = 1 to all_entries.len) + var/datum/codex_entry/entry = all_entries[i] + codex_data += "" + codex_data += "
[entry.name]View
" + var/datum/browser/popup = new(showing_mob, "codex-disambig", "Codex Disambiguation") + popup.set_content(jointext(codex_data, null)) + popup.open() +// HEARTH MODIFICATION END \ No newline at end of file diff --git a/code/controllers/subsystems/initialization/materials.dm b/code/controllers/subsystems/initialization/materials.dm index 2a547041e71..270a5d5bdd6 100644 --- a/code/controllers/subsystems/initialization/materials.dm +++ b/code/controllers/subsystems/initialization/materials.dm @@ -127,7 +127,7 @@ SUBSYSTEM_DEF(materials) var/list/all_random_reagents = decls_repository.get_decls_of_type(/decl/material/liquid/random) for(var/rtype in all_random_reagents) var/decl/material/liquid/random/random = all_random_reagents[rtype] - if(only_if_unique && random.initialized) + if(only_if_unique && random.data_initialized) continue if(random.randomize_data(temperature)) return random.type diff --git a/code/controllers/subsystems/input.dm b/code/controllers/subsystems/input.dm index 91f9a10a46c..4a7d56790d7 100644 --- a/code/controllers/subsystems/input.dm +++ b/code/controllers/subsystems/input.dm @@ -19,8 +19,7 @@ SUBSYSTEM_DEF(input) "Any" = "\"KeyDown \[\[*\]\]\"", "Any+UP" = "\"KeyUp \[\[*\]\]\"", "Back" = "\".winset \\\"outputwindow.input.text=\\\"\\\"\\\"\"", - "Tab" = "\".winset \\\"outputwindow.input.focus=true?mapwindow.map.focus=true outputwindow.input.background-color=[COLOR_INPUT_DISABLED]:outputwindow.input.focus=true outputwindow.input.background-color=[COLOR_INPUT_ENABLED]\\\"\"", - "Escape" = "Reset-Held-Keys", + "Escape" = "Reset-Held-Keys" ) // Badmins just wanna have fun ♪ diff --git a/code/controllers/subsystems/jobs.dm b/code/controllers/subsystems/jobs.dm index 8d022e1dcee..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) @@ -509,12 +506,9 @@ SUBSYSTEM_DEF(jobs) H.buckled.set_dir(H.dir) if(!(ASSIGNMENT_ROBOT in job.event_categories) && !(ASSIGNMENT_COMPUTER in job.event_categories)) //These guys get their emails later. - var/domain = "freemail.net" - if(H.char_branch?.email_domain) - domain = H.char_branch.email_domain var/datum/computer_network/network = get_local_network_at(get_turf(H)) if(network) - network.create_email(H, H.real_name, domain, rank) + network.create_account(H, H.real_name, null, H.real_name, null, TRUE) // If they're head, give them the account info for their department if(H.mind && job.head_position) @@ -549,7 +543,8 @@ SUBSYSTEM_DEF(jobs) if(job.supervisors) to_chat(H, "As the [alt_title ? alt_title : rank] you answer directly to [job.supervisors]. Special circumstances may change this.") - to_chat(H, "To speak on your department's radio channel use :h. For the use of other channels, examine your headset.") + if(H.has_headset_in_ears()) + to_chat(H, "To speak on your department's radio channel use :h. For the use of other channels, examine your headset.") if(job.req_admin_notify) to_chat(H, "You are playing a job that is important for Game Progression. If you have to disconnect, please notify the admins via adminhelp.") diff --git a/code/controllers/subsystems/machines.dm b/code/controllers/subsystems/machines.dm index 1d531410119..48d7b425037 100644 --- a/code/controllers/subsystems/machines.dm +++ b/code/controllers/subsystems/machines.dm @@ -62,9 +62,9 @@ SUBSYSTEM_DEF(machines) #define INTERNAL_PROCESS_STEP(this_step, check_resumed, proc_to_call, cost_var, next_step)\ if(current_step == this_step || (check_resumed && !resumed)) {\ - timer = TICK_USAGE_REAL;\ + timer = TICK_USAGE;\ proc_to_call(resumed);\ - cost_var = MC_AVERAGE(cost_var, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer));\ + cost_var = MC_AVERAGE(cost_var, TICK_DELTA_TO_MS(TICK_USAGE - timer));\ if(state != SS_RUNNING){\ return;\ }\ @@ -73,7 +73,7 @@ if(current_step == this_step || (check_resumed && !resumed)) {\ } /datum/controller/subsystem/machines/fire(resumed = 0) - var/timer = TICK_USAGE_REAL + var/timer = TICK_USAGE INTERNAL_PROCESS_STEP(SSMACHINES_PIPENETS,TRUE,process_pipenets,cost_pipenets,SSMACHINES_MACHINERY) INTERNAL_PROCESS_STEP(SSMACHINES_MACHINERY,FALSE,process_machinery,cost_machinery,SSMACHINES_POWERNETS) diff --git a/code/controllers/subsystems/timer.dm b/code/controllers/subsystems/timer.dm index d62ad0af676..efa049d26c2 100644 --- a/code/controllers/subsystems/timer.dm +++ b/code/controllers/subsystems/timer.dm @@ -3,7 +3,7 @@ /// Helper for getting the correct bucket for a given timer #define BUCKET_POS(timer) (((round((timer.timeToRun - timer.timer_subsystem.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN) /// Gets the maximum time at which timers will be invoked from buckets, used for deferring to secondary queue -#define TIMER_MAX(timer_ss) (world.time + TICKS2DS(min(BUCKET_LEN-(timer_ss.practical_offset-DS2TICKS(world.time - timer_ss.head_offset))-1, BUCKET_LEN-1))) +#define TIMER_MAX(timer_ss) (timer_ss.head_offset + TICKS2DS(BUCKET_LEN + timer_ss.practical_offset - 1)) /// Max float with integer precision #define TIMER_ID_MAX (2**24) @@ -460,7 +460,7 @@ SUBSYSTEM_DEF(timer) if(buckethead == src) bucket_list[bucket_pos] = next timer_subsystem.bucket_count-- - else if(timeToRun < TIMER_MAX(timer_subsystem)) + else if(bucket_joined) timer_subsystem.bucket_count-- else var/l = length(second_queue) diff --git a/code/datums/mil_ranks.dm b/code/datums/mil_ranks.dm index e8625132ef9..23a0b69cb66 100644 --- a/code/datums/mil_ranks.dm +++ b/code/datums/mil_ranks.dm @@ -111,9 +111,6 @@ var/global/datum/mil_branches/mil_branches = new() var/assistant_job - // Email addresses will be created under this domain name. Mostly for the looks. - var/email_domain = "freemail.net" - var/list/min_skill /datum/mil_branch/New() diff --git a/code/datums/mind/mind.dm b/code/datums/mind/mind.dm index 11af09f173c..4f386030978 100644 --- a/code/datums/mind/mind.dm +++ b/code/datums/mind/mind.dm @@ -60,7 +60,8 @@ //put this here for easier tracking ingame var/datum/money_account/initial_account - var/list/initial_email_login = list("login" = "", "password" = "") + var/list/initial_account_login = list("login" = "", "password" = "") + var/account_network // Network id of the network the account was created on. /datum/mind/New(var/key) src.key = key diff --git a/code/datums/movement/mob.dm b/code/datums/movement/mob.dm index d31a94d6e22..7675f341da4 100644 --- a/code/datums/movement/mob.dm +++ b/code/datums/movement/mob.dm @@ -188,47 +188,6 @@ mob.moving = FALSE return - var/turf/new_loc = mob.loc - if(istype(new_loc)) - for(var/atom/movable/AM AS_ANYTHING in mob.ret_grab()) - if(AM != src && AM.loc != mob.loc && !AM.anchored && old_turf.Adjacent(AM)) - AM.glide_size = mob.glide_size // This is adjusted by grabs again from events/some of the procs below, but doing it here makes it more likely to work with recursive movement. - AM.DoMove(get_dir(get_turf(AM), old_turf), mob, TRUE) - - for(var/obj/item/grab/G AS_ANYTHING in mob.get_active_grabs()) - if(G.assailant_reverse_facing()) - mob.set_dir(global.reverse_dir[direction]) - G.assailant_moved() - G.adjust_position() - - if(length(mob.grabbed_by)) - mob.reset_offsets() - mob.reset_plane_and_layer() - - if(direction & (UP|DOWN)) - var/txt_dir = (direction & UP) ? "upwards" : "downwards" - old_turf.visible_message(SPAN_NOTICE("[mob] moves [txt_dir].")) - for(var/obj/item/grab/G AS_ANYTHING in mob.get_active_grabs()) - if(!G.affecting) - continue - var/turf/start = G.affecting.loc - var/turf/destination = (direction == UP) ? GetAbove(G.affecting) : GetBelow(G.affecting) - if(!start.CanZPass(G.affecting, direction)) - to_chat(mob, SPAN_WARNING("\The [start] blocked your pulled object!")) - qdel(G) - continue - if(!destination.CanZPass(G.affecting, direction)) - to_chat(mob, SPAN_WARNING("The [G.affecting] you were pulling bumps up against \the [destination].")) - qdel(G) - continue - for(var/atom/A in destination) - if(!A.CanMoveOnto(G.affecting, start, 1.5, direction)) - to_chat(mob, SPAN_WARNING("\The [A] blocks the [G.affecting] you were pulling.")) - qdel(G) - continue - G.affecting.forceMove(destination) - continue - // Sprinting uses up stamina and causes exertion effects. if(MOVING_QUICKLY(mob)) mob.last_quick_move_time = world.time diff --git a/code/datums/recipe.dm b/code/datums/recipe.dm index bcfc17a3877..3c227000311 100644 --- a/code/datums/recipe.dm +++ b/code/datums/recipe.dm @@ -6,11 +6,11 @@ * * * * * * * * * * * * * * * * * * * * * * * * * */ /decl/recipe - var/display_name // Descriptive name of the recipe, should be unique to avoid codex pages being unsearchable. + var/display_name // Descriptive name of the recipe, should be unique to avoid codex pages being unsearchable. If not set, codex uses initial name of product. var/list/reagents // example: = list(/decl/material/liquid/drink/juice/berry = 5) // do not list same reagent twice var/list/items // example: = list(/obj/item/crowbar, /obj/item/welder, /obj/item/screwdriver = 2) // place /foo/bar before /foo var/list/fruit // example: = list("fruit" = 3) - var/result // example: = /obj/item/chems/food/donut/normal + var/result // example: = /obj/item/chems/food/donut var/time = 100 // Cooking time in deciseconds. var/result_quantity = 1 // How many items to create. Where possible, just use fewer ingredients instead. var/coating = null // Required coating on all items in the recipe. The default value of null explitly requires no coating diff --git a/code/datums/repositories/decls.dm b/code/datums/repositories/decls.dm index 7b93c17b94f..90537bd8ddf 100644 --- a/code/datums/repositories/decls.dm +++ b/code/datums/repositories/decls.dm @@ -82,10 +82,14 @@ var/global/repository/decls/decls_repository = new var/uid var/abstract_type = /decl var/crash_on_abstract_init = FALSE + var/initialized = FALSE /decl/proc/Initialize() SHOULD_CALL_PARENT(TRUE) SHOULD_NOT_SLEEP(TRUE) + if(initialized) + CRASH("[type] initialized more than once!") + initialized = TRUE return INITIALIZE_HINT_NORMAL /decl/Destroy() diff --git a/code/datums/supplypacks/engineering.dm b/code/datums/supplypacks/engineering.dm index 4a192ad0649..d5abd966516 100644 --- a/code/datums/supplypacks/engineering.dm +++ b/code/datums/supplypacks/engineering.dm @@ -61,7 +61,7 @@ /decl/hierarchy/supply_pack/engineering/emitter name = "Equipment - Emitter" - contains = list(/obj/machinery/power/emitter = 2) + contains = list(/obj/machinery/emitter = 2) containertype = /obj/structure/closet/crate/secure/large containername = "emitter crate" access = access_engine_equip @@ -82,7 +82,7 @@ /decl/hierarchy/supply_pack/engineering/collector name = "Power - Collector" - contains = list(/obj/machinery/power/rad_collector = 2) + contains = list(/obj/machinery/rad_collector = 2) containertype = /obj/structure/closet/crate/secure/large containername = "collector crate" access = access_engine_equip @@ -122,7 +122,7 @@ /decl/hierarchy/supply_pack/engineering/teg name = "Power - Mark I Thermoelectric Generator" - contains = list(/obj/machinery/power/generator) + contains = list(/obj/machinery/generator) containertype = /obj/structure/closet/crate/secure/large containername = "\improper Mk1 TEG crate" access = access_engine_equip diff --git a/code/datums/uplink/devices and tools.dm b/code/datums/uplink/devices and tools.dm index 788163c0a5c..c7e67d8857d 100644 --- a/code/datums/uplink/devices and tools.dm +++ b/code/datums/uplink/devices and tools.dm @@ -136,7 +136,7 @@ /datum/uplink_item/item/tools/supply_beacon name = "Hacked Supply Beacon (DANGER!)" - desc = "Wrench this large beacon onto an exposed power cable, in order to activate it. This will call in a \ + desc = "Wrench this large beacon onto an exposed power cable and add some cabling, in order to activate it. This will call in a \ drop pod to the target location, containing a random assortment of (possibly useful) items. \ The ship's computer system will announce when this pod is enroute." item_cost = 52 diff --git a/code/datums/wires/shield_generator.dm b/code/datums/wires/shield_generator.dm index 9989b3a32f5..11335db17c2 100644 --- a/code/datums/wires/shield_generator.dm +++ b/code/datums/wires/shield_generator.dm @@ -1,5 +1,5 @@ /datum/wires/shield_generator - holder_type = /obj/machinery/power/shield_generator/ + holder_type = /obj/machinery/shield_generator/ wire_count = 5 descriptions = list( new /datum/wire_description(SHIELDGEN_WIRE_POWER, "This wire seems to be carrying a heavy current.", SKILL_EXPERT), @@ -15,13 +15,13 @@ var/global/const/SHIELDGEN_WIRE_AICONTROL = 8 // Cut to disable AI control. Men var/global/const/SHIELDGEN_WIRE_NOTHING = 16 // A blank wire that doesn't have any specific function /datum/wires/shield_generator/CanUse() - var/obj/machinery/power/shield_generator/S = holder + var/obj/machinery/shield_generator/S = holder if(S.panel_open) return 1 return 0 /datum/wires/shield_generator/UpdateCut(index, mended) - var/obj/machinery/power/shield_generator/S = holder + var/obj/machinery/shield_generator/S = holder switch(index) if(SHIELDGEN_WIRE_POWER) S.input_cut = !mended @@ -38,7 +38,7 @@ var/global/const/SHIELDGEN_WIRE_NOTHING = 16 // A blank wire that doesn't have S.ai_control_disabled = !mended /datum/wires/shield_generator/UpdatePulsed(var/index) - var/obj/machinery/power/shield_generator/S = holder + var/obj/machinery/shield_generator/S = holder switch(index) if(SHIELDGEN_WIRE_HACK) S.hacked = 1 \ No newline at end of file diff --git a/code/game/atoms_init.dm b/code/game/atoms_init.dm index 48db58219d9..43b9de7e503 100644 --- a/code/game/atoms_init.dm +++ b/code/game/atoms_init.dm @@ -66,6 +66,8 @@ return /atom/Destroy() + UNQUEUE_TEMPERATURE_ATOM(src) + QDEL_NULL(reagents) LAZYCLEARLIST(our_overlays) @@ -87,24 +89,19 @@ if (!follow_repository.excluded_subtypes[type] && follow_repository.followed_subtypes_tcache[type]) follow_repository.add_subject(src) - if (virtual_mob && ispath(initial(virtual_mob))) + if(ispath(virtual_mob)) virtual_mob = new virtual_mob(get_turf(src), src) // Fire Entered events for freshly created movables. + // Changing this behavior will almost certainly break power; update accordingly. if (!ml && loc) loc.Entered(src, null) /atom/movable/Destroy() - // These must be done before event cleanup in the parent call - if(LAZYLEN(movement_handlers) && !ispath(movement_handlers[1])) - QDEL_NULL_LIST(movement_handlers) - if (bound_overlay) - QDEL_NULL(bound_overlay) + unregister_all_movement(loc, src) // unregister events before destroy to avoid expensive checking - if(virtual_mob && !ispath(virtual_mob)) - qdel(virtual_mob) - virtual_mob = null + . = ..() #ifdef DISABLE_DEBUG_CRASH // meh do nothing. we know what we're doing. pro engineers. @@ -121,4 +118,11 @@ vis_locs = null //clears this atom out of all vis_contents vis_contents.Cut() - . = ..() // called last so that events are unregistered before the parent call + if (bound_overlay) + QDEL_NULL(bound_overlay) + + if(ismob(virtual_mob)) + QDEL_NULL(virtual_mob) + + vis_locs = null //clears this atom out of all vis_contents + clear_vis_contents(src) diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 2b2ca6005c5..38c85cd0bd1 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -384,7 +384,7 @@ return buckled_mob /atom/movable/proc/can_buckle_mob(var/mob/living/dropping) - . = (can_buckle && istype(dropping) && !dropping.buckled && !dropping.buckled_mob && !buckled_mob) + . = (can_buckle && istype(dropping) && !dropping.buckled && !dropping.anchored && !dropping.buckled_mob && !buckled_mob) /atom/movable/receive_mouse_drop(atom/dropping, mob/living/user) . = ..() diff --git a/code/game/atoms_temperature.dm b/code/game/atoms_temperature.dm index a9306ee8e42..a5e292d3cf8 100644 --- a/code/game/atoms_temperature.dm +++ b/code/game/atoms_temperature.dm @@ -24,7 +24,7 @@ create_matter() /obj/proc/HandleObjectHeating(var/obj/item/heated_by, var/mob/user, var/adjust_temp) - if(ATOM_IS_TEMPERATURE_SENSITIVE(src)) + if(ATOM_SHOULD_TEMPERATURE_ENQUEUE(src)) visible_message(SPAN_NOTICE("\The [user] carefully heats \the [src] with \the [heated_by].")) var/diff_temp = (adjust_temp - temperature) if(diff_temp >= 0) 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/gamemodes/godmode/form_items/wizard_structures.dm b/code/game/gamemodes/godmode/form_items/wizard_structures.dm index 61a7a7d9d62..d64bcf04feb 100644 --- a/code/game/gamemodes/godmode/form_items/wizard_structures.dm +++ b/code/game/gamemodes/godmode/form_items/wizard_structures.dm @@ -14,7 +14,7 @@ return hitter.visible_message("\The [hitter] dips their hands into \the [src], a soft glow emanating from them.") - if(do_after(hitter,300,src,needhand=0)) + if(do_after(hitter,300,src,check_holding=0)) for(var/s in hitter.mind.learned_spells) var/spell/spell = s spell.charge_counter = spell.charge_max diff --git a/code/game/gamemodes/objectives/objective_heist.dm b/code/game/gamemodes/objectives/objective_heist.dm index e1894777085..b136af48fb4 100644 --- a/code/game/gamemodes/objectives/objective_heist.dm +++ b/code/game/gamemodes/objectives/objective_heist.dm @@ -40,7 +40,7 @@ target_amount = 1 loot = "a gravitational generator" if(3) - target = /obj/machinery/power/emitter + target = /obj/machinery/emitter target_amount = 4 loot = "four emitters" if(4) diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index 2c15361663c..aa8f06c356b 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -47,14 +47,6 @@ return FALSE return TRUE -//Checks if the access (constant or list) is contained in one of the entries of access_patterns, a list of lists. -/proc/has_access_pattern(list/access_patterns, access) - if(!islist(access)) - access = list(access) - for(var/access_pattern in access_patterns) - if(has_access(access_pattern, access)) - return 1 - // Used for retrieving required access information, if available /atom/movable/proc/get_req_access() return null diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 27736d8c7e1..5ae6f630695 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -127,11 +127,13 @@ check_victim() if(src.victim && get_turf(victim) == get_turf(src) && victim.lying) to_chat(usr, "\The [src] is already occupied!") - return 0 + return FALSE if(patient.buckled) to_chat(usr, "Unbuckle \the [patient] first!") - return 0 - return 1 + return FALSE + if(patient.anchored) + return FALSE + return TRUE /obj/machinery/optable/power_change() . = ..() diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 5fca1d98f41..b7c3f581f02 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -128,16 +128,25 @@ occupant.SetStasis(stasis) /obj/machinery/sleeper/on_update_icon() - overlays.Cut() + cut_overlays() icon_state = "med_pod" + if(occupant) - var/image/pickle = new - pickle.appearance = occupant + var/mutable_appearance/pickle = new /mutable_appearance(occupant) + var/list/icon_scale_values = occupant.get_icon_scale_mult() + var/desired_scale_x = icon_scale_values[1] + var/desired_scale_y = icon_scale_values[2] + + var/matrix/M = matrix() + M.Scale(desired_scale_x, desired_scale_y) + M.Translate(0, (1.5 * world.icon_size) * (desired_scale_y - 1)) + pickle.transform = M + pickle.layer = FLOAT_LAYER pickle.pixel_z = 12 - overlays += pickle - var/image/I = image(icon, "med_lid[!!(occupant && !(stat & (BROKEN|NOPOWER)))]") - overlays += I + add_overlay(pickle) + + add_overlay(image(icon, "med_lid[!!(occupant && !(stat & (BROKEN|NOPOWER)))]")) /obj/machinery/sleeper/DefaultTopicState() return global.outside_topic_state @@ -297,7 +306,7 @@ pump = !pump /obj/machinery/sleeper/proc/go_in(var/mob/M, var/mob/user) - if(!M) + if(!M || M.anchored) return if(stat & (BROKEN|NOPOWER)) return diff --git a/code/game/machinery/_machines_base/machine_construction/noninteractive.dm b/code/game/machinery/_machines_base/machine_construction/noninteractive.dm index 0800bc4adc3..a59c77cf501 100644 --- a/code/game/machinery/_machines_base/machine_construction/noninteractive.dm +++ b/code/game/machinery/_machines_base/machine_construction/noninteractive.dm @@ -4,4 +4,7 @@ // Use for actual machines is discouraged. /decl/machine_construction/noninteractive - visible_components = FALSE \ No newline at end of file + visible_components = FALSE + +/decl/machine_construction/noninteractive/terminal/mechanics_info() + . += "Use a wirecutter to disconnect the terminal from the machine." \ No newline at end of file diff --git a/code/game/machinery/_machines_base/machinery.dm b/code/game/machinery/_machines_base/machinery.dm index 1390bb305cb..ee5ca435d93 100644 --- a/code/game/machinery/_machines_base/machinery.dm +++ b/code/game/machinery/_machines_base/machinery.dm @@ -246,7 +246,11 @@ Class Procs: if(info) to_chat(usr, jointext(info, "
")) return TOPIC_HANDLED - + if(href_list["power_text"]) // As above. Reports OOC info on how to use installed power sources. + var/list/info = get_power_sources_info() + if(info) + to_chat(usr, jointext(info, "
")) + return TOPIC_HANDLED . = ..() if(. == TOPIC_REFRESH) updateUsrDialog() // Update legacy UIs to the extent possible. @@ -254,6 +258,14 @@ Class Procs: /obj/machinery/proc/get_tool_manipulation_info() return construct_state?.mechanics_info() +/obj/machinery/proc/get_power_sources_info() + . = list() + var/list/power_sources = get_all_components_of_type(/obj/item/stock_parts/power, FALSE) + if(!length(power_sources)) + . += "The machine has no power sources installed." + for(var/obj/item/stock_parts/power/source in power_sources) + . += source.get_source_info() + //////////////////////////////////////////////////////////////////////////////////////////// /obj/machinery/attack_ai(mob/living/silicon/ai/user) @@ -404,8 +416,13 @@ Class Procs: to_chat(user, "It is missing a screen, making it hard to interact with.") else if(stat & NOINPUT) to_chat(user, "It is missing any input device.") - else if((stat & NOPOWER) && !interact_offline) - to_chat(user, "It is not receiving power.") + + if((stat & NOPOWER)) + if(interact_offline) + to_chat(user, "It is not receiving power.") + else + to_chat(user, "It is not receiving power, making it hard to interact with.") + if(construct_state && construct_state.mechanics_info()) to_chat(user, "It can be manipulated using tools.") var/list/missing = missing_parts() diff --git a/code/game/machinery/_machines_base/machinery_components.dm b/code/game/machinery/_machines_base/machinery_components.dm index 8dc17b1d543..05079878980 100644 --- a/code/game/machinery/_machines_base/machinery_components.dm +++ b/code/game/machinery/_machines_base/machinery_components.dm @@ -37,7 +37,7 @@ var/global/list/machine_path_to_circuit_type for(var/access_list in initial_access) // Each part is an AND component. var/obj/item/stock_parts/network_receiver/network_lock/lock = install_component(/obj/item/stock_parts/network_receiver/network_lock/buildable, refresh_parts = FALSE) - lock.grants = islist(access_list) ? access_list : list(access_list) + lock.groups = islist(access_list) ? access_list : list(access_list) // Create the parts we are supposed to have. If not full_populate, this is only hard-baked parts, and more will be added later. for(var/component_path in uncreated_component_parts) diff --git a/code/game/machinery/_machines_base/machinery_power.dm b/code/game/machinery/_machines_base/machinery_power.dm index 0deba04fa82..60c3fcb2d3d 100644 --- a/code/game/machinery/_machines_base/machinery_power.dm +++ b/code/game/machinery/_machines_base/machinery_power.dm @@ -83,12 +83,13 @@ This is /obj/machinery level code to properly manage power usage from the area. return // Do not do power stuff in New/Initialize until after ..() -/obj/machinery/Initialize() +/obj/machinery/Initialize(mapload) if(MACHINE_UPDATES_FROM_AREA_POWER) var/area/my_area = get_area(src) if(istype(my_area)) events_repository.register(/decl/observ/area_power_change, my_area, src, .proc/power_change) - REPORT_POWER_CONSUMPTION_CHANGE(0, get_power_usage()) + if(mapload) // currently outside mapload, movables trigger loc/Entered(src, null) in ..(), which will update power. + REPORT_POWER_CONSUMPTION_CHANGE(0, get_power_usage()) events_repository.register(/decl/observ/moved, src, src, .proc/update_power_on_move) power_init_complete = TRUE . = ..() @@ -107,6 +108,9 @@ This is /obj/machinery level code to properly manage power usage from the area. area_changed(get_area(old_loc), get_area(new_loc)) /obj/machinery/proc/area_changed(area/old_area, area/new_area) + if(!power_init_complete) + return // this is possible if called externally + if(old_area == new_area) return var/power = get_power_usage() @@ -162,5 +166,22 @@ This is /obj/machinery level code to properly manage power usage from the area. if(power_init_complete && (use_power_mode == use_power)) REPORT_POWER_CONSUMPTION_CHANGE(old_power, new_power_consumption) +// Return the powernet of a cable node underneath the machine. +/obj/machinery/proc/get_powernet() + var/turf/T = loc + if(!istype(T)) + return + + var/obj/structure/cable/C = T.get_cable_node() + if(C) + return C.powernet + +// Adds available power to a connected powernet, if available. +/obj/machinery/proc/generate_power(var/amount) + var/datum/powernet/P = get_powernet() + if(!P) + return + P.newavail += amount + #undef REPORT_POWER_CONSUMPTION_CHANGE -#undef MACHINE_UPDATES_FROM_AREA_POWER \ No newline at end of file +#undef MACHINE_UPDATES_FROM_AREA_POWER diff --git a/code/game/machinery/_machines_base/machinery_public_vars.dm b/code/game/machinery/_machines_base/machinery_public_vars.dm index 4e894858e81..27ef2d83efb 100644 --- a/code/game/machinery/_machines_base/machinery_public_vars.dm +++ b/code/game/machinery/_machines_base/machinery_public_vars.dm @@ -120,9 +120,9 @@ Public methods machines can expose. Pretty bare-bones; just wraps a proc and giv /decl/public_access/public_method/proc/perform(datum/owner, ...) if(forward_args) - call(owner, call_proc)(arglist(args.Copy(2))) + return call(owner, call_proc)(arglist(args.Copy(2))) else - call(owner, call_proc)() + return call(owner, call_proc)() /* Machinery implementation diff --git a/code/game/machinery/_machines_base/stock_parts/network_lock.dm b/code/game/machinery/_machines_base/stock_parts/network_lock.dm index 15a3deaf83f..e7fe59b476e 100644 --- a/code/game/machinery/_machines_base/stock_parts/network_lock.dm +++ b/code/game/machinery/_machines_base/stock_parts/network_lock.dm @@ -1,3 +1,5 @@ +#define MAX_PATTERNS 10 + /obj/item/stock_parts/network_receiver/network_lock name = "network access lock" desc = "An id-based access lock preventing tampering with a machine's hardware and software. Connects wirelessly to network." @@ -8,10 +10,15 @@ var/auto_deny_all // Set this to TRUE to deny all access attempts if network connection is lost. var/initial_network_id // The address to the network var/initial_network_key // network KEY - var/list/grants = list() // List of grants required to operate the device. + var/selected_parent_group // Current selected parent_group for access assignment. + + var/list/groups // List of lists of groups. In order to access the device, users must have membership in at least one + // of the groups in each list. + var/selected_pattern // Index of the group pattern selected. + var/emagged // Whether or not this has been emagged. var/error - var/signal_strength = NETWORK_CONNECTION_WIRELESS // How good the wireless capabilities are of this card. + var/interact_sounds = list("keyboard", "keystroke") var/interact_sound_volume = 40 var/static/legacy_compatibility_mode = TRUE // Makes legacy access on ids play well with mapped devices with network locks. Override if your server is fully using network-enabled ids or has no mapped access. @@ -22,13 +29,17 @@ /obj/item/stock_parts/network_receiver/network_lock/emag_act(remaining_charges, mob/user, emag_source) . = ..() - if(length(req_access) && istype(loc, /obj/machinery)) // Don't emag it outside; you can just cut access without it anyway. + if(istype(loc, /obj/machinery)) // Don't emag it outside; you can just cut access without it anyway. emagged = TRUE to_chat(user, SPAN_NOTICE("You slide the card into \the [src]. It flashes purple briefly, then disengages.")) . = max(., 1) // Override. This checks the network and builds a dynamic req_access list for the device it's attached to. /obj/item/stock_parts/network_receiver/network_lock/get_req_access() + // Broken network locks require no access. + if(!is_functional()) + return list() + . = get_default_access() var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) var/datum/computer_network/network = D.get_network() @@ -39,18 +50,20 @@ if(!access_controller) return - var/list/resulting_grants = list() - for(var/grant_data in grants) - var/datum/computer_file/data/grant_record/grant = access_controller.get_grant(grant_data) - if(!istype(grant)) - continue // couldn't find. - resulting_grants |= uppertext("[D.network_id].[grant_data]") - if(legacy_compatibility_mode) - resulting_grants |= grant_data // This lets just grant_data, which is the access string for mapped machines, be used as an alternative. - - if(!resulting_grants.len) + LAZYINITLIST(groups) + var/list/resulting_access = list() + for(var/list/pattern in groups) + var/list/resulting_pattern = list() + for(var/group in pattern) + if(!(group in access_controller.get_all_groups())) + pattern -= group // This group doesn't exist anymore - delete it. + continue + resulting_pattern |= "[group].[D.network_id]" + if(resulting_pattern.len) + resulting_access += list(resulting_pattern) + if(!length(resulting_access)) return - return list(resulting_grants) // List of lists is an OR type access configuration. + return resulting_access /obj/item/stock_parts/network_receiver/network_lock/proc/get_default_access() if(auto_deny_all) @@ -62,6 +75,23 @@ if(emagged && user.skill_check_multiple(list(SKILL_FORENSICS = SKILL_EXPERT, SKILL_COMPUTER = SKILL_EXPERT))) to_chat(user, SPAN_WARNING("On close inspection, there is something odd about the interface. You suspect it may have been tampered with.")) +/obj/item/stock_parts/network_receiver/network_lock/attackby(obj/item/W, mob/user) + . = ..() + if(istype(W, /obj/item/card/id)) + if(check_access(W)) + playsound(src, 'sound/machines/ping.ogg', 20, 0) + else + playsound(src, 'sound/machines/buzz-two.ogg', 20, 0) + +/obj/item/stock_parts/network_receiver/network_lock/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + if(istype(target, /obj/item/stock_parts/network_receiver/network_lock)) + var/obj/item/stock_parts/network_receiver/network_lock/other_lock = target + if(length(other_lock.groups)) // Prevent mistakingly copying from a blank lock instead of vice versa. + groups = other_lock.groups.Copy() + playsound(src, 'sound/machines/ping.ogg', 20, 0) + to_chat(user, SPAN_NOTICE("\The [src] pings as it successfully copies its access requirements from the other network lock.")) + + /obj/item/stock_parts/network_receiver/network_lock/attack_self(var/mob/user) ui_interact(user) @@ -83,15 +113,48 @@ return data["connected"] = TRUE data["default_state"] = auto_deny_all - var/list/grants_data = list() if(!network.access_controller) return - for(var/datum/computer_file/data/grant_record/GR in network.access_controller.get_all_grants()) - grants_data.Add(list(list( - "grant_name" = GR.stored_data, - "assigned" = (GR.stored_data in grants) - ))) - data["grants"] = grants_data + + data["patterns"] = list() + var/pattern_index = 0 + for(var/list/pattern in groups) + pattern_index++ + data["patterns"].Add(list(list( + "index" = "[pattern_index]", + "groups" = english_list(pattern, "No groups assigned!", and_text = " or ") + ))) + + var/list/group_dictionary = network.access_controller.get_group_dict() + var/list/parent_groups_data + var/list/child_groups_data + + var/list/pattern = LAZYACCESS(groups, selected_pattern) + + if(pattern) + if(selected_parent_group) + if(!(selected_parent_group in group_dictionary)) + selected_parent_group = null + else + var/list/child_groups = group_dictionary[selected_parent_group] + if(child_groups) + child_groups_data = list() + for(var/child_group in child_groups) + child_groups_data.Add(list(list( + "child_group" = child_group, + "assigned" = (LAZYISIN(pattern, child_group)) + ))) + if(!selected_parent_group) // Check again in case we ended up with a non-existent selected parent group instead of breaking the UI. + parent_groups_data = list() + for(var/parent_group in group_dictionary) + parent_groups_data.Add(list(list( + "parent_group" = parent_group, + "assigned" = (LAZYISIN(pattern, parent_group)) + ))) + data["parent_groups"] = parent_groups_data + data["child_groups"] = child_groups_data + data["selected_parent_group"] = selected_parent_group + data["selected_pattern"] = "[selected_pattern]" /obj/item/stock_parts/network_receiver/network_lock/OnTopic(mob/user, href_list, datum/topic_state/state) . = ..() @@ -118,22 +181,59 @@ auto_deny_all = TRUE return TOPIC_REFRESH - if(href_list["remove_grant"]) - grants -= href_list["remove_grant"] + if(href_list["add_pattern"]) + if(length(groups) >= MAX_PATTERNS) + to_chat(usr, SPAN_WARNING("You cannot add more than [MAX_PATTERNS] patterns to \the [src]!")) + return TOPIC_HANDLED + LAZYADD(groups, list(list())) + return TOPIC_REFRESH + + if(href_list["remove_pattern"]) + var/pattern_index = text2num(href_list["remove_pattern"]) + LAZYREMOVE(groups, list(LAZYACCESS(groups, pattern_index))) // We have to encapsulate the pattern in another list to actually delete it. + if(selected_pattern == pattern_index) + selected_pattern = null + else if(selected_pattern > pattern_index) + selected_pattern-- + return TOPIC_REFRESH + + if(href_list["select_pattern"]) + var/pattern_index = text2num(href_list["select_pattern"]) + if(pattern_index > LAZYLEN(groups)) + return TOPIC_HANDLED + selected_pattern = pattern_index + return TOPIC_REFRESH + + if(href_list["select_parent_group"]) + selected_parent_group = href_list["select_parent_group"] return TOPIC_REFRESH + + if(href_list["info"]) + switch(href_list["info"]) + if("pattern") + to_chat(user, SPAN_NOTICE("In order to access the device, users must be a member of at least one group in each access pattern.")) + if("parent_groups") + to_chat(user, SPAN_NOTICE("Assigning a parent group to an access pattern will permit any member of its child groups access to the pattern.")) - if(href_list["assign_grant"]) - grants |= href_list["assign_grant"] + var/list/pattern_list = LAZYACCESS(groups, selected_pattern) + if(!pattern_list) + return TOPIC_REFRESH + + if(href_list["assign_group"]) + pattern_list |= href_list["assign_group"] + return TOPIC_REFRESH + + if(href_list["remove_group"]) + pattern_list -= href_list["remove_group"] return TOPIC_REFRESH /obj/item/stock_parts/network_receiver/network_lock/ui_interact(mob/user, ui_key, datum/nanoui/ui, force_open, datum/nanoui/master_ui, datum/topic_state/state) var/data = ui_data(user) ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) - ui = new(user, src, ui_key, "network_lock.tmpl", capitalize(name), 380, 500) + ui = new(user, src, ui_key, "network_lock.tmpl", capitalize(name), 500, 500) ui.set_initial_data(data) ui.open() - ui.set_auto_update(1) /obj/item/stock_parts/network_receiver/network_lock/CouldUseTopic(var/mob/user) ..() @@ -146,4 +246,15 @@ /obj/item/stock_parts/network_receiver/network_lock/buildable part_flags = PART_FLAG_HAND_REMOVE material = /decl/material/solid/metal/steel - matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) \ No newline at end of file + matter = list(/decl/material/solid/fiberglass = MATTER_AMOUNT_REINFORCEMENT) + +// Prevent tampering with machinery you don't have access to. +/obj/machinery/cannot_transition_to(state_path, mob/user) + var/decl/machine_construction/state = GET_DECL(state_path) + if(state && !state.locked && construct_state && construct_state.locked) + for(var/obj/item/stock_parts/network_receiver/network_lock/lock in get_all_components_of_type(/obj/item/stock_parts/network_receiver/network_lock)) + if(!lock.check_access(user)) + return SPAN_WARNING("\The [lock] flashes red! You lack the access to unlock this.") + return ..() + +#undef MAX_PATTERNS \ No newline at end of file diff --git a/code/game/machinery/_machines_base/stock_parts/power/battery.dm b/code/game/machinery/_machines_base/stock_parts/power/battery.dm index 0ce2a336d40..e873518f8c4 100644 --- a/code/game/machinery/_machines_base/stock_parts/power/battery.dm +++ b/code/game/machinery/_machines_base/stock_parts/power/battery.dm @@ -190,6 +190,9 @@ /obj/item/stock_parts/power/battery/get_cell() return cell +/obj/item/stock_parts/power/battery/get_source_info() + return "The machine can receive power from an installed power cell." + /obj/item/stock_parts/power/battery/buildable part_flags = PART_FLAG_HAND_REMOVE material = /decl/material/solid/metal/steel diff --git a/code/game/machinery/_machines_base/stock_parts/power/power.dm b/code/game/machinery/_machines_base/stock_parts/power/power.dm index 91a5f8d0e3f..f71217884a7 100644 --- a/code/game/machinery/_machines_base/stock_parts/power/power.dm +++ b/code/game/machinery/_machines_base/stock_parts/power/power.dm @@ -34,4 +34,7 @@ return 0 // This alerts the part that it does not need to provide power anymore. -/obj/item/stock_parts/power/proc/not_needed(var/obj/machinery/machine) \ No newline at end of file +/obj/item/stock_parts/power/proc/not_needed(var/obj/machinery/machine) + +// Returns OOC information about how to use this power source on examine, if the machine is not receiving power. +/obj/item/stock_parts/power/proc/get_source_info() \ No newline at end of file diff --git a/code/game/machinery/_machines_base/stock_parts/power/terminal.dm b/code/game/machinery/_machines_base/stock_parts/power/terminal.dm index bfd5fd5e48f..9a762251e5f 100644 --- a/code/game/machinery/_machines_base/stock_parts/power/terminal.dm +++ b/code/game/machinery/_machines_base/stock_parts/power/terminal.dm @@ -12,7 +12,7 @@ if(status & PART_STAT_ACTIVE) machine.update_power_channel(cached_channel) unset_status(machine, PART_STAT_ACTIVE) - unset_terminal(loc, terminal) + qdel(terminal) // Will call unset_terminal(). ..() /obj/item/stock_parts/power/terminal/Destroy() @@ -27,8 +27,6 @@ machine.power_change() return - - var/surplus = terminal.surplus() var/usage = machine.get_power_usage() @@ -73,7 +71,9 @@ unset_terminal(machine, terminal) terminal = new_terminal terminal.master = src + events_repository.register(/decl/observ/destroyed, terminal, src, .proc/unset_terminal) + terminal.queue_icon_update() set_extension(src, /datum/extension/event_registration/shuttle_stationary, GET_DECL(/decl/observ/moved), machine, .proc/machine_moved, get_area(src)) set_status(machine, PART_STAT_CONNECTED) @@ -151,35 +151,31 @@ make_terminal(machine) return TRUE - if(isWirecutter(I) && terminal) - var/turf/T = get_step(machine, terminal_dir) - if(terminal_dir && user.loc != T) - return FALSE // Wrong terminal handler. - if(istype(T) && !T.is_plating()) - to_chat(user, "You must remove the floor plating in front of \the [machine] first.") - return TRUE - user.visible_message("\The [user] dismantles the power terminal from \the [machine].", \ - "You begin to cut the cables...") - playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) - if(do_after(user, 50, machine)) - if(terminal && (machine == loc) && machine.components_are_accessible(type)) - if (prob(50) && electrocute_mob(user, terminal.powernet, terminal)) - spark_at(machine, amount=5, cardinal_only = TRUE) - if(HAS_STATUS(user, STAT_STUN)) - return TRUE - new /obj/item/stack/cable_coil(T, 10) - to_chat(user, "You cut the cables and dismantle the power terminal.") - qdel(terminal) - return TRUE +/obj/item/stock_parts/power/terminal/get_source_info() + . = "The machine can receive power by direct connection to the powernet. " + if(terminal) + if(!terminal.get_powernet()) + . += "The power terminal must be connected to the powernet using additional cables." + else + . += "The connected powernet must be powered." + else + . += "Add cables to the machine to construct a power terminal." /obj/item/stock_parts/power/terminal/buildable part_flags = PART_FLAG_HAND_REMOVE material = /decl/material/solid/metal/steel -/decl/stock_part_preset/terminal_setup +/decl/stock_part_preset/terminal_connect expected_part_type = /obj/item/stock_parts/power/terminal -/decl/stock_part_preset/terminal_setup/apply(obj/machinery/machine, var/obj/item/stock_parts/power/terminal/part) +/decl/stock_part_preset/terminal_connect/apply(obj/machinery/machine, var/obj/item/stock_parts/power/terminal/part) var/obj/machinery/power/terminal/term = locate() in machine.loc if(istype(term) && !term.master) - part.set_terminal(machine, term) \ No newline at end of file + part.set_terminal(machine, term) + +/decl/stock_part_preset/terminal_setup + expected_part_type = /obj/item/stock_parts/power/terminal + +/decl/stock_part_preset/terminal_setup/apply(obj/machinery/machine, var/obj/item/stock_parts/power/terminal/part) + if(isturf(machine.loc)) + part.make_terminal(machine) \ No newline at end of file diff --git a/code/game/machinery/_machines_base/stock_parts/power/tesla.dm b/code/game/machinery/_machines_base/stock_parts/power/tesla.dm index a84044af506..23fb1646ab6 100644 --- a/code/game/machinery/_machines_base/stock_parts/power/tesla.dm +++ b/code/game/machinery/_machines_base/stock_parts/power/tesla.dm @@ -26,6 +26,9 @@ A.use_power_oneoff(amount, channel) return amount +/obj/item/stock_parts/power/apc/get_source_info() + return "The machine can receive power wirelessly from a nearby area power controller." + /obj/item/stock_parts/power/apc/buildable part_flags = PART_FLAG_HAND_REMOVE material = /decl/material/solid/metal/steel \ No newline at end of file diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 04ab8cc6737..2e8bb4475ff 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -1,5 +1,5 @@ /obj/machinery/portable_atmospherics/powered/scrubber - name = "Portable Air Scrubber" + name = "portable air scrubber" icon = 'icons/obj/atmos.dmi' icon_state = "pscrubber:0" diff --git a/code/game/machinery/bodyscanner.dm b/code/game/machinery/bodyscanner.dm index 1bbb5e45066..a1388520a19 100644 --- a/code/game/machinery/bodyscanner.dm +++ b/code/game/machinery/bodyscanner.dm @@ -77,7 +77,7 @@ return ..() /obj/machinery/bodyscanner/proc/user_can_move_target_inside(var/mob/target, var/mob/user) - if(!istype(user) || !istype(target)) + if(!istype(user) || !istype(target) || target.anchored) return FALSE if(occupant) to_chat(user, "The scanner is already occupied!") @@ -113,6 +113,9 @@ /obj/machinery/bodyscanner/receive_mouse_drop(var/atom/dropping, var/mob/user) . = ..() if(!. && isliving(dropping)) + var/mob/living/M = dropping + if(M.anchored) + return FALSE user.visible_message( \ SPAN_NOTICE("\The [user] begins placing \the [dropping] into \the [src]."), \ SPAN_NOTICE("You start placing \the [dropping] into \the [src].")) diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 16e5b40c0df..d98a396a479 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -1,5 +1,5 @@ /obj/machinery/computer - name = "computer" + name = "computer console" icon = 'icons/obj/computer.dmi' icon_state = "computer" density = 1 diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index f153ddc892a..ace15c0940c 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -280,19 +280,24 @@ control_computer = null /obj/machinery/cryopod/proc/check_occupant_allowed(mob/M) + + if(!istype(M) || M.anchored) + return FALSE + var/correct_type = 0 for(var/type in allow_occupant_types) if(istype(M, type)) correct_type = 1 break - if(!correct_type) return 0 + if(!correct_type) + return FALSE for(var/type in disallow_occupant_types) if(istype(M, type)) - return 0 + return FALSE - return 1 + return TRUE /obj/machinery/cryopod/examine(mob/user) . = ..() diff --git a/code/game/machinery/doors/_door.dm b/code/game/machinery/doors/_door.dm index 5606dce267e..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) @@ -544,6 +549,9 @@ if(lock.locked && length(lock.req_access)) . |= lock.req_access + for(var/obj/item/stock_parts/network_receiver/network_lock/lock in get_all_components_of_type(/obj/item/stock_parts/network_receiver/network_lock)) + . |= lock.get_req_access() + /obj/machinery/door/do_simple_ranged_interaction(var/mob/user) if((!requiresID() || allowed(null)) && can_operate(user) && can_open_manually) toggle() diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 9f6f37302a5..8240edc22bd 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -295,10 +295,12 @@ About the new airlock wires panel: return 0 /obj/machinery/door/airlock/on_update_icon(state=0, override=0) - if(connections & (NORTH|SOUTH)) - set_dir(EAST) - else - set_dir(SOUTH) + + if(set_dir_on_update) + if(connections & (NORTH|SOUTH)) + set_dir(EAST) + else + set_dir(SOUTH) switch(state) if(0) diff --git a/code/game/machinery/doors/blast_door.dm b/code/game/machinery/doors/blast_door.dm index 268b67616da..f7431bd00e1 100644 --- a/code/game/machinery/doors/blast_door.dm +++ b/code/game/machinery/doors/blast_door.dm @@ -82,6 +82,13 @@ // Parameters: None // Description: Updates icon of this object. Uses icon state variables. /obj/machinery/door/blast/on_update_icon() + + if(set_dir_on_update) + if(connections & (NORTH|SOUTH)) + set_dir(EAST) + else + set_dir(SOUTH) + if(density) if(stat & BROKEN) icon_state = icon_state_closed_broken @@ -103,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 @@ -119,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() @@ -179,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) @@ -218,6 +230,15 @@ sleep(5 SECONDS) close() +/obj/machinery/door/blast/dismantle() + var/obj/structure/door_assembly/da = ..() + . = da + + da.anchored = 1 + da.state = 1 + da.created_name = name + da.update_icon() + /decl/public_access/public_method/close_door_delayed name = "delayed close door" desc = "Closes the door if possible, after a short delay." 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/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index 72cd5660c24..a1d2d678cdc 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -67,7 +67,7 @@ /obj/machinery/embedded_controller/radio/Destroy() if(radio_controller) radio_controller.remove_object(src,frequency) - ..() + return ..() /obj/machinery/embedded_controller/radio/on_update_icon() overlays.Cut() diff --git a/code/game/machinery/floor_light.dm b/code/game/machinery/floor_light.dm index 2f14396c871..5911e499a7c 100644 --- a/code/game/machinery/floor_light.dm +++ b/code/game/machinery/floor_light.dm @@ -25,6 +25,10 @@ var/global/list/floor_light_cache = list() /obj/machinery/floor_light/prebuilt anchored = 1 +/obj/machinery/floor_light/Initialize() + . = ..() + update_icon() + /obj/machinery/floor_light/attackby(var/obj/item/W, var/mob/user) if(isScrewdriver(W)) anchored = !anchored @@ -100,7 +104,7 @@ var/global/list/floor_light_cache = list() set_light(0) /obj/machinery/floor_light/on_update_icon() - overlays.Cut() + cut_overlays() if((use_power == POWER_USE_ACTIVE) && !(stat & (NOPOWER | BROKEN))) if(isnull(damaged)) var/cache_key = "floorlight-[default_light_color]" @@ -110,7 +114,7 @@ var/global/list/floor_light_cache = list() I.plane = plane I.layer = layer+0.001 floor_light_cache[cache_key] = I - overlays |= floor_light_cache[cache_key] + add_overlay(floor_light_cache[cache_key]) else if(damaged == 0) //Needs init. damaged = rand(1,4) @@ -121,7 +125,7 @@ var/global/list/floor_light_cache = list() I.plane = plane I.layer = layer+0.001 floor_light_cache[cache_key] = I - overlays |= floor_light_cache[cache_key] + add_overlay(floor_light_cache[cache_key]) update_brightness() /obj/machinery/floor_light/explosion_act(severity) diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm index f169aa2cc90..8c5e8d67000 100644 --- a/code/game/machinery/rechargestation.dm +++ b/code/game/machinery/rechargestation.dm @@ -30,6 +30,9 @@ /obj/machinery/recharge_station/receive_mouse_drop(var/atom/dropping, var/mob/user) . = ..() if(!. && isliving(dropping)) + var/mob/living/M = dropping + if(M.anchored) + return FALSE user.visible_message( \ SPAN_NOTICE("\The [user] begins placing \the [dropping] into \the [src]."), \ SPAN_NOTICE("You start placing \the [dropping] into \the [src].")) @@ -178,10 +181,7 @@ /obj/machinery/recharge_station/proc/go_in(var/mob/M) - if(occupant) - return - - if(!hascell(M)) + if(occupant || M.anchored || !hascell(M)) return add_fingerprint(M) 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/machinery/suit_cycler_units.dm b/code/game/machinery/suit_cycler_units.dm index d4696ce8e11..1a6fc2f316e 100644 --- a/code/game/machinery/suit_cycler_units.dm +++ b/code/game/machinery/suit_cycler_units.dm @@ -102,7 +102,9 @@ /obj/machinery/suit_cycler/generic name = "generic suit cycler" model_text = "Generic" + req_access = list() initial_access = list() + locked = FALSE /obj/machinery/suit_cycler/generic/prepared buildable = FALSE diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index ccc91b16d48..6c644989e36 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -14,7 +14,7 @@ anchored = 1 density = 1 - + var/temptext = "" var/selfdestructing = 0 var/charges = 1 @@ -55,12 +55,11 @@ src.updateUsrDialog() return charges -= 1 - switch(rand(1,2)) - if(1) - temptext = "Double-crosser. You planned to betray us from the start. Allow us to repay the favor in kind." - src.updateUsrDialog() - spawn(rand(50,200)) selfdestruct() - return + if(prob(50)) + temptext = "Double-crosser. You planned to betray us from the start. Allow us to repay the favor in kind." + src.updateUsrDialog() + addtimer(CALLBACK(src, .proc/selfdestruct), rand(5, 20) SECONDS) + return if(istype(M, /mob/living/carbon/human)) var/mob/living/carbon/human/N = M to_chat(M, "You have joined the ranks of the Syndicate and become a traitor to the station!") @@ -76,94 +75,89 @@ /obj/machinery/syndicate_beacon/proc/selfdestruct() selfdestructing = 1 - spawn() explosion(src.loc, 1, rand(1,3), rand(3,8), 10) + INVOKE_ASYNC(GLOBAL_PROC, .proc/explosion, src.loc, 1, rand(1, 3), rand(3, 8), 10) //////////////////////////////////////// //Singularity beacon //////////////////////////////////////// -/obj/machinery/power/singularity_beacon +/obj/machinery/singularity_beacon name = "ominous beacon" desc = "This looks suspicious..." icon = 'icons/obj/singularity.dmi' icon_state = "beacon" + uncreated_component_parts = list(/obj/item/stock_parts/power/terminal) anchored = 0 density = 1 layer = BASE_ABOVE_OBJ_LAYER //so people can't hide it and it's REALLY OBVIOUS stat = 0 + use_power = POWER_USE_OFF - var/active = 0 var/icontype = "beacon" -/obj/machinery/power/singularity_beacon/proc/Activate(mob/user = null) - if(surplus() < 1500) - if(user) to_chat(user, "The connected wire doesn't have enough current.") - return +/obj/machinery/singularity_beacon/proc/Activate(mob/user = null) for(var/obj/singularity/singulo in global.singularities) if(singulo.z == z) singulo.target = src icon_state = "[icontype]1" - active = 1 - - START_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) + update_use_power(POWER_USE_ACTIVE) if(user) to_chat(user, "You activate the beacon.") - -/obj/machinery/power/singularity_beacon/proc/Deactivate(mob/user = null) +/obj/machinery/singularity_beacon/proc/Deactivate(mob/user = null) for(var/obj/singularity/singulo in global.singularities) if(singulo.target == src) singulo.target = null icon_state = "[icontype]0" - active = 0 + update_use_power(POWER_USE_OFF) if(user) to_chat(user, "You deactivate the beacon.") - -/obj/machinery/power/singularity_beacon/physical_attack_hand(var/mob/user) +/obj/machinery/singularity_beacon/physical_attack_hand(var/mob/user) . = TRUE if(anchored) - if(active) + if(use_power) Deactivate(user) else Activate(user) else to_chat(user, "You need to screw the beacon to the floor first!") -/obj/machinery/power/singularity_beacon/attackby(obj/item/W, mob/user) +/obj/machinery/singularity_beacon/attackby(obj/item/W, mob/user) if(isScrewdriver(W)) - if(active) + if(use_power) to_chat(user, "You need to deactivate the beacon first!") return if(anchored) anchored = 0 to_chat(user, "You unscrew the beacon from the floor.") - disconnect_from_network() return else - if(!connect_to_network()) - to_chat(user, "This device must be placed over an exposed cable.") - return anchored = 1 - to_chat(user, "You screw the beacon to the floor and attach the cable.") + to_chat(user, "You screw the beacon to the floor.") return ..() return +// Ensure the terminal is always accessible to be plugged in. +/obj/machinery/singularity_beacon/components_are_accessible(var/path) + if(ispath(path, /obj/item/stock_parts/power/terminal)) + return TRUE + return ..() -/obj/machinery/power/singularity_beacon/Destroy() - if(active) +/obj/machinery/singularity_beacon/Destroy() + if(use_power) Deactivate() - ..() + . = ..() -//stealth direct power usage -/obj/machinery/power/singularity_beacon/Process() - if(!active) - return PROCESS_KILL - if(draw_power(1500) < 1500) +/obj/machinery/singularity_beacon/power_change() + . = ..() + if(!. || !use_power) + return + if(stat & NOPOWER) Deactivate() -/obj/machinery/power/singularity_beacon/syndicate +/obj/machinery/singularity_beacon/syndicate icontype = "beaconsynd" icon_state = "beaconsynd0" diff --git a/code/game/machinery/telecomms/broadcaster.dm b/code/game/machinery/telecomms/broadcaster.dm index d7d4023996a..d4743b6f866 100644 --- a/code/game/machinery/telecomms/broadcaster.dm +++ b/code/game/machinery/telecomms/broadcaster.dm @@ -103,7 +103,7 @@ var/global/message_delay = 0 // To make sure restarting the recentmessages list // In case message_delay is left on 1, otherwise it won't reset the list and people can't say the same thing twice anymore. if(message_delay) message_delay = 0 - ..() + return ..() /* diff --git a/code/game/machinery/telecomms/telecomunications.dm b/code/game/machinery/telecomms/telecomunications.dm index 3dca22439dc..168b9d6a550 100644 --- a/code/game/machinery/telecomms/telecomunications.dm +++ b/code/game/machinery/telecomms/telecomunications.dm @@ -144,7 +144,7 @@ var/global/list/obj/machinery/telecomms/telecomms_list = list() for(var/obj/machinery/telecomms/comm in telecomms_list) comm.links -= src links = list() - ..() + return ..() // Used in auto linking /obj/machinery/telecomms/proc/add_link(var/obj/machinery/telecomms/T) diff --git a/code/game/machinery/vending/botany.dm b/code/game/machinery/vending/botany.dm index 7731a5579f4..98efc34a101 100644 --- a/code/game/machinery/vending/botany.dm +++ b/code/game/machinery/vending/botany.dm @@ -84,10 +84,6 @@ /obj/item/chems/spray/waterflower = 1 ) -/obj/machinery/vending/hydroseeds/vend(var/datum/stored_items/vending_products/R, mob/user) - ..() - flick("[icon_state]-shelf[rand(3)]", src) - /obj/machinery/vending/hydroseeds/generic icon_state = "seeds_generic" icon_vend = "seeds_generic-vend" diff --git a/code/game/machinery/vending/security.dm b/code/game/machinery/vending/security.dm index d467f57d303..5298fab79bb 100644 --- a/code/game/machinery/vending/security.dm +++ b/code/game/machinery/vending/security.dm @@ -15,7 +15,7 @@ /obj/item/grenade/flashbang = 4, /obj/item/grenade/chem_grenade/teargas = 4, /obj/item/flash = 5, - /obj/item/chems/food/donut/normal = 12, + /obj/item/chems/food/donut = 12, /obj/item/storage/box/evidence = 6 ) contraband = list(/obj/item/clothing/glasses/sunglasses = 2,/obj/item/storage/box/donut = 2) diff --git a/code/game/movietitles.dm b/code/game/movietitles.dm index 9f37d5e716c..8b57417b5b6 100644 --- a/code/game/movietitles.dm +++ b/code/game/movietitles.dm @@ -86,9 +86,9 @@ var/global/list/end_titles /obj/screen/credit/Destroy() var/client/P = parent - if(parent) + if(istype(P)) P.screen -= src - LAZYREMOVE(P?.credits, src) + LAZYREMOVE(P.credits, src) parent = null return ..() diff --git a/code/game/objects/effects/chem/foam.dm b/code/game/objects/effects/chem/foam.dm index d5bf5087030..9b6d0c587b3 100644 --- a/code/game/objects/effects/chem/foam.dm +++ b/code/game/objects/effects/chem/foam.dm @@ -139,7 +139,7 @@ /obj/structure/foamedmetal/Destroy() set_density(0) update_nearby_tiles(1) - ..() + return ..() /obj/structure/foamedmetal/on_update_icon() ..() diff --git a/code/game/objects/items/books/skill_book.dm b/code/game/objects/items/books/skill_book.dm index c9b67b52726..4923de11526 100644 --- a/code/game/objects/items/books/skill_book.dm +++ b/code/game/objects/items/books/skill_book.dm @@ -33,21 +33,21 @@ Skill books that increase your skills while you activate and hold them */ /obj/item/book/skill - name = "default textbook" // requires default names for tradershop, cant rely on Initialize for names + name = "textbook" // requires default names for tradershop, cant rely on Initialize for names desc = "A blank textbook. (Notify admin)" 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/devices/scanners/health.dm b/code/game/objects/items/devices/scanners/health.dm index 662ad713087..8837a258951 100644 --- a/code/game/objects/items/devices/scanners/health.dm +++ b/code/game/objects/items/devices/scanners/health.dm @@ -205,7 +205,7 @@ var/found_disloc for(var/obj/item/organ/external/e in H.get_external_organs()) if(e) - if(!found_disloc && e.dislocated == 2) + if(!found_disloc && e.is_dislocated()) dat += "Dislocation detected. Advanced scanner required for location." found_disloc = TRUE if(!found_bleed && (e.status & ORGAN_ARTERY_CUT)) diff --git a/code/game/objects/items/stacks/tiles/tile_types.dm b/code/game/objects/items/stacks/tiles/tile_types.dm index b19d2628bfb..d219a2ee7eb 100644 --- a/code/game/objects/items/stacks/tiles/tile_types.dm +++ b/code/game/objects/items/stacks/tiles/tile_types.dm @@ -215,7 +215,7 @@ /obj/item/stack/tile/stone name = "stone slabs" - singular name = "stone slab" + singular_name = "stone slab" desc = "A smooth, flat slab of some kind of stone." icon_state = "tile_stone" diff --git a/code/game/objects/items/weapons/RPD.dm b/code/game/objects/items/weapons/RPD.dm index bfbef1c04dc..26afb5ec415 100644 --- a/code/game/objects/items/weapons/RPD.dm +++ b/code/game/objects/items/weapons/RPD.dm @@ -147,7 +147,7 @@ var/global/list/rpd_pipe_selection_skilled = list() return playsound(get_turf(user), 'sound/items/Deconstruct.ogg', 50, 1) - P.build(T, 1, pipe_colors[pipe_color]) + P.build(T, new/datum/fabricator_build_order(P, 1, list("slected_color" = pipe_colors[pipe_color]))) if(prob(20)) spark_at(src, amount = 5, holder = src) diff --git a/code/game/objects/items/weapons/cards_ids.dm b/code/game/objects/items/weapons/cards_ids.dm index 1617f2e0543..4637fcc8a99 100644 --- a/code/game/objects/items/weapons/cards_ids.dm +++ b/code/game/objects/items/weapons/cards_ids.dm @@ -16,6 +16,7 @@ desc = "Does card things." icon = 'icons/obj/card.dmi' w_class = ITEM_SIZE_TINY + material = /decl/material/solid/plastic slot_flags = SLOT_EARS drop_sound = 'sound/foley/paperpickup1.ogg' pickup_sound = 'sound/foley/paperpickup2.ogg' @@ -157,7 +158,9 @@ var/global/const/NO_EMAG_ACT = -50 var/list/access = list() var/registered_name = "Unknown" // The name registered_name on the card var/associated_account_number = 0 - var/list/associated_email_login = list("login" = "", "password" = "") + + // Associated network account. For normal IDs this is simply informational, but for network enabled IDs this is used for group-based access. + var/list/associated_network_account = list("login" = "", "password" = "") var/age = "\[UNSET\]" var/blood_type = "\[UNSET\]" @@ -299,7 +302,7 @@ var/global/const/NO_EMAG_ACT = -50 return /obj/item/card/id/GetAccess() - return access + return access.Copy() /obj/item/card/id/GetIdCard() return src @@ -469,53 +472,121 @@ var/global/const/NO_EMAG_ACT = -50 /obj/item/card/id/network var/network_id // The network_id that this card is paired to. - var/user_id // The user's ID this card belongs to. This is typically their access_record UID, which is their cortical stack ID. - var/datum/computer_file/report/crew_record/access_record // A cached link to the access_record belonging to this card. Do not save this. + var/weakref/current_account + color = COLOR_GRAY80 + detail_color = COLOR_SKY_BLUE /obj/item/card/id/network/Initialize() set_extension(src, /datum/extension/network_device/lazy) - if(!access_record) - refresh_access_record() return ..() -/obj/item/card/id/network/GetAccess() - if(!access_record) - refresh_access_record() - return access - -/obj/item/card/id/network/verb/resync() - set name = "Resync ID Card" - set category = "Object" - set src in usr - +/obj/item/card/id/network/GetAccess(var/ignore_account) + . = ..() + var/datum/computer_file/data/account/access_account = resolve_account() var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) - var/datum/computer_network/network = D.get_network() - if(!network) - if(usr.skill_check(SKILL_DEVICES, SKILL_EXPERT)) - to_chat(usr, SPAN_NOTICE("The red LED on the card flashes once, signaling it has no network.")) - else - to_chat(usr, "Pressing the synchronization button on the card causes a red LED to flash once.") + if(network && access_account && access_account.login != ignore_account) + var/location = "[network.network_id]" + if(access_account) + . += "[access_account.login]@[location]" // User access uses '@' + for(var/group in access_account.groups) + . += "[group].[location]" // Group access uses '.' + for(var/group in access_account.parent_groups) // Membership in a child group grants access to anything with an access requirement set to the parent group. + . += "[group].[location]" + +/obj/item/card/id/network/proc/resolve_account() + if(!current_account) return - if(refresh_access_record(network)) - to_chat(usr, "A green light flashes as the card is synchronized with its network.") - return - if(usr.skill_check(SKILL_DEVICES, SKILL_EXPERT)) - to_chat(usr, SPAN_NOTICE("The red LED on the card flashes three times, signaling it failed to synchronize the card with the network.")) + var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) + var/datum/computer_network/network = D.get_network() + + var/login = associated_network_account["login"] + var/password = associated_network_account["password"] + + var/error + var/datum/computer_file/data/account/check_account = current_account.resolve() + if(!network) // No network or connectivity. + error = "No network found" + else if(!istype(check_account)) + error = "The specified account could not be found" + else if(check_account.login != login || check_account.password != password) // The most likely case - login or password were changed. + error = "Incorrect username or password" + // Check if the account can be located on the network in case it was moved. + else if(!(check_account in network.get_accounts())) + error = "The specified account could not be found" + + if(error) + current_account = null + visible_message(SPAN_WARNING("\The [src] flashes an error: \'[error]!\'"), null, null,1) else - to_chat(usr, SPAN_WARNING("Pressing the synchronization button on the card causes a red LED to flash three times.")) + return check_account + +/obj/item/card/id/network/ui_interact(mob/user, ui_key = "main",var/datum/nanoui/ui = null) + var/data[0] + var/login = associated_network_account["login"] + var/password = associated_network_account["password"] + + data["login"] = login ? login : "Enter Login" + data["password"] = password ? stars(password, 0) : "Enter Password" + ui = SSnano.try_update_ui(user, src, ui_key, ui, data) + if (!ui) + ui = new(user, src, ui_key, "network_id.tmpl", "Network ID Settings", 540, 326) + ui.set_initial_data(data) + ui.open() + +/obj/item/card/id/network/Topic(href, href_list, datum/topic_state/state) + . = ..() + if(.) + return + var/login = associated_network_account["login"] + var/password = associated_network_account["password"] + if(href_list["change_login"]) + var/new_login = sanitize(input(usr, "Enter your account login:", "Account login", login) as text|null) + if(new_login == login || !CanInteract(usr, DefaultTopicState())) + return TOPIC_NOACTION + associated_network_account["login"] = new_login + + current_account = null + password = null + return TOPIC_REFRESH + + if(href_list["change_password"]) + var/new_password = sanitize(input(usr, "Enter your account password:", "Account password") as text|null) + if(new_password == password || !CanInteract(usr, DefaultTopicState())) + return TOPIC_NOACTION + associated_network_account["password"] = new_password + + current_account = null + return TOPIC_REFRESH + + if(href_list["login_account"]) + if(login_account()) + to_chat(usr, SPAN_NOTICE("Account successfully logged in.")) + else + to_chat(usr, SPAN_WARNING("Could not login to account. Check password or network connectivity.")) + return TOPIC_REFRESH -/obj/item/card/id/network/proc/refresh_access_record(var/datum/computer_network/network) - if(!network) + if(href_list["settings"]) var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) - network = D.get_network() + D.ui_interact(usr) + return TOPIC_HANDLED + +/obj/item/card/id/network/proc/login_account() + . = FALSE + var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) + var/datum/computer_network/network = D.get_network() if(!network) return - for(var/datum/extension/network_device/mainframe/mainframe in network.get_mainframes_by_role(MF_ROLE_CREW_RECORDS)) - for(var/datum/computer_file/report/crew_record/ar in mainframe.get_all_files()) - if(ar.user_id != user_id) - continue // Mismatch user file. - // We have a match! - access_record = ar - access = ar.get_access(network_id) - return TRUE \ No newline at end of file + var/login = associated_network_account["login"] + var/password = associated_network_account["password"] + for(var/datum/computer_file/data/account/check_account in network.get_accounts()) + if(check_account.login == login && check_account.password == password) + current_account = weakref(check_account) + return TRUE + +/obj/item/card/id/network/verb/adjust_settings() + set name = "Adjust Settings" + set category = "Object" + set src in usr + + ui_interact(usr) \ No newline at end of file diff --git a/code/game/objects/items/weapons/circuitboards/machinery/pacman.dm b/code/game/objects/items/weapons/circuitboards/machinery/pacman.dm index ae0f7cb586e..478632e5a8b 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/pacman.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/pacman.dm @@ -1,6 +1,6 @@ /obj/item/stock_parts/circuitboard/pacman name = "circuitboard (portable generator)" - build_path = /obj/machinery/power/port_gen/pacman + build_path = /obj/machinery/port_gen/pacman board_type = "machine" origin_tech = "{'programming':3,'powerstorage':3,'exoticmatter':3,'engineering':3}" req_components = list( @@ -15,15 +15,15 @@ ) /obj/item/stock_parts/circuitboard/pacman/super name = "circuitboard (portable fission generator)" - build_path = /obj/machinery/power/port_gen/pacman/super + build_path = /obj/machinery/port_gen/pacman/super origin_tech = "{'programming':3,'powerstorage':4,'engineering':4}" /obj/item/stock_parts/circuitboard/pacman/super/potato name = "circuitboard (PTTO-3 nuclear generator)" - build_path = /obj/machinery/power/port_gen/pacman/super/potato + build_path = /obj/machinery/port_gen/pacman/super/potato origin_tech = "{'programming':3,'powerstorage':5,'engineering':4}" /obj/item/stock_parts/circuitboard/pacman/mrs name = "circuitboard (portable fusion generator)" - build_path = /obj/machinery/power/port_gen/pacman/mrs + build_path = /obj/machinery/port_gen/pacman/mrs origin_tech = "{'programming':3,'powerstorage':5,'engineering':5}" diff --git a/code/game/objects/items/weapons/circuitboards/machinery/power.dm b/code/game/objects/items/weapons/circuitboards/machinery/power.dm index 2760881d7ef..46a04d8e228 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/power.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/power.dm @@ -64,7 +64,7 @@ /obj/item/stock_parts/circuitboard/turbine/motor name = "circuitboard (small turbine motor)" - build_path = /obj/machinery/power/turbinemotor + build_path = /obj/machinery/turbinemotor board_type = "machine" origin_tech = "{'powerstorage':4,'engineering':4}" req_components = list( @@ -87,7 +87,7 @@ /obj/item/stock_parts/circuitboard/big_turbine/center name = "circuitboard (large turbine motor)" - build_path = /obj/machinery/power/turbine + build_path = /obj/machinery/turbine board_type = "machine" origin_tech = "{'powerstorage':4,'engineering':4}" req_components = list( @@ -115,7 +115,7 @@ /obj/item/stock_parts/circuitboard/teg_turbine/motor name = "circuitboard (thermoelectric generator motor)" - build_path = /obj/machinery/power/generator + build_path = /obj/machinery/generator board_type = "machine" origin_tech = "{'powerstorage':4,'engineering':4}" req_components = list( diff --git a/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm b/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm index 8711635e311..a89768cdb25 100644 --- a/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm +++ b/code/game/objects/items/weapons/circuitboards/machinery/shieldgen.dm @@ -2,7 +2,7 @@ /obj/item/stock_parts/circuitboard/shield_generator name = "circuitboard (advanced shield generator)" board_type = "machine" - build_path = /obj/machinery/power/shield_generator + build_path = /obj/machinery/shield_generator origin_tech = "{'magnets':3,'powerstorage':4}" req_components = list( /obj/item/stock_parts/capacitor = 1, @@ -11,7 +11,7 @@ additional_spawn_components = list( /obj/item/stock_parts/console_screen = 1, /obj/item/stock_parts/keyboard = 1, - /obj/item/stock_parts/power/apc/buildable = 1 + /obj/item/stock_parts/power/terminal = 1 ) /obj/item/stock_parts/circuitboard/shield_diffuser diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm index f46c6bcf36e..c6751683dd4 100644 --- a/code/game/objects/items/weapons/cosmetics.dm +++ b/code/game/objects/items/weapons/cosmetics.dm @@ -61,7 +61,7 @@ else user.visible_message("[user] begins to do [H]'s lips with \the [src].", \ "You begin to apply \the [src].") - if(do_after(user, 20, H) && do_after(H, 20, needhand = 0, progress = 0, incapacitation_flags = INCAPACITATION_NONE)) //user needs to keep their active hand, H does not. + if(do_after(user, 20, H) && do_after(H, 20, check_holding = 0, progress = 0, incapacitation_flags = INCAPACITATION_NONE)) //user needs to keep their active hand, H does not. user.visible_message("[user] does [H]'s lips with \the [src].", \ "You apply \the [src].") H.lip_style = color 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/shields.dm b/code/game/objects/items/weapons/shields.dm index c48cc200ddb..87ebc16d983 100644 --- a/code/game/objects/items/weapons/shields.dm +++ b/code/game/objects/items/weapons/shields.dm @@ -30,7 +30,7 @@ return 1 /obj/item/shield - name = "shield" + name = "abstract shield" var/base_block_chance = 60 /obj/item/shield/handle_shield(mob/user, var/damage, atom/damage_source = null, mob/attacker = null, var/def_zone = null, var/attack_text = "the attack") @@ -111,7 +111,7 @@ /obj/item/shield/buckler name = "buckler" - desc = "A wooden buckler used to block sharp things from entering your body back in the day.." + desc = "A wooden buckler used to block sharp things from entering your body back in the day." icon = 'icons/obj/items/shield/buckler.dmi' icon_state = "buckler" slot_flags = SLOT_BACK 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/misc.dm b/code/game/objects/items/weapons/storage/misc.dm index cd5817acec5..479848b460c 100644 --- a/code/game/objects/items/weapons/storage/misc.dm +++ b/code/game/objects/items/weapons/storage/misc.dm @@ -33,7 +33,7 @@ can_hold = list(/obj/item/chems/food/donut) foldable = /obj/item/stack/material/cardstock - startswith = list(/obj/item/chems/food/donut/normal = 6) + startswith = list(/obj/item/chems/food/donut = 6) /obj/item/storage/box/donut/on_update_icon() overlays.Cut() diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index 319df9873a4..35b2db95d8b 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -38,7 +38,7 @@ . = (loc == user && istype(over, /obj/screen)) || ..() /obj/item/storage/handle_mouse_drop(var/atom/over, var/mob/user) - if(canremove && (ishuman(user) || isrobot(user))) + if(canremove && (ishuman(user) || isrobot(user) || isanimal(user)) && !user.incapacitated(INCAPACITATION_DISRUPTED)) if(over == user) open(user) return TRUE @@ -50,6 +50,21 @@ return TRUE . = ..() +/obj/item/storage/AltClick(mob/user) + if(!canremove) + return + + if(!Adjacent(user)) + return + + if(!(ishuman(user) || isrobot(user) || issmall(user))) + return + + if(user.incapacitated(INCAPACITATION_DISRUPTED)) + return + + open(user) + /obj/item/storage/proc/return_inv() var/list/L = list( ) diff --git a/code/game/objects/items/weapons/storage/storage_ui/default.dm b/code/game/objects/items/weapons/storage/storage_ui/default.dm index 41540a2b609..a78a165d6fc 100644 --- a/code/game/objects/items/weapons/storage/storage_ui/default.dm +++ b/code/game/objects/items/weapons/storage/storage_ui/default.dm @@ -176,22 +176,26 @@ row_num = round((adjusted_contents-1) / 7) // 7 is the maximum allowed width. arrange_item_slots(row_num, col_count) +#define SCREEN_LOC_MOD_FIRST 3 +#define SCREEN_LOC_MOD_SECOND 1.7 +#define SCREEN_LOC_MOD_DIVIDED (0.5 * world.icon_size) + //This proc draws out the inventory and places the items on it. It uses the standard position. -/datum/storage_ui/default/proc/arrange_item_slots(var/rows, var/cols) - var/cx = 4 - var/cy = 2+rows - boxes.screen_loc = "LEFT+4:16,BOTTOM+2:16 to LEFT+[4+cols]:16,BOTTOM+[2+rows]:16" +/datum/storage_ui/default/proc/arrange_item_slots(rows, cols) + var/cx = SCREEN_LOC_MOD_FIRST + var/cy = SCREEN_LOC_MOD_SECOND + rows + boxes.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED] to LEFT+[SCREEN_LOC_MOD_FIRST + cols]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND + rows]:[SCREEN_LOC_MOD_DIVIDED]" for(var/obj/O in storage.contents) - O.screen_loc = "LEFT+[cx]:16,BOTTOM+[cy]:16" + O.screen_loc = "LEFT+[cx]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[cy]:[SCREEN_LOC_MOD_DIVIDED]" O.maptext = "" O.hud_layerise() cx++ - if (cx > (4+cols)) - cx = 4 + if (cx > (SCREEN_LOC_MOD_FIRST + cols)) + cx = SCREEN_LOC_MOD_FIRST cy-- - closer.screen_loc = "LEFT+[4+cols+1]:16,BOTTOM+2:16" + closer.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST + cols + 1]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED]" /datum/storage_ui/default/proc/space_orient_objs() @@ -206,9 +210,9 @@ M.Scale((storage_width-storage_cap_width*2+3)/32,1) storage_continue.transform = M - storage_start.screen_loc = "LEFT+4:16,BOTTOM+2:16" - storage_continue.screen_loc = "LEFT+4:[storage_cap_width+(storage_width-storage_cap_width*2)/2+2],BOTTOM+2:16" - storage_end.screen_loc = "LEFT+4:[19+storage_width-storage_cap_width],BOTTOM+2:16" + storage_start.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST]:[SCREEN_LOC_MOD_DIVIDED],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED]" + storage_continue.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST]:[storage_cap_width+(storage_width-storage_cap_width*2)/2+2],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED]" + storage_end.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST]:[19+storage_width-storage_cap_width],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED]" var/startpoint = 0 var/endpoint = 1 @@ -231,11 +235,12 @@ storage_start.overlays += stored_continue storage_start.overlays += stored_end - O.screen_loc = "LEFT+4:[round((startpoint+endpoint)/2)+2-O.pixel_x],BOTTOM+2:[16-O.pixel_y]" + O.reset_offsets() + O.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST]:[round((startpoint+endpoint)/2)+2-O.pixel_x],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED-O.pixel_y]" O.maptext = "" O.hud_layerise() - closer.screen_loc = "LEFT+4:[storage_width+19],BOTTOM+2:16" + closer.screen_loc = "LEFT+[SCREEN_LOC_MOD_FIRST]:[storage_width+19],BOTTOM+[SCREEN_LOC_MOD_SECOND]:[SCREEN_LOC_MOD_DIVIDED]" // Sets up numbered display to show the stack size of each stored mineral // NOTE: numbered display is turned off currently because it's broken @@ -249,3 +254,7 @@ arrange_item_slots(row_num, col_count) if(user && user.s_active) user.s_active.show_to(user) + +#undef SCREEN_LOC_MOD_FIRST +#undef SCREEN_LOC_MOD_SECOND +#undef SCREEN_LOC_MOD_DIVIDED 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/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm index 8ea112c4984..0ca60c4e1cf 100644 --- a/code/game/objects/items/weapons/storage/wallets.dm +++ b/code/game/objects/items/weapons/storage/wallets.dm @@ -101,6 +101,20 @@ else return ..() + +/obj/item/storage/wallet/AltClick(mob/user) + if (user != loc || user.incapacitated() || !ishuman(user)) + return ..() + + var/obj/item/card/id/id = GetIdCard() + if (istype(id)) + remove_from_storage(id) + user.put_in_hands(id) + return + + return ..() + + /obj/item/storage/wallet/random/Initialize() . = ..() var/item1_type = pick( /obj/item/cash/c10,/obj/item/cash/c100,/obj/item/cash/c1000,/obj/item/cash/c20,/obj/item/cash/c200,/obj/item/cash/c50, /obj/item/cash/c500) diff --git a/code/game/objects/random/random.dm b/code/game/objects/random/random.dm index 940481e935c..67eb6906c95 100644 --- a/code/game/objects/random/random.dm +++ b/code/game/objects/random/random.dm @@ -693,7 +693,6 @@ /obj/item/chems/food/candy/proteinbar, /obj/item/chems/food/syndicake, /obj/item/chems/food/donut, - /obj/item/chems/food/donut/cherryjelly, /obj/item/chems/food/donut/jelly, /obj/item/pizzabox/meat, /obj/item/pizzabox/vegetable, @@ -1242,8 +1241,8 @@ var/global/list/random_useful_ /obj/random/mre/dessert/spawn_choices() return list(/obj/item/chems/food/candy, /obj/item/chems/food/candy/proteinbar, - /obj/item/chems/food/donut/normal, - /obj/item/chems/food/donut/cherryjelly, + /obj/item/chems/food/donut, + /obj/item/chems/food/donut/jelly, /obj/item/chems/food/chocolatebar, /obj/item/chems/food/cookie, /obj/item/chems/food/poppypretzel, @@ -1256,7 +1255,7 @@ var/global/list/random_useful_ /obj/random/mre/dessert/vegan/spawn_choices() return list(/obj/item/chems/food/candy, /obj/item/chems/food/chocolatebar, - /obj/item/chems/food/donut/cherryjelly, + /obj/item/chems/food/donut/jelly, /obj/item/chems/food/plumphelmetbiscuit) /obj/random/mre/drink diff --git a/code/game/objects/structures/__structure.dm b/code/game/objects/structures/__structure.dm index 2843132ae55..86fec6c512b 100644 --- a/code/game/objects/structures/__structure.dm +++ b/code/game/objects/structures/__structure.dm @@ -7,7 +7,6 @@ var/health = 0 var/maxhealth = 50 var/hitsound = 'sound/weapons/smash.ogg' - var/breakable var/parts_type var/parts_amount var/footstep_type @@ -179,18 +178,6 @@ AM.reset_offsets() AM.reset_plane_and_layer() -/obj/structure/attack_hand(mob/user) - ..() - if(breakable) - if(MUTATION_HULK in user.mutations) - user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" )) - attack_generic(user,1,"smashes") - else if(istype(user,/mob/living/carbon/human)) - var/mob/living/carbon/human/H = user - if(H.species.can_shred(user)) - attack_generic(user,1,"slices") - return ..() - /obj/structure/grab_attack(var/obj/item/grab/G) if (!G.force_danger()) to_chat(G.assailant, SPAN_WARNING("You need a better grip to do that!")) diff --git a/code/game/objects/structures/_structure_construction.dm b/code/game/objects/structures/_structure_construction.dm index 22566ca1693..2665c3743f6 100644 --- a/code/game/objects/structures/_structure_construction.dm +++ b/code/game/objects/structures/_structure_construction.dm @@ -138,3 +138,19 @@ if(.) user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) add_fingerprint(user) + +/obj/structure/attack_generic(var/mob/user, var/damage, var/attack_verb, var/environment_smash) + if(environment_smash >= 1) + damage = max(damage, 10) + + if(istype(user)) + user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) + user.do_attack_animation(src) + if(!damage) + return FALSE + if(damage >= 10) + visible_message(SPAN_DANGER("\The [user] [attack_verb] into [src]!")) + take_damage(damage) + else + visible_message(SPAN_NOTICE("\The [user] bonks \the [src] harmlessly.")) + return TRUE \ No newline at end of file diff --git a/code/game/objects/structures/catwalk.dm b/code/game/objects/structures/catwalk.dm index daaceef5817..79374098453 100644 --- a/code/game/objects/structures/catwalk.dm +++ b/code/game/objects/structures/catwalk.dm @@ -79,6 +79,19 @@ if(!QDELETED(src) && severity != 3) physically_destroyed() +/obj/structure/catwalk/grab_attack(var/obj/item/grab/G) + var/mob/living/affecting_mob = G.get_affecting_mob() + if(atom_flags & ATOM_FLAG_CLIMBABLE) + var/obj/occupied = turf_is_crowded() + if (occupied) + to_chat(G.assailant, SPAN_WARNING("There's \a [occupied] in the way.")) + return TRUE + G.affecting.forceMove(src.loc) + if(affecting_mob) + SET_STATUS_MAX(affecting_mob, STAT_WEAK, rand(2,5)) + visible_message(SPAN_DANGER("[G.assailant] puts [G.affecting] on \the [src].")) + return TRUE + /obj/structure/catwalk/attack_robot(var/mob/user) if(Adjacent(user)) attack_hand(user) diff --git a/code/game/objects/structures/displaycase.dm b/code/game/objects/structures/displaycase.dm index 3e7bd53dc0d..fe1b1d17920 100644 --- a/code/game/objects/structures/displaycase.dm +++ b/code/game/objects/structures/displaycase.dm @@ -117,13 +117,7 @@ if(!locked || destroyed) var/obj/item/selected_item - var/list/options = list() - - for(var/atom/movable/AM in src) - var/image/radial_button = image(icon = AM.icon, icon_state = AM.icon_state) - options[AM] = radial_button - - selected_item = show_radial_menu(user, src, options, radius = 42, require_near = TRUE, use_labels = TRUE) + selected_item = show_radial_menu(user, src, make_item_radial_menu_choices(src), radius = 42, require_near = TRUE, use_labels = TRUE) if(QDELETED(selected_item) || !contents.Find(selected_item) || !Adjacent(user) || user.incapacitated()) return diff --git a/code/game/objects/structures/door_assembly.dm b/code/game/objects/structures/door_assembly.dm index 8c9d6830f27..1e46a7db320 100644 --- a/code/game/objects/structures/door_assembly.dm +++ b/code/game/objects/structures/door_assembly.dm @@ -45,6 +45,28 @@ bound_width = world.icon_size bound_height = width * world.icon_size +/obj/structure/door_assembly/examine(mob/user) + . = ..() + switch(state) + if(0) + to_chat(user, "Use a wrench to [anchored ? "un" : ""]anchor it.") + if(!anchored) + if(glass == 1) + var/decl/material/glass_material_datum = GET_DECL(glass_material) + if(glass_material_datum) + var/mat_name = glass_material_datum.solid_name || glass_material_datum.name + to_chat(user, "Use a welder to remove the [mat_name] plating currently attached.") + else + to_chat(user, "Use a welder to disassemble completely.") + else + to_chat(user, "Use a cable coil to wire in preparation for electronics.") + if(1) + to_chat(user, "Use a wirecutter to remove the wiring and expose the frame.") + to_chat(user, "Insert electronics to proceed with construction.") + if(2) + to_chat(user, "Use a crowbar to remove the electronics.") + to_chat(user, "Use a screwdriver to complete assembly.") + /obj/structure/door_assembly/door_assembly_hatch icon = 'icons/obj/doors/hatch/door.dmi' panel_icon = 'icons/obj/doors/hatch/panel.dmi' diff --git a/code/game/objects/structures/doors/_door.dm b/code/game/objects/structures/doors/_door.dm index ffdaa951296..5f86d8b6db9 100644 --- a/code/game/objects/structures/doors/_door.dm +++ b/code/game/objects/structures/doors/_door.dm @@ -39,7 +39,7 @@ /obj/structure/door/on_update_icon() ..() - icon_state = "[icon_base][!density ? "open" : ""]" + icon_state = "[icon_base][!density ? "_open" : ""]" /obj/structure/door/proc/post_change_state() update_nearby_tiles() diff --git a/code/game/objects/structures/fires.dm b/code/game/objects/structures/fires.dm index 030b4e54fe4..2230f35c05e 100644 --- a/code/game/objects/structures/fires.dm +++ b/code/game/objects/structures/fires.dm @@ -180,7 +180,7 @@ /obj/structure/fire_source/attackby(var/obj/item/thing, var/mob/user) - if(ATOM_IS_TEMPERATURE_SENSITIVE(thing) && user.a_intent != I_HURT) + if(ATOM_SHOULD_TEMPERATURE_ENQUEUE(thing) && user.a_intent != I_HURT) thing.HandleObjectHeating(src, user, DIRECT_HEAT) return TRUE diff --git a/code/game/objects/structures/inflatable.dm b/code/game/objects/structures/inflatable.dm index 7612d769a05..1b8976775a2 100644 --- a/code/game/objects/structures/inflatable.dm +++ b/code/game/objects/structures/inflatable.dm @@ -1,5 +1,5 @@ /obj/item/inflatable - name = "inflatable" + name = "inflatable item" w_class = ITEM_SIZE_NORMAL icon = 'icons/obj/structures/inflatable.dmi' var/deploy_path = null @@ -37,7 +37,7 @@ deploy_path = /obj/structure/inflatable/door /obj/structure/inflatable - name = "inflatable" + name = "inflatable structure" desc = "An inflated membrane. Do not puncture." density = 1 anchored = 1 diff --git a/code/game/objects/structures/mineral_bath.dm b/code/game/objects/structures/mineral_bath.dm index 8c70a19bd64..c21573fba0b 100644 --- a/code/game/objects/structures/mineral_bath.dm +++ b/code/game/objects/structures/mineral_bath.dm @@ -27,7 +27,7 @@ /obj/structure/mineral_bath/proc/enter_bath(var/mob/living/patient, var/mob/user) - if(!istype(patient)) + if(!istype(patient) || patient.anchored) return FALSE var/self_drop = (user == patient) @@ -107,7 +107,7 @@ var/repaired_organ // Replace limbs for crystalline species. - if(H.species.is_crystalline && prob(10)) + if((H.species.species_flags & SPECIES_FLAG_CRYSTALLINE) && prob(10)) for(var/limb_type in H.species.has_limbs) var/obj/item/organ/external/E = H.get_organ(limb_type) if(E && !E.is_usable() && !(E.limb_flags & ORGAN_FLAG_HEALS_OVERKILL)) diff --git a/code/game/objects/structures/stool_bed_chair_nest/bed.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm similarity index 99% rename from code/game/objects/structures/stool_bed_chair_nest/bed.dm rename to code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm index 404aa77f9a7..f1562760cdb 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/bed.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/bed.dm @@ -86,6 +86,7 @@ C.use(1) if(!isturf(src.loc)) src.forceMove(get_turf(src)) + playsound(src.loc, 'sound/effects/rustle5.ogg', 50, 1) to_chat(user, "You add padding to \the [src].") add_padding(padding_type) return diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/chairs.dm similarity index 90% rename from code/game/objects/structures/stool_bed_chair_nest/chairs.dm rename to code/game/objects/structures/stool_bed_chair_nest_sofa/chairs.dm index 100cb5a06bf..c9884e99798 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/chairs.dm @@ -46,7 +46,7 @@ I.appearance_flags |= RESET_COLOR I.color = reinf_material.color add_overlay(I) - if(has_special_overlay && buckled_mob) + if(has_special_overlay) I = image(icon, "[icon_state]_special") I.layer = buckled_mob ? ABOVE_HUMAN_LAYER : FLOAT_LAYER if(material_alteration & MAT_FLAG_ALTERATION_COLOR) @@ -220,6 +220,32 @@ /obj/structure/bed/chair/office/comfy/yellow reinf_material = /decl/material/solid/cloth/yellow +/obj/structure/bed/chair/rounded + name = "rounded chair" + desc = "It's a rounded chair. It looks comfy." + icon_state = "roundedchair" + +/obj/structure/bed/chair/rounded/brown + reinf_material = /decl/material/solid/leather +/obj/structure/bed/chair/rounded/red + reinf_material = /decl/material/solid/carpet +/obj/structure/bed/chair/rounded/teal + reinf_material = /decl/material/solid/cloth/teal +/obj/structure/bed/chair/rounded/black + reinf_material = /decl/material/solid/cloth/black +/obj/structure/bed/chair/rounded/green + reinf_material = /decl/material/solid/cloth/green +/obj/structure/bed/chair/rounded/purple + reinf_material = /decl/material/solid/cloth/purple +/obj/structure/bed/chair/rounded/blue + reinf_material = /decl/material/solid/cloth/blue +/obj/structure/bed/chair/rounded/beige + reinf_material = /decl/material/solid/cloth/beige +/obj/structure/bed/chair/rounded/lime + reinf_material = /decl/material/solid/cloth/lime +/obj/structure/bed/chair/rounded/yellow + reinf_material = /decl/material/solid/cloth/yellow + /obj/structure/bed/chair/shuttle name = "shuttle seat" desc = "A comfortable, secure seat. It has a sturdy-looking buckling system for smoother flights." @@ -247,7 +273,7 @@ desc = "Old is never too old to not be in fashion." icon_state = "wooden_chair" color = WOOD_COLOR_GENERIC - var/chair_material = /decl/material/solid/wood + material = /decl/material/solid/wood /obj/structure/bed/chair/wood/attackby(obj/item/W, mob/user) if(istype(W,/obj/item/stack) || isWirecutter(W)) diff --git a/code/game/objects/structures/stool_bed_chair_nest_sofa/sofa.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/sofa.dm new file mode 100644 index 00000000000..f615b5076c8 --- /dev/null +++ b/code/game/objects/structures/stool_bed_chair_nest_sofa/sofa.dm @@ -0,0 +1,157 @@ +/obj/structure/bed/sofa + name = "sofa" + desc = "A wide and comfy sofa - no one assistant was ate by it due production! It's made of wood and covered with colored cloth." + icon_state = "sofa" + color = "#666666" + buckle_dir = FALSE + buckle_lying = FALSE //force people to sit up in chairs when buckled + obj_flags = OBJ_FLAG_ROTATABLE + material = /decl/material/solid/wood + + var/has_special_overlay = FALSE + +/obj/structure/bed/sofa/do_simple_ranged_interaction(var/mob/user) + if(!buckled_mob && user) + rotate(user) + return TRUE + +/obj/structure/bed/sofa/post_buckle_mob() + update_icon() + return ..() + +/obj/structure/bed/attackby(obj/item/W, mob/user) //made to be able to rotate the sofa + . = ..() + if(.) + return + if(!isWrench(W)) + return + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + anchored = !anchored + if(anchored) + to_chat(user, "You disanchored \the [src].") + else + to_chat(user, "You anchored \the [src].") + +/obj/structure/bed/sofa/on_update_icon() + ..() + var/image/I = image(icon, "[icon_state]_over") + I.layer = buckled_mob ? ABOVE_HUMAN_LAYER : FLOAT_LAYER + if(material_alteration & MAT_FLAG_ALTERATION_COLOR) + I.appearance_flags |= RESET_COLOR + I.color = material.color + add_overlay(I) + I = image(icon, "[icon_state]_armrest") + I.layer = buckled_mob ? ABOVE_HUMAN_LAYER : FLOAT_LAYER + if(material_alteration & MAT_FLAG_ALTERATION_COLOR) + I.appearance_flags |= RESET_COLOR + I.color = material.color + add_overlay(I) + if(reinf_material) + I = image(icon, "[icon_state]_padding_over") + I.layer = buckled_mob ? ABOVE_HUMAN_LAYER : FLOAT_LAYER + if(material_alteration & MAT_FLAG_ALTERATION_COLOR) + I.appearance_flags |= RESET_COLOR + I.color = reinf_material.color + add_overlay(I) + I = image(icon, "[icon_state]_padding_armrest") + I.layer = buckled_mob ? ABOVE_HUMAN_LAYER : FLOAT_LAYER + if(material_alteration & MAT_FLAG_ALTERATION_COLOR) + I.appearance_flags |= RESET_COLOR + I.color = reinf_material.color + add_overlay(I) + if(has_special_overlay && buckled_mob) + I = image(icon, "[icon_state]_special") + I.layer = buckled_mob ? ABOVE_HUMAN_LAYER : FLOAT_LAYER + if(material_alteration & MAT_FLAG_ALTERATION_COLOR) + I.appearance_flags |= RESET_COLOR + I.color = material.color + add_overlay(I) + +/obj/structure/bed/sofa/rotate(mob/user) + if(!CanPhysicallyInteract(user) || anchored) + to_chat(user, SPAN_NOTICE("You can't interact with \the [src] right now!")) + return + + set_dir(turn(dir, 90)) + update_icon() + +/obj/structure/bed/sofa/m/rotate(mob/user) + if(!CanPhysicallyInteract(user) || anchored) + to_chat(user, SPAN_NOTICE("You can't interact with \the [src] right now!")) + return + + set_dir(turn(dir, 45)) + update_icon() + +/obj/structure/bed/sofa/m/red + reinf_material = /decl/material/solid/carpet +/obj/structure/bed/sofa/m/brown + reinf_material = /decl/material/solid/leather +/obj/structure/bed/sofa/m/teal + reinf_material = /decl/material/solid/cloth/teal +/obj/structure/bed/sofa/m/black + reinf_material = /decl/material/solid/cloth/black +/obj/structure/bed/sofa/m/green + reinf_material = /decl/material/solid/cloth/green +/obj/structure/bed/sofa/m/purple + reinf_material = /decl/material/solid/cloth/purple +/obj/structure/bed/sofa/m/blue + reinf_material = /decl/material/solid/cloth/blue +/obj/structure/bed/sofa/m/beige + reinf_material = /decl/material/solid/cloth/beige +/obj/structure/bed/sofa/m/lime + reinf_material = /decl/material/solid/cloth/lime +/obj/structure/bed/sofa/m/yellow + reinf_material = /decl/material/solid/cloth/yellow + +/obj/structure/bed/sofa/r + name = "sofa" + desc = "A wide and comfy sofa - no one assistant was ate by it due production! It's made of wood and covered with colored cloth." + icon_state = "sofa_r" + +/obj/structure/bed/sofa/r/red + reinf_material = /decl/material/solid/carpet +/obj/structure/bed/sofa/r/brown + reinf_material = /decl/material/solid/leather +/obj/structure/bed/sofa/r/teal + reinf_material = /decl/material/solid/cloth/teal +/obj/structure/bed/sofa/r/black + reinf_material = /decl/material/solid/cloth/black +/obj/structure/bed/sofa/r/green + reinf_material = /decl/material/solid/cloth/green +/obj/structure/bed/sofa/r/purple + reinf_material = /decl/material/solid/cloth/purple +/obj/structure/bed/sofa/r/blue + reinf_material = /decl/material/solid/cloth/blue +/obj/structure/bed/sofa/r/beige + reinf_material = /decl/material/solid/cloth/beige +/obj/structure/bed/sofa/r/lime + reinf_material = /decl/material/solid/cloth/lime +/obj/structure/bed/sofa/r/yellow + reinf_material = /decl/material/solid/cloth/yellow + +/obj/structure/bed/sofa/l + name = "sofa" + desc = "A wide and comfy sofa - no one assistant was ate by it due production! It's made of wood and covered with colored cloth." + icon_state = "sofa_l" + +/obj/structure/bed/sofa/l/red + reinf_material = /decl/material/solid/carpet +/obj/structure/bed/sofa/l/brown + reinf_material = /decl/material/solid/leather +/obj/structure/bed/sofa/l/teal + reinf_material = /decl/material/solid/cloth/teal +/obj/structure/bed/sofa/l/black + reinf_material = /decl/material/solid/cloth/black +/obj/structure/bed/sofa/l/green + reinf_material = /decl/material/solid/cloth/green +/obj/structure/bed/sofa/l/purple + reinf_material = /decl/material/solid/cloth/purple +/obj/structure/bed/sofa/l/blue + reinf_material = /decl/material/solid/cloth/blue +/obj/structure/bed/sofa/l/beige + reinf_material = /decl/material/solid/cloth/beige +/obj/structure/bed/sofa/l/lime + reinf_material = /decl/material/solid/cloth/lime +/obj/structure/bed/sofa/l/yellow + reinf_material = /decl/material/solid/cloth/yellow \ No newline at end of file diff --git a/code/game/objects/structures/stool_bed_chair_nest/stools.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm similarity index 100% rename from code/game/objects/structures/stool_bed_chair_nest/stools.dm rename to code/game/objects/structures/stool_bed_chair_nest_sofa/stools.dm diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm similarity index 100% rename from code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm rename to code/game/objects/structures/stool_bed_chair_nest_sofa/wheelchair.dm diff --git a/code/game/objects/structures/tables.dm b/code/game/objects/structures/tables.dm index bc260edffef..a85955be361 100644 --- a/code/game/objects/structures/tables.dm +++ b/code/game/objects/structures/tables.dm @@ -693,6 +693,7 @@ icon_state = "solid_preview" color = WOOD_COLOR_GENERIC material = /decl/material/solid/wood + reinf_material = /decl/material/solid/wood /obj/structure/table/woodentable/mahogany color = WOOD_COLOR_RICH diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 972ddb3732b..aae8c3fe045 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -170,23 +170,6 @@ user.visible_message("[user.name] knocks on the [src.name].", "You knock on the [src.name].", "You hear a knocking sound.") - return - -/obj/structure/window/attack_generic(var/mob/user, var/damage, var/attack_verb, var/environment_smash) - if(environment_smash >= 1) - damage = max(damage, 10) - - if(istype(user)) - user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN) - user.do_attack_animation(src) - if(!damage) - return - if(damage >= 10) - visible_message(SPAN_DANGER("[user] [attack_verb] into [src]!")) - take_damage(damage) - else - visible_message(SPAN_NOTICE("\The [user] bonks \the [src] harmlessly.")) - return 1 /obj/structure/window/do_simple_ranged_interaction(var/mob/user) visible_message(SPAN_NOTICE("Something knocks on \the [src].")) @@ -306,14 +289,16 @@ if(G.damage_stage() < 2) G.affecting.visible_message(SPAN_DANGER("[G.assailant] bashes [G.affecting] against \the [src]!")) if(prob(50)) - SET_STATUS_MAX(affecting_mob, STAT_WEAK, 1) + SET_STATUS_MAX(affecting_mob, STAT_WEAK, 2) affecting_mob.apply_damage(10, BRUTE, def_zone, used_weapon = src) hit(25) + qdel(G) else G.affecting.visible_message(SPAN_DANGER("[G.assailant] crushes [G.affecting] against \the [src]!")) SET_STATUS_MAX(affecting_mob, STAT_WEAK, 5) affecting_mob.apply_damage(20, BRUTE, def_zone, used_weapon = src) hit(50) + qdel(G) return TRUE /obj/structure/window/proc/hit(var/damage, var/sound_effect = 1) @@ -333,6 +318,13 @@ set_dir(turn(dir, 90)) update_nearby_tiles(need_rebuild=1) +/obj/structure/window/set_dir(ndir) + . = ..() + if(is_fulltile()) + atom_flags &= ~ATOM_FLAG_CHECKS_BORDER + else + atom_flags |= ATOM_FLAG_CHECKS_BORDER + /obj/structure/window/update_nearby_tiles(need_rebuild) . = ..() for(var/obj/structure/S in orange(loc, 1)) @@ -421,7 +413,7 @@ basestate = reinf_basestate else basestate = initial(basestate) - + ..() if (paint_color) diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 44cf8fe566c..6813c685dd2 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -1,7 +1,7 @@ /turf/simulated name = "station" initial_gas = list( - /decl/material/gas/oxygen = MOLES_O2STANDARD, + /decl/material/gas/oxygen = MOLES_O2STANDARD, /decl/material/gas/nitrogen = MOLES_N2STANDARD ) open_turf_type = /turf/simulated/open @@ -149,7 +149,7 @@ if(istype(M)) for(var/obj/effect/decal/cleanable/blood/B in contents) - if(!LAZYACCESS(B.blood_DNA, M.dna.unique_enzymes)) + if(M.dna?.unique_enzymes && !LAZYACCESS(B.blood_DNA, M.dna.unique_enzymes)) LAZYSET(B.blood_DNA, M.dna.unique_enzymes, M.dna.b_type) LAZYSET(B.blood_data, M.dna.unique_enzymes, REAGENT_DATA(M.vessel, M.species.blood_reagent)) var/datum/extension/forensic_evidence/forensics = get_or_create_extension(B, /datum/extension/forensic_evidence) @@ -167,13 +167,8 @@ else if( istype(M, /mob/living/silicon/robot )) new /obj/effect/decal/cleanable/blood/oil(src) -/turf/simulated/proc/can_build_cable(var/mob/user) - return 0 - /turf/simulated/attackby(var/obj/item/thing, var/mob/user) - if(isCoil(thing) && can_build_cable(user)) - var/obj/item/stack/cable_coil/coil = thing - coil.turf_place(src, user) + if(isCoil(thing) && try_build_cable(thing, user)) return TRUE return ..() diff --git a/code/game/turfs/simulated/floor_attackby.dm b/code/game/turfs/simulated/floor_attackby.dm index 63ce88569a9..e9786b2a55d 100644 --- a/code/game/turfs/simulated/floor_attackby.dm +++ b/code/game/turfs/simulated/floor_attackby.dm @@ -173,11 +173,19 @@ update_icon() return 1 -/turf/simulated/floor/can_build_cable(var/mob/user) - if(!is_plating() || flooring) - to_chat(user, "Removing the tiling first.") - return 0 +/turf/simulated/floor/why_cannot_build_cable(var/mob/user, var/cable_error) + switch(cable_error) + if(0) + return + if(1) + to_chat(user, SPAN_WARNING("Removing the tiling first.")) + if(2) + to_chat(user, SPAN_WARNING("This section is too damaged to support anything. Use a welder to fix the damage.")) + else //Fallback + . = ..() + +/turf/simulated/floor/cannot_build_cable() if(broken || burnt) - to_chat(user, "This section is too damaged to support anything. Use a welder to fix the damage.") - return 0 - return 1 + return 2 + if(!is_plating()) + return 1 diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index e5b8825fc0d..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 @@ -437,3 +440,21 @@ var/global/const/enterloopsanity = 100 LAZYADD(., weather) if(flooded) LAZYADD(., global.flood_object) + +/**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() + return 1 + +/**Sends a message to the user explaining why they can't build a cable here */ +/turf/proc/why_cannot_build_cable(var/mob/user, var/cable_error) + to_chat(user, SPAN_WARNING("You cannot place a cable here!")) + +/**Place a cable if possible, if not warn the user appropriately */ +/turf/proc/try_build_cable(var/obj/item/stack/cable_coil/C, var/mob/user) + var/cable_error = cannot_build_cable(user) + if(cable_error) + why_cannot_build_cable(user, cable_error) + return FALSE + return C.turf_place(src, user) diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index c995ab4a492..b4b9c3cbbe1 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -104,7 +104,7 @@ // end of lighting stuff - // Outside/weather stuff. set_outside() updates weather already + // Outside/weather stuff. set_outside() updates weather already // so only call it again if it doesn't already handle it. if(!keep_outside || !W.set_outside(old_outside)) W.update_weather() @@ -159,10 +159,16 @@ floor_type = other.floor_type construction_stage = other.construction_stage + damage = other.damage + + // Do not set directly to other.can_open since it may be in the WALL_OPENING state. + if(other.can_open) + can_open = WALL_CAN_OPEN + update_material() return TRUE -//No idea why resetting the base appearence from New() isn't enough, but without this it doesn't work +//No idea why resetting the base appearance from New() isn't enough, but without this it doesn't work /turf/simulated/shuttle/wall/corner/transport_properties_from(turf/simulated/other) . = ..() reset_base_appearance() diff --git a/code/game/world.dm b/code/game/world.dm index 2890f54a39d..a3bea421ac9 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -1,23 +1,26 @@ -var/global/game_id = null +GLOBAL_PROTECTED_UNTYPED(game_id, null) -/hook/global_init/proc/generate_gameid() - if(game_id != null) +/hook/global_init/proc/generate_game_id() + if(!isnull(global.game_id)) return - game_id = "" - - var/list/c = list("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0") - var/l = c.len - - var/t = world.timeofday - for(var/_ = 1 to 4) - game_id = "[c[(t % l) + 1]][game_id]" - t = round(t / l) - game_id = "-[game_id]" - t = round(world.realtime / (10 * 60 * 60 * 24)) - for(var/_ = 1 to 3) - game_id = "[c[(t % l) + 1]][game_id]" - t = round(t / l) - return 1 + + global.game_id = "" + + var/list/characters = global.alphabet + global.alphabet_capital + global.numbers + var/server_time = world.timeofday + + for(var/a = 1 to 4) + global.game_id = "[characters[(server_time % characters.len) + 1]][global.game_id]" + server_time = round(server_time / characters.len) + + global.game_id = "-[global.game_id]" + server_time = round(world.realtime / (10 * 60 * 60 * 24)) + + for(var/a = 1 to 3) + global.game_id = "[characters[(server_time % characters.len) + 1]][global.game_id]" + server_time = round(server_time / characters.len) + + return TRUE // Find mobs matching a given string // diff --git a/code/modules/admin/buildmode/areas.dm b/code/modules/admin/buildmode/areas.dm index 0d86bb979ba..cd983e3ca2d 100644 --- a/code/modules/admin/buildmode/areas.dm +++ b/code/modules/admin/buildmode/areas.dm @@ -73,7 +73,7 @@ Right Click - List/Create Area var/used_colors = 0 var/list/max_colors = length(distinct_colors) var/list/vision_colors = list() - for (var/turf/T in range(get_effective_view(user.client), user)) + for (var/turf/T in range(user?.client?.view || world.view, user)) var/image/I = new('icons/turf/overlays.dmi', T, "whiteOverlay") var/ref = "\ref[T.loc]" if (!vision_colors[ref]) diff --git a/code/modules/admin/callproc/callproc.dm b/code/modules/admin/callproc/callproc.dm index 179751c6ba1..bf013e00126 100644 --- a/code/modules/admin/callproc/callproc.dm +++ b/code/modules/admin/callproc/callproc.dm @@ -120,7 +120,7 @@ return CANCEL switch(input("Type of [arguments.len+1]\th variable", "argument [arguments.len+1]") as null|anything in list( "finished", "null", "text", "num", "type", "obj reference", "mob reference", - "area/turf reference", "icon", "file", "client", "mob's area", "marked datum", "click on atom")) + "area/turf reference", "icon", "file", "client", "mob's area", "path", "marked datum", "click on atom")) if(null) return CANCEL @@ -177,6 +177,11 @@ if("Cancel") return CANCEL + if ("path") + current = text2path(input("Enter path for [arguments.len+1]\th argument") as null|text) + if (isnull(current)) + return CANCEL + if("marked datum") current = C.holder.marked_datum() if(!current) @@ -220,7 +225,7 @@ returnval = call(target, procname)() else log_admin("[key_name(src)] called [procname]() with [arguments.len ? "the arguments [list2params(arguments)]" : "no arguments"].") - returnval = call(procname)(arglist(arguments)) + returnval = call(text2path("/proc/[procname]"))(arglist(arguments)) to_chat(usr, "[procname]() returned: [json_encode(returnval)]") SSstatistics.add_field_details("admin_verb","APC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/code/modules/admin/map_capture.dm b/code/modules/admin/map_capture.dm index cdecac7c51e..903aed3dc5e 100644 --- a/code/modules/admin/map_capture.dm +++ b/code/modules/admin/map_capture.dm @@ -21,7 +21,7 @@ var/ligths = 0 if(alert("Do you want lighting to be included in capture?", "Map Capture", "No", "Yes") == "Yes") ligths = 1 - var/cap = generate_image(tx ,ty ,tz ,range, CAPTURE_MODE_PARTIAL, null, ligths, 1) + var/cap = create_area_image(tx ,ty ,tz, range, ligths) var/file_name = "map_capture_x[tx]_y[ty]_z[tz]_r[range].png" to_chat(usr, "Saved capture in cache as [file_name].") send_rsc(usr, cap, file_name) @@ -30,7 +30,7 @@ /datum/admins/proc/capture_map_capture_next(currentz, currentx, currenty, ligths) if(locate(currentx, currenty, currentz)) - var/cap = generate_image(currentx ,currenty ,currentz ,32, CAPTURE_MODE_PARTIAL, null, ligths, 1) + var/cap = create_area_image(currentx,currenty, currentz, 32, ligths) var/file_name = "map_capture_x[currentx]_y[currenty]_z[currentz]_r32.png" to_chat(usr, "Saved capture in cache as [file_name].") send_rsc(usr, cap, file_name) @@ -41,7 +41,7 @@ currenty = currenty + 32 currentx = 1 if(locate(currentx, currenty, currentz)) - var/cap = generate_image(currentx ,currenty ,currentz ,32, CAPTURE_MODE_PARTIAL, null, ligths, 1) + var/cap = create_area_image(currentx,currenty, currentz, 32, ligths) var/file_name = "map_capture_x[currentx]_y[currenty]_z[currentz]_r32.png" to_chat(usr, "Saved capture in cache as [file_name].") send_rsc(usr, cap, file_name) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 25127ad555e..9f7a65f59c2 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -311,7 +311,7 @@ if(alert("Are you sure? This will start up the engine. Should only be used during debug!",,"Yes","No") != "Yes") return - for(var/obj/machinery/power/emitter/E in SSmachines.machinery) + for(var/obj/machinery/emitter/E in SSmachines.machinery) if(E.anchored) E.active = 1 @@ -337,7 +337,7 @@ //S.dissipate_track = 0 //S.dissipate_strength = 10 - for(var/obj/machinery/power/rad_collector/Rad in SSmachines.machinery) + for(var/obj/machinery/rad_collector/Rad in SSmachines.machinery) if(Rad.anchored) if(!Rad.loaded_tank) Rad.loaded_tank = new /obj/item/tank/hydrogen(Rad) diff --git a/code/modules/assembly/assembly.dm b/code/modules/assembly/assembly.dm index 4743d3cea0f..8b99f51878f 100644 --- a/code/modules/assembly/assembly.dm +++ b/code/modules/assembly/assembly.dm @@ -23,6 +23,17 @@ var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate() var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message +/obj/item/assembly/Destroy() + if(!QDELETED(holder)) + if(holder.a_left == src) + holder.a_left = null + if(holder.a_right == src) + holder.a_right = null + QDEL_NULL(holder) + else + holder = null + return ..() + /obj/item/assembly/proc/activate() //What the device does when turned on return diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 4e30fdbec65..407e91f94f0 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -21,9 +21,20 @@ /obj/item/assembly_holder/Destroy() global.listening_objects -= src - a_left = null - a_right = null - special_assembly = null + if(!QDELETED(a_left)) + a_left.holder = null + QDEL_NULL(a_left) + else + a_left = null + if(!QDELETED(a_right)) + a_right.holder = null + QDEL_NULL(a_right) + else + a_right = null + if(!QDELETED(special_assembly)) + QDEL_NULL(special_assembly) + else + special_assembly = null return ..() /obj/item/assembly_holder/proc/attach(var/obj/item/D, var/obj/item/D2, var/mob/user) diff --git a/code/modules/atmospherics/components/binary_devices/pipeturbine.dm b/code/modules/atmospherics/components/binary_devices/pipeturbine.dm index 1639d4f4516..ba253c217e2 100644 --- a/code/modules/atmospherics/components/binary_devices/pipeturbine.dm +++ b/code/modules/atmospherics/components/binary_devices/pipeturbine.dm @@ -73,7 +73,7 @@ if (kin_energy > 1000000) overlays += image('icons/obj/pipeturbine.dmi', "hi-turb") -/obj/machinery/power/turbinemotor +/obj/machinery/turbinemotor name = "motor" desc = "Electrogenerator. Converts rotation into power." icon = 'icons/obj/pipeturbine.dmi' @@ -88,26 +88,26 @@ uncreated_component_parts = null construct_state = /decl/machine_construction/default/panel_closed -/obj/machinery/power/turbinemotor/Initialize() +/obj/machinery/turbinemotor/Initialize() . = ..() updateConnection() -/obj/machinery/power/turbinemotor/proc/updateConnection() +/obj/machinery/turbinemotor/proc/updateConnection() turbine = null if(src.loc && anchored) turbine = locate(/obj/machinery/atmospherics/pipeturbine) in get_step(src,dir) if (turbine.stat & (BROKEN) || !turbine.anchored || turn(turbine.dir,180) != dir) turbine = null -/obj/machinery/power/turbinemotor/wrench_floor_bolts(user) +/obj/machinery/turbinemotor/wrench_floor_bolts(user) . = ..() updateConnection() -/obj/machinery/power/turbinemotor/Process() +/obj/machinery/turbinemotor/Process() updateConnection() if(!turbine || !anchored || stat & (BROKEN)) return var/power_generated = kin_to_el_ratio * turbine.kin_energy turbine.kin_energy -= power_generated - add_avail(power_generated) + generate_power(power_generated) diff --git a/code/modules/atmospherics/components/unary/outlet_injector.dm b/code/modules/atmospherics/components/unary/outlet_injector.dm index 03fdab593a0..2594b3e395a 100644 --- a/code/modules/atmospherics/components/unary/outlet_injector.dm +++ b/code/modules/atmospherics/components/unary/outlet_injector.dm @@ -6,7 +6,7 @@ icon = 'icons/atmos/injector.dmi' icon_state = "off" - name = "injector" + name = "injector outlet" desc = "Passively injects air into its surroundings. Has a valve attached to it that can control flow rate." use_power = POWER_USE_OFF 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/components/unary/vent_scrubber.dm b/code/modules/atmospherics/components/unary/vent_scrubber.dm index f1e76e438e9..b5f0871da85 100644 --- a/code/modules/atmospherics/components/unary/vent_scrubber.dm +++ b/code/modules/atmospherics/components/unary/vent_scrubber.dm @@ -2,7 +2,7 @@ icon = 'icons/atmos/vent_scrubber.dmi' icon_state = "map_scrubber_off" - name = "Air Scrubber" + name = "air scrubber" desc = "Has a valve and pump attached to it." use_power = POWER_USE_OFF idle_power_usage = 150 //internal circuitry, friction losses and stuff 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/butchery/butchery.dm b/code/modules/butchery/butchery.dm index d4496496cc9..7cf20be41e4 100644 --- a/code/modules/butchery/butchery.dm +++ b/code/modules/butchery/butchery.dm @@ -111,7 +111,7 @@ return TRUE /obj/structure/kitchenspike/proc/try_spike(var/mob/living/target, var/mob/living/user) - if(!istype(target) || !Adjacent(user) || user.incapacitated()) + if(!istype(target) || !Adjacent(user) || user.incapacitated() || target.anchored) return if(!anchored) diff --git a/code/modules/client/client_color_definitions.dm b/code/modules/client/client_color_definitions.dm index 38c4788e055..86648ee16fa 100644 --- a/code/modules/client/client_color_definitions.dm +++ b/code/modules/client/client_color_definitions.dm @@ -78,8 +78,8 @@ client_color = list(0.299,0.299,0.299, 0.587,0.587,0.587, 0.114,0.114,0.114) priority = 200 -/datum/client_color/thirdeye - client_color = list(0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.05, 0.05, 0.05) +// Defining this twice so a detective dosing on Gleam doesn't lose the overlay. +/datum/client_color/noir/thirdeye priority = 300 /datum/client_color/berserk diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index f3fa098b3e1..8ddba9206b3 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -54,9 +54,9 @@ //So admins know why it isn't working - Used to determine what other accounts previously logged in from this computer id var/related_accounts_cid = "Requires database" - ///custom movement keys for this client + /// Custom movement keys for this client var/list/movement_keys = list() - ///Are we locking our movement input? + /// Are we locking our movement input? var/movement_locked = FALSE /// A buffer of currently held keys. diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index d3af3dc0e1c..ca8e3897008 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -102,7 +102,7 @@ var/global/list/localhost_addresses = list( //byond bug ID:2694120 if(href_list["reset_macros"]) - reset_macros(skip_alert = TRUE) + reset_macros(TRUE) return ..() //redirect to hsrc.Topic() @@ -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) @@ -530,22 +531,23 @@ var/global/const/MAX_VIEW = 41 if(mob) mob.reload_fullscreen() -/client/proc/toggle_fullscreen(new_value) - if((new_value == PREF_BASIC) || (new_value == PREF_FULL)) - winset(src, "mainwindow", "is-maximized=false;can-resize=false;titlebar=false") - if(new_value == PREF_FULL) - winset(src, "mainwindow", "menu=null;statusbar=false") - winset(src, "mainwindow.split", "pos=0x0") - else - winset(src, "mainwindow", "is-maximized=false;can-resize=true;titlebar=true") - winset(src, "mainwindow", "menu=menu;statusbar=true") - winset(src, "mainwindow.split", "pos=3x0") - winset(src, "mainwindow", "is-maximized=true") +/client/proc/toggle_fullscreen(value) + set waitfor = FALSE + + winset(src, null, {" + mainwindow.is-maximized = false; + mainwindow.can-resize = [(value == PREF_BASIC) || (value == PREF_FULL) ? "false" : "true"]; + mainwindow.titlebar = [(value == PREF_BASIC) || (value == PREF_FULL) ? "false" : "true"]; + mainwindow.menu = [value == PREF_FULL ? "null" : "menu"]; + mainwindow.statusbar = [value == PREF_FULL ? "false" : "true"]; + mainwindow.split.pos = [(value == PREF_BASIC) || (value == PREF_FULL) ? "0x0" : "3x0"]; + "}) + winset(src, null, "mainwindow.is-maximized = true;") /client/verb/fit_viewport() set name = "Fit Viewport" - set category = "OOC" set desc = "Fit the width of the map window to match the viewport" + set category = "OOC" set waitfor = FALSE // Fetch aspect ratio @@ -632,7 +634,7 @@ var/global/const/MAX_VIEW = 41 * * Handles adding macros for the keys that need it * And adding movement keys to the clients movement_keys list - * At the time of writing this, communication(OOC, Say, IC) require macros + * At the time of writing this, communication(OOC, LOOC, Say, Me) require macros * Arguments: * * direct_prefs - the preference we're going to get keybinds from */ @@ -645,31 +647,34 @@ var/global/const/MAX_VIEW = 41 for(var/key in D.key_bindings) for(var/kb_name in D.key_bindings[key]) switch(kb_name) - if("North") + if("north") movement_keys[key] = NORTH - if("East") + if("east") movement_keys[key] = EAST - if("West") + if("west") movement_keys[key] = WEST - if("South") + if("south") movement_keys[key] = SOUTH - if("Say") - winset(src, "default-\ref[key]", "parent=default;name=[key];command=.say") + if("admin_help") + winset(src, "default-\ref[key]", "parent=default;name=[key];command=adminhelp") communication_hotkeys += key - if("OOC") + if("ooc") winset(src, "default-\ref[key]", "parent=default;name=[key];command=ooc") communication_hotkeys += key - if("LOOC") + if("looc") winset(src, "default-\ref[key]", "parent=default;name=[key];command=looc") communication_hotkeys += key - if("Me") + if("say") + winset(src, "default-\ref[key]", "parent=default;name=[key];command=.say") + communication_hotkeys += key + if("me") winset(src, "default-\ref[key]", "parent=default;name=[key];command=.me") communication_hotkeys += key // winget() does not work for F1 and F2 for(var/key in communication_hotkeys) if(!(key in list("F1","F2")) && !winget(src, "default-\ref[key]", "command")) - to_chat(src, "You probably entered the game with a different keyboard layout.\nPlease switch to the English layout and click here to fix the communication hotkeys.") + to_chat(src, SPAN_WARNING("You probably entered the game with a different keyboard layout.\nPlease switch to the English layout and click here to fix the communication hotkeys.")) break /client/proc/get_byond_membership() diff --git a/code/modules/client/preference_setup/controls/01_keybindings.dm b/code/modules/client/preference_setup/controls/01_keybindings.dm index 5215515f68b..b8b1e73cc6b 100644 --- a/code/modules/client/preference_setup/controls/01_keybindings.dm +++ b/code/modules/client/preference_setup/controls/01_keybindings.dm @@ -1,7 +1,8 @@ /datum/preferences + /// Whether or not this client has standard hotkeys enabled + var/hotkeys = TRUE /// Custom Keybindings var/list/key_bindings = list() - var/hotkeys = TRUE /// checks through keybindings for outdated unbound keys and updates them /datum/preferences/proc/check_keybindings() @@ -46,7 +47,7 @@ for(var/item in notadded) var/datum/keybinding/conflicted = item to_chat(client, SPAN_DANGER("[conflicted.category]: [conflicted.full_name] needs updating.")) - LAZYADD(key_bindings["None"], conflicted.name) // set it to unbound to prevent this from opening up again in the future + LAZYADD(key_bindings["Unbound"], conflicted.name) // set it to unbound to prevent this from opening up again in the future /datum/category_item/player_setup_item/controls/keybindings name = "Keybindings" @@ -77,14 +78,20 @@ kb_categories[kb.category] += list(kb) . += "
" + + . += "
" + . += "Reset to default" + . += "

" + . += "
" + for (var/category in kb_categories) . += "

[category]

" . += "" for (var/i in kb_categories[category]) var/datum/keybinding/kb = i - if(!length(user_binds[kb.name]) || (user_binds[kb.name][1] == "None" && length(user_binds[kb.name]) == 1)) - . += "" + if(!length(user_binds[kb.name]) || (user_binds[kb.name][1] == "Unbound" && length(user_binds[kb.name]) == 1)) + . += "" var/list/default_keys = pref.hotkeys ? kb.hotkey_keys : kb.classic_keys var/class if(user_binds[kb.name] ~= default_keys) @@ -103,7 +110,7 @@ normal_name = _kbMap_reverse[bound_key] ? _kbMap_reverse[bound_key] : bound_key . += "" if(length(user_binds[kb.name]) < MAX_KEYS_PER_KEYBIND) - . += "" + . += "" for(var/j in 1 to MAX_KEYS_PER_KEYBIND - (length(user_binds[kb.name]) + 1)) . += "" var/list/default_keys = pref.hotkeys ? kb.hotkey_keys : kb.classic_keys @@ -112,8 +119,6 @@ . += "
[kb.full_name]None
[kb.full_name]Unbound[normal_name]NoneUnbound
" . += "
" - . += "

" - . += "Reset to default" . += "
" return jointext(., null) @@ -172,8 +177,8 @@ if(clear_key) if(pref.key_bindings[old_key]) pref.key_bindings[old_key] -= kb_name - if(!(kb_name in pref.key_bindings["None"])) - LAZYADD(pref.key_bindings["None"], kb_name) + if(!(kb_name in pref.key_bindings["Unbound"])) + LAZYADD(pref.key_bindings["Unbound"], kb_name) if(!length(pref.key_bindings[old_key])) pref.key_bindings -= old_key show_browser(user, null, "window=capturekeypress") diff --git a/code/modules/client/preference_setup/global/03_pai.dm b/code/modules/client/preference_setup/global/03_pai.dm index 86bccf57f94..d705485bdf6 100644 --- a/code/modules/client/preference_setup/global/03_pai.dm +++ b/code/modules/client/preference_setup/global/03_pai.dm @@ -4,8 +4,8 @@ var/icon/pai_preview var/datum/paiCandidate/candidate - var/icon/bgstate = DEFAULT_WALL_MATERIAL - var/list/bgstate_options = list("FFF", DEFAULT_WALL_MATERIAL, "white") + var/icon/bgstate = "steel" + var/list/bgstate_options = list("FFF", "steel", "white") /datum/category_item/player_setup_item/player_global/pai/load_preferences(datum/pref_record_reader/R) if(!candidate) @@ -81,7 +81,7 @@ bgstate = next_in_list(bgstate, bgstate_options) update_pai_preview(user) . = TOPIC_HARD_REFRESH - return + return return ..() diff --git a/code/modules/client/preference_setup/global/preferences.dm b/code/modules/client/preference_setup/global/preferences.dm index ebd6ace1a1f..a4fe9445119 100644 --- a/code/modules/client/preference_setup/global/preferences.dm +++ b/code/modules/client/preference_setup/global/preferences.dm @@ -95,9 +95,8 @@ var/global/list/_client_preferences_by_type key = "SOUND_LOBBY" /datum/client_preference/play_lobby_music/changed(var/mob/preference_mob, var/new_value) - if(new_value == PREF_YES) - if(isnewplayer(preference_mob)) - global.using_map.lobby_track.play_to(preference_mob) + if(new_value == PREF_YES && isnewplayer(preference_mob)) + global.using_map.lobby_track.play_to(preference_mob) else sound_to(preference_mob, sound(null, repeat = 0, wait = 0, volume = 85, channel = sound_channels.lobby_channel)) @@ -205,8 +204,7 @@ var/global/list/_client_preferences_by_type /datum/client_preference/fullscreen_mode description = "Fullscreen Mode" key = "FULLSCREEN" - options = list(PREF_BASIC, PREF_FULL, PREF_NO) - default_value = PREF_NO + options = list(PREF_NO, PREF_BASIC, PREF_FULL) /datum/client_preference/fullscreen_mode/changed(mob/preference_mob, new_value) if(preference_mob.client) 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/client/preferences.dm b/code/modules/client/preferences.dm index 07e9f75ee2e..10807b7389b 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -342,7 +342,7 @@ var/global/list/time_prefs_fixed = list() // Sanitizing rather than saving as someone might still be editing when copy_to occurs. player_setup.sanitize_setup() character.personal_aspects = list() - character.set_species(species) + character.change_species(species) character.set_bodytype((character.species.get_bodytype_by_name(bodytype) || character.species.default_bodytype), FALSE) if(be_random_name) @@ -376,8 +376,6 @@ var/global/list/time_prefs_fixed = list() character.h_style = h_style character.f_style = f_style - character.species.handle_limbs_setup(character) - QDEL_NULL_LIST(character.worn_underwear) character.worn_underwear = list() diff --git a/code/modules/client/ui_style.dm b/code/modules/client/ui_style.dm index eabc0ca3b5a..f20646370b6 100644 --- a/code/modules/client/ui_style.dm +++ b/code/modules/client/ui_style.dm @@ -26,40 +26,40 @@ var/global/all_tooltip_styles = list( /client/verb/change_ui() set name = "Change UI" - set category = "OOC" set desc = "Configure your user interface" + set category = "OOC" - if(!ishuman(usr)) - to_chat(usr, "You must be human to use this verb.") + if(!ishuman(mob)) + to_chat(src, SPAN_WARNING("You must be human to use this verb.")) return - var/UI_style_new = input(usr, "Select a style. White is recommended for customization") as null|anything in all_ui_styles - if(!UI_style_new) return - - var/UI_style_alpha_new = input(usr, "Select a new alpha (transparency) parameter for your UI, between 50 and 255") as null|num - if(!UI_style_alpha_new || !(UI_style_alpha_new <= 255 && UI_style_alpha_new >= 50)) + var/UI_style_new = input(src, "Select a style. White is recommended for customization", "Change UI: Style", prefs.UI_style) as null|anything in global.all_ui_styles + if(!UI_style_new) return - var/UI_style_color_new = input(usr, "Choose your UI color. Dark colors are not recommended!") as color|null + var/UI_style_color_new = input(src, "Choose your UI color. Dark colors are not recommended!", "Change UI: Color", prefs.UI_style_color) as color|null if(!UI_style_color_new) return - //update UI - var/list/icons = usr.hud_used.adding + usr.hud_used.other + usr.hud_used.hotkeybuttons - icons.Add(usr.zone_sel) - icons.Add(usr.gun_setting_icon) - icons.Add(usr.item_use_icon) - icons.Add(usr.gun_move_icon) - icons.Add(usr.radio_use_icon) + var/UI_style_alpha_new = input(src, "Select a new alpha (transparency) parameter for your UI, between 50 and 255", "Change UI: Alpha", prefs.UI_style_alpha) as null|num + if(!UI_style_alpha_new) + return + + UI_style_alpha_new = clamp(UI_style_alpha_new, 50, 255) + + var/list/icons = mob.hud_used.adding + mob.hud_used.other + mob.hud_used.hotkeybuttons - var/icon/ic = all_ui_styles[UI_style_new] + icons.Add( + mob.zone_sel, + mob.gun_setting_icon, + mob.item_use_icon, + mob.gun_move_icon, + mob.radio_use_icon + ) - for(var/obj/screen/I in icons) - if(I.name in list(I_HELP, I_HURT, I_DISARM, I_GRAB)) continue - I.icon = ic - I.color = UI_style_color_new - I.alpha = UI_style_alpha_new + var/icon/UI_style_icon_new = all_ui_styles[UI_style_new] + apply_ui_style(icons, UI_style_icon_new, UI_style_color_new, UI_style_alpha_new) if(alert("Like it? Save changes?",,"Yes", "No") == "Yes") prefs.UI_style = UI_style_new @@ -67,3 +67,12 @@ var/global/all_tooltip_styles = list( prefs.UI_style_color = UI_style_color_new SScharacter_setup.queue_preferences_save(prefs) to_chat(usr, "Your UI settings were saved.") + else + apply_ui_style(icons) + +/client/proc/apply_ui_style(list/atoms, ui_icon = all_ui_styles[prefs.UI_style], ui_color = prefs.UI_style_color, ui_alpha = prefs.UI_style_alpha) + for(var/obj/screen/S in atoms) + if(!(S.name in list(I_HELP, I_HURT, I_DISARM, I_GRAB))) + S.icon = ui_icon + S.color = ui_color + S.alpha = ui_alpha 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/clothing_accessories.dm b/code/modules/clothing/clothing_accessories.dm index 9705e8495ee..f9a9cfc8f2e 100644 --- a/code/modules/clothing/clothing_accessories.dm +++ b/code/modules/clothing/clothing_accessories.dm @@ -109,11 +109,7 @@ var/obj/item/clothing/accessory/A if(LAZYLEN(accessories) > 1) - var/list/options = list() - for(var/obj/item/clothing/accessory/i in accessories) - var/image/radial_button = image(icon = i.icon, icon_state = i.icon_state) - options[i] = radial_button - A = show_radial_menu(M, M, options, radius = 42, tooltips = TRUE) + A = show_radial_menu(M, M, make_item_radial_menu_choices(accessories), radius = 42, tooltips = TRUE) else A = accessories[1] diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 8cb0026fc93..500f10b7ad5 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -285,7 +285,7 @@ if(!failed_to_seal && wearer.back == src && piece == compare_piece) - if(seal_delay && !instant && !do_after(wearer,seal_delay,src,needhand=0)) + if(seal_delay && !instant && !do_after(wearer,seal_delay,src,check_holding=0)) failed_to_seal = 1 switch(msg_type) 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/codex/categories/_category.dm b/code/modules/codex/categories/_category.dm index 164ebdc5c99..b5e79a0b5ae 100644 --- a/code/modules/codex/categories/_category.dm +++ b/code/modules/codex/categories/_category.dm @@ -5,29 +5,25 @@ var/guide_name var/guide_html - var/list/guide_strings //Children should call ..() at the end after filling the items list -/decl/codex_category/Initialize() - . = ..() +/decl/codex_category/proc/Populate() if(items.len) - var/datum/codex_entry/entry = new(_display_name = "[name] (category)") - entry.lore_text = desc + "
" + var/lore_text = desc + "
" if(guide_name && guide_html) - entry.lore_text += "This category has an associated guide.
" + lore_text += "This category has an associated guide.
" var/list/links = list() for(var/item in items) links+= "[item]" - entry.lore_text += jointext(links, "
") - SScodex.add_entry_by_string(lowertext(entry.name), entry) + lore_text += jointext(links, "
") + new /datum/codex_entry( + _display_name = "[name] (category)", + _lore_text = lore_text + ) if(guide_html) - if(guide_name) - LAZYDISTINCTADD(guide_strings, lowertext(guide_name)) - var/datum/codex_entry/entry = new( \ - _display_name = "Guide to [capitalize(guide_name || name)]", \ - _associated_strings = guide_strings \ - ) - entry.mechanics_text = guide_html - SScodex.add_entry_by_string(lowertext(entry.name), entry) + new /datum/codex_entry( + _display_name = "Guide to [capitalize(guide_name || name)]", + _mechanics_text = guide_html + ) diff --git a/code/modules/codex/categories/category_cocktails.dm b/code/modules/codex/categories/category_cocktails.dm index 38dc29f5051..4f903aa2f4f 100644 --- a/code/modules/codex/categories/category_cocktails.dm +++ b/code/modules/codex/categories/category_cocktails.dm @@ -2,16 +2,15 @@ name = "Cocktails" desc = "Various mixes of drinks, alcoholic and otherwise, that can be made by a skilled bartender." guide_name = "Bartending" - guide_strings = list("bartender", "cocktails", "bartending") -/decl/codex_category/cocktails/Initialize() +/decl/codex_category/cocktails/Populate() var/list/entries_to_register = list() var/list/cocktails = decls_repository.get_decls_of_subtype(/decl/cocktail) guide_html = "

Mixology 101

Here's a guide for mixing decent cocktails." for(var/ctype in cocktails) var/decl/cocktail/cocktail = cocktails[ctype] - if(cocktail.hidden_from_codex) + if(cocktail.hidden_from_codex || cocktail.is_abstract()) continue var/mechanics_text = "Cocktails will change the name of bartending glasses when mixed properly.

" @@ -30,13 +29,11 @@ entries_to_register += new /datum/codex_entry( \ _display_name = "[cocktail.name] (cocktail)", \ - _associated_strings = list(lowertext(cocktail.name)), \ _lore_text = cocktail.description, \ _mechanics_text = mechanics_text, \ ) for(var/datum/codex_entry/entry in entries_to_register) - SScodex.add_entry_by_string(entry.name, entry) items |= entry.name . = ..() \ No newline at end of file diff --git a/code/modules/codex/categories/category_cultures.dm b/code/modules/codex/categories/category_cultures.dm index 0580c793dbe..17e5ea5c42b 100644 --- a/code/modules/codex/categories/category_cultures.dm +++ b/code/modules/codex/categories/category_cultures.dm @@ -2,13 +2,14 @@ name = "Factions and Culture" desc = "Prominent planets, cultures, factions and religions of known space." -/decl/codex_category/cultures/Initialize() +/decl/codex_category/cultures/Populate() var/list/all_cultural_info = decls_repository.get_decls_of_subtype(/decl/cultural_info) for(var/thing in all_cultural_info) var/decl/cultural_info/culture = all_cultural_info[thing] - if(culture.name && !culture.hidden_from_codex) - var/datum/codex_entry/entry = new(_display_name = "[culture.name] ([lowertext(culture.desc_type)])") - entry.lore_text = culture.description - SScodex.add_entry_by_string(culture.name, entry) - items |= culture.name + if(culture.name && !culture.hidden_from_codex && !culture.is_abstract()) + var/datum/codex_entry/entry = new( + _display_name = "[culture.name] ([lowertext(culture.desc_type)])", + _lore_text = culture.description + ) + items |= entry.name . = ..() \ No newline at end of file diff --git a/code/modules/codex/categories/category_fusion_reaction.dm b/code/modules/codex/categories/category_fusion_reaction.dm index 76dec5be5c1..b33370389cd 100644 --- a/code/modules/codex/categories/category_fusion_reaction.dm +++ b/code/modules/codex/categories/category_fusion_reaction.dm @@ -2,11 +2,11 @@ name = "Fusion Reactions" desc = "A list of fusion reactions that the R-UST tokamak can ignite." -/decl/codex_category/fusion_reaction/Initialize() +/decl/codex_category/fusion_reaction/Populate() var/list/reactions = decls_repository.get_decls_of_subtype(/decl/fusion_reaction) for(var/rtype in reactions) var/decl/fusion_reaction/reaction = reactions[rtype] - if(reaction.hidden_from_codex) + if(reaction.hidden_from_codex || reaction.is_abstract()) continue var/decl/material/p_mat = GET_DECL(reaction.p_react) @@ -25,7 +25,10 @@ reaction_info += "In the process of [p_mat.name]-[s_mat.name] fusion, [english_list(products_list)] [LAZYLEN(products_list) == 1 ? "is" : "are"] produced." else reaction_info += "Nothing is produced in the process of [p_mat.name]-[s_mat.name] fusion." - var/datum/codex_entry/entry = new(_display_name = lowertext(trim("[p_mat.name]-[s_mat.name] (fusion reaction)")), _mechanics_text = jointext(reaction_info, "
")) - SScodex.add_entry_by_string(entry.name, entry) + + var/datum/codex_entry/entry = new( + _display_name = lowertext(trim("[reaction.codex_name || "[p_mat.name]-[s_mat.name]"] (fusion reaction)")), + _mechanics_text = jointext(reaction_info, "
") + ) items |= entry.name . = ..() \ No newline at end of file diff --git a/code/modules/codex/categories/category_languages.dm b/code/modules/codex/categories/category_languages.dm index 90a8491fe2f..7aa78e137d2 100644 --- a/code/modules/codex/categories/category_languages.dm +++ b/code/modules/codex/categories/category_languages.dm @@ -2,7 +2,7 @@ name = "Languages" desc = "Languages spoken in known space." -/decl/codex_category/languages/Initialize() +/decl/codex_category/languages/Populate() var/example_line = "This is just some random words. What did you expect here? Hah hah!" var/language_types = decls_repository.get_decls_of_subtype(/decl/language) for(var/langname in language_types) @@ -29,9 +29,11 @@ lang_lore += "" lang_lore += "CodexBot [lang_example]" - var/datum/codex_entry/entry = new(_display_name = "[L.name] (language)", _lore_text = jointext(lang_lore, "
"), _mechanics_text = jointext(lang_info, "
")) - entry.associated_strings += L.name - entry.associated_strings += L.shorthand - SScodex.add_entry_by_string(entry.name, entry) + var/datum/codex_entry/entry = new( + _display_name = "[L.name] (language)", + _lore_text = jointext(lang_lore, "
"), + _mechanics_text = jointext(lang_info, "
") + ) items |= entry.name + . = ..() \ No newline at end of file diff --git a/code/modules/codex/categories/category_phenomena.dm b/code/modules/codex/categories/category_phenomena.dm new file mode 100644 index 00000000000..f9777ed43fa --- /dev/null +++ b/code/modules/codex/categories/category_phenomena.dm @@ -0,0 +1,19 @@ +/decl/codex_category/phenomena + name = "Phenomena" + desc = "Rumoured esoteric effects, fringe pseudoscience, and the kind of nonsense our field researchers submit at 4:55 on a Friday before heading to the pub." + +/decl/codex_category/phenomena/Populate() + + // This needs duplicate checking but I resent even having to spend time on spellcode. + var/list/spells = list() + for(var/thing in subtypesof(/spell)) + var/spell/spell = thing + if(!initial(spell.hidden_from_codex) && initial(spell.desc) && initial(spell.name)) + spells["[initial(spell.name)] (phenomena)"] = initial(spell.desc) + for(var/spell in spells) + var/datum/codex_entry/entry = new( + _display_name = spell, + _antag_text = spells[spell] + ) + items |= entry.name + . = ..() diff --git a/code/modules/codex/categories/category_reactions.dm b/code/modules/codex/categories/category_reactions.dm index 035fbb9ad8c..5747fbb59f1 100644 --- a/code/modules/codex/categories/category_reactions.dm +++ b/code/modules/codex/categories/category_reactions.dm @@ -1,10 +1,9 @@ -/decl/codex_category/reactions - name = "Reactions" +/decl/codex_category/chemistry + name = "Chemical Reactions" desc = "Chemical reactions with mundane, interesting or spectacular effects." guide_name = "Chemistry" - guide_strings = list("chemist", "reactions") -/decl/codex_category/reactions/Initialize() +/decl/codex_category/chemistry/Populate() guide_html = {"

Chemistry Basics

@@ -26,7 +25,7 @@ var/list/all_reactions = decls_repository.get_decls_of_subtype(/decl/chemical_reaction) for(var/reactiontype in all_reactions) var/decl/chemical_reaction/reaction = all_reactions[reactiontype] - if(!reaction || !reaction.name || reaction.hidden_from_codex || istype(reaction, /decl/chemical_reaction/recipe)) + if(!reaction || !reaction.name || reaction.hidden_from_codex || istype(reaction, /decl/chemical_reaction/recipe) || reaction.is_abstract()) continue // Food recipes are handled in category_recipes.dm. var/mechanics_text = "This reaction requires the following reagents:
" if(reaction.mechanics_text) @@ -34,14 +33,14 @@ var/list/reactant_values = list() for(var/reactant_id in reaction.required_reagents) var/decl/material/reactant = GET_DECL(reactant_id) - var/reactant_name = "[reactant.name]" + var/reactant_name = "[reactant.name]" reactant_values += "[reaction.required_reagents[reactant_id]]u [reactant_name]" mechanics_text += " [jointext(reactant_values, " + ")]" var/list/inhibitors = list() for(var/inhibitor_id in reaction.inhibitors) var/decl/material/inhibitor = GET_DECL(inhibitor_id) - var/inhibitor_name = "[inhibitor.name]" + var/inhibitor_name = "[inhibitor.name]" inhibitors += inhibitor_name if(length(inhibitors)) mechanics_text += " (inhibitors: [jointext(inhibitors, ", ")])" @@ -49,7 +48,7 @@ var/list/catalysts = list() for(var/catalyst_id in reaction.catalysts) var/decl/material/catalyst = GET_DECL(catalyst_id) - var/catalyst_name = "[catalyst.name]" + var/catalyst_name = "[catalyst.name]" catalysts += "[reaction.catalysts[catalyst_id]]u [catalyst_name]" if(length(catalysts)) mechanics_text += " (catalysts: [jointext(catalysts, ", ")])" @@ -57,8 +56,8 @@ var/produces if(reaction.result && reaction.result_amount) var/decl/material/product = GET_DECL(reaction.result) - produces = product.name - mechanics_text += "
It will produce [reaction.result_amount]u [produces]." + produces = product.codex_name || product.name + mechanics_text += "
It will produce [reaction.result_amount]u [product.name]." if(reaction.maximum_temperature != INFINITY) mechanics_text += "
The reaction will not occur if the temperature is above [reaction.maximum_temperature]K." if(reaction.minimum_temperature > 0) @@ -96,16 +95,14 @@ guide_html += reaction.lore_text guide_html += "" - entries_to_register += new /datum/codex_entry( \ - _display_name = "[reaction_name] (reaction)", \ - _associated_strings = list(lowertext(reaction.name), lowertext(reaction_name)), \ - _lore_text = reaction.lore_text, \ - _mechanics_text = mechanics_text \ + entries_to_register += new /datum/codex_entry( \ + _display_name = "[reaction.name] (reaction)", \ + _lore_text = reaction.lore_text, \ + _mechanics_text = mechanics_text \ ) guide_html += "" for(var/datum/codex_entry/entry in entries_to_register) - SScodex.add_entry_by_string(entry.name, entry) items |= entry.name - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/codex/categories/category_recipes.dm b/code/modules/codex/categories/category_recipes.dm index f575ffec50b..5da23f1ea54 100644 --- a/code/modules/codex/categories/category_recipes.dm +++ b/code/modules/codex/categories/category_recipes.dm @@ -2,9 +2,8 @@ name = "Recipes" desc = "Recipes for a variety of different kinds of foods and condiments." guide_name = "Cooking" - guide_strings = list("chef", "cooking", "recipes") -/decl/codex_category/recipes/Initialize() +/decl/codex_category/recipes/Populate() var/list/entries_to_register = list() @@ -27,7 +26,6 @@ var/mechanics_text var/lore_text - var/product_name var/category_name if(istype(food, /decl/chemical_reaction/recipe/food)) var/decl/chemical_reaction/recipe/food/food_ref = food @@ -35,14 +33,12 @@ if(!product) continue category_name = "mix recipe" - product_name = initial(product.name) lore_text = initial(product.desc) mechanics_text = "This recipe produces \a [initial(product.name)].
It should be performed in a mixing bowl or beaker, and requires the following ingredients:" else var/decl/material/product = food.result if(!product) continue - product_name = initial(product.name) lore_text = initial(product.lore_text) if(ispath(food.result, /decl/material/liquid/drink) || ispath(food.result, /decl/material/liquid/ethanol)) category_name = "drink recipe" @@ -69,9 +65,6 @@ entries_to_register += new /datum/codex_entry( \ _display_name = "[lowertext(food.name)] ([category_name])", \ - _associated_strings = list( \ - lowertext(food.name), \ - lowertext(product_name)), \ _lore_text = lore_text, \ _mechanics_text = mechanics_text, \ ) @@ -79,7 +72,7 @@ var/list/all_recipes = decls_repository.get_decls_of_subtype(/decl/recipe) for(var/rtype in all_recipes) var/decl/recipe/recipe = all_recipes[rtype] - if(!istype(recipe) || recipe.hidden_from_codex || !recipe.result) + if(!istype(recipe) || recipe.hidden_from_codex || !recipe.result || recipe.is_abstract()) continue var/mechanics_text = "" @@ -105,19 +98,18 @@ mechanics_text += "
This recipe takes [CEILING(recipe.time/10)] second\s to cook in [recipe.get_appliances_string()] and creates [plural ? recipe.result_quantity : "a(n)"] [initial(recipe_product.name)][plural ? "s" : ""]." var/recipe_name = recipe.display_name || sanitize(initial(recipe_product.name)) guide_html += "

[capitalize(recipe_name)]

Place [english_list(ingredients)] into [recipe.get_appliances_string()] for [CEILING(recipe.time/10)] second\s." + var/lore_text = recipe.lore_text + if(!lore_text) + lore_text = initial(recipe_product.desc) entries_to_register += new /datum/codex_entry( \ _display_name = "[recipe_name] (recipe)", \ - _associated_strings = list(lowertext(recipe_name)), \ - _lore_text = recipe.lore_text || initial(recipe_product.desc), \ + _lore_text = lore_text, \ _mechanics_text = mechanics_text, \ _antag_text = recipe.antag_text \ ) for(var/datum/codex_entry/entry in entries_to_register) - SScodex.add_entry_by_string(entry.name, entry) - for(var/str in entry.associated_strings) - SScodex.add_entry_by_string(str, entry) items |= entry.name . = ..() \ No newline at end of file diff --git a/code/modules/codex/categories/category_skills.dm b/code/modules/codex/categories/category_skills.dm index 50b1819e7d6..2d52a0820f4 100644 --- a/code/modules/codex/categories/category_skills.dm +++ b/code/modules/codex/categories/category_skills.dm @@ -1,8 +1,8 @@ -/decl/codex_category/skills/ +/decl/codex_category/skills name = "Skills" desc = "Certifiable skills." -/decl/codex_category/skills/Initialize() +/decl/codex_category/skills/Populate() for(var/decl/hierarchy/skill/skill in global.skills) var/list/skill_info = list() if(skill.prerequisites) @@ -13,8 +13,10 @@ skill_info += "Prerequisites: [english_list(reqs)]" for(var/level in skill.levels) skill_info += "

[level]

[skill.levels[level]]" - var/datum/codex_entry/entry = new(_display_name = lowertext(trim("[skill.name] (skill)")), _lore_text = skill.desc, _mechanics_text = jointext(skill_info, "
")) - SScodex.add_entry_by_string(entry.name, entry) - SScodex.add_entry_by_string(skill.name, entry) - items |= skill.name + var/datum/codex_entry/entry = new( + _display_name = lowertext(trim("[skill.name] (skill)")), + _lore_text = skill.desc, + _mechanics_text = jointext(skill_info, "
") + ) + items |= entry.name . = ..() \ No newline at end of file diff --git a/code/modules/codex/categories/category_species.dm b/code/modules/codex/categories/category_species.dm index 86e332ea267..c3fc2fa8270 100644 --- a/code/modules/codex/categories/category_species.dm +++ b/code/modules/codex/categories/category_species.dm @@ -1,15 +1,15 @@ -/decl/codex_category/species/ +/decl/codex_category/species name = "Species" desc = "Sapient species encountered in known space." -/decl/codex_category/species/Initialize() +/decl/codex_category/species/Populate() for(var/thing in get_all_species()) var/decl/species/species = get_species_by_key(thing) - if(!species.hidden_from_codex) - var/datum/codex_entry/entry = new(_display_name = "[species.name] (species)") - entry.lore_text = species.codex_description - entry.mechanics_text = species.ooc_codex_information - SScodex.add_entry_by_string(entry.name, entry) - SScodex.add_entry_by_string(species.name, entry) + if(!species.hidden_from_codex && !species.is_abstract()) + var/datum/codex_entry/entry = new( + _display_name = "[species.name] (species)", + _lore_text = species.codex_description, + _mechanics_text = species.ooc_codex_information + ) items |= entry.name . = ..() diff --git a/code/modules/codex/categories/category_substances.dm b/code/modules/codex/categories/category_substances.dm index 2f9c9691162..42d56c6287f 100644 --- a/code/modules/codex/categories/category_substances.dm +++ b/code/modules/codex/categories/category_substances.dm @@ -2,18 +2,14 @@ name = "Substances" desc = "Various natural and artificial substances." -/decl/codex_category/substances/Initialize() +/decl/codex_category/substances/Populate() for(var/thing in SSmaterials.materials) var/decl/material/mat = thing - if(!mat.hidden_from_codex) - var/datum/codex_entry/entry = new(_display_name = "[mat.name] (substance)", _associated_strings = list("[mat.name] pill")) + if(!mat.hidden_from_codex && !mat.is_abstract()) var/new_lore_text = initial(mat.lore_text) if(mat.taste_description) new_lore_text = "[new_lore_text]
It apparently tastes of [mat.taste_description]." - entry.lore_text = new_lore_text - entry.antag_text = mat.antag_text var/list/material_info = list(mat.mechanics_text) - var/list/production_strings = list() for(var/react in SSmaterials.chemical_reactions_by_result[thing]) var/decl/chemical_reaction/reaction = react @@ -157,7 +153,11 @@ material_info += "
  • It can be used to pad furniture.
  • " material_info += "" - entry.mechanics_text = jointext(material_info, null) - SScodex.add_entry_by_string(entry.name, entry) + var/datum/codex_entry/entry = new( + _display_name = "[mat.codex_name || mat.name] (substance)", + _lore_text = new_lore_text, + _antag_text = mat.antag_text, + _mechanics_text = jointext(material_info, null) + ) items |= entry.name . = ..() \ No newline at end of file diff --git a/code/modules/codex/categories/category_surgery.dm b/code/modules/codex/categories/category_surgery.dm index 8b9a157afec..96bf93becc3 100644 --- a/code/modules/codex/categories/category_surgery.dm +++ b/code/modules/codex/categories/category_surgery.dm @@ -3,7 +3,6 @@ desc = "A list of surgeries, their requirements and their effects." guide_name = "Surgery Basics" - guide_strings = list("surgery") guide_html = {"

    Surgery Basics

    This guide contains some quick and dirty basic outlines of common surgical procedures. @@ -67,11 +66,11 @@ "} -/decl/codex_category/surgery/Initialize() +/decl/codex_category/surgery/Populate() var/list/procedures = decls_repository.get_decls_of_subtype(/decl/surgery_step) for(var/stype in procedures) var/decl/surgery_step/procedure = procedures[stype] - if(procedure.hidden_from_codex || !procedure.name) + if(procedure.hidden_from_codex || !procedure.name || procedure.is_abstract()) continue var/list/surgery_info = list() @@ -112,7 +111,10 @@ if(procedure.additional_codex_lines) surgery_info += procedure.additional_codex_lines - var/datum/codex_entry/entry = new(_display_name = lowertext(trim("[lowertext(procedure.name)] (surgery)")), _lore_text = procedure.description, _mechanics_text = jointext(surgery_info, "
    ")) - SScodex.add_entry_by_string(entry.name, entry) + var/datum/codex_entry/entry = new( + _display_name = lowertext(trim("[lowertext(procedure.name)] (surgery)")), + _lore_text = procedure.description, + _mechanics_text = jointext(surgery_info, "
    ") + ) items |= entry.name . = ..() diff --git a/code/modules/codex/entries/_codex_entry.dm b/code/modules/codex/entries/_codex_entry.dm index 64528b1cc7f..3924fb21982 100644 --- a/code/modules/codex/entries/_codex_entry.dm +++ b/code/modules/codex/entries/_codex_entry.dm @@ -5,12 +5,15 @@ var/lore_text var/mechanics_text var/antag_text + var/disambiguator /datum/codex_entry/dd_SortValue() return name /datum/codex_entry/New(var/_display_name, var/list/_associated_paths, var/list/_associated_strings, var/_lore_text, var/_mechanics_text, var/_antag_text) + SScodex.all_entries += src + if(_display_name) name = _display_name if(_associated_paths) associated_paths = _associated_paths if(_associated_strings) associated_strings = _associated_strings @@ -18,16 +21,43 @@ if(_mechanics_text) mechanics_text = _mechanics_text if(_antag_text) antag_text = _antag_text - if(associated_paths && associated_paths.len) + if(length(associated_paths)) for(var/tpath in associated_paths) var/atom/thing = tpath - LAZYADD(associated_strings, sanitize(lowertext(initial(thing.name)))) + var/thing_name = sanitize(initial(thing.name)) + if(disambiguator) + thing_name = "[thing_name] ([disambiguator])" + LAZYDISTINCTADD(associated_strings, thing_name) + for(var/associated_path in associated_paths) + if(SScodex.entries_by_path[associated_path]) + PRINT_STACK_TRACE("Trying to save codex entry for [name] by path [associated_path] but one already exists!") + SScodex.entries_by_path[associated_path] = src + if(name) - LAZYADD(associated_strings, name) - else if(associated_strings && associated_strings.len) + var/clean_name = lowertext(sanitize(name)) + LAZYDISTINCTADD(associated_strings, clean_name) + else if(length(associated_strings)) name = associated_strings[1] + + if(length(associated_strings)) + + var/list/cleaned_strings = list() + for(var/associated_string in associated_strings) + cleaned_strings |= lowertext(trim(associated_string)) + associated_strings = cleaned_strings + + if(length(associated_strings)) + for(var/key_string in associated_strings) + if(SScodex.entries_by_string[key_string]) + PRINT_STACK_TRACE("Trying to save codex entry for [name] by string [key_string] but one already exists!") + SScodex.entries_by_string[key_string] = src + ..() +/datum/codex_entry/Destroy(force) + SScodex.all_entries -= src + . = ..() + /datum/codex_entry/proc/get_header(var/mob/presenting_to) var/list/dat = list() var/datum/codex_entry/linked_entry = SScodex.get_entry_by_string("nexus") diff --git a/code/modules/codex/entries/atmospherics.dm b/code/modules/codex/entries/atmospherics.dm index 9958d225983..7b1db22cc75 100644 --- a/code/modules/codex/entries/atmospherics.dm +++ b/code/modules/codex/entries/atmospherics.dm @@ -1,123 +1,48 @@ /datum/codex_entry/atmos_pipe associated_paths = list(/obj/machinery/atmospherics/pipe) - mechanics_text = "This pipe, and all other pipes, can be connected or disconnected by a wrench. The internal pressure of the pipe must \ - be below 300 kPa to do this. More pipes can be obtained from the pipe dispenser." - -//HE pipes -/datum/codex_entry/atmos_he - associated_paths = list(/obj/machinery/atmospherics/pipe/simple/heat_exchanging) - mechanics_text = "This radiates heat from the pipe's gas to space, cooling it down." - -//Supply/Scrubber pipes -/datum/codex_entry/atmos_visible_scrub - associated_paths = list(/obj/machinery/atmospherics/pipe/simple/visible/scrubbers) - mechanics_text = "This is a special 'scrubber' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -/datum/codex_entry/atmos_visible_supply - associated_paths = list(/obj/machinery/atmospherics/pipe/simple/visible/supply) - mechanics_text = "This is a special 'supply' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -/datum/codex_entry/atmos_hidden_supply - associated_paths = list(/obj/machinery/atmospherics/pipe/simple/hidden/supply) - mechanics_text = "This is a special 'supply' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -/datum/codex_entry/atmos_hidden_scrub - associated_paths = list(/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers) - mechanics_text = "This is a special 'scrubber' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -//Universal adapters -/datum/codex_entry/atmos_visible_universal - associated_paths = list(/obj/machinery/atmospherics/pipe/simple/visible/universal) - mechanics_text = "This allows you to connect 'normal' pipes, red 'scrubber' pipes, and blue 'supply' pipes." - -/datum/codex_entry/atmos_hidden_universal - associated_paths = list(/obj/machinery/atmospherics/pipe/simple/hidden/universal) - mechanics_text = "This allows you to connect 'normal' pipes, red 'scrubber' pipes, and blue 'supply' pipes." - -//Three way manifolds -/datum/codex_entry/atmos_manifold - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold) - mechanics_text = "A normal pipe with three ends to connect to." - -/datum/codex_entry/atmos_manifold_visible_scrub - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold/visible/scrubbers) - mechanics_text = "This is a special 'scrubber' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -/datum/codex_entry/atmos_manifold_visible_supply - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold/visible/supply) - mechanics_text = "This is a special 'supply' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -/datum/codex_entry/atmos_manifold_hidden_scrub - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold/hidden/scrubbers) - mechanics_text = "This is a special 'scrubber' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -/datum/codex_entry/atmos_manifold_hidden_supply - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold/hidden/supply) - mechanics_text = "This is a special 'supply' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -//Four way manifolds -/datum/codex_entry/atmos_manifold_manifold_four - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold4w) - mechanics_text = "This is a four-way pipe." - -/datum/codex_entry/atmos_manifold_manifold_four_visible_scrub - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold4w/visible/scrubbers) - mechanics_text = "This is a special 'scrubber' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -/datum/codex_entry/atmos_manifold_manifold_four_visible_supply - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold4w/hidden/supply) - mechanics_text = "This is a special 'supply' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -/datum/codex_entry/atmos_manifold_manifold_four_hidden_scrub - associated_paths = list(/obj/machinery/atmospherics/pipe/manifold4w/hidden/scrubbers) - mechanics_text = "This is a special 'scrubber' pipe, which does not connect to 'normal' pipes. If you want to connect it, use \ - a Universal Adapter pipe." - -//Endcaps -/datum/codex_entry/atmos_cap - associated_paths = list(/obj/machinery/atmospherics/pipe/cap) - mechanics_text = "This is a cosmetic attachment, as pipes currently do not spill their contents into the air." + mechanics_text = "All pipes can be connected or disconnected with a wrench. The internal pressure of the pipe must be below 300 kPa to do this. More pipes can be obtained from a pipe dispenser. \ +
    Some pipes, like scrubbers and supply pipes, do not connect to 'normal' pipes. If you want to connect them, use a universal adapter pipe. \ +
    Pipes generally do not exchange thermal energy with the environment (ie. they do not heat up or cool down their turf), but heat exchange pipes are an exception. \ +
    To join three or more pipe segments, you can use a pipe manifold.\ +
    To terminate a pipeline, use a cap to prevent the gas escaping into the environment." + disambiguator = "atmospherics" //T-shaped valves /datum/codex_entry/atmos_tvalve associated_paths = list(/obj/machinery/atmospherics/tvalve) mechanics_text = "Click this to toggle the mode. The direction with the green light is where the gas will flow." + disambiguator = "atmospherics" //Normal valves /datum/codex_entry/atmos_valve associated_paths = list(/obj/machinery/atmospherics/valve) mechanics_text = "Click this to turn the valve. If red, the pipes on each end are seperated. Otherwise, they are connected." + disambiguator = "atmospherics" //TEG ports /datum/codex_entry/atmos_circulator associated_paths = list(/obj/machinery/atmospherics/binary/circulator) mechanics_text = "This generates electricity, depending on the difference in temperature between each side of the machine. The meter in \ the center of the machine gives an indicator of how much elecrtricity is being generated." + disambiguator = "atmospherics" //Passive gates /datum/codex_entry/atmos_gate associated_paths = list(/obj/machinery/atmospherics/binary/passive_gate) mechanics_text = "This is a one-way regulator, allowing gas to flow only at a specific pressure and flow rate. If the light is green, it is flowing." + disambiguator = "atmospherics" //Normal pumps (high power one inherits from this) /datum/codex_entry/atmos_pump associated_paths = list(/obj/machinery/atmospherics/binary/pump) mechanics_text = "This moves gas from one pipe to another. A higher target pressure demands more energy. The side with the red end is the output." + disambiguator = "atmospherics" //Vents /datum/codex_entry/atmos_vent_pump associated_paths = list(/obj/machinery/atmospherics/unary/vent_pump) mechanics_text = "This pumps the contents of the attached pipe out into the atmosphere, if needed. It can be controlled from an Air Alarm." + disambiguator = "atmospherics" //Freezers /datum/codex_entry/atmos_freezer @@ -125,6 +50,7 @@ mechanics_text = "Cools down the gas of the pipe it is connected to. It uses massive amounts of electricity while on. \ It can be upgraded by replacing the capacitors, manipulators, and matter bins. It can be deconstructed by screwing the maintenance panel open with a \ screwdriver, and then using a crowbar." + disambiguator = "atmospherics" //Heaters /datum/codex_entry/atmos_heater @@ -132,29 +58,34 @@ mechanics_text = "Heats up the gas of the pipe it is connected to. It uses massive amounts of electricity while on. \ It can be upgraded by replacing the capacitors, manipulators, and matter bins. It can be deconstructed by screwing the maintenance panel open with a \ screwdriver, and then using a crowbar." + disambiguator = "atmospherics" //Gas injectors /datum/codex_entry/atmos_injector associated_paths = list(/obj/machinery/atmospherics/unary/outlet_injector) mechanics_text = "Outputs the pipe's gas into the atmosphere, similar to an airvent. It can be controlled by a nearby atmospherics computer. \ A green light on it means it is on." + disambiguator = "atmospherics" //Scrubbers /datum/codex_entry/atmos_vent_scrubber associated_paths = list(/obj/machinery/atmospherics/unary/vent_scrubber) mechanics_text = "This filters the atmosphere of harmful gas. Filtered gas goes to the pipes connected to it, typically a scrubber pipe. \ It can be controlled from an Air Alarm. It can be configured to drain all air rapidly with a 'panic syphon' from an air alarm." + disambiguator = "atmospherics" //Omni filters /datum/codex_entry/atmos_omni_filter associated_paths = list(/obj/machinery/atmospherics/omni/filter) mechanics_text = "Filters gas from a custom input direction, with up to two filtered outputs and a 'everything else' \ output. The filtered output's arrows glow orange." + disambiguator = "atmospherics" //Omni mixers /datum/codex_entry/atmos_omni_mixer associated_paths = list(/obj/machinery/atmospherics/omni/mixer) mechanics_text = "Combines gas from custom input and output directions. The percentage of combined gas can be defined." + disambiguator = "atmospherics" //Canisters /datum/codex_entry/atmos_canister @@ -164,6 +95,7 @@ can be filled by the canister, by using the tank on the canister, increasing the release pressure, then opening the valve until it is full, and then close it. \ *DO NOT* remove the tank until the valve is closed. A gas analyzer can be used to check the contents of the canister." antag_text = "Canisters can be damaged, spilling their contents into the air, or you can just leave the release valve open." + disambiguator = "atmospherics" //Portable pumps /datum/codex_entry/atmos_power_pump @@ -171,6 +103,7 @@ mechanics_text = "Invaluable for filling air in a room rapidly after a breach repair. The internal gas container can be filled by \ connecting it to a connector port. The pump can pump the air in (sucking) or out (blowing), at a specific target pressure. The powercell inside can be \ replaced by using a screwdriver, and then adding a new cell. A tank of gas can also be attached to the air pump." + disambiguator = "machine" //Portable scrubbers /datum/codex_entry/atmos_power_scrubber @@ -178,11 +111,13 @@ mechanics_text = "Filters the air, placing harmful gases into the internal gas container. The container can be emptied by \ connecting it to a connector port. The pump can pump the air in (sucking) or out (blowing), at a specific target pressure. The powercell inside can be \ replaced by using a screwdriver, and then adding a new cell. A tank of gas can also be attached to the scrubber. " + disambiguator = "machine" //Meters /datum/codex_entry/atmos_meter associated_paths = list(/obj/machinery/meter) mechanics_text = "Measures the volume and temperature of the pipe under the meter." + disambiguator = "atmospherics" //Pipe dispensers /datum/codex_entry/atmos_pipe_dispenser @@ -194,6 +129,7 @@ associated_paths = list(/obj/item/transfer_valve) mechanics_text = "This machine is used to merge the contents of two different gas tanks. Plug the tanks into the transfer, then open the valve to mix them together. You can also attach various assembly devices to trigger this process." antag_text = "With a tank of hot hydrogen and cold oxygen, this benign little atmospheric device becomes an incredibly deadly bomb. You don't want to be anywhere near it when it goes off." + disambiguator = "component" /datum/codex_entry/gas_tank associated_paths = list(/obj/item/tank) @@ -208,9 +144,11 @@ antag_text = "Each tank may be incited to burn by attaching wires and an igniter assembly, though the igniter can only be used once and the mixture only burn if the igniter pushes a flammable gas mixture above the minimum burn temperature (126?C). \ Wired and assembled tanks may be disarmed with a set of wirecutters. Any exploding or rupturing tank will generate shrapnel, assuming their relief valves have been welded beforehand. Even if not, they can be incited to expel hot gas on ignition if pushed above 173?C. \ Relatively easy to make, the single tank bomb requries no tank transfer valve, and is still a fairly formidable weapon that can be manufactured from any tank." + disambiguator = "atmospherics" /datum/codex_entry/gas_analyzer associated_paths = list(/obj/item/scanner/gas) mechanics_text = "A device that analyzes the gas contents of a tile or atmospherics devices. Has 3 modes: Default operates without \ additional output data; Moles and volume shows the moles per gas in the mixture and the total moles and volume; Gas \ - traits and data describes the traits per gas, how it interacts with the world, and some of its property constants." \ No newline at end of file + traits and data describes the traits per gas, how it interacts with the world, and some of its property constants." + disambiguator = "equipment" diff --git a/code/modules/codex/entries/codex.dm b/code/modules/codex/entries/codex.dm index 9a548c6b2dd..1aeb2e928f3 100644 --- a/code/modules/codex/entries/codex.dm +++ b/code/modules/codex/entries/codex.dm @@ -1,6 +1,5 @@ /datum/codex_entry/codex name = "The Codex" - associated_strings = list("codex") lore_text = "The Codex is the overall body containing the document that you are currently reading. It is a distributed encyclopedia maintained by a dedicated, hard-working staff of journalists, bartenders, hobos, space pirates and xeno-intelligences, all with the goal of providing you, the user, with supposedly up-to-date, nominally useful documentation on the topics you want to know about. \

    \ Access to the Codex is afforded instantly by a variety of unobtrusive devices, including PDA attachments, retinal implants, neuro-memetic indoctrination and hover-drone. All our access devices are affordable, stylish and guaranteed not to expose you to encephalo-malware. \ diff --git a/code/modules/codex/entries/engineering.dm b/code/modules/codex/entries/engineering.dm index b20bfb018d0..4a2a49bb6ef 100644 --- a/code/modules/codex/entries/engineering.dm +++ b/code/modules/codex/entries/engineering.dm @@ -25,31 +25,25 @@ Wires can be pulsed remotely with a signaler attached to it. A powersink will also drain any APCs connected to the same wire the powersink is on." /datum/codex_entry/inflatable_item - associated_paths = list(/obj/item/inflatable) - mechanics_text = "Inflate by using it in your hand. The inflatable barrier will inflate on your tile. To deflate it, use the 'deflate' verb." - -/datum/codex_entry/inflatable_deployed - associated_paths = list(/obj/structure/inflatable) - mechanics_text = "To remove these safely, use the 'deflate' verb. Hitting these with any objects will probably puncture and break it forever." - -/datum/codex_entry/inflatable_door - associated_paths = list(/obj/structure/inflatable/door) - mechanics_text = "Click the door to open or close it. It only stops air while closed.
    \ - To remove these safely, use the 'deflate' verb. Hitting these with any objects will probably puncture and break it forever." + associated_paths = list(/obj/item/inflatable, /obj/structure/inflatable, /obj/structure/inflatable/door) + mechanics_text = "Inflate by using it in your hand. The inflatable barrier will inflate on your tile. To deflate it, use the 'deflate' verb. Hitting this with any object will probably puncture and break it forever.
    Walls are static, but doors may be clicked to open or close them. They only stop air while closed." /datum/codex_entry/welding_pack associated_paths = list(/obj/item/weldpack) mechanics_text = "This pack acts as a portable source of welding fuel. Use a welder on it to refill its tank - but make sure it's not lit! You can use this kit on a fuel tank or appropriate reagent dispenser to replenish its reserves." lore_text = "The Shenzhen Chain of 2133 was an industrial accident of noteworthy infamy that occurred at Earth's L3 Lagrange Point. An apprentice welder, working for the Shenzhen Space Fabrication Group, failed to properly seal her fuel port, triggering a chain reaction that spread from laborer to laborer, instantly vaporizing a crew of fourteen. Don't let this happen to you!" antag_text = "In theory, you could hold an open flame to this pack and produce some pretty catastrophic results. The trick is getting out of the blast radius." + disambiguator = "equipment" /datum/codex_entry/gripper associated_paths = list(/obj/item/gripper) mechanics_text = "Click an item to pick it up with your gripper. Use it as you would normally use anything in your hand. The Drop Item verb will allow you to release the item." + disambiguator = "equipment" /datum/codex_entry/diffuser_item associated_paths = list(/obj/item/shield_diffuser) mechanics_text = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern), in a similar way the floor mounted variant does. It is, however, portable and run by an internal battery. Can be recharged with a regular recharger." + disambiguator = "equipment" /datum/codex_entry/hacking associated_strings = list("hacking") diff --git a/code/modules/codex/entries/machinery.dm b/code/modules/codex/entries/machinery.dm index decfc152914..bfb4c64486b 100644 --- a/code/modules/codex/entries/machinery.dm +++ b/code/modules/codex/entries/machinery.dm @@ -1,12 +1,11 @@ /datum/codex_entry/airlock associated_paths = list(/obj/machinery/door/airlock) - associated_strings = list("airlock", "door", "hatch") mechanics_text = "An airtight mechanical door. Most airlocks require an ID to open (wearing it on your ID slot is enough), and many have special access requirements to be on your ID. You can open an airlock by clicking on one, or simply walking into it, and clicking on an open airlock will close it. If the lights on the door flash red, you don't have the required access to open the airlock, and if they're consistently red, the door is bolted. Airlocks also require power to operate. In a situation without power, a crowbar can be used to force it open.

    Airlocks can also be hacked.
    Airlock hacking information:
    * Door bolts will lock the door, preventing it from being opened by any means until the bolts are raised again. Pulsing the correct wire will toggle the bolts, but cutting it will only drop them.
    * IDscan light indicates the door can scan ID cards. If the wire for this is pulsed it will cause the door to flash red, and cutting it will cause doors with restricted access to be unable to scan ID, essentially locking it.
    * The AI control light shows whether or not AI and robots can control the door. Pulsing is only temporary, while cutting is more permanent.
    * The test light shows whether the door has power or not. When turned off, the door can be opened with a crowbar.
    * If the door sparks, it is electrified. Pulsing the corresponding wire causes this to happen for 30 seconds, and cutting it electrifies the door until mended.
    * The check wiring light will turn on if the safety is turned off. This causes the airlock to close even when someone is standing on it, crushing them.
    * If the Check Timing Mechanism light is on then the door will close much faster than normal. Dangerous in conjuction with the Check Wiring light.

    Deconstruction
    To pull apart an airlock, you must open the maintenance panel, disable the power via hacking (or any other means), weld the door shut, and crowbar the electroncs out, cut the wires with a wirecutter, unsecure the whole assembly with a wrench, and then cut it into metal sheets with a welding tool." antag_text = "Electrifying a door makes for a good trap, hurting and stunning whoever touches it. The same goes for a combination of disabling the safety and timing mechanisms. Bolting a door can also help ensure privacy, or slow down those in search of you. It's also a good idea to disable AI interaction when needed." + disambiguator = "machine" /datum/codex_entry/computer associated_paths = list(/obj/machinery/computer, /obj/machinery/constructable_frame/computerframe) - associated_strings = list("computer","console") mechanics_text = "(This entry refers to the older single-purpose computers, not modular computers.)

    \ Computers are used primarily for controlling other machines and systems, or for providing information about said systems. They require power to function and run off the Equipment power channel on an APC. and sometimes requries ID access. \

    Constructing a computer:
    \ @@ -24,10 +23,10 @@ 3.) Use a wirecutter to remove the cables.
    \ 4.) Use a wrench on to take the frame apart.
    \ 5.) Use a welder to cut the frame into sheets." + disambiguator = "machine" /datum/codex_entry/computer/modular associated_paths = list(/obj/machinery/computer/modular) - associated_strings = list("modular computer") lore_text = "Modular computers allow for customized hardware and downloadable software, enabling users to create their own workplace experience." mechanics_text = "(This entry is for Modular Computers. Modular Computers are not the same as regular computers.)

    \ Modular computers can have a variety of functions depending on the installed hardware and software. They also come available as tablets, laptops, wallscreens and PDA.
    \ @@ -37,73 +36,85 @@ * If the computer has a RFID card slot, ID cards can be inserted by hand, and removed using right click(or the verb Remove-ID)
    \ * PDA usually come with a pen, which can be remove with the right click menu(or Remove-Pen verb). The pen can be inserted again by using it on the PDA." antag_text = "You can use an emag on the computer to reveal a secret list of downloadable software." + disambiguator = "machine" /datum/codex_entry/conveyor_construct associated_paths = list(/obj/machinery/conveyor, /obj/item/conveyor_construct) mechanics_text = "This device must be connected to a switch assembly before placement by clicking the switch on the conveyor belt assembly. When active it will move objects on top of it to the adjacent space based on its direction and if it is runnnig in forward or reverse mode. Can be removed with a crowbar." + disambiguator = "machine" -/datum/codex_entry/conveyor_construct +/datum/codex_entry/conveyor_switch associated_paths = list(/obj/machinery/conveyor_switch,/obj/machinery/conveyor_switch/oneway,/obj/item/conveyor_switch_construct,/obj/item/conveyor_switch_construct/oneway) mechanics_text = "This device can connect to a number of conveyor belts and control their movement. A two-way switch will allow you to make the conveyors run in forward and reverse mode, the one-way switch will only allow one direction. Can be removed with a crowbar." + disambiguator = "machine" /datum/codex_entry/cryopod associated_paths = list(/obj/machinery/cryopod) mechanics_text = "To enter a cryopod, or put someone in a cryopod, click+drag. When someone is in a cryopod, and they use the ghost verb, their character will despawn, be removed from the game round along with all their belongings, and freeing their job slot for someone else to take. This also occurs if a character is inside a cryopod for 15 minutes. Any particularly important belongings that the game can't do without will be stored. The item can then be retrieved from the nearby oversight console." + disambiguator = "machine" /datum/codex_entry/diffuser_machine associated_paths = list(/obj/machinery/shield_diffuser) mechanics_text = "This device disrupts shields on directly adjacent tiles (in a + shaped pattern). They are commonly installed around exterior airlocks to prevent shields from blocking EVA access." + disambiguator = "machine" /datum/codex_entry/disposal associated_paths = list(/obj/machinery/disposal) mechanics_text = "A high-tech garbage bin. Inserting an item causes the disposal unit (after a delay) to pneumatically launch the item through a series of pipes leading to either a garbage processing room, or space, depending on the ship/station. Larger objects can be inserted via click+drag.
    You can remove a disposal unit by turning it off, using a screwdriver, then a welding tool, and removing the floor tile underneath it." antag_text = "People can be inserted into the disposal unit. If they're capable of moving however, it's easy for them to get out. Be careful though, putting things in disposal units doesn't always mean they're gone forever.
    If you turn it off, it can be used to hide in. Just be careful no one turns it back on while you're still in there!" + disambiguator = "machine" /datum/codex_entry/emitter - associated_paths = list(/obj/machinery/power/emitter) - mechanics_text = "You must secure this in place with a wrench and weld it to the floor before using it. The emitter will only fire if it is installed above a cable endpoint. Clicking will toggle it on and off, at which point, so long as it remains powered, it will fire in a single direction in bursts of four." - lore_text = "Lasers like this one have been in use for ages, in applications such as mining, cutting, and in the startup sequence of many advanced space station and starship engines." + associated_paths = list(/obj/machinery/emitter) + mechanics_text = "You must secure this in place with a wrench and weld it to the floor before using it. Using cables on the emitter will allow you to connect it to the powernet with a terminal, placed over a cable node. Clicking will toggle it on and off, at which point, so long as it remains powered, it will fire in a single direction in bursts of four." + lore_text = "Lasers like this one have been in use for ages, in applications such as mining, cutting, and in the startup sequence of many advanced space station and starshipn engines." antag_text = "This baby is capable of slicing through walls, sealed lockers, and people." + disambiguator = "machine" /datum/codex_entry/fusion_fuel_injector associated_paths = list(/obj/machinery/fusion_fuel_injector, /obj/machinery/computer/fusion/fuel_control) - associated_strings = list("fuel injector","fuel injection control computer") lore_text = "A simple magnetic accelerator used to inject small pellets of fuel into a fusion field." mechanics_text = "Accepts a fuel rod produced by the fuel compressor and regularly fires fuel pellets into the fusion field. Rods can be swapped by hand when the injector is not firing. Power outages will require the injector to be turned on again. If there is no electromagnetic field active to catch the injected fuel, the results can be very unhealthy for anyone standing in the firing path.
    Controlled via the fuel injection control computer." + disambiguator = "machine" /datum/codex_entry/fusion_core - associated_paths = list(/obj/machinery/power/fusion_core, /obj/machinery/computer/fusion/core_control) - associated_strings = list("R-UST Mk. 8 Tokamak core","\improper R-UST Mk. 8 core control","rust","fusion","tokamak") + associated_paths = list(/obj/machinery/fusion_core, /obj/machinery/computer/fusion/core_control) lore_text = "An old but more or less reliable fusion field generator. Probably sourced from a retired cargo freighter." mechanics_text = "Generates the field used to contain reaction material from fuel injectors, and dumps power into the power network under it based on plasma heat. Needs 500W or more in the network to start the field. Field will become unstable if it intersects with windows or other objects, and from some reactions, and will eventually rupture violently if not stabilized by a gyrotron. Turning the field off without letting it cool below 1000K will cause a violent explosion and EMP depending on the contents of the field.
    Controlled via the R-UST Mk. 8 core control. Make careful note of the instability." antag_text = "Be careful when blowing this thing up. The blast is fairly large and can happen instantly depending how you do it." + disambiguator = "machine" /datum/codex_entry/fuel_compressor associated_paths = list(/obj/machinery/fuel_compressor) lore_text = "A highly secret design that can compress many varieties of solid and liquid matter into fuel rods for nuclear power production." mechanics_text = "Uses sheets of material or units of reagents to produce fuel rods. Material/units are inserted by hand. Can also have some objects click-dragged onto it for more exotic fuel." + disambiguator = "machine" /datum/codex_entry/gyrotron - associated_paths = list(/obj/machinery/power/emitter/gyrotron, /obj/machinery/computer/fusion/gyrotron) - associated_strings = list ("gyrotron","gyrotron control console") + associated_paths = list(/obj/machinery/emitter/gyrotron, /obj/machinery/computer/fusion/gyrotron) lore_text = "A high-power industrial laser used to excite plasma for fusion reactions. Also used to excite careless engineers, usually fatally." mechanics_text = "Fires in pulses and will heat up a plasma toroid that is below 1000K. Mostly used to lower field instability after heating it to ignition point. Very power hungry, uses 20k per point of power.
    Controlled via the gyrotron control console." + disambiguator = "machine" /datum/codex_entry/pacman - associated_paths = list(/obj/machinery/power/port_gen/pacman) + associated_paths = list(/obj/machinery/port_gen/pacman) mechanics_text = "Lends itself to being portable thanks to the small size and ease of use. Some versions use radioactive fuel and as such produces radiation, while others may produce dangerous byproducts as gasses. Ideally one should wear protective gear while interacting with an active generator. While active it also produces heat and will eventually overheat and explode. While the power output can be increased, doing this causes it to heat up faster. Must be secured using a wrench before use." antag_text = "Can be used as a makeshift delayed explosive when power output is set to unsafe levels, though it may take some time to go off." + disambiguator = "machine" /datum/codex_entry/replicator associated_paths = list(/obj/machinery/fabricator/replicator) mechanics_text = "The food replicator is operated through voice commands. To inquire available dishes on the menu, say 'menu'. To dispense a dish, say the name of the dish listed in its menu. Dishes can only be produced as long as the replicator has biomass. To check on the biomass level of the replicator, say 'status'. Various food items or plants may be inserted to refill biomass." + disambiguator = "machine" /datum/codex_entry/smes associated_paths = list(/obj/machinery/power/smes) mechanics_text = "It's a big battery. An important part of the power network that ensures that you still have power when your generators eventually explode. Maximum input and output settings are available.
    The lights on the front show the status of the SMES: The column on the left shows stored power, a blinking red light at top right shows that it's on but receiving no power, blinking yellow shows that the SMES is charging, and the little light on the middle right shows whether it's outputting power or not.
    A floor terminal puts power into the SMES, and power is output to a wire underneath." + disambiguator = "machine" /datum/codex_entry/vending associated_paths = list(/obj/machinery/vending) - associated_strings = list("vendomat","vending machine") + associated_strings = list("vending machine", "vendor") mechanics_text = "A machine that dispenses items from a category of items at the user's selection.
    Vending machines sometimes require payment via an ID card, cash or charge card. Some dispense items for free, and such vending machines are usually access restricted. Items that have been dispensed can sometimes be returned simply by inserting it back into the vending machine by hand. Vending machines can also be restocked with an appropriate Vendor Restock, usually ordered via supply management, and then click+dragged onto the vending machine.

    Vending machines can be hacked.
    Vending machine hacking
    * The orange light shows if the vending machine is electrified.
    * The red light indicates whether or not the vending machine is firing out its contents randomly. Sometimes this can happen as a random event.
    * The green light indicates whether or not the vending machine is dispensing it's hidden inventory. Nearly every vending machine has a list of secret goods that are usually considered to be contraband.
    * The purple or yellow light shows whether or not the ID scanner for the vending machine is working. When this function is disabled, anyone can access the vending machine, even if it normally has restricted access." - antag_text = "Accessing the secret inventory of a vending machine can sometimes be very useful, especially for department focused machines." \ No newline at end of file + antag_text = "Accessing the secret inventory of a vending machine can sometimes be very useful, especially for department focused machines." + disambiguator = "machine" diff --git a/code/modules/codex/entries/medical.dm b/code/modules/codex/entries/medical.dm index 738eee6c504..319a3abe635 100644 --- a/code/modules/codex/entries/medical.dm +++ b/code/modules/codex/entries/medical.dm @@ -29,20 +29,8 @@ Right-click the cell and click 'Eject Occupant' to remove them. You can enter the cell yourself by right clicking and selecting 'Enter Sleeper'. \ Note that you cannot control the sleeper while inside of it." -/datum/codex_entry/cryobag_furled - associated_paths = list(/obj/item/bodybag/cryobag) - mechanics_text = "This stasis bag will preserve the occupant, stopping most forms of harm from occuring, such as from oxygen \ - deprivation, irradiation, shock, and chemicals inside the occupant, at least until the bag is opened again.
    \ -
    \ - Stasis bags can only be used once, and are rather costly, so use them well. They are ideal for situations where someone may die \ - before being able to bring them back to safety, or if they are in a hostile enviroment, such as in vacuum or in a toxins leak, as \ - the bag will protect the occupant from most outside enviromental hazards. If you open a bag by mistake, closing the bag with no \ - occupant will not use up the bag, and you can pick it back up.
    \ -
    \ - You can use a health analyzer to scan the occupant's vitals without opening the bag by clicking the occupied bag with the analyzer." - -/datum/codex_entry/cryobag_open - associated_paths = list(/obj/structure/closet/body_bag/cryobag) +/datum/codex_entry/cryobag + associated_paths = list(/obj/item/bodybag/cryobag, /obj/structure/closet/body_bag/cryobag) mechanics_text = "This stasis bag will preserve the occupant, stopping most forms of harm from occuring, such as from oxygen \ deprivation, irradiation, shock, and chemicals inside the occupant, at least until the bag is opened again.
    \
    \ diff --git a/code/modules/codex/entries/misc.dm b/code/modules/codex/entries/misc.dm index 9e845528dde..18757131473 100644 --- a/code/modules/codex/entries/misc.dm +++ b/code/modules/codex/entries/misc.dm @@ -16,15 +16,4 @@ associated_paths = list(/obj/item/gun/launcher/money) mechanics_text = "Load money into the cannon by picking it up with the gun, or feeding it directly by hand. Use in your hand to configure how much money you want to fire per shot." lore_text = "These devices were invented several centuries ago and are a distinctly human cultural infection. They have produced knockoffs as timeless and as insipid as the potato gun and the paddle ball, showing up in all corners of the galaxy. The Money Cannon variation is noteworthy for its sturdiness and build quality, but is, at the end of the day, just another knockoff of the ancient originals." - -/datum/codex_entry/moneygun/New(_display_name, list/_associated_paths, list/_associated_strings, _lore_text, _mechanics_text, _antag_text) - . = ..() antag_text = "Sliding a cryptographic sequencer into the receptacle will short the motors and override their speed. If you set the cannon to dispense 100 units or more, this might make a handy weapon." - -/datum/codex_entry/textbook - associated_paths = list(/obj/item/book/skill) - associated_strings = list("textbook","skill book") - lore_text = "Education, written down and made overly expensive." - mechanics_text = "Textbooks provide skill buffs, raising the relevant skill by one level. In order to use textbook, you must have the matching skill that the textbook teaches, at the level it can buff. To use a textbook, simply hold it in your active hand and click it. You must then wait for a short period of time, after which you will have the skill buff indefinitely. However, the skill buff will be removed the moment the textbook is closed (used again) or removed from your hand in any way (dropped, put in a container, destroyed, etc). This process can be repeated without consequence, though only one textbook can be used at any one time.

    \ -

    Custom Skill Books

    \ - Those with Master level Literacy can create a textbook with the use of an autobinder and a suitable writing implement. This works much the same way as writing a normal book(use pen on book) but rather than manually writing the contents, the Skill option is selected from the menu. To imbue the book with any given skill, the writer must possess the relevant skill at the level they desire. After successfully selecting the skill and level, the writer must then repeatedly use the pen on the book until it is complete. Only once complete can it be used as a skill book. Important note: You may only write two skill books during a round. If a textbook you wrote gets destroyed, it does not enable you to write another to replace it." \ No newline at end of file diff --git a/code/modules/codex/entries/mobs.dm b/code/modules/codex/entries/mobs.dm index 57cf142ed4d..fd179f6bbf5 100644 --- a/code/modules/codex/entries/mobs.dm +++ b/code/modules/codex/entries/mobs.dm @@ -1,5 +1,4 @@ /datum/codex_entry/maint_drone - name = "maintenance drone" associated_paths = list(/mob/living/silicon/robot/drone) mechanics_text = "Drones are player-controlled synthetics which are lawed to maintain their assigned vessel and not \ interfere with anyone else, except for other drones. They hold a wide array of tools to build, repair, maintain, and clean. \ @@ -8,6 +7,7 @@ An inactive drone can be rebooted by swiping an ID card on it with engineering or robotics access, and an active drone can be shut down in the same manner. \ Maintenance drone presence can be requested to specific areas from any maintenance drone control console." antag_text = "A crypotgraphic sequencer, available via a traitor uplink, can be used to subvert the drone to your cause." + disambiguator = "synthetic" /datum/codex_entry/uncertified_module associated_paths = list(/obj/item/borg/upgrade/uncertified) diff --git a/code/modules/codex/entries/paperwork.dm b/code/modules/codex/entries/paperwork.dm index c7108813827..12b9902eb91 100644 --- a/code/modules/codex/entries/paperwork.dm +++ b/code/modules/codex/entries/paperwork.dm @@ -1,5 +1,8 @@ -/datum/codex_entry/pen - associated_paths = list(/obj/item/pen, /obj/item/book, /obj/item/paper) +/datum/codex_entry/writing + associated_strings = list("pencode") + associated_paths = list(/obj/item/pen, /obj/item/paper, /obj/item/book) + disambiguator = "writing" + mechanics_text = {"Used for writing down your thoughts, on paper or elsewhere. The following special commands are available:

    \[br\] : Creates a linebreak.
    \[center\] - \[/center\] : Centers the text.
    @@ -28,3 +31,11 @@ \[fontblue\] - \[/font\] : Makes the text blue.
    \[fontgreen\] - \[/font\] : Makes the text green.
    \[redacted\] : Adds R E D A C T E D in black font on a black background.

    "} + +/datum/codex_entry/textbook + associated_paths = list(/obj/item/book/skill) + associated_strings = list("skillbook","skill book") + lore_text = "Education, written down and made overly expensive." + mechanics_text = "Textbooks provide skill buffs, raising the relevant skill by one level. In order to use textbook, you must have the matching skill that the textbook teaches, at the level it can buff. To use a textbook, simply hold it in your active hand and click it. You must then wait for a short period of time, after which you will have the skill buff indefinitely. However, the skill buff will be removed the moment the textbook is closed (used again) or removed from your hand in any way (dropped, put in a container, destroyed, etc). This process can be repeated without consequence, though only one textbook can be used at any one time.

    \ +

    Custom Skill Books

    \ + Those with Master level Literacy can create a textbook with the use of an autobinder and a suitable writing implement. This works much the same way as writing a normal book(use pen on book) but rather than manually writing the contents, the Skill option is selected from the menu. To imbue the book with any given skill, the writer must possess the relevant skill at the level they desire. After successfully selecting the skill and level, the writer must then repeatedly use the pen on the book until it is complete. Only once complete can it be used as a skill book. Important note: You may only write two skill books during a round. If a textbook you wrote gets destroyed, it does not enable you to write another to replace it." diff --git a/code/modules/codex/entries/spells.dm b/code/modules/codex/entries/spells.dm deleted file mode 100644 index 24609f5af89..00000000000 --- a/code/modules/codex/entries/spells.dm +++ /dev/null @@ -1,115 +0,0 @@ -/datum/codex_entry/eternal_darkness - associated_paths = list(/spell/targeted/shatter) - antag_text = "Assaults the mind of the target with fear of the unknown, shattering their sanity and causing brain damage." - -/datum/codex_entry/torment - associated_paths = list(/spell/targeted/torment) - antag_text = "Darkness stabs at the bodies of those around you. All within a medium range will suffer significant pain." - -/datum/codex_entry/blood_shard - associated_paths = list(/spell/hand/charges/blood_shard) - antag_text = "Launches a blood-red shard directly infront of you. Anyone hit by this will have their blood explode out of them in a spray of smaller shards. Stores two charges." - -/datum/codex_entry/drain_blood - associated_paths = list(/spell/aoe_turf/drain_blood) - antag_text = "Allows you to drain the blood from any targets in a short range, restoring your own." - -/datum/codex_entry/starburst - associated_paths = list(/spell/targeted/genetic/blind/starburst) - antag_text = "Grants you the power to blind non-cultists nearby." - -/datum/codex_entry/exchange_wounds - associated_paths = list(/spell/aoe_turf/exchange_wounds) - antag_text = "Allows you to sacrifice your own well-being for that of those around you." - -/datum/codex_entry/starlight - associated_paths = list(/spell/radiant_aura/starlight) - antag_text = "This spell makes you immune to laser fire, for a short while at least." - -/datum/codex_entry/burning_hand - associated_paths = list(/spell/targeted/equip_item/burning_hand) - antag_text = "Sets your hand aflame, allowing you to burn people with an ever-increasing flame." - -/datum/codex_entry/burning_grip - associated_paths = list(/spell/hand/burning_grip) - antag_text = "Burns an item in someone's hand so badly it causes them to burn." - -/datum/codex_entry/blood_boil - associated_paths = list(/spell/targeted/blood_boil) - antag_text = "Allow you to concentrate so deeply on a target that their body temperature increases, eventually setting them on fire." - -/datum/codex_entry/fireball - associated_paths = list(/spell/targeted/projectile/dumbfire/fireball) - antag_text = "A classic spell, grants you the ability to throw an exploding ball of flame in any direction." - -/datum/codex_entry/starlight - associated_paths = list(/spell/aoe_turf/disable_tech/starlight) - antag_text = "Gives you a spell of disabling machinery, and mechanical hearts." - -/datum/codex_entry/heal_target - associated_paths = list(/spell/targeted/heal_target) - antag_text = "Grants you the ability to heal yourself or a nearby target slightly." - -/datum/codex_entry/create_air/tower - associated_paths = list(/spell/create_air/tower) - antag_text = "Allows you to generate a livable atmosphere in the area you are in." - -/datum/codex_entry/acid_spray/tower - associated_paths = list(/spell/acid_spray/tower) - antag_text = "The simplest form of aggressive conjuration: acid spray is quite effective in melting both man and object." - -/datum/codex_entry/forcewall/tower - associated_paths = list(/spell/aoe_turf/conjure/forcewall/tower) - antag_text = "A temporary invincible wall for you to summon." - -/datum/codex_entry/faithful_hound/tower - associated_paths = list(/spell/aoe_turf/conjure/faithful_hound/tower) - antag_text = "This spell allows you to summon a singular spectral dog that guards the nearby area. Anyone without the password is barked at or bitten." - -/datum/codex_entry/dyrnwyn/tower - associated_paths = list(/spell/targeted/equip_item/dyrnwyn/tower) - antag_text = "This spell allows you to summon a golden firey sword for a short duration." - -/datum/codex_entry/shield/tower - associated_paths = list(/spell/targeted/equip_item/shield/tower) - antag_text = "This spell allows you to summon a magical shield for a short duration." - -/datum/codex_entry/fireball/tower - associated_paths = list(/spell/targeted/projectile/dumbfire/fireball/tower) - antag_text = "Imbue yourself with the power of exploding fire." - -/datum/codex_entry/force_portal/tower - associated_paths = list(/spell/aoe_turf/conjure/force_portal/tower) - antag_text = "This spell allows you to summon a force portal. Anything that hits the portal gets sucked inside and is then thrown out when the portal explodes." - -/datum/codex_entry/slippery_surface/tower - associated_paths = list(/spell/hand/slippery_surface/tower) - antag_text = "Allows you to slicken a small patch of floor. Anyone without sure-footing will find it hard to stay upright." - -/datum/codex_entry/smoke/tower - associated_paths = list(/spell/aoe_turf/smoke/tower) - antag_text = "Allows you to distill the nearby air into smoke." - -/datum/codex_entry/knock/tower - associated_paths = list(/spell/aoe_turf/knock/tower) - antag_text = "Allows you to open nearby doors without the keys." - -/datum/codex_entry/burning_grip/tower - associated_paths = list(/spell/hand/burning_grip/tower) - antag_text = "Allows you cause an object to heat up intensly in someone's hand, making them drop it and whatever skin is attached." - -/datum/codex_entry/ethereal_jaunt/tower - associated_paths = list(/spell/targeted/ethereal_jaunt/tower) - antag_text = "Allows you to liquify for a short duration, letting them pass through all dense objects." - -/datum/codex_entry/heal_target/tower - associated_paths = list(/spell/targeted/heal_target/tower) - antag_text = "Allows you to heal yourself, or others, for a slight amount." - -/datum/codex_entry/heal_target/major/tower - associated_paths = list(/spell/targeted/heal_target/major/tower) - antag_text = "Allows you to heal others for a great amount." - -/datum/codex_entry/heal_target/area/tower - associated_paths = list(/spell/targeted/heal_target/area/tower) - antag_text = "Allows you to heal everyone in an area for minor damage." \ No newline at end of file diff --git a/code/modules/codex/entries/stacks.dm b/code/modules/codex/entries/stacks.dm index b54f26afdbb..df3681cc031 100644 --- a/code/modules/codex/entries/stacks.dm +++ b/code/modules/codex/entries/stacks.dm @@ -27,6 +27,5 @@ You can replenish your supply of metal as a synthetic by recharging." /datum/codex_entry/material_sheet - name = "material sheet" associated_paths = list(/obj/item/stack/material) mechanics_text = "Use in your hand to bring up the recipe menu. If you have enough sheets, click on something on the list to build it." \ No newline at end of file diff --git a/code/modules/codex/entries/structures.dm b/code/modules/codex/entries/structures.dm index e82d5b1e131..9459123486d 100644 --- a/code/modules/codex/entries/structures.dm +++ b/code/modules/codex/entries/structures.dm @@ -3,16 +3,19 @@ mechanics_text = "Use material sheets on this while anchored to build a wall. Adding sheets while unanchored will reinforce it.
    \ A false wall can be made by using a screwdriver on this girder, and then adding material sheets.
    \ You can dismantle the grider with a wrench." + disambiguator = "structure" /datum/codex_entry/grille associated_paths = list(/obj/structure/grille) mechanics_text = "A powered and knotted wire underneath this will cause the grille (provided it is made of a conductive material) to shock anyone not wearing insulated gloves.
    \ Wirecutters will turn the grille into rods instantly. Grilles are typically made with steel rods." + disambiguator = "structure" /datum/codex_entry/lattice associated_paths = list(/obj/structure/lattice) mechanics_text = "Add a metal floor tile to build a floor on top of the lattice.
    \ Lattices can be made by applying rods to a space tile." + disambiguator = "structure" /datum/codex_entry/bed associated_paths = list(/obj/structure/bed) @@ -20,41 +23,51 @@
    \ Anyone with restraints, such as handcuffs, will not be able to unbuckle themselves. They must use the Resist button, or verb, to break free of \ the buckles, instead." + disambiguator = "structure" /datum/codex_entry/cheval associated_paths = list(/obj/structure/barricade, /obj/structure/barricade/spike) lore_text = "The cheval de frise (Frisian horse) is an ancient anti-cavalry barricade so named because they were widely deployed by the Frisians, who lacked easy access to horses to field their own cavalry." mechanics_text = "A simple barricade is crafted from any material. You can make it a cheval de frise by adding rods of any material to a barricade constructed of any material, this structure will injure anyone who moves into it." + disambiguator = "structure" /datum/codex_entry/deity/altar associated_paths = list(/obj/structure/deity/altar) mechanics_text = "To place someone upon the altar, first have them in an aggressive grab and click the altar while adjacent." antag_text = "This structure anchors your deity within this realm, granting them additional power to influence it and empower their followers. Additionally, using it as a focus for their powers, they can convert someone laying on top of the altar.
    " + disambiguator = "occult" /datum/codex_entry/deity/blood_forge associated_paths = list(/obj/structure/deity/blood_forge) antag_text = "Allows creation of special items by feeding your blood into it. Only usable by cultists of the aligned deity." + disambiguator = "occult" /datum/codex_entry/deity/blood_stone associated_paths = list(/obj/structure/deity/blood_stone) antag_text = "Allows the user to feed blood directly to the aligned deity, granting it power." + disambiguator = "occult" /datum/codex_entry/deity/gateway associated_paths = list(/obj/structure/deity/gateway) antag_text = "Stand within the gateway to be transported to an unknown dimension and transformed into a flaming Starborn, a mysterious Blueforged or a subtle Shadowling." + disambiguator = "occult" /datum/codex_entry/deity/radiant_statue associated_paths = list(/obj/structure/deity/radiant_statue) antag_text = "Allows storage of power if cultists are nearby. This stored power can be expended to charge Starlight items." + disambiguator = "occult" /datum/codex_entry/deity/blood_forge/starlight associated_paths = list(/obj/structure/deity/blood_forge/starlight) antag_text = "Allows creation of special Starlight items. Only usable by cultists of the aligned deity. Use of this powerful forge will inflict burns." + disambiguator = "occult" /datum/codex_entry/deity/wizard_recharger associated_paths = list(/obj/structure/deity/wizard_recharger) antag_text = "A well of arcane power. When charged, a cultist may absorb this power to refresh their spells." + disambiguator = "occult" /datum/codex_entry/deity/pylon associated_paths = list(/obj/structure/deity/pylon) - antag_text = "Serves as a conduit for a deity to speak through, making their will known in this dimension to any who hear it." \ No newline at end of file + antag_text = "Serves as a conduit for a deity to speak through, making their will known in this dimension to any who hear it." + disambiguator = "occult" diff --git a/code/modules/codex/entries/turfs.dm b/code/modules/codex/entries/turfs.dm index 69233735ff8..75e98b7dec2 100644 --- a/code/modules/codex/entries/turfs.dm +++ b/code/modules/codex/entries/turfs.dm @@ -2,4 +2,5 @@ associated_paths = list(/turf/simulated/wall) mechanics_text = "You can deconstruct this by welding it, and then wrenching the girder.
    \ You can build a wall by using metal sheets to make a girder, then adding almost any material as plating.
    \ - Walls are typically made of steel girders, plated with steel or plasteel." \ No newline at end of file + Walls are typically made of steel girders, plated with steel or plasteel." + disambiguator = "structure" \ No newline at end of file diff --git a/code/modules/crafting/crafting_recipes/improvised_crafting/crafting_buckler.dm b/code/modules/crafting/crafting_recipes/improvised_crafting/crafting_buckler.dm new file mode 100644 index 00000000000..4270ffc3b33 --- /dev/null +++ b/code/modules/crafting/crafting_recipes/improvised_crafting/crafting_buckler.dm @@ -0,0 +1,22 @@ +/decl/crafting_stage/buckler_frame + descriptor = "improvised buckler" + begins_with_object_type = /obj/item/stool + completion_trigger_type = /obj/item/circular_saw + item_desc = "It's the seat of a stool with the legs cut off." + item_icon_state = "buckler1" + progress_message = "You crudely sever the legs off the stool and remove the seat." + consume_completion_trigger = FALSE + next_stages = list(/decl/crafting_stage/buckler_panels) + +/decl/crafting_stage/buckler_panels + item_desc = "It's the seat of a stool with the legs sawn off and wooden planks layered over the top, ready to secure in place." + completion_trigger_type = /obj/item/stack/material/plank + consume_completion_trigger = FALSE + stack_consume_amount = 3 + item_icon_state = "buckler2" + progress_message = "You layer planks over the frame to serve as armour panels." + next_stages = list(/decl/crafting_stage/screwdriver/buckler_finish) + +/decl/crafting_stage/screwdriver/buckler_finish + progress_message = "You secure the buckler's panels in place and finish it off." + product = /obj/item/shield/buckler diff --git a/code/modules/culture_descriptor/culture/cultures_human.dm b/code/modules/culture_descriptor/culture/cultures_human.dm index 9c70a00a896..592c18eac6b 100644 --- a/code/modules/culture_descriptor/culture/cultures_human.dm +++ b/code/modules/culture_descriptor/culture/cultures_human.dm @@ -1,6 +1,7 @@ /decl/cultural_info/culture/other name = "Other Culture" description = "You are from one of the many small, relatively unknown cultures scattered across the galaxy." + language = /decl/language/human/common secondary_langs = list( /decl/language/human/common, /decl/language/sign @@ -9,6 +10,7 @@ /decl/cultural_info/culture/human name = "Human Culture" description = "You are from one of various planetary cultures of humankind." + language = /decl/language/human/common secondary_langs = list( /decl/language/human/common, /decl/language/sign diff --git a/code/modules/culture_descriptor/faction/factions_human.dm b/code/modules/culture_descriptor/faction/factions_human.dm index aca4b8691b8..1d828c52b78 100644 --- a/code/modules/culture_descriptor/faction/factions_human.dm +++ b/code/modules/culture_descriptor/faction/factions_human.dm @@ -1,4 +1,4 @@ /decl/cultural_info/faction/other - name = "Other Faction" + name = "Misc. - Other Faction" description = "You belong to one of the many other factions that operate in the galaxy. Numerous, too numerous to list, these factions represent a variety of interests, purposes, intents and goals." subversive_potential = 25 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/economy/worth_misc.dm b/code/modules/economy/worth_misc.dm index 4e2cfa1e455..fb9014523d3 100644 --- a/code/modules/economy/worth_misc.dm +++ b/code/modules/economy/worth_misc.dm @@ -6,6 +6,6 @@ PATH/get_value_multiplier() { . = 1 } \ PATH/get_base_value() { . = AMT } -ARBITRARY_WORTH(/obj/machinery/power/emitter, 700) -ARBITRARY_WORTH(/obj/machinery/power/rad_collector, 500) +ARBITRARY_WORTH(/obj/machinery/emitter, 700) +ARBITRARY_WORTH(/obj/machinery/rad_collector, 500) ARBITRARY_WORTH(/obj/structure/particle_accelerator, 500) diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index 1a0a13bba23..7d7ed5f238d 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -63,21 +63,21 @@ var/list/shields = list() if(overmap_only) for(var/obj/effect/overmap/visitable/sector AS_ANYTHING in overmap_sectors) - var/list/sector_shields = sector.get_linked_machines_of_type(/obj/machinery/power/shield_generator) + var/list/sector_shields = sector.get_linked_machines_of_type(/obj/machinery/shield_generator) if(length(sector_shields)) shields |= sector_shields else - for(var/obj/machinery/power/shield_generator/G in SSmachines.machinery) + for(var/obj/machinery/shield_generator/G in SSmachines.machinery) if(G.z in affecting_z) shields |= G - for(var/obj/machinery/power/shield_generator/G AS_ANYTHING in shields) + for(var/obj/machinery/shield_generator/G AS_ANYTHING in shields) if(!(G.running) || !G.check_flag(MODEFLAG_EM)) shields -= G var/shielded = FALSE if(length(shields)) - var/obj/machinery/power/shield_generator/shield_gen = pick(shields) + var/obj/machinery/shield_generator/shield_gen = pick(shields) //Minor breaches aren't enough to let through frying amounts of power if(shield_gen.take_shield_damage(30 * severity, SHIELD_DAMTYPE_EM) <= SHIELD_BREACHED_MINOR) shielded = TRUE diff --git a/code/modules/fabrication/_fabricator.dm b/code/modules/fabrication/_fabricator.dm index e34397bd246..166ce660ee0 100644 --- a/code/modules/fabrication/_fabricator.dm +++ b/code/modules/fabrication/_fabricator.dm @@ -63,6 +63,12 @@ // If TRUE, fills fabricator with material on initalize var/prefilled = FALSE + //Collapsing Menus stuff + var/ui_expand_queue = FALSE + var/ui_expand_resources = FALSE + var/ui_expand_config = FALSE + var/ui_nb_categories = 1 //Cached amount of categories in loaded designs. Used to decide if we display the category filter or not + /obj/machinery/fabricator/Destroy() QDEL_NULL(currently_building) QDEL_NULL_LIST(queued_orders) @@ -146,7 +152,9 @@ if(length(unlocked_tech)) design_cache |= unlocked_tech + var/list/unique_categories for(var/datum/fabricator_recipe/R in design_cache) + LAZYDISTINCTADD(unique_categories, R.category) if(!length(R.species_locked)) continue @@ -160,6 +168,7 @@ return design_cache = sortTim(design_cache, /proc/cmp_name_asc) + ui_nb_categories = LAZYLEN(unique_categories) /obj/machinery/fabricator/state_transition(var/decl/machine_construction/default/new_state) . = ..() diff --git a/code/modules/fabrication/_fabricator_build_order.dm b/code/modules/fabrication/_fabricator_build_order.dm index 6dedfb8afc9..06f39cf4904 100644 --- a/code/modules/fabrication/_fabricator_build_order.dm +++ b/code/modules/fabrication/_fabricator_build_order.dm @@ -3,7 +3,27 @@ var/multiplier = 1 var/remaining_time = 0 var/list/earmarked_materials = list() + var/list/data //Fabricator specific data + +/datum/fabricator_build_order/New(var/datum/fabricator_recipe/_target_recipe, var/_multiplier = 1, var/list/_data = null) + ..() + target_recipe = _target_recipe + multiplier = _multiplier + data = _data /datum/fabricator_build_order/Destroy() target_recipe = null . = ..() + +/datum/fabricator_build_order/proc/set_data(var/name, var/value) + if(!name || !value) + return + LAZYSET(data, name, value) + +//Returns the data entry for "name" or whatever is in "default" if there's nothing found +//Helps not having to deal with writing extra conditionals +/datum/fabricator_build_order/proc/get_data(var/name, var/default = null) + if(!name) + return default + var/value = LAZYACCESS(data, name) + return value ? value : default diff --git a/code/modules/fabrication/designs/_design.dm b/code/modules/fabrication/designs/_design.dm index 210eb222492..02fd2d99f85 100644 --- a/code/modules/fabrication/designs/_design.dm +++ b/code/modules/fabrication/designs/_design.dm @@ -24,7 +24,7 @@ name = get_product_name() if(required_technology == TRUE) if(ispath(path, /obj/item)) - var/list/res = build(null, 1) + var/list/res = build(null, new/datum/fabricator_build_order(src, 1)) if(length(res)) var/obj/item/O = res[1] var/tech = O.get_origin_tech() @@ -63,10 +63,10 @@ for(var/R in reagents.reagent_volumes) .[R] = REAGENT_VOLUME(reagents, R) -/datum/fabricator_recipe/proc/build(var/turf/location, var/amount = 1) +/datum/fabricator_recipe/proc/build(var/turf/location, var/datum/fabricator_build_order/order) . = list() if(ispath(path, /obj/item/stack)) - . += new path(location, amount) + . += new path(location, order.multiplier) else - for(var/i = 1, i <= amount, i++) + for(var/i = 1, i <= order.multiplier, i++) . += new path(location) \ No newline at end of file diff --git a/code/modules/fabrication/designs/general/designs_general.dm b/code/modules/fabrication/designs/general/designs_general.dm index a0c6cbbf25d..cc5f7f75273 100644 --- a/code/modules/fabrication/designs/general/designs_general.dm +++ b/code/modules/fabrication/designs/general/designs_general.dm @@ -138,3 +138,9 @@ /datum/fabricator_recipe/umbrella path = /obj/item/umbrella + +/datum/fabricator_recipe/network_id + path = /obj/item/card/id/network + fabricator_types = list( + FABRICATOR_CLASS_GENERAL + ) diff --git a/code/modules/fabrication/designs/industrial/designs_armour.dm b/code/modules/fabrication/designs/industrial/designs_armour.dm new file mode 100644 index 00000000000..6b291d87a25 --- /dev/null +++ b/code/modules/fabrication/designs/industrial/designs_armour.dm @@ -0,0 +1,7 @@ +/datum/fabricator_recipe/industrial/armour + category = "Shields and Armour" + path = /obj/item/shield/riot + hidden = TRUE + +/datum/fabricator_recipe/industrial/armour/metal + path = /obj/item/shield/riot/metal diff --git a/code/modules/fabrication/designs/pipe/device_pipe_datums.dm b/code/modules/fabrication/designs/pipe/device_pipe_datums.dm index c0ef78dc28c..82969909689 100644 --- a/code/modules/fabrication/designs/pipe/device_pipe_datums.dm +++ b/code/modules/fabrication/designs/pipe/device_pipe_datums.dm @@ -166,7 +166,7 @@ pipe_class = PIPE_CLASS_OTHER /datum/fabricator_recipe/pipe/device/outlet_injector - name = "injector" + name = "injector outlet" desc = "Passively injects gas into its surroundings. Has a valve attached to it that can control flow rate." build_icon = 'icons/atmos/injector.dmi' build_icon_state = "map_injector" diff --git a/code/modules/fabrication/designs/pipe/pipe_datum_base.dm b/code/modules/fabrication/designs/pipe/pipe_datum_base.dm index 84ca4a943a0..9e3c5092ce1 100644 --- a/code/modules/fabrication/designs/pipe/pipe_datum_base.dm +++ b/code/modules/fabrication/designs/pipe/pipe_datum_base.dm @@ -25,9 +25,9 @@ for(var/path in building_cost) resources[path] = building_cost[path] * FABRICATOR_EXTRA_COST_FACTOR -/datum/fabricator_recipe/pipe/build(var/turf/location, var/amount = 1, var/color = PIPE_COLOR_WHITE) +/datum/fabricator_recipe/pipe/build(var/turf/location, var/datum/fabricator_build_order/order) . = list() - for(var/i = 1, i <= amount, i++) + for(var/i = 1, i <= order.multiplier, i++) var/obj/item/pipe/new_item = new path(location) if(istype(new_item)) if(connect_types != null) @@ -36,7 +36,7 @@ new_item.rotate_class = rotate_class new_item.constructed_path = constructed_path if(colorable) - new_item.color = color + new_item.color = order.get_data("selected_color", PIPE_COLOR_WHITE) else if (pipe_color != null) new_item.color = pipe_color new_item.SetName(name) @@ -47,9 +47,9 @@ new_item.icon_state = build_icon_state . += new_item -/datum/fabricator_recipe/pipe/disposal_dispenser/build(var/turf/location, var/amount = 1, var/color = PIPE_COLOR_GREY) +/datum/fabricator_recipe/pipe/disposal_dispenser/build(var/turf/location, var/datum/fabricator_build_order/order) . = ..() - for(var/i = 1, i <= amount, i++) + for(var/i = 1, i <= order.multiplier, i++) var/obj/structure/disposalconstruct/new_item = new path(location) new_item.SetName(name) if(desc) diff --git a/code/modules/fabrication/designs/protolathe/designs_power_cells.dm b/code/modules/fabrication/designs/protolathe/designs_power_cells.dm index ac13c53275d..829d07e5de2 100644 --- a/code/modules/fabrication/designs/protolathe/designs_power_cells.dm +++ b/code/modules/fabrication/designs/protolathe/designs_power_cells.dm @@ -6,7 +6,7 @@ FABRICATOR_CLASS_ROBOTICS ) -/datum/fabricator_recipe/protolathe/cell/build(var/turf/location, var/amount = 1) +/datum/fabricator_recipe/protolathe/cell/build() . = ..() for(var/obj/item/cell/C in .) C.charge = 0 diff --git a/code/modules/fabrication/designs/robotics/designs_organs.dm b/code/modules/fabrication/designs/robotics/designs_organs.dm index 2c6e338f869..ee17ae235f4 100644 --- a/code/modules/fabrication/designs/robotics/designs_organs.dm +++ b/code/modules/fabrication/designs/robotics/designs_organs.dm @@ -18,10 +18,13 @@ LAZYSET(resources, /decl/material/solid/metal/steel, meat_amount) LAZYREMOVE(resources, /decl/material/solid/meat) -/datum/fabricator_recipe/robotics/organ/build() +/datum/fabricator_recipe/robotics/organ/build(turf/location, datum/fabricator_build_order/order) . = ..() + var/species = order.get_data("species", global.using_map.default_species) for(var/obj/item/organ/internal/I in .) + I.set_species(species) I.robotize() + I.status |= ORGAN_CUT_AWAY /datum/fabricator_recipe/robotics/organ/heart path = /obj/item/organ/internal/heart diff --git a/code/modules/fabrication/designs/robotics/designs_prosthetics.dm b/code/modules/fabrication/designs/robotics/designs_prosthetics.dm index b113201e4be..d9d13ae2d6c 100644 --- a/code/modules/fabrication/designs/robotics/designs_prosthetics.dm +++ b/code/modules/fabrication/designs/robotics/designs_prosthetics.dm @@ -67,9 +67,12 @@ var/decl/prosthetics_manufacturer/brand = GET_DECL(model) return "[.] ([brand.name])" -/datum/fabricator_recipe/robotics/prosthetic/build() +/datum/fabricator_recipe/robotics/prosthetic/build(var/turf/location, var/datum/fabricator_build_order/order) . = ..() + var/species = order.get_data("species", global.using_map.default_species) for(var/obj/item/organ/external/E in .) + E.set_species(species) E.robotize(model) + E.status |= ORGAN_CUT_AWAY DEFINE_ROBOLIMB_DESIGNS(/decl/prosthetics_manufacturer, generic) diff --git a/code/modules/fabrication/fabricator_bioprinter.dm b/code/modules/fabrication/fabricator_bioprinter.dm index b4a3dc88038..b26d2b2bfcf 100644 --- a/code/modules/fabrication/fabricator_bioprinter.dm +++ b/code/modules/fabrication/fabricator_bioprinter.dm @@ -1,6 +1,8 @@ +#define BIOPRINTER_BLOOD_SAMPLE_SIZE 5 + /obj/machinery/fabricator/bioprinter name = "bioprinter" - desc = "It's a machine that fabricates organs out of meat." + desc = "Fabricator used for cloning organs from DNA." icon = 'icons/obj/machines/fabricators/bioprinter.dmi' icon_state = "bioprinter" base_icon_state = "bioprinter" @@ -9,28 +11,75 @@ base_storage_capacity = list( /decl/material/solid/meat = SHEET_MATERIAL_AMOUNT * 100 ) - var/datum/dna/loaded_dna_datum + var/datum/dna/loaded_dna //DNA for biological organs + +/obj/machinery/fabricator/bioprinter/get_nano_template() + return "fabricator_bioprinter.tmpl" + +/obj/machinery/fabricator/bioprinter/make_order(datum/fabricator_recipe/recipe, multiplier) + var/datum/fabricator_build_order/order = ..() + //Keep these in the order so changing settings while queueing things up won't screw up older orders in the queue + order.set_data("dna", loaded_dna) + return order -/obj/machinery/fabricator/bioprinter/do_build(var/datum/fabricator_recipe/recipe, var/amount) +/obj/machinery/fabricator/bioprinter/do_build(datum/fabricator_build_order/order) . = ..() + //Fetch params as they were when the order was passed + var/datum/dna/D = order.get_data("dna") for(var/obj/item/organ/O in .) - if(loaded_dna_datum) - O.set_dna(loaded_dna_datum) + if(D) + O.set_dna(D) O.status |= ORGAN_CUT_AWAY /obj/machinery/fabricator/bioprinter/attackby(obj/item/W, mob/user) if(istype(W,/obj/item/chems/syringe)) var/obj/item/chems/syringe/S = W if(REAGENT_VOLUME(S.reagents, /decl/material/liquid/blood)) - var/loaded_dna = REAGENT_DATA(S.reagents, /decl/material/liquid/blood) - if(islist(loaded_dna)) - var/weakref/R = loaded_dna["donor"] + var/sample = REAGENT_DATA(S.reagents, /decl/material/liquid/blood) + if(islist(sample)) + var/weakref/R = sample["donor"] var/mob/living/carbon/human/H = R.resolve() if(H && istype(H) && H.species && H.dna) - loaded_dna_datum = H.dna && H.dna.Clone() + loaded_dna = H.dna.Clone() to_chat(user, SPAN_INFO("You inject the blood sample into \the [src].")) - S.reagents.clear_reagents() + S.reagents.remove_any(BIOPRINTER_BLOOD_SAMPLE_SIZE) + //Tell nano to do its job + SSnano.update_uis(src) return TRUE to_chat(user, SPAN_WARNING("\The [src] displays an error: no viable blood sample could be obtained from \the [W].")) return TRUE . = ..() + +/obj/machinery/fabricator/bioprinter/OnTopic(user, href_list, state) + . = ..() + if(href_list["flush_dna"]) + if(fab_status_flags & FAB_BUSY) + state("Can't flush DNA while printing in progress!") + else + loaded_dna = null + . = TOPIC_REFRESH + +/obj/machinery/fabricator/bioprinter/proc/ui_data_dna(mob/user, ui_key) + if(!loaded_dna) + return null + return list( + "real_name" = loaded_dna.real_name, + "UE" = loaded_dna.unique_enzymes, + "species" = loaded_dna.species, + "btype" = loaded_dna.b_type, + ) + +/obj/machinery/fabricator/bioprinter/ui_draw_config(mob/user, ui_key) + return TRUE //Always draw it for us + +/obj/machinery/fabricator/bioprinter/ui_data_config(mob/user, ui_key) + if(!(. = ..())) + return + var/list/dnaentry = ui_data_dna(user, ui_key) + LAZYSET(., "dna", dnaentry) + +//Only let us print things if we got a DNA set +/obj/machinery/fabricator/bioprinter/can_build(datum/fabricator_recipe/recipe, multiplier) + return ..() && loaded_dna +/obj/machinery/fabricator/bioprinter/ui_fabricator_build_option_is_available(datum/fabricator_recipe/R, max_sheets) + return ..() && loaded_dna diff --git a/code/modules/fabrication/fabricator_books.dm b/code/modules/fabrication/fabricator_books.dm index 6800874675a..60d7afa9097 100644 --- a/code/modules/fabrication/fabricator_books.dm +++ b/code/modules/fabrication/fabricator_books.dm @@ -14,15 +14,20 @@ ) color_selectable = TRUE -/obj/machinery/fabricator/book/do_build(datum/fabricator_recipe/recipe, amount) - . = recipe.build(get_turf(src), amount, selected_color) +/obj/machinery/fabricator/book/make_order(datum/fabricator_recipe/recipe, multiplier) + var/datum/fabricator_build_order/order = ..() + LAZYSET(order.data, "selected_color", selected_color) + return order -/datum/fabricator_recipe/book/skill/build(var/turf/location, var/amount = 1, var/color = COLOR_WHITE) +/obj/machinery/fabricator/book/do_build(datum/fabricator_build_order/order) + . = order.target_recipe.build(get_turf(src), order) + +/datum/fabricator_recipe/book/skill/build(var/turf/location, var/datum/fabricator_build_order/order) . = list() - for(var/i = 1, i <= amount, i++) + for(var/i = 1, i <= order.multiplier, i++) var/obj/item/book/skill/custom/new_item = new path(location) if(colorable) - new_item.color = color + new_item.color = order.get_data("selected_color", COLOR_WHITE) new_item.overlays += overlay_image('icons/obj/library.dmi', "tb_over_pages", null, RESET_COLOR) . += new_item diff --git a/code/modules/fabrication/fabricator_build.dm b/code/modules/fabrication/fabricator_build.dm index 8ae7f043a8c..9a0b4b143d7 100644 --- a/code/modules/fabrication/fabricator_build.dm +++ b/code/modules/fabrication/fabricator_build.dm @@ -9,13 +9,13 @@ return // Print the item. - do_build(currently_building.target_recipe, currently_building.multiplier) + do_build(currently_building) QDEL_NULL(currently_building) get_next_build() update_icon() -/obj/machinery/fabricator/proc/do_build(var/datum/fabricator_recipe/recipe, var/amount) - . = recipe.build(get_turf(src), amount) +/obj/machinery/fabricator/proc/do_build(var/datum/fabricator_build_order/order) + . = order.target_recipe.build(get_turf(src), order) if(output_dir) for(var/atom/movable/product in .) step(product, output_dir) @@ -43,7 +43,6 @@ updateUsrDialog() /obj/machinery/fabricator/proc/try_queue_build(var/datum/fabricator_recipe/recipe, var/multiplier) - // Do some basic sanity checking. if(!is_functioning() || !istype(recipe) || !(recipe in design_cache)) return @@ -56,12 +55,13 @@ if(stored_material[material] < round(recipe.resources[material] * mat_efficiency) * multiplier) return + //Ask subclass if its okay to print + if(!can_build(recipe, multiplier)) + return + // Generate and track a new order. - var/datum/fabricator_build_order/order = new - order.remaining_time = recipe.build_time - order.target_recipe = recipe - order.multiplier = multiplier - queued_orders += order + var/datum/fabricator_build_order/order = make_order(recipe, multiplier) + queued_orders += order // Remove/earmark resources. for(var/material in recipe.resources) @@ -72,4 +72,16 @@ if(!currently_building) get_next_build() else - start_building() \ No newline at end of file + start_building() + +//Allow storing more details in the order for fabricator subclasses +/obj/machinery/fabricator/proc/make_order(var/datum/fabricator_recipe/recipe, var/multiplier) + var/datum/fabricator_build_order/order = new + order.remaining_time = recipe.build_time + order.target_recipe = recipe + order.multiplier = multiplier + return order + +//Override this to add more conditions to printing an object +/obj/machinery/fabricator/proc/can_build(var/datum/fabricator_recipe/recipe, var/multiplier) + return TRUE \ No newline at end of file diff --git a/code/modules/fabrication/fabricator_industrial.dm b/code/modules/fabrication/fabricator_industrial.dm index 5b3c8b38b77..3e467d2472a 100644 --- a/code/modules/fabrication/fabricator_industrial.dm +++ b/code/modules/fabrication/fabricator_industrial.dm @@ -10,6 +10,7 @@ fabricator_class = FABRICATOR_CLASS_INDUSTRIAL base_storage_capacity = list( /decl/material/solid/metal/steel = SHEET_MATERIAL_AMOUNT * 100, + /decl/material/solid/metal/plasteel = SHEET_MATERIAL_AMOUNT * 100, /decl/material/solid/fiberglass = SHEET_MATERIAL_AMOUNT * 100, /decl/material/solid/metal/osmium = SHEET_MATERIAL_AMOUNT * 100, /decl/material/solid/metal/aluminium = SHEET_MATERIAL_AMOUNT * 100, diff --git a/code/modules/fabrication/fabricator_pipe.dm b/code/modules/fabrication/fabricator_pipe.dm index 626b25c6aad..e4ec2a71356 100644 --- a/code/modules/fabrication/fabricator_pipe.dm +++ b/code/modules/fabrication/fabricator_pipe.dm @@ -32,10 +32,15 @@ // Pipe objects do not contain matter, and will not provide a refund on materials used to make them, but can be recycled to prevent clutter. if(istype(thing, /obj/item/pipe) && (. == SUBSTANCE_TAKEN_NONE)) return SUBSTANCE_TAKEN_ALL - -/obj/machinery/fabricator/pipe/do_build(var/datum/fabricator_recipe/recipe, var/amount) - . = recipe.build(get_turf(src), amount, pipe_colors[selected_color]) - use_power_oneoff(500 * amount) + +/obj/machinery/fabricator/pipe/make_order(datum/fabricator_recipe/recipe, multiplier) + var/datum/fabricator_build_order/order = ..() + order.set_data("selected_color", selected_color) + return order + +/obj/machinery/fabricator/pipe/do_build(datum/fabricator_build_order/order) + . = order.target_recipe.build(get_turf(src), order) + use_power_oneoff(500 * order.multiplier) /obj/machinery/fabricator/pipe/disposal name = "disposal pipe dispenser" diff --git a/code/modules/fabrication/fabricator_presets.dm b/code/modules/fabrication/fabricator_presets.dm index f702f36a25a..44d4ac711d5 100644 --- a/code/modules/fabrication/fabricator_presets.dm +++ b/code/modules/fabrication/fabricator_presets.dm @@ -27,3 +27,6 @@ /obj/machinery/fabricator/textile/filled prefilled = TRUE + +/obj/machinery/fabricator/bioprinter/filled + prefilled = TRUE \ No newline at end of file diff --git a/code/modules/fabrication/fabricator_robotics.dm b/code/modules/fabrication/fabricator_robotics.dm index b93c7950ff7..5964797ec82 100644 --- a/code/modules/fabrication/fabricator_robotics.dm +++ b/code/modules/fabrication/fabricator_robotics.dm @@ -19,8 +19,29 @@ /decl/material/solid/metal/uranium = SHEET_MATERIAL_AMOUNT * 100, /decl/material/solid/gemstone/diamond = SHEET_MATERIAL_AMOUNT * 100 ) + var/picked_prosthetic_species //Prosthetics will be printed with this species -/obj/machinery/fabricator/robotics/do_build(var/datum/fabricator_recipe/recipe, var/amount) +/obj/machinery/fabricator/robotics/Initialize() . = ..() - for(var/obj/item/organ/O in .) - O.status |= ORGAN_CUT_AWAY + picked_prosthetic_species = global.using_map?.default_species //Set it by default to the base species to preserve earlier behavior for now + +/obj/machinery/fabricator/robotics/make_order(datum/fabricator_recipe/recipe, multiplier) + var/datum/fabricator_build_order/order = ..() + order.set_data("species", picked_prosthetic_species) + return order + + +/obj/machinery/fabricator/robotics/OnTopic(user, href_list, state) + . = ..() + if(href_list["pick_species"]) + var/chosen_species = input(user, "Choose a specie to produce prosthetics for", "Target Species", null) in get_playable_species() + if(chosen_species) + picked_prosthetic_species = chosen_species + . = TOPIC_REFRESH + +/obj/machinery/fabricator/robotics/ui_data(mob/user, ui_key) + . = ..() + LAZYSET(., "species", picked_prosthetic_species) + +/obj/machinery/fabricator/robotics/get_nano_template() + return "fabricator_robot.tmpl" diff --git a/code/modules/fabrication/fabricator_topic.dm b/code/modules/fabrication/fabricator_topic.dm index fc80be0e28a..417b17cb8d2 100644 --- a/code/modules/fabrication/fabricator_topic.dm +++ b/code/modules/fabrication/fabricator_topic.dm @@ -18,7 +18,7 @@ try_dump_material(href_list["eject_mat"]) . = TOPIC_REFRESH - if(href_list["settings"]) + if(href_list["network_settings"]) var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) D.ui_interact(user) . = TOPIC_REFRESH @@ -39,6 +39,17 @@ if(CanInteract(user, DefaultTopicState())) filter_string = new_filter_string . = TOPIC_REFRESH + + //Tab expanding/collapsing + if(href_list["toggle_resources"]) + ui_expand_resources = !ui_expand_resources + . = TOPIC_REFRESH + if(href_list["toggle_queue"]) + ui_expand_queue = !ui_expand_queue + . = TOPIC_REFRESH + if(href_list["toggle_config"]) + ui_expand_config = !ui_expand_config + . = TOPIC_REFRESH /obj/machinery/fabricator/proc/try_cancel_build(var/datum/fabricator_build_order/order) if(istype(order) && currently_building != order && is_functioning()) diff --git a/code/modules/fabrication/fabricator_ui.dm b/code/modules/fabrication/fabricator_ui.dm index e1e89cedcef..ac49a86c487 100644 --- a/code/modules/fabrication/fabricator_ui.dm +++ b/code/modules/fabrication/fabricator_ui.dm @@ -1,94 +1,202 @@ #define PRINT_MULTIPLIER_DIVISOR 5 -/obj/machinery/fabricator/ui_interact(mob/user, ui_key = "fab", datum/nanoui/ui=null, force_open=1, var/master_ui = null, var/datum/topic_state/state = global.default_topic_state) - var/list/data = list() +//Allows overriding the default window size of the fabricator +/obj/machinery/fabricator/proc/get_fabricator_window_size() + return list("x" = 480, "y" = 410) + +//Can be overriden to use a different nano template file +/obj/machinery/fabricator/proc/get_nano_template() + return "fabricator.tmpl" + +//Returns a list of templates with the format "name" = "file.tmpl" to be loaded in addition to the main template. Name is used to access in the tmpl files. +/obj/machinery/fabricator/proc/get_extra_templates() + return list( + "net_shared" = "network_shared.tmpl", //Shared network UI stuff + "fab_shared" = "fabricator_shared.tmpl", //fab_shared should be included in all fabricator templates + ) + +/obj/machinery/fabricator/proc/ui_fabricator_resource_data() + var/material_storage = list() + for(var/material in storage_capacity) + var/list/material_data = list() + var/mat_name = capitalize(stored_substances_to_names[material]) + material_data["name"] = mat_name + material_data["stored"] = stored_material[material] ? stored_material[material] : 0 + material_data["max"] = storage_capacity[material] + material_data["unit"] = SHEET_UNIT + material_data["eject_key"] = stored_substances_to_names[material] + material_data["eject_label"] = ispath(material, /decl/material) ? "Eject" : "Flush" + material_storage += list(material_data) + return material_storage + +/obj/machinery/fabricator/proc/ui_fabricator_current_build_data() + var/list/current_build + if(currently_building) + current_build = list() + current_build["name"] = currently_building.target_recipe.name + current_build["multiplier"] = currently_building.multiplier + current_build["progress"] = "[100-round((currently_building.remaining_time/currently_building.target_recipe.build_time)*100)]%" + return current_build + +/obj/machinery/fabricator/proc/ui_fabricator_build_queue_data() + var/list/build_queue + for(var/datum/fabricator_build_order/order in queued_orders) + LAZYADD(build_queue, list(ui_fabricator_build_queue_entry_data(order))) + return build_queue + +/obj/machinery/fabricator/proc/ui_fabricator_build_queue_entry_data(var/datum/fabricator_build_order/order) + var/list/order_data + if(order) + order_data = list() + order_data["name"] = order.target_recipe.name + order_data["multiplier"] = order.multiplier + order_data["reference"] = "\ref[order]" + return order_data + +//Fill out the data for the displayed buildable designs +/obj/machinery/fabricator/proc/ui_fabricator_build_options_data() + var/list/build_options + for(var/datum/fabricator_recipe/R in design_cache) + if(R.hidden && !(fab_status_flags & FAB_HACKED)) + continue + if(show_category != "All" && show_category != R.category) + continue + if(filter_string && !findtextEx_char(lowertext(R.name), lowertext(filter_string))) + continue + LAZYADD(build_options, list(ui_fabricator_build_option_entry_data(R))) + return build_options + +//Fill out the data for a single build option +/obj/machinery/fabricator/proc/ui_fabricator_build_option_entry_data(var/datum/fabricator_recipe/R) + var/list/build_option = list() + var/max_sheets = (!length(R.resources)) ? 100 : 0 + var/list/material_costs = ui_fabricator_build_option_cost_list(R, max_sheets) + + build_option["name"] = R.name + build_option["reference"] = "\ref[R]" + build_option["illegal"] = R.hidden + + if(material_costs) + build_option["unavailable"] = !(material_costs["available"]) + var/list/mats = material_costs["materials"] + build_option["materials"] = length(mats) > 0? mats : null + + build_option["multiplier"] = ui_fabricator_build_option_entry_multiplier_data(R, max_sheets) + return build_option + +//Returns a list containing a boolean "available" to determine if we can build the recipe, and a list of resources costs () +/obj/machinery/fabricator/proc/ui_fabricator_build_option_cost_list(var/datum/fabricator_recipe/R, var/max_sheets) + //Make sure it's buildable and list required resources. + var/list/material_components = list() + var/has_missing_resource = FALSE + for(var/material_path in R.resources) + var/required_amount = round(R.resources[material_path] * mat_efficiency) + var/sheets = round(stored_material[material_path] / required_amount) + var/has_enough = TRUE + + if(max_sheets == 0 || max_sheets < sheets) + max_sheets = sheets + if(stored_material[material_path] < required_amount) + has_missing_resource = TRUE + has_enough = FALSE + + //Must make it a double list here or the fields are just overwriting eachothers + material_components += list(list( + "name" = stored_substances_to_names[material_path], + "amount" = required_amount, + "has_enough" = has_enough, + )) + return list("available" = !has_missing_resource && ui_fabricator_build_option_is_available(R, max_sheets), "materials" = material_components) +//Override to add more checks to make a build option unavailable. EX: if the machine requires a setting to be set first +/obj/machinery/fabricator/proc/ui_fabricator_build_option_is_available(var/datum/fabricator_recipe/R, var/max_sheets) + return TRUE + +/obj/machinery/fabricator/proc/ui_fabricator_build_option_entry_multiplier_data(var/datum/fabricator_recipe/R, var/max_sheets) + var/list/multiplier + if(R.max_amount >= PRINT_MULTIPLIER_DIVISOR && max_sheets >= PRINT_MULTIPLIER_DIVISOR) + multiplier = list() + for(var/i = 1 to FLOOR(min(R.max_amount, max_sheets)/PRINT_MULTIPLIER_DIVISOR)) + var/mult = i * PRINT_MULTIPLIER_DIVISOR + multiplier += list(list("label" = "x[mult]", "multiplier" = mult)) + return multiplier + +// +// UI sections data +// +/obj/machinery/fabricator/ui_data(mob/user, ui_key) + var/list/data = ..() + //Common fab data + data += ui_data_status(user, ui_key) //status is still displayed when not working for convenience + if(is_functioning()) + data += ui_data_resources(user, ui_key) + data += ui_data_queue(user, ui_key) + data += ui_data_config(user, ui_key) + data += ui_data_filter(user, ui_key) + data += ui_data_build_options(user, ui_key) + return data + +/obj/machinery/fabricator/proc/ui_data_status(mob/user, ui_key) + var/list/data = list() var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) - data["network"] = (D.network_id && D.network_tag) - data["category"] = show_category + data["network"] = (D.network_id && D.network_tag) + data["network_id"] = D.network_id data["functional"] = is_functioning() - data["filtering"] = filter_string || "No filter set." - - if(is_functioning()) - data["color_selectable"] = color_selectable - data["color"] = selected_color - - var/current_storage = list() - data["material_storage"] = current_storage - for(var/material in stored_material) - var/list/material_data = list() - var/mat_name = capitalize(stored_substances_to_names[material]) - material_data["name"] = mat_name - material_data["stored"] = "[stored_material[material]][SHEET_UNIT]" - material_data["max"] = storage_capacity[material] - material_data["eject_key"] = stored_substances_to_names[material] - material_data["eject_label"] = ispath(material, /decl/material) ? "Eject" : "Flush" - data["material_storage"] += list(material_data) - - var/list/current_build = list() - data["current_build"] = current_build - if(currently_building) - current_build["name"] = currently_building.target_recipe.name - current_build["multiplier"] = currently_building.multiplier - current_build["progress"] = "[100-round((currently_building.remaining_time/currently_building.target_recipe.build_time)*100)]%" - else - current_build["name"] = "Nothing." - current_build["multiplier"] = "-" - current_build["progress"] = "-" - - data["build_queue"] = list() - if(length(queued_orders)) - for(var/datum/fabricator_build_order/order in queued_orders) - var/list/order_data = list() - order_data["name"] = order.target_recipe.name - order_data["multiplier"] = order.multiplier - order_data["reference"] = "\ref[order]" - data["build_queue"] += list(order_data) - else - var/list/order_data = list() - order_data["name"] = "Nothing." - order_data["multiplier"] = "-" - data["build_queue"] += list(order_data) - - data["build_options"] = list() - for(var/datum/fabricator_recipe/R in design_cache) - if(R.hidden && !(fab_status_flags & FAB_HACKED)) - continue - if(show_category != "All" && show_category != R.category) - continue - if(filter_string && !findtextEx_char(lowertext(R.name), lowertext(filter_string))) - continue - var/list/build_option = list() - var/max_sheets = 0 - build_option["name"] = R.name - build_option["reference"] = "\ref[R]" - build_option["illegal"] = R.hidden - if(!length(R.resources)) - build_option["cost"] = "No resources required." - max_sheets = 100 - else - //Make sure it's buildable and list required resources. - var/list/material_components = list() - for(var/material in R.resources) - var/sheets = round(stored_material[material]/round(R.resources[material]*mat_efficiency)) - if(isnull(max_sheets) || max_sheets < sheets) - max_sheets = sheets - if(stored_material[material] < round(R.resources[material]*mat_efficiency)) - build_option["unavailable"] = 1 - material_components += "[round(R.resources[material] * mat_efficiency)][SHEET_UNIT] [stored_substances_to_names[material]]" - build_option["cost"] = "[capitalize(jointext(material_components, ", "))]." - if(R.max_amount >= PRINT_MULTIPLIER_DIVISOR && max_sheets >= PRINT_MULTIPLIER_DIVISOR) - build_option["multiplier"] = list() - for(var/i = 1 to FLOOR(min(R.max_amount, max_sheets)/PRINT_MULTIPLIER_DIVISOR)) - var/mult = i * PRINT_MULTIPLIER_DIVISOR - build_option["multiplier"] += list(list("label" = "x[mult]", "multiplier" = mult)) - data["build_options"] += list(build_option) + return data + +/obj/machinery/fabricator/proc/ui_data_resources(mob/user, ui_key) + var/list/data = list() + data["expand_resources"] = ui_expand_resources + data["material_storage"] = ui_fabricator_resource_data() + return data + +/obj/machinery/fabricator/proc/ui_data_queue(mob/user, ui_key) + var/list/data = list() + data["expand_queue"] = ui_expand_queue + data["current_build"] = ui_fabricator_current_build_data() + data["build_queue"] = ui_fabricator_build_queue_data() + return data +//Handles populating config data, meant to be overriden +/obj/machinery/fabricator/proc/ui_data_config(mob/user, ui_key) + var/list/data = list() + data["expand_config"] = ui_expand_config + data["skip_config"] = !ui_draw_config(user, ui_key) //Setting this to true just skip over drawing the config tab completely when its empty + data["color_selectable"] = color_selectable + data["color"] = selected_color + return data + +/obj/machinery/fabricator/proc/ui_data_filter(mob/user, ui_key) + var/list/data = list() + data["category"] = show_category + data["filtering"] = filter_string || "No filter set." + data["hide_categories"] = ui_nb_categories <= 1 //Only show categories if we have more than one category of things + return data + +/obj/machinery/fabricator/proc/ui_data_build_options(mob/user, ui_key) + var/list/data = list() + data["build_options"] = ui_fabricator_build_options_data() + return data + +//Shouldn't need to override this in subclasses. +/obj/machinery/fabricator/ui_interact(mob/user, ui_key = "fab", datum/nanoui/ui=null, force_open=1, var/master_ui = null, var/datum/topic_state/state = global.default_topic_state) + var/list/data = ui_data(user, ui_key) ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) - ui = new(user, src, ui_key, "fabricator.tmpl", "[capitalize(name)]", 480, 410, state = state) + var/list/window_size = get_fabricator_window_size() + ui = new(user, src, ui_key, get_nano_template(), "[capitalize(name)]", window_size["x"], window_size["y"], state = state) ui.set_initial_data(data) + + //Add extra templates + var/list/extratemplates = get_extra_templates() + for(var/key in extratemplates) + ui.add_template(key, extratemplates[key]) + ui.open() - ui.set_auto_update(1) + ui.set_auto_update(TRUE) + +//Returns whether we should bother drawing the config tab. Meant to be overriden +/obj/machinery/fabricator/proc/ui_draw_config(mob/user, ui_key) + return color_selectable //Only if we can pick a color by default #undef PRINT_MULTIPLIER_DIVISOR \ No newline at end of file diff --git a/code/modules/flufftext/TextFilters.dm b/code/modules/flufftext/TextFilters.dm index 4feb20f02bd..8805fe92da1 100644 --- a/code/modules/flufftext/TextFilters.dm +++ b/code/modules/flufftext/TextFilters.dm @@ -14,8 +14,8 @@ if(lowertext(newletter)=="a") newletter="ah" if(lowertext(newletter)=="c") newletter="k" switch(rand(1,7)) - if(1,3,5,8) newletter="[lowertext(newletter)]" - if(2,4,6,15) newletter="[uppertext(newletter)]" + if(1,3,5) newletter="[lowertext(newletter)]" + if(2,4,6) newletter="[uppertext(newletter)]" if(7) newletter+="'" //if(9,10) newletter="[newletter]" //if(11,12) newletter="[newletter]" diff --git a/code/modules/food/recipes/recipes_fryer.dm b/code/modules/food/recipes/recipes_fryer.dm index 8658b55c8a4..476e7f38f7f 100644 --- a/code/modules/food/recipes/recipes_fryer.dm +++ b/code/modules/food/recipes/recipes_fryer.dm @@ -35,6 +35,7 @@ result = /obj/item/chems/food/fishfingers /decl/recipe/fries + display_name = "potato fries" appliance = APPLIANCE_FRYER items = list( /obj/item/chems/food/rawsticks @@ -50,6 +51,7 @@ result = /obj/item/chems/food/onionrings /decl/recipe/jellydonut + display_name = "berry jelly donut" appliance = APPLIANCE_FRYER reagents = list(/decl/material/liquid/drink/juice/berry = 5, /decl/material/liquid/nutriment/sugar = 5) items = list( @@ -59,17 +61,19 @@ result = /obj/item/chems/food/donut/jelly /decl/recipe/jellydonut/cherry + display_name = "cherry jelly donut" reagents = list(/decl/material/liquid/nutriment/cherryjelly = 5, /decl/material/liquid/nutriment/sugar = 5) items = list( /obj/item/chems/food/dough ) - result = /obj/item/chems/food/donut/cherryjelly + result = /obj/item/chems/food/donut/jelly/cherry /decl/recipe/donut + display_name = "plain donut" appliance = APPLIANCE_FRYER reagents = list(/decl/material/liquid/nutriment/sugar = 5) items = list( /obj/item/chems/food/dough ) reagent_mix = REAGENT_REPLACE // simplify end product - result = /obj/item/chems/food/donut/normal \ No newline at end of file + result = /obj/item/chems/food/donut // isn't frosted \ No newline at end of file diff --git a/code/modules/food/recipes/recipes_mix.dm b/code/modules/food/recipes/recipes_mix.dm index 51658136efb..3366312b9e4 100644 --- a/code/modules/food/recipes/recipes_mix.dm +++ b/code/modules/food/recipes/recipes_mix.dm @@ -68,13 +68,14 @@ ) result = /obj/item/chems/food/classichotdog -/decl/recipe/meatburger +/decl/recipe/plainburger + display_name = "plain burger" appliance = APPLIANCE_MIX|APPLIANCE_MICROWAVE items = list( /obj/item/chems/food/bun, /obj/item/chems/food/cutlet ) - result = /obj/item/chems/food/meatburger + result = /obj/item/chems/food/burger /decl/recipe/brainburger appliance = APPLIANCE_MIX|APPLIANCE_MICROWAVE @@ -148,6 +149,7 @@ result = /obj/item/chems/food/bunbun /decl/recipe/hotdog + display_name = "plain hotdog" appliance = APPLIANCE_MIX|APPLIANCE_MICROWAVE items = list( /obj/item/chems/food/bun, @@ -174,7 +176,7 @@ /decl/recipe/spellburger appliance = APPLIANCE_MIX|APPLIANCE_MICROWAVE items = list( - /obj/item/chems/food/meatburger, + /obj/item/chems/food/burger, /obj/item/clothing/head/wizard, ) result = /obj/item/chems/food/spellburger @@ -182,7 +184,7 @@ /decl/recipe/bigbiteburger appliance = APPLIANCE_MIX|APPLIANCE_MICROWAVE items = list( - /obj/item/chems/food/meatburger, + /obj/item/chems/food/burger, /obj/item/chems/food/meat = 2, /obj/item/chems/food/egg, ) @@ -190,6 +192,7 @@ result = /obj/item/chems/food/bigbiteburger /decl/recipe/sandwich + display_name = "plain sandwich" appliance = APPLIANCE_MICROWAVE|APPLIANCE_MIX items = list( /obj/item/chems/food/meatsteak, @@ -205,6 +208,7 @@ result = /obj/item/chems/food/boiledrice/chazuke /decl/recipe/taco + display_name = "meat taco" appliance = APPLIANCE_MIX|APPLIANCE_MICROWAVE items = list( /obj/item/chems/food/doughslice, diff --git a/code/modules/food/recipes/recipes_oven.dm b/code/modules/food/recipes/recipes_oven.dm index d213b8a0dc3..3d04e06dd7b 100644 --- a/code/modules/food/recipes/recipes_oven.dm +++ b/code/modules/food/recipes/recipes_oven.dm @@ -15,12 +15,14 @@ warm_up(being_cooked) /decl/recipe/donkpocket/rawmeat + display_name = "Raw Donk-Pocket" items = list( /obj/item/chems/food/doughslice, /obj/item/chems/food/rawmeatball ) /decl/recipe/donkpocket/warm + display_name = "Warm Donk-Pocket" appliance = APPLIANCE_OVEN | APPLIANCE_MICROWAVE reagents = list() //This is necessary since this is a child object of the above recipe and we don't want donk pockets to need flour items = list( @@ -44,6 +46,7 @@ return list(being_cooked) /decl/recipe/meatbread + display_name = "plain meatbread loaf" appliance = APPLIANCE_OVEN items = list( /obj/item/chems/food/dough = 2, @@ -72,6 +75,7 @@ result = /obj/item/chems/food/sliceable/bananabread /decl/recipe/muffin + display_name = "plain muffin" appliance = APPLIANCE_OVEN reagents = list(/decl/material/liquid/nutriment/batter/cakebatter = 10) reagent_mix = REAGENT_REPLACE // simplify end product @@ -129,6 +133,7 @@ result = /obj/item/chems/food/spacylibertyduff /decl/recipe/cookie + display_name = "plain cookie" appliance = APPLIANCE_OVEN reagents = list(/decl/material/liquid/nutriment/batter/cakebatter = 5, /decl/material/liquid/nutriment/coco = 5) reagent_mix = REAGENT_REPLACE // Don't include the cakebatter @@ -270,6 +275,7 @@ result = /obj/item/chems/food/baguette /decl/recipe/bun + display_name = "plain bun" appliance = APPLIANCE_OVEN items = list( /obj/item/chems/food/dough @@ -277,6 +283,7 @@ result = /obj/item/chems/food/bun /decl/recipe/flatbread + display_name = "plain flatbread" appliance = APPLIANCE_OVEN items = list( /obj/item/chems/food/sliceable/flatdough @@ -284,6 +291,7 @@ result = /obj/item/chems/food/flatbread /decl/recipe/bread + display_name = "loaf of bread" appliance = APPLIANCE_OVEN items = list( /obj/item/chems/food/dough = 4 @@ -312,6 +320,7 @@ result = /obj/item/chems/food/sliceable/pumpkinpie /decl/recipe/plumphelmetbiscuit + display_name = "plain plump helmet biscuit" appliance = APPLIANCE_OVEN fruit = list("plumphelmet" = 1) reagents = list(/decl/material/liquid/nutriment/batter = 10) @@ -319,6 +328,7 @@ result = /obj/item/chems/food/plumphelmetbiscuit /decl/recipe/plumphelmetbiscuitvegan + display_name = "vegan plump helmet biscuit" appliance = APPLIANCE_OVEN fruit = list("plumphelmet" = 1) reagents = list(/decl/material/liquid/nutriment/flour = 10, /decl/material/liquid/water = 10) diff --git a/code/modules/food/recipes/recipes_stove.dm b/code/modules/food/recipes/recipes_stove.dm index 447b7350600..7625721da51 100644 --- a/code/modules/food/recipes/recipes_stove.dm +++ b/code/modules/food/recipes/recipes_stove.dm @@ -15,6 +15,7 @@ result = /obj/item/chems/food/chocolateegg /decl/recipe/sausage + display_name = "plain sausage" appliance = APPLIANCE_SKILLET items = list( /obj/item/chems/food/rawmeatball, @@ -53,6 +54,7 @@ reagents = list(/decl/material/liquid/blood = 30) reagent_mix = REAGENT_REPLACE // simplify end product result = /obj/item/chems/food/bloodsoup + hidden_from_codex = TRUE /decl/recipe/mysterysoup appliance = APPLIANCE_MICROWAVE|APPLIANCE_SAUCEPAN|APPLIANCE_POT @@ -173,6 +175,7 @@ result = /obj/item/chems/food/pelmeni_boiled /decl/recipe/meatball + display_name = "cooked meatball" appliance = APPLIANCE_SKILLET|APPLIANCE_MICROWAVE items = list( /obj/item/chems/food/rawmeatball @@ -225,12 +228,14 @@ result = /obj/item/chems/food/waffles /decl/recipe/pancakes + display_name = "plain pancakes" appliance = APPLIANCE_SKILLET reagents = list(/decl/material/liquid/nutriment/batter = 20) reagent_mix = REAGENT_REPLACE // the batter should be cooked result = /obj/item/chems/food/pancakes /decl/recipe/pancakes/blu + display_name = null // autoset fruit = list("blueberries" = 2) result = /obj/item/chems/food/pancakesblu diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index a9751b1243f..c3e7963161d 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -42,14 +42,14 @@ reagents.clear_reagents() // Fill the object up with the appropriate reagents. for(var/rid in seed.chems) - var/list/reagent_data = seed.chems[rid] - if(reagent_data && reagent_data.len) - var/rtotal = reagent_data[1] - var/list/data = list() - if(reagent_data.len > 1 && potency > 0) - rtotal += round(potency/reagent_data[2]) + var/list/reagent_amounts = seed.chems[rid] + if(LAZYLEN(reagent_amounts)) + var/rtotal = reagent_amounts[1] + var/list/data = null + if(LAZYACCESS(reagent_amounts,2) && potency > 0) + rtotal += round(potency/reagent_amounts[2]) if(rid == /decl/material/liquid/nutriment) - data[seed.seed_name] = max(1,rtotal) + LAZYSET(data, seed.seed_name, max(1,rtotal)) reagents.add_reagent(rid,max(1,rtotal),data) update_desc() if(reagents.total_volume > 0) diff --git a/code/modules/hydroponics/seed.dm b/code/modules/hydroponics/seed.dm index a5ce42a70a5..8ff7425075c 100644 --- a/code/modules/hydroponics/seed.dm +++ b/code/modules/hydroponics/seed.dm @@ -200,40 +200,42 @@ environment.adjust_gas(/decl/material/gas/carbon_dioxide, -req_CO2_moles, 1) environment.adjust_gas(/decl/material/gas/oxygen, req_CO2_moles, 1) +/datum/seed/proc/make_splat(var/turf/T, var/obj/item/thrown) + if(!splat_type || (locate(splat_type) in T)) + return + var/atom/splat = new splat_type(T, src) + splat.SetName("[seed_name] [pick("smear","smudge","splatter")]") + if(get_trait(TRAIT_BIOLUM)) + var/clr + if(get_trait(TRAIT_BIOLUM_COLOUR)) + clr = get_trait(TRAIT_BIOLUM_COLOUR) + splat.set_light(get_trait(TRAIT_BIOLUM), l_color = clr) + splat.color = get_trait(TRAIT_FLESH_COLOUR) || get_trait(TRAIT_PRODUCT_COLOUR) + //Splatter a turf. -/datum/seed/proc/splatter(var/turf/T,var/obj/item/thrown) - if(splat_type && !(locate(/obj/effect/vine) in T)) - var/obj/effect/vine/splat = new splat_type(T, src) - if(!istype(splat)) // Plants handle their own stuff. - splat.SetName("[thrown.name] [pick("smear","smudge","splatter")]") - if(get_trait(TRAIT_BIOLUM)) - var/clr - if(get_trait(TRAIT_BIOLUM_COLOUR)) - clr = get_trait(TRAIT_BIOLUM_COLOUR) - splat.set_light(get_trait(TRAIT_BIOLUM), l_color = clr) - var/flesh_colour = get_trait(TRAIT_FLESH_COLOUR) - if(!flesh_colour) flesh_colour = get_trait(TRAIT_PRODUCT_COLOUR) - if(flesh_colour) splat.color = get_trait(TRAIT_PRODUCT_COLOUR) - - if(chems) - for(var/mob/living/M in T.contents) - if(!M.reagents) - continue - var/body_coverage = SLOT_HEAD|SLOT_FACE|SLOT_EYES|SLOT_UPPER_BODY|SLOT_LOWER_BODY|SLOT_LEGS|SLOT_FEET|SLOT_ARMS|SLOT_HANDS - var/held_items = M.get_held_items() - for(var/obj/item/clothing/clothes in M) - if(clothes in held_items) - continue - body_coverage &= ~(clothes.body_parts_covered) - if(!body_coverage) - continue - var/datum/reagents/R = M.reagents - var/mob/living/carbon/human/H = M - if(istype(H)) - R = H.get_contact_reagents() - if(istype(R)) - for(var/chem in chems) - R.add_reagent(chem,min(5,max(1,get_trait(TRAIT_POTENCY)/3))) +//Thrown can be null, but T cannot. +/datum/seed/proc/splatter(var/turf/T, var/obj/item/thrown) + make_splat(T, thrown) + + var/datum/reagents/splat_reagents = thrown?.reagents + if(!splat_reagents?.maximum_volume) // if thrown doesn't exist or has no reagents, use the seed's default reagents. + splat_reagents = new /datum/reagents(INFINITY, global.temp_reagents_holder) + var/potency = get_trait(TRAIT_POTENCY) + for(var/rid in chems) + var/list/reagent_amounts = chems[rid] + if(LAZYLEN(reagent_amounts)) + var/rtotal = reagent_amounts[1] + var/list/data = null + if(reagent_amounts?[2] && potency > 0) + rtotal += round(potency/reagent_amounts[2]) + if(rid == /decl/material/liquid/nutriment) + LAZYSET(data, seed_name, max(1,rtotal)) + splat_reagents.add_reagent(rid,max(1,rtotal),data) + if(splat_reagents) + var/splat_range = min(10,max(1,get_trait(TRAIT_POTENCY)/15)) + splat_reagents.splash_area(T, range = splat_range) + qdel(splat_reagents) + qdel(thrown) //Applies an effect to a target atom. /datum/seed/proc/thrown_at(var/obj/item/thrown,var/atom/target, var/force_explode) @@ -242,65 +244,22 @@ var/turf/origin_turf = get_turf(target) if(force_explode || get_trait(TRAIT_EXPLOSIVE)) - create_spores(origin_turf) - - var/flood_dist = min(10,max(1,get_trait(TRAIT_POTENCY)/15)) - var/list/open_turfs = list() - var/list/closed_turfs = list() - var/list/valid_turfs = list() - open_turfs |= origin_turf - - // Flood fill to get affected turfs. - while(open_turfs.len) - var/turf/T = pick(open_turfs) - open_turfs -= T - closed_turfs |= T - valid_turfs |= T - - for(var/dir in global.alldirs) - var/turf/neighbor = get_step(T,dir) - if(!neighbor || (neighbor in closed_turfs) || (neighbor in open_turfs)) - continue - if(neighbor.density || get_dist(neighbor,origin_turf) > flood_dist || isspaceturf(neighbor)) - closed_turfs |= neighbor - continue - // Check for windows. - var/no_los - var/turf/last_turf = origin_turf - for(var/turf/target_turf in getline(origin_turf,neighbor)) - if(!last_turf.Enter(target_turf) || target_turf.density) - no_los = 1 - break - last_turf = target_turf - if(!no_los && !origin_turf.Enter(neighbor)) - no_los = 1 - if(no_los) - closed_turfs |= neighbor - continue - open_turfs |= neighbor - - for(var/turf/T in valid_turfs) - for(var/mob/living/M in T.contents) - apply_special_effect(M) - splatter(T,thrown) if(origin_turf) - origin_turf.visible_message("The [thrown.name] explodes!") - qdel(thrown) + origin_turf.visible_message(SPAN_DANGER("\The [thrown] explodes!")) + splatter(origin_turf,thrown) return if(istype(target,/mob/living)) splatted = apply_special_effect(target,thrown) else if(isturf(target)) - splatted = 1 - for(var/mob/living/M in target.contents) - apply_special_effect(M) + for(var/mob/living/M in target) + splatted |= apply_special_effect(M, thrown) if(get_trait(TRAIT_JUICY) && splatted) - splatter(origin_turf,thrown) if(origin_turf) - origin_turf.visible_message("The [thrown.name] splatters against [target]!") - qdel(thrown) + origin_turf.visible_message(SPAN_DANGER("\The [thrown] splatters against [target]!")) + splatter(origin_turf,thrown) /datum/seed/proc/handle_environment(var/turf/current_turf, var/datum/gas_mixture/environment, var/light_supplied, var/check_only) @@ -363,8 +322,6 @@ return health_change /datum/seed/proc/apply_special_effect(var/mob/living/target,var/obj/item/thrown) - - var/impact = 1 do_sting(target,thrown) do_thorns(target,thrown) @@ -380,9 +337,7 @@ spark_at(target, cardinal_only = TRUE) new/obj/effect/decal/cleanable/molten_item(get_turf(target)) // Leave a pile of goo behind for dramatic effect... target.forceMove(T) // And teleport them to the chosen location. - impact = 1 - - return impact + return TRUE /datum/seed/proc/generate_name() var/prefix = "" diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm index 825c98a8d6e..1faf254b670 100644 --- a/code/modules/hydroponics/seed_datums.dm +++ b/code/modules/hydroponics/seed_datums.dm @@ -390,6 +390,12 @@ splat_type = /obj/effect/vine kitchen_tag = "mushroom" +/datum/seed/mushroom/make_splat(var/turf/T, var/obj/item/thrown) + if(!splat_type || (locate(splat_type) in T)) + return + new splat_type(T, src) + // No further logic; the vine will handle it. + /datum/seed/mushroom/New() ..() set_trait(TRAIT_MATURATION,7) diff --git a/code/modules/keybindings/_defines.dm b/code/modules/keybindings/_defines.dm index 128e012ff3d..469a4f57fc2 100644 --- a/code/modules/keybindings/_defines.dm +++ b/code/modules/keybindings/_defines.dm @@ -1,7 +1,7 @@ #define CATEGORY_CLIENT "CLIENT" -#define CATEGORY_EMOTE "EMOTE" #define CATEGORY_ADMIN "ADMIN" -#define CATEGORY_CARBON "CARBON" +#define CATEGORY_MOB "MOB" +#define CATEGORY_LIVING "LIVING" #define CATEGORY_HUMAN "HUMAN" #define CATEGORY_ROBOT "ROBOT" #define CATEGORY_MISC "MISC" diff --git a/code/modules/keybindings/_keybindings.dm b/code/modules/keybindings/_keybindings.dm index 00e93993068..d23b116879e 100644 --- a/code/modules/keybindings/_keybindings.dm +++ b/code/modules/keybindings/_keybindings.dm @@ -1,9 +1,17 @@ /datum/keybinding + /// A default hotkey keys. var/list/hotkey_keys + /// A classic hotkey keys when client don't use hotkey mode. Uses `hotkey_keys` if not defined. var/list/classic_keys + + /// A unique keybind id for preference storing. var/name + /// A full keybind name for displaying. var/full_name - var/description = "" + /// A bit informative description what this keybind does. + var/description + + /// A keybind category for sorting in preference menu. var/category = CATEGORY_MISC /datum/keybinding/New() diff --git a/code/modules/keybindings/admin.dm b/code/modules/keybindings/admin.dm index 657e31de302..9b433212567 100644 --- a/code/modules/keybindings/admin.dm +++ b/code/modules/keybindings/admin.dm @@ -4,14 +4,24 @@ /datum/keybinding/admin/can_use(client/user) return !!user.holder -/datum/keybinding/admin/admin_say +/datum/keybinding/admin/mod_say hotkey_keys = list("F5") + name = "mod_say" + full_name = "Mod Say" + description = "Talk with other moderators." + +/datum/keybinding/admin/mod_say/down(client/user) + user.cmd_mod_say(input(user, null, "dsay \"text\"") as text|null) + return TRUE + +/datum/keybinding/admin/admin_say + hotkey_keys = list("ShiftF5") name = "admin_say" full_name = "Admin Say" description = "Talk with other admins." /datum/keybinding/admin/admin_say/down(client/user) - user.get_admin_say() + user.cmd_admin_say(input(user, null, "asay \"text\"") as text|null) return TRUE /datum/keybinding/admin/admin_ghost @@ -61,11 +71,11 @@ description = "Allows you to send a message to dead chat" /datum/keybinding/admin/dead_say/down(client/user) - user.get_dead_say() + user.dsay(input(src, null, "dsay \"text\"") as text|null) return TRUE /datum/keybinding/admin/deadmin - hotkey_keys = list("None") + hotkey_keys = list("Unbound") name = "deadmin" full_name = "De-Admin" description = "Shed your admin powers" @@ -75,7 +85,7 @@ return TRUE /datum/keybinding/admin/readmin - hotkey_keys = list("None") + hotkey_keys = list("Unbound") name = "readmin" full_name = "Re-Admin" description = "Regain your admin powers" @@ -83,11 +93,3 @@ /datum/keybinding/admin/readmin/down(client/user) user.readmin_self() return TRUE - -/client/proc/get_admin_say() - var/msg = input(src, null, "asay \"text\"") as text|null - cmd_admin_say(msg) - -/client/proc/get_dead_say() - var/msg = input(src, null, "dsay \"text\"") as text - dsay(msg) diff --git a/code/modules/keybindings/carbon.dm b/code/modules/keybindings/carbon.dm deleted file mode 100644 index 208b1eeb522..00000000000 --- a/code/modules/keybindings/carbon.dm +++ /dev/null @@ -1,67 +0,0 @@ -/datum/keybinding/carbon - category = CATEGORY_CARBON - -/datum/keybinding/carbon/can_use(client/user) - return iscarbon(user.mob) - -/datum/keybinding/carbon/toggle_throw_mode - hotkey_keys = list("R", "Southwest") // PAGEDOWN - name = "toggle_throw_mode" - full_name = "Toggle Throw Mode" - description = "Toggle throwing the current item or not" - -/datum/keybinding/carbon/toggle_throw_mode/down(client/user) - var/mob/living/carbon/C = user.mob - C.toggle_throw_mode() - return TRUE - -/datum/keybinding/carbon/select_help_intent - hotkey_keys = list("1") - name = "select_help_intent" - full_name = "Select Help Intent" - description = "" - -/datum/keybinding/carbon/select_help_intent/down(client/user) - user.mob?.a_intent_change(I_HELP) - return TRUE - -/datum/keybinding/carbon/select_disarm_intent - hotkey_keys = list("2") - name = "select_disarm_intent" - full_name = "Select Disarm Intent" - description = "" - -/datum/keybinding/carbon/select_disarm_intent/down(client/user) - user.mob?.a_intent_change(I_DISARM) - return TRUE - -/datum/keybinding/carbon/select_grab_intent - hotkey_keys = list("3") - name = "select_grab_intent" - full_name = "Select Grab Intent" - description = "" - -/datum/keybinding/carbon/select_grab_intent/down(client/user) - user.mob?.a_intent_change(I_GRAB) - return TRUE - -/datum/keybinding/carbon/select_harm_intent - hotkey_keys = list("4") - name = "select_harm_intent" - full_name = "Select Harm Intent" - description = "" - -/datum/keybinding/carbon/select_harm_intent/down(client/user) - user.mob?.a_intent_change(I_HURT) - return TRUE - -/datum/keybinding/carbon/swap_hands - hotkey_keys = list("X", "Northeast") // PAGEUP - name = "swap_hands" - full_name = "Swap Hands" - description = "" - -/datum/keybinding/carbon/swap_hands/down(client/user) - var/mob/living/carbon/C = user.mob - C.swap_hand() - return TRUE diff --git a/code/modules/keybindings/client.dm b/code/modules/keybindings/client.dm index 62f2ba5efb2..e6f5ea8ec35 100644 --- a/code/modules/keybindings/client.dm +++ b/code/modules/keybindings/client.dm @@ -1,52 +1,52 @@ /datum/keybinding/client category = CATEGORY_CLIENT +/datum/keybinding/client/hotkey_mode + hotkey_keys = list("Tab") + name = "hotkey_mode" + full_name = "Toggle Hotkeys" + +/datum/keybinding/client/hotkey_mode/down(client/user) + if(user.prefs) + user.prefs.hotkeys = !user.prefs.hotkeys + if(user.prefs.hotkeys) + winset(user, null, "outputwindow.input.background-color=[COLOR_INPUT_DISABLED];mapwindow.map.focus=true") + else + winset(user, null, "outputwindow.input.background-color=[COLOR_INPUT_ENABLED];outputwindow.input.focus=true") + return TRUE + /datum/keybinding/client/admin_help hotkey_keys = list("F1") name = "admin_help" full_name = "Admin Help" description = "Ask an admin for help" -/datum/keybinding/client/admin_help/down(client/user) - user.adminhelp() - return TRUE - /datum/keybinding/client/screenshot - hotkey_keys = list("None") + hotkey_keys = list("Unbound") name = "screenshot" - full_name = "Screenshot" + full_name = "Save screenshot as..." description = "Take a screenshot" /datum/keybinding/client/screenshot/down(client/user) - winset(user, null, "command=.screenshot [!user.keys_held["shift"] ? "auto" : ""]") - return TRUE - -/datum/keybinding/client/fit_viewport - hotkey_keys = list("CtrlF11") - name = "fit_viewport" - full_name = "Fit Viewport" - description = "Fits your viewport" - -/datum/keybinding/client/fit_viewport/down(client/user) - user.fit_viewport() + winset(user, null, "command=.screenshot") return TRUE /datum/keybinding/client/toggle_fullscreen hotkey_keys = list("F11") name = "toggle_fullscreen" full_name = "Toggle Fullscreen" - description = "Take a screenshot" + description = "Toggles fullscreen mode" /datum/keybinding/client/toggle_fullscreen/down(client/user) - user.toggle_fullscreen() + user.cycle_preference(/datum/client_preference/fullscreen_mode) return TRUE -/datum/keybinding/client/minimal_hud - hotkey_keys = list("F12") - name = "minimal_hud" - full_name = "Minimal HUD" - description = "Hide most HUD features" +/datum/keybinding/client/fit_viewport + hotkey_keys = list("ShiftF11") + name = "fit_viewport" + full_name = "Fit Viewport" + description = "Fits your viewport" -/datum/keybinding/client/minimal_hud/down(client/user) - user.mob.button_pressed_F12() +/datum/keybinding/client/fit_viewport/down(client/user) + user.fit_viewport() return TRUE diff --git a/code/modules/keybindings/communication.dm b/code/modules/keybindings/communication.dm index fc065971e78..9d6fadf4a1c 100644 --- a/code/modules/keybindings/communication.dm +++ b/code/modules/keybindings/communication.dm @@ -1,22 +1,22 @@ /datum/keybinding/client/communication category = CATEGORY_COMMUNICATION -/datum/keybinding/client/communication/say - hotkey_keys = list("F3", "T") - name = "Say" - full_name = "IC Say" - /datum/keybinding/client/communication/ooc - hotkey_keys = list("F2", "O") - name = "OOC" + hotkey_keys = list("O", "F2") + name = "ooc" full_name = "Out Of Character Say (OOC)" /datum/keybinding/client/communication/looc hotkey_keys = list("L") - name = "LOOC" + name = "looc" full_name = "Local Out Of Character Say (LOOC)" +/datum/keybinding/client/communication/say + hotkey_keys = list("T", "F3") + name = "say" + full_name = "IC Say" + /datum/keybinding/client/communication/me - hotkey_keys = list("F4", "M") - name = "Me" + hotkey_keys = list("M", "F4") + name = "me" full_name = "Custom Emote (/Me)" diff --git a/code/modules/keybindings/human.dm b/code/modules/keybindings/human.dm index b5cf427cc83..9cddab5e8c2 100644 --- a/code/modules/keybindings/human.dm +++ b/code/modules/keybindings/human.dm @@ -54,7 +54,7 @@ return TRUE /datum/keybinding/human/give - hotkey_keys = list("None") + hotkey_keys = list("G") name = "give_item" full_name = "Give Item" description = "Give the item you're currently holding" diff --git a/code/modules/keybindings/living.dm b/code/modules/keybindings/living.dm index 67dea8f3124..334811f9524 100644 --- a/code/modules/keybindings/living.dm +++ b/code/modules/keybindings/living.dm @@ -1,5 +1,5 @@ /datum/keybinding/living - category = CATEGORY_HUMAN + category = CATEGORY_LIVING /datum/keybinding/living/can_use(client/user) return isliving(user.mob) @@ -25,14 +25,3 @@ var/mob/living/L = user.mob L.resist() return TRUE - -/datum/keybinding/living/drop_item - hotkey_keys = list("Q", "Northwest") // HOME - name = "drop_item" - full_name = "Drop Item" - description = "" - -/datum/keybinding/living/drop_item/down(client/user) - var/mob/living/L = user.mob - L.drop_item() - return TRUE diff --git a/code/modules/keybindings/mob.dm b/code/modules/keybindings/mob.dm index 5e19e0f97ad..87f1b581e26 100644 --- a/code/modules/keybindings/mob.dm +++ b/code/modules/keybindings/mob.dm @@ -1,29 +1,103 @@ /datum/keybinding/mob - category = CATEGORY_HUMAN + category = CATEGORY_MOB /datum/keybinding/mob/can_use(client/user) - return ismob(user.mob) ? TRUE : FALSE + return ismob(user.mob) + +/datum/keybinding/mob/toggle_throw_mode + hotkey_keys = list("R", "Southwest") + name = "toggle_throw_mode" + full_name = "Toggle Throw mode" + description = "Toggle throwing the current item or not." + +/datum/keybinding/mob/toggle_throw_mode/down(client/user) + user.mob.toggle_throw_mode() + return TRUE + +/datum/keybinding/mob/hold_throw_mode + hotkey_keys = list("Space") + name = "hold_throw_mode" + full_name = "Hold throw mode" + description = "Hold this to turn on throw mode, and release it to turn off throw mode" + +/datum/keybinding/mob/hold_throw_mode/down(client/user) + user.mob.throw_mode_on() + return TRUE + +/datum/keybinding/mob/hold_throw_mode/up(client/user) + user.mob.throw_mode_off() + return TRUE + +/datum/keybinding/mob/swap_hands + hotkey_keys = list("X", "Northeast") + name = "swap_hands" + full_name = "Swap Hands" + +/datum/keybinding/mob/swap_hands/down(client/user) + user.mob.swap_hand() + return TRUE + +/datum/keybinding/mob/drop_item + hotkey_keys = list("Q", "Northwest") + name = "drop_item" + full_name = "Drop Item" + +/datum/keybinding/mob/drop_item/down(client/user) + user.mob.drop_item() + return TRUE + +/datum/keybinding/mob/select_help_intent + hotkey_keys = list("1") + name = "select_help_intent" + full_name = "Select Help Intent" + +/datum/keybinding/mob/select_help_intent/down(client/user) + user.mob.a_intent_change(I_HELP) + return TRUE + +/datum/keybinding/mob/select_disarm_intent + hotkey_keys = list("2") + name = "select_disarm_intent" + full_name = "Select Disarm Intent" + +/datum/keybinding/mob/select_disarm_intent/down(client/user) + user.mob.a_intent_change(I_DISARM) + return TRUE + +/datum/keybinding/mob/select_grab_intent + hotkey_keys = list("3") + name = "select_grab_intent" + full_name = "Select Grab Intent" + +/datum/keybinding/mob/select_grab_intent/down(client/user) + user.mob.a_intent_change(I_GRAB) + return TRUE + +/datum/keybinding/mob/select_harm_intent + hotkey_keys = list("4") + name = "select_harm_intent" + full_name = "Select Harm Intent" + +/datum/keybinding/mob/select_harm_intent/down(client/user) + user.mob.a_intent_change(I_HURT) + return TRUE /datum/keybinding/mob/cycle_intent_right hotkey_keys = list("G", "Insert") name = "cycle_intent_right" full_name = "Сycle Intent: Right" - description = "" /datum/keybinding/mob/cycle_intent_right/down(client/user) - var/mob/M = user.mob - M.a_intent_change(INTENT_HOTKEY_RIGHT) + user.mob.a_intent_change(INTENT_HOTKEY_RIGHT) return TRUE /datum/keybinding/mob/cycle_intent_left hotkey_keys = list("F") name = "cycle_intent_left" full_name = "Сycle Intent: Left" - description = "" /datum/keybinding/mob/cycle_intent_left/down(client/user) - var/mob/M = user.mob - M.a_intent_change(INTENT_HOTKEY_LEFT) + user.mob.a_intent_change(INTENT_HOTKEY_LEFT) return TRUE /datum/keybinding/mob/activate_inhand @@ -33,15 +107,13 @@ description = "Uses whatever item you have inhand" /datum/keybinding/mob/activate_inhand/down(client/user) - var/mob/M = user.mob - M.mode() + user.mob.mode() return TRUE /datum/keybinding/mob/target_head_cycle hotkey_keys = list("Numpad8") name = "target_head_cycle" full_name = "Target: Cycle Head" - description = "" /datum/keybinding/mob/target_head_cycle/down(client/user) user.body_toggle_head() @@ -51,7 +123,6 @@ hotkey_keys = list("Numpad4") name = "target_r_arm" full_name = "Target: Right Arm" - description = "" /datum/keybinding/mob/target_r_arm/down(client/user) user.body_r_arm() @@ -61,7 +132,6 @@ hotkey_keys = list("Numpad5") name = "target_body_chest" full_name = "Target: Body" - description = "" /datum/keybinding/mob/target_body_chest/down(client/user) user.body_chest() @@ -71,7 +141,6 @@ hotkey_keys = list("Numpad6") name = "target_left_arm" full_name = "Target: Left Arm" - description = "" /datum/keybinding/mob/target_left_arm/down(client/user) user.body_l_arm() @@ -81,7 +150,6 @@ hotkey_keys = list("Numpad1") name = "target_right_leg" full_name = "Target: Right leg" - description = "" /datum/keybinding/mob/target_right_leg/down(client/user) user.body_r_leg() @@ -91,7 +159,6 @@ hotkey_keys = list("Numpad2") name = "target_body_groin" full_name = "Target: Groin" - description = "" /datum/keybinding/mob/target_body_groin/down(client/user) user.body_groin() @@ -101,42 +168,17 @@ hotkey_keys = list("Numpad3") name = "target_left_leg" full_name = "Target: Left Leg" - description = "" /datum/keybinding/mob/target_left_leg/down(client/user) user.body_l_leg() return TRUE -/datum/keybinding/mob/prevent_movement - hotkey_keys = list("Ctrl") - name = "block_movement" - full_name = "Block movement" - description = "Prevents you from moving" - -/datum/keybinding/mob/prevent_movement/down(client/user) - user.movement_locked = TRUE - return TRUE +/datum/keybinding/mob/minimal_hud + hotkey_keys = list("F12") + name = "minimal_hud" + full_name = "Minimal HUD" + description = "Hide most HUD features" -/datum/keybinding/mob/prevent_movement/up(client/user) - user.movement_locked = FALSE +/datum/keybinding/mob/minimal_hud/down(client/user) + user.mob.minimize_hud() return TRUE - -/datum/keybinding/mob/move_up - hotkey_keys = list(",") - name = "move_up" - full_name = "Move Up" - description = "Makes you go up" - -/datum/keybinding/mob/move_up/down(client/user) - var/mob/M = user.mob - M.move_up() - -/datum/keybinding/mob/move_down - hotkey_keys = list(".") - name = "move_down" - full_name = "Move Down" - description = "Makes you go down" - -/datum/keybinding/mob/move_down/down(client/user) - var/mob/M = user.mob - M.move_down() diff --git a/code/modules/keybindings/movement.dm b/code/modules/keybindings/movement.dm index 91ac0de44a3..df313a4fd83 100644 --- a/code/modules/keybindings/movement.dm +++ b/code/modules/keybindings/movement.dm @@ -3,28 +3,66 @@ /datum/keybinding/movement/north hotkey_keys = list("W", "North") - name = "North" + classic_keys = list("North") + name = "north" full_name = "Move North" description = "Moves your character north" /datum/keybinding/movement/south hotkey_keys = list("S", "South") - name = "South" + classic_keys = list("South") + name = "south" full_name = "Move South" description = "Moves your character south" /datum/keybinding/movement/west hotkey_keys = list("A", "West") - name = "West" + classic_keys = list("West") + name = "west" full_name = "Move West" description = "Moves your character left" /datum/keybinding/movement/east hotkey_keys = list("D", "East") - name = "East" + classic_keys = list("East") + name = "east" full_name = "Move East" description = "Moves your character east" +/datum/keybinding/movement/prevent_movement + hotkey_keys = list("Ctrl") + name = "block_movement" + full_name = "Block movement" + description = "Prevents you from moving" + +/datum/keybinding/movement/prevent_movement/down(client/user) + user.movement_locked = TRUE + return TRUE + +/datum/keybinding/movement/prevent_movement/up(client/user) + user.movement_locked = FALSE + return TRUE + +/datum/keybinding/movement/move_up + hotkey_keys = list(",") + name = "move_up" + full_name = "Move Up" + description = "Makes you go up" + +/datum/keybinding/movement/move_up/down(client/user) + user.mob.move_up() + return TRUE + +/datum/keybinding/movement/move_down + hotkey_keys = list(".") + name = "move_down" + full_name = "Move Down" + description = "Makes you go down" + +/datum/keybinding/movement/move_down/down(client/user) + user.mob.move_down() + return TRUE + /datum/keybinding/movement/move_quickly hotkey_keys = list("Shift") name = "moving_quickly" diff --git a/code/modules/keybindings/robot.dm b/code/modules/keybindings/robot.dm index e9a7376aa72..800078894b3 100644 --- a/code/modules/keybindings/robot.dm +++ b/code/modules/keybindings/robot.dm @@ -44,8 +44,7 @@ description = "Cycles the intent left" /datum/keybinding/robot/intent_cycle/down(client/user) - var/mob/living/silicon/robot/R = user.mob - R.a_intent_change(INTENT_HOTKEY_LEFT) + user.mob.a_intent_change(INTENT_HOTKEY_LEFT) return TRUE /datum/keybinding/robot/module_cycle diff --git a/code/modules/keybindings/setup.dm b/code/modules/keybindings/setup.dm index b122f23f479..846dd32d3ad 100644 --- a/code/modules/keybindings/setup.dm +++ b/code/modules/keybindings/setup.dm @@ -41,19 +41,16 @@ // byond bug ID:2694120 /client/verb/reset_macros_wrapper() - set category = "OOC" set name = "Fix Keybindings" - + set category = "OOC" reset_macros() -/client/proc/reset_macros(skip_alert = FALSE) - var/ans - if(!skip_alert) - ans = alert(src, "Change your keyboard language to ENG and press Ok", "Reset macros") - - if(skip_alert || ans == "Ok") - set_macros() - to_chat(src, SPAN_NOTICE("Keybindings were fixed.")) // not yet but set_macros works fast enough +/client/proc/reset_macros(skip = FALSE) + if(!skip) + if(alert(src, "Change your keyboard language to ENG and press Ok", "Reset macros") != "Ok") + return + to_chat(src, SPAN_NOTICE("Keybindings should be fixed now.")) + set_macros() /** * Manually clears any held keys, in case due to lag or other undefined behavior a key gets stuck. diff --git a/code/modules/materials/_materials.dm b/code/modules/materials/_materials.dm index fa970879802..80ad930d04f 100644 --- a/code/modules/materials/_materials.dm +++ b/code/modules/materials/_materials.dm @@ -67,14 +67,15 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) abstract_type = /decl/material - var/name // Prettier name for display. + var/name // Prettier name for display. + var/codex_name // Override for the codex article name. var/adjective_name var/solid_name var/gas_name var/liquid_name var/use_name - var/wall_name = "wall" // Name given to walls of this material - var/flags = 0 // Various status modifiers. + var/wall_name = "wall" // Name given to walls of this material + var/flags = 0 // Various status modifiers. var/hidden_from_codex var/lore_text var/mechanics_text @@ -110,26 +111,48 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) var/list/stack_origin_tech = "{'materials':1}" // Research level for stacks. // Attributes - var/exoplanet_rarity = MAT_RARITY_MUNDANE // How rare is this material generally? - var/cut_delay = 0 // Delay in ticks when cutting through this wall. - var/radioactivity // Radiation var. Used in wall and object processing to irradiate surroundings. - var/ignition_point // K, point at which the material catches on fire. - var/melting_point = 1800 // K, walls will take damage if they're next to a fire hotter than this - var/boiling_point = 3000 // K, point that material will become a gas. - var/latent_heat = 7000 // kJ/kg, enthalpy of vaporization - var/molar_mass = 0.06 // kg/mol, - var/brute_armor = 2 // Brute damage to a wall is divided by this value if the wall is reinforced by this material. - var/burn_armor // Same as above, but for Burn damage type. If blank brute_armor's value is used. - var/integrity = 150 // General-use HP value for products. - var/opacity = 1 // Is the material transparent? 0.5< makes transparent walls/doors. - var/explosion_resistance = 5 // Only used by walls currently. - var/conductive = 1 // Objects with this var add CONDUCTS to flags on spawn. - var/luminescence // Does this material glow? - var/wall_support_value = 30 // Used for checking if a material can function as a wall support. - var/sparse_material_weight // Ore generation constant for rare materials. - var/rich_material_weight // Ore generation constant for common materials. - var/min_fluid_opacity = FLUID_MIN_ALPHA // How transparent can fluids be? - var/max_fluid_opacity = FLUID_MAX_ALPHA // How opaque can fluids be? + /// How rare is this material generally? + var/exoplanet_rarity = MAT_RARITY_MUNDANE + /// Delay in ticks when cutting through this wall. + var/cut_delay = 0 + /// Radiation var. Used in wall and object processing to irradiate surroundings. + var/radioactivity + /// K, point at which the material catches on fire. + var/ignition_point + /// K, walls will take damage if they're next to a fire hotter than this + var/melting_point = 1800 + /// K, point that material will become a gas. + var/boiling_point = 3000 + /// kJ/kg, enthalpy of vaporization + var/latent_heat = 7000 + /// kg/mol, + var/molar_mass = 0.06 + /// Brute damage to a wall is divided by this value if the wall is reinforced by this material. + var/brute_armor = 2 + /// Same as above, but for Burn damage type. If blank brute_armor's value is used. + var/burn_armor + /// General-use HP value for products. + var/integrity = 150 + /// Is the material transparent? 0.5< makes transparent walls/doors. + var/opacity = 1 + /// Only used by walls currently. + var/explosion_resistance = 5 + /// Objects with this var add CONDUCTS to flags on spawn. + var/conductive = 1 + /// Does this material glow? + var/luminescence + /// Used for checking if a material can function as a wall support. + var/wall_support_value = 30 + /// Ore generation constant for rare materials. + var/sparse_material_weight + /// Ore generation constant for common materials. + var/rich_material_weight + /// How transparent can fluids be? + var/min_fluid_opacity = FLUID_MIN_ALPHA + /// How opaque can fluids be? + var/max_fluid_opacity = FLUID_MAX_ALPHA + /// Point at which the fluid will proc turf interaction logic. Workaround for mops being ruined forever by 1u of anything else being added. + var/turf_touch_threshold = FLUID_QDEL_POINT // Damage values. var/hardness = MAT_VALUE_HARD // Prob of wall destruction by hulk, used for edge damage in weapons. @@ -169,7 +192,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) // Gas behavior. var/gas_overlay_limit - var/gas_specific_heat + var/gas_specific_heat = 20 // J/(mol*K) var/gas_symbol_html var/gas_symbol var/gas_flags = 0 @@ -414,7 +437,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /decl/material/proc/get_wall_texture() return -/decl/material/proc/on_leaving_metabolism(var/mob/parent, var/metabolism_class) +/decl/material/proc/on_leaving_metabolism(var/atom/parent, var/metabolism_class) return #define ACID_MELT_DOSE 10 @@ -459,7 +482,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/gas_overlay) /decl/material/proc/touch_turf(var/turf/T, var/amount, var/datum/reagents/holder) // Cleaner cleaning, lube lubbing, etc, all go here - if(REAGENT_VOLUME(holder, type) < FLUID_QDEL_POINT) + if(REAGENT_VOLUME(holder, type) < turf_touch_threshold) return if(istype(T, /turf/simulated)) diff --git a/code/modules/materials/definitions/gasses/_mat_gas.dm b/code/modules/materials/definitions/gasses/_mat_gas.dm index b0b356cdd03..8d3cfb0c300 100644 --- a/code/modules/materials/definitions/gasses/_mat_gas.dm +++ b/code/modules/materials/definitions/gasses/_mat_gas.dm @@ -7,7 +7,6 @@ conductive = 0 value = 0.15 burn_product = /decl/material/gas/carbon_dioxide - gas_specific_heat = 20 // J/(mol*K) molar_mass = 0.032 // kg/mol latent_heat = 213 reflectiveness = 0 diff --git a/code/modules/materials/definitions/gasses/material_gas_mundane.dm b/code/modules/materials/definitions/gasses/material_gas_mundane.dm index 82963ee85dc..73452fc1e1f 100644 --- a/code/modules/materials/definitions/gasses/material_gas_mundane.dm +++ b/code/modules/materials/definitions/gasses/material_gas_mundane.dm @@ -300,6 +300,7 @@ /decl/material/gas/hydrogen name = "hydrogen" + codex_name = "elemental hydrogen" uid = "gas_hydrogen" lore_text = "A colorless, flammable gas." flags = MAT_FLAG_FUSION_FUEL @@ -320,6 +321,7 @@ /decl/material/gas/hydrogen/tritium name = "tritium" + codex_name = null uid = "gas_tritium" lore_text = "A radioactive isotope of hydrogen. Useful as a fusion reactor fuel material." mechanics_text = "Tritium is useable as a fuel in some forms of portable generator. It can also be converted into a fuel rod suitable for a R-UST fusion plant injector by using a fuel compressor. It fuses hotter than deuterium but is correspondingly more unstable." @@ -332,6 +334,7 @@ /decl/material/gas/hydrogen/deuterium name = "deuterium" + codex_name = null uid = "gas_deuterium" lore_text = "One of the two stable isotopes of hydrogen; also known as heavy hydrogen. Useful as a chemically synthesised fusion reactor fuel material." mechanics_text = "Deuterium can be converted into a fuel rod suitable for a R-UST fusion plant injector by using a fuel compressor. It is the most 'basic' fusion fuel." diff --git a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm index 1184553b59f..aaaab3b49ce 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_toxins.dm @@ -150,8 +150,10 @@ M.timeofdeath = world.time M.add_chemical_effect(CE_NOPULSE, 1) -/decl/material/liquid/zombiepowder/on_leaving_metabolism(mob/parent, metabolism_class) - parent?.status_flags &= ~FAKEDEATH +/decl/material/liquid/zombiepowder/on_leaving_metabolism(atom/parent, metabolism_class) + var/mob/M = parent + if(istype(M)) + M.status_flags &= ~FAKEDEATH . = ..() /decl/material/liquid/fertilizer //Reagents used for plant fertilizers. @@ -246,6 +248,7 @@ /decl/material/liquid/bromide name = "bromide" + codex_name = "elemental bromide" uid = "liquid_bromide" lore_text = "A dark, nearly opaque, red-orange, toxic element." taste_description = "pestkiller" diff --git a/code/modules/materials/definitions/liquids/materials_liquid_water.dm b/code/modules/materials/definitions/liquids/materials_liquid_water.dm index 88af098ac8d..a40ac3711e3 100644 --- a/code/modules/materials/definitions/liquids/materials_liquid_water.dm +++ b/code/modules/materials/definitions/liquids/materials_liquid_water.dm @@ -22,6 +22,7 @@ glass_desc = "The father of all refreshments." slipperiness = 8 dirtiness = DIRTINESS_CLEAN + turf_touch_threshold = 0.1 chilling_point = T0C chilling_products = list( /decl/material/solid/ice = 1 diff --git a/code/modules/materials/definitions/solids/materials_solid_glass.dm b/code/modules/materials/definitions/solids/materials_solid_glass.dm index 68b7f46c002..605e0c9bb46 100644 --- a/code/modules/materials/definitions/solids/materials_solid_glass.dm +++ b/code/modules/materials/definitions/solids/materials_solid_glass.dm @@ -1,5 +1,6 @@ /decl/material/solid/glass name = "glass" + codex_name = "silica glass" uid = "solid_glass" lore_text = "A brittle, transparent material made from molten silicates. It is generally not a liquid." flags = MAT_FLAG_BRITTLE @@ -32,6 +33,7 @@ /decl/material/solid/glass/borosilicate name = "borosilicate glass" + codex_name = null uid = "solid_borosilicate_glass" lore_text = "An extremely heat-resistant form of glass." flags = MAT_FLAG_BRITTLE diff --git a/code/modules/materials/definitions/solids/materials_solid_ice.dm b/code/modules/materials/definitions/solids/materials_solid_ice.dm index 5ef23a1eb98..cb0146d42d9 100644 --- a/code/modules/materials/definitions/solids/materials_solid_ice.dm +++ b/code/modules/materials/definitions/solids/materials_solid_ice.dm @@ -4,6 +4,7 @@ /decl/material/liquid/water = 1 ) name = "water" + codex_name = "water ice" taste_description = "ice" ore_spread_chance = 25 ore_scan_icon = "mineral_common" @@ -29,6 +30,7 @@ /decl/material/solid/ice/aspium name = "aspium" + codex_name = null heating_products = list( /decl/material/liquid/fuel/hydrazine = 0.3, /decl/material/liquid/water = 0.7 @@ -40,6 +42,7 @@ /decl/material/solid/ice/lukrite name = "lukrite" + codex_name = null heating_products = list( /decl/material/solid/sulfur = 0.4, /decl/material/liquid/water = 0.2, @@ -52,6 +55,7 @@ /decl/material/solid/ice/rubenium name = "rubenium" + codex_name = null heating_products = list( /decl/material/solid/metal/radium = 0.4, /decl/material/liquid/water = 0.4, @@ -64,6 +68,7 @@ /decl/material/solid/ice/trigarite name = "trigarite" + codex_name = null heating_products = list( /decl/material/liquid/acid/hydrochloric = 0.2, /decl/material/liquid/water = 0.2, @@ -76,6 +81,7 @@ /decl/material/solid/ice/ediroite name = "ediroite" + codex_name = null heating_products = list( /decl/material/gas/ammonia = 0.4, /decl/material/liquid/water = 0.2, @@ -87,7 +93,8 @@ rich_material_weight = 16 /decl/material/solid/ice/hydrogen - name = "hydrogen" + name = "hydrogen ice" + codex_name = null uid = "solid_ice_hydrogen" heating_products = list( /decl/material/gas/hydrogen = 0.2, @@ -105,13 +112,15 @@ //Hydrates gas are basically bubbles of gas trapped in water ice lattices /decl/material/solid/ice/hydrate + codex_name = null uid = "solid_hydrate" heating_point = T0C //the melting point is always water's + abstract_type = /decl/material/solid/ice/hydrate //Little helper macro, since hydrates are all basically the same // DISPLAY_NAME is needed because of compounds with white spaces in their names #define DECLARE_HYDRATE_DNAME_PATH(PATH, NAME, DISPLAY_NAME) \ -/decl/material/solid/ice/hydrate/##NAME/uid = "solid_hydrate_##NAME"; \ +/decl/material/solid/ice/hydrate/##NAME/uid = "solid_hydrate_##NAME"; \ /decl/material/solid/ice/hydrate/##NAME/Initialize(){ \ name = "[##DISPLAY_NAME] hydrate"; \ heating_products = list(PATH = 0.2, /decl/material/liquid/water = 0.8); \ diff --git a/code/modules/materials/definitions/solids/materials_solid_metal.dm b/code/modules/materials/definitions/solids/materials_solid_metal.dm index b0ea78787cd..acd7844f7f1 100644 --- a/code/modules/materials/definitions/solids/materials_solid_metal.dm +++ b/code/modules/materials/definitions/solids/materials_solid_metal.dm @@ -17,6 +17,7 @@ /decl/material/solid/metal/uranium name = "uranium" + codex_name = "elemental uranium" uid = "solid_uranium" lore_text = "A silvery-white metallic chemical element in the actinide series, weakly radioactive. Commonly used as fuel in fission reactors." mechanics_text = "Uranium can be used as fuel in fission reactors." @@ -66,6 +67,7 @@ /decl/material/solid/metal/gold name = "gold" + codex_name = "elemental gold" uid = "solid_gold" lore_text = "A heavy, soft, ductile metal. Once considered valuable enough to back entire currencies, now predominantly used in corrosion-resistant electronics." color = COLOR_GOLD @@ -85,6 +87,7 @@ /decl/material/solid/metal/bronze name = "bronze" + codex_name = "bronze alloy" uid = "solid_bronze" lore_text = "An alloy of copper and tin. Once used in weapons and laboring tools." color = "#ccbc63" @@ -160,6 +163,7 @@ /decl/material/solid/metal/steel name = "steel" + codex_name = "carbon steel" uid = "solid_steel" lore_text = "A strong, flexible alloy of iron and carbon. Probably the single most fundamentally useful and ubiquitous substance in human space." weight = MAT_VALUE_NORMAL @@ -189,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) @@ -268,6 +272,7 @@ /decl/material/solid/metal/plasteel name = "plasteel" + codex_name = "plasteel alloy" uid = "solid_plasteel" lore_text = "An alloy of steel and platinum. When regular high-tensile steel isn't tough enough to get the job done, the smart consumer turns to frankly absurd alloys of steel and platinum." integrity = 400 @@ -329,6 +334,7 @@ /decl/material/solid/metal/plasteel/ocp name = "osmium-carbide plasteel" + codex_name = null uid = "solid_osmium_carbide_plasteel" integrity = 200 melting_point = 12000 diff --git a/code/modules/materials/definitions/solids/materials_solid_mineral.dm b/code/modules/materials/definitions/solids/materials_solid_mineral.dm index b39e188490c..ee5591adcb2 100644 --- a/code/modules/materials/definitions/solids/materials_solid_mineral.dm +++ b/code/modules/materials/definitions/solids/materials_solid_mineral.dm @@ -28,6 +28,7 @@ /decl/material/solid/graphite name = "graphite" + codex_name = "loose graphite" uid = "solid_graphite" color = "#444444" ore_name = "graphite" @@ -74,7 +75,7 @@ ) /decl/material/solid/pyrite - name = "pyrite" + name = "fool's gold" uid = "solid_pyrite" ore_name = "pyrite" ore_result_amount = 10 diff --git a/code/modules/materials/definitions/solids/materials_solid_organic.dm b/code/modules/materials/definitions/solids/materials_solid_organic.dm index f4d21e787fa..a0d10ed1dd1 100644 --- a/code/modules/materials/definitions/solids/materials_solid_organic.dm +++ b/code/modules/materials/definitions/solids/materials_solid_organic.dm @@ -9,7 +9,7 @@ use_reinf_state = null color = COLOR_EGGSHELL door_icon_base = "plastic" - hardness = MAT_VALUE_FLEXIBLE + hardness = MAT_VALUE_FLEXIBLE + 10 weight = MAT_VALUE_LIGHT melting_point = T0C+371 //assuming heat resistant plastic stack_origin_tech = "{'materials':3}" @@ -404,7 +404,7 @@ uid = "solid_scaled_hide" color = "#434b31" integrity = 75 - hardness = MAT_VALUE_RIGID + hardness = MAT_VALUE_FLEXIBLE + 5 weight = MAT_VALUE_LIGHT reflectiveness = MAT_VALUE_SHINY diff --git a/code/modules/materials/definitions/solids/materials_solid_wood.dm b/code/modules/materials/definitions/solids/materials_solid_wood.dm index 2ae171c7e83..674dc32c359 100644 --- a/code/modules/materials/definitions/solids/materials_solid_wood.dm +++ b/code/modules/materials/definitions/solids/materials_solid_wood.dm @@ -46,6 +46,9 @@ . += new/datum/stack_recipe/sandals(src) . += new/datum/stack_recipe/tile/wood(src) . += create_recipe_list(/datum/stack_recipe/furniture/chair/wood) + . += new/datum/stack_recipe/furniture/sofa/m(src) + . += new/datum/stack_recipe/furniture/sofa/l(src) + . += new/datum/stack_recipe/furniture/sofa/r(src) . += new/datum/stack_recipe/crossbowframe(src) . += new/datum/stack_recipe/furniture/coffin/wooden(src) . += new/datum/stack_recipe/beehive_assembly(src) diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm index 262a56316a2..2009872755f 100644 --- a/code/modules/materials/material_recipes.dm +++ b/code/modules/materials/material_recipes.dm @@ -28,21 +28,47 @@ if(reinforce_material) //recipes below don't support composite materials return - // If is_brittle() returns true, these are only good for a single strike. - . += new/datum/stack_recipe/ashtray(src) - . += new/datum/stack_recipe/ring(src) - . += new/datum/stack_recipe/clipboard(src) - . += new/datum/stack_recipe/cross(src) - . += new/datum/stack_recipe/improvised_armour(src) - . += new/datum/stack_recipe/armguards(src) - . += new/datum/stack_recipe/legguards(src) - . += new/datum/stack_recipe/gauntlets(src) + if(hardness >= MAT_VALUE_FLEXIBLE + 10) + // If is_brittle() returns true, these are only good for a single strike. + . += new/datum/stack_recipe/improvised_armour(src) + . += new/datum/stack_recipe/armguards(src) + . += new/datum/stack_recipe/legguards(src) + . += new/datum/stack_recipe/gauntlets(src) - if(hardness >= MAT_VALUE_FLEXIBLE) + if(integrity >= 50) + . += new/datum/stack_recipe/furniture/door(src) + . += new/datum/stack_recipe/furniture/barricade(src) + . += new/datum/stack_recipe/furniture/stool(src) + . += new/datum/stack_recipe/furniture/bar_stool(src) + . += new/datum/stack_recipe/furniture/coatrack(src) + . += new/datum/stack_recipe/furniture/bed(src) + . += new/datum/stack_recipe/furniture/pew(src) + . += new/datum/stack_recipe/furniture/pew_left(src) + . += new/datum/stack_recipe/furniture/closet(src) + . += new/datum/stack_recipe/furniture/coffin(src) + . += new/datum/stack_recipe/furniture/chair(src) //NOTE: the wood material has it's own special chair recipe + . += new/datum/stack_recipe/furniture/chair/padded(src) + . += new/datum/stack_recipe/furniture/chair/office/comfy(src) + . += new/datum/stack_recipe/furniture/chair/comfy(src) + . += new/datum/stack_recipe/furniture/chair/arm(src) + . += new/datum/stack_recipe/furniture/chair/roundedchair(src) + . += new/datum/stack_recipe/lock(src) + . += new/datum/stack_recipe/key(src) + . += new/datum/stack_recipe/rod(src) + + if(hardness > MAT_VALUE_RIGID + 10) + . += new/datum/stack_recipe/fork(src) + . += new/datum/stack_recipe/knife(src) + . += new/datum/stack_recipe/bell(src) + . += new/datum/stack_recipe/blade(src) + . += new/datum/stack_recipe/drill_head(src) + . += new/datum/stack_recipe/ashtray(src) + . += new/datum/stack_recipe/ring(src) + . += new/datum/stack_recipe/clipboard(src) + . += new/datum/stack_recipe/cross(src) . += new/datum/stack_recipe/baseball_bat(src) . += new/datum/stack_recipe/urn(src) . += new/datum/stack_recipe/spoon(src) - . += new/datum/stack_recipe/furniture/door(src) var/list/coin_recipes = list() var/list/all_currencies = decls_repository.get_decls_of_subtype(/decl/currency) @@ -54,33 +80,6 @@ if(length(coin_recipes)) . += new/datum/stack_recipe_list("antique coins", coin_recipes) - if(integrity >= 50 && hardness >= MAT_VALUE_FLEXIBLE + 10) - . += new/datum/stack_recipe/furniture/door(src) - . += new/datum/stack_recipe/furniture/barricade(src) - . += new/datum/stack_recipe/furniture/stool(src) - . += new/datum/stack_recipe/furniture/bar_stool(src) - . += new/datum/stack_recipe/furniture/coatrack(src) - . += new/datum/stack_recipe/furniture/bed(src) - . += new/datum/stack_recipe/furniture/pew(src) - . += new/datum/stack_recipe/furniture/pew_left(src) - . += new/datum/stack_recipe/furniture/closet(src) - . += new/datum/stack_recipe/furniture/coffin(src) - . += new/datum/stack_recipe/furniture/chair(src) //NOTE: the wood material has it's own special chair recipe - . += new/datum/stack_recipe/furniture/chair/padded(src) - . += new/datum/stack_recipe/furniture/chair/office/comfy(src) - . += new/datum/stack_recipe/furniture/chair/comfy(src) - . += new/datum/stack_recipe/furniture/chair/arm(src) - . += new/datum/stack_recipe/lock(src) - . += new/datum/stack_recipe/key(src) - . += new/datum/stack_recipe/rod(src) - - if(hardness > MAT_VALUE_RIGID + 10) - . += new/datum/stack_recipe/fork(src) - . += new/datum/stack_recipe/knife(src) - . += new/datum/stack_recipe/bell(src) - . += new/datum/stack_recipe/blade(src) - . += new/datum/stack_recipe/drill_head(src) - /decl/material/proc/generate_strut_recipes(var/reinforce_material) . = list() diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 56fc1204354..2116205efbd 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -1,5 +1,6 @@ // Stacked resources. They use a material datum for a lot of inherited values. /obj/item/stack/material + name = "material sheet" force = 5.0 throwforce = 5 w_class = ITEM_SIZE_LARGE diff --git a/code/modules/materials/material_synth.dm b/code/modules/materials/material_synth.dm index 7f6f7a2c0f4..65bf330b524 100644 --- a/code/modules/materials/material_synth.dm +++ b/code/modules/materials/material_synth.dm @@ -18,35 +18,43 @@ matter = null /obj/item/stack/material/cyborg/plastic + name = "cyborg plastic synthesiser" icon_state = "sheet" material = /decl/material/solid/plastic /obj/item/stack/material/cyborg/steel + name = "cyborg steel synthesiser" icon_state = "sheet" material = /decl/material/solid/metal/steel /obj/item/stack/material/cyborg/plasteel + name = "cyborg plasteel synthesiser" icon_state = "sheet-reinf" material = /decl/material/solid/metal/plasteel /obj/item/stack/material/cyborg/wood + name = "cyborg wood synthesiser" icon_state = "sheet-wood" material = /decl/material/solid/wood /obj/item/stack/material/cyborg/glass + name = "cyborg glass synthesiser" icon_state = "sheet" material = /decl/material/solid/glass /obj/item/stack/material/cyborg/fiberglass + name = "cyborg fiberglass synthesiser" icon_state = "sheet" material = /decl/material/solid/fiberglass /obj/item/stack/material/cyborg/glass/reinforced + name = "cyborg reinforced glass synthesiser" icon_state = "sheet-reinf" material = /decl/material/solid/glass reinf_material = /decl/material/solid/metal/steel charge_costs = list(500, 1000) /obj/item/stack/material/cyborg/aluminium + name = "cyborg aluminium synthesiser" icon_state = "sheet" material = /decl/material/solid/metal/aluminium diff --git a/code/modules/materials/recipes_furniture.dm b/code/modules/materials/recipes_furniture.dm index 656cf98da88..a198e004550 100644 --- a/code/modules/materials/recipes_furniture.dm +++ b/code/modules/materials/recipes_furniture.dm @@ -24,10 +24,16 @@ return modifiers ? jointext(modifiers + title, " ") : title // Bypass material /datum/stack_recipe/furniture/chair/office/comfy result_type = /obj/structure/bed/chair/office/comfy + title = "office comfy chair" /datum/stack_recipe/furniture/chair/comfy result_type = /obj/structure/bed/chair/comfy + title = "comfy chair" /datum/stack_recipe/furniture/chair/arm result_type = /obj/structure/bed/chair/armchair + title = "armchair" +/datum/stack_recipe/furniture/chair/roundedchair + result_type = /obj/structure/bed/chair/rounded + title = "rounded chair" /datum/stack_recipe/furniture/chair/wood /datum/stack_recipe/furniture/chair/wood/normal @@ -36,6 +42,18 @@ result_type = /obj/structure/bed/chair/wood/wings modifiers = list("fancy") +/datum/stack_recipe/furniture/sofa/m + result_type = /obj/structure/bed/sofa/m + title = "middle sofa" + +/datum/stack_recipe/furniture/sofa/l + result_type = /obj/structure/bed/sofa/l + title = "left sofa" + +/datum/stack_recipe/furniture/sofa/r + result_type = /obj/structure/bed/sofa/r + title = "right sofa" + /datum/stack_recipe/furniture/door title = "door" result_type = /obj/structure/door diff --git a/code/modules/mechs/mech_wreckage.dm b/code/modules/mechs/mech_wreckage.dm index 2c180cb721d..b2ab905ca0b 100644 --- a/code/modules/mechs/mech_wreckage.dm +++ b/code/modules/mechs/mech_wreckage.dm @@ -79,4 +79,4 @@ thing.forceMove(get_turf(src)) else qdel(thing) - ..() + return ..() diff --git a/code/modules/merchant/merchant_programs.dm b/code/modules/merchant/merchant_programs.dm index f5ba581772b..c6ce67c1d21 100644 --- a/code/modules/merchant/merchant_programs.dm +++ b/code/modules/merchant/merchant_programs.dm @@ -7,7 +7,7 @@ nanomodule_path = /datum/nano_module/program/merchant size = 12 usage_flags = PROGRAM_CONSOLE - required_access = list(access_merchant) + read_access = list(access_merchant) var/datum/trade_hub/current_hub var/datum/trader/current_trader var/obj/machinery/merchant_pad/pad = null diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index a213b154f4c..592bb063c5a 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -121,7 +121,7 @@ /obj/machinery/mining/drill/physical_attack_hand(mob/user) check_supports() if(need_player_check) - if(can_use_power_oneoff(10 KILOWATTS)) + if(can_use_power_oneoff(10 KILOWATTS) <= 0) system_error("insufficient charge") else if(anchored) get_resource_field() diff --git a/code/modules/mob/grab/grab_object.dm b/code/modules/mob/grab/grab_object.dm index 55c6d33fa42..b68dcaf26d4 100644 --- a/code/modules/mob/grab/grab_object.dm +++ b/code/modules/mob/grab/grab_object.dm @@ -242,11 +242,14 @@ current_grab.handle_resist(src) /obj/item/grab/proc/adjust_position(var/force = 0) - if(force) + + if(!QDELETED(assailant) && force) affecting.forceMove(assailant.loc) - if(!assailant || !affecting || !assailant.Adjacent(affecting)) + + if(QDELETED(assailant) || QDELETED(affecting) || !assailant.IsMultiZAdjacent(affecting)) qdel(src) return 0 + var/adir = get_dir(assailant, affecting) if(assailant) assailant.set_dir(adir) diff --git a/code/modules/mob/grab/normal/grab_normal.dm b/code/modules/mob/grab/normal/grab_normal.dm index 53d6fcd5d6c..3417621f12b 100644 --- a/code/modules/mob/grab/normal/grab_normal.dm +++ b/code/modules/mob/grab/normal/grab_normal.dm @@ -43,7 +43,7 @@ return FALSE var/mob/living/assailant = G.assailant - if(!assailant || !assailant.skill_check(SKILL_COMBAT, SKILL_ADEPT)) + if(!assailant) return FALSE var/obj/item/organ/external/O = G.get_targeted_organ() @@ -51,6 +51,10 @@ to_chat(assailant, SPAN_WARNING("\The [affecting] is missing that body part!")) return FALSE + if(!assailant.skill_check(SKILL_COMBAT, SKILL_ADEPT)) + to_chat(assailant, SPAN_WARNING("You clumsily attempt to jointlock \the [affecting]'s [O.name], but fail!")) + return FALSE + assailant.visible_message(SPAN_DANGER("\The [assailant] begins to [pick("bend", "twist")] \the [affecting]'s [O.name] into a jointlock!")) if(do_mob(assailant, affecting, action_cooldown - 1)) G.action_used() @@ -72,7 +76,7 @@ return FALSE var/mob/living/assailant = G.assailant - if(!assailant || !assailant.skill_check(SKILL_COMBAT, SKILL_ADEPT)) + if(!assailant) return FALSE var/obj/item/organ/external/O = G.get_targeted_organ() @@ -80,18 +84,22 @@ to_chat(assailant, SPAN_WARNING("\The [affecting] is missing that body part!")) return FALSE - if(!O.dislocated) + if(!assailant.skill_check(SKILL_COMBAT, SKILL_ADEPT)) + to_chat(assailant, SPAN_WARNING("You clumsily attempt to dislocate \the [affecting]'s [O.name], but fail!")) + return FALSE + + if(!O.is_dislocated() && (O.limb_flags & ORGAN_FLAG_CAN_DISLOCATE)) assailant.visible_message(SPAN_DANGER("\The [assailant] begins to dislocate \the [affecting]'s [O.joint]!")) if(do_mob(assailant, affecting, action_cooldown - 1)) G.action_used() - O.dislocate(1) + O.dislocate() assailant.visible_message(SPAN_DANGER("\The [affecting]'s [O.joint] [pick("gives way","caves in","crumbles","collapses")]!")) playsound(assailant.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) return TRUE affecting.visible_message(SPAN_WARNING("\The [assailant] fails to dislocate \the [affecting]'s [O.joint].")) return FALSE - if (O.dislocated > 0) + if(O.limb_flags & ORGAN_FLAG_CAN_DISLOCATE) to_chat(assailant, SPAN_WARNING("\The [affecting]'s [O.joint] is already dislocated!")) else to_chat(assailant, SPAN_WARNING("You can't dislocate \the [affecting]'s [O.joint]!")) @@ -283,4 +291,4 @@ if(W.hitsound) playsound(affecting.loc, W.hitsound, 50, 1, -1) G.last_action = world.time admin_attack_log(user, affecting, "hamstrung their victim", "was hamstrung", "hamstrung") - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/mob/hear_say.dm b/code/modules/mob/hear_say.dm index e2c812c0eb3..fcfc12315da 100644 --- a/code/modules/mob/hear_say.dm +++ b/code/modules/mob/hear_say.dm @@ -284,6 +284,8 @@ src.show_message(message) /mob/proc/hear_sleep(var/message) + if (is_deaf()) + return var/heard = "" if(prob(15)) var/list/punctuation = list(",", "!", ".", ";", "?") 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/bot/mulebot.dm b/code/modules/mob/living/bot/mulebot.dm index 345686fd41a..08305c5dec0 100644 --- a/code/modules/mob/living/bot/mulebot.dm +++ b/code/modules/mob/living/bot/mulebot.dm @@ -249,7 +249,7 @@ return beaconlist /mob/living/bot/mulebot/proc/load(var/atom/movable/C) - if(busy || load || get_dist(C, src) > 1 || !isturf(C.loc)) + if(busy || load || get_dist(C, src) > 1 || !isturf(C.loc) || C.anchored) return for(var/obj/structure/plasticflaps/P in src.loc)//Takes flaps into account diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index d9df7977302..dc684b7ff66 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -1,8 +1,11 @@ /mob/living/carbon/Initialize() //setup reagent holders - bloodstr = new/datum/reagents/metabolism(120, src, CHEM_INJECT) - touching = new/datum/reagents/metabolism(1000, src, CHEM_TOUCH) - reagents = bloodstr + if(!bloodstr) + bloodstr = new/datum/reagents/metabolism(120, src, CHEM_INJECT) + if(!reagents) + reagents = bloodstr + if(!touching) + touching = new/datum/reagents/metabolism(1000, src, CHEM_TOUCH) if (!default_language && species_language) default_language = species_language @@ -10,7 +13,8 @@ /mob/living/carbon/Destroy() QDEL_NULL(touching) - bloodstr = null // We don't qdel(bloodstr) because it's the same as qdel(reagents) + QDEL_NULL(bloodstr) + reagents = null //We assume reagents is a reference to bloodstr here delete_organs() QDEL_NULL_LIST(hallucinations) if(loc) diff --git a/code/modules/mob/living/carbon/carbon_organs.dm b/code/modules/mob/living/carbon/carbon_organs.dm index a504920991d..11c81785c73 100644 --- a/code/modules/mob/living/carbon/carbon_organs.dm +++ b/code/modules/mob/living/carbon/carbon_organs.dm @@ -12,30 +12,33 @@ /mob/living/carbon/has_external_organs() return LAZYLEN(external_organs) > 0 - + /mob/living/carbon/has_internal_organs() return LAZYLEN(internal_organs) > 0 +//Deletes all references to organs /mob/living/carbon/proc/delete_organs() for(var/obj/item/organ/O in get_organs()) - remove_organ(O, FALSE, FALSE, TRUE, TRUE, FALSE) //Remove them first so we don't trigger removal effects by just calling delete on them - QDEL_LIST_ASSOC_VAL(organs_by_tag) + qdel(O) organs_by_tag = null internal_organs = null external_organs = null -/mob/living/carbon/add_organ(var/obj/item/organ/O, var/obj/item/organ/external/affected = null, var/in_place = FALSE, var/update_icon = TRUE) - if(LAZYACCESS(organs_by_tag, O.organ_tag)) - CRASH("mob/living/carbon/add_organ(): '[O]' tried to overwrite [src]'s existing organ '[organs_by_tag[O.organ_tag]]' in slot '[O.organ_tag]'!") +/mob/living/carbon/add_organ(obj/item/organ/O, obj/item/organ/external/affected, in_place, update_icon, detached) + var/obj/item/organ/existing = LAZYACCESS(organs_by_tag, O.organ_tag) + if(existing && O != existing) + CRASH("mob/living/carbon/add_organ(): '[O]' tried to overwrite [src]'s existing organ '[existing]' in slot '[O.organ_tag]'!") if(O.parent_organ && !LAZYACCESS(organs_by_tag, O.parent_organ)) CRASH("mob/living/carbon/add_organ(): Tried to add an internal organ to a non-existing parent external organ!") - LAZYSET(organs_by_tag, O.organ_tag, O) - if(O.is_internal()) + //We don't add internal organs to the lists if we're detached + if(O.is_internal() && !detached) + LAZYSET(organs_by_tag, O.organ_tag, O) LAZYDISTINCTADD(internal_organs, O) - else + //External organs must always be in the organ list even when detached. Otherwise icon updates won't show the limb, and limb attach surgeries won't work + else if(!O.is_internal()) + LAZYSET(organs_by_tag, O.organ_tag, O) LAZYDISTINCTADD(external_organs, O) - . = ..() /mob/living/carbon/remove_organ(var/obj/item/organ/O, var/drop_organ = TRUE, var/detach = TRUE, var/ignore_children = FALSE, var/in_place = FALSE, var/update_icon = TRUE) @@ -47,6 +50,7 @@ if(client) client.screen -= O + LAZYREMOVE(organs_by_tag, O.organ_tag) if(O.is_internal()) LAZYREMOVE(internal_organs, O) @@ -56,7 +60,7 @@ //Should handle vital organ checks, icon updates, events /mob/living/carbon/on_lost_organ(var/obj/item/organ/O) if(!(. = ..())) - return + return //Check if we should die if(species.is_vital_organ(src, O)) 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/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index d6c47d09af1..fa578f17176 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -1,18 +1,22 @@ /mob/living/carbon/human/gib() for(var/obj/item/organ/I in get_internal_organs()) + if(!I.is_droppable()) + continue //Skip anything that cannot be dropped remove_organ(I) if(!QDELETED(I) && isturf(loc)) I.throw_at(get_edge_target_turf(src, pick(global.alldirs)), rand(1,3), THROWFORCE_GIBS) for(var/obj/item/organ/external/E in get_external_organs()) - E.dismember(0,DISMEMBER_METHOD_EDGE,1) + if(!E.parent_organ || !E.is_droppable()) + continue //Skip root organ, or undroppables + E.dismember(FALSE, DISMEMBER_METHOD_EDGE, TRUE) sleep(1) - for(var/obj/item/I in src) + for(var/obj/item/I in get_contained_external_atoms()) drop_from_inventory(I) if(!QDELETED(I)) - I.throw_at(get_edge_target_turf(src,pick(global.alldirs)), rand(1,3), round(THROWFORCE_GIBS/I.w_class)) + I.throw_at(get_edge_target_turf(src, pick(global.alldirs)), rand(1,3), round(THROWFORCE_GIBS/I.w_class)) ..(species.gibbed_anim) gibs(loc, dna, null, species.get_flesh_colour(src), species.get_blood_color(src)) diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm index dcf11b4dddc..28e5f54b008 100644 --- a/code/modules/mob/living/carbon/human/examine.dm +++ b/code/modules/mob/living/carbon/human/examine.dm @@ -106,7 +106,7 @@ coating = E.coating break if(coating) - msg += "There's something something on [G.his] hands!\n" + msg += "There's something on [G.his] hands!\n" //belt if(belt) @@ -267,7 +267,7 @@ if(wounddesc != "nothing") wound_flavor_text[E.name] += "[G.He] [G.has] [wounddesc] on [G.his] [E.name].
    " if(!hidden || distance <=1) - if(E.dislocated > 0) + if(E.is_dislocated()) wound_flavor_text[E.name] += "[G.His] [E.joint] is dislocated!
    " if(((E.status & ORGAN_BROKEN) && E.brute_dam > E.min_broken_damage) || (E.status & ORGAN_MUTATED)) wound_flavor_text[E.name] += "[G.His] [E.name] is dented and swollen!
    " diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 1510e6fcab5..0b9e25b1955 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -641,7 +641,7 @@ update_body() //set_species should not handle the entirety of initing the mob, and should not trigger deep updates -//It focuses on setting up specie related data, without force applying them uppon organs and the mob's appearence. +//It focuses on setting up species-related data, without force applying them uppon organs and the mob's appearance. // For transforming an existing mob, look at change_species() /mob/living/carbon/human/proc/set_species(var/new_species_name) if(!new_species_name) @@ -698,7 +698,7 @@ update_emotes() return TRUE -//Syncs cultural tokens to the currently set specie, and may trigger a language update +//Syncs cultural tokens to the currently set species, and may trigger a language update /mob/living/carbon/human/proc/apply_species_cultural_info() var/update_lang for(var/token in ALL_CULTURAL_TAGS) @@ -712,7 +712,7 @@ if(update_lang) update_languages() -//Drop anything that cannot be worn by the current specie of the mob +//Drop anything that cannot be worn by the current species of the mob /mob/living/carbon/human/proc/apply_species_inventory_restrictions() if(species) if(!(species.appearance_flags & HAS_UNDERWEAR)) @@ -731,7 +731,7 @@ icon_state = lowertext(SPECIES_HUMAN) skin_colour = COLOR_BLACK else - species.apply_appearence(src) + species.apply_appearance(src) force_update_limbs() //updates bodytype default_pixel_x = initial(pixel_x) + bodytype.pixel_offset_x @@ -1019,7 +1019,7 @@ status += "MISSING" if(org.status & ORGAN_MUTATED) status += "misshapen" - if(org.dislocated == 2) + if(org.is_dislocated()) status += "dislocated" if(org.status & ORGAN_BROKEN) status += "hurts when touched" @@ -1289,7 +1289,7 @@ //Set species and real name data set_real_name(new_dna.real_name) set_species(new_dna.species) - //Revive actually regen organs, reset their appearence and makes sure if the player is kicked out they get reinserted in + //Revive actually regen organs, reset their appearance and makes sure if the player is kicked out they get reinserted in revive() species.handle_pre_spawn(src) @@ -1328,14 +1328,16 @@ dna.ready_dna(src) //regen dna filler only if we haven't forced the dna already species.handle_pre_spawn(src) - apply_species_cultural_info() - apply_species_appearance() if(!LAZYLEN(get_external_organs())) species.create_missing_organs(src) //Syncs DNA when adding organs + apply_species_cultural_info() + apply_species_appearance() species.handle_post_spawn(src) - UpdateAppearance() //Apply dna appearence to mob, causes DNA to change because filler values are regenerated - reset_blood() + UpdateAppearance() //Apply dna appearance to mob, causes DNA to change because filler values are regenerated + //Prevent attempting to create blood container if its already setup + if(!vessel) + reset_blood() //If the mob has its default name it'll try to generate /obtain a proper one /mob/living/carbon/human/proc/try_generate_default_name() diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index e9d2716a902..31aaaf9dff3 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -322,11 +322,12 @@ In most cases it makes more sense to use apply_damage() instead! And make sure t This function restores all organs. */ /mob/living/carbon/human/restore_all_organs(var/ignore_prosthetic_prefs) + species?.create_missing_organs(src) for(var/bodypart in global.all_limb_tags_by_depth) var/obj/item/organ/external/current_organ = get_organ(bodypart) if(istype(current_organ)) current_organ.rejuvenate(ignore_prosthetic_prefs) - bad_external_organs.Cut() // otherwise hanging refs will prevent gc after rejuv + recheck_bad_external_organs() verbs -= /mob/living/carbon/human/proc/undislocate /mob/living/carbon/human/proc/HealDamage(zone, brute, burn) @@ -339,7 +340,7 @@ This function restores all organs. return /mob/living/carbon/human/apply_damage(var/damage = 0, var/damagetype = BRUTE, var/def_zone = null, var/damage_flags = 0, var/obj/used_weapon = null, var/armor_pen, var/silent = FALSE, var/obj/item/organ/external/given_organ = null) - + if(status_flags & GODMODE) return //godmode var/obj/item/organ/external/organ = given_organ if(!organ) if(isorgan(def_zone)) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 13167560423..2d74243def2 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -217,6 +217,9 @@ meteor_act if(W.damtype != BRUTE) return + if(!should_have_organ(BP_HEART)) + return + //make non-sharp low-force weapons less likely to be bloodied if(W.sharp || prob(effective_force*4)) if(!(W.atom_flags & ATOM_FLAG_NO_BLOOD)) @@ -266,7 +269,7 @@ meteor_act C.update_clothing_icon() /mob/living/carbon/human/proc/attack_joint(var/obj/item/organ/external/organ, var/obj/item/W, var/effective_force, var/dislocate_mult, var/blocked) - if(!organ || (organ.dislocated == 2) || (organ.dislocated == -1) || blocked >= 100) + if(!organ || organ.is_dislocated() || !(organ.limb_flags & ORGAN_FLAG_CAN_DISLOCATE) || blocked >= 100) return 0 if(W.damtype != BRUTE) return 0 diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index f88316c68a5..e83c32463f9 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -45,8 +45,9 @@ var/voice = "" //Instead of new say code calling GetVoice() over and over and over, we're just going to ask this variable, which gets updated in Life() var/last_dam = -1 //Used for determining if we need to process all organs or just some or even none. - var/list/bad_external_organs = list() // organs we check until they are good. - + /// organs we check until they are good. + var/list/bad_external_organs + var/xylophone = 0 //For the spoooooooky xylophone cooldown var/mob/remoteview_target = null diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 24d3e43ab7a..dfc05cfb3f7 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -171,7 +171,7 @@ next_sonar_ping += 10 SECONDS var/heard_something = FALSE to_chat(src, "You take a moment to listen in to your environment...") - for(var/mob/living/L in range(get_effective_view(client), src)) + for(var/mob/living/L in range(client?.view || world.view, src)) var/turf/T = get_turf(L) if(!T || L == src || L.stat == DEAD || is_below_sound_pressure(T)) continue diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm index e039327bc41..787c791414a 100644 --- a/code/modules/mob/living/carbon/human/human_organs.dm +++ b/code/modules/mob/living/carbon/human/human_organs.dm @@ -18,33 +18,36 @@ last_dam = damage_this_tick /mob/living/carbon/human/proc/recheck_bad_external_organs() - bad_external_organs.Cut() + LAZYCLEARLIST(bad_external_organs) for(var/obj/item/organ/external/E in get_external_organs()) - if(!E || !E.need_process()) - continue - bad_external_organs |= E + if(E.need_process()) + LAZYDISTINCTADD(bad_external_organs, E) // Takes care of organ related updates, such as broken and missing limbs /mob/living/carbon/human/proc/handle_organs() - var/force_process = should_recheck_bad_external_organs() - if(force_process) - recheck_bad_external_organs() //processing internal organs is pretty cheap, do that first. - for(var/obj/item/organ/I in get_internal_organs()) + for(var/obj/item/organ/I in internal_organs) I.Process() + var/force_process = should_recheck_bad_external_organs() + + if(force_process) + recheck_bad_external_organs() + for(var/obj/item/organ/external/Ex in get_external_organs()) + LAZYDISTINCTADD(bad_external_organs, Ex) + handle_stance() handle_grasp() - if(!force_process && !length(bad_external_organs)) + if(!force_process && !LAZYLEN(bad_external_organs)) return for(var/obj/item/organ/external/E in bad_external_organs) if(!E) continue if(!E.need_process()) - bad_external_organs -= E + LAZYREMOVE(bad_external_organs, E) continue else E.Process() @@ -221,7 +224,10 @@ /mob/living/carbon/human/proc/sync_organ_dna() for(var/obj/item/organ/O in get_organs()) - O.set_dna(dna) + if(!BP_IS_PROSTHETIC(O)) + O.setup_as_organic(dna) + else + O.setup_as_prosthetic() /mob/living/proc/is_asystole() return FALSE @@ -250,7 +256,7 @@ //Registers an organ and setup the organ hierachy properly. //affected : Parent organ if applicable. //in_place : If true, we're performing an in-place replacement, without triggering anything related to adding the organ in-game as part of surgery or else. -/mob/living/carbon/human/add_organ(var/obj/item/organ/O, var/obj/item/organ/external/affected = null, var/in_place = FALSE, var/update_icon = TRUE) +/mob/living/carbon/human/add_organ(obj/item/organ/O, obj/item/organ/external/affected, in_place, update_icon, detached) if(!(. = ..())) return if(!O.is_internal()) @@ -271,11 +277,12 @@ //ignore_children: Skips recursively removing this organ's child organs. //in_place : If true we remove only the organ (no children items or implants) and avoid triggering mob changes and parent organs changes as much as possible. // Meant to be used for init and species transforms, without triggering any updates to mob state or anything related to losing a limb as part of surgery or combat. -/mob/living/carbon/human/remove_organ(var/obj/item/organ/O, var/drop_organ = TRUE, var/detach = TRUE, var/ignore_children = FALSE, var/in_place = FALSE, var/update_icon = TRUE) +/mob/living/carbon/human/remove_organ(obj/item/organ/O, drop_organ, detach, ignore_children, in_place, update_icon) if(!(. = ..())) return if(!O.is_internal()) refresh_modular_limb_verbs() + LAZYREMOVE(bad_external_organs, O) //#TODO: wish we could invalidate the human icons to trigger a single update when the organ state changes multiple times in a row if(update_icon) @@ -288,11 +295,11 @@ /mob/living/carbon/human/on_lost_organ(var/obj/item/organ/O) if(!(. = ..())) - return + return //Move some blood over to the organ if(!BP_IS_PROSTHETIC(O) && O.species && O.reagents?.total_volume < 5) vessel.trans_to(O, 5 - O.reagents.total_volume, 1, 1) /mob/living/carbon/human/delete_organs() . = ..() - bad_external_organs?.Cut() \ No newline at end of file + LAZYCLEARLIST(bad_external_organs) diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm index 31dfff1edee..31171561c84 100644 --- a/code/modules/mob/living/carbon/human/human_species.dm +++ b/code/modules/mob/living/carbon/human/human_species.dm @@ -45,7 +45,7 @@ /mob/living/carbon/human/dummy/mannequin/add_to_dead_mob_list() return FALSE -/mob/living/carbon/human/dummy/mannequin/fully_replace_character_name(new_name) +/mob/living/carbon/human/dummy/mannequin/fully_replace_character_name(new_name, in_depth = TRUE) ..("[new_name] (mannequin)", FALSE) /mob/living/carbon/human/dummy/mannequin/InitializeHud() diff --git a/code/modules/mob/living/carbon/human/human_verbs.dm b/code/modules/mob/living/carbon/human/human_verbs.dm index 5c27d8efbb2..bca7b528db3 100644 --- a/code/modules/mob/living/carbon/human/human_verbs.dm +++ b/code/modules/mob/living/carbon/human/human_verbs.dm @@ -284,7 +284,7 @@ var/list/limbs = list() for(var/obj/item/organ/external/current_limb in get_external_organs()) - if(current_limb && current_limb.dislocated > 0 && !current_limb.is_parent_dislocated()) //if the parent is also dislocated you will have to relocate that first + if(current_limb && current_limb.is_dislocated() && !current_limb.is_parent_dislocated()) //if the parent is also dislocated you will have to relocate that first limbs |= current_limb var/obj/item/organ/external/current_limb = input(usr,"Which joint do you wish to relocate?") as null|anything in limbs diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 9570a983a8e..90b05d0b315 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -844,8 +844,7 @@ mind.changeling.regenerate() /mob/living/carbon/human/proc/handle_shock() - if(status_flags & GODMODE) return 0 //godmode - if(!can_feel_pain()) + if(!can_feel_pain() || (status_flags & GODMODE)) shock_stage = 0 return diff --git a/code/modules/mob/living/carbon/human/stripping.dm b/code/modules/mob/living/carbon/human/stripping.dm index cb82e09913c..031d4a48dcc 100644 --- a/code/modules/mob/living/carbon/human/stripping.dm +++ b/code/modules/mob/living/carbon/human/stripping.dm @@ -60,11 +60,7 @@ var/obj/item/clothing/accessory/A if(LAZYLEN(holder.accessories) > 1) - var/list/options = list() - for(var/obj/item/clothing/accessory/i in holder.accessories) - var/image/radial_button = image(icon = i.icon, icon_state = i.icon_state) - options[i] = radial_button - A = show_radial_menu(user, user, options, radius = 42, tooltips = TRUE) + A = show_radial_menu(user, user, make_item_radial_menu_choices(holder.accessories), radius = 42, tooltips = TRUE) else A = holder.accessories[1] @@ -73,7 +69,7 @@ visible_message("\The [user] is trying to remove \the [src]'s [A.name]!") - if(!do_after(user, HUMAN_STRIP_DELAY, src, progress = 0)) + if(!do_after(user, HUMAN_STRIP_DELAY, src, check_holding = FALSE, progress = FALSE)) return if(!A || holder.loc != src || !(A in holder.accessories)) @@ -102,7 +98,7 @@ else visible_message("\The [user] is trying to put \a [held] on \the [src]!") - if(!do_mob(user, src, HUMAN_STRIP_DELAY)) + if(!do_mob(user, src, HUMAN_STRIP_DELAY, check_holding = FALSE)) return if(stripping) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index dbc93bf06e3..1eea34bf977 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -236,18 +236,20 @@ Please contact me on #coderbus IRC. ~Carn x turn_angle = -90 else if(dir & EAST) turn_angle = 90 - else + else turn_angle = pick(-90, 90) M.Turn(turn_angle) M.Scale(desired_scale_y, desired_scale_x) - M.Translate(1, -6-default_pixel_z) + M.Translate(turn_angle == 90 ? 1 : -2, (turn_angle == 90 ? -6 : -5) - default_pixel_z) else M.Scale(desired_scale_x, desired_scale_y) - M.Translate(0, 16*(desired_scale_y-1)) + M.Translate(0, 16 * (desired_scale_y - 1)) + if(transform_animate_time) animate(src, transform = M, time = transform_animate_time) else transform = M + return transform var/global/list/damage_icon_parts = list() @@ -704,7 +706,7 @@ var/global/list/damage_icon_parts = list() /mob/living/carbon/human/proc/get_tail_icon(var/obj/item/organ/external/tail/tail_organ) if(!istype(tail_organ)) return - var/icon_key = "[tail_organ.get_tail()][tail_organ.icon][tail_organ.get_tail_blend(src)][species.appearance_flags & HAS_SKIN_COLOR][skin_colour][tail_organ.get_tail_hair()][tail_organ.get_tail_hair_blend()][hair_colour]" + var/icon_key = "[tail_organ.get_tail()]\ref[tail_organ.icon][tail_organ.get_tail_blend(src)][species.appearance_flags & HAS_SKIN_COLOR][skin_colour][tail_organ.get_tail_hair()][tail_organ.get_tail_hair_blend()][hair_colour]" var/icon/tail_icon = tail_icon_cache[icon_key] if(!tail_icon) //generate a new one diff --git a/code/modules/mob/living/deity/phenomena/transmutation.dm b/code/modules/mob/living/deity/phenomena/transmutation.dm index d7d9908ae0b..7f6f20a8399 100644 --- a/code/modules/mob/living/deity/phenomena/transmutation.dm +++ b/code/modules/mob/living/deity/phenomena/transmutation.dm @@ -22,5 +22,5 @@ /datum/phenomena/rock_form/activate(var/mob/living/carbon/human/H) ..() to_chat(H, "You feel your body harden as it rapidly is transformed into living crystal!") - H.set_species(SPECIES_GOLEM) + H.change_species(SPECIES_GOLEM) SET_STATUS_MAX(H, STAT_WEAK, 5) \ No newline at end of file diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 42d5b2e3c4f..845a45ab0e3 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -188,7 +188,7 @@ default behaviour is: /mob/living/proc/updatehealth() if(status_flags & GODMODE) - health = 100 + health = maxHealth set_stat(CONSCIOUS) else health = maxHealth - getOxyLoss() - getToxLoss() - getFireLoss() - getBruteLoss() - getCloneLoss() - getHalLoss() @@ -493,21 +493,79 @@ default behaviour is: /mob/living/proc/UpdateDamageIcon() return -/mob/living/handle_grabs_after_move() +/mob/living/handle_grabs_after_move(var/turf/old_loc, var/direction) + ..() - if(!skill_check(SKILL_MEDICAL, SKILL_BASIC)) - for(var/obj/item/grab/grab in get_active_grabs()) + + if(!isturf(loc)) + for(var/G in get_active_grabs()) + qdel(G) + return + + if(isturf(old_loc)) + for(var/atom/movable/AM AS_ANYTHING in ret_grab()) + if(AM != src && AM.loc != loc && !AM.anchored && old_loc.Adjacent(AM)) + AM.glide_size = glide_size // This is adjusted by grabs again from events/some of the procs below, but doing it here makes it more likely to work with recursive movement. + AM.DoMove(get_dir(get_turf(AM), old_loc), src, TRUE) + + var/list/mygrabs = get_active_grabs() + for(var/obj/item/grab/G AS_ANYTHING in mygrabs) + if(G.assailant_reverse_facing()) + set_dir(global.reverse_dir[direction]) + G.assailant_moved() + if(QDELETED(G) || QDELETED(G.affecting)) + mygrabs -= G + + if(!length(mygrabs)) + return + + if(length(grabbed_by)) + reset_offsets() + reset_plane_and_layer() + + if(direction & (UP|DOWN)) + var/txt_dir = (direction & UP) ? "upwards" : "downwards" + if(old_loc) + old_loc.visible_message(SPAN_NOTICE("\The [src] moves [txt_dir].")) + for(var/obj/item/grab/G AS_ANYTHING in mygrabs) + var/turf/start = G.affecting.loc + var/turf/destination = (direction == UP) ? GetAbove(G.affecting) : GetBelow(G.affecting) + if(!start.CanZPass(G.affecting, direction)) + to_chat(src, SPAN_WARNING("\The [start] blocked your pulled object!")) + mygrabs -= G + qdel(G) + continue + if(!destination.CanZPass(G.affecting, direction)) + to_chat(src, SPAN_WARNING("The [G.affecting] you were pulling bumps up against \the [destination].")) + mygrabs -= G + qdel(G) + continue + for(var/atom/A in destination) + if(!A.CanMoveOnto(G.affecting, start, 1.5, direction)) + to_chat(src, SPAN_WARNING("\The [A] blocks the [G.affecting] you were pulling.")) + mygrabs -= G + qdel(G) + continue + G.affecting.forceMove(destination) + if(QDELETED(G) || QDELETED(G.affecting)) + mygrabs -= G + continue + + if(length(mygrabs) && !skill_check(SKILL_MEDICAL, SKILL_BASIC)) + for(var/obj/item/grab/grab AS_ANYTHING in mygrabs) var/mob/living/affecting_mob = grab.get_affecting_mob() if(affecting_mob) affecting_mob.handle_grab_damage() -/mob/living/Move(a, b, flag) +/mob/living/Move(NewLoc, Dir) if (buckled) return + var/turf/old_loc = loc . = ..() - handle_grabs_after_move() - if (s_active && !( s_active in contents ) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents ) first so we hopefully don't have to call get_turf() so much. - s_active.close(src) + if(.) + handle_grabs_after_move(old_loc, Dir) + if (s_active && !( s_active in contents ) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents ) first so we hopefully don't have to call get_turf() so much. + s_active.close(src) /mob/living/verb/resist() set name = "Resist" @@ -598,6 +656,7 @@ default behaviour is: return resting = !resting UpdateLyingBuckledAndVerbStatus() + update_icon() to_chat(src, SPAN_NOTICE("You are now [resting ? "resting" : "getting up"].")) //called when the mob receives a bright flash @@ -631,6 +690,10 @@ default behaviour is: /mob/living/carbon/get_contained_external_atoms() . = ..() LAZYREMOVE(., get_organs()) + //Since organs are cleared on destroy, add this separate check here + for(var/obj/item/organ/O in .) + if(!O.is_droppable()) + LAZYREMOVE(., O) /mob/proc/can_be_possessed_by(var/mob/observer/ghost/possessor) return istype(possessor) && possessor.client @@ -847,7 +910,8 @@ default behaviour is: if(!has_gravity()) return if(isturf(loc) && pull_damage() && prob(getBruteLoss() / 6)) - blood_splatter(loc, src, large = TRUE) + if (!should_have_organ(BP_HEART)) + blood_splatter(loc, src, large = TRUE) if(prob(25)) adjustBruteLoss(1) visible_message(SPAN_DANGER("\The [src]'s [isSynthetic() ? "state worsens": "wounds open more"] from being dragged!")) @@ -880,7 +944,7 @@ default behaviour is: return "Living" /mob/living/handle_mouse_drop(atom/over, mob/user) - if(user == src && user != over) + if(!anchored && user == src && user != over) if(isturf(over)) var/turf/T = over @@ -963,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 @@ -975,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() @@ -987,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/living_organs.dm b/code/modules/mob/living/living_organs.dm index 298c75ac2fa..c667f6e3321 100644 --- a/code/modules/mob/living/living_organs.dm +++ b/code/modules/mob/living/living_organs.dm @@ -20,19 +20,32 @@ /mob/living/proc/has_internal_organs() return LAZYLEN(get_internal_organs()) > 0 -//Needed for organ surgery -/mob/living/proc/add_organ(var/obj/item/organ/O, var/obj/item/organ/external/affected = null, var/in_place = FALSE, var/update_icon = TRUE) - if(!in_place) +//Can be called when we want to add an organ in a detached state or an attached state. +/mob/living/proc/add_organ(var/obj/item/organ/O, var/obj/item/organ/external/affected = null, var/in_place = FALSE, var/update_icon = TRUE, var/detached = FALSE) + . = O.do_install(src, affected, in_place, update_icon, detached) + //Only run install effects if we're not detached and we're not adding in place + if(!in_place && !(O.status & ORGAN_CUT_AWAY)) on_gained_organ(O) - . = O.do_install(src, affected, in_place, update_icon) + updatehealth() + return TRUE -/mob/living/proc/remove_organ(var/obj/item/organ/O, var/drop_organ = TRUE, var/detach = TRUE, var/ignore_children = FALSE, var/in_place = FALSE, var/update_icon = TRUE) - if(!in_place) +//Can be called when the organ is detached or attached. +/mob/living/proc/remove_organ(var/obj/item/organ/O, var/drop_organ = TRUE, var/detach = FALSE, var/ignore_children = FALSE, var/in_place = FALSE, var/update_icon = TRUE) + //Only run effects if we're not already detached, and we're not doing a in-place removal + if(!in_place && !(O.status & ORGAN_CUT_AWAY)) //Gotta check the flag here, because of prosthetics handling detached state differently on_lost_organ(O) + . = O.do_uninstall(in_place, detach, ignore_children, update_icon) + if(!in_place) + updatehealth() + + //Shall not drop undroppable things if(drop_organ) - O.dropInto(get_turf(src)) + if(O.is_droppable()) + O.dropInto(get_turf(src)) + else + qdel(O) //Should handle vital organ checks, icon updates, events /mob/living/proc/on_lost_organ(var/obj/item/organ/O) 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/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 6a5c8735375..ee6ce2ed631 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -300,7 +300,7 @@ else changed_name = "[modtype] [braintype]-[num2text(ident)]" - create_or_rename_email(changed_name, "root.rt") + create_or_update_account(changed_name) real_name = changed_name name = real_name if(mind) diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 2883201f28e..904c01b1841 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -75,7 +75,7 @@ /mob/living/silicon/fully_replace_character_name(new_name) ..() - create_or_rename_email(new_name, "root.rt") + create_or_update_account(new_name) if(istype(idcard)) idcard.registered_name = new_name diff --git a/code/modules/mob/living/simple_animal/friendly/mouse.dm b/code/modules/mob/living/simple_animal/friendly/mouse.dm index 442359e40af..566a122645f 100644 --- a/code/modules/mob/living/simple_animal/friendly/mouse.dm +++ b/code/modules/mob/living/simple_animal/friendly/mouse.dm @@ -54,10 +54,6 @@ else if(prob(5)) INVOKE_ASYNC(src, .proc/audible_emote, "snuffles.") -/mob/living/simple_animal/mouse/lay_down() - ..() - icon_state = resting ? "mouse_[body_color]_sleep" : "mouse_[body_color]" - /mob/living/simple_animal/mouse/Initialize() verbs += /mob/living/proc/ventcrawl verbs += /mob/living/proc/hide diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm index 70a67a3ba7c..4e1bbe04743 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/parrot.dm @@ -526,6 +526,8 @@ return null /mob/living/simple_animal/hostile/retaliate/parrot/proc/give_up() + if(!length(enemies)) + return enemies = list() LoseTarget() visible_message(SPAN_NOTICE("\The [src] seems to calm down.")) diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 9532faf3a58..a208a54d7d4 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -119,10 +119,10 @@ icon_state = ICON_STATE_WORLD if(stat == DEAD && (mob_icon_state_flags & MOB_ICON_HAS_DEAD_STATE)) icon_state += "-dead" - else if(!incapacitated() && resting && (mob_icon_state_flags & MOB_ICON_HAS_REST_STATE)) - icon_state += "-resting" - else if((lying || incapacitated()) && (mob_icon_state_flags & MOB_ICON_HAS_SLEEP_STATE)) + else if(stat == UNCONSCIOUS && (mob_icon_state_flags & MOB_ICON_HAS_SLEEP_STATE)) icon_state += "-sleeping" + else if(resting && (mob_icon_state_flags & MOB_ICON_HAS_REST_STATE)) + icon_state += "-resting" z_flags &= ~ZMM_MANGLE_PLANES if(stat == CONSCIOUS) diff --git a/code/modules/mob/login.dm b/code/modules/mob/login.dm index 73e9a16cdb4..c866a66e3ba 100644 --- a/code/modules/mob/login.dm +++ b/code/modules/mob/login.dm @@ -91,7 +91,7 @@ client.update_skybox(1) - if(machine) + if(istype(machine)) machine.on_user_login(src) /mob/proc/hud_reset(var/full_reset = FALSE) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index f252f1ea43c..12c8b8bb3b9 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -33,28 +33,26 @@ return ..() /mob/proc/remove_screen_obj_references() - hands = null - purged = null - internals = null - oxygen = null - i_select = null - m_select = null - toxin = null - fire = null - bodytemp = null - healths = null - throw_icon = null - nutrition_icon = null - hydration_icon = null - pressure = null - pain = null - up_hint = null - item_use_icon = null - radio_use_icon = null - gun_move_icon = null - gun_setting_icon = null - ability_master = null - zone_sel = null + QDEL_NULL_SCREEN(hands) + QDEL_NULL_SCREEN(purged) + QDEL_NULL_SCREEN(internals) + QDEL_NULL_SCREEN(oxygen) + QDEL_NULL_SCREEN(toxin) + QDEL_NULL_SCREEN(fire) + QDEL_NULL_SCREEN(bodytemp) + QDEL_NULL_SCREEN(healths) + QDEL_NULL_SCREEN(throw_icon) + QDEL_NULL_SCREEN(nutrition_icon) + QDEL_NULL_SCREEN(hydration_icon) + QDEL_NULL_SCREEN(pressure) + QDEL_NULL_SCREEN(pain) + QDEL_NULL_SCREEN(up_hint) + QDEL_NULL_SCREEN(item_use_icon) + QDEL_NULL_SCREEN(radio_use_icon) + QDEL_NULL_SCREEN(gun_move_icon) + QDEL_NULL_SCREEN(gun_setting_icon) + QDEL_NULL_SCREEN(ability_master) + QDEL_NULL_SCREEN(zone_sel) /mob/Initialize() . = ..() @@ -512,7 +510,7 @@ if(over == user && user != src && !istype(user, /mob/living/silicon/ai)) show_inv(user) return TRUE - if(istype(over, /obj/vehicle/train)) + if(!anchored && istype(over, /obj/vehicle/train)) var/obj/vehicle/train/beep = over if(!beep.load(src)) to_chat(user, SPAN_WARNING("You were unable to load \the [src] onto \the [over].")) @@ -560,8 +558,10 @@ if(client.holder) if(statpanel("MC")) - stat("CPU:","[world.cpu]") + stat("CPU:", "[Master.format_color_cpu()]") + stat("Map CPU:", "[Master.format_color_cpu_map()]") stat("Instances:","[world.contents.len]") + stat("World Time:", "[world.time]") stat(null) if(Master) Master.stat_entry() @@ -1103,3 +1103,36 @@ // We're inside, and more than one z-level below the roof, so ignore it. return WEATHER_IGNORE + +/mob/proc/IsMultiZAdjacent(var/atom/neighbor) + + var/turf/T = get_turf(src) + var/turf/N = get_turf(neighbor) + + // Not on valid turfs. + if(QDELETED(src) || QDELETED(neighbor) || !istype(T) || !istype(N)) + return FALSE + + // On the same z-level, we don't need to care about multiz. + if(N.z == T.z) + return Adjacent(neighbor) + + // More than one z-level away from each other. + if(abs(N.x - T.x) > 1 || abs(N.y - T.y) > 1 || abs(N.z - T.z) > 1) + return FALSE + + // Not in a connected z-volume. + if(!(N.z in GetConnectedZlevels(T.z))) + return FALSE + + // Are they below us? + if(N.z < T.z && HasBelow(T.z)) + var/turf/B = GetBelow(T) + return T.is_open() && neighbor.Adjacent(B) + + // Are they above us? + if(HasAbove(T.z)) + var/turf/A = GetAbove(T) + return A.is_open() && neighbor.Adjacent(A) + + return FALSE diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index af4c60d9cb0..fd2cb343be7 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -43,8 +43,6 @@ var/obj/screen/purged = null var/obj/screen/internals = null var/obj/screen/oxygen = null - var/obj/screen/i_select = null - var/obj/screen/m_select = null var/obj/screen/toxin = null var/obj/screen/fire = null var/obj/screen/bodytemp = null @@ -60,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_grabs.dm b/code/modules/mob/mob_grabs.dm index ad8ed94cad1..7040a2bd0ec 100644 --- a/code/modules/mob/mob_grabs.dm +++ b/code/modules/mob/mob_grabs.dm @@ -32,7 +32,7 @@ /mob/proc/handle_grab_damage() set waitfor = FALSE -/mob/proc/handle_grabs_after_move() +/mob/proc/handle_grabs_after_move(var/turf/old_loc, var/direction) set waitfor = FALSE /mob/proc/add_grab(var/obj/item/grab/grab, var/defer_hand = FALSE) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 3efc505088e..54280d78b16 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -244,12 +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,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 @@ -660,7 +658,7 @@ var/global/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT) if(mob.real_name == real_name) if(!mob.mind) return - return mob.mind.initial_email_login["login"] + return mob.mind.initial_account_login["login"] + "@[mob.mind.account_network]" //This gets an input while also checking a mob for whether it is incapacitated or not. /mob/proc/get_input(var/message, var/title, var/default, var/choice_type, var/obj/required_item) diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm index 9687967ec5f..7374d9610fd 100644 --- a/code/modules/mob/mob_transformation_simple.dm +++ b/code/modules/mob/mob_transformation_simple.dm @@ -47,7 +47,7 @@ if(subspecies && istype(M,/mob/living/carbon/human)) var/mob/living/carbon/human/H = M - H.set_species(subspecies) + H.change_species(subspecies) if(delete_old_mob) QDEL_IN(src, 1) diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index 1e3e66a7bd4..1e6817b0e13 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -27,7 +27,7 @@ verbs += /mob/proc/toggle_antag_pool /mob/new_player/Destroy() - panel = null + QDEL_NULL(panel) . = ..() /mob/new_player/proc/show_lobby_menu(force = FALSE) diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 9f198bde157..14c401765f8 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -44,8 +44,10 @@ return var/update_icon = FALSE - mannequin.rejuvenate() copy_to(mannequin, TRUE) + mannequin.restore_all_organs() + mannequin.sync_organ_dna() + mannequin.force_update_limbs() var/datum/job/previewJob if(equip_preview_mob) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 22f9d76397b..320add46d12 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -30,7 +30,7 @@ for(var/obj/item/W in src) drop_from_inventory(W) - set_species(species.primitive_form) + change_species(species.primitive_form) dna.SetSEState(global.MONKEYBLOCK,1) dna.SetSEValueRange(global.MONKEYBLOCK,0xDAC, 0xFFF) diff --git a/code/modules/modular_computers/computers/subtypes/dev_console.dm b/code/modules/modular_computers/computers/subtypes/dev_console.dm index 3410a3259fc..b43928efee6 100644 --- a/code/modules/modular_computers/computers/subtypes/dev_console.dm +++ b/code/modules/modular_computers/computers/subtypes/dev_console.dm @@ -1,5 +1,5 @@ /obj/machinery/computer/modular - name = "console" + name = "modular console" maximum_component_parts = list(/obj/item/stock_parts = 14) //There's a lot of stuff that goes in these var/list/interact_sounds = list("keyboard", "keystroke") var/wired_connection = FALSE // Whether or not this console will start with a wired connection beneath it. diff --git a/code/modules/modular_computers/computers/subtypes/preset_console.dm b/code/modules/modular_computers/computers/subtypes/preset_console.dm index e0f001c6a0f..903640a8c3d 100644 --- a/code/modules/modular_computers/computers/subtypes/preset_console.dm +++ b/code/modules/modular_computers/computers/subtypes/preset_console.dm @@ -79,7 +79,8 @@ /datum/computer_file/program/records, /datum/computer_file/program/docking, /datum/computer_file/program/records, - /datum/computer_file/program/wordprocessor + /datum/computer_file/program/wordprocessor, + /datum/computer_file/program/accounts ) /obj/machinery/computer/modular/preset/security diff --git a/code/modules/modular_computers/file_system/computer_file.dm b/code/modules/modular_computers/file_system/computer_file.dm index 7545feca905..0f7ea405b4e 100644 --- a/code/modules/modular_computers/file_system/computer_file.dm +++ b/code/modules/modular_computers/file_system/computer_file.dm @@ -1,15 +1,24 @@ var/global/file_uid = 0 +#define OS_READ_ACCESS BITFLAG(0) +#define OS_WRITE_ACCESS BITFLAG(1) +#define OS_MOD_ACCESS BITFLAG(2) + /datum/computer_file var/filename = "NewFile" // Placeholder. No spacebars var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case var/size = 1 // File size in GQ. Integers only! - var/obj/item/stock_parts/computer/hard_drive/holder // Holder that contains this file. + var/obj/item/stock_parts/computer/hard_drive/holder // Holder that contains this file. var/unsendable = 0 // Whether the file may be sent to someone via file transfer or other means. var/undeletable = 0 // Whether the file may be deleted. Setting to 1 prevents deletion/renaming/etc. var/uid // UID of this file - var/list/metadata // Any metadata the file uses. + var/list/metadata // Any metadata the file uses. var/papertype = /obj/item/paper + var/copy_string = "(Copy)" + + var/list/read_access + var/list/write_access + var/list/mod_access /datum/computer_file/New(var/list/md = null) ..() @@ -34,8 +43,99 @@ var/global/file_uid = 0 if(metadata) temp.metadata = metadata.Copy() if(rename) - temp.filename = filename + "(Copy)" + temp.filename = filename + copy_string else temp.filename = filename temp.filetype = filetype - return temp \ No newline at end of file + temp.read_access = read_access + temp.write_access = write_access + temp.mod_access = mod_access + return temp + +/datum/computer_file/proc/get_file_perms(var/list/accesses, var/mob/user) + . = 0 + if(!accesses || (isghost(user) && check_rights(R_ADMIN, 0, user))) // No access list past means internal usage, so grant full access. Also override for use by admin ghosts. + return (OS_READ_ACCESS | OS_WRITE_ACCESS | OS_MOD_ACCESS) + if(!LAZYLEN(read_access) || has_access(read_access, accesses)) + . |= OS_READ_ACCESS + if(!LAZYLEN(write_access) || has_access(write_access, accesses)) + . |= OS_WRITE_ACCESS + if(!LAZYLEN(mod_access) || has_access(mod_access, accesses)) + . |= OS_MOD_ACCESS + +/datum/computer_file/proc/get_perms_readable() + var/list/msg = list() + msg += "Permissions for file [filename]:" + var/read_perms + if(LAZYLEN(read_access)) + read_perms = english_list(read_access[1]) + else + read_perms = "No access required." + msg += "Read access: [read_perms]" + var/write_perms + if(LAZYLEN(write_access)) + write_perms = english_list(write_access[1]) + else + write_perms = "No access required." + msg += "Write access: [write_perms]" + var/mod_perms + if(LAZYLEN(mod_access)) + mod_perms = english_list(mod_access[1]) + else + mod_perms = "No access required." + msg += "Permission modification access: [mod_perms]" + return msg + +/datum/computer_file/proc/change_perms(var/change, var/perm, var/access_key, var/changer_accesses) + + if(!has_access(mod_access, changer_accesses)) + return FALSE + + if(perm == (OS_READ_ACCESS || OS_WRITE_ACCESS)) + var/list/modded_list = (perm == OS_READ_ACCESS ? read_access : write_access) + if(change == "+") + if(!LAZYLEN(modded_list)) + modded_list = list() + modded_list += list(list()) + modded_list[1] += access_key + return TRUE + else if(change == "-") + if(!LAZYLEN(modded_list)) // There weren't any access requirements to begin with. + return TRUE + modded_list[1] -= access_key + if(!length(modded_list[1])) + modded_list[1] = null + modded_list = null + return TRUE + else + return FALSE // Something unexpected was passed into the change argument. + + else if(perm == OS_MOD_ACCESS) + var/list/test_list // You can't modify access such that you can't access the file any longer, so we test changes first. + if(change == "+") + if(!LAZYLEN(mod_access)) + test_list = list(list(access_key)) + if(!has_access(test_list, changer_accesses)) + return FALSE + mod_access = list() + mod_access += list(list()) + mod_access[1] += access_key + return TRUE + test_list = list(mod_access[1] + access_key) + if(!has_access(test_list, changer_accesses)) + return FALSE + mod_access[1] += access_key + return TRUE + else if(change == "-") + if(!LAZYLEN(mod_access)) + return TRUE + test_list = list(mod_access[1] - access_key) + if(!has_access(test_list, changer_accesses)) + return FALSE + mod_access[1] -= access_key + if(!length(mod_access[1])) + mod_access[1] = null + mod_access = null + return TRUE + else + return FALSE \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/grant.dm b/code/modules/modular_computers/file_system/grant.dm deleted file mode 100644 index 543dbe4817a..00000000000 --- a/code/modules/modular_computers/file_system/grant.dm +++ /dev/null @@ -1,8 +0,0 @@ -/datum/computer_file/data/grant_record - filetype = "GRT" - size = 2 - do_not_edit = 1 // Whether the user will be reminded that the file probably shouldn't be edited. - -/datum/computer_file/data/grant_record/proc/set_value(var/value) - stored_data = replacetext(sanitize(uppertext(value)), " ", "_") - filename = stored_data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index a3a8a6956c2..abfbab7e49d 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -2,9 +2,8 @@ /datum/computer_file/program filetype = "PRG" filename = "UnknownProgram" // File name. FILE NAME MUST BE UNIQUE IF YOU WANT THE PROGRAM TO BE DOWNLOADABLE FROM NETWORK! - var/list/required_access = list() // List of required accesses to run/download the program. - var/requires_access_to_run = 1 // Whether the program checks for required_access when run. - var/requires_access_to_download = 1 // Whether the program checks for required_access when downloading. + var/requires_access_to_run = 1 // Whether the program checks for read_access when run. + var/requires_access_to_download = 1 // Whether the program checks for read_access when downloading. var/datum/nano_module/NM = null // If the program uses NanoModule, put it here and it will be automagically opened. Otherwise implement ui_interact. var/nanomodule_path = null // Path to nanomodule, make sure to set this if implementing new program. var/program_state = PROGRAM_STATE_KILLED // PROGRAM_STATE_KILLED or PROGRAM_STATE_BACKGROUND or PROGRAM_STATE_ACTIVE - specifies whether this program is running. @@ -19,12 +18,14 @@ var/requires_network_feature = 0 // Optional, if above is set to 1 checks for specific function of network (currently NETWORK_SOFTWAREDOWNLOAD, NETWORK_PEERTOPEER, NETWORK_SYSTEMCONTROL and NETWORK_COMMUNICATION) var/usage_flags = PROGRAM_ALL & ~PROGRAM_PDA // Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET, PROGRAM_PDA combination) or PROGRAM_ALL var/network_destination = null // Optional string that describes what network server/system this program connects to. Used in default logging. - var/available_on_network = 1 // Whether the program can be downloaded from network. Set to 0 to disable. + var/available_on_network = 1 // Whether the program can be downloaded from network. Set to 0 to disable. var/available_on_syndinet = 0 // Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable. var/computer_emagged = 0 // Set to 1 if computer that's running us was emagged. Computer updates this every Process() tick var/ui_header = null // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /nano/images/status_icons. Be careful not to use too large images! var/operator_skill = SKILL_MIN // Holder for skill value of current/recent operator for programs that tick. + mod_access = list(list(access_network)) + /datum/computer_file/program/Destroy() if(computer && computer.active_program == src) computer.kill_program(src) @@ -36,7 +37,7 @@ /datum/computer_file/program/clone() var/datum/computer_file/program/temp = ..() - temp.required_access = required_access + temp.read_access = read_access temp.nanomodule_path = nanomodule_path temp.filedesc = filedesc temp.program_icon_state = program_icon_state @@ -83,37 +84,16 @@ // Check if the user can run program. Only humans can operate computer. Automatically called in run_program() // User has to wear their ID or have it inhand for ID Scan to work. // Can also be called manually, with optional parameter being access_to_check to scan the user's ID -/datum/computer_file/program/proc/can_run(var/mob/living/user, var/loud = 0, var/list/accesses_to_check, var/datum/computer_network/network) +/datum/computer_file/program/proc/can_run(var/list/accesses, var/mob/user, var/loud = 0) if(!requires_access_to_run) return TRUE - // Checks to see if network access is enabled, and then defaults to required_access if not. - if(!accesses_to_check) - if(network) - var/datum/extension/network_device/acl/access_controller = network.access_controller - if(access_controller && access_controller.program_control) - accesses_to_check = access_controller.get_program_access(filename) - - if(!length(accesses_to_check)) - accesses_to_check = required_access - - if(!length(accesses_to_check)) // No required_access, allow it. - return TRUE - // Admin override - allows operation of any computer as aghosted admin, as if you had any required access. - if(isghost(user) && check_rights(R_ADMIN, 0, user)) + if(get_file_perms(accesses, user) & OS_READ_ACCESS) return TRUE if(!istype(user)) return FALSE - var/obj/item/card/id/I = user.GetIdCard() - if(!I) - if(loud) - to_chat(user, SPAN_WARNING("The OS flashes an \"RFID Error - Unable to scan ID\" warning.")) - return FALSE - - if(has_access(accesses_to_check, I.access)) - return TRUE if(loud) to_chat(user, SPAN_WARNING("The OS flashes an \"Access Denied\" warning.")) return FALSE @@ -133,11 +113,16 @@ if(nanomodule_path) NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src) if(user) - NM.using_access = user.GetAccess() + NM.using_access = computer.get_access() // Programs nab access from users in their get_access() proc so don't bother adding it + // to the using list as well. if(requires_network && network_destination) generate_network_log("Connection opened to [network_destination].") return 1 +/datum/computer_file/program/proc/update_access() + if(NM) + NM.using_access = computer.get_access() + // Use this proc to kill the program. Designed to be implemented by each program if it requires on-quit logic, such as the NTNRC client. /datum/computer_file/program/proc/on_shutdown(var/forced = 0) SHOULD_CALL_PARENT(TRUE) diff --git a/code/modules/modular_computers/file_system/programs/command/accounts.dm b/code/modules/modular_computers/file_system/programs/command/accounts.dm new file mode 100644 index 00000000000..c38136c1c59 --- /dev/null +++ b/code/modules/modular_computers/file_system/programs/command/accounts.dm @@ -0,0 +1,280 @@ +#define STATE_ERROR -1 +#define STATE_MENU 0 +#define STATE_SELF 1 +#define STATE_OTHER 2 + +/datum/computer_file/program/accounts + filename = "accountman" + filedesc = "Account management program" + nanomodule_path = /datum/nano_module/program/accounts + program_icon_state = "id" + program_key_state = "id_key" + program_menu_icon = "key" + extended_desc = "Program for managing network accounts and group membership." + size = 8 + category = PROG_COMMAND + +/datum/nano_module/program/accounts + name = "Account management program" + var/prog_state = STATE_MENU + var/datum/computer_file/data/account/selected_account + var/selected_parent_group + +/datum/nano_module/program/accounts/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = global.default_topic_state) + var/list/data = host.initial_data() + var/datum/computer_network/network = get_network() + if(!network) + data["error"] = "No accounts found. Check network connectivity." + prog_state = STATE_ERROR + switch(prog_state) + if(STATE_SELF) + data["account_name"] = selected_account.login + data["account_password"] = selected_account.password + data["account_fullname"] = selected_account.fullname + data["account_groups"] = selected_account.groups + if(STATE_OTHER) // Modifying other accounts. + var/datum/extension/network_device/acl/net_acl = network.access_controller + if(!net_acl) + data["error"] = "No access controller found on the network!" + prog_state = STATE_ERROR + else + var/list/group_dict = net_acl.get_group_dict() + if(!istype(selected_account)) + var/list/accounts = network.get_accounts() + data["accounts"] = list() + for(var/datum/computer_file/data/account/A in accounts) + data["accounts"] += list(list( + "account" = A.login, + "fullname" = A.fullname + )) + else + data["account_name"] = selected_account.login + data["account_fullname"] = selected_account.fullname + if(!selected_parent_group) + data["parent_groups"] = list() + for(var/parent_group in group_dict) + data["parent_groups"] += list(list( + "name" = parent_group, + "member" = (parent_group in selected_account.groups) + )) + else + var/list/child_groups = group_dict[selected_parent_group] + if(!child_groups) + data["error"] = "Invalid parent selected!" + selected_parent_group = null + return + data["parent_group"] = selected_parent_group + data["child_groups"] = list() + for(var/child_group in child_groups) + data["child_groups"] += list(list( + "name" = child_group, + "member" = (child_group in selected_account.groups) + )) + data["sub_management"] = net_acl.allow_submanagement + data["prog_state"] = prog_state + ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) + if (!ui) + ui = new(user, src, ui_key, "account_management.tmpl", name, 600, 700, state = state) + ui.auto_update_layout = 1 + ui.set_initial_data(data) + ui.open() + +/datum/computer_file/program/accounts/Topic(href, href_list, state) + if(..()) + return 1 + + var/mob/user = usr + + var/datum/computer_network/network = computer.get_network() + var/datum/nano_module/program/accounts/module = NM + if(!network) + return + switch(module.prog_state) + if(STATE_MENU) + if(href_list["self_mode"]) + var/datum/computer_file/data/account/A = computer.get_account() + if(A) + module.selected_account = A + module.prog_state = STATE_SELF + return TOPIC_REFRESH + else + to_chat(user, SPAN_WARNING("No account found. Please login through your system before reattempting.")) + return TOPIC_HANDLED + + if(href_list["other_mode"]) + module.selected_account = null // Reset selected account just in case. + module.prog_state = STATE_OTHER + return TOPIC_REFRESH + + if(STATE_SELF) + if(!module.selected_account || computer.get_account() != module.selected_account) + module.selected_account = null + module.prog_state = STATE_MENU + to_chat(user, SPAN_WARNING("Authentication error. Please relogin to your account.")) + return TOPIC_REFRESH + + if(href_list["change_password"]) + var/new_password = input(user, "Input new account password.", "Change Password", module.selected_account.password) as text|null + var/password_check = alert(user, "Your new password will be [new_password]. Is this alright?", "Change Password", "Yes", "No") + if(password_check == "Yes") + if(!CanInteract(user,state)) + return TOPIC_HANDLED + module.selected_account.password = new_password + // Update the program's password too so the user doesn't have to login again. + computer.password = new_password + return TOPIC_REFRESH + return TOPIC_HANDLED + + if(href_list["change_fullname"]) + var/new_fullname = input(user, "Input your full name.", "Change name", module.selected_account.fullname) as text|null + if(!CanInteract(user,state)) + return TOPIC_HANDLED + module.selected_account.fullname = new_fullname + return TOPIC_REFRESH + + if(STATE_OTHER) + if(href_list["select_account"]) + var/account_login = href_list["select_account"] + for(var/datum/computer_file/data/account/A in network.get_accounts()) + if(A.login == account_login) + module.selected_account = A + return TOPIC_REFRESH + + // Further actions require the ACL to be present on the network, so check if it exists first. + var/datum/extension/network_device/acl/net_acl = network.access_controller + if(!net_acl) + module.selected_parent_group = null // Just in case. + return TOPIC_REFRESH + var/list/group_dict = net_acl.get_group_dict() + + if(href_list["create_account"]) + var/list/account_creation_access + + // Passing null access to create_account will ignore access checks on account servers, so only assign this + // if the user doesn't have parent group account creation authorization. + if(!net_acl.check_account_creation_auth(computer.get_account())) + account_creation_access = NM.get_access(user) + var/new_login = input(user, "Input new account login. Login will be sanitized.", "Account Creation") as text|null + + var/login_check = alert(user, "Account login will be [sanitize_for_account(new_login)]. Is this acceptable?","Account Creation", "Yes", "No") + if(login_check == "Yes") + var/new_password = input(user, "Input new account password.", "Account Creation") as text|null + var/new_fullname = input(user, "Enter full name for account (e.g. John Smith)", "Account Creation") as text|null + if(!CanInteract(user,state)) + return TOPIC_HANDLED + if(network.create_account(null, new_login, new_password, new_fullname, account_creation_access, FALSE)) + to_chat(user, SPAN_NOTICE("Account successfully created!")) + return TOPIC_REFRESH + else + to_chat(user, SPAN_WARNING("Account could not be created. It's possible an account with a matching login already exists, or you lack access to account servers.")) + return TOPIC_HANDLED + else + to_chat(user, SPAN_NOTICE("Account creation aborted.")) + return TOPIC_HANDLED + + if(href_list["recover_account"]) + var/list/account_recovery_access + + if(!net_acl.check_account_creation_auth(computer.get_account())) + account_recovery_access = NM.get_access(user) + + var/list/backups = network.get_account_backups(account_recovery_access) + if(!length(backups)) + to_chat(user, SPAN_WARNING("No account backups found on account servers!")) + return TOPIC_HANDLED + + var/selected_login = input(user, "Select the account backup you would like to recover:", "Account Backups") as anything in backups + if(!CanInteract(user, state) || !selected_login) + return TOPIC_HANDLED + + if(network.find_account_by_login(selected_login)) + to_chat(user, SPAN_WARNING("An account with that login already exists on the network! Cannot recover backup.")) + return TOPIC_HANDLED + + var/datum/computer_file/data/account/backup = backups[selected_login] + backup.backup = FALSE + backup.filename = replacetext(backup.filename, backup.copy_string, null) // Remove the backup signifier on the file. + to_chat(user, SPAN_NOTICE("Successfully recovered account [selected_login] from backup!")) + return TOPIC_REFRESH + + if(href_list["mod_group"]) + if(!module.selected_account || QDELETED(module.selected_account)) + module.selected_parent_group = null + return TOPIC_REFRESH + var/group_name = href_list["mod_group"] + var/is_parent_group = (group_name in group_dict) + if(!is_parent_group && !LAZYISIN(group_dict[module.selected_parent_group], group_name)) // Safety check. + module.selected_parent_group = null + return TOPIC_REFRESH + // This is a parent group, or group submanagement is disabled - no matter what, direct access to the ACL is required. + if(is_parent_group || !net_acl.allow_submanagement) + if(!net_acl.has_access(module.get_access(user))) + to_chat(user, SPAN_WARNING("Access denied.")) + return TOPIC_HANDLED + if(group_name in module.selected_account.groups) + module.selected_account.groups -= group_name + else + module.selected_account.groups += group_name + else + // Manual group membership check since the normal parent group access string normally grants child group members access. Modifying child groups is permitted if the program account + // is a member of the parent group. + var/datum/computer_file/data/account/A = computer.get_account() + var/is_parent_member = (module.selected_parent_group in A.groups) + if(is_parent_member || net_acl.has_access(module.get_access(user))) + if(group_name in module.selected_account.groups) + module.selected_account.groups -= group_name + else + module.selected_account.groups += group_name + else + to_chat(user, SPAN_WARNING("Access denied.")) + return TOPIC_HANDLED + + // Check to see if the account still has membership in a child group of the parent group and add to the parent groups list. + if(!is_parent_group) + for(var/group in module.selected_account.groups) + if(LAZYISIN(group_dict[module.selected_parent_group], group_name)) + module.selected_account.parent_groups |= module.selected_parent_group + return TOPIC_REFRESH + module.selected_account.parent_groups -= module.selected_parent_group + + if(href_list["select_parent_group"]) + if(module.selected_parent_group) + return TOPIC_REFRESH + var/parent_group = href_list["select_parent_group"] + if(parent_group in group_dict) + // In order to see child group membership, you need submanagement access or direct ACL access. + if(!net_acl.has_access(module.get_access(user))) + if(net_acl.allow_submanagement) + var/list/child_access = list("[parent_group].[network.network_id]") + if(!has_access(child_access, module.get_access(user))) + to_chat(user, SPAN_WARNING("Access denied.")) + return TOPIC_HANDLED + else + to_chat(user, SPAN_WARNING("Access denied.")) + return TOPIC_HANDLED + module.selected_parent_group = parent_group + return TOPIC_REFRESH + return TOPIC_HANDLED + + if(href_list["back"]) + switch(module.prog_state) + if(STATE_ERROR) + module.prog_state = STATE_MENU + if(STATE_SELF) + module.prog_state = STATE_MENU + module.selected_account = null + if(STATE_OTHER) + if(module.selected_parent_group) + module.selected_parent_group = null + else if(module.selected_account) + module.selected_account = null + else + module.prog_state = STATE_MENU + return TOPIC_REFRESH + SSnano.update_uis(module) + return 1 + +#undef STATE_ERROR +#undef STATE_MENU +#undef STATE_SELF +#undef STATE_OTHER \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index ee2d1ae7109..1866a762abd 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -7,6 +7,7 @@ program_menu_icon = "key" extended_desc = "Program for programming crew ID cards." size = 8 + write_access = list(access_change_ids) category = PROG_COMMAND /datum/nano_module/program/card_mod @@ -25,15 +26,15 @@ data["assignments"] = show_assignments data["have_id_slot"] = !!card_slot data["have_printer"] = program.computer.has_component(PART_PRINTER) - data["authenticated"] = program.can_run(user) + data["authenticated"] = program.get_file_perms(get_access(user), user) & OS_WRITE_ACCESS if(!data["have_id_slot"] || !data["have_printer"]) mod_mode = 0 //We can't modify IDs when there is no card reader if(card_slot) var/obj/item/card/id/id_card = card_slot.stored_card data["has_id"] = !!id_card data["id_account_number"] = id_card ? id_card.associated_account_number : null - data["id_email_login"] = id_card ? id_card.associated_email_login["login"] : null - data["id_email_password"] = id_card ? stars(id_card.associated_email_login["password"], 0) : null + data["network_account_login"] = id_card ? id_card.associated_network_account["login"] : null + data["network_account_password"] = id_card ? stars(id_card.associated_network_account["password"], 0) : null data["id_rank"] = id_card && id_card.assignment ? id_card.assignment : "Unassigned" data["id_owner"] = id_card && id_card.registered_name ? id_card.registered_name : "-----" data["id_name"] = id_card ? id_card.name : "-----" @@ -149,34 +150,33 @@ else module.show_assignments = 1 if("print") - if(!authorized(user_id_card)) - to_chat(usr, "Access denied.") + if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) + to_chat(usr, SPAN_WARNING("Access denied.")) return if(computer.has_component(PART_PRINTER)) //This option should never be called if there is no printer if(module.mod_mode) - if(can_run(user, 1)) - var/contents = {"

    Access Report

    - Prepared By: [user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
    - For: [id_card.registered_name ? id_card.registered_name : "Unregistered"]
    -
    - Assignment: [id_card.assignment]
    - Account Number: #[id_card.associated_account_number]
    - Email account: [id_card.associated_email_login["login"]] - Email password: [stars(id_card.associated_email_login["password"], 0)] - Blood Type: [id_card.blood_type]

    - Age: [id_card.age]

    - Sex: [id_card.sex]

    - Access:
    - "} + var/contents = {"

    Access Report

    + Prepared By: [user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
    + For: [id_card.registered_name ? id_card.registered_name : "Unregistered"]
    +
    + Assignment: [id_card.assignment]
    + Account Number: #[id_card.associated_account_number]
    + Network account: [id_card.associated_network_account["login"]] + Network password: [stars(id_card.associated_network_account["password"], 0)] + Blood Type: [id_card.blood_type]

    + Age: [id_card.age]

    + Sex: [id_card.sex]

    + Access:
    + "} - var/known_access_rights = get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM) - for(var/A in id_card.access) - if(A in known_access_rights) - contents += " [get_access_desc(A)]" + var/known_access_rights = get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM) + for(var/A in id_card.access) + if(A in known_access_rights) + contents += " [get_access_desc(A)]" - if(!computer.print_paper(contents,"access report")) - to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") - return + if(!computer.print_paper(contents,"access report")) + to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") + return else var/contents = {"

    Crew Manifest


    @@ -192,18 +192,18 @@ else card_slot.insert_id(user.get_active_hand(), user) if("terminate") - if(!authorized(user_id_card)) - to_chat(usr, "Access denied.") + if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) + to_chat(usr, SPAN_WARNING("Access denied.")) return - if(computer && can_run(user, 1)) + if(computer) id_card.assignment = "Terminated" remove_nt_access(id_card) callHook("terminate_employee", list(id_card)) if("edit") - if(!authorized(user_id_card)) - to_chat(usr, "Access denied.") + if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) + to_chat(usr, SPAN_WARNING("Access denied.")) return - if(computer && can_run(user, 1)) + if(computer) var/static/regex/hash_check = regex(@"^[0-9a-fA-F]{32}$") if(href_list["name"]) var/temp_name = sanitize_name(input("Enter name.", "Name", id_card.registered_name),allow_numbers=TRUE) @@ -216,12 +216,12 @@ else if(href_list["account"]) var/account_num = text2num(input("Enter account number.", "Account", id_card.associated_account_number)) id_card.associated_account_number = account_num - else if(href_list["elogin"]) - var/email_login = input("Enter email login.", "Email login", id_card.associated_email_login["login"]) - id_card.associated_email_login["login"] = email_login - else if(href_list["epswd"]) - var/email_password = input("Enter email password.", "Email password") - id_card.associated_email_login["password"] = email_password + else if(href_list["alogin"]) + var/account_login = input("Enter network account login.", "Network account login", id_card.associated_network_account["login"]) + id_card.associated_network_account["login"] = account_login + else if(href_list["apswd"]) + var/account_password = input("Enter network account password.", "Network account password") + id_card.associated_network_account["password"] = account_password else if(href_list["sex"]) var/sex = input("Type gender.", "Gender", id_card.sex) if(!isnull(sex) && CanUseTopic(user)) @@ -274,10 +274,10 @@ remove_nt_access(id_card) apply_access(id_card, access) if("assign") - if(!authorized(user_id_card)) - to_chat(usr, "Access denied.") + if(!(get_file_perms(module.get_access(user), user) & OS_WRITE_ACCESS)) + to_chat(usr, SPAN_WARNING("Access denied.")) return - if(computer && can_run(user, 1) && id_card) + if(computer && id_card) var/t1 = href_list["assign_target"] if(t1 == "Custom") var/temp_t = sanitize(input("Enter a custom job assignment.","Assignment", id_card.assignment), 45) @@ -298,11 +298,11 @@ callHook("reassign_employee", list(id_card)) if("access") - if(href_list["allowed"] && computer && can_run(user, 1) && id_card) + if(href_list["allowed"] && id_card) var/access_type = href_list["access_target"] var/access_allowed = text2num(href_list["allowed"]) if(access_type in get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM)) - for(var/access in user_id_card.access) + for(var/access in module.get_access(user)) var/region_type = get_access_region_by_id(access_type) if(access in global.using_map.access_modify_region[region_type]) id_card.access -= access_type @@ -319,7 +319,4 @@ id_card.access -= get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM) /datum/computer_file/program/card_mod/proc/apply_access(var/obj/item/card/id/id_card, var/list/accesses) - id_card.access |= accesses - -/datum/computer_file/program/card_mod/proc/authorized(var/obj/item/card/id/id_card) - return id_card && (access_change_ids in id_card.access) \ No newline at end of file + id_card.access |= accesses \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/command/comm.dm b/code/modules/modular_computers/file_system/programs/command/comm.dm index 6c8c5d4ffed..832381f2deb 100644 --- a/code/modules/modular_computers/file_system/programs/command/comm.dm +++ b/code/modules/modular_computers/file_system/programs/command/comm.dm @@ -11,7 +11,7 @@ program_menu_icon = "flag" nanomodule_path = /datum/nano_module/program/comm extended_desc = "Used to command and control. Can relay long-range communications. This program can not be run on tablet computers." - required_access = list(access_bridge) + read_access = list(access_bridge) requires_network = 1 size = 12 usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP @@ -109,7 +109,7 @@ /datum/nano_module/program/comm/proc/is_authenticated(var/mob/user) if(program) - return program.can_run(user, program.computer.get_network()) + return program.get_file_perms(get_access(user), user) & OS_READ_ACCESS return 1 /datum/nano_module/program/comm/proc/get_shunt() diff --git a/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm index 332e6bb3185..6fc8fc0269e 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/atmos_control.dm @@ -6,7 +6,7 @@ program_key_state = "atmos_key" program_menu_icon = "shuffle" extended_desc = "This program allows remote control of air alarms. This program can not be run on tablet computers." - required_access = list(access_atmospherics) + read_access = list(access_atmospherics) requires_network = 1 network_destination = "atmospheric control system" requires_network_feature = NETWORK_SYSTEMCONTROL diff --git a/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm b/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm index ba3815318ca..a1c2c3a4c50 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/network_monitoring.dm @@ -51,7 +51,7 @@ mainframes.Add(list(rdata)) data["mainframes"] = mainframes - if(length(network.get_mainframes_by_role(MF_ROLE_LOG_SERVER, user))) + if(length(network.get_mainframes_by_role(MF_ROLE_LOG_SERVER, user.GetAccess()))) var/list/logs[0] for(var/datum/extension/network_device/mainframe/M in network.get_mainframes_by_role(MF_ROLE_LOG_SERVER, user)) var/list/logdata[0] diff --git a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm index 5e65a99be1f..d1f4da5b5d6 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/power_monitor.dm @@ -7,7 +7,7 @@ program_menu_icon = "battery-3" extended_desc = "This program connects to sensors to provide information about electrical systems" ui_header = "power_norm.gif" - required_access = list(access_engine) + read_access = list(access_engine) requires_network = 1 network_destination = "power monitoring system" size = 9 @@ -102,7 +102,7 @@ /datum/nano_module/program/power_monitor/proc/is_sysadmin(var/mob/user) if(program) - return program.can_run(user, program.computer.get_network()) + has_access(list(access_network), get_access(user)) return FALSE // Allows us to process UI clicks, which are relayed in form of hrefs. diff --git a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm index 6a9eceb4b5c..97c913b9219 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/rcon_console.dm @@ -6,7 +6,7 @@ program_key_state = "rd_key" program_menu_icon = "power" extended_desc = "This program allows remote control of power distribution systems. This program can not be run on tablet computers." - required_access = list(access_engine) + read_access = list(access_engine) network_destination = "RCON remote control system" requires_network_feature = NETWORK_SYSTEMCONTROL usage_flags = PROGRAM_LAPTOP | PROGRAM_CONSOLE diff --git a/code/modules/modular_computers/file_system/programs/engineering/shields_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/shields_monitor.dm index 2fb63ebc2d0..be8ff5a3b5a 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/shields_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/shields_monitor.dm @@ -13,13 +13,13 @@ /datum/nano_module/program/shields_monitor name = "Shields monitor" - var/obj/machinery/power/shield_generator/active = null + var/obj/machinery/shield_generator/active = null /datum/nano_module/program/shields_monitor/Destroy() . = ..() deselect_shield() -/datum/nano_module/program/shields_monitor/proc/can_connect_to_shield(obj/machinery/power/shield_generator/S) +/datum/nano_module/program/shields_monitor/proc/can_connect_to_shield(obj/machinery/shield_generator/S) var/datum/computer_network/network = get_network() if(!network) return FALSE @@ -27,7 +27,7 @@ /datum/nano_module/program/shields_monitor/proc/get_shields() var/list/shields = list() - for(var/obj/machinery/power/shield_generator/S in SSmachines.machinery) + for(var/obj/machinery/shield_generator/S in SSmachines.machinery) if(!can_connect_to_shield(S)) continue shields.Add(S) @@ -71,7 +71,7 @@ data["active"] = null var/list/shields = get_shields() var/list/shields_info = list() - for(var/obj/machinery/power/shield_generator/S in shields) + for(var/obj/machinery/shield_generator/S in shields) var/area/A = get_area(S) var/list/temp = list(list( "shield_status" = S.running, @@ -100,7 +100,7 @@ return 1 if( href_list["ref"] ) var/list/shields = get_shields() - var/obj/machinery/power/shield_generator/S = locate(href_list["ref"]) in shields + var/obj/machinery/shield_generator/S = locate(href_list["ref"]) in shields if(S) deselect_shield() events_repository.register(/decl/observ/destroyed, S, src, /datum/nano_module/program/shields_monitor/proc/deselect_shield) diff --git a/code/modules/modular_computers/file_system/programs/engineering/shutoff_valve.dm b/code/modules/modular_computers/file_system/programs/engineering/shutoff_valve.dm index eefb86db80b..c18c96809a1 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/shutoff_valve.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/shutoff_valve.dm @@ -6,7 +6,7 @@ program_key_state = "atmos_key" program_menu_icon = "shuffle" extended_desc = "This program allows remote control and monitoring of shutoff valves." - required_access = list(access_atmospherics) + read_access = list(access_atmospherics) requires_network = 1 network_destination = "atmospheric control system" requires_network_feature = NETWORK_SYSTEMCONTROL diff --git a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm b/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm index 5d4b99c0716..ec384fa9aaf 100644 --- a/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm +++ b/code/modules/modular_computers/file_system/programs/engineering/supermatter_monitor.dm @@ -11,7 +11,7 @@ program_menu_icon = "notice" extended_desc = "This program connects to specially calibrated supermatter sensors to provide information on the status of supermatter-based engines." ui_header = "smmon_0.gif" - required_access = list(access_engine) + read_access = list(access_engine) network_destination = "supermatter monitoring system" size = 5 category = PROG_ENG diff --git a/code/modules/modular_computers/file_system/programs/generic/deck_management.dm b/code/modules/modular_computers/file_system/programs/generic/deck_management.dm index f515202ac57..a9c4306f9a4 100644 --- a/code/modules/modular_computers/file_system/programs/generic/deck_management.dm +++ b/code/modules/modular_computers/file_system/programs/generic/deck_management.dm @@ -7,7 +7,7 @@ filename = "deckmngr" filedesc = "Deck Management" nanomodule_path = /datum/nano_module/deck_management - required_access = list(list(access_mining, access_cargo, access_bridge)) + read_access = list(list(access_mining, access_cargo, access_bridge)) program_icon_state = "request" program_key_state = "rd_key" program_menu_icon = "clock" @@ -103,7 +103,7 @@ L["name"] = report.display_name() L["index"] = i L["exists"] = locate(report) in selected_mission.other_reports - L["access_edit"] = report.verify_access_edit(get_access(user)) + L["access_edit"] = report.get_file_perms(get_access(user), user) & OS_WRITE_ACCESS other_reports += list(L) data["other_reports"] = other_reports @@ -113,7 +113,7 @@ if(!istype(selected_report)) prog_state = DECK_MISSION_DETAILS return - data["report_data"] = selected_report.generate_nano_data(get_access(user)) + data["report_data"] = selected_report.generate_nano_data(get_access(user), user) data["shuttle_name"] = selected_shuttle.name data["mission_data"] = generate_mission_data(selected_mission) data["view_only"] = can_view_only @@ -185,12 +185,22 @@ prototype_shuttle = selected_shuttle report_prototypes = list() for(var/report_type in subtypesof(/datum/computer_file/report/recipient/shuttle)) - var/datum/computer_file/report/recipient/shuttle/new_report = new report_type - if(new_report.access_shuttle) - new_report.set_access(null, selected_shuttle.logging_access, override = 0) - report_prototypes += new_report + report_prototypes += create_report(report_type, selected_shuttle) return 1 +/datum/nano_module/deck_management/proc/create_report(report_type, datum/shuttle/given_shuttle) + var/datum/computer_file/report/recipient/shuttle/new_report = new report_type + if(new_report.access_shuttle && given_shuttle.logging_access) + var/old_access = new_report.write_access?.Copy() + var/new_access = list() + for(var/group in old_access) // We add logging_access as an OR option to every AND requirement + var/new_group = list() + new_group += group // this listifies it if it was not already a list + new_group |= given_shuttle.logging_access + new_access += list(new_group) + new_report.set_access(null, new_access, TRUE) + return new_report + /datum/nano_module/deck_management/proc/set_mission(mission_ID) var/datum/shuttle_log/my_log = SSshuttle.shuttle_logs[selected_shuttle] var/datum/shuttle_mission/mission = my_log.mission_from_ID(mission_ID) @@ -256,8 +266,7 @@ if(selected_mission.flight_plan) selected_report = selected_mission.flight_plan.clone()//We always make a new one to buffer changes until submitted. else - selected_report = new /datum/computer_file/report/flight_plan - selected_report.set_access(null, selected_shuttle.logging_access, override = 0) + selected_report = create_report(/datum/computer_file/report/flight_plan, selected_shuttle) else if(selected_mission.stage in list(SHUTTLE_MISSION_PLANNED, SHUTTLE_MISSION_QUEUED)) return 1 //Hold your horses until the mission is started on these reports. @@ -287,14 +296,14 @@ return 1 var/field_ID = text2num(href_list["ID"]) var/datum/report_field/field = selected_report.field_from_ID(field_ID) - if(!field || !field.verify_access_edit(get_access(user))) + if(!field || !(field.get_perms(get_access(user), user) & OS_WRITE_ACCESS)) return 1 field.ask_value(user) //Handles the remaining IO. return 1 if(href_list["submit"]) if(!ensure_valid_mission() || !selected_report) return 1 - if(!selected_report.verify_access_edit(get_access(user))) + if(!(selected_report.get_file_perms(get_access(user), user) & OS_WRITE_ACCESS)) return 1 var/datum/shuttle_log/my_log = SSshuttle.shuttle_logs[selected_shuttle] if(my_log.submit_report(selected_mission, selected_report, user)) @@ -346,7 +355,7 @@ var/datum/report_field/people/manifest = selected_mission.flight_plan.manifest if(!manifest.get_value()) return 1 - manifest.send_email(user) + manifest.send_email(user, get_access(user)) return 1 #undef DECK_HOME diff --git a/code/modules/modular_computers/file_system/programs/generic/docks.dm b/code/modules/modular_computers/file_system/programs/generic/docks.dm index cc9c8078e65..b40254aecc7 100644 --- a/code/modules/modular_computers/file_system/programs/generic/docks.dm +++ b/code/modules/modular_computers/file_system/programs/generic/docks.dm @@ -1,7 +1,7 @@ /datum/computer_file/program/docking filename = "docking" filedesc = "Docking Control" - required_access = list(access_bridge) + read_access = list(access_bridge) nanomodule_path = /datum/nano_module/program/docking program_icon_state = "supply" program_key_state = "rd_key" diff --git a/code/modules/modular_computers/file_system/programs/generic/email_client.dm b/code/modules/modular_computers/file_system/programs/generic/email_client.dm index b377f86ac69..896d62c117b 100644 --- a/code/modules/modular_computers/file_system/programs/generic/email_client.dm +++ b/code/modules/modular_computers/file_system/programs/generic/email_client.dm @@ -7,60 +7,46 @@ program_menu_icon = "mail-closed" size = 7 available_on_network = 1 - var/stored_login = "" - var/stored_password = "" usage_flags = PROGRAM_ALL category = PROG_OFFICE nanomodule_path = /datum/nano_module/program/email_client -// Persistency. Unless you log out, or unless your password changes, this will pre-fill the login data when restarting the program -/datum/computer_file/program/email_client/on_shutdown() - if(NM) - var/datum/nano_module/program/email_client/NME = NM - if(NME.current_account) - stored_login = NME.stored_login - stored_password = NME.stored_password - NME.log_out() - else - stored_login = "" - stored_password = "" - . = ..() - /datum/computer_file/program/email_client/on_startup() . = ..() if(NM) var/datum/nano_module/program/email_client/NME = NM - NME.stored_login = stored_login - NME.stored_password = stored_password - NME.log_in() NME.error = "" - NME.check_for_new_messages(1) + NME.check_for_new_messages(TRUE, computer.get_account_nocheck()) /datum/computer_file/program/email_client/proc/new_mail_notify(notification_sound) computer.visible_notification(notification_sound) computer.audible_notification("sound/machines/ping.ogg") +/datum/computer_file/program/email_client/proc/mail_received(datum/computer_file/data/email_message/received) + if(NM) + var/datum/nano_module/program/email_client/NME = NM + NME.mail_received(received) + /datum/computer_file/program/email_client/process_tick() ..() var/datum/nano_module/program/email_client/NME = NM if(!istype(NME)) return NME.relayed_process(computer.get_network_status()) - - var/check_count = NME.check_for_new_messages() - if(check_count) - if(check_count == 2 && !NME.current_account.notification_mute) - new_mail_notify(NME.current_account.notification_sound) - ui_header = "ntnrc_new.gif" - else - ui_header = "ntnrc_idle.gif" + var/datum/computer_file/data/account/current_account = computer.get_account_nocheck() + if(current_account) + var/check_count = NME.check_for_new_messages(FALSE, current_account) + if(check_count) + if(check_count == 2 && !current_account.notification_mute) + new_mail_notify(current_account.notification_sound) + ui_header = "ntnrc_new.gif" + else + ui_header = "ntnrc_idle.gif" /datum/nano_module/program/email_client/ name = "Email Client" - var/stored_login = "" - var/stored_password = "" var/error = "" var/msg_title = "" @@ -78,7 +64,6 @@ var/download_progress = 0 var/download_speed = 0 - var/datum/computer_file/data/email_account/current_account = null var/datum/computer_file/data/email_message/current_message = null /datum/nano_module/program/email_client/proc/get_functional_drive() @@ -87,7 +72,7 @@ /datum/nano_module/program/email_client/proc/get_email_addresses() var/datum/computer_network/net = get_network() if(net) - return net.get_email_addresses() + return net.get_accounts() /datum/nano_module/program/email_client/proc/mail_received(var/datum/computer_file/data/email_message/received_message) var/mob/living/L = host.get_recursive_loc_of_type(/mob/living) @@ -102,64 +87,12 @@ msg += "*--*" to_chat(L, jointext(msg, null)) -/datum/nano_module/program/email_client/Destroy() - log_out() - . = ..() - -/datum/nano_module/program/email_client/proc/log_in() - var/list/id_login - var/atom/movable/A = nano_host() - var/obj/item/card/id/id = A.GetIdCard() - if(!id && ismob(A.loc)) - var/mob/M = A.loc - id = M.GetIdCard() - if(id) - id_login = id.associated_email_login.Copy() - - if(!get_network()) - error = "Network error" - return 0 - var/datum/computer_file/data/email_account/target - for(var/datum/computer_file/data/email_account/account in get_email_addresses()) - if(!account || !account.can_login) - continue - if(id_login && id_login["login"] == account.login) - target = account - break - if(stored_login && stored_login == account.login) - target = account - break - - if(!target) - error = "Invalid Login" - return 0 - - if(target.suspended) - error = "This account has been suspended. Please contact the system administrator for assistance." - return 0 - - var/use_pass - if(stored_password) - use_pass = stored_password - else if(id_login) - use_pass = id_login["password"] - - if(use_pass == target.password) - current_account = target - current_account.connected_clients |= src - return 1 - else - error = "Invalid Password" - return 0 - // Returns 0 if no new messages were received, 1 if there is an unread message but notification has already been sent. // and 2 if there is a new message that appeared in this tick (and therefore notification should be sent by the program). -/datum/nano_module/program/email_client/proc/check_for_new_messages(var/messages_read = FALSE) +/datum/nano_module/program/email_client/proc/check_for_new_messages(var/messages_read = FALSE, var/datum/computer_file/data/account/current_account) if(!current_account) - return 0 - if(!get_network()) - return 0 - var/list/allmails = current_account.all_emails() + return + var/list/allmails = current_account.all_incoming_emails() if(allmails.len > last_message_count) . = 2 @@ -168,34 +101,26 @@ else . = 0 + if(.) // See if we can actually find the passed account. We wait to do this til now because finding the account requires some hoop jumping that shouldn't occur every tick. + if(program.computer.get_account() != current_account) + return 0 + last_message_count = allmails.len if(messages_read) read_message_count = allmails.len - -/datum/nano_module/program/email_client/proc/log_out() - if(current_account) - current_account.connected_clients -= src - current_account = null - downloading = null - download_progress = 0 - last_message_count = 0 - read_message_count = 0 - /datum/nano_module/program/email_client/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = global.default_topic_state) var/list/data = host.initial_data() - - // Password has been changed by other client connected to this email account - if(current_account) - if(current_account.password != stored_password) - if(!log_in()) - log_out() - error = "Invalid Password" - // Banned. - else if(current_account.suspended) - log_out() - error = "This account has been suspended. Please contact the system administrator for assistance." - + var/datum/computer_network/network = get_network() + var/datum/computer_file/data/account/current_account = program.computer.get_account() + if(!network) + error = "No network found. Check network connection." + else + if(istype(current_account)) + if(current_account.suspended) + error = "This account has been suspended. Please contact the system administrator for assistance." + else + error = "No account logged in. Please login through your system to proceed." if(error) data["error"] = error else if(downloading) @@ -204,19 +129,21 @@ data["down_progress"] = download_progress data["down_size"] = downloading.size data["down_speed"] = download_speed - - else if(istype(current_account)) + else data["current_account"] = current_account.login data["notification_mute"] = current_account.notification_mute if(addressbook) var/list/all_accounts = list() - for(var/datum/computer_file/data/email_account/account in get_email_addresses()) + for(var/datum/computer_file/data/account/account in get_email_addresses()) if(!account.can_login) continue + var/datum/computer_file/report/crew_record/record = network.get_crew_record_by_name(account.fullname) + var/job = record ? record.get_job() : "Undefined" + var/domain = "@[network.network_id]" // TODO: Placeholder before cross-network email is a thing. all_accounts.Add(list(list( "name" = account.fullname, - "job" = account.assignment, - "login" = account.login + "job" = job, + "address" = account.login + domain ))) data["addressbook"] = 1 data["accounts"] = all_accounts @@ -267,9 +194,6 @@ ))) data["messages"] = all_messages data["messagecount"] = all_messages.len - else - data["stored_login"] = stored_login - data["stored_password"] = stars(stored_password, 0) ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -280,7 +204,7 @@ ui.set_initial_data(data) ui.open() -/datum/nano_module/program/email_client/proc/find_message_by_fuid(var/fuid) +/datum/nano_module/program/email_client/proc/find_message_by_fuid(var/fuid, var/datum/computer_file/data/account/current_account) if(!istype(current_account)) return @@ -325,18 +249,12 @@ if(..()) return 1 var/mob/living/user = usr + var/datum/computer_file/data/account/current_account = program.computer.get_account() if(href_list["open"]) ui_interact() - check_for_new_messages(1) // Any actual interaction (button pressing) is considered as acknowledging received message, for the purpose of notification icons. - if(href_list["login"]) - log_in() - return 1 - - if(href_list["logout"]) - log_out() - return 1 + check_for_new_messages(TRUE, current_account) // Any actual interaction (button pressing) is considered as acknowledging received message, for the purpose of notification icons. if(href_list["reset"]) error = "" @@ -389,22 +307,10 @@ addressbook = 0 return 1 - if(href_list["edit_login"]) - var/newlogin = sanitize(input(user,"Enter login", "Login", stored_login), 100) - if(newlogin) - stored_login = newlogin - return 1 - - if(href_list["edit_password"]) - var/newpass = sanitize(input(user,"Enter password", "Password"), 100) - if(newpass) - stored_password = newpass - return 1 - if(href_list["delete"]) if(!istype(current_account)) return 1 - var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["delete"]) + var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["delete"], current_account) if(!istype(M)) return 1 if(folder == "Deleted") @@ -427,10 +333,13 @@ if(!length(msg_title)) msg_title = "No subject" + var/datum/computer_network/network = get_network() var/datum/computer_file/data/email_message/message = new() + if(!network) + return TOPIC_REFRESH message.title = msg_title message.stored_data = msg_body - message.source = current_account.login + message.source = current_account.login + "@[network.network_id]" message.attachment = msg_attachment if(!current_account.send_mail(msg_recipient, message, get_network())) error = "Error sending email: this address doesn't exist." @@ -445,7 +354,7 @@ return 1 if(href_list["reply"]) - var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["reply"]) + var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["reply"], current_account) if(!istype(M)) return 1 error = null @@ -459,39 +368,11 @@ return 1 if(href_list["view"]) - var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["view"]) + var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["view"], current_account) if(istype(M)) current_message = M return 1 - if(href_list["changepassword"]) - var/oldpassword = sanitize(input(user,"Please enter your old password:", "Password Change"), 100) - if(!oldpassword) - return 1 - var/newpassword1 = sanitize(input(user,"Please enter your new password:", "Password Change"), 100) - if(!newpassword1) - return 1 - var/newpassword2 = sanitize(input(user,"Please re-enter your new password:", "Password Change"), 100) - if(!newpassword2) - return 1 - - if(!istype(current_account)) - error = "Please log in before proceeding." - return 1 - - if(current_account.password != oldpassword) - error = "Incorrect original password" - return 1 - - if(newpassword1 != newpassword2) - error = "The entered passwords do not match." - return 1 - - current_account.password = newpassword1 - stored_password = newpassword1 - error = "Your password has been successfully changed!" - return 1 - if(href_list["set_notification"]) var/new_notification = sanitize(input(user, "Enter your desired notification sound:", "Set Notification", current_account.notification_sound) as text|null) if(new_notification && current_account) @@ -514,7 +395,7 @@ if(!filename) return 1 - var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["save"]) + var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["save"], current_account) var/datum/computer_file/data/mail = istype(M) ? M.export() : null if(!istype(mail)) return 1 diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index b5777037f6b..f495ba88e99 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -43,6 +43,7 @@ return var/mob/user = usr + var/list/accesses = computer.get_access(user) if(href_list["PRG_change_filesource"]) . = TOPIC_HANDLED @@ -60,7 +61,7 @@ if(!network) return TOPIC_REFRESH // Helper for some user-friendliness. Try to select the first available mainframe. - var/list/file_servers = network.get_file_server_tags(MF_ROLE_FILESERVER, user) + var/list/file_servers = network.get_file_server_tags(MF_ROLE_FILESERVER, accesses) var/datum/file_storage/network/N = current_filesource if(!file_servers.len) N.server = null // Don't allow players to see files on mainframes they cannot access. @@ -73,7 +74,7 @@ var/datum/computer_network/network = computer.get_network() if(!network) return - var/list/file_servers = network.get_file_server_tags(MF_ROLE_FILESERVER, user) + var/list/file_servers = network.get_file_server_tags(MF_ROLE_FILESERVER, accesses) var/file_server = input(usr, "Choose a fileserver to view files on:", "Select File Server") as null|anything in file_servers if(file_server) var/datum/file_storage/network/N = file_sources[/datum/file_storage/network] @@ -87,8 +88,13 @@ if(href_list["PRG_openfile"]) . = TOPIC_HANDLED - open_file = href_list["PRG_openfile"] - + var/datum/computer_file/data/F = current_filesource.get_file(href_list["PRG_openfile"]) + if(F && (F.get_file_perms(accesses, user)) & OS_READ_ACCESS) + open_file = href_list["PRG_openfile"] + . = TOPIC_REFRESH + else + to_chat(user, SPAN_WARNING("You do not have permission to read this file.")) + . = TOPIC_HANDLED if(href_list["PRG_newtextfile"]) . = TOPIC_HANDLED var/newname = sanitize(input(usr, "Enter file name or leave blank to cancel:", "File rename")) @@ -136,7 +142,9 @@ if(F.read_only) error = "This file is read only. You cannot edit it." return - + if(!(F.get_file_perms(accesses, user) & OS_WRITE_ACCESS)) + error = "You do not have write access to this file." + return var/oldtext = html_decode(F.stored_data) oldtext = replacetext(oldtext, "\[br\]", "\n") @@ -145,7 +153,7 @@ return if(F) - current_filesource.save_file(F.filename, newtext) + current_filesource.save_file(F.filename, newtext, accesses, user) return TOPIC_REFRESH if(href_list["PRG_printfile"]) @@ -170,6 +178,15 @@ if(!F || !istype(F) || F.unsendable) error = "I/O ERROR: Unable to transfer file." return + var/copying = alert(usr, "Would you like to copy the file or transfer it? Transfering files requires write access.", "Copying file", "Copy", "Transfer") + if(copying == "Transfer") + if(!(F.get_file_perms(accesses, user) & OS_WRITE_ACCESS)) + error = "ACCESS ERROR: You do not have permission to transfer this file" + return + else + if(!(F.get_file_perms(accesses, user) & OS_READ_ACCESS)) + error = "ACCESS ERROR: You do not have permission to copy this file" + return var/list/choices = list() for(var/T in file_sources) var/datum/file_storage/FS = file_sources[T] @@ -183,7 +200,7 @@ if(nope) to_chat(user, SPAN_WARNING("Cannot transfer file to [dst] for following reason: [nope]")) return - current_transfer = new(current_filesource, dst, F, FALSE) + current_transfer = new(current_filesource, dst, F, copying == "Copy" ? TRUE : FALSE) ui_header = "downloader_running.gif" /datum/computer_file/program/filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = global.default_topic_state) diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm index 09468e8b7be..6b710693975 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm @@ -136,7 +136,7 @@ var/list/category_list[0] for(var/datum/computer_file/program/P in net.get_software_list(category)) // Only those programs our user can run will show in the list - if(!P.can_run(user) && P.requires_access_to_download) + if(!P.can_run(get_access(user), user, FALSE) && P.requires_access_to_download) continue if(!P.is_supported_by_hardware(program.computer.get_hardware_flag(), user, TRUE)) continue diff --git a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm index 995e5504edf..7c7556914d6 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm @@ -87,7 +87,7 @@ channel = null return 1 var/mob/living/user = usr - if(can_run(usr, 1, list(access_network))) + if(has_access(list(access_network), usr.GetAccess())) if(channel) var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No") if(response == "Yes") diff --git a/code/modules/modular_computers/file_system/programs/generic/records.dm b/code/modules/modular_computers/file_system/programs/generic/records.dm index c6c5e29e019..027c03131ef 100644 --- a/code/modules/modular_computers/file_system/programs/generic/records.dm +++ b/code/modules/modular_computers/file_system/programs/generic/records.dm @@ -24,10 +24,10 @@ send_rsc(user, active_record.photo_front, "front_[active_record.uid].png") send_rsc(user, active_record.photo_side, "side_[active_record.uid].png") data["pic_edit"] = check_access(user, access_bridge) || check_access(user, access_security) - data += active_record.generate_nano_data(user_access) + data += active_record.generate_nano_data(user_access, user) else var/list/all_records = list() - + var/list/searchable_names = list() data["show_milrank"] = (global.using_map.flags & MAP_HAS_BRANCH) for(var/datum/computer_file/report/crew_record/R in get_records()) all_records.Add(list(list( @@ -36,10 +36,9 @@ "milrank" = R.get_rank(), "id" = R.uid ))) + searchable_names |= R.searchable_fields data["all_records"] = all_records - data["creation"] = check_access(user, access_bridge) - data["dnasearch"] = check_access(user, access_medical) || check_access(user, access_forensics_lockers) - data["fingersearch"] = check_access(user, access_security) + data["searchable"] = searchable_names ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) if (!ui) @@ -50,7 +49,7 @@ /datum/nano_module/program/records/proc/get_record_access(var/mob/user) - var/list/user_access = using_access || user.GetAccess() + var/list/user_access = get_access(user) var/obj/PC = nano_host() var/datum/extension/interactive/os/os = get_extension(PC, /datum/extension/interactive/os) @@ -67,7 +66,7 @@ var/datum/report_field/F = R.field_from_ID(field_ID) if(!F) return - if(!F.verify_access_edit(get_record_access(user))) + if(!(F.get_perms(get_access(user),user) & OS_WRITE_ACCESS)) to_chat(user, "\The [nano_host()] flashes an \"Access Denied\" warning.") return F.ask_value(user) @@ -85,7 +84,10 @@ var/ID = text2num(href_list["set_active"]) for(var/datum/computer_file/report/crew_record/R in get_records()) if(R.uid == ID) - active_record = R + if(R.get_file_perms(get_access(usr), usr) & OS_READ_ACCESS) + active_record = R + else + to_chat(usr, SPAN_WARNING("Access Denied")) break return 1 if(href_list["new_record"]) @@ -93,12 +95,11 @@ if(!network) to_chat(usr, SPAN_WARNING("Network error.")) return - if(!check_access(usr, access_bridge)) - to_chat(usr, "Access Denied.") + if(!network.store_file(active_record, MF_ROLE_CREW_RECORDS)) + to_chat(usr, SPAN_WARNING("You may not have access to generate new crew records, or there may not be a crew record mainframe active on the network.")) return active_record = new/datum/computer_file/report/crew_record() global.all_crew_records.Add(active_record) - network.store_file(active_record, MF_ROLE_CREW_RECORDS) return 1 if(href_list["print_active"]) if(!active_record) @@ -115,11 +116,17 @@ if(!search) return for(var/datum/computer_file/report/crew_record/R in get_records()) + if(!(R.get_file_perms(get_access(usr), usr) & OS_READ_ACCESS)) + continue var/datum/report_field/field = R.field_from_name(field_name) + if(!field.searchable) + continue + if(!(field.get_perms(get_access(usr), usr) & OS_READ_ACCESS)) + continue if(findtext(lowertext(field.get_value()), lowertext(search))) active_record = R return 1 - message = "Unable to find record containing '[search]'" + message = "Unable to find record containing '[search]'. You may lack access to search for this." return 1 var/datum/computer_file/report/crew_record/R = active_record diff --git a/code/modules/modular_computers/file_system/programs/generic/reports.dm b/code/modules/modular_computers/file_system/programs/generic/reports.dm index a7f03136ec1..9d060a306fd 100644 --- a/code/modules/modular_computers/file_system/programs/generic/reports.dm +++ b/code/modules/modular_computers/file_system/programs/generic/reports.dm @@ -24,7 +24,7 @@ switch(prog_state) if(REPORTS_VIEW) if(selected_report) - data["report_data"] = selected_report.generate_nano_data(get_access(user)) + data["report_data"] = selected_report.generate_nano_data(get_access(user), user) data["view_only"] = can_view_only data["printer"] = program.computer.has_component(PART_PRINTER) if(REPORTS_DOWNLOAD) @@ -65,7 +65,8 @@ if(!program.computer || !program.computer.has_component(PART_HDD)) to_chat(user, "Unable to find hard drive.") return - selected_report.rename_file() + if(save_as) // We're copying, so don't overwrite the actual report. + selected_report.rename_file() if(program.computer.store_file(selected_report)) saved_report = selected_report selected_report = saved_report.clone() @@ -85,12 +86,12 @@ var/datum/computer_file/report/chosen_report = choices[choice] var/editing = alert(user, "Would you like to view or edit the report", "Loading Report", "View", "Edit") if(editing == "View") - if(!chosen_report.verify_access(get_access(user))) + if(!(chosen_report.get_file_perms(get_access(user), user) & OS_READ_ACCESS)) to_chat(user, "You lack access to view this report.") return can_view_only = 1 else - if(!chosen_report.verify_access_edit(get_access(user))) + if(!(chosen_report.get_file_perms(get_access(user), user) & OS_WRITE_ACCESS)) to_chat(user, "You lack access to edit this report.") return can_view_only = 0 @@ -115,16 +116,17 @@ if(href_list["save"]) if(!selected_report) return 1 - if(!selected_report.verify_access(get_access(user))) - return 1 var/save_as = text2num(href_list["save_as"]) + var/req_access = save_as ? OS_READ_ACCESS : OS_WRITE_ACCESS + if(!(selected_report.get_file_perms(get_access(user), user) & req_access)) + return 1 save_report(user, save_as) if(href_list["submit"]) if(!selected_report) return 1 - if(!selected_report.verify_access_edit(get_access(user))) + if(!(selected_report.get_file_perms(get_access(user), user) & OS_WRITE_ACCESS)) return 1 - if(selected_report.submit(user)) + if(selected_report.submit(user, get_access(user))) to_chat(user, "The [src] has been submitted.") if(alert(user, "Would you like to save a copy?","Save Report", "Yes.", "No.") == "Yes.") save_report(user) @@ -139,24 +141,24 @@ return 1 var/field_ID = text2num(href_list["ID"]) var/datum/report_field/field = selected_report.field_from_ID(field_ID) - if(!field || !field.verify_access_edit(get_access(user))) + if(!field || !(field.get_perms(get_access(usr), usr) & OS_WRITE_ACCESS)) return 1 field.ask_value(user) //Handles the remaining IO. return 1 if(href_list["print"]) - if(!selected_report || !selected_report.verify_access(get_access(user))) + if(!selected_report || !(selected_report.get_file_perms(get_access(user), user) & OS_READ_ACCESS)) return 1 var/with_fields = text2num(href_list["print_mode"]) - var/text = selected_report.generate_pencode(get_access(user), with_fields) + var/text = selected_report.generate_pencode(get_access(user), user, with_fields) if(!program.computer.print_paper(text, selected_report.display_name())) to_chat(user, "Hardware error: Printer was unable to print the file. It may be out of paper.") return 1 if(href_list["export"]) - if(!selected_report || !selected_report.verify_access(get_access(user))) + if(!selected_report || !(selected_report.get_file_perms(get_access(user), user) & OS_READ_ACCESS)) return 1 var/datum/computer_file/data/text/file = new selected_report.rename_file() - file.stored_data = selected_report.generate_pencode(get_access(user), no_html = 1) //TXT files can't have html; they use pencode only. + file.stored_data = selected_report.generate_pencode(get_access(user), user, no_html = 1) //TXT files can't have html; they use pencode only. file.filename = selected_report.filename if(program.computer.store_file(file)) to_chat(user, "The report has been exported as '[file.filename].[file.filetype]'.") @@ -170,7 +172,7 @@ if(href_list["get_report"]) var/uid = text2num(href_list["report"]) var/datum/computer_network/net = program.computer.get_network() - for(var/datum/computer_file/report/report in net.fetch_reports(get_access(user))) + for(var/datum/computer_file/report/report in net.fetch_reports(get_access(user), user)) if(report.uid == uid) selected_report = report.clone() can_view_only = 0 diff --git a/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm b/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm index 763bc28a420..5a510ed4f22 100644 --- a/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm +++ b/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm @@ -17,15 +17,27 @@ var/error var/is_edited -/datum/computer_file/program/wordprocessor/proc/open_file(var/filename) - var/datum/computer_file/data/F = get_file(filename) +/datum/computer_file/program/wordprocessor/on_shutdown(forced) + . = ..() + browsing = null + open_file = null + loaded_data = null + error = null + is_edited = null + +/datum/computer_file/program/wordprocessor/proc/open_file(var/openingfile, var/list/accesses, var/mob/user) + var/datum/computer_file/data/F = get_file(openingfile) if(F) + if(!(F.get_file_perms(accesses, user) & OS_READ_ACCESS)) + error = "I/O error: You do not have permission to read file '[openingfile]'." + return FALSE open_file = F.filename loaded_data = F.stored_data - return 1 + return TRUE + error = "I/O error: Unable to open file '[openingfile]'." -/datum/computer_file/program/wordprocessor/proc/save_file(var/filename) - . = computer.save_file(filename, loaded_data, /datum/computer_file/data/text) +/datum/computer_file/program/wordprocessor/proc/save_file(var/savingfile) + . = computer.save_file(savingfile, loaded_data, /datum/computer_file/data/text) if(.) is_edited = 0 @@ -63,8 +75,7 @@ if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes") save_file(open_file) browsing = 0 - if(!open_file(href_list["PRG_openfile"])) - error = "I/O error: Unable to open file '[href_list["PRG_openfile"]]'." + open_file(href_list["PRG_openfile"], NM.get_access(usr), usr) if(href_list["PRG_newfile"]) . = 1 @@ -79,6 +90,13 @@ if(F) open_file = F.filename loaded_data = "" + + // Set the write/mod access to the current account if it exists. + var/datum/computer_file/data/account/A = computer.get_account() + if(A) + var/datum/computer_network/network = computer.get_network() + LAZYADD(F.write_access, list(list("[A.login]@[network.network_id]"))) + LAZYADD(F.mod_access, list(list("[A.login]@[network.network_id]"))) return 1 else error = "I/O error: Unable to create file '[href_list["PRG_saveasfile"]]'." @@ -90,6 +108,12 @@ return 1 var/datum/computer_file/data/F = create_file(newname, loaded_data, /datum/computer_file/data/text) if(F) + var/datum/computer_file/data/account/A = computer.get_account() + if(A) + var/datum/computer_network/network = computer.get_network() + LAZYADD(F.write_access, list(list("[A.login]@[network.network_id]"))) + LAZYADD(F.mod_access, list(list("[A.login]@[network.network_id]"))) + open_file = F.filename else error = "I/O error: Unable to create file '[href_list["PRG_saveasfile"]]'." @@ -102,13 +126,16 @@ if(!open_file) return 0 if(!save_file(open_file)) - error = "I/O error: Unable to save file '[open_file]'." + error = "I/O error: Unable to save file '[open_file]'. Access may be denied." return 1 if(href_list["PRG_editfile"]) var/oldtext = html_decode(loaded_data) oldtext = replacetext(oldtext, "\[br\]", "\n") - + var/datum/computer_file/data/F = get_file(open_file) + if(!(F.get_file_perms(NM.get_access(usr), usr) & OS_WRITE_ACCESS)) + error = "I/O error: You do not have permission to edit this file." + return 1 var/newtext = sanitize(replacetext(input(usr, "Editing file '[open_file]'. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH) if(!newtext) return diff --git a/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm index e336d276e09..3caeafe0740 100644 --- a/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm +++ b/code/modules/modular_computers/file_system/programs/medical/suit_sensors.dm @@ -7,7 +7,7 @@ program_key_state = "med_key" program_menu_icon = "heart" extended_desc = "This program connects to life signs monitoring system to provide basic information on crew health." - required_access = list(access_medical) + read_access = list(access_medical) network_destination = "crew lifesigns monitoring system" size = 11 category = PROG_MONITOR diff --git a/code/modules/modular_computers/file_system/programs/research/ai_restorer.dm b/code/modules/modular_computers/file_system/programs/research/ai_restorer.dm index 720b8b555c7..37aa8a4fcd6 100644 --- a/code/modules/modular_computers/file_system/programs/research/ai_restorer.dm +++ b/code/modules/modular_computers/file_system/programs/research/ai_restorer.dm @@ -6,7 +6,7 @@ program_menu_icon = "person" extended_desc = "This program is capable of reconstructing damaged AI systems. It can also be used to upload basic laws to the AI. Requires direct AI connection via inteliCard slot." size = 12 - required_access = list(access_bridge) + read_access = list(access_bridge) requires_access_to_run = 0 available_on_network = 1 nanomodule_path = /datum/nano_module/program/computer_aidiag/ diff --git a/code/modules/modular_computers/file_system/programs/research/email_administration.dm b/code/modules/modular_computers/file_system/programs/research/email_administration.dm index cdf5fea06b1..81480ffef96 100644 --- a/code/modules/modular_computers/file_system/programs/research/email_administration.dm +++ b/code/modules/modular_computers/file_system/programs/research/email_administration.dm @@ -9,13 +9,13 @@ requires_network = 1 available_on_network = 1 nanomodule_path = /datum/nano_module/program/email_administration - required_access = list(access_network) + read_access = list(access_network) category = PROG_ADMIN /datum/nano_module/program/email_administration name = "Email Administration" available_to_ai = TRUE - var/datum/computer_file/data/email_account/current_account = null + var/datum/computer_file/data/account/current_account = null var/datum/computer_file/data/email_message/current_message = null var/error = "" @@ -26,13 +26,13 @@ if(!user.skill_check(SKILL_COMPUTER, SKILL_BASIC)) var/datum/extension/fake_data/fake_data = get_or_create_extension(src, /datum/extension/fake_data, 15) data["skill_fail"] = fake_data.update_and_return_data() - data["terminal"] = !!program var/datum/computer_network/network = program?.computer?.get_network() if(!network) error = "NETWORK FAILURE: Check connection to the network." - else if(!length(network.get_mainframes_by_role(MF_ROLE_EMAIL_SERVER, user))) - error = "NETWORK FAILURE: No email servers detected." + + else if(!length(network.get_mainframes_by_role(MF_ROLE_ACCOUNT_SERVER, user))) + error = "NETWORK FAILURE: No account servers detected." if(error) data["error"] = error @@ -56,12 +56,11 @@ data["messagecount"] = all_messages.len else var/list/all_accounts = list() - for(var/datum/computer_file/data/email_account/account in network.get_email_addresses()) + for(var/datum/computer_file/data/account/account in network.get_accounts(get_access(user))) if(!account.can_login) continue all_accounts.Add(list(list( - "login" = account.login, - "uid" = account.uid + "login" = account.login ))) data["accounts"] = all_accounts data["accountcount"] = all_accounts.len @@ -91,14 +90,9 @@ var/datum/computer_network/network = program?.computer?.get_network() if(!network) return TOPIC_HANDLED - if(!length(network.mainframes[MF_ROLE_EMAIL_SERVER])) - error = "NETWORK FAILURE: No email servers detected." - return TOPIC_HANDLED - // High security - can only be operated when the user has an ID with access on them. - var/obj/item/card/id/I = user.GetIdCard() - if(!istype(I) || !(access_network in I.access)) - return TOPIC_HANDLED + // This is just for logging, not access checking so don't bother actually checking if the account has changed. + var/datum/computer_file/data/account/user_account = program.computer.get_account_nocheck() if(href_list["back"]) if(error) @@ -115,22 +109,10 @@ current_account.suspended = !current_account.suspended if(network.intrusion_detection_enabled) - program.computer.add_log("EMAIL LOG: SA-EDIT Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended by SA [I.registered_name] ([I.assignment]).") + program.computer.add_log("EMAIL LOG: SA-EDIT Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended by SA [user_account.login].") error = "Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended." return TOPIC_REFRESH - if(href_list["changepass"]) - if(!current_account) - return TOPIC_HANDLED - - var/newpass = sanitize(input(user,"Enter new password for account [current_account.login]", "Password"), 100) - if(!newpass || !CanUseTopic(user, state)) - return TOPIC_HANDLED - current_account.password = newpass - if(network.intrusion_detection_enabled) - program.computer.add_log("EMAIL LOG: SA-EDIT Password for account [current_account.login] has been changed by SA [I.registered_name] ([I.assignment]).") - return TOPIC_REFRESH - if(href_list["viewmail"]) if(!current_account) return TOPIC_HANDLED @@ -142,28 +124,5 @@ return TOPIC_REFRESH if(href_list["viewaccount"]) - for(var/datum/computer_file/data/email_account/email_account in network.get_email_addresses()) - if(email_account.uid == text2num(href_list["viewaccount"])) - current_account = email_account - break - return TOPIC_REFRESH - - if(href_list["newaccount"]) - var/newdomain = sanitize(input(user,"Pick domain:", "Domain name") as null|anything in global.using_map.usable_email_tlds) - if(!newdomain) - return TOPIC_HANDLED - var/newlogin = sanitize(input(user,"Pick account name (@[newdomain]):", "Account name"), 100) - if(!newlogin || !CanUseTopic(user, state)) - return TOPIC_HANDLED - - var/complete_login = "[newlogin]@[newdomain]" - if(network.find_email_by_name(complete_login)) - error = "Error creating account: An account with same address already exists." - return TOPIC_REFRESH - - var/datum/computer_file/data/email_account/new_account = new/datum/computer_file/data/email_account() - new_account.login = complete_login - new_account.password = GenerateKey() - network.add_email_account(new_account) - error = "Email [new_account.login] has been created, with generated password [new_account.password]" - return TOPIC_REFRESH + current_account = network.find_account_by_login(href_list["viewaccount"], get_access(user)) + return TOPIC_REFRESH \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm b/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm index 7e59f25805e..36b7fb37146 100644 --- a/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm +++ b/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm @@ -10,7 +10,7 @@ var/global/list/all_warrants program_menu_icon = "star" requires_network = 1 available_on_network = 1 - required_access = list(access_security) + read_access = list(access_security) nanomodule_path = /datum/nano_module/program/digitalwarrant/ category = PROG_SEC @@ -37,7 +37,7 @@ var/global/list/all_warrants var/list/data = host.initial_data() if(active) - data["details"] = active.generate_nano_data(using_access) + data["details"] = active.generate_nano_data(get_access(user), user) else for(var/datum/computer_file/report/warrant/W in global.all_warrants) LAZYADD(data[W.get_category()], W.get_nano_summary()) @@ -111,7 +111,7 @@ var/global/list/all_warrants var/datum/report_field/F = active.field_from_ID(text2num(href_list["edit_field"])) if(!F) return - if(!F.verify_access_edit(using_access)) + if(!(F.get_perms(get_access(usr), usr) & OS_WRITE_ACCESS)) to_chat(usr, SPAN_WARNING("\The [nano_host()] flashes an \"Access Denied\" warning.")) return F.ask_value(usr) diff --git a/code/modules/modular_computers/file_system/programs/security/forceauthorization.dm b/code/modules/modular_computers/file_system/programs/security/forceauthorization.dm index f5b0b58c9e0..0452024f6f5 100644 --- a/code/modules/modular_computers/file_system/programs/security/forceauthorization.dm +++ b/code/modules/modular_computers/file_system/programs/security/forceauthorization.dm @@ -8,7 +8,7 @@ program_menu_icon = "locked" requires_network = 1 available_on_network = 1 - required_access = list(access_armory) + read_access = list(access_armory) nanomodule_path = /datum/nano_module/program/forceauthorization/ category = PROG_SEC diff --git a/code/modules/modular_computers/file_system/reports/crew_record.dm b/code/modules/modular_computers/file_system/reports/crew_record.dm index 33e4d9be068..d12422b7461 100644 --- a/code/modules/modular_computers/file_system/reports/crew_record.dm +++ b/code/modules/modular_computers/file_system/reports/crew_record.dm @@ -11,52 +11,16 @@ var/global/arrest_security_status = "Arrest" size = 2 var/icon/photo_front = null var/icon/photo_side = null - //More variables below. - var/list/grants = list() // List of weakrefs to grant files. - var/user_id // A unique identifier linking a mob/player/user to this access record and their grants. - + /datum/computer_file/report/crew_record/New() ..() filename = "record[random_id(type, 100,999)]" - user_id = "[sequential_id("datum/computer_file/report/crew_record")]" load_from_mob(null) /datum/computer_file/report/crew_record/Destroy() . = ..() global.all_crew_records.Remove(src) -/datum/computer_file/report/crew_record/proc/add_grant(var/datum/computer_file/data/grant_record/new_grant) - grants |= weakref(new_grant) - -/datum/computer_file/report/crew_record/proc/remove_grant(var/grant_name) - for(var/weakref/grant in grants) - var/datum/computer_file/data/grant_record/GR = grant.resolve() - if(!GR) - grants -= grant - continue - if(GR.stored_data == grant_name) - grants -= grant - return - -/datum/computer_file/report/crew_record/proc/calculate_size() - size = max(1, round(length(user_id) + length(grants) / 20)) - -/datum/computer_file/report/crew_record/proc/get_access(var/network_id) - var/list/access_grants = list() - for(var/datum/computer_file/data/grant_record/grant in get_valid_grants()) - LAZYDISTINCTADD(access_grants, uppertext("[network_id].[grant.stored_data]")) - return access_grants - -/datum/computer_file/report/crew_record/proc/get_valid_grants() - var/list/valid_grants = list() - for(var/weakref/grant in grants) - var/datum/computer_file/data/grant_record/GR = grant.resolve() - if(!istype(GR) || GR.holder != holder) - grants.Remove(grant) - continue // This is a bad grant. File is gone or moved. - LAZYDISTINCTADD(valid_grants, GR) - return valid_grants - /datum/computer_file/report/crew_record/proc/load_from_mob(var/mob/living/carbon/human/H) if(istype(H)) @@ -225,7 +189,7 @@ var/global/arrest_security_status = "Arrest" var/dat = "

    RECORD DATABASE DATA DUMP

    Generated on: [stationdate2text()] [stationtime2text()]
    ******************************
    " dat += "" for(var/datum/report_field/F in CR.fields) - if(F.verify_access(access)) + if(F.get_perms(access) & OS_READ_ACCESS) dat += "" @@ -251,54 +215,54 @@ var/global/arrest_security_status = "Arrest" #define GETTER_SETTER(PATH, KEY) /datum/computer_file/report/crew_record/proc/get_##KEY(){var/datum/report_field/F = locate(/datum/report_field/##PATH/##KEY) in fields; if(F) return F.get_value()} \ /datum/computer_file/report/crew_record/proc/set_##KEY(given_value){var/datum/report_field/F = locate(/datum/report_field/##PATH/##KEY) in fields; if(F) F.set_value(given_value)} -#define SETUP_FIELD(NAME, KEY, PATH, ACCESS, ACCESS_EDIT) GETTER_SETTER(PATH, KEY); /datum/report_field/##PATH/##KEY;\ -/datum/computer_file/report/crew_record/generate_fields(){..(); var/datum/report_field/##KEY = add_field(/datum/report_field/##PATH/##KEY, ##NAME);\ +#define SETUP_FIELD(NAME, KEY, PATH, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS, SEARCHABLE) GETTER_SETTER(PATH, KEY); /datum/report_field/##PATH/##KEY;\ +/datum/computer_file/report/crew_record/generate_fields(){..(); var/datum/report_field/##KEY = add_field(/datum/report_field/##PATH/##KEY, ##NAME, searchable = SEARCHABLE, can_mod_access = CAN_MOD_ACCESS);\ KEY.set_access(ACCESS, ACCESS_EDIT || ACCESS || access_bridge)} // Fear not the preprocessor, for it is a friend. To add a field, use one of these, depending on value type and if you need special access to see it. // It will also create getter/setter procs for record datum, named like /get_[key here]() /set_[key_here](value) e.g. get_name() set_name(value) // Use getter setters to avoid errors caused by typoing the string key. -#define FIELD_SHORT(NAME, KEY, ACCESS, ACCESS_EDIT) SETUP_FIELD(NAME, KEY, simple_text/crew_record, ACCESS, ACCESS_EDIT) -#define FIELD_LONG(NAME, KEY, ACCESS, ACCESS_EDIT) SETUP_FIELD(NAME, KEY, pencode_text/crew_record, ACCESS, ACCESS_EDIT) -#define FIELD_NUM(NAME, KEY, ACCESS, ACCESS_EDIT) SETUP_FIELD(NAME, KEY, number/crew_record, ACCESS, ACCESS_EDIT) -#define FIELD_LIST(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT) FIELD_LIST_EDIT(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT) -#define FIELD_LIST_EDIT(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT) SETUP_FIELD(NAME, KEY, options/crew_record, ACCESS, ACCESS_EDIT);\ +#define FIELD_SHORT(NAME, KEY, ACCESS, ACCESS_EDIT, SEARCHABLE, CAN_MOD_ACCESS) SETUP_FIELD(NAME, KEY, simple_text/crew_record, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS, SEARCHABLE) +#define FIELD_LONG(NAME, KEY, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS) SETUP_FIELD(NAME, KEY, pencode_text/crew_record, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS, FALSE) +#define FIELD_NUM(NAME, KEY, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS) SETUP_FIELD(NAME, KEY, number/crew_record, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS, FALSE) +#define FIELD_LIST(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS) FIELD_LIST_EDIT(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS) +#define FIELD_LIST_EDIT(NAME, KEY, OPTIONS, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS) SETUP_FIELD(NAME, KEY, options/crew_record, ACCESS, ACCESS_EDIT, CAN_MOD_ACCESS, FALSE);\ /datum/report_field/options/crew_record/##KEY/get_options(){return OPTIONS} // GENERIC RECORDS -FIELD_SHORT("Name", name, null, access_change_ids) -FIELD_SHORT("Formal Name", formal_name, null, access_change_ids) -FIELD_SHORT("Job", job, null, access_change_ids) -FIELD_LIST("Sex", sex, record_genders(), null, access_change_ids) -FIELD_NUM("Age", age, null, access_change_ids) -FIELD_LIST_EDIT("Status", status, global.physical_statuses, null, access_medical) +FIELD_SHORT("Name", name, null, access_change_ids, TRUE, TRUE) +FIELD_SHORT("Formal Name", formal_name, null, access_change_ids, FALSE, TRUE) +FIELD_SHORT("Job", job, null, access_change_ids, FALSE, TRUE) +FIELD_LIST("Sex", sex, record_genders(), null, access_change_ids, TRUE) +FIELD_NUM("Age", age, null, access_change_ids, TRUE) +FIELD_LIST_EDIT("Status", status, global.physical_statuses, null, access_medical, TRUE) -FIELD_SHORT("Species",species_name, null, access_change_ids) -FIELD_LIST("Branch", branch, record_branches(), null, access_change_ids) -FIELD_LIST("Rank", rank, record_ranks(), null, access_change_ids) -FIELD_SHORT("Religion", religion, access_chapel_office, access_change_ids) +FIELD_SHORT("Species",species_name, null, access_change_ids, FALSE, TRUE) +FIELD_LIST("Branch", branch, record_branches(), null, access_change_ids, TRUE) +FIELD_LIST("Rank", rank, record_ranks(), null, access_change_ids, TRUE) +FIELD_SHORT("Religion", religion, access_chapel_office, access_change_ids, FALSE, TRUE) -FIELD_LONG("General Notes (Public)", public_record, null, access_bridge) +FIELD_LONG("General Notes (Public)", public_record, null, access_bridge, TRUE) // MEDICAL RECORDS -FIELD_LIST("Blood Type", bloodtype, get_all_blood_types(), access_medical, access_medical) -FIELD_LONG("Medical Record", medical_record, access_medical, access_medical) -FIELD_LONG("Known Implants", implants, access_medical, access_medical) +FIELD_LIST("Blood Type", bloodtype, get_all_blood_types(), access_medical, access_medical, TRUE) +FIELD_LONG("Medical Record", medical_record, access_medical, access_medical, TRUE) +FIELD_LONG("Known Implants", implants, access_medical, access_medical, TRUE) // SECURITY RECORDS -FIELD_LIST("Criminal Status", criminalStatus, global.security_statuses, access_security, access_security) -FIELD_LONG("Security Record", security_record, access_security, access_security) -FIELD_SHORT("DNA", dna, access_security, access_security) -FIELD_SHORT("Fingerprint", fingerprint, access_security, access_security) +FIELD_LIST("Criminal Status", criminalStatus, global.security_statuses, access_security, access_security, TRUE) +FIELD_LONG("Security Record", security_record, access_security, access_security, TRUE) +FIELD_SHORT("DNA", dna, access_security, access_security, TRUE, TRUE) +FIELD_SHORT("Fingerprint", fingerprint, access_security, access_security, TRUE, TRUE) // EMPLOYMENT RECORDS -FIELD_LONG("Employment Record", employment_record, access_bridge, access_bridge) -FIELD_SHORT("Home System", homeSystem, access_bridge, access_change_ids) -FIELD_SHORT("Faction", faction, access_bridge, access_bridge) -FIELD_LONG("Qualifications", skillset, access_bridge, access_bridge) +FIELD_LONG("Employment Record", employment_record, access_bridge, access_bridge, TRUE) +FIELD_SHORT("Home System", homeSystem, access_bridge, access_change_ids, FALSE, TRUE) +FIELD_SHORT("Faction", faction, access_bridge, access_bridge, FALSE, TRUE) +FIELD_LONG("Qualifications", skillset, access_bridge, access_bridge, TRUE) // ANTAG RECORDS -FIELD_LONG("Exploitable Information", antag_record, access_syndicate, access_syndicate) +FIELD_LONG("Exploitable Information", antag_record, access_syndicate, access_syndicate, FALSE) //Options builderes /datum/report_field/options/crew_record/rank/proc/record_ranks() diff --git a/code/modules/modular_computers/file_system/reports/deck_reports.dm b/code/modules/modular_computers/file_system/reports/deck_reports.dm index 84e48d9a46e..08f75763ad3 100644 --- a/code/modules/modular_computers/file_system/reports/deck_reports.dm +++ b/code/modules/modular_computers/file_system/reports/deck_reports.dm @@ -6,10 +6,7 @@ var/datum/report_field/people/leader //Give these a special name for easier access. var/datum/report_field/people/manifest var/datum/report_field/planned_depart - -/datum/computer_file/report/flight_plan/New() - ..() - set_access(null, access_bridge) + write_access = list(access_bridge) /datum/computer_file/report/flight_plan/Destroy() leader = null @@ -31,11 +28,8 @@ /datum/computer_file/report/recipient/shuttle var/datum/report_field/shuttle var/datum/report_field/mission - var/access_shuttle = 0 //Set to 1 to give the shuttle's logging access as an access_edit pattern. - -/datum/computer_file/report/recipient/shuttle/New() - ..() - set_access(null, access_bridge) + var/access_shuttle = 0 //Set to 1 to give the shuttle's logging access write permissions when created + write_access = list(access_bridge) /datum/computer_file/report/recipient/shuttle/Destroy() shuttle = null @@ -52,10 +46,7 @@ /datum/computer_file/report/recipient/shuttle/damage form_name = "DC243" title = "Post-flight Damage Assessment" - -/datum/computer_file/report/recipient/shuttle/damage/New() - ..() - set_access(null, access_cargo, override = 0) + write_access = list(list(access_bridge, access_cargo)) /datum/computer_file/report/recipient/shuttle/damage/generate_fields() ..() @@ -68,10 +59,7 @@ /datum/computer_file/report/recipient/shuttle/fuel form_name = "DC12" title = "Post-flight Refueling Report" - -/datum/computer_file/report/recipient/shuttle/fuel/New() - ..() - set_access(null, access_cargo, override = 0) + write_access = list(list(access_bridge, access_cargo)) /datum/computer_file/report/recipient/shuttle/fuel/generate_fields() ..() @@ -83,10 +71,7 @@ /datum/computer_file/report/recipient/shuttle/atmos form_name = "DC245" title = "Post-flight Atmospherics Assessment" - -/datum/computer_file/report/recipient/shuttle/atmos/New() - ..() - set_access(null, access_cargo, override = 0) + write_access = list(list(access_bridge, access_cargo)) /datum/computer_file/report/recipient/shuttle/atmos/generate_fields() ..() @@ -98,10 +83,7 @@ /datum/computer_file/report/recipient/shuttle/gear form_name = "DC248b" title = "Post-flight Emergency Supply Inventory; Summary Version" - -/datum/computer_file/report/recipient/shuttle/gear/New() - ..() - set_access(null, access_cargo, override = 0) + write_access = list(list(access_bridge, access_cargo)) /datum/computer_file/report/recipient/shuttle/gear/generate_fields() ..() diff --git a/code/modules/modular_computers/file_system/reports/people.dm b/code/modules/modular_computers/file_system/reports/people.dm index 8bc2113bf8b..928dfa431f3 100644 --- a/code/modules/modular_computers/file_system/reports/people.dm +++ b/code/modules/modular_computers/file_system/reports/people.dm @@ -1,5 +1,5 @@ //Field with people in it; has some communications procs available to it. -/datum/report_field/people/proc/send_email(mob/user) +/datum/report_field/people/proc/send_email(mob/user, list/access) if(!get_value()) return //No one to send to anyway. var/subject = sanitize(input(user, "Email Subject:", "Document Email", "Report Submission: [owner.display_name()]") as null|text) @@ -9,12 +9,15 @@ return var/datum/computer_file/data/text/report_file if(attach_report) - var/list/user_access = list() - var/obj/item/card/id/I = user.GetIdCard() - if(I) - user_access |= I.access + var/list/user_access + if(access) + user_access = access.Copy() + else + var/obj/item/card/id/I = user.GetIdCard() + if(I) + user_access += I.access report_file = new - report_file.stored_data = owner.generate_pencode(user_access, no_html = 1) //TXT files can't have html; they use pencode only. + report_file.stored_data = owner.generate_pencode(user_access, user, no_html = 1) //TXT files can't have html; they use pencode only. report_file.filename = owner.filename if(perform_send(subject, body, report_file)) to_chat(user, "The email has been sent.") @@ -33,7 +36,7 @@ var/datum/computer_network/net = get_used_network() if(!net) return - var/datum/computer_file/data/email_account/server = net.find_email_by_name(EMAIL_DOCUMENTS) + var/datum/computer_file/data/account/server = net.find_account_by_login(EMAIL_DOCUMENTS) var/datum/computer_file/data/email_message/message = new() message.title = subject message.stored_data = body diff --git a/code/modules/modular_computers/file_system/reports/report.dm b/code/modules/modular_computers/file_system/reports/report.dm index d61fb527a7b..14e4a2277df 100644 --- a/code/modules/modular_computers/file_system/reports/report.dm +++ b/code/modules/modular_computers/file_system/reports/report.dm @@ -5,55 +5,45 @@ var/form_name = "AB1" //Form code, for maximum bureaucracy. var/creator //The name of the mob that made the report. var/file_time //Time submitted. - var/list/access_edit = list(list()) //The access required to submit the report. See documentation below. - var/list/access = list(list()) //The access required to view the report. + write_access = list() //The access required to submit the report. + read_access = list() //The access required to view the report. + mod_access = list(list(access_bridge)) //Changing the read/write access of the file should generally require higher access than the write access itself. var/list/datum/report_field/fields = list() //A list of fields the report comes with, in order that they should be displayed. var/available_on_network = 0 //Whether this report type should show up for download. var/logo //Can be set to a pencode logo for use with some display methods. + var/list/searchable_fields = list() //The names of fields in the report which are searchable. /datum/computer_file/report/New() ..() generate_fields() + initialize_access() /datum/computer_file/report/Destroy() QDEL_NULL_LIST(fields) . = ..() /* -Access stuff. The report's access/access_edit should control whether it can be opened/submitted. -For field editing or viewing, use the field's access/access_edit permission instead. -The access system is based on "access patterns", lists of access values. -A user needs all access values in a pattern to be granted access. -A user needs to only match one of the potentially several stored access patterns to be granted access. -You must have access to have edit access. - -This proc resets the access to the report, resulting in just one access pattern for access/edit. -Arguments can be access values (numbers) or lists of access values. +This proc resets the access to the report, resulting in just one access requirement for read/write. If null is passed to one of the arguments, that access type is left alone. Pass list() to reset to no access needed instead. The recursive option resets access to all fields in the report as well. -If the override option is set to 0, the access supplied will instead be added as another access pattern, rather than resetting the access. */ -/datum/computer_file/report/proc/set_access(access, access_edit, recursive = 1, override = 1) - if(access) - if(!islist(access)) - access = list(access) - override ? (src.access = list(access)) : (src.access += list(access)) //Note that this is a list of lists. - if(access_edit) - if(!islist(access_edit)) - access_edit = list(access_edit) - override ? (src.access_edit = list(access_edit)) : (src.access_edit += list(access_edit)) +/datum/computer_file/report/proc/set_access(read_access, write_access, recursive = TRUE) + if(read_access) + if(!islist(read_access)) + read_access = list(read_access) + src.read_access = read_access + if(write_access) + if(!islist(write_access)) + write_access = list(write_access) + src.write_access = write_access if(recursive) for(var/datum/report_field/field in fields) - field.set_access(access, access_edit, override) + field.set_access(read_access, write_access, TRUE) -//Strongly recommended to use these procs to check for access. They can take access values (numbers) or lists of values. -/datum/computer_file/report/proc/verify_access(given_access) - return has_access_pattern(access, given_access) - -/datum/computer_file/report/proc/verify_access_edit(given_access) - if(!verify_access(given_access)) - return //Need access for access_edit - return has_access_pattern(access_edit, given_access) +// The default behavior propagates (non-empty) preset access to the fields which allow such propogation. +// You can override or modify this behavior on subtypes. +/datum/computer_file/report/proc/initialize_access() + set_access(length(read_access) ? read_access : null, length(write_access) ? write_access : null, TRUE) //Looking up fields. Names might not be unique unless you ensure otherwise. /datum/computer_file/report/proc/field_from_ID(ID) @@ -93,13 +83,17 @@ If the override option is set to 0, the access supplied will instead be added as filename = "[form_name]_[append]" //Don't add fields except through this proc. -/datum/computer_file/report/proc/add_field(field_type, name, value = null, required = 0) +/datum/computer_file/report/proc/add_field(field_type, name, value = null, required = 0, searchable = 0, can_mod_access = 1) var/datum/report_field/field = new field_type(src) field.name = name if(value) field.value = value if(required) field.required = 1 + if(searchable) + field.searchable = 1 + searchable_fields += field.name + field.can_mod_access = can_mod_access field.ID = sequential_id(type) fields += field return field @@ -110,8 +104,6 @@ If the override option is set to 0, the access supplied will instead be added as temp.form_name = form_name temp.creator = creator temp.file_time = file_time - temp.access_edit = access_edit - temp.access = access for(var/i = 1, i <= length(fields), i++) var/datum/report_field/new_field = temp.fields[i] new_field.copy_value(fields[i]) @@ -121,7 +113,7 @@ If the override option is set to 0, the access supplied will instead be added as return "Form [form_name]: [title]" //if access is given, will include access information by performing checks against it. -/datum/computer_file/report/proc/generate_nano_data(list/given_access) +/datum/computer_file/report/proc/generate_nano_data(list/given_access, mob/user) . = list() .["name"] = display_name() .["uid"] = uid @@ -129,22 +121,23 @@ If the override option is set to 0, the access supplied will instead be added as .["file_time"] = file_time .["fields"] = list() if(given_access) - .["access"] = verify_access(given_access) - .["access_edit"] = verify_access_edit(given_access) + var/access_flags = get_file_perms(given_access, user) + .["access"] = access_flags & OS_READ_ACCESS + .["access_edit"] = access_flags & OS_WRITE_ACCESS for(var/datum/report_field/field in fields) - .["fields"] += list(field.generate_nano_data(given_access)) + .["fields"] += list(field.generate_nano_data(given_access, user)) /* This formats the report into pencode for use with paper and printing. Setting access to null will bypass access checks. with_fields will include a field link after the field value (useful to print fillable forms). no_html will strip any html, possibly killing useful formatting in the process. */ -/datum/computer_file/report/proc/generate_pencode(access, with_fields, no_html) +/datum/computer_file/report/proc/generate_pencode(access, mob/user, with_fields, no_html) . = list() . += "\[center\][logo]\[/center\]" . += "\[center\]\[h2\][display_name()]\[/h2\]\[/center\]" . += "\[grid\]" for(var/datum/report_field/F in fields) - . += F.generate_row_pencode(access, with_fields) + . += F.generate_row_pencode(access, user, with_fields) . += "\[/grid\]" . = JOINTEXT(.) if(no_html) @@ -161,6 +154,27 @@ no_html will strip any html, possibly killing useful formatting in the process. /datum/computer_file/report/recipient/generate_fields() recipients = add_field(/datum/report_field/people/list_from_manifest, "Send Copies To") -/datum/computer_file/report/recipient/submit(mob/user) +/datum/computer_file/report/recipient/submit(mob/user, list/access) if((. = ..())) - recipients.send_email(user) \ No newline at end of file + recipients.send_email(user) + +/* +Access stuff. The report's read/write access should control whether it can be opened/submitted. +For field editing or viewing, use the field's read/write access instead. + +Overriden so that read access is required to have write access +*/ +/datum/computer_file/report/get_file_perms(list/accesses, mob/user) + var/perms = ..() + if(!(perms & OS_WRITE_ACCESS)) + perms &= ~OS_READ_ACCESS + return perms + +// Manually changing the permissions of a report will change *all* contained fields to match. +// TODO: Make report creation and access modification a bit more flexible. +/datum/computer_file/report/change_perms(change, perm, access_key, changer_accesses) + . = ..() + if(!.) + return + for(var/datum/report_field/field in fields) + field.set_access(read_access, write_access, TRUE) \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/reports/report_field.dm b/code/modules/modular_computers/file_system/reports/report_field.dm index ff11b6b8ed4..3c6d64b24f2 100644 --- a/code/modules/modular_computers/file_system/reports/report_field.dm +++ b/code/modules/modular_computers/file_system/reports/report_field.dm @@ -7,8 +7,10 @@ var/ID // A unique (per report) id; don't set manually. var/needs_big_box = 0 // Suggests that the output won't look good in-line. Useful in nanoui logic. var/ignore_value = 0 // Suggests that the value should not be displayed. - var/list/access_edit = list(list()) // The access required to edit the field. - var/list/access = list(list()) // The access required to view the field. + var/searchable = 0 // Whether or not the field will be searchable in the crew records computer. + var/can_mod_access = TRUE // Whether or not the access requirements of this field can be modified recursively from the record's access. + var/list/read_access = list() // The access required to edit the field. + var/list/write_access = list() // The access required to view the field. /datum/report_field/New(datum/computer_file/report/report) owner = report @@ -19,29 +21,34 @@ . = ..() //Access stuff. Can be given access constants or lists. See report access procs for documentation. -/datum/report_field/proc/set_access(access, access_edit, override = 1) - if(access) - if(!islist(access)) - access = list(access) - override ? (src.access = list(access)) : (src.access += list(access)) - if(access_edit) - if(!islist(access_edit)) - access_edit = list(access_edit) - override ? (src.access_edit = list(access_edit)) : (src.access_edit += list(access_edit)) - -/datum/report_field/proc/verify_access(given_access) - return has_access_pattern(access, given_access) - -/datum/report_field/proc/verify_access_edit(given_access) - if(!verify_access(given_access)) +//For fields, the recursive argument indicates whether this access set is being propogated onto the whole report at once or not. +/datum/report_field/proc/set_access(read_access, write_access, recursive = FALSE) + if(recursive && !can_mod_access) return - return has_access_pattern(access_edit, given_access) + if(read_access) + if(!islist(read_access)) + read_access = list(read_access) + src.read_access = read_access + if(write_access) + if(!islist(write_access)) + write_access = list(write_access) + src.write_access = write_access + +// Analogous to get_file_perms on reports. Read access is required to have write access. +/datum/report_field/proc/get_perms(accesses, mob/user) + if(!accesses || (isghost(user) && check_rights(R_ADMIN, 0, user))) // For internal use/use by admin ghosts. + return (OS_READ_ACCESS | OS_WRITE_ACCESS) + if(!LAZYLEN(read_access) || has_access(read_access, accesses)) + . |= OS_READ_ACCESS + + if(!LAZYLEN(write_access) || has_access(write_access, accesses)) + . |= OS_WRITE_ACCESS //Assumes the old and new fields are of the same type. Override if the field stores information differently. /datum/report_field/proc/copy_value(datum/report_field/old_field) value = old_field.value - access = old_field.access - access_edit = old_field.access_edit + read_access = old_field.read_access + write_access = old_field.write_access //Gives the user prompts to fill out the field. /datum/report_field/proc/ask_value(mob/user) @@ -58,11 +65,11 @@ /datum/report_field/proc/display_name() return name -/datum/report_field/proc/generate_row_pencode(access, with_fields) +/datum/report_field/proc/generate_row_pencode(access, mob/user, with_fields) if(!ignore_value) . += "\[row\]\[cell\]\[b\][display_name()]:\[/b\]" var/field = ((with_fields && can_edit) ? "\[field\]" : "" ) - if(!access || verify_access(access)) + if(!access || (get_perms(access, user) & OS_READ_ACCESS)) . += (needs_big_box ? "\[/grid\][get_value()][field]\[grid\]" : "\[cell\][get_value()][field]") else . += "\[cell\]\[REDACTED\][field]" @@ -70,11 +77,12 @@ . += "\[/grid\][display_name()]\[grid\]" . = JOINTEXT(.) -/datum/report_field/proc/generate_nano_data(list/given_access) +/datum/report_field/proc/generate_nano_data(list/given_access, mob/user) var/dat = list() if(given_access) - dat["access"] = verify_access(given_access) - dat["access_edit"] = verify_access_edit(given_access) + var/access_flags = get_perms(given_access, user) + dat["access"] = access_flags & OS_READ_ACCESS + dat["access_edit"] = access_flags & OS_WRITE_ACCESS dat["name"] = display_name() dat["value"] = get_value() dat["can_edit"] = can_edit diff --git a/code/modules/modular_computers/file_system/reports/warrant.dm b/code/modules/modular_computers/file_system/reports/warrant.dm index 29febf9abc5..c32a22435b2 100644 --- a/code/modules/modular_computers/file_system/reports/warrant.dm +++ b/code/modules/modular_computers/file_system/reports/warrant.dm @@ -35,13 +35,13 @@ /datum/computer_file/report/warrant/arrest title = "Arrest Warrant" form_name = "W-104-A" + write_access = list(access_security) /datum/computer_file/report/warrant/arrest/generate_fields() add_field(/datum/report_field/text_label/header, "Arrest Warrant") add_field(/datum/report_field/simple_text, "Name", "Unknown") add_field(/datum/report_field/pencode_text, "Charges", "No charges") add_field(/datum/report_field/signature, "Authorized by", "Unathorized") - set_access(access_edit = access_security) /datum/computer_file/report/warrant/arrest/get_category() . = ..() @@ -74,13 +74,13 @@ /datum/computer_file/report/warrant/search title = "Search Warrant" form_name = "W-104-S" + write_access = list(access_security) /datum/computer_file/report/warrant/search/generate_fields() add_field(/datum/report_field/text_label/header, "Search Warrant") add_field(/datum/report_field/simple_text, "Person/Location", "Unknown") add_field(/datum/report_field/pencode_text, "Reason", "No reason") add_field(/datum/report_field/signature, "Authorized by", "Unathorized") - set_access(access_edit = access_security) /datum/computer_file/report/warrant/search/get_category() . = ..() diff --git a/code/modules/modular_computers/hardware/hard_drive.dm b/code/modules/modular_computers/hardware/hard_drive.dm index 8b288e7cefe..1084a08c91e 100644 --- a/code/modules/modular_computers/hardware/hard_drive.dm +++ b/code/modules/modular_computers/hardware/hard_drive.dm @@ -90,7 +90,7 @@ // Use this proc to remove file from the drive. Returns 1 on success and 0 on failure. Contains necessary sanity checks. -/obj/item/stock_parts/computer/hard_drive/proc/remove_file(var/datum/computer_file/F) +/obj/item/stock_parts/computer/hard_drive/proc/remove_file(var/datum/computer_file/F, list/accesses, mob/user) if(!F || !istype(F)) return 0 @@ -99,7 +99,9 @@ if(!check_functionality()) return 0 - + + if(!(F.get_file_perms(accesses, user) & OS_WRITE_ACCESS)) + return 0 if(F in stored_files) stored_files -= F F.holder = null diff --git a/code/modules/modular_computers/networking/_network.dm b/code/modules/modular_computers/networking/_network.dm index 3469ee73ed5..c54260b4efa 100644 --- a/code/modules/modular_computers/networking/_network.dm +++ b/code/modules/modular_computers/networking/_network.dm @@ -33,7 +33,7 @@ /datum/computer_network/Destroy() for(var/datum/extension/network_device/D in devices) - D.disconnect() + D.disconnect(TRUE) QDEL_NULL_LIST(chat_channels) devices = null mainframes = null @@ -47,9 +47,6 @@ return FALSE if(D in devices) return TRUE - D.network_tag = get_unique_tag(D.network_tag) - devices |= D - devices_by_tag[D.network_tag] = D if(istype(D, /datum/extension/network_device/mainframe)) var/datum/extension/network_device/mainframe/M = D mainframes |= M @@ -59,11 +56,18 @@ else if(istype(D, /datum/extension/network_device/broadcaster/relay)) relays |= D add_log("Relay ONLINE", D.network_tag) - else if(istype(D, /datum/extension/network_device/acl) && !access_controller) - set_access_controller(D) + else if(istype(D, /datum/extension/network_device/acl)) + if(access_controller) + return FALSE + access_controller = D + add_log("New main access controller set.", D.network_tag) else if(istype(D, /datum/extension/network_device/camera)) var/datum/extension/network_device/camera/C = D add_camera_to_channels(C, C.channels) + + D.network_tag = get_unique_tag(D.network_tag) + devices |= D + devices_by_tag[D.network_tag] = D return TRUE /datum/computer_network/proc/remove_device(datum/extension/network_device/D) @@ -136,6 +140,7 @@ return if(R.allow_wifi && (R.long_range || (get_z(R.holder) == get_z(D.holder)))) . = WIRELESS_CONNECTION + /datum/computer_network/proc/get_signal_strength(datum/extension/network_device/D) var/connection_status = check_connection(D) if(!connection_status) @@ -208,31 +213,22 @@ if(net.router && ARE_Z_CONNECTED(get_z(net.router.holder), get_z(T))) return net -/datum/computer_network/proc/get_mainframes_by_role(mainframe_role = MF_ROLE_FILESERVER, mob/user) - // if administrator, give full access. - if(!user) - return mainframes_by_role[mainframe_role] - var/obj/item/card/id/network/id = user.GetIdCard() - if(id && istype(id, /obj/item/card/id/network) && access_controller && (id.user_id in access_controller.administrators)) +/datum/computer_network/proc/get_mainframes_by_role(mainframe_role = MF_ROLE_FILESERVER, list/accesses) + // Don't check for access if none is passed, for internal usage. + if(!accesses) return mainframes_by_role[mainframe_role] var/list/allowed_mainframes = list() for(var/datum/extension/network_device/D in mainframes_by_role[mainframe_role]) - if(D.has_access(user)) + if(D.has_access(accesses)) allowed_mainframes |= D return allowed_mainframes -/datum/computer_network/proc/get_devices_by_type(var/type, var/mob/user) - var/bypass_auth = !user - if(!bypass_auth) - // Check for admin. - var/obj/item/card/id/network/id = user.GetIdCard() - if(id && istype(id, /obj/item/card/id/network) && access_controller && (id.user_id in access_controller.administrators)) - bypass_auth = TRUE - +/datum/computer_network/proc/get_devices_by_type(type, list/accesses) var/list/results = list() + var/bypass_auth = !accesses for(var/datum/extension/network_device/device in devices) if(istype(device.holder, type)) - if(bypass_auth || device.has_access(user)) + if(bypass_auth || device.has_access(accesses)) results += device.holder return results diff --git a/code/modules/modular_computers/networking/accounts/_network_accounts.dm b/code/modules/modular_computers/networking/accounts/_network_accounts.dm new file mode 100644 index 00000000000..25d17ec1833 --- /dev/null +++ b/code/modules/modular_computers/networking/accounts/_network_accounts.dm @@ -0,0 +1,99 @@ +/datum/computer_network/proc/get_accounts(accesses) + var/list/result = list() + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(MF_ROLE_ACCOUNT_SERVER, accesses)) + for(var/datum/computer_file/data/account/E in M.get_all_files()) + if(E.backup) + continue + ADD_SORTED(result, E, /proc/cmp_accounts_asc) + return result + +/datum/computer_network/proc/get_accounts_unsorted(accesses) + var/list/result = list() + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(MF_ROLE_ACCOUNT_SERVER, accesses)) + for(var/datum/computer_file/data/account/E in M.get_all_files()) + if(E.backup) + continue + result |= E + return result + +// Return account backups keyed by account login +/datum/computer_network/proc/get_account_backups(accesses) + var/list/result = list() + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(MF_ROLE_ACCOUNT_SERVER, accesses)) + for(var/datum/computer_file/data/account/E in M.get_all_files()) + if(E.login in result) + continue + if(E.backup) + result[E.login] = E + + return result + +/datum/computer_network/proc/add_account(datum/computer_file/data/account/acc, accesses) + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(MF_ROLE_ACCOUNT_SERVER, accesses)) + if(M.store_file(acc)) + return TRUE + +/datum/computer_network/proc/find_account_by_login(login, accesses) + for(var/datum/computer_file/data/account/A in get_accounts(accesses)) + if(A.login == login) + return A + +/datum/computer_network/proc/create_account(mob/user, desired_login, desired_password, full_name, list/accesses, force_create = TRUE) + desired_login = sanitize_for_account(desired_login) + var/login = "[desired_login]" + if(!length(login)) + return FALSE + if(find_account_by_login(login)) + if(force_create) + login = "[desired_login][random_id(/datum/computer_file/data/account/, 100, 999)]" + else + return FALSE + if(!desired_password) + if(force_create) + desired_password = GenerateKey() + var/datum/computer_file/data/account/EA = new/datum/computer_file/data/account(login, desired_password, full_name) + if(add_account(EA, accesses)) + if(user) + user.store_account_credentials(EA.login, EA.password, network_id) + return TRUE + return FALSE + +/datum/computer_network/proc/rename_account(old_login, desired_login) + var/datum/computer_file/data/account/account = find_account_by_login(old_login) + var/new_login = sanitize_for_account(desired_login) + if(new_login == old_login) + return TRUE//If we aren't going to be changing the login, we quit silently. + if(find_account_by_login(new_login)) + return FALSE + account.login = new_login + add_log("Account login changed: [old_login] changed to [new_login]") + +// Mob procs +/mob/proc/store_account_credentials(login, password, network_id) + if(mind) + mind.initial_account_login["login"] = login + mind.initial_account_login["password"] = password + mind.account_network = network_id + StoreMemory("Your [network_id] login is [login] and the password is [password].", /decl/memory_options/system) + var/obj/item/card/id/I = GetIdCard() + if(I) + I.associated_network_account = list("login" = login, "password" = password) + +/mob/proc/update_acount_login(login) + if(mind) + mind.initial_account_login["login"] = login + StoreMemory("Your network account login has been changed to [login].", /decl/memory_options/system) + var/obj/item/card/id/I = GetIdCard() + if(I && I.associated_network_account) + I.associated_network_account["login"] = login + +/mob/proc/create_or_update_account(newname) + if(!mind) + return + var/datum/computer_network/network = get_local_network_at(get_turf(src)) + if(network) + var/old_account = mind.initial_account_login["login"] + if(!old_account) + network.create_account(src, newname) + else + network.rename_account(old_account, newname) \ No newline at end of file diff --git a/code/modules/modular_computers/networking/accounts/account.dm b/code/modules/modular_computers/networking/accounts/account.dm new file mode 100644 index 00000000000..68846759b56 --- /dev/null +++ b/code/modules/modular_computers/networking/accounts/account.dm @@ -0,0 +1,146 @@ +/datum/computer_file/data/account + filetype = "ACT" + var/login = "" + var/password = "" + + var/can_login = TRUE // Whether you can log in with this account. Set to false for system accounts + var/suspended = FALSE // Whether the account is banned by the SA. + var/list/logged_in_os = list() // OS which are currently logged into this account. Used for e-mail notifications, currently. + + var/list/groups = list() // Groups which this account is a member of. + var/list/parent_groups = list() // Parent groups with a child/children which this account is a member of. + + var/fullname = "N/A" + + // E-Mail + var/list/inbox = list() + var/list/outbox = list() + var/list/spam = list() + var/list/deleted = list() + + var/notification_mute = FALSE + var/notification_sound = "*beep*" + var/backup = FALSE // Backups are not returned when searching for accounts, but can be recovered using the accounts program. + + copy_string = "(Backup)" + +/datum/computer_file/data/account/calculate_size() + size = 1 + for(var/datum/computer_file/data/email_message/stored_message in all_emails()) + stored_message.calculate_size() + size += stored_message.size + +/datum/computer_file/data/account/New(_login, _password, _fullname) + if(_login) + login = _login + stored_data = "[login]" + if(_password) + password = _password + if(_fullname) + fullname = _fullname + if(filename == initial(filename)) + filename = "account[random_id(type, 100,999)]" + ..() + +/datum/computer_file/data/account/Destroy() + for(var/weakref/os_ref in logged_in_os) + var/datum/extension/interactive/os/os = os_ref.resolve() + if(os.login == login) + os.logout_account() + + logged_in_os.Cut() + groups.Cut() + parent_groups.Cut() + + QDEL_NULL_LIST(inbox) + QDEL_NULL_LIST(outbox) + QDEL_NULL_LIST(spam) + QDEL_NULL_LIST(deleted) + . = ..() + +/datum/computer_file/data/account/proc/all_emails() + return (inbox | spam | deleted | outbox) + +/datum/computer_file/data/account/proc/all_incoming_emails() + return (inbox | spam | deleted) + +/datum/computer_file/data/account/proc/send_mail(var/recipient_address, var/datum/computer_file/data/email_message/message, var/datum/computer_network/network) + if(!network) + return + var/datum/computer_file/data/account/recipient + for(var/datum/computer_file/data/account/account in network.get_accounts()) + if((account.login + "@[network.network_id]") == recipient_address) // TODO: Add support for cross network email. + recipient = account + break + + if(!istype(recipient)) + return 0 + + if(!recipient.receive_mail(message, network)) + return + + outbox.Add(message) + if(network.intrusion_detection_enabled) + network.add_log("EMAIL LOG: [login] -> [recipient.login] title: [message.title].") + return 1 + +/datum/computer_file/data/account/proc/receive_mail(var/datum/computer_file/data/email_message/received_message, var/datum/computer_network/network) + if(!network) + return + var/datum/computer_file/data/email_message/received_copy = received_message.clone() + received_copy.set_timestamp() + inbox.Add(received_copy) + + for(var/weakref/os_ref in logged_in_os) + var/datum/extension/interactive/os/os = os_ref.resolve() + if(istype(os)) + os.mail_received(received_copy) + else + logged_in_os -= os_ref + return 1 + +/datum/computer_file/data/account/clone() + var/datum/computer_file/data/account/copy = ..(TRUE) // We always rename the file since a copied account is always a backup. + copy.backup = TRUE + + copy.login = login + copy.password = password + copy.can_login = can_login + copy.suspended = suspended + + copy.groups = groups.Copy() + copy.parent_groups = parent_groups.Copy() + + copy.fullname = fullname + + // TODO: Don't backup e-mails for now - they are themselves other files which makes this complicated. In the future + // accounts will point to e-mails stored seperately on a server. + return copy + +// Address namespace (@internal-services.net) for email addresses with special purpose only!. +/datum/computer_file/data/account/service + can_login = FALSE + +/datum/computer_file/data/account/service/broadcaster + login = EMAIL_BROADCAST + +/datum/computer_file/data/account/service/broadcaster/receive_mail(var/datum/computer_file/data/email_message/received_message, var/datum/computer_network/network) + if(!network || !istype(received_message)) + return 0 + // Possibly exploitable for user spamming so keep admins informed. + if(!received_message.spam) + log_and_message_admins("Broadcast email address used by [usr]. Message title: [received_message.title].") + + for(var/datum/computer_file/data/account/email_account in network.get_accounts()) + if(istype(email_account, /datum/computer_file/data/account/service/broadcaster)) + continue + var/datum/computer_file/data/email_message/new_message = received_message.clone() + send_mail(email_account.login + "@[network.network_id]", new_message, network) + + return 1 + +/datum/computer_file/data/account/service/document + login = EMAIL_DOCUMENTS + +/datum/computer_file/data/account/service/sysadmin + login = EMAIL_SYSADMIN \ No newline at end of file diff --git a/code/modules/modular_computers/networking/emails/email_message.dm b/code/modules/modular_computers/networking/accounts/email_message.dm similarity index 88% rename from code/modules/modular_computers/networking/emails/email_message.dm rename to code/modules/modular_computers/networking/accounts/email_message.dm index d499195db28..6901695b145 100644 --- a/code/modules/modular_computers/networking/emails/email_message.dm +++ b/code/modules/modular_computers/networking/accounts/email_message.dm @@ -1,5 +1,5 @@ // Currently not actually represented in file systems, though the support for it is in place already. -/datum/computer_file/data/email_message/ +/datum/computer_file/data/email_message stored_data = "" var/title = "" var/source = "" @@ -7,6 +7,10 @@ var/timestamp = "" var/datum/computer_file/attachment = null +/datum/computer_file/data/email_message/Destroy() + . = ..() + QDEL_NULL(attachment) + /datum/computer_file/data/email_message/clone() var/datum/computer_file/data/email_message/temp = ..() temp.title = title @@ -17,7 +21,6 @@ temp.attachment = attachment.clone() return temp - // Turns /email_message/ file into regular /data/ file. /datum/computer_file/data/email_message/proc/export() var/datum/computer_file/data/dat = new/datum/computer_file/data() diff --git a/code/modules/modular_computers/networking/device_types/_network_device.dm b/code/modules/modular_computers/networking/device_types/_network_device.dm index aa0ad6d872a..e154b926896 100644 --- a/code/modules/modular_computers/networking/device_types/_network_device.dm +++ b/code/modules/modular_computers/networking/device_types/_network_device.dm @@ -15,6 +15,10 @@ var/last_rand_time // Last time a random method was called. + // These variables are for the *device's* public variables and methods, if they exist. + var/list/device_variables + var/list/device_methods + /datum/extension/network_device/New(datum/holder, n_id, n_key, c_type, autojoin = TRUE) ..() network_id = n_id @@ -26,6 +30,13 @@ network_tag = "[uppertext(replacetext(O.name, " ", "_"))]-[sequential_id(type)]" if(autojoin) SSnetworking.queue_connection(src) + + if(length(device_variables)) + for(var/path in device_variables) + device_variables[path] = GET_DECL(path) + if(length(device_methods)) + for(var/path in device_methods) + device_methods[path] = GET_DECL(path) if(has_commands) reload_commands() @@ -40,7 +51,7 @@ return FALSE return net.add_device(src) -/datum/extension/network_device/proc/disconnect() +/datum/extension/network_device/proc/disconnect(net_down) var/datum/computer_network/net = SSnetworking.networks[network_id] if(!net) return FALSE @@ -225,33 +236,53 @@ do_change_net_tag(user) return TOPIC_REFRESH -/datum/extension/network_device/proc/has_access(mob/user) +/datum/extension/network_device/proc/has_access(list/accesses) var/datum/computer_network/network = get_network() if(!network) return TRUE // If not on network, always TRUE for access, as there isn't anything to access. - if(!user) - return FALSE - var/obj/item/card/id/network/id = user.GetIdCard() - if(id && istype(id, /obj/item/card/id/network) && network.access_controller && (id.user_id in network.access_controller.administrators)) - return TRUE var/obj/M = holder - return M.check_access(user) - -// Return the target of any commands passed to this device. -/datum/extension/network_device/proc/get_command_target() - if(istype(holder, /obj/machinery)) + if(!accesses) + accesses = list() + return M.check_access_list(accesses) + +// Return the target of the command passed to this device. +/datum/extension/network_device/proc/get_command_target(command) + var/decl/public_access/public_thing + if(command_and_call && command_and_call[command]) + public_thing = command_and_call[command] + else if(command_and_write && command_and_write[command]) + public_thing = command_and_write[command] + else + return + if(device_variables && (public_thing.type in device_variables)) + return src + if(device_methods && (public_thing.type in device_methods)) + return src + if((public_thing.type in get_holder_variables()) || (public_thing in get_holder_methods())) return holder // Return the public methods and variables available for commands. /datum/extension/network_device/proc/get_public_methods() - var/obj/machinery/M = get_command_target() - if(istype(M)) - return M.public_methods + var/list/public_methods = get_holder_methods() + if(device_methods) + public_methods += device_methods + return public_methods /datum/extension/network_device/proc/get_public_variables() - var/obj/machinery/M = get_command_target() + var/list/public_variables = get_holder_variables() + if(device_variables) + public_variables += device_variables + return public_variables + +/datum/extension/network_device/proc/get_holder_methods() + var/obj/machinery/M = holder if(istype(M)) - return M.public_variables + return M.public_methods?.Copy() + +/datum/extension/network_device/proc/get_holder_variables() + var/obj/machinery/M = holder + if(istype(M)) + return M.public_variables?.Copy() /datum/extension/network_device/proc/set_command_reference(list/selected_commands, alias, reference) if(!alias || !reference) @@ -292,18 +323,18 @@ return FALSE // Any modification of public vars and calling methods from network commands. Return text feedback on success/unsuccess of command. -/datum/extension/network_device/proc/on_command(command, command_args, mob/user) - var/datum/command_target = get_command_target() +/datum/extension/network_device/proc/on_command(command, command_args, list/access) + var/datum/command_target = get_command_target(command) if(!has_commands) return "Device cannot receive commands." if(!command_target) return "No valid target found for command '[command]'" - if(!has_access(user)) + if(!has_access(access)) return "Access denied." if(LAZYACCESS(command_and_call, command)) var/decl/public_access/public_method/method = command_and_call[command] - method.perform(command_target, command_args) - return "Successfully called method '[command]' on [network_tag]." + var/output = method.perform(arglist(list(command_target) + command_args)) + return "Successfully called method '[command]' on [network_tag]." + "[output ? " Device reported: [output]" : null]" if(LAZYACCESS(command_and_write, command)) var/decl/public_access/public_variable/variable = command_and_write[command] if(command_args) // Write to a var. @@ -352,10 +383,10 @@ return FALSE // Calls a random method for skill failure etc. -/datum/extension/network_device/proc/random_method(var/user) +/datum/extension/network_device/proc/random_method(list/access) if(world.time < last_rand_time + 5 SECONDS) return "Reinstancing command system, please try again in a few moments." - if(user && !has_access(user)) + if(access && !has_access(access)) return "Access denied" var/rand_alias = SAFEPICK(command_and_call) var/decl/public_access/public_method/rand_method = LAZYACCESS(command_and_call, rand_alias) @@ -370,10 +401,6 @@ LAZYCLEARLIST(command_and_call) LAZYCLEARLIST(command_and_write) - var/obj/machinery/M = get_command_target() - if(!has_commands || !istype(M)) - return - var/list/pub_methods = get_public_methods() var/list/pub_vars = get_public_variables() diff --git a/code/modules/modular_computers/networking/device_types/acl.dm b/code/modules/modular_computers/networking/device_types/acl.dm index aeef8e409e7..3f3ae9230b3 100644 --- a/code/modules/modular_computers/networking/device_types/acl.dm +++ b/code/modules/modular_computers/networking/device_types/acl.dm @@ -1,45 +1,128 @@ /datum/extension/network_device/acl connection_type = NETWORK_CONNECTION_STRONG_WIRELESS - var/list/administrators = list() // A list of numerical user IDs of users that are administrators on a network. - var/program_control = FALSE // Whether the ACL controls program access or not. - var/list/program_access = list() // List of lists containing perimitted accesses for programs. + var/list/group_dict = list() // Groups on the network, stored as strings. + // Parents groups with children have a list of child groups associated with the string key -/datum/extension/network_device/acl/New() - . = ..() - for(var/prog_type in subtypesof(/datum/computer_file/program)) - var/datum/computer_file/program/prog = prog_type - if(!initial(prog.available_on_network)) - continue - program_access[initial(prog.filename)] = list() + var/list/all_groups = list() // All group strings, not sorted by parent or child group. Used for uniqueness checking. -/datum/extension/network_device/acl/proc/add_admin(var/user_id) - administrators |= user_id + var/allow_submanagement = FALSE // If enabled, users who are members of a parent group will be able to manage membership of all child groups under that parent group, + // Otherwise, assignment is strictly by access to the network access controller + var/parent_account_creation = FALSE // If enabled, all users who are members of ANY parent group will be able to create new accounts. + // Otherwise, account creation is restricted to those with access to the account server(s) -/datum/extension/network_device/acl/proc/rem_admin(var/user_id) - administrators -= user_id + has_commands = TRUE + device_methods = list( + /decl/public_access/public_method/add_group, + /decl/public_access/public_method/remove_group, + /decl/public_access/public_method/list_groups + ) -/datum/extension/network_device/acl/proc/get_grant(var/grant_data) +/datum/extension/network_device/acl/proc/get_group_dict() + return group_dict + +/datum/extension/network_device/acl/proc/get_all_groups() + return all_groups + +// Check if the passed user account can create new accounts by seeing if parent account creation is enabled, then checking +// if they are a member of a parent group. Otherwise, account creation permission is solely based on access to account servers. +/datum/extension/network_device/acl/proc/check_account_creation_auth(datum/computer_file/data/account/check_account) + if(parent_account_creation && check_account) + for(var/group in check_account.groups) + if(group in group_dict) + return TRUE + return FALSE + +/datum/extension/network_device/acl/proc/add_group(group_name, parent_group) + group_name = sanitize_for_group("[group_name]") + if(!length(group_name) || !istext(group_name) || (parent_group && !istext(parent_group))) + return "Input Error" + if(length(group_name) > 15) + return "Maximum group name length is 15 characters." + if(group_name in all_groups) + return "Group name must be unique" + + // Adding a child group. + if(parent_group) + if(!(parent_group in group_dict)) + return "No parent group with name \"[parent_group]\" found." + if(!group_dict[parent_group]) + group_dict[parent_group] = list() + group_dict[parent_group] += group_name + else + group_dict += group_name + + all_groups += group_name + return "Added [parent_group ? "child group" : "parent group"] \"[group_name]\"" + "[parent_group ? " to \"[parent_group]\"." : "."]" + +/datum/extension/network_device/acl/proc/remove_group(group_name, parent_group) + if(!istext(group_name) || (parent_group && !istext(parent_group))) + return "Input Error" + if(parent_group) + if(!(parent_group in group_dict)) + return "No parent group with name \"[parent_group]\" found." + if(!group_dict[parent_group] || !(group_name in group_dict[parent_group])) + return "No child group with name \"[group_name]\" found in \"[parent_group]\"" + group_dict[parent_group] -= group_name + if(!length(group_dict[parent_group])) // Cull the list until we add a new child group. + group_dict[parent_group] = null + all_groups -= group_name + return "Removed child group \"[group_name]\" from parent group \"[parent_group]\"." + if(group_name in group_dict) + if(length(group_dict[group_name])) + for(var/child_name in group_dict[group_name]) + all_groups -= child_name + group_dict -= group_name + all_groups -= group_name + return "Removed parent group \"[group_name]\" and associated child groups." + else + return "No parent group with name \"[parent_group]\" found." + +/datum/extension/network_device/acl/proc/toggle_parent_account_creation() + parent_account_creation = !parent_account_creation var/datum/computer_network/network = get_network() - if(!network) - return - return network.find_file_by_name(grant_data, MF_ROLE_CREW_RECORDS) + if(network.access_controller == src) + network.add_log("Access controller parent account creation toggled [parent_account_creation ? "ON" : "OFF"]", network_tag) -/datum/extension/network_device/acl/proc/get_all_grants() - var/list/grants = list() + +/datum/extension/network_device/acl/proc/toggle_submanagement() + allow_submanagement = !allow_submanagement var/datum/computer_network/network = get_network() - if(!network) - return grants - var/list/grant_files = network.get_all_files_of_type(/datum/computer_file/data/grant_record, MF_ROLE_CREW_RECORDS, TRUE) - for(var/datum/computer_file/data/grant_record/GR in grant_files) - grants |= GR - return grants - -/datum/extension/network_device/acl/proc/get_program_access(var/program_name) - if(!length(program_access[program_name])) - return list() - . = list() - for(var/access in program_access[program_name]) - . += uppertext("[network_id].[access]") - - return list(.) \ No newline at end of file + if(network.access_controller == src) + network.add_log("Access controller submanagement toggled [allow_submanagement ? "ON" : "OFF"]", network_tag) + +/datum/extension/network_device/acl/proc/get_group_listing(parent_group) + if(parent_group && !istext(parent_group)) + return "Input Error" + if(parent_group) + if(!(parent_group in group_dict)) + return "No parent group with name \"[parent_group]\" found." + return "[parent_group]: [english_list(group_dict[parent_group])]" + return "Parent groups: [english_list(group_dict)]" + +// Helper proc for adding multiple groups at once for preset ACLs etc. +/datum/extension/network_device/acl/proc/add_groups(list/preset_groups) + for(var/parent_group in preset_groups) + add_group(parent_group) + var/list/child_groups = preset_groups[parent_group] + if(islist(child_groups)) + for(var/child_group in child_groups) + add_group(child_group, parent_group) + +/decl/public_access/public_method/add_group + name = "Add group" + desc = "Adds a new group with the passed name. A parent group can be passed to add a child group." + call_proc = /datum/extension/network_device/acl/proc/add_group + forward_args = TRUE + +/decl/public_access/public_method/remove_group + name = "Remove group" + desc = "Removes a group with the passed name. A parent group can be passed to remove a child group of that parent." + call_proc = /datum/extension/network_device/acl/proc/remove_group + forward_args = TRUE + +/decl/public_access/public_method/list_groups + name = "List groups" + desc = "Lists groups on the group access controller. A parent group can be passed list children of that parent group." + call_proc = /datum/extension/network_device/acl/proc/get_group_listing + forward_args = TRUE \ No newline at end of file diff --git a/code/modules/modular_computers/networking/device_types/mainframe.dm b/code/modules/modular_computers/networking/device_types/mainframe.dm index b7c91be4f7b..cc7ebc8d258 100644 --- a/code/modules/modular_computers/networking/device_types/mainframe.dm +++ b/code/modules/modular_computers/networking/device_types/mainframe.dm @@ -1,9 +1,9 @@ var/global/list/all_mainframe_roles = list( MF_ROLE_SOFTWARE, - MF_ROLE_FILESERVER, - MF_ROLE_EMAIL_SERVER, + MF_ROLE_FILESERVER, MF_ROLE_LOG_SERVER, - MF_ROLE_CREW_RECORDS + MF_ROLE_CREW_RECORDS, + MF_ROLE_ACCOUNT_SERVER ) /datum/extension/network_device/mainframe @@ -42,13 +42,13 @@ var/global/list/all_mainframe_roles = list( if(HDD) return HDD.find_file_by_name(filename) -/datum/extension/network_device/mainframe/proc/delete_file(filename) +/datum/extension/network_device/mainframe/proc/delete_file(filename, list/accesses, mob/user) var/obj/item/stock_parts/computer/hard_drive/HDD = get_storage() if(HDD) var/datum/computer_file/data/F = HDD.find_file_by_name(filename) if(!F || F.undeletable) return FALSE - return HDD.remove_file(F) + return HDD.remove_file(F, accesses, user) /datum/extension/network_device/mainframe/proc/store_file(datum/computer_file/file) var/obj/item/stock_parts/computer/hard_drive/HDD = get_storage() diff --git a/code/modules/modular_computers/networking/device_types/relay.dm b/code/modules/modular_computers/networking/device_types/relay.dm index cc56e7d4f91..4179effbf61 100644 --- a/code/modules/modular_computers/networking/device_types/relay.dm +++ b/code/modules/modular_computers/networking/device_types/relay.dm @@ -1,6 +1,7 @@ /datum/extension/network_device/broadcaster/relay connection_type = NETWORK_CONNECTION_STRONG_WIRELESS expected_type = /obj/machinery + var/reconnect_time /datum/extension/network_device/broadcaster/relay/get_nearby_networks() if(long_range) @@ -21,7 +22,22 @@ return FALSE if(!long_range && !(network_id in get_nearby_networks())) return FALSE - return net.add_device(src) + . = net.add_device(src) + if(.) + reconnect_time = null +/datum/extension/network_device/broadcaster/relay/disconnect(net_down) + . = ..() + // Router went down, we should try to hook back up when it's up + if(net_down) + reconnect_time = world.time + 30 SECONDS + START_PROCESSING(SSprocessing, src) + +/datum/extension/network_device/broadcaster/relay/Process() + if(world.time > reconnect_time) + if(connect()) + return PROCESS_KILL + reconnect_time = world.time + 30 SECONDS + /datum/extension/network_device/broadcaster/relay/long_range long_range = TRUE diff --git a/code/modules/modular_computers/networking/emails/_network_email.dm b/code/modules/modular_computers/networking/emails/_network_email.dm deleted file mode 100644 index 40fbedaccf0..00000000000 --- a/code/modules/modular_computers/networking/emails/_network_email.dm +++ /dev/null @@ -1,70 +0,0 @@ -/datum/computer_network/proc/get_email_addresses() - var/list/result = list() - for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(MF_ROLE_EMAIL_SERVER)) - for(var/datum/computer_file/data/email_account/E in M.get_all_files()) - ADD_SORTED(result, E, /proc/cmp_emails_asc) - return result - -/datum/computer_network/proc/add_email_account(datum/computer_file/data/email_account/acc) - for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(MF_ROLE_EMAIL_SERVER)) - if(M.store_file(acc)) - return TRUE - -/datum/computer_network/proc/find_email_by_name(var/login) - for(var/datum/computer_file/data/email_account/A in get_email_addresses()) - if(A.login == login) - return A - -/datum/computer_network/proc/create_email(mob/user, desired_name, domain, assignment) - desired_name = sanitize_for_email(desired_name) - var/login = "[desired_name]@[domain]" - if(find_email_by_name(login)) - login = "[desired_name][random_id(/datum/computer_file/data/email_account/, 100, 999)]@[domain]" - - var/datum/computer_file/data/email_account/EA = new/datum/computer_file/data/email_account(login, user.real_name, assignment) - EA.password = GenerateKey() - add_email_account(EA) - user.store_email_credentials(EA.login, EA.password) - -/datum/computer_network/proc/rename_email(mob/user, old_login, desired_name, domain) - var/datum/computer_file/data/email_account/account = find_email_by_name(old_login) - var/new_login = sanitize_for_email(desired_name) - new_login += "@[domain]" - if(new_login == old_login) - return //If we aren't going to be changing the login, we quit silently. - if(find_email_by_name(new_login)) - to_chat(user, "Your email could not be updated: the new username is invalid.") - return - account.login = new_login - add_log("Email address changed for [user]: [old_login] changed to [new_login]") - user.update_email_name(new_login) - - -// Mob procs -/mob/proc/store_email_credentials(login, password) - if(mind) - mind.initial_email_login["login"] = login - mind.initial_email_login["password"] = password - StoreMemory("Your email account address is [login] and the password is [password].", /decl/memory_options/system) - var/obj/item/card/id/I = GetIdCard() - if(I) - I.associated_email_login = list("login" = login, "password" = password) - -/mob/proc/update_email_name(login) - if(mind) - mind.initial_email_login["login"] = login - StoreMemory("Your email account address has been changed to [login].", /decl/memory_options/system) - var/obj/item/card/id/I = GetIdCard() - if(I && I.associated_email_login) - I.associated_email_login["login"] = login - -/mob/proc/create_or_rename_email(newname, domain) - if(!mind) - return - var/datum/computer_network/network = get_local_network_at(get_turf(src)) - if(network) - var/old_email = mind.initial_email_login["login"] - if(!old_email) - network.create_email(src, newname, domain) - else - network.rename_email(src, old_email, newname, domain) \ No newline at end of file diff --git a/code/modules/modular_computers/networking/machinery/_network_machine.dm b/code/modules/modular_computers/networking/machinery/_network_machine.dm index 2a40a70b213..3441446f4d8 100644 --- a/code/modules/modular_computers/networking/machinery/_network_machine.dm +++ b/code/modules/modular_computers/networking/machinery/_network_machine.dm @@ -16,7 +16,6 @@ var/inefficiency = 0.12 // How much power is waste heat. var/heat_threshold = 90 CELSIUS // At what temperature the machine will lock up. var/overheated = FALSE - var/runtimeload // Use this for calling an even later lateload. /obj/machinery/network/Initialize() set_extension(src, network_device_type, initial_network_id, initial_network_key, NETWORK_CONNECTION_STRONG_WIRELESS) @@ -54,18 +53,11 @@ env.merge(removed) /obj/machinery/network/Process() - if(runtimeload) - RuntimeInitialize() - runtimeload = FALSE - set_overheated(is_overheated()) if(stat & (BROKEN|NOPOWER)) return produce_heat() -/obj/machinery/network/proc/RuntimeInitialize() - - /obj/machinery/network/interface_interact(user) ui_interact(user) return TRUE diff --git a/code/modules/modular_computers/networking/machinery/acl.dm b/code/modules/modular_computers/networking/machinery/acl.dm index 487282da33a..566d6e17816 100644 --- a/code/modules/modular_computers/networking/machinery/acl.dm +++ b/code/modules/modular_computers/networking/machinery/acl.dm @@ -1,5 +1,6 @@ /obj/machinery/network/acl name = "network access controller" + desc = "A mainframe that manages encryption keys tied to groups and their children on its parent network." icon = 'icons/obj/machines/tcomms/aas.dmi' icon_state = "aas" network_device_type = /datum/extension/network_device/acl @@ -8,274 +9,110 @@ uncreated_component_parts = null stat_immune = 0 base_type = /obj/machinery/network/acl - runtimeload = TRUE - // Datum file source for where grants/records are. - var/datum/file_storage/network/file_source = /datum/file_storage/network/machine - var/editing_user // Numerical user ID of the user being editing on this device. - var/editing_program // Name of current program being edited. - var/list/initial_grants //defaults to all possible station accesses if left null - -/obj/machinery/network/acl/merchant - initial_grants = list( - access_crate_cash, - access_merchant - ) - -/obj/machinery/network/acl/antag - initial_grants = list( - access_syndicate - ) + var/current_group // The group currently being edited. + var/list/preset_groups // Dictionary of parent groups->list(child_groups) /obj/machinery/network/acl/Initialize() . = ..() - if(isnull(initial_grants)) - initial_grants = get_all_station_access() - if(ispath(file_source)) - file_source = new file_source(null, src) - -/obj/machinery/network/acl/RuntimeInitialize() - // Get default file server. - var/datum/extension/network_device/acl/FW = get_extension(src, /datum/extension/network_device) - var/datum/computer_network/network = FW.get_network() - if(network) - var/datum/extension/network_device/file_server = network.get_file_server_by_role(MF_ROLE_CREW_RECORDS) - if(file_server) - file_source.server = file_server.network_tag - // Populate initial grants. - for(var/grant in initial_grants) - create_grant(grant) - -/obj/machinery/network/acl/attackby(var/obj/item/W, var/mob/user) - if(istype(W, /obj/item/card/id)) // ID Card, try to insert it. - var/obj/item/card/id/I = W - var/obj/item/stock_parts/computer/card_slot/card_slot = get_component_of_type(/obj/item/stock_parts/computer/card_slot) - if(!card_slot) - return - card_slot.insert_id(I, user) - return - . = ..() + if(preset_groups) + var/datum/extension/network_device/acl/D = get_extension(src, /datum/extension/network_device) + D.add_groups(preset_groups) /obj/machinery/network/acl/OnTopic(mob/user, href_list, datum/topic_state/state) . = ..() if(.) return - var/datum/extension/network_device/acl/computer = get_extension(src, /datum/extension/network_device) - var/datum/computer_network/network = computer.get_network() - if(!network) - error = "NETWORK ERROR: Connection lost." + var/datum/extension/network_device/acl/D = get_extension(src, /datum/extension/network_device) + var/datum/computer_network/network = D.get_network() + if(!network || network.access_controller != D) + error = "NETWORK ERROR: Connection lost. Another access controller may be active on the network." return TOPIC_REFRESH if(href_list["back"]) - editing_user = null - editing_program = null + current_group = null return TOPIC_REFRESH - - if(href_list["change_file_server"]) - var/list/file_servers = network.get_file_server_tags(MF_ROLE_CREW_RECORDS) - var/file_server = input(usr, "Choose a fileserver to view access records on:", "Select File Server") as null|anything in file_servers - if(file_server) - file_source.server = file_server - return TOPIC_REFRESH - - if(href_list["assign_grant"]) - // Resolve our selection back to a file. - var/datum/computer_file/data/grant_record/grant = computer.get_grant(href_list["assign_grant"]) - if(!grant) - error = "ERROR: Grant record not found." - return TOPIC_REFRESH - if(editing_user) - var/datum/computer_file/report/crew_record/AR = get_access_record() - if(!AR) - error = "ERROR: Access record not found." - return TOPIC_REFRESH - AR.add_grant(grant) - else if(editing_program) - var/list/program_access = computer.program_access[editing_program] - program_access |= grant.stored_data - return TOPIC_REFRESH - - if(href_list["remove_grant"]) - if(editing_user) - var/datum/computer_file/report/crew_record/AR = get_access_record() - if(!AR) - error = "ERROR: Access record not found." - return TOPIC_REFRESH - AR.remove_grant(href_list["remove_grant"]) // Add the grant to the record. - if(editing_program) - var/datum/computer_file/data/grant_record/grant = computer.get_grant(href_list["remove_grant"]) - if(!grant) - error = "ERROR: Grant record not found." - return TOPIC_REFRESH - var/list/program_access = computer.program_access[editing_program] - program_access -= grant.stored_data + + if(href_list["create_group"]) + var/group_name = sanitize_for_group(input(usr, "Enter the name of the new group. Maximum 15 characters, only alphanumeric characters, _ and - are allowed:", "Create Group")) + if(!length(group_name)) + return TOPIC_HANDLED + if(!CanInteract(user, global.default_topic_state)) return TOPIC_REFRESH - - if(href_list["clear_program_access"]) - if(editing_program) - computer.program_access[editing_program] = list() - else - error = "ERROR: Program not found." - return TOPIC_REFRESH - - if(href_list["toggle_program_control"]) - computer.program_control = !computer.program_control + + var/output = D.add_group(group_name, current_group) + if(group_name in D.all_groups) + to_chat(user, SPAN_NOTICE(output)) + else // Failure! + to_chat(user, SPAN_WARNING(output)) return TOPIC_REFRESH - if(href_list["create_grant"]) - var/new_grant_name = uppertext(sanitize(input(usr, "Enter the name of the new grant:", "Create Grant"))) + if(href_list["remove_group"]) + var/group_name = href_list["remove_group"] + var/removal_check = alert(user, "Are you sure you want to remove the group [group_name]?", "Group Removal", "No", "Yes") if(!CanInteract(user, global.default_topic_state)) return TOPIC_REFRESH - if(!new_grant_name) - return TOPIC_REFRESH - if(!create_grant(new_grant_name)) - error = "MAINFRAME ERROR: Unable to store grant on mainframe." + if(removal_check == "Yes") + var/output = D.remove_group(group_name, current_group) + if(group_name in D.all_groups) // Failure! + to_chat(user, SPAN_WARNING(output)) + else + to_chat(user, SPAN_NOTICE(output)) return TOPIC_REFRESH - - if(href_list["delete_grant"]) - if(!file_source.delete_file(href_list["delete_grant"])) - error = "FIREWALL ERROR: Unable to delete grant." - return TOPIC_REFRESH - - if(href_list["view_user"]) - editing_user = href_list["view_user"] - editing_program = null + + if(href_list["toggle_submanagement"]) + D.toggle_submanagement() return TOPIC_REFRESH - - if(href_list["view_program"]) - var/prog = href_list["view_program"] - var/list/programs = computer.program_access - if(!(prog in programs)) - error = "ERROR: Program not found." - return TOPIC_REFRESH - editing_program = prog - editing_user = null + + if(href_list["toggle_parent_account_creation"]) + D.toggle_parent_account_creation() return TOPIC_REFRESH - - if(href_list["write_id"]) - var/obj/item/stock_parts/computer/card_slot/card_slot = get_component_of_type(/obj/item/stock_parts/computer/card_slot) - if(!card_slot) - error = "HARDWARE ERROR: No GOOSE-v2 compatible device found." - return TOPIC_REFRESH - var/obj/item/card/id/network/card = card_slot.stored_card - if(!card) - error = "HARDWARE ERROR: No valid card inserted." - return TOPIC_REFRESH - // Write to the card. - var/datum/computer_file/report/crew_record/AR = get_access_record() - card.network_id = computer.network_id - card.user_id = AR.user_id - card.access_record = AR - visible_message(SPAN_NOTICE("\The [src] clicks and hums, writing new data to \a [card].")) - - if(href_list["eject_id"]) - var/obj/item/stock_parts/computer/card_slot/card_slot = get_component_of_type(/obj/item/stock_parts/computer/card_slot) - if(!card_slot) - error = "HARDWARE ERROR: No GOOSE-v2 compatible device found." - return TOPIC_REFRESH - if(!card_slot.stored_card) - error = "HARDWARE ERROR: No valid card inserted." - return TOPIC_REFRESH - card_slot.eject_id(user) - - if(href_list["add_admin"]) - network.access_controller.add_admin(editing_user) - - if(href_list["remove_admin"]) - network.access_controller.rem_admin(editing_user) + + if(href_list["view_child_groups"]) + var/parent_group = href_list["view_child_groups"] + if(parent_group && (parent_group in D.group_dict)) + current_group = parent_group + else + current_group = null + return TOPIC_REFRESH + + if(href_list["info"]) + switch(href_list["info"]) + if("submanagement") + to_chat(user, SPAN_NOTICE("If parent group submanagement is toggled on, members of the parent group will be allowed to manage account membership in child groups. This may be useful for departmental heads.")) + if("parent_account_creation") + to_chat(user, SPAN_NOTICE("If parent account creation is toggled on, members of any parent group will be allowed to create new user accounts. Otherwise, account creation requires access to an account server.")) /obj/machinery/network/acl/ui_data(mob/user, ui_key) . = ..() if(error) + .["error"] = error return - var/obj/item/stock_parts/computer/card_slot/card_slot = get_component_of_type(/obj/item/stock_parts/computer/card_slot) - if(card_slot) - .["card_inserted"] = !!card_slot.stored_card - - var/datum/extension/network_device/acl/computer = get_extension(src, /datum/extension/network_device) - if(!computer.get_network()) + var/datum/extension/network_device/acl/D = get_extension(src, /datum/extension/network_device) + if(!D.get_network()) .["connected"] = FALSE return .["connected"] = TRUE - .["file_server"] = file_source.server - .["editing_user"] = editing_user - .["editing_program"] = editing_program - - // Let's build some data. - if(editing_user) - var/datum/computer_network/network = computer.get_network() - .["user_id"] = editing_user - .["is_admin"] = (editing_user in network.access_controller.administrators) - var/datum/computer_file/report/crew_record/AR = get_access_record() - if(!istype(AR)) - // Something has gone wrong. Our AR file is missing. - error = "NETWORK ERROR: Unable to find access record for user [editing_user]." - return - var/list/grants[0] - var/list/assigned_grants = AR.get_valid_grants() - // We're editing a user, so we only need to build a subset of data. - .["desired_name"] = AR.get_name() - .["grant_count"] = length(assigned_grants) - .["size"] = AR.size - for(var/datum/computer_file/data/grant_record/GR in computer.get_all_grants()) - grants.Add(list(list( - "grant_name" = GR.stored_data, - "assigned" = (GR in assigned_grants) - ))) - .["grants"] = grants - else if(editing_program) // Editing program access. - .["program_name"] = editing_program - var/list/program_access = computer.program_access[editing_program] - var/list/grants[0] - .["cleared_control"] = !length(program_access) - for(var/datum/computer_file/data/grant_record/GR in computer.get_all_grants()) - grants.Add(list(list( - "grant_name" = GR.stored_data, - "assigned" = (GR.stored_data in program_access) - ))) - .["grants"] = grants + .["allow_submanagement"] = D.allow_submanagement + .["parent_account_creation"] = D.parent_account_creation + + // Modifying parent group, display child groups + if(current_group) + .["current_group"] = current_group + var/list/child_groups[0] + if(D.group_dict[current_group]) + for(var/child_group in D.group_dict[current_group]) + child_groups.Add(list(list( + "group_name" = child_group, + ))) + .["child_groups"] = child_groups else - // We're looking at all records. Or lack thereof. - var/list/users[0] - for(var/datum/computer_file/report/crew_record/AR in get_all_users()) - users.Add(list(list( - "desired_name" = AR.get_name(), - "user_id" = AR.user_id, - "grant_count" = length(AR.get_valid_grants()), - "size" = AR.size - ))) - .["users"] = users - var/list/programs[0] - .["program_control"] = computer.program_control - for(var/prog_name in computer.program_access) - programs.Add(list(list( - "name" = prog_name, - "grants" = length(computer.program_access[prog_name]) + // We're looking at all parent groups. + var/list/parent_groups[0] + for(var/parent_group in D.group_dict) + parent_groups.Add(list(list( + "group_name" = parent_group, ))) - .["programs"] = programs - -/obj/machinery/network/acl/proc/get_all_users() - var/list/users = list() - for(var/datum/computer_file/report/crew_record/CR in file_source.get_all_files()) - users |= CR - return users - -// Get the access record for the user we're *currently* editing. -/obj/machinery/network/acl/proc/get_access_record(var/for_specific_id) - var/for_user = for_specific_id - if(!for_user) - for_user = editing_user - for(var/datum/computer_file/report/crew_record/AR in get_all_users()) - if(AR.user_id != for_user) - continue - return AR - -/obj/machinery/network/acl/proc/create_grant(var/grant_data) - if(!file_source.create_file(grant_data, /datum/computer_file/data/grant_record)) - return FALSE - var/datum/computer_file/data/grant_record/grant = file_source.get_file(grant_data) - grant.set_value(grant_data) - grant.calculate_size() - return TRUE \ No newline at end of file + .["parent_groups"] = parent_groups \ No newline at end of file diff --git a/code/modules/modular_computers/networking/machinery/mainframe.dm b/code/modules/modular_computers/networking/machinery/mainframe.dm index e39c4268130..ef57b295d0c 100644 --- a/code/modules/modular_computers/networking/machinery/mainframe.dm +++ b/code/modules/modular_computers/networking/machinery/mainframe.dm @@ -10,10 +10,10 @@ base_type = /obj/machinery/network/mainframe var/list/initial_roles = list( MF_ROLE_FILESERVER, - MF_ROLE_EMAIL_SERVER, MF_ROLE_LOG_SERVER, MF_ROLE_CREW_RECORDS, - MF_ROLE_SOFTWARE + MF_ROLE_SOFTWARE, + MF_ROLE_ACCOUNT_SERVER ) /obj/machinery/network/mainframe/Initialize() @@ -57,8 +57,8 @@ /obj/machinery/network/mainframe/files initial_roles = list(MF_ROLE_FILESERVER) -/obj/machinery/network/mainframe/email - initial_roles = list(MF_ROLE_EMAIL_SERVER) +/obj/machinery/network/mainframe/account + initial_roles = list(MF_ROLE_ACCOUNT_SERVER) /obj/machinery/network/mainframe/logs initial_roles = list(MF_ROLE_LOG_SERVER) diff --git a/code/modules/modular_computers/networking/machinery/relay.dm b/code/modules/modular_computers/networking/machinery/relay.dm index bd4f2fb4099..5e0b44bebf8 100644 --- a/code/modules/modular_computers/networking/machinery/relay.dm +++ b/code/modules/modular_computers/networking/machinery/relay.dm @@ -16,6 +16,7 @@ if(!istype(R)) return data data["wifi"] = R.allow_wifi + data["reconnect"] = R.reconnect_time && round((R.reconnect_time - world.time)/10) return data /obj/machinery/network/relay/OnTopic(mob/user, href_list, datum/topic_state/state) @@ -24,7 +25,11 @@ if(R) R.allow_wifi = !R.allow_wifi return TOPIC_REFRESH - + if(href_list["reconnect"]) + var/datum/extension/network_device/broadcaster/relay/R = get_extension(src, /datum/extension/network_device) + if(R) + R.connect() + return TOPIC_REFRESH . = ..() /obj/machinery/network/relay/long_range diff --git a/code/modules/modular_computers/networking/network_files.dm b/code/modules/modular_computers/networking/network_files.dm index aa4df7eb8e2..d712e344d8e 100644 --- a/code/modules/modular_computers/networking/network_files.dm +++ b/code/modules/modular_computers/networking/network_files.dm @@ -1,13 +1,13 @@ -/datum/computer_network/proc/find_file_by_name(filename, mainframe_role = MF_ROLE_FILESERVER, mob/user) - for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, user)) +/datum/computer_network/proc/find_file_by_name(filename, mainframe_role = MF_ROLE_FILESERVER, list/accesses) + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, accesses)) var/datum/computer_file/F = M.get_file(filename) if(F) return F -/datum/computer_network/proc/get_all_files_of_type(file_type, mainframe_role = MF_ROLE_FILESERVER, uniques_only = FALSE, mob/user) +/datum/computer_network/proc/get_all_files_of_type(file_type, mainframe_role = MF_ROLE_FILESERVER, uniques_only = FALSE, list/accesses) . = list() var/list/found_filenames = list() - for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, user)) + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, accesses)) for(var/datum/computer_file/F in M.get_all_files()) if(istype(F, file_type)) if(uniques_only && (F.filename in found_filenames)) @@ -15,28 +15,28 @@ . |= F found_filenames |= F.filename -/datum/computer_network/proc/store_file(datum/computer_file/F, mainframe_role = MF_ROLE_FILESERVER, mob/user) - for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, user)) +/datum/computer_network/proc/store_file(datum/computer_file/F, mainframe_role = MF_ROLE_FILESERVER, list/accesses) + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, accesses)) if(M.store_file(F)) return TRUE -/datum/computer_network/proc/remove_file(datum/computer_file/F, mainframe_role = MF_ROLE_FILESERVER, mob/user) - for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, user)) +/datum/computer_network/proc/remove_file(datum/computer_file/F, mainframe_role = MF_ROLE_FILESERVER, list/accesses) + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, accesses)) if(M.delete_file(F)) return TRUE -/datum/computer_network/proc/find_file_location(filename, mainframe_role = MF_ROLE_FILESERVER, mob/user) - for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, user)) +/datum/computer_network/proc/find_file_location(filename, mainframe_role = MF_ROLE_FILESERVER, list/accesses) + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(mainframe_role, accesses)) var/datum/computer_file/F = M.get_file(filename) if(F) return M.network_tag // Reports -/datum/computer_network/proc/fetch_reports(access) +/datum/computer_network/proc/fetch_reports(access, mob/user) . = get_all_files_of_type(/datum/computer_file/report) if(access) for(var/datum/computer_file/report/report in .) - if(!report.verify_access_edit(access)) + if(!(report.get_file_perms(access, user) & OS_WRITE_ACCESS)) . -= report // Software @@ -80,14 +80,14 @@ return CR // Misc helpers -/datum/computer_network/proc/get_file_server_tags(var/role = MF_ROLE_FILESERVER, var/mob/user) +/datum/computer_network/proc/get_file_server_tags(role = MF_ROLE_FILESERVER, list/accesses) . = list() var/list/mainframes = get_mainframes_by_role(role) for(var/datum/extension/network_device/mainframe/M in mainframes) - if(user && !M.has_access(user)) + if(!M.has_access(accesses)) continue // We only check if user is provided. If no user is provided, it's assumed to be an admin check. . |= M.network_tag -/datum/computer_network/proc/get_file_server_by_role(var/role, var/user) - if(length(get_mainframes_by_role(role, user)) > 0) - return get_mainframes_by_role(role, user)[1] +/datum/computer_network/proc/get_file_server_by_role(role, list/accesses) + if(length(get_mainframes_by_role(role, accesses)) > 0) + return get_mainframes_by_role(role, accesses)[1] diff --git a/code/modules/modular_computers/os/files.dm b/code/modules/modular_computers/os/files.dm index 4dc9ac0411f..125dbc71f93 100644 --- a/code/modules/modular_computers/os/files.dm +++ b/code/modules/modular_computers/os/files.dm @@ -39,13 +39,14 @@ return FALSE return disk.try_store_file(file) -/datum/extension/interactive/os/proc/save_file(var/newname, var/data, var/file_type = /datum/computer_file/data, var/list/metadata, var/obj/item/stock_parts/computer/hard_drive/disk = get_component(PART_HDD)) +/datum/extension/interactive/os/proc/save_file(var/newname, var/data, var/file_type = /datum/computer_file/data, var/list/metadata, var/obj/item/stock_parts/computer/hard_drive/disk = get_component(PART_HDD), var/list/accesses, var/mob/user) if(!disk) return FALSE var/datum/computer_file/data/F = disk.find_file_by_name(newname) if(!F) //try to make one if it doesn't exist return !!create_file(newname, data, file_type, metadata, disk) - + if(!(F.get_file_perms(accesses, user) & OS_WRITE_ACCESS)) + return FALSE //Try to save file, possibly won't fit size-wise var/datum/computer_file/data/backup = F.clone() disk.remove_file(F) @@ -57,7 +58,7 @@ return FALSE return TRUE -/datum/extension/interactive/os/proc/delete_file(var/filename, var/obj/item/stock_parts/computer/hard_drive/disk = get_component(PART_HDD)) +/datum/extension/interactive/os/proc/delete_file(var/filename, var/obj/item/stock_parts/computer/hard_drive/disk = get_component(PART_HDD), var/list/accesses, var/mob/user) if(!disk) return FALSE @@ -65,27 +66,32 @@ if(!F || F.undeletable) return FALSE - return disk.remove_file(F) + return disk.remove_file(F, accesses, user) -/datum/extension/interactive/os/proc/clone_file(var/filename, var/obj/item/stock_parts/computer/hard_drive/disk = get_component(PART_HDD)) +/datum/extension/interactive/os/proc/clone_file(var/filename, var/obj/item/stock_parts/computer/hard_drive/disk = get_component(PART_HDD), var/list/accesses, var/mob/user) if(!disk) return FALSE var/datum/computer_file/F = disk.find_file_by_name(filename) if(!F) return FALSE + + if(!(F.get_file_perms(accesses, user) & OS_READ_ACCESS)) + return FALSE var/datum/computer_file/C = F.clone(1) return disk.store_file(C) -/datum/extension/interactive/os/proc/copy_between_disks(var/filename, var/obj/item/stock_parts/computer/hard_drive/disk_from, var/obj/item/stock_parts/computer/hard_drive/disk_to) +/datum/extension/interactive/os/proc/copy_between_disks(var/filename, var/obj/item/stock_parts/computer/hard_drive/disk_from, var/obj/item/stock_parts/computer/hard_drive/disk_to, var/list/accesses, var/mob/user) if(!istype(disk_from) || !istype(disk_to)) return FALSE var/datum/computer_file/F = disk_from.find_file_by_name(filename) if(!istype(F)) return FALSE + if(!(F.get_file_perms(accesses, user) & OS_READ_ACCESS)) + return FALSE var/datum/computer_file/C = F.clone(0) return disk_to.store_file(C) @@ -172,9 +178,9 @@ var/datum/extension/network_device/mainframe/M = get_mainframe() return M && M.store_file(stored) -/datum/file_storage/network/delete_file(filename) +/datum/file_storage/network/delete_file(filename, list/accesses, mob/user) var/datum/extension/network_device/mainframe/M = get_mainframe() - return M && M.delete_file(filename) + return M && M.delete_file(filename, accesses, user) /datum/file_storage/network/save_file(filename, new_data) var/datum/extension/network_device/mainframe/M = get_mainframe() @@ -253,15 +259,15 @@ return FALSE return os.store_file(stored, get_disk()) -/datum/file_storage/disk/save_file(filename, new_data) +/datum/file_storage/disk/save_file(filename, new_data, list/accesses, mob/user) if(check_errors()) return FALSE - return os.save_file(filename, new_data, get_disk()) + return os.save_file(filename, new_data, get_disk(), accesses, user) -/datum/file_storage/disk/delete_file(filename) +/datum/file_storage/disk/delete_file(filename, list/accesses, mob/user) if(check_errors()) return FALSE - return os.delete_file(filename, get_disk()) + return os.delete_file(filename, get_disk(), accesses, user) /datum/file_storage/disk/get_transfer_speed() if(check_errors()) diff --git a/code/modules/modular_computers/os/os.dm b/code/modules/modular_computers/os/os.dm index f0460a05d72..c18612b287c 100644 --- a/code/modules/modular_computers/os/os.dm +++ b/code/modules/modular_computers/os/os.dm @@ -2,10 +2,15 @@ base_type = /datum/extension/interactive/os expected_type = /atom/movable flags = EXTENSION_FLAG_IMMEDIATE + var/on = FALSE var/datum/computer_file/program/active_program = null // A currently active program running on the computer. var/list/running_programs = list() // All programms currently running, background or active. + var/login // The current network account login + var/password // The current network account password. + var/weakref/current_account // Reference to the current account to improve lookup speed. + var/screen_icon_file // dmi where the screen overlays are kept, defaults to holder's icon if unset var/menu_icon = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen. var/screensaver_icon = "standby" @@ -51,6 +56,92 @@ if(D) return D.get_network() +/datum/extension/interactive/os/proc/get_access(var/mob/user) + . = list() + var/datum/computer_file/data/account/access_account = get_account() + if(access_account) + var/datum/computer_network/network = get_network() + if(network) + var/location = "[network.network_id]" + . += "[access_account.login]@[location]" // User access uses '@' + for(var/group in access_account.groups) + . += "[group].[location]" // Group access uses '.' + for(var/group in access_account.parent_groups) // Membership in a child group grants access to anything with an access requirement set to the parent group. + . += "[group].[location]" + if(user) + var/obj/item/card/id/I = user.GetIdCard() + if(I) + . += I.GetAccess(access_account?.login) // Ignore any access that's already on the user account. + +// Returns the current account, if possible. User var is passed only for updating program access from ID, if no account is found. +/datum/extension/interactive/os/proc/get_account(var/mob/user) + if(!current_account) + return null + var/datum/computer_file/data/account/check_account = current_account?.resolve() + if(!istype(check_account)) + logout_account(user) + return null + if(check_account.login != login || check_account.password != password) // The most likely case - login or password were changed. + logout_account(user) + return null + // Check if the account can be located on the network. + var/datum/computer_network/network = get_network() + if(!network || !(check_account in network.get_accounts_unsorted())) + logout_account(user) + return null + return check_account + +// Returns the current account without bothering to check if it can still be found. +/datum/extension/interactive/os/proc/get_account_nocheck() + return current_account?.resolve() + +/datum/extension/interactive/os/proc/login_account(var/new_login, var/new_password, var/mob/user) + var/datum/computer_network/network = get_network() + var/datum/computer_file/data/account/prev_acount = get_account(user) + if(prev_acount) + if(prev_acount.login == new_login && (prev_acount.password == new_password)) + return TRUE + else // Log out of the current account first if we're trying to log into something else. + logout_account(user) + if(network) + for(var/datum/computer_file/data/account/check_account in network.get_accounts()) + if(check_account.login == new_login && check_account.password == new_password) + login = new_login + password = new_password + current_account = weakref(check_account) + check_account.logged_in_os += weakref(src) + + for(var/datum/computer_file/program/P in running_programs) + P.update_access(user) + return TRUE + +/datum/extension/interactive/os/proc/logout_account(var/mob/user) + var/datum/computer_file/data/account/check_account = current_account?.resolve() + if(check_account) + check_account.logged_in_os -= weakref(src) + current_account = null + login = null + password = null + for(var/datum/computer_file/program/P in running_programs) + P.update_access(user) + +/datum/extension/interactive/os/proc/login_prompt(var/mob/user) + var/obj/item/card/id/I = user.GetIdCard() + var/default_login = I?.associated_network_account["login"] + var/default_password = I?.associated_network_account["password"] + + var/new_login = sanitize(input(user, "Enter your account login:", "Account login", default_login) as text|null) + if(!new_login || !CanUseTopic(user, global.default_topic_state)) + return + var/new_password = sanitize(input(user, "Enter your account password:", "Account password", default_password) as text|null) + if(!new_password || !CanUseTopic(user, global.default_topic_state)) + return + + if(login_account(new_login, new_password, user)) + to_chat(user, SPAN_NOTICE("Account login successful: Welcome [new_login]!")) + else + to_chat(user, SPAN_NOTICE("Account login failed: Check login or password.")) + /datum/extension/interactive/os/proc/system_shutdown() on = FALSE for(var/datum/computer_file/program/P in running_programs) @@ -60,6 +151,7 @@ if(network_card) var/datum/extension/network_device/D = get_extension(network_card, /datum/extension/network_device) D?.disconnect() + logout_account() if(updating) updating = FALSE @@ -113,7 +205,7 @@ if(P in running_programs) P.program_state = PROGRAM_STATE_ACTIVE active_program = P - else if(P.can_run(user, 1, null, get_network())) + else if(P.can_run(get_access(user), user, TRUE)) P.on_startup(user, src) active_program = P else @@ -199,4 +291,9 @@ /datum/extension/interactive/os/proc/get_processing_power() var/obj/item/stock_parts/computer/processor_unit/CPU = get_component(PART_CPU) - return CPU?.processing_power \ No newline at end of file + return CPU?.processing_power + +/datum/extension/interactive/os/proc/mail_received(datum/computer_file/data/email_message/received) + var/datum/computer_file/program/email_client/e_client = locate() in running_programs + if(e_client) + e_client.mail_received(received) \ No newline at end of file diff --git a/code/modules/modular_computers/os/subtypes/device.dm b/code/modules/modular_computers/os/subtypes/device.dm index dc8f05fbc87..a786e8b4927 100644 --- a/code/modules/modular_computers/os/subtypes/device.dm +++ b/code/modules/modular_computers/os/subtypes/device.dm @@ -32,12 +32,6 @@ if(assembly && assembly.enabled) assembly.shutdown_device() -/datum/extension/interactive/os/device/system_boot() - ..() - if(holder) - var/datum/extension/assembly/modular_computer/assembly = get_extension(holder, /datum/extension/assembly) - return assembly && assembly.turn_on() - /datum/extension/interactive/os/device/extension_act(href, href_list, user) . = ..() var/obj/item/modular_computer/C = holder diff --git a/code/modules/modular_computers/os/ui.dm b/code/modules/modular_computers/os/ui.dm index 2b92e1cccea..cd4e01ca1b1 100644 --- a/code/modules/modular_computers/os/ui.dm +++ b/code/modules/modular_computers/os/ui.dm @@ -118,6 +118,14 @@ if( href_list["PC_terminal"] ) open_terminal(usr) return TOPIC_HANDLED + + if( href_list["PC_login"]) + login_prompt(usr) + return TOPIC_REFRESH + + if( href_list["PC_logout"]) + logout_account(usr) + return TOPIC_REFRESH /datum/extension/interactive/os/proc/regular_ui_update() var/ui_update_needed = 0 @@ -215,6 +223,9 @@ data["PC_stationtime"] = stationtime2text() data["PC_hasheader"] = !updating data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen + + var/datum/computer_file/data/account/cur_account = get_account_nocheck() + data["PC_loggedin"] = cur_account?.login return data /datum/extension/interactive/os/initial_data() diff --git a/code/modules/modular_computers/terminal/terminal.dm b/code/modules/modular_computers/terminal/terminal.dm index 0b8b945a03c..0066c0de5e6 100644 --- a/code/modules/modular_computers/terminal/terminal.dm +++ b/code/modules/modular_computers/terminal/terminal.dm @@ -89,7 +89,18 @@ /datum/terminal/proc/update_content() var/list/content = history.Copy() - content += "> " + var/account_name + // current_account will be reset on access check if account look up fails. + var/datum/extension/interactive/os/account_computer = get_account_computer() + if(account_computer.login && account_computer.current_account) + var/datum/computer_network/network = account_computer.get_network() + if(network) + account_name = "[account_computer.login]@[network.network_id]" + else + account_name = "LOCAL" + else + account_name = "GUEST" + content += "> [account_name] " content += "type `man` for a list of available commands." panel.set_content("[jointext(content, "
    ")]
    ") @@ -136,3 +147,11 @@ candidates[scf] = scf.weight var/datum/terminal_skill_fail/chosen = pickweight(candidates) return chosen.execute(src) + +// Returns the computer used for the terminal's account +/datum/terminal/proc/get_account_computer() + return computer + +/datum/terminal/proc/get_access(mob/user) + var/datum/extension/interactive/os/account_computer = get_account_computer() + return(account_computer.get_access(user)) \ No newline at end of file diff --git a/code/modules/modular_computers/terminal/terminal_commands.dm b/code/modules/modular_computers/terminal/terminal_commands.dm index 1ed922f8288..609deacc48f 100644 --- a/code/modules/modular_computers/terminal/terminal_commands.dm +++ b/code/modules/modular_computers/terminal/terminal_commands.dm @@ -22,8 +22,8 @@ var/global/list/terminal_commands regex = new (pattern, regex_flags) ..() -/datum/terminal_command/proc/check_access(mob/user) - return has_access(req_access, user.GetAccess()) +/datum/terminal_command/proc/check_access(list/access) + return has_access(req_access, access) /datum/terminal_command/proc/get_nid(text) if(!nid_regex) @@ -41,7 +41,7 @@ var/global/list/terminal_commands return if(!user.skill_check(core_skill, skill_needed)) return skill_fail_message() - if(!check_access(user)) + if(!check_access(terminal.get_access(user))) return "[name]: ACCESS DENIED" if(needs_network && !terminal.computer.get_network_status()) return "NETWORK ERROR: Check connection and try again." @@ -287,7 +287,7 @@ Subtypes var/datum/extension/network_device/mainframe/M = network.get_device_by_tag(network_tag) if(!istype(M)) return "cd: Could not locate file server with tag [network_tag]." - if(!M.has_access(user)) + if(!M.has_access(terminal.get_access(user))) return "cd: Access denied to file server with tag [network_tag]" terminal.current_disk = terminal.disks[/datum/file_storage/network] if(!terminal.current_disk) @@ -336,15 +336,15 @@ Subtypes var/file_name = copytext(text, 4) - var/deleted = terminal.current_disk.delete_file(file_name) + var/deleted = terminal.current_disk.delete_file(file_name, terminal.get_access(user), user) if(deleted) return "rm: Removed file [file_name]." else - return "rm: Failed to remove file [file_name]." + return "rm: Failed to remove file [file_name]. Additional access may be required." /datum/terminal_command/move name = "mv" - man_entry = list("Format: mv \[file name\] \[destination\]", "Moves a file in the current disk to another.") + man_entry = list("Format: mv \[file name\] \[destination\] \[copying (0/1) \]", "Moves a file in the current disk to another.") pattern = @"^mv" /datum/terminal_command/move/proper_input_entered(text, mob/user, datum/terminal/terminal) @@ -353,11 +353,17 @@ Subtypes var/list/mv_args = get_arguments(text) if(length(mv_args) < 2) - return "mv: Improper syntax, use mv \[file name\] \[destination\]." + return "mv: Improper syntax, use mv \[file name\] \[destination\] \[copying (0/1) \]." var/datum/computer_file/F = terminal.current_disk.get_file(mv_args[1]) if(!F) return "mv: Could not find file with name [mv_args[1]]." - + var/copying = length(mv_args > 2) ? text2num(mv_args[3]) : FALSE + if(copying == TRUE) + if(!(F.get_file_perms(terminal.get_access(user), user) & OS_READ_ACCESS)) + return "mv: You do not have read access to this file." + else + if(!(F.get_file_perms(terminal.get_access(user), user) & OS_WRITE_ACCESS)) + return "mv: You do not have write access to this file. Write access is required when not copying files with mv" // Find the destination. var/datum/file_storage/dest var/target = uppertext(mv_args[2]) @@ -383,7 +389,7 @@ Subtypes var/datum/extension/network_device/mainframe/M = network.get_device_by_tag(target) if(!istype(M)) return "mv: Could not locate destination with tag [target]." - if(!M.has_access(user)) + if(!M.has_access(terminal.get_access(user))) return "mv: Access denied to destination with tag [target]" dest = terminal.disks[/datum/file_storage/network] if(!dest) @@ -398,7 +404,7 @@ Subtypes if(!dest) return "mv: Could not locate file destination." - terminal.current_move = new(terminal.current_disk, dest, F) + terminal.current_move = new(terminal.current_disk, dest, F, copying) return "mv: Beginning file move..." /datum/terminal_command/copy @@ -462,6 +468,106 @@ Subtypes terminal.network_target = uppertext(target_args[1]) return "target: Changed network target to [terminal.network_target]." +/datum/terminal_command/login + name = "login" + man_entry = list("Format: login \[account login\] \[account password\]", "Logs in to the given user account.") + pattern = @"^login" + needs_network = TRUE + +/datum/terminal_command/login/proper_input_entered(text, mob/user, datum/terminal/terminal) + + var/list/login_args = get_arguments(text) + + if(length(login_args) < 2) + return "login: Improper syntax, use login \[account login\] \[account password\]." + + var/datum/extension/interactive/os/account_computer = terminal.get_account_computer() + var/login_success = account_computer.login_account(login_args[1], login_args[2]) + if(login_success) + return "login: Login successful. Welcome [login_args[1]]!" + return "login: Could not log in to account [login_args[1]]. Check password or network connectivity." + +/datum/terminal_command/logout + name = "logout" + man_entry = list("Format: logout", "Logs out of the current user account.") + pattern = @"^logout" + needs_network = TRUE + +/datum/terminal_command/logout/proper_input_entered(text, mob/user, datum/terminal/terminal) + var/datum/extension/interactive/os/account_computer = terminal.get_account_computer() + account_computer.logout_account() + return "logout: Log out successful." + +/datum/terminal_command/permmod + name = "permmod" + man_entry = list("Format: permmod \[file name\] \[access key\] \[permission mod flags\]", "Modifies or lists the permissions of the given file. Do not pass an access key to list permissions.", + "Supported flags are as follows:", + "'+/-' - Add or Remove access requirement", + "'r/w/m' - Target read/write/modification access", + "'u/g' - Add group or individual user access requirement", + "For example, the command 'permmod TestFile bronte.lowe +ru' would add bronte.lowe to the read permissions list of the file TestFile." + ) // TODO: Add an actual flags option for terminal commands in addition to arguments + pattern = @"^permmod" + needs_network = TRUE + +/datum/terminal_command/permmod/proper_input_entered(text, mob/user, datum/terminal/terminal) + + if(!terminal.current_disk) + return "permmod: No disk selected." + + var/list/permmod_args = get_arguments(text) + if(!length(permmod_args)) + return "permmod: Improper syntax, use permmod \[file name\] \[access key\] \[permission mod flags\]." + + var/datum/computer_file/F = terminal.current_disk.get_file(permmod_args[1]) + if(!F) + return "permmod: Could not find file with name [permmod_args[1]]." + + if(length(permmod_args) < 3) + return F.get_perms_readable() + + var/flags = permmod_args[3] + var/mode + var/access_key + var/perm + if(findtext(flags, "-")) + mode = "-" + else if(findtext(flags, "+")) + mode = "+" + if(!mode) + return "permmod: Invalid flag syntax. Use man command to learn more about permmod flag syntax." + + if(findtext(flags, "r")) + perm = OS_READ_ACCESS + else if(findtext(flags, "w")) + perm = OS_WRITE_ACCESS + else if(findtext(flags, "m")) + perm = OS_MOD_ACCESS + else + return "permmod: Invalid flag syntax. Use man command to learn more about permmod flag syntax." + var/datum/computer_network/network = terminal.computer.get_network() + if(findtext(flags, "u")) + var/account_login = permmod_args[2] + if(!network.find_account_by_login(account_login)) + return "permmod: No user account with login '[account_login]' found." + access_key = "[account_login]@[network.network_id]" + else if(findtext(flags, "g")) + var/group_name = permmod_args[2] + var/datum/extension/network_device/acl/acl = network.access_controller + if(!istype(acl)) + return "permmod: No active ACL could be located on the network." + if(!(group_name in acl.get_all_groups())) + return "permmod: No group with name '[group_name]' found." + access_key = "[group_name].[network.network_id]" + else + return "permmod: Invalid flag syntax. Use man command to learn more about permmod flag syntax." + + var/success = F.change_perms(mode, perm, access_key, terminal.get_access(user)) + if(success) + return "permmod: Successfully changed permissions for [F.filename]." + else + return "permmod: Could not change permissions for [F.filename]. You may lack permission modification access." + // Terminal commands for passing information to public variables and methods follows /datum/terminal_command/com name = "com" @@ -510,7 +616,7 @@ Subtypes else if(length(com_args) == 2) called_args = com_args[2] - return D.on_command(com_args[1], called_args, user) + return D.on_command(com_args[1], called_args, terminal.get_access(user)) // Lists the commands available on the target device. /datum/terminal_command/listcom diff --git a/code/modules/modular_computers/terminal/terminal_remote.dm b/code/modules/modular_computers/terminal/terminal_remote.dm index 9cf9740ea53..e74f04f7191 100644 --- a/code/modules/modular_computers/terminal/terminal_remote.dm +++ b/code/modules/modular_computers/terminal/terminal_remote.dm @@ -25,4 +25,7 @@ if(!origin_computer.get_network_status() || !computer.get_network_status()) return FALSE - return TRUE \ No newline at end of file + return TRUE + +/datum/terminal/remote/get_account_computer() + return origin_computer \ No newline at end of file diff --git a/code/modules/modular_computers/terminal/terminal_skill_fail.dm b/code/modules/modular_computers/terminal/terminal_skill_fail.dm index dd596e75cd4..0f1ffccc8bf 100644 --- a/code/modules/modular_computers/terminal/terminal_skill_fail.dm +++ b/code/modules/modular_computers/terminal/terminal_skill_fail.dm @@ -41,8 +41,10 @@ var/global/list/terminal_fails var/fulldata = "" for(var/datum/computer_file/data/L in logs) fulldata += L.generate_file_data() - var/datum/computer_file/data/email_account/server = network.find_email_by_name(EMAIL_DOCUMENTS) - for(var/datum/computer_file/data/email_account/email in network.get_email_addresses()) + var/datum/computer_file/data/account/server = network.find_account_by_login(EMAIL_DOCUMENTS) + if(!server) + return + for(var/datum/computer_file/data/account/email in network.get_accounts_unsorted()) if(!email.can_login || email.suspended) continue var/datum/computer_file/data/email_message/message = new() diff --git a/code/modules/multiz/structures.dm b/code/modules/multiz/ladder.dm similarity index 75% rename from code/modules/multiz/structures.dm rename to code/modules/multiz/ladder.dm index 73b2d03185e..8422de3bc85 100644 --- a/code/modules/multiz/structures.dm +++ b/code/modules/multiz/ladder.dm @@ -1,7 +1,3 @@ -////////////////////////////// -//Contents: Ladders, Stairs.// -////////////////////////////// - /obj/structure/ladder name = "ladder" desc = "A ladder. You can climb it up and down." @@ -111,7 +107,7 @@ return ..() /obj/structure/ladder/attackby(obj/item/I, mob/user) - . = ..() + . = !istype(I, /obj/item/grab) && ..() if(!.) climb(user, I) @@ -167,16 +163,11 @@ add_fingerprint(M) - for(var/obj/item/grab/G in M.get_active_grabs()) - G.adjust_position() - var/direction = target_ladder == target_up ? "up" : "down" M.visible_message(SPAN_NOTICE("\The [M] begins climbing [direction] \the [src].")) target_ladder.audible_message(SPAN_NOTICE("You hear something coming [direction] \the [src].")) if(do_after(M, climb_time, src)) climbLadder(M, target_ladder, I) - for (var/obj/item/grab/G in M) - G.adjust_position(force = 1) /obj/structure/ladder/attack_ghost(var/mob/M) instant_climb(M) @@ -225,13 +216,15 @@ if(incapacitated()) to_chat(src, SPAN_WARNING("You are physically unable to climb \the [ladder].")) return FALSE - var/carry_count = 0 + + var/can_carry = can_pull_size + if(loc?.has_gravity()) + can_carry = FLOOR(can_carry * 0.75) for(var/obj/item/grab/G in get_active_grabs()) - to_chat(src, SPAN_WARNING("You can't carry \the [G.affecting] up \the [ladder].")) - return FALSE - if(carry_count > 1) - to_chat(src, SPAN_WARNING("You can't carry more than one person up \the [ladder].")) - return FALSE + can_carry -= G.affecting.get_object_size() + if(can_carry < 0) + to_chat(src, SPAN_WARNING("You can't carry \the [G.affecting] up \the [ladder].")) + return FALSE return TRUE @@ -246,14 +239,14 @@ //We cannot use the ladder, but we probably can remove the obstruction var/atom/movable/M = A if(istype(M) && M.movable_flags & MOVABLE_FLAG_Z_INTERACT) - if(isnull(I)) + if(isnull(I) || istype(I, /obj/item/grab)) M.attack_hand(user) else M.attackby(I, user) return FALSE playsound(src, pick(climbsounds), 50) playsound(target_ladder, pick(climbsounds), 50) - return user.Move(T) + return user.Move(T, (loc.z > T.z) ? DOWN : UP) /obj/structure/ladder/CanPass(obj/mover, turf/source, height, airflow) return airflow || !density @@ -270,79 +263,3 @@ underlays = list(I) else underlays.Cut() - -/obj/structure/stairs - name = "stairs" - desc = "Stairs leading to another deck. Not too useful if the gravity goes out." - icon = 'icons/obj/stairs.dmi' - density = 0 - opacity = 0 - anchored = 1 - layer = RUNE_LAYER - -/obj/structure/stairs/Initialize() - for(var/turf/turf in locs) - var/turf/above = GetAbove(turf) - if(!istype(above)) - warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])") - return INITIALIZE_HINT_QDEL - if(!above.is_open()) - above.ChangeTurf(/turf/simulated/open) - . = ..() - -/obj/structure/stairs/CheckExit(atom/movable/mover, turf/target) - if(get_dir(loc, target) == dir && upperStep(mover.loc)) - return FALSE - return ..() - -/obj/structure/stairs/Bumped(atom/movable/A) - var/turf/target = get_step(GetAbove(A), dir) - var/turf/source = A.loc - var/turf/above = GetAbove(A) - if(above.CanZPass(source, UP) && target.Enter(A, src)) - A.forceMove(target) - if(isliving(A)) - var/mob/living/L = A - for(var/obj/item/grab/G in L.get_active_grabs()) - G.affecting.forceMove(target) - if(ishuman(A)) - var/mob/living/carbon/human/H = A - if(H.has_footsteps()) - playsound(source, 'sound/effects/stairs_step.ogg', 50) - playsound(target, 'sound/effects/stairs_step.ogg', 50) - else - to_chat(A, SPAN_WARNING("Something blocks the path.")) - -/obj/structure/stairs/proc/upperStep(var/turf/T) - return (T == loc) - -/obj/structure/stairs/CanPass(obj/mover, turf/source, height, airflow) - return airflow || !density - -// type paths to make mapping easier. -/obj/structure/stairs/north - dir = NORTH - bound_height = 64 - bound_y = -32 - pixel_y = -32 - -/obj/structure/stairs/south - dir = SOUTH - bound_height = 64 - -/obj/structure/stairs/east - dir = EAST - bound_width = 64 - bound_x = -32 - pixel_x = -32 - -/obj/structure/stairs/west - dir = WEST - bound_width = 64 - -/obj/structure/stairs/short - bound_height = 32 - bound_width = 32 - -/obj/structure/stairs/short/west - dir = WEST \ No newline at end of file diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 1360d30d76a..c0bed448fb5 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -188,7 +188,7 @@ /atom/movable/proc/handle_fall(var/turf/landing) var/turf/previous = get_turf(loc) - forceMove(landing) + Move(landing, get_dir(previous, landing)) if(locate(/obj/structure/stairs) in landing) return 1 if(landing.get_fluid_depth() >= FLUID_DEEP) @@ -228,6 +228,8 @@ return BASE_STORAGE_COST(w_class) /mob/living/carbon/human/apply_fall_damage(var/turf/landing) + if(status_flags & GODMODE) + return if(species && species.handle_fall_special(src, landing)) return var/min_damage = 7 @@ -246,7 +248,7 @@ var/list/victims = list() for(var/tag in list(BP_L_FOOT, BP_R_FOOT, BP_L_ARM, BP_R_ARM)) var/obj/item/organ/external/E = get_organ(tag) - if(E && !E.is_stump() && !E.dislocated && !BP_IS_PROSTHETIC(E)) + if(E && !E.is_stump() && !E.is_dislocated() && (E.limb_flags & ORGAN_FLAG_CAN_DISLOCATE) && !BP_IS_PROSTHETIC(E)) victims += E if(victims.len) var/obj/item/organ/external/victim = pick(victims) @@ -323,7 +325,7 @@ /mob/living/simple_animal/aquatic/can_float() return TRUE - + /mob/living/simple_animal/hostile/aquatic/can_float() return TRUE @@ -392,4 +394,4 @@ return /atom/movable/z_observer/singuloCanEat() - return \ No newline at end of file + return diff --git a/code/modules/multiz/stairs.dm b/code/modules/multiz/stairs.dm new file mode 100644 index 00000000000..f37d3e8c4ce --- /dev/null +++ b/code/modules/multiz/stairs.dm @@ -0,0 +1,67 @@ +/obj/structure/stairs + name = "stairs" + desc = "Stairs leading to another deck. Not too useful if the gravity goes out." + icon = 'icons/obj/stairs.dmi' + density = FALSE + opacity = FALSE + anchored = TRUE + layer = RUNE_LAYER + +/obj/structure/stairs/Initialize() + for(var/turf/turf in locs) + var/turf/above = GetAbove(turf) + if(!istype(above)) + warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])") + return INITIALIZE_HINT_QDEL + if(!above.is_open()) + above.ChangeTurf(/turf/simulated/open) + . = ..() + +/obj/structure/stairs/CheckExit(atom/movable/mover, turf/target) + if((get_dir(loc, target) == dir) && (get_turf(mover) == loc)) + return FALSE + return ..() + +/obj/structure/stairs/Bumped(atom/movable/A) + var/turf/target = get_step(GetAbove(A), dir) + var/turf/source = get_turf(A) + var/turf/above = GetAbove(A) + if(above.CanZPass(source, UP) && target.Enter(A, src)) + A.forceMove(target) + if(isliving(A)) + var/mob/living/L = A + for(var/obj/item/grab/G in L.get_active_grabs()) + G.affecting.forceMove(target) + if(ishuman(A)) + var/mob/living/carbon/human/H = A + if(H.has_footsteps()) + playsound(source, 'sound/effects/stairs_step.ogg', 50) + playsound(target, 'sound/effects/stairs_step.ogg', 50) + else + to_chat(A, SPAN_WARNING("Something blocks the path.")) + +/obj/structure/stairs/CanPass(obj/mover, turf/source, height, airflow) + return airflow || !density + +// type paths to make mapping easier. + +/obj/structure/stairs/long + icon = 'icons/obj/stairs_64.dmi' + bound_height = 64 + +/obj/structure/stairs/long/north + dir = NORTH + bound_y = -32 + pixel_y = -32 + +/obj/structure/stairs/long/east + dir = EAST + bound_width = 64 + bound_height = 32 + bound_x = -32 + pixel_x = -32 + +/obj/structure/stairs/long/west + dir = WEST + bound_width = 64 + bound_height = 32 diff --git a/code/modules/multiz/turf.dm b/code/modules/multiz/turf.dm index 725f3a5037e..432248dead2 100644 --- a/code/modules/multiz/turf.dm +++ b/code/modules/multiz/turf.dm @@ -16,6 +16,9 @@ return 0 return 1 +//////////////////////////////// +// Open SIMULATED +//////////////////////////////// /turf/simulated/open name = "open space" icon = 'icons/turf/space.dmi' @@ -24,6 +27,10 @@ pathweight = 100000 //Seriously, don't try and path over this one numbnuts z_flags = ZM_MIMIC_DEFAULTS | ZM_MIMIC_OVERWRITE | ZM_MIMIC_NO_AO | ZM_ALLOW_ATMOS +/turf/simulated/open/flooded + name = "open water" + flooded = TRUE + /turf/simulated/open/CanZPass(atom/A, direction) if(locate(/obj/structure/catwalk, src)) if(z == A.z) @@ -105,9 +112,7 @@ return TRUE //To lay cable. - if(isCoil(C)) - var/obj/item/stack/cable_coil/coil = C - coil.turf_place(src, user) + if(isCoil(C) && try_build_cable(C, user)) return TRUE for(var/atom/movable/M in below) @@ -121,13 +126,14 @@ //Most things use is_plating to test if there is a cover tile on top (like regular floors) /turf/simulated/open/is_plating() - return 1 + return TRUE -/turf/simulated/open/flooded - name = "open water" - flooded = TRUE +/turf/simulated/open/cannot_build_cable() + return 0 -// Whole lot of copypaste below sorry. +//////////////////////////////// +// Open EXTERIOR +//////////////////////////////// /turf/exterior/open name = "open space" icon = 'icons/turf/space.dmi' @@ -205,16 +211,17 @@ return TRUE //To lay cable. - if(isCoil(C)) - var/obj/item/stack/cable_coil/coil = C - coil.turf_place(src, user) + if(isCoil(C) && try_build_cable(C, user)) return TRUE for(var/atom/movable/M in below) if(M.movable_flags & MOVABLE_FLAG_Z_INTERACT) return M.attackby(C, user) -/turf/simulated/open/attack_hand(mob/user) +/turf/exterior/open/attack_hand(mob/user) for(var/atom/movable/M in below) if(M.movable_flags & MOVABLE_FLAG_Z_INTERACT) return M.attack_hand(user) + +/turf/exterior/open/cannot_build_cable() + return 0 diff --git a/code/modules/nano/interaction/default.dm b/code/modules/nano/interaction/default.dm index 3c00638f3b6..307134c5b5e 100644 --- a/code/modules/nano/interaction/default.dm +++ b/code/modules/nano/interaction/default.dm @@ -25,7 +25,7 @@ var/global/datum/topic_state/default/default_topic_state = new return // robots can interact with things they can see within their view range - if((src_object in view(src)) && get_dist(src_object, src) <= get_effective_view(client)) + if((src_object in view(client?.view || world.view, src)) && get_dist(src_object, src) <= get_effective_view(client)) return STATUS_INTERACTIVE // interactive (green visibility) return STATUS_DISABLED // no updates, completely disabled (red visibility) @@ -42,7 +42,7 @@ var/global/datum/topic_state/default/default_topic_state = new return STATUS_CLOSE // If an object is in view then we can interact with it - if(src_object in view(get_effective_view(client), src)) + if(src_object in view(client.view, src)) return STATUS_INTERACTIVE // If we're installed in a chassi, rather than transfered to an inteliCard or other container, then check if we have camera view diff --git a/code/modules/nano/interaction/view.dm b/code/modules/nano/interaction/view.dm index 72c1ba2a7e9..328d83f20fa 100644 --- a/code/modules/nano/interaction/view.dm +++ b/code/modules/nano/interaction/view.dm @@ -9,7 +9,7 @@ var/global/datum/topic_state/view/view_topic_state = new /mob/proc/view_can_use_topic(src_object) if(!client) return STATUS_CLOSE - if(src_object in view(get_effective_view(client), src)) + if(src_object in view(client.view, src)) return shared_nano_interaction(src_object) return STATUS_CLOSE diff --git a/code/modules/nano/modules/nano_module.dm b/code/modules/nano/modules/nano_module.dm index 6fd4e7b05ea..299b7d6f85a 100644 --- a/code/modules/nano/modules/nano_module.dm +++ b/code/modules/nano/modules/nano_module.dm @@ -26,11 +26,11 @@ //returns a list. /datum/nano_module/proc/get_access(mob/user) - . = using_access - if(istype(user)) + . = using_access.Copy() + if(user) // Insist on scanning ID again to make things a little less clunky. var/obj/item/card/id/I = user.GetIdCard() if(I) - . |= I.access + . |= I.GetAccess() /datum/nano_module/proc/check_access(var/mob/user, var/access) if(!access) diff --git a/code/modules/organs/ailments/ailments_medical.dm b/code/modules/organs/ailments/ailments_medical.dm index 694165ff25f..16571b88324 100644 --- a/code/modules/organs/ailments/ailments_medical.dm +++ b/code/modules/organs/ailments/ailments_medical.dm @@ -111,7 +111,7 @@ /datum/ailment/sore_joint/can_apply_to(obj/item/organ/_organ) var/obj/item/organ/external/E = _organ - . = ..() && !isnull(E.joint) && E.dislocated > -1 + . = ..() && !isnull(E.joint) && (E.limb_flags & ORGAN_FLAG_CAN_DISLOCATE) /datum/ailment/sore_back name = "sore back" diff --git a/code/modules/organs/external/_external.dm b/code/modules/organs/external/_external.dm index acb4f95c323..b4c25bf7001 100644 --- a/code/modules/organs/external/_external.dm +++ b/code/modules/organs/external/_external.dm @@ -26,7 +26,7 @@ var/pain_disability_threshold // Point at which a limb becomes unusable due to pain. // A bitfield for a collection of limb behavior flags. - var/limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_BREAK + var/limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE // Appearance vars. var/icon_name = null // Icon state base. @@ -58,7 +58,6 @@ // Joint/state stuff. var/joint = "joint" // Descriptive string used in dislocation. var/amputation_point // Descriptive string used in amputation. - var/dislocated = 0 // If you target a joint, you can dislocate the limb, causing temporary damage to the organ. var/encased // Needs to be opened with a saw to access the organs. var/artery_name = "artery" // Flavour text for cartoid artery, aorta, etc. var/arterial_bleed_severity = 1 // Multiplier for bleeding in a limb. @@ -100,34 +99,26 @@ F.completeness = rand(10,90) forensics.add_data(/datum/forensics/fingerprints, F) -/obj/item/organ/external/Initialize(mapload, material_key, datum/dna/given_dna) +/obj/item/organ/external/Initialize(mapload, material_key, datum/dna/given_dna) . = ..() - if(. == INITIALIZE_HINT_QDEL) - return - if(isnull(pain_disability_threshold)) - pain_disability_threshold = (max_damage * 0.75) + if(. != INITIALIZE_HINT_QDEL && isnull(pain_disability_threshold)) + pain_disability_threshold = (max_damage * 0.75) /obj/item/organ/external/Destroy() - - if(wounds) - for(var/datum/wound/wound in wounds) - qdel(wound) - - if(parent) - LAZYREMOVE(parent.children, src) - parent = null - QDEL_NULL_LIST(children) - QDEL_NULL_LIST(internal_organs) - + //Update the hierarchy BEFORE clearing all the vars and refs + . = ..() + //Clear all leftover refs + splinted = null //Splints got deleted in parent proc + parent = null applied_pressure = null - if(splinted && splinted.loc == src) - qdel(splinted) - splinted = null - + QDEL_NULL_LIST(wounds) LAZYCLEARLIST(autopsy_data) - QDEL_NULL_LIST(implants) + LAZYCLEARLIST(children) + LAZYCLEARLIST(internal_organs) + LAZYCLEARLIST(implants) - return ..() + if(owner) + LAZYREMOVE(owner.bad_external_organs, src) /obj/item/organ/external/set_species(specie_name) . = ..() @@ -239,10 +230,11 @@ if(loc != E) return - LAZYDISTINCTADD(E.children, src) - parent = E - owner = E.owner - status &= ~ORGAN_CUT_AWAY + if(istype(E.owner)) + E.owner.add_organ(src, E) + else + do_install(null, E) + combined = TRUE else if(E.parent_organ == organ_tag) @@ -254,10 +246,11 @@ if(!user.unEquip(E, src)) return - LAZYDISTINCTADD(children, E) - E.parent = src - E.owner = owner - E.status &= ~ORGAN_CUT_AWAY + if(istype(E.owner)) + E.owner.add_organ(E, src) + else + E.do_install(null, src) + combined = TRUE else @@ -272,42 +265,77 @@ E.update_icon() return + //Remove sub-limbs + if(W.get_tool_quality(TOOL_SAW) && LAZYLEN(children) && try_saw_off_child(W, user)) + return + //Remove internal items/organs/implants + if(try_remove_internal_item(W, user)) + return + ..() + +//Handles removing internal organs/implants/items still in the detached limb. +/obj/item/organ/external/proc/try_remove_internal_item(var/obj/item/W, var/mob/user) switch(stage) if(0) if(W.sharp) - user.visible_message("[user] cuts [src] open with [W]!") + user.visible_message(SPAN_DANGER("[user] cuts [src] open with [W]!")) stage++ - return + return TRUE if(1) if(istype(W)) - user.visible_message("[user] cracks [src] open like an egg with [W]!") + user.visible_message(SPAN_DANGER("[user] cracks [src] open like an egg with [W]!")) stage++ - return + return TRUE if(2) if(W.sharp || istype(W,/obj/item/hemostat) || isWirecutter(W)) - var/list/organs = get_contents_recursive() - if(LAZYLEN(organs)) - var/obj/item/removing = pick(organs) - var/obj/item/organ/external/current_child = removing.loc - - LAZYREMOVE(current_child.implants, removing) - LAZYREMOVE(current_child.internal_organs, removing) - - status |= ORGAN_CUT_AWAY - if(istype(removing, /obj/item/organ/internal/mmi_holder)) - var/obj/item/organ/internal/mmi_holder/O = removing - removing = O.transfer_and_delete() + var/list/radial_buttons = make_item_radial_menu_choices(get_contents_recursive()) + if(LAZYLEN(radial_buttons)) + var/obj/item/removing = show_radial_menu(user, src, radial_buttons, radius = 42, require_near = TRUE, use_labels = TRUE, check_locs = list(src)) + if(removing) + if(istype(removing, /obj/item/organ)) + var/obj/item/organ/O = removing + O.do_uninstall() + removing.forceMove(get_turf(user)) + + if(user.get_empty_hand_slot()) + user.put_in_hands(removing) + user.visible_message(SPAN_DANGER("[user] extracts [removing] from [src] with [W]!")) + else + user.visible_message(SPAN_DANGER("[user] fishes around fruitlessly in [src] with [W].")) + return TRUE + return FALSE - removing.forceMove(get_turf(user)) +//Handles removing child limbs from the detached limb. +/obj/item/organ/external/proc/try_saw_off_child(var/obj/item/W, var/mob/user) - if(user.get_empty_hand_slot()) - user.put_in_hands(removing) - user.visible_message("[user] extracts [removing] from [src] with [W]!") - else - user.visible_message("[user] fishes around fruitlessly in [src] with [W].") - return - ..() + //Add icons to radial menu + var/list/radial_buttons = make_item_radial_menu_choices(get_limbs_recursive(TRUE)) + if(!LAZYLEN(radial_buttons)) + return + //Display radial menu + var/obj/item/organ/external/removing = show_radial_menu(user, src, radial_buttons, radius = 42, require_near = TRUE, use_labels = TRUE, check_locs = list(src)) + if(!istype(removing)) + return TRUE + + var/cutting_result = !W.do_tool_interaction(TOOL_SAW, user, src, 3 SECONDS, "cutting \the [removing] off") + //Check if the limb is still in the hierarchy + if(cutting_result == 1 || !(removing in get_limbs_recursive(TRUE))) + if(cutting_result != -1) + user.visible_message(SPAN_DANGER("[user] stops trying to cut \the [removing].")) + return TRUE + + //Actually remove it + removing.do_uninstall() + removing.forceMove(get_turf(user)) + compile_icon() + update_icon() + removing.compile_icon() + removing.update_icon() + if(user.get_empty_hand_slot()) + user.put_in_hands(removing) + user.visible_message(SPAN_DANGER("[user] cuts off \the [removing] from [src] with [W]!")) + return TRUE /** * Get a list of contents of this organ and all the child organs @@ -325,20 +353,27 @@ return all_items +/obj/item/organ/external/proc/get_limbs_recursive(var/no_stumps = FALSE) + var/list/all_limbs = list() + for(var/obj/item/organ/external/child in children) + if(no_stumps && child.is_stump()) + continue + all_limbs += child + var/list/sublimbs = child.get_limbs_recursive(no_stumps) + if(sublimbs) + all_limbs += sublimbs + return all_limbs + /obj/item/organ/external/proc/is_dislocated() - if(dislocated > 0) - return 1 - if(is_parent_dislocated()) - return 1//if any parent is dislocated, we are considered dislocated as well - return 0 + return (status & ORGAN_DISLOCATED) || is_parent_dislocated() //if any parent is dislocated, we are considered dislocated as well /obj/item/organ/external/proc/is_parent_dislocated() var/obj/item/organ/external/O = parent - while(O && O.dislocated != -1) - if(O.dislocated == 1) - return 1 + while(O && (O.limb_flags & ORGAN_FLAG_CAN_DISLOCATE)) + if(O.status & ORGAN_DISLOCATED) + return TRUE O = O.parent - return 0 + return FALSE /obj/item/organ/external/proc/update_internal_organs_cost() internal_organs_size = 0 @@ -346,24 +381,31 @@ internal_organs_size += org.get_storage_cost() /obj/item/organ/external/proc/dislocate() - if(dislocated == -1) + if(owner && (owner.status_flags & GODMODE)) + return + if(!(limb_flags & ORGAN_FLAG_CAN_DISLOCATE)) return - dislocated = 1 + status |= ORGAN_DISLOCATED if(owner) + if(can_feel_pain()) + add_pain(20) + owner.apply_effect(5, STUN) owner.verbs |= /mob/living/carbon/human/proc/undislocate -/obj/item/organ/external/proc/undislocate() - if(dislocated == -1) +/obj/item/organ/external/proc/undislocate(var/skip_pain = FALSE) + if(!(limb_flags & ORGAN_FLAG_CAN_DISLOCATE)) return - dislocated = 0 + status &= (~ORGAN_DISLOCATED) if(owner) - owner.shock_stage += 20 + if(!skip_pain && can_feel_pain()) + add_pain(20) + owner.apply_effect(2, STUN) //check to see if we still need the verb for(var/obj/item/organ/external/limb in owner.get_external_organs()) - if(limb.dislocated == 1) + if(limb.is_dislocated()) return owner.verbs -= /mob/living/carbon/human/proc/undislocate @@ -372,37 +414,39 @@ return //If "in_place" is TRUE will make organs skip their install/uninstall effects and the sub-limbs and internal organs -/obj/item/organ/external/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place, update_icon) +/obj/item/organ/external/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place, update_icon, detached) if(!(. = ..())) return //If attached to an owner mob if(istype(owner)) //If we expect a parent organ set it up here - if(!parent && parent_organ) - parent = affected? affected : owner.get_organ(parent_organ) + if(!affected && parent_organ) + parent = owner.get_organ(parent_organ) + else + parent = affected // //If we contain any child organs add them to the owner // for(var/obj/item/organ/organ in internal_organs) - owner.add_organ(organ, src, in_place, update_icon) + owner.add_organ(organ, src, in_place, update_icon, detached) for(var/obj/item/organ/external/organ in children) - owner.add_organ(organ, src, in_place, update_icon) + owner.add_organ(organ, src, in_place, update_icon, detached) // //Add any existing organs in the owner that have us as parent // for(var/obj/item/organ/internal/I in owner.get_internal_organs()) if(I.parent_organ == organ_tag) - LAZYDISTINCTADD(I, internal_organs) + LAZYDISTINCTADD(internal_organs, I) update_internal_organs_cost() for(var/obj/item/organ/external/E in owner.get_external_organs()) if(E.parent_organ == organ_tag) E.parent = src - LAZYDISTINCTADD(E, children) + LAZYDISTINCTADD(children, E) //Add any existing implants that should be refering us for(var/obj/implant in implants) @@ -416,18 +460,32 @@ //Handle installing into a stand-alone parent limb to keep dropped limbs in some kind of coherent state if(!affected) affected = loc - if(istype(affected) && parent_type == affected.organ_tag) - parent = affected + if(istype(affected)) + if(parent_organ != affected.organ_tag) + log_warning("obj/item/organ/external/do_install(): The parent organ in the parameters '[affected]'('[affected.organ_tag]') doesn't match the expected parent organ ('[parent_organ]') for '[src]'!") + parent = affected + + //When no owner, make sure we update all our children. Everything else should be implicitely at the right place + for(var/obj/item/organ/external/organ in children) + organ.do_install(null, src, in_place, update_icon, detached) + + //This proc refers to owner's species and all kind of risky stuff, so it cannot be done in_place + if(!in_place) + update_wounds() //Parent hieracrchy handling if(parent) - LAZYDISTINCTADD(parent.children, src) - //Remove all stump wounds since limb is not missing anymore + //Add ourselves to our parent organ's data + LAZYDISTINCTADD(parent.children, src) //Even when detached the limb has to be in the children list, because of the way limbs icon are handled + + //Remove any stump wound for this slot for(var/datum/wound/lost_limb/W in parent.wounds) - //#FIXME: this only remove one random wound.. If there's more than one removed organ, it probably won't work visually? - qdel(W) - break - parent.update_damages() + if(W.limb_tag == organ_tag) + qdel(W) //Removes itself from parent.wounds + break + + if(!in_place) + parent.update_wounds() /obj/item/organ/external/proc/drop_equipped_clothing() if(!owner) @@ -494,18 +552,23 @@ This function completely restores a damaged organ to perfect condition. */ /obj/item/organ/external/rejuvenate(var/ignore_prosthetic_prefs) - damage_state = "00" + damage_state = "00" status = 0 brute_dam = 0 + brute_ratio = 0 burn_dam = 0 + burn_ratio = 0 germ_level = 0 - pain = 0 genetic_degradation = 0 + for(var/datum/wound/wound in wounds) qdel(wound) number_wounds = 0 + damage = 0 + pain = 0 + // handle internal organs for(var/obj/item/organ/current_organ in internal_organs) current_organ.rejuvenate(ignore_prosthetic_prefs) @@ -522,6 +585,8 @@ This function completely restores a damaged organ to perfect condition. aspect.apply(owner) owner.updatehealth() + undislocate(TRUE) + if(!QDELETED(src) && species) species.post_organ_rejuvenate(src, owner) @@ -598,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) @@ -774,7 +839,7 @@ Note that amputating the affected organ does in fact remove the infection from t // slow healing var/heal_amt = 0 // if damage >= 50 AFTER treatment then it's probably too severe to heal within the timeframe of a round. - if (!GET_CHEMICAL_EFFECT(owner, CE_TOXIN) && W.can_autoheal() && W.wound_damage() && brute_ratio < 0.5 && burn_ratio < 0.5) + if (owner && !GET_CHEMICAL_EFFECT(owner, CE_TOXIN) && W.can_autoheal() && W.wound_damage() && brute_ratio < 0.5 && burn_ratio < 0.5) heal_amt += 0.5 //we only update wounds once in [wound_update_accuracy] ticks so have to emulate realtime @@ -788,7 +853,7 @@ Note that amputating the affected organ does in fact remove the infection from t var/dam_type = BRUTE if(W.damage_type == BURN) dam_type = BURN - if(owner.can_autoheal(dam_type)) + if(owner?.can_autoheal(dam_type)) W.heal_damage(heal_amt) // sync the organ's damage with its wounds @@ -950,7 +1015,7 @@ Note that amputating the affected organ does in fact remove the infection from t if(istype(last_owner) && !QDELETED(last_owner) && LAZYLEN(last_owner.get_external_organs()) <= 1) last_owner.physically_destroyed(FALSE, disintegrate) - if(QDELETED(src)) + if(QDELETED(src) || is_stump()) return if(original_parent) @@ -958,6 +1023,7 @@ Note that amputating the affected organ does in fact remove the infection from t var/obj/item/organ/external/damaged_organ = original_parent if(!clean) var/obj/item/organ/external/stump/stump = new (victim, 0, src) + victim.add_organ(stump, damaged_organ) stump.add_pain(max_damage) damaged_organ = stump if(disintegrate != DISMEMBER_METHOD_BURN) @@ -1008,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) @@ -1026,9 +1092,6 @@ Note that amputating the affected organ does in fact remove the infection from t HELPERS ****************************************************/ -/obj/item/organ/external/proc/is_stump() - return 0 - /obj/item/organ/external/proc/release_restraints(var/mob/living/carbon/human/holder) if(!holder) holder = owner @@ -1189,17 +1252,26 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/setup_as_prosthetic() . = ..(model ? model : /decl/prosthetics_manufacturer) -/obj/item/organ/external/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics = 0, var/keep_organs = 0, var/apply_material = /decl/material/solid/metal/steel) +/obj/item/organ/external/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics = 0, var/keep_organs = 0, var/apply_material = /decl/material/solid/metal/steel, var/check_bodytype, var/check_species) . = ..() slowdown = 0 - if(!ispath(company, /decl/prosthetics_manufacturer)) - PRINT_STACK_TRACE("Limb [type] robotize() was supplied a null or non-decl manufacturer: '[company]'") + var/decl/prosthetics_manufacturer/R + if(istype(company, /decl/prosthetics_manufacturer)) + //Handling for decl + R = company + company = R.type + else + //Handling for paths + if(!ispath(company)) + PRINT_STACK_TRACE("Limb [type] robotize() was supplied a null or non-decl manufacturer: '[company]'") + company = /decl/prosthetics_manufacturer + R = GET_DECL(company) + + //If can't install fallback to default + if(!R.check_can_install(organ_tag, (check_bodytype || owner?.get_bodytype_category() || global.using_map.default_bodytype), (check_species || owner?.get_species_name() || global.using_map.default_species))) company = /decl/prosthetics_manufacturer - - var/decl/prosthetics_manufacturer/R = GET_DECL(company) - if(!R.check_can_install(organ_tag, (owner?.get_bodytype_category() || global.using_map.default_bodytype), (owner?.get_species_name() || global.using_map.default_species))) R = GET_DECL(/decl/prosthetics_manufacturer) model = company @@ -1209,7 +1281,8 @@ Note that amputating the affected organ does in fact remove the infection from t slowdown = R.movement_slowdown max_damage *= R.hardiness min_broken_damage *= R.hardiness - dislocated = -1 + status &= (~ORGAN_DISLOCATED) + limb_flags &= (~ORGAN_FLAG_CAN_DISLOCATE) remove_splint() update_icon(1) unmutate() @@ -1285,8 +1358,9 @@ Note that amputating the affected organ does in fact remove the infection from t W.forceMove(owner) /obj/item/organ/external/do_uninstall(in_place, detach, ignore_children, update_icon) - var/mob/living/carbon/human/victim = owner - . = ..() + var/mob/living/carbon/human/victim = owner //parent proc clears owner + if(!(. = ..())) + return if(victim) if(in_place) @@ -1305,33 +1379,42 @@ Note that amputating the affected organ does in fact remove the infection from t for(var/atom/movable/implant in implants) //large items and non-item objs fall to the floor, everything else stays var/obj/item/I = implant + if(QDELETED(implant)) + LAZYREMOVE(implants, implant) + continue if(istype(I) && I.w_class < ITEM_SIZE_NORMAL) + if(istype(I, /obj/item/implant)) + var/obj/item/implant/imp = I + imp.removed() implant.forceMove(src) else + //Dumpt the rest on the turf LAZYREMOVE(implants, implant) implant.forceMove(get_turf(src)) if(!ignore_children) //Move our chilren limb into our contents for(var/obj/item/organ/external/O in children) - victim.remove_organ(O, drop_organ = FALSE, ignore_children = TRUE) + victim.remove_organ(O, FALSE, FALSE, FALSE, in_place, update_icon) if(QDELETED(O)) + LAZYREMOVE(children, O) continue - // if we didn't lose the organ we still want it as a child - O.forceMove(src) - LAZYDISTINCTADD(children, O) - O.parent = src + O.do_install(null, src, FALSE, update_icon, FALSE) //Forcemove the organ and properly set it up in our internal data // Grab all the children internal organs - for(var/obj/item/organ/organ in internal_organs) - victim.remove_organ(organ, FALSE, FALSE, FALSE, in_place, update_icon) // Organ stays inside and connected - if(!QDELETED(organ)) - organ.forceMove(src) + for(var/obj/item/organ/internal/organ in internal_organs) + victim.remove_organ(organ, FALSE, FALSE, FALSE, in_place, update_icon) + if(QDELETED(organ)) + LAZYREMOVE(internal_organs, organ) + continue + organ.do_install(null, src, FALSE, update_icon, FALSE) //Forcemove the organ and properly set it up in our internal data + + //Note that we don't need to change our own hierarchy when not removing from a mob // Remove parent references if(parent) LAZYREMOVE(parent.children, src) - parent = null + parent = null /obj/item/organ/external/on_remove_effects(mob/living/last_owner) . = ..() @@ -1401,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 @@ -1479,13 +1563,15 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/add_ailment(var/datum/ailment/ailment) . = ..() if(. && owner) - owner.bad_external_organs |= src + LAZYDISTINCTADD(owner.bad_external_organs, src) /obj/item/organ/external/die() //External organs dying on a dime causes some real issues in combat 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) @@ -1494,3 +1580,10 @@ Note that amputating the affected organ does in fact remove the infection from t /obj/item/organ/external/is_internal() return FALSE + +/obj/item/organ/external/get_contained_external_atoms() + . = ..() + //Prevent stumps and things that shouldn't be dropped from getting dumped out + for(var/obj/item/organ/O in .) + if(!O.is_droppable()) + . -= O diff --git a/code/modules/organs/external/diagnostics.dm b/code/modules/organs/external/diagnostics.dm index a6eb88e1e2a..37eb71784f0 100644 --- a/code/modules/organs/external/diagnostics.dm +++ b/code/modules/organs/external/diagnostics.dm @@ -84,7 +84,7 @@ . += "[capitalize(artery_name)] ruptured" if(status & ORGAN_TENDON_CUT) . += "Severed [tendon_name]" - if(dislocated == 2) // non-magical constants when + if(is_dislocated()) . += "Dislocated" if(splinted) . += "Splinted" @@ -163,7 +163,7 @@ if(status & ORGAN_TENDON_CUT) to_chat(user, "The tendons in [name] are severed!") - if(dislocated == 2) + if(is_dislocated()) to_chat(user, "The [joint] is dislocated!") return 1 diff --git a/code/modules/organs/external/head.dm b/code/modules/organs/external/head.dm index 8b116205194..489af427207 100644 --- a/code/modules/organs/external/head.dm +++ b/code/modules/organs/external/head.dm @@ -15,7 +15,7 @@ artery_name = "carotid artery" cavity_name = "cranial" - limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_HEALS_OVERKILL | ORGAN_FLAG_CAN_BREAK + limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_HEALS_OVERKILL | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE var/draw_eyes = TRUE var/glowing_eyes = FALSE @@ -76,8 +76,8 @@ /obj/item/organ/external/head/get_agony_multiplier() return (owner && owner.headcheck(organ_tag)) ? 1.50 : 1 -/obj/item/organ/external/head/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics, var/keep_organs, var/apply_material = /decl/material/solid/metal/steel) - . = ..(company, skip_prosthetics, 1) +/obj/item/organ/external/head/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics = 0, var/keep_organs = 1, var/apply_material = /decl/material/solid/metal/steel, var/check_bodytype, var/check_species) + . = ..() has_lips = null if(model) var/decl/prosthetics_manufacturer/R = GET_DECL(model) diff --git a/code/modules/organs/external/standard.dm b/code/modules/organs/external/standard.dm index eddfcc498b6..fd5da9c8f9b 100644 --- a/code/modules/organs/external/standard.dm +++ b/code/modules/organs/external/standard.dm @@ -16,7 +16,6 @@ vital = 1 amputation_point = "spine" joint = "neck" - dislocated = -1 parent_organ = null encased = "ribcage" artery_name = "aorta" @@ -38,6 +37,10 @@ return . = ..() +//Can't drop root limb +/obj/item/organ/external/chest/is_droppable() + return FALSE + /obj/item/organ/external/groin name = "lower body" organ_tag = BP_GROIN @@ -50,7 +53,6 @@ parent_organ = BP_CHEST amputation_point = "lumbar" joint = "hip" - dislocated = -1 artery_name = "iliac artery" cavity_name = "abdominal" limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_BREAK @@ -75,7 +77,7 @@ tendon_name = "palmaris longus tendon" artery_name = "basilic vein" arterial_bleed_severity = 0.75 - limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK + limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE /obj/item/organ/external/arm/right organ_tag = BP_R_ARM @@ -100,7 +102,7 @@ tendon_name = "cruciate ligament" artery_name = "femoral artery" arterial_bleed_severity = 0.75 - limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK + limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE /obj/item/organ/external/leg/right organ_tag = BP_R_LEG @@ -125,7 +127,7 @@ amputation_point = "left ankle" tendon_name = "Achilles tendon" arterial_bleed_severity = 0.5 - limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK + limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE /obj/item/organ/external/foot/right organ_tag = BP_R_FOOT @@ -151,12 +153,12 @@ amputation_point = "left wrist" tendon_name = "carpal ligament" arterial_bleed_severity = 0.5 - limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_FINGERPRINT | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK + limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_FINGERPRINT | ORGAN_FLAG_HAS_TENDON | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE var/gripper_ui_label = "L" var/gripper_ui_loc = ui_lhand var/overlay_slot_id = BP_L_HAND -/obj/item/organ/external/hand/do_install(mob/living/carbon/human/target, affected, in_place, update_icon) +/obj/item/organ/external/hand/do_install(mob/living/carbon/human/target, affected, in_place, update_icon, detached) if(!(. = ..())) return owner?.add_held_item_slot(organ_tag, gripper_ui_loc, overlay_slot_id, gripper_ui_label) diff --git a/code/modules/organs/external/stump.dm b/code/modules/organs/external/stump.dm index 8ae5e37f0fd..3c12fe4ae0b 100644 --- a/code/modules/organs/external/stump.dm +++ b/code/modules/organs/external/stump.dm @@ -1,12 +1,7 @@ /obj/item/organ/external/stump name = "limb stump" icon_name = "" - dislocated = -1 - -/obj/item/organ/external/stump/on_remove_effects(mob/living/last_owner) - if(!(. = ..())) - return - qdel(src) + limb_flags = ORGAN_FLAG_CAN_AMPUTATE //Needs this for limb replacement surgery. Since you need to remove stumps first /obj/item/organ/external/stump/Initialize(mapload, var/internal, var/obj/item/organ/external/limb) if(istype(limb)) @@ -27,4 +22,19 @@ status |= ORGAN_CRYSTAL // Likewise with crystalline limbs. /obj/item/organ/external/stump/is_stump() - return 1 + return TRUE + +//Don't let stumps be dropped +/obj/item/organ/external/stump/is_droppable() + return FALSE + +//Stumps don't generate droplimb messages +/obj/item/organ/external/stump/get_droplimb_messages_for(droptype, clean) + return + +//Warn on dropping a stump. +/obj/item/organ/external/stump/dropInto(atom/destination) + . = ..() + //QDeleted stumps will be dropped due to how items works. But otherwise make sure to make a stack trace + if(!QDELETED(src)) + CRASH("[src] was dropped into the world!") \ No newline at end of file diff --git a/code/modules/organs/external/tail.dm b/code/modules/organs/external/tail.dm index 1bca218536e..90111c3d2d6 100644 --- a/code/modules/organs/external/tail.dm +++ b/code/modules/organs/external/tail.dm @@ -12,7 +12,7 @@ amputation_point = "tail" artery_name = "vein" arterial_bleed_severity = 0.3 - limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_BREAK + limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_BREAK | ORGAN_FLAG_CAN_DISLOCATE var/tail // Name of tail state in species effects icon file. var/tail_animation // If set, the icon to obtain tail animation states from. @@ -29,7 +29,7 @@ if(update_icon && !istype(H) && H != owner) H.update_tail_showing(FALSE) -/obj/item/organ/external/tail/do_install(mob/living/carbon/human/target, affected, in_place, update_icon) +/obj/item/organ/external/tail/do_install(mob/living/carbon/human/target, affected, in_place, update_icon, detached) . = ..() if(update_icon && istype(owner)) owner.update_tail_showing(FALSE) diff --git a/code/modules/organs/external/unbreakable.dm b/code/modules/organs/external/unbreakable.dm index 43261f3dcd8..c390b07dfc3 100644 --- a/code/modules/organs/external/unbreakable.dm +++ b/code/modules/organs/external/unbreakable.dm @@ -1,55 +1,44 @@ // Slime/xeno limbs. /obj/item/organ/external/chest/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = 0 /obj/item/organ/external/groin/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE /obj/item/organ/external/arm/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE /obj/item/organ/external/arm/right/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE /obj/item/organ/external/leg/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND /obj/item/organ/external/leg/right/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE /obj/item/organ/external/foot/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE | ORGAN_FLAG_CAN_STAND /obj/item/organ/external/foot/right/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE /obj/item/organ/external/hand/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE /obj/item/organ/external/hand/right/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE /obj/item/organ/external/head/unbreakable - dislocated = -1 arterial_bleed_severity = 0 limb_flags = ORGAN_FLAG_CAN_AMPUTATE 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/external/wounds/wound_types.dm b/code/modules/organs/external/wounds/wound_types.dm index 86556090e23..e2c538a45b9 100644 --- a/code/modules/organs/external/wounds/wound_types.dm +++ b/code/modules/organs/external/wounds/wound_types.dm @@ -269,10 +269,12 @@ /** EXTERNAL ORGAN LOSS **/ /datum/wound/lost_limb + var/limb_tag /datum/wound/lost_limb/New(var/obj/item/organ/external/lost_limb, var/losstype, var/clean) var/damage_amt = lost_limb.max_damage if(clean) damage_amt /= 2 + limb_tag = lost_limb.organ_tag switch(losstype) if(DISMEMBER_METHOD_EDGE, DISMEMBER_METHOD_BLUNT) diff --git a/code/modules/organs/internal/_internal.dm b/code/modules/organs/internal/_internal.dm index c096f544092..f047eb7867b 100644 --- a/code/modules/organs/internal/_internal.dm +++ b/code/modules/organs/internal/_internal.dm @@ -15,8 +15,6 @@ /obj/item/organ/internal/Initialize(mapload, material_key, datum/dna/given_dna) if(!alive_icon) alive_icon = initial(icon_state) - if(max_damage) - min_bruised_damage = FLOOR(max_damage / 4) . = ..() /obj/item/organ/internal/set_species(species_name) @@ -24,38 +22,46 @@ if(species.organs_icon) icon = species.organs_icon -//disconnected the organ from it's owner but does not remove it, instead it becomes an implant that can be removed with implant surgery -//TODO move this to organ/internal once the FPB port comes through -/obj/item/organ/proc/cut_away(var/mob/living/user) - var/obj/item/organ/external/parent = owner.get_organ(parent_organ) - if(istype(parent)) //TODO ensure that we don't have to check this. - do_uninstall(detach = TRUE) // - LAZYADD(parent.implants, src) +/obj/item/organ/internal/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place, update_icon, detached) + . = ..() -/obj/item/organ/internal/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place) - if(status & ORGAN_CUT_AWAY || !(. = ..())) - return + if(!affected) + log_warning("'[src]' called obj/item/organ/internal/do_install(), but its expected parent organ is null!") - STOP_PROCESSING(SSobj, src) - LAZYDISTINCTADD(affected.internal_organs, src) - affected.cavity_max_w_class = max(affected.cavity_max_w_class, w_class) - affected.update_internal_organs_cost() + //The organ may only update and etc if its being attached, or isn't cut away. + //Calls up the chain should have set the CUT_AWAY flag already + if(status & ORGAN_CUT_AWAY) + LAZYDISTINCTADD(affected.implants, src) //Add us to the detached organs list + LAZYREMOVE(affected.internal_organs, src) + else + STOP_PROCESSING(SSobj, src) + LAZYREMOVE(affected.implants, src) //Make sure we're not in the implant list anymore + LAZYDISTINCTADD(affected.internal_organs, src) + affected.cavity_max_w_class = max(affected.cavity_max_w_class, w_class) + affected.update_internal_organs_cost() -/obj/item/organ/internal/do_uninstall(in_place, detach, ignore_children) +/obj/item/organ/internal/do_uninstall(in_place, detach, ignore_children, update_icon) //Make sure we're removed from whatever parent organ we have, either in a mob or not - var/obj/item/organ/external/P = null + var/obj/item/organ/external/affected if(owner) - P = owner.get_organ(parent_organ) + affected = owner.get_organ(parent_organ) else if(istype(loc, /obj/item/organ/external)) var/obj/item/organ/external/E = loc if(E.organ_tag == parent_organ) - P = E - - if(P) - LAZYREMOVE(P.internal_organs, src) - P.update_internal_organs_cost() + affected = E + //We can be removed from a mob even if we have no parents, if we're in a detached state + if(affected) + LAZYREMOVE(affected.internal_organs, src) + affected.update_internal_organs_cost() . = ..() + //Remove it from the implants if we are fully removing, or add it to the implants if we are detaching + if(affected) + if(status & ORGAN_CUT_AWAY) + LAZYDISTINCTADD(affected.implants, src) + else + LAZYREMOVE(affected.implants, src) + //#TODO: Remove rejuv hacks /obj/item/organ/internal/remove_rejuv() do_uninstall() @@ -64,8 +70,8 @@ /obj/item/organ/internal/is_usable() return ..() && !is_broken() -/obj/item/organ/internal/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics, var/keep_organs, var/apply_material = /decl/material/solid/metal/steel) - ..() +/obj/item/organ/internal/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics = 0, var/keep_organs = 0, var/apply_material = /decl/material/solid/metal/steel, var/check_bodytype, var/check_species) + . = ..() min_bruised_damage += 5 min_broken_damage += 10 @@ -151,7 +157,7 @@ heal_damage(damage) /obj/item/organ/internal/proc/get_scarring_level() - . = (initial(max_damage) - max_damage)/initial(max_damage) + . = (absolute_max_damage - max_damage)/absolute_max_damage /obj/item/organ/internal/get_scan_results() . = ..() diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm index b5497469ff8..ec288a3cd09 100644 --- a/code/modules/organs/internal/brain.dm +++ b/code/modules/organs/internal/brain.dm @@ -23,7 +23,7 @@ var/healed_threshold = 1 var/oxygen_reserve = 6 -/obj/item/organ/internal/brain/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics, var/keep_organs, var/apply_material = /decl/material/solid/metal/steel) +/obj/item/organ/internal/brain/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics = 0, var/keep_organs = 0, var/apply_material = /decl/material/solid/metal/steel, var/check_bodytype, var/check_species) replace_self_with(/obj/item/organ/internal/posibrain) /obj/item/organ/internal/brain/mechassist() @@ -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 @@ -78,17 +78,17 @@ else to_chat(user, "This one seems particularly lifeless. Perhaps it will regain some of its luster later..") -/obj/item/organ/internal/brain/do_install(mob/living/carbon/target, affected, in_place, update_icon) +/obj/item/organ/internal/brain/do_install(mob/living/carbon/target, affected, in_place, update_icon, detached) if(!(. = ..())) return if(istype(owner)) SetName(initial(name)) //Reset the organ's name to stay coherent if we're putting it back into someone's skull /obj/item/organ/internal/brain/do_uninstall(in_place, detach, ignore_children, update_icon) - if(!(. = ..())) - return if(!in_place && istype(owner) && name == initial(name)) SetName("\the [owner.real_name]'s [initial(name)]") + if(!(. = ..())) + return /obj/item/organ/internal/brain/on_remove_effects() if(istype(owner)) diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index f8847baaedc..c40ec28e3eb 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -83,7 +83,7 @@ /obj/item/organ/internal/eyes/proc/additional_flash_effects(var/intensity) return -1 -/obj/item/organ/internal/eyes/do_install(mob/living/carbon/human/target, affected, in_place, update_icon) +/obj/item/organ/internal/eyes/do_install(mob/living/carbon/human/target, affected, in_place, update_icon, detached) // Apply our eye colour to the target. if(istype(target) && eye_colour) target.eye_colour = eye_colour @@ -98,8 +98,8 @@ verbs -= /obj/item/organ/internal/eyes/proc/change_eye_color verbs -= /obj/item/organ/internal/eyes/proc/toggle_eye_glow -/obj/item/organ/internal/eyes/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics, var/keep_organs, var/apply_material = /decl/material/solid/metal/steel) - ..() +/obj/item/organ/internal/eyes/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics = 0, var/keep_organs = 0, var/apply_material = /decl/material/solid/metal/steel, var/check_bodytype, var/check_species) + . = ..() name = "optical sensor" icon = 'icons/obj/robot_component.dmi' diff --git a/code/modules/organs/internal/heart.dm b/code/modules/organs/internal/heart.dm index 2416b75701c..d58df75f2e1 100644 --- a/code/modules/organs/internal/heart.dm +++ b/code/modules/organs/internal/heart.dm @@ -214,4 +214,11 @@ . = "[pulsesound] pulse" /obj/item/organ/internal/heart/get_mechanical_assisted_descriptor() - return "pacemaker-assisted [name]" \ No newline at end of file + return "pacemaker-assisted [name]" + +/obj/item/organ/internal/heart/rejuvenate(ignore_prosthetic_prefs) + . = ..() + if(!BP_IS_PROSTHETIC(src)) + pulse = PULSE_NORM + else + pulse = PULSE_NONE \ No newline at end of file diff --git a/code/modules/organs/internal/posibrain.dm b/code/modules/organs/internal/posibrain.dm index 56d5fdf4684..a529eb8fc8a 100644 --- a/code/modules/organs/internal/posibrain.dm +++ b/code/modules/organs/internal/posibrain.dm @@ -176,8 +176,8 @@ transfer_identity(owner) return ..() -/obj/item/organ/internal/posibrain/do_install(var/mob/living/target) - if(!(. = ..())) +/obj/item/organ/internal/posibrain/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place, update_icon, detached) + if(!(. = ..())) return if(istype(owner)) SetName(initial(name)) //Reset the organ's name to stay coherent if we're put back into someone's skull @@ -248,7 +248,7 @@ return 0 return cell && cell.use(amount) -/obj/item/organ/internal/cell/proc/get_power_drain() +/obj/item/organ/internal/cell/proc/get_power_drain() var/damage_factor = 1 + 10 * damage/max_damage return servo_cost * damage_factor @@ -321,10 +321,13 @@ /obj/item/organ/internal/mmi_holder/Destroy() stored_mmi = null + persistantMind = null return ..() -/obj/item/organ/internal/mmi_holder/Initialize(mapload, var/internal) - . = ..() +/obj/item/organ/internal/mmi_holder/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place) + if(status & ORGAN_CUT_AWAY || !(. = ..())) + return + if(!stored_mmi) stored_mmi = new(src) update_from_mmi() @@ -355,14 +358,6 @@ owner.switch_from_dead_to_living_mob_list() owner.visible_message("\The [owner] twitches visibly!") -/obj/item/organ/internal/mmi_holder/cut_away(var/mob/living/user) - var/obj/item/organ/external/parent = owner.get_organ(parent_organ) - if(istype(parent)) - do_uninstall(detach = TRUE) //#TODO: detach proc for organs/mobs so we have less args to pass.. - var/brain = transfer_and_delete() - if(brain) - LAZYADD(parent.implants, brain) - /obj/item/organ/internal/mmi_holder/on_remove_effects(mob/living/last_owner) if(last_owner && last_owner.mind) persistantMind = last_owner.mind @@ -380,4 +375,10 @@ var/response = input(find_dead_player(ownerckey, 1), "Your [initial(stored_mmi.name)] has been removed from your body. Do you wish to return to life?", "Robotic Rebirth") as anything in list("Yes", "No") if(response == "Yes") persistantMind.transfer_to(stored_mmi.brainmob) - qdel(src) \ No newline at end of file + qdel(src) + +//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) + . = ..() + if (!QDELETED(src)) + transfer_and_delete() diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 2f02f7d0dd3..076243b47a2 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -26,18 +26,21 @@ // Damage vars. var/damage = 0 // Current damage to the organ var/min_broken_damage = 30 // Damage before becoming broken - var/max_damage = 30 // Damage cap + var/max_damage = 30 // Damage cap, including scarring + var/absolute_max_damage = 0 // Lifetime damage cap, ignoring scarring. var/rejecting // Is this organ already being rejected? - var/death_time // world.time at moment of death. + var/death_time // REALTIMEOFDAY at moment of death. var/scale_max_damage_to_species_health // Whether or not we should scale the damage values of this organ to the owner species. /obj/item/organ/Destroy() if(owner) - owner.remove_organ(src, FALSE, FALSE, TRUE, TRUE, FALSE) //Tell our parent we're unisntalling silently + owner.remove_organ(src, FALSE, FALSE, TRUE, TRUE, FALSE) //Tell our parent we're unisntalling in place else do_uninstall(TRUE, FALSE, FALSE, FALSE) //Don't ignore children here since we might own/contain them owner = null dna = null + species = null + bodytype = null QDEL_NULL_LIST(ailments) return ..() @@ -56,38 +59,30 @@ //Third rgument may be a dna datum; if null will be set to holder's dna. /obj/item/organ/Initialize(mapload, material_key, var/datum/dna/given_dna) . = ..(mapload, material_key) - if(. == INITIALIZE_HINT_QDEL) - return - - if(max_damage) - min_broken_damage = FLOOR(max_damage / 2) - else - max_damage = min_broken_damage * 2 - - if(!BP_IS_PROSTHETIC(src)) - setup_as_organic(given_dna) - else - setup_as_prosthetic() - - update_icon() + if(. != INITIALIZE_HINT_QDEL) + if(!BP_IS_PROSTHETIC(src)) + setup_as_organic(given_dna) + else + setup_as_prosthetic() /obj/item/organ/proc/setup_as_organic(var/datum/dna/given_dna) //Null DNA setup 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 @@ -103,7 +98,7 @@ if(istype(material)) robotize(forced_model, apply_material = material.type) - else + else robotize(forced_model) return TRUE @@ -115,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() @@ -128,21 +122,29 @@ 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)]") + bodytype = owner?.bodytype || species.default_bodytype species.resize_organ(src) // 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 - if(max_damage) - max_damage = max(1, FLOOR(max_damage * total_health_coefficient)) - min_broken_damage = max(1, FLOOR(max_damage * 0.5)) + + //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)) else min_broken_damage = max(1, FLOOR(min_broken_damage * total_health_coefficient)) - max_damage = max(1, FLOOR(min_broken_damage * 2)) + absolute_max_damage = max(1, FLOOR(min_broken_damage * 2)) + max_damage = absolute_max_damage // resets scarring, but ah well /obj/item/organ/proc/die() damage = max_damage @@ -156,7 +158,7 @@ /obj/item/organ/Process() - if(loc != owner) + if(loc != owner) //#FIXME: looks like someone was trying to hide a bug :P That probably could break organs placed inside a wrapper though owner = null //dead already, no need for more processing @@ -319,7 +321,7 @@ if (can_recover()) damage = between(0, damage - round(amount, 0.1), max_damage) -/obj/item/organ/proc/robotize(var/company, var/skip_prosthetics = 0, var/keep_organs = 0, var/apply_material = /decl/material/solid/metal/steel) +/obj/item/organ/proc/robotize(var/company = /decl/prosthetics_manufacturer, var/skip_prosthetics = 0, var/keep_organs = 0, var/apply_material = /decl/material/solid/metal/steel, var/check_bodytype, var/check_species) status = ORGAN_PROSTHETIC QDEL_NULL(dna) reagents?.clear_reagents() @@ -488,24 +490,32 @@ var/global/list/ailment_reference_cache = list() //if we're an internal organ, having a null "target" is legal if we have an "affected" //CASES: // 1. When creating organs and running their init this is called to properly set them up -// 2. When installing an organ through surgery via replaced this is called. +// 2. When installing an organ through surgery this is called. +// 3. When attaching a detached organ through surgery this is called. // The organ may be inside an external organ that's not inside a mob, or inside a mob -/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) +//detached : If true, the organ will be installed in a detached state, otherwise it will be added in an attached state +/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) forceMove(owner) - for(var/datum/ailment/ailment in ailments) - ailment.begin_ailment_event() + if(!(status & ORGAN_CUT_AWAY)) //Don't run ailments if we're still detached + for(var/datum/ailment/ailment in ailments) + ailment.begin_ailment_event() else if(affected) forceMove(affected) //When installed in a limb with no owner return src //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. +// 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 screen_loc = null @@ -515,6 +525,8 @@ 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) return src @@ -538,3 +550,10 @@ var/global/list/ailment_reference_cache = list() /obj/item/organ/proc/is_internal() return FALSE +//Used to tell stumps from other organs. Stumps don't behave like regular organs, and require special handling. +/obj/item/organ/proc/is_stump() + return FALSE + +//Used to override organ drop behavior, so we don't drop organs that shouldn't be dropped into the world, like stumps and root limbs, or wrappers +/obj/item/organ/proc/is_droppable() + return TRUE diff --git a/code/modules/overmap/contacts/_contacts.dm b/code/modules/overmap/contacts/_contacts.dm index acdb52aa31e..ba45493b57c 100644 --- a/code/modules/overmap/contacts/_contacts.dm +++ b/code/modules/overmap/contacts/_contacts.dm @@ -66,8 +66,8 @@ var/obj/effect/overmap/visitable/visitable_effect = effect if(!visitable_effect || !istype(visitable_effect)) return FALSE - for(var/thing in visitable_effect.get_linked_machines_of_type(/obj/machinery/power/shield_generator)) - var/obj/machinery/power/shield_generator/S = thing + for(var/thing in visitable_effect.get_linked_machines_of_type(/obj/machinery/shield_generator)) + var/obj/machinery/shield_generator/S = thing if(S.running == SHIELD_RUNNING) return TRUE return FALSE diff --git a/code/modules/overmap/events/event.dm b/code/modules/overmap/events/event.dm index d3a9289811d..e3ac5c555f4 100644 --- a/code/modules/overmap/events/event.dm +++ b/code/modules/overmap/events/event.dm @@ -14,7 +14,7 @@ // Acquire the list of not-yet utilized overmap turfs on this Z-level var/list/candidate_turfs = block(locate(OVERMAP_EDGE, OVERMAP_EDGE, overmap.assigned_z),locate(overmap.map_size_x - OVERMAP_EDGE, overmap.map_size_y - OVERMAP_EDGE, overmap.assigned_z)) - candidate_turfs = where(candidate_turfs, /proc/can_not_locate, /obj/effect/overmap/visitable) + candidate_turfs = where(candidate_turfs, /proc/can_not_locate, /obj/effect/overmap) for(var/i = 1 to overmap.event_areas) if(!candidate_turfs.len) diff --git a/code/modules/overmap/exoplanets/exoplanet_atmosphere.dm b/code/modules/overmap/exoplanets/exoplanet_atmosphere.dm index 3278bd7564c..26a263a566d 100644 --- a/code/modules/overmap/exoplanets/exoplanet_atmosphere.dm +++ b/code/modules/overmap/exoplanets/exoplanet_atmosphere.dm @@ -37,6 +37,8 @@ var/list/all_materials = decls_repository.get_decls_of_subtype(/decl/material) for(var/mat_type in all_materials) var/decl/material/mat = all_materials[mat_type] + if(mat.is_abstract()) + continue if(mat.exoplanet_rarity == MAT_RARITY_NOWHERE) continue if(isnull(mat.boiling_point) || mat.boiling_point > target_temp) diff --git a/code/modules/overmap/ftl_shunt/core.dm b/code/modules/overmap/ftl_shunt/core.dm index afe4d1b34b3..668d94a837e 100644 --- a/code/modules/overmap/ftl_shunt/core.dm +++ b/code/modules/overmap/ftl_shunt/core.dm @@ -95,7 +95,7 @@ power_channel = EQUIP idle_power_usage = 1600 light_color = COLOR_BLUE - + stock_part_presets = list(/decl/stock_part_preset/terminal_setup) //Base procs /obj/machinery/ftl_shunt/core/Initialize(mapload, d, populate_parts) @@ -111,7 +111,7 @@ conduits.icon_state = "conduits" portal = new portal.icon_state = "loop-base" - charge_indicator = new + charge_indicator = new charge_indicator.icon_state = null pumps = new pumps.icon_state = "coolant_pumps_composite_off" @@ -154,11 +154,11 @@ if(chargepercent == 0 || isnull(chargepercent)) new_charge_color ="#fa0a0a" else - #if DM_VERSION > 513 + #if DM_VERSION > 513 new_charge_color = gradient("#fa0a0a", "#0de405", clamp(chargepercent/100, 0, 100)) #endif #if DM_VERSION < 514 - new_charge_color = HSVtoRGB(RotateHue(hsv(0, 255, 255), 120 * (1 - chargepercent/100))) + new_charge_color = HSVtoRGB(RotateHue(hsv(0, 255, 255), 120 * (1 - chargepercent/100))) #endif animate(charge_indicator, color = new_charge_color, 1 SECOND) @@ -359,9 +359,9 @@ continue if(H.skill_check(SKILL_ENGINES, SKILL_EXPERT)) to_chat(H, SPAN_DANGER("The deck vibrates with a harmonic that sets your teeth on edge and fills you with dread.")) - + var/announcetxt = replacetext(shunt_start_text, "%%TIME%%", "[round(jump_delay/600)] minutes.") - + ftl_announcement.Announce(announcetxt, "FTL Shunt Management System", new_sound = sound('sound/misc/notice2.ogg')) cached_security_level = security_state.current_security_level @@ -645,7 +645,7 @@ if(!use_power_oneoff(input,EQUIP)) last_power_drawn = input accumulated_charge += input * CELLRATE - + return TRUE else return FALSE @@ -671,9 +671,9 @@ shake_camera(M, rand(1,2), rand(1,2)) last_stress_sound = world.time -// +// // Construction MacGuffins down here. -// +// /obj/item/stock_parts/circuitboard/ftl_shunt name = "circuit board (superluminal shunt)" diff --git a/code/modules/overmap/sectors.dm b/code/modules/overmap/sectors.dm index 1c6f6b034bc..89a5ac137b7 100644 --- a/code/modules/overmap/sectors.dm +++ b/code/modules/overmap/sectors.dm @@ -14,8 +14,11 @@ var/global/list/known_overmap_sectors var/list/restricted_waypoints = list() //waypoints for specific shuttle types var/docking_codes - var/start_x //Coordinates for self placing - var/start_y //will use random values if unset + // Custom spawn coordinates. Will pick random place if one of them or both not set. + /// Custom X coordinate to spawn. Require `start_y` set to work. + var/start_x + /// Custom Y coordinate to spawn. Require `start_x` set to work. + var/start_y var/sector_flags = OVERMAP_SECTOR_IN_SPACE @@ -96,9 +99,24 @@ var/global/list/known_overmap_sectors /obj/effect/overmap/visitable/proc/move_to_starting_location() var/datum/overmap/overmap = global.overmaps_by_name[overmap_id] - start_x = start_x || rand(OVERMAP_EDGE, overmap.map_size_x - OVERMAP_EDGE) - start_y = start_y || rand(OVERMAP_EDGE, overmap.map_size_y - OVERMAP_EDGE) - forceMove(locate(start_x, start_y, overmap.assigned_z)) + var/location + + if(start_x && start_y) + location = locate(start_x, start_y, overmap.assigned_z) + else + var/list/candidate_turfs = block( + locate(OVERMAP_EDGE, OVERMAP_EDGE, overmap.assigned_z), + locate(overmap.map_size_x - OVERMAP_EDGE, overmap.map_size_y - OVERMAP_EDGE, overmap.assigned_z) + ) + + candidate_turfs = where(candidate_turfs, /proc/can_not_locate, /obj/effect/overmap) + location = SAFEPICK(candidate_turfs) || locate( + rand(OVERMAP_EDGE, overmap.map_size_x - OVERMAP_EDGE), + rand(OVERMAP_EDGE, overmap.map_size_y - OVERMAP_EDGE), + overmap.assigned_z + ) + + forceMove(location) //This is called later in the init order by SSshuttle to populate sector objects. Importantly for subtypes, shuttles will be created by then. /obj/effect/overmap/visitable/proc/populate_sector_objects() diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm index 015580998c3..915dc88f5b7 100644 --- a/code/modules/overmap/ships/computers/ship.dm +++ b/code/modules/overmap/ships/computers/ship.dm @@ -60,7 +60,11 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov if(linked) user.reset_view(linked) if(user.client) - user.client.view = world.view + extra_view + if(istext(user.client.view)) + var/list/retrieved_view = splittext(user.client.view, "x") + user.client.view = "[text2num(retrieved_view[1]) + extra_view]x[text2num(retrieved_view[2]) + extra_view]" + else + user.client.view = user.client.view + extra_view if(linked) for(var/obj/machinery/computer/ship/sensors/sensor in linked.get_linked_machines_of_type(/obj/machinery/computer/ship)) sensor.reveal_contacts(user) @@ -75,9 +79,7 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov /obj/machinery/computer/ship/proc/unlook(var/mob/user) user.reset_view() if(user.client) - user.client.view = world.view user.client.OnResize() - user.reset_view() if(linked) for(var/obj/machinery/computer/ship/sensors/sensor in linked.get_linked_machines_of_type(/obj/machinery/computer/ship)) sensor.hide_contacts(user) diff --git a/code/modules/overmap/ships/machines/fusion_thruster.dm b/code/modules/overmap/ships/machines/fusion_thruster.dm index a9373ef5034..1dc0d773847 100644 --- a/code/modules/overmap/ships/machines/fusion_thruster.dm +++ b/code/modules/overmap/ships/machines/fusion_thruster.dm @@ -7,7 +7,7 @@ idle_power_usage = 13600 var/initial_id_tag - var/obj/machinery/power/fusion_core/harvest_from + var/obj/machinery/fusion_core/harvest_from /obj/machinery/atmospherics/unary/engine/fusion/Initialize() ..() @@ -30,7 +30,7 @@ var/datum/local_network/lan = lanm.get_local_network() if(lan) - var/list/fusion_cores = lan.get_devices(/obj/machinery/power/fusion_core) + var/list/fusion_cores = lan.get_devices(/obj/machinery/fusion_core) if(fusion_cores && fusion_cores.len) harvest_from = fusion_cores[1] return harvest_from diff --git a/code/modules/overmap/ships/machines/gas_thruster.dm b/code/modules/overmap/ships/machines/gas_thruster.dm index 3666da36ff1..226f693e528 100644 --- a/code/modules/overmap/ships/machines/gas_thruster.dm +++ b/code/modules/overmap/ships/machines/gas_thruster.dm @@ -14,7 +14,7 @@ base_type = /obj/machinery/atmospherics/unary/engine use_power = POWER_USE_OFF power_channel = EQUIP - idle_power_usage = 11600 + idle_power_usage = 2320 var/engine_extension = /datum/extension/ship_engine/gas /obj/machinery/atmospherics/unary/engine/Initialize() @@ -89,4 +89,4 @@ // This comes with an additional terminal component and tries to set it up on init (you should map a terminal beneath it). This is for mapping only. /obj/machinery/atmospherics/unary/engine/terminal uncreated_component_parts = list(/obj/item/stock_parts/power/terminal/buildable) - stock_part_presets = list(/decl/stock_part_preset/terminal_setup) + stock_part_presets = list(/decl/stock_part_preset/terminal_connect) diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 62c8c556bf3..e072d871be2 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -152,7 +152,7 @@ else user.visible_message("[user] begins to wipe [H]'s lipstick off with \the [src].", \ "You begin to wipe off [H]'s lipstick.") - if(do_after(user, 10, H) && do_after(H, 10, needhand = 0)) //user needs to keep their active hand, H does not. + if(do_after(user, 10, H) && do_after(H, 10, check_holding = 0)) //user needs to keep their active hand, H does not. user.visible_message("[user] wipes [H]'s lipstick off with \the [src].", \ "You wipe off [H]'s lipstick.") H.lip_style = null diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm index 1631ff94409..cb52bfce618 100644 --- a/code/modules/paperwork/photography.dm +++ b/code/modules/paperwork/photography.dm @@ -148,12 +148,14 @@ var/global/photo_count = 0 var/icon_on = "camera" var/icon_off = "camera_off" var/size = 3 + /obj/item/camera/on_update_icon() var/datum/extension/base_icon_state/bis = get_extension(src, /datum/extension/base_icon_state) if(on) icon_state = "[bis.base_icon_state]" else icon_state = "[bis.base_icon_state]_off" + /obj/item/camera/Initialize() set_extension(src, /datum/extension/base_icon_state, icon_state) update_icon() @@ -249,7 +251,7 @@ var/global/photo_count = 0 var/x_c = target.x - (size-1)/2 var/y_c = target.y - (size-1)/2 var/z_c = target.z - var/icon/photoimage = generate_image(x_c, y_c, z_c, size, CAPTURE_MODE_REGULAR, user, 0) + var/icon/photoimage = create_area_image(x_c, y_c, z_c, size, TRUE, user) var/obj/item/photo/p = new() p.img = photoimage diff --git a/code/modules/pointdefense/pointdefense.dm b/code/modules/pointdefense/pointdefense.dm index 6d6e42f6f3c..641e57fb90b 100644 --- a/code/modules/pointdefense/pointdefense.dm +++ b/code/modules/pointdefense/pointdefense.dm @@ -109,7 +109,7 @@ idle_power_usage = 0.1 KILOWATTS construct_state = /decl/machine_construction/default/panel_closed base_type = /obj/machinery/pointdefense - stock_part_presets = list(/decl/stock_part_preset/terminal_setup) + stock_part_presets = list(/decl/stock_part_preset/terminal_connect) uncreated_component_parts = null appearance_flags = PIXEL_SCALE | LONG_GLIDE var/active = TRUE diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index 98a72f48766..682f00bbc68 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -147,6 +147,7 @@ var/global/list/all_apcs = list() uncreated_component_parts = list( /obj/item/cell/apc ) + stock_part_presets = list(/decl/stock_part_preset/terminal_setup) /obj/machinery/power/apc/buildable uncreated_component_parts = null @@ -190,11 +191,10 @@ var/global/list/all_apcs = list() . = ..() - if (populate_parts) - init_round_start() - else + if(!populate_parts) operating = 0 - queue_icon_update() + + queue_icon_update() if(operating) force_update_channels() @@ -230,11 +230,6 @@ var/global/list/all_apcs = list() playsound(src, 'sound/machines/apc_nopower.ogg', 75, 0) failure_timer = max(failure_timer, round(duration)) -/obj/machinery/power/apc/proc/init_round_start() - var/obj/item/stock_parts/power/terminal/term = get_component_of_type(/obj/item/stock_parts/power/terminal) - term.make_terminal(src) // intentional crash if there is no terminal - queue_icon_update() - /obj/machinery/power/apc/proc/terminal(var/functional_only) var/obj/item/stock_parts/power/terminal/term = get_component_of_type(/obj/item/stock_parts/power/terminal) if(term && (!functional_only || term.is_functional())) diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index d25b64c507c..025af3c9864 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -87,9 +87,9 @@ By design, d1 is the smallest direction and d2 is the highest user.examinate(src) // following code taken from attackby (multitool) if(powernet && (powernet.avail > 0)) - to_chat(user, "[get_wattage()] in power network.") + to_chat(user, SPAN_WARNING("[get_wattage()] in power network.")) else - to_chat(user, "The cable is not powered.") + to_chat(user, SPAN_WARNING("\The [src] is not powered.")) return /////////////////////////////////// @@ -97,10 +97,10 @@ By design, d1 is the smallest direction and d2 is the highest /////////////////////////////////// /obj/structure/cable/proc/get_wattage() - if(powernet.avail >= 1000000000) - return "[round(powernet.avail/1000000, 0.01)] MW" - if(powernet.avail >= 1000000) - return "[round(powernet.avail/1000, 0.01)] kW" + if(powernet.avail >= 1 GIGAWATTS) + return "[round(powernet.avail/(1 MEGAWATTS), 0.01)] MW" + if(powernet.avail >= 1 MEGAWATTS) + return "[round(powernet.avail/(1 KILOWATTS), 0.01)] kW" return "[round(powernet.avail)] W" //If underfloor, hide the cable @@ -153,10 +153,10 @@ By design, d1 is the smallest direction and d2 is the highest else if(isMultitool(W)) if(powernet && (powernet.avail > 0)) // is it powered? - to_chat(user, "[get_wattage()] in power network.") + to_chat(user, SPAN_WARNING("[get_wattage()] in power network.")) else - to_chat(user, "The cable is not powered.") + to_chat(user, SPAN_WARNING("\The [src] is not powered.")) shock(user, 5, 0.2) @@ -166,10 +166,10 @@ By design, d1 is the smallest direction and d2 is the highest var/delay_holder if(W.force < 5) - visible_message("[user] starts sawing away roughly at the cable with \the [W].") + visible_message(SPAN_WARNING("[user] starts sawing away roughly at \the [src] with \the [W].")) delay_holder = 8 SECONDS else - visible_message("[user] begins to cut through the cable with \the [W].") + visible_message(SPAN_WARNING("[user] begins to cut through \the [src] with \the [W].")) delay_holder = 3 SECONDS if(user.do_skilled(delay_holder, SKILL_ELECTRICAL, src)) @@ -177,7 +177,7 @@ By design, d1 is the smallest direction and d2 is the highest if(W.obj_flags & OBJ_FLAG_CONDUCTIBLE) shock(user, 66, 0.7) else - visible_message("[user] stops cutting before any damage is done.") + visible_message(SPAN_WARNING("[user] stops cutting before any damage is done.")) src.add_fingerprint(user) @@ -187,11 +187,11 @@ By design, d1 is the smallest direction and d2 is the highest return if(d1 == UP || d2 == UP) - to_chat(user, "You must cut this cable from above.") + to_chat(user, SPAN_WARNING("You must cut this [name] from above.")) return if(breaker_box) - to_chat(user, "This cable is connected to a nearby breaker box. Use the breaker box to interact with it.") + to_chat(user, SPAN_WARNING("This [name] is connected to a nearby breaker box. Use the breaker box to interact with it.")) return if (shock(user, 50)) @@ -199,7 +199,7 @@ By design, d1 is the smallest direction and d2 is the highest new/obj/item/stack/cable_coil(T, (src.d1 ? 2 : 1), color) - visible_message("[user] cuts the cable.") + visible_message(SPAN_WARNING("[user] cuts \the [src].")) if(HasBelow(z)) for(var/turf/turf in GetBelow(src)) @@ -535,7 +535,7 @@ By design, d1 is the smallest direction and d2 is the highest return ..() if(BP_IS_BRITTLE(S)) - to_chat(user, "\The [H]'s [S.name] is hard and brittle - \the [src] cannot repair it.") + to_chat(user, SPAN_WARNING("\The [H]'s [S.name] is hard and brittle - \the [src] cannot repair it.")) return 1 var/use_amt = min(src.amount, CEILING(S.burn_dam/3), 5) @@ -573,7 +573,7 @@ By design, d1 is the smallest direction and d2 is the highest selected_color = "Red" final_color = possible_cable_colours[selected_color] color = final_color - to_chat(user, "You change \the [src]'s color to [lowertext(selected_color)].") + to_chat(user, SPAN_NOTICE("You change \the [src]'s color to [lowertext(selected_color)].")) /obj/item/stack/cable_coil/proc/update_wclass() if(amount == 1) @@ -587,11 +587,11 @@ By design, d1 is the smallest direction and d2 is the highest return if(get_amount() == 1) - to_chat(user, "A short piece of power cable.") + to_chat(user, "A [singular_name] of cable.") else if(get_amount() == 2) - to_chat(user, "A piece of power cable.") + to_chat(user, "Two [plural_name] of cable.") else - to_chat(user, "A coil of power cable. There are [get_amount()] lengths of cable in the coil.") + to_chat(user, "A coil of power cable. There are [get_amount()] [plural_name] of cable in the coil.") /obj/item/stack/cable_coil/verb/make_restraint() @@ -602,13 +602,13 @@ By design, d1 is the smallest direction and d2 is the highest if(ishuman(M) && !M.incapacitated()) if(!isturf(usr.loc)) return if(!src.use(15)) - to_chat(usr, "You need at least 15 lengths to make restraints!") + to_chat(usr, SPAN_WARNING("You need at least 15 [plural_name] of cable to make restraints!")) return var/obj/item/handcuffs/cable/B = new /obj/item/handcuffs/cable(usr.loc) B.color = color - to_chat(usr, "You wind some cable together to make some restraints.") + to_chat(usr, SPAN_NOTICE("You wind some [plural_name] of cable together to make some restraints.")) else - to_chat(usr, "You cannot do that.") + to_chat(usr, SPAN_NOTICE("You cannot do that.")) /obj/item/stack/cable_coil/cyborg/verb/set_colour() set name = "Change Colour" @@ -644,15 +644,15 @@ By design, d1 is the smallest direction and d2 is the highest return if(get_amount() < 1) // Out of cable - to_chat(user, "There is no cable left.") + to_chat(user, SPAN_WARNING("There is no [plural_name] of cable left.")) return if(get_dist(F,user) > 1) // Too far - to_chat(user, "You can't lay cable at a place that far away.") + to_chat(user, SPAN_WARNING("You can't lay cable at a place that far away.")) return if(!F.is_plating()) // Ff floor is intact, complain - to_chat(user, "You can't lay cable there unless the floor tiles are removed.") + to_chat(user, SPAN_WARNING("You can't lay cable there unless the floor tiles are removed.")) return var/dirn @@ -664,18 +664,19 @@ By design, d1 is the smallest direction and d2 is the highest var/end_dir = 0 if(istype(F) && F.is_open()) if(!can_use(2)) - to_chat(user, "You don't have enough cable to do this!") + to_chat(user, SPAN_WARNING("You don't have enough [plural_name] of cable to do this!")) return end_dir = DOWN for(var/obj/structure/cable/LC in F) if((LC.d1 == dirn && LC.d2 == end_dir ) || ( LC.d2 == dirn && LC.d1 == end_dir)) - to_chat(user, "There's already a cable at that position.") + to_chat(user, SPAN_WARNING("There's already a cable at that position.")) return put_cable(F, user, end_dir, dirn) if(end_dir == DOWN) put_cable(GetBelow(F), user, UP, 0) + return TRUE // called when cable_coil is click on an installed obj/cable // or click on a turf that already contains a "node" cable @@ -690,19 +691,18 @@ By design, d1 is the smallest direction and d2 is the highest return if(get_dist(C, user) > 1) // make sure it's close enough - to_chat(user, "You can't lay cable at a place that far away.") + to_chat(user, SPAN_WARNING("You can't lay cable at a place that far away.")) return if(U == T) //if clicked on the turf we're standing on, try to put a cable in the direction we're facing - turf_place(T,user) - return + return turf_place(T,user) var/dirn = get_dir(C, user) // one end of the clicked cable is pointing towards us if(C.d1 == dirn || C.d2 == dirn) if(!U.is_plating()) // can't place a cable if the floor is complete - to_chat(user, "You can't lay cable there unless the floor tiles are removed.") + to_chat(user, SPAN_WARNING("You can't lay cable there unless the floor tiles are removed.")) return else // cable is pointing at us, we're standing on an open tile @@ -712,10 +712,10 @@ By design, d1 is the smallest direction and d2 is the highest for(var/obj/structure/cable/LC in U) // check to make sure there's not a cable there already if(LC.d1 == fdirn || LC.d2 == fdirn) - to_chat(user, "There's already a cable at that position.") + to_chat(user, SPAN_WARNING("There's already a cable at that position.")) return put_cable(U,user,0,fdirn) - return + return TRUE // exisiting cable doesn't point at our position, so see if it's a stub else if(C.d1 == 0) @@ -733,7 +733,7 @@ By design, d1 is the smallest direction and d2 is the highest if(LC == C) // skip the cable we're interacting with continue if((LC.d1 == nd1 && LC.d2 == nd2) || (LC.d1 == nd2 && LC.d2 == nd1) ) // make sure no cable matches either direction - to_chat(user, "There's already a cable at that position.") + to_chat(user, SPAN_WARNING("There's already a cable at that position.")) return @@ -765,11 +765,17 @@ By design, d1 is the smallest direction and d2 is the highest return C.denode()// this call may have disconnected some cables that terminated on the centre of the turf, if so split the powernets. - return + return TRUE + + else if(C.d1 == UP) //Special cases for zcables, since they behave weirdly + . = turf_place(T, user) + if(.) + to_chat(user, SPAN_NOTICE("You connect the cable hanging from the ceiling.")) + return . /obj/item/stack/cable_coil/proc/put_cable(turf/F, mob/user, d1, d2) if(!istype(F)) - return + return FALSE var/obj/structure/cable/C = new(F) C.cableColor(color) @@ -792,11 +798,12 @@ By design, d1 is the smallest direction and d2 is the highest if(C.d2 & (C.d2 - 1))// if the cable is layed diagonally, check the others 2 possible directions C.mergeDiagonalsNetworks(C.d2) - use(1) + . = use(1) if (C.shock(user, 50)) if (prob(50)) //fail new/obj/item/stack/cable_coil(C.loc, 1, C.color) qdel(C) + return FALSE ////////////////////////////// // Misc. diff --git a/code/modules/power/fission/core.dm b/code/modules/power/fission/core.dm index d56c2da7247..8af642d5c9a 100644 --- a/code/modules/power/fission/core.dm +++ b/code/modules/power/fission/core.dm @@ -221,7 +221,7 @@ . = ..() /obj/machinery/atmospherics/unary/fission_core/proc/jump_start() - if((stat & (BROKEN|NOPOWER)) || can_use_power_oneoff(5 KILOWATTS)) + if((stat & (BROKEN|NOPOWER)) || (can_use_power_oneoff(5 KILOWATTS) <= 0)) visible_message("\The [src] flashes an 'Insufficient Power' error.") return use_power_oneoff(5 KILOWATTS) diff --git a/code/modules/power/fusion/_setup.dm b/code/modules/power/fusion/_setup.dm index 25020bc624d..d6a1fde3058 100644 --- a/code/modules/power/fusion/_setup.dm +++ b/code/modules/power/fusion/_setup.dm @@ -17,7 +17,7 @@ to_chat(usr, "Error: you are not an admin!") return - if(!(locate(/obj/machinery/power/fusion_core/mapped) in SSmachines.machinery)) + if(!(locate(/obj/machinery/fusion_core/mapped) in SSmachines.machinery)) to_chat(usr, "This map is not appropriate for this verb.") return @@ -31,14 +31,14 @@ log_and_message_admins("## FUSION CORE SETUP - Setup initiated by [usr].") - var/obj/machinery/power/fusion_core/mapped/core = locate() in SSmachines.machinery + var/obj/machinery/fusion_core/mapped/core = locate() in SSmachines.machinery if(core.jumpstart(15000)) for(var/obj/machinery/fusion_fuel_injector/mapped/injector in SSmachines.machinery) injector.cur_assembly = new /obj/item/fuel_assembly/deuterium(injector) injector.BeginInjecting() - for(var/obj/machinery/power/emitter/gyrotron/gyro in SSmachines.machinery) + for(var/obj/machinery/emitter/gyrotron/gyro in SSmachines.machinery) gyro.activate(usr) var/list/delayed_objects = list() diff --git a/code/modules/power/fusion/consoles/core_control.dm b/code/modules/power/fusion/consoles/core_control.dm index c6c013e0c42..4c5e4d38a99 100644 --- a/code/modules/power/fusion/consoles/core_control.dm +++ b/code/modules/power/fusion/consoles/core_control.dm @@ -5,7 +5,7 @@ /obj/machinery/computer/fusion/core_control/OnTopic(var/mob/user, var/href_list, var/datum/topic_state/state) if(href_list["toggle_active"] || href_list["str"]) - var/obj/machinery/power/fusion_core/C = locate(href_list["machine"]) + var/obj/machinery/fusion_core/C = locate(href_list["machine"]) if(!istype(C)) return TOPIC_NOACTION @@ -13,7 +13,7 @@ if(!lan || !lan.is_connected(C)) return TOPIC_NOACTION - if(!C.check_core_status()) + if(C.stat & BROKEN) return TOPIC_NOACTION if(href_list["toggle_active"]) @@ -38,10 +38,11 @@ var/datum/local_network/lan = fusion.get_local_network() var/list/cores = list() if(lan) - var/list/fusion_cores = lan.get_devices(/obj/machinery/power/fusion_core) + var/list/fusion_cores = lan.get_devices(/obj/machinery/fusion_core) for(var/i = 1 to LAZYLEN(fusion_cores)) var/list/core = list() - var/obj/machinery/power/fusion_core/C = fusion_cores[i] + var/obj/machinery/fusion_core/C = fusion_cores[i] + var/datum/powernet/p_network = C.get_powernet() core["id"] = "#[i]" core["ref"] = "\ref[C]" core["field"] = !isnull(C.owned_field) @@ -49,7 +50,7 @@ core["size"] = C.owned_field ? "[C.owned_field.size] meter\s" : "Field offline." core["instability"] = C.owned_field ? "[C.owned_field.percent_unstable * 100]%" : "Field offline." core["temperature"] = C.owned_field ? "[C.owned_field.plasma_temperature + 295]K" : "Field offline." - core["powerstatus"] = "[C.avail()]/[C.active_power_usage] W" + core["powerstatus"] = "[p_network ? p_network.avail : 0]/[C.active_power_usage] W" var/fuel_string = "
    [F.display_name()]" if(F.needs_big_box) dat += "
    " if(C.owned_field && LAZYLEN(C.owned_field.reactants)) for(var/reactant in C.owned_field.reactants) diff --git a/code/modules/power/fusion/consoles/gyrotron_control.dm b/code/modules/power/fusion/consoles/gyrotron_control.dm index 20eacdc5122..02980fa492e 100644 --- a/code/modules/power/fusion/consoles/gyrotron_control.dm +++ b/code/modules/power/fusion/consoles/gyrotron_control.dm @@ -9,12 +9,12 @@ if(href_list["modifypower"] || href_list["modifyrate"] || href_list["toggle"]) - var/obj/machinery/power/emitter/gyrotron/G = locate(href_list["machine"]) + var/obj/machinery/emitter/gyrotron/G = locate(href_list["machine"]) if(!istype(G)) return TOPIC_NOACTION var/datum/local_network/lan = get_local_network() - var/list/gyrotrons = lan.get_devices(/obj/machinery/power/emitter/gyrotron) + var/list/gyrotrons = lan.get_devices(/obj/machinery/emitter/gyrotron) if(!lan || !gyrotrons || !gyrotrons[G]) return TOPIC_NOACTION @@ -49,10 +49,10 @@ var/datum/local_network/lan = fusion.get_local_network() var/list/gyrotrons = list() if(lan && gyrotrons) - var/list/lan_gyrotrons = lan.get_devices(/obj/machinery/power/emitter/gyrotron) + var/list/lan_gyrotrons = lan.get_devices(/obj/machinery/emitter/gyrotron) for(var/i = 1 to LAZYLEN(lan_gyrotrons)) var/list/gyrotron = list() - var/obj/machinery/power/emitter/gyrotron/G = lan_gyrotrons[i] + var/obj/machinery/emitter/gyrotron/G = lan_gyrotrons[i] gyrotron["id"] = "#[i]" gyrotron["ref"] = "\ref[G]" gyrotron["active"] = G.active diff --git a/code/modules/power/fusion/core/_core.dm b/code/modules/power/fusion/core/_core.dm index cafba07b148..559a4dc5104 100644 --- a/code/modules/power/fusion/core/_core.dm +++ b/code/modules/power/fusion/core/_core.dm @@ -1,7 +1,7 @@ #define MAX_FIELD_STR 10000 #define MIN_FIELD_STR 1 -/obj/machinery/power/fusion_core +/obj/machinery/fusion_core name = "\improper R-UST Mk. 8 Tokamak core" desc = "An enormous solenoid for generating extremely high power electromagnetic fields. It includes a kinetic energy harvester." icon = 'icons/obj/machines/power/fusion_core.dmi' @@ -15,34 +15,35 @@ construct_state = /decl/machine_construction/default/panel_closed uncreated_component_parts = null stat_immune = 0 - base_type = /obj/machinery/power/fusion_core + base_type = /obj/machinery/fusion_core + stock_part_presets = list(/decl/stock_part_preset/terminal_setup) var/obj/effect/fusion_em_field/owned_field var/field_strength = 1//0.01 var/initial_id_tag -/obj/machinery/power/fusion_core/mapped +/obj/machinery/fusion_core/mapped anchored = 1 -/obj/machinery/power/fusion_core/Initialize() +/obj/machinery/fusion_core/Initialize() . = ..() - connect_to_network() set_extension(src, /datum/extension/local_network_member) if(initial_id_tag) var/datum/extension/local_network_member/fusion = get_extension(src, /datum/extension/local_network_member) fusion.set_tag(null, initial_id_tag) -/obj/machinery/power/fusion_core/modify_mapped_vars(map_hash) +/obj/machinery/fusion_core/modify_mapped_vars(map_hash) ..() ADJUST_TAG_VAR(initial_id_tag, map_hash) -/obj/machinery/power/fusion_core/Process() - if((stat & BROKEN) || !powernet || !owned_field) - Shutdown() - else - owned_field.handle_tick() +/obj/machinery/fusion_core/Process() + if(use_power == POWER_USE_ACTIVE) + if((stat & BROKEN) || !owned_field) + update_use_power(POWER_USE_IDLE) + else + owned_field.handle_tick() -/obj/machinery/power/fusion_core/Topic(href, href_list) +/obj/machinery/fusion_core/Topic(href, href_list) if(..()) return 1 if(href_list["str"]) @@ -52,7 +53,12 @@ if(owned_field) owned_field.ChangeFieldStrength(field_strength) -/obj/machinery/power/fusion_core/proc/Startup() +/obj/machinery/fusion_core/update_use_power(new_use_power) + . = ..() + if(use_power == POWER_USE_IDLE && owned_field) + Shutdown() + +/obj/machinery/fusion_core/proc/Startup() if(owned_field) return owned_field = new(loc, src) @@ -61,7 +67,7 @@ update_use_power(POWER_USE_ACTIVE) . = 1 -/obj/machinery/power/fusion_core/proc/Shutdown(var/force_rupture) +/obj/machinery/fusion_core/proc/Shutdown(var/force_rupture) if(owned_field) icon_state = "core0" if(force_rupture || owned_field.plasma_temperature > 1000) @@ -70,31 +76,29 @@ owned_field.RadiateAll() qdel(owned_field) owned_field = null - update_use_power(POWER_USE_IDLE) -/obj/machinery/power/fusion_core/proc/AddParticles(var/name, var/quantity = 1) +/obj/machinery/fusion_core/proc/AddParticles(var/name, var/quantity = 1) if(owned_field) owned_field.AddParticles(name, quantity) . = 1 -/obj/machinery/power/fusion_core/bullet_act(var/obj/item/projectile/Proj) +/obj/machinery/fusion_core/bullet_act(var/obj/item/projectile/Proj) if(owned_field) . = owned_field.bullet_act(Proj) -/obj/machinery/power/fusion_core/proc/set_strength(var/value) +/obj/machinery/fusion_core/proc/set_strength(var/value) value = Clamp(value, MIN_FIELD_STR, MAX_FIELD_STR) field_strength = value change_power_consumption(5 * value, POWER_USE_ACTIVE) if(owned_field) owned_field.ChangeFieldStrength(value) -/obj/machinery/power/fusion_core/physical_attack_hand(var/mob/user) +/obj/machinery/fusion_core/physical_attack_hand(var/mob/user) visible_message(SPAN_NOTICE("\The [user] hugs \the [src] to make it feel better!")) - if(owned_field) - Shutdown() + Shutdown() return TRUE -/obj/machinery/power/fusion_core/attackby(var/obj/item/W, var/mob/user) +/obj/machinery/fusion_core/attackby(var/obj/item/W, var/mob/user) if(owned_field) to_chat(user,"Shut \the [src] off first!") @@ -120,17 +124,10 @@ return ..() -/obj/machinery/power/fusion_core/proc/jumpstart(var/field_temperature) +/obj/machinery/fusion_core/proc/jumpstart(var/field_temperature) field_strength = 200 // 3x3, generally a good size. Startup() if(!owned_field) return FALSE owned_field.plasma_temperature = field_temperature - return TRUE - -/obj/machinery/power/fusion_core/proc/check_core_status() - if(stat & BROKEN) - return FALSE - if(idle_power_usage > avail()) - return FALSE - . = TRUE + return TRUE \ No newline at end of file diff --git a/code/modules/power/fusion/core/core_field.dm b/code/modules/power/fusion/core/core_field.dm index 642090c1d0d..c053090aac8 100644 --- a/code/modules/power/fusion/core/core_field.dm +++ b/code/modules/power/fusion/core/core_field.dm @@ -26,7 +26,7 @@ var/fusion_reactant_cap var/cohesion_regeneration = 1 - var/obj/machinery/power/fusion_core/owned_core + var/obj/machinery/fusion_core/owned_core var/list/reactants = list() var/list/particle_catchers = list() @@ -34,7 +34,9 @@ /obj/item/projectile, /obj/effect, /obj/structure/cable, - /obj/machinery/atmospherics + /obj/machinery/atmospherics, + /obj/machinery/air_sensor, + /obj/machinery/power/terminal ) var/light_min_range = 2 @@ -45,7 +47,7 @@ var/last_range var/last_power -/obj/effect/fusion_em_field/Initialize(mapload, var/obj/machinery/power/fusion_core/new_owned_core) +/obj/effect/fusion_em_field/Initialize(mapload, var/obj/machinery/fusion_core/new_owned_core) . = ..() set_light(light_min_range, light_min_power) @@ -111,7 +113,7 @@ React() // Dump power to our powernet. - owned_core.add_avail(FUSION_ENERGY_PER_K * plasma_temperature) + owned_core.generate_power(FUSION_ENERGY_PER_K * plasma_temperature) // Energy decay. if(plasma_temperature >= 1) diff --git a/code/modules/power/fusion/fusion_circuits.dm b/code/modules/power/fusion/fusion_circuits.dm index e9b5d70f4a5..73814a03224 100644 --- a/code/modules/power/fusion/fusion_circuits.dm +++ b/code/modules/power/fusion/fusion_circuits.dm @@ -30,9 +30,12 @@ /obj/item/stock_parts/circuitboard/fusion_core name = "circuitboard (fusion core)" - build_path = /obj/machinery/power/fusion_core + build_path = /obj/machinery/fusion_core board_type = "machine" origin_tech = "{'wormholes':2,'magnets':4,'powerstorage':4}" + additional_spawn_components = list( + /obj/item/stock_parts/power/terminal = 1 + ) req_components = list( /obj/item/stock_parts/manipulator/pico = 2, /obj/item/stock_parts/micro_laser/ultra = 1, @@ -56,10 +59,13 @@ /obj/item/stock_parts/circuitboard/gyrotron name = "circuitboard (gyrotron)" - build_path = /obj/machinery/power/emitter/gyrotron + build_path = /obj/machinery/emitter/gyrotron board_type = "machine" origin_tech = "{'powerstorage':4,'engineering':4}" + additional_spawn_components = list( + /obj/item/stock_parts/power/terminal = 1 + ) req_components = list( /obj/item/stack/cable_coil = 20, /obj/item/stock_parts/micro_laser/ultra = 2 - ) + ) \ No newline at end of file diff --git a/code/modules/power/fusion/fusion_reactions.dm b/code/modules/power/fusion/fusion_reactions.dm index 6ebe9f894f8..36a4bbb4fb3 100644 --- a/code/modules/power/fusion/fusion_reactions.dm +++ b/code/modules/power/fusion/fusion_reactions.dm @@ -12,6 +12,7 @@ var/minimum_reaction_temperature = 100 var/priority = 100 var/hidden_from_codex = FALSE + var/codex_name /decl/fusion_reaction/proc/handle_reaction_special(var/obj/effect/fusion_em_field/holder) return 0 @@ -22,6 +23,7 @@ /decl/fusion_reaction/deuterium_deuterium p_react = /decl/material/gas/hydrogen/deuterium s_react = /decl/material/gas/hydrogen/deuterium + codex_name = "deuterium-deuterium (He3)" energy_consumption = 1 * FUSION_PROCESSING_TIME_MULT energy_production = 1 * FUSION_PROCESSING_TIME_MULT radiation = 1 * FUSION_PROCESSING_TIME_MULT @@ -32,6 +34,7 @@ /decl/fusion_reaction/deuterium_deuterium_alternate //There are two fusion pathways in D-D fusion - one makes He3, one makes Tritium. That's why this is here. p_react = /decl/material/gas/hydrogen/deuterium s_react = /decl/material/gas/hydrogen/deuterium + codex_name = "deuterium-deuterium (H-3)" energy_consumption = 1 * FUSION_PROCESSING_TIME_MULT energy_production = 1 * FUSION_PROCESSING_TIME_MULT radiation = 1 * FUSION_PROCESSING_TIME_MULT diff --git a/code/modules/power/fusion/gyrotron/gyrotron.dm b/code/modules/power/fusion/gyrotron/gyrotron.dm index 5917b3efaa1..a50c2671e1c 100644 --- a/code/modules/power/fusion/gyrotron/gyrotron.dm +++ b/code/modules/power/fusion/gyrotron/gyrotron.dm @@ -1,6 +1,6 @@ #define GYRO_POWER 25000 -/obj/machinery/power/emitter/gyrotron +/obj/machinery/emitter/gyrotron name = "gyrotron" icon = 'icons/obj/machines/power/fusion.dmi' desc = "It is a heavy duty industrial gyrotron suited for powering fusion reactors." @@ -15,16 +15,15 @@ construct_state = /decl/machine_construction/default/panel_closed uncreated_component_parts = list( - /obj/item/stock_parts/radio/receiver, + /obj/item/stock_parts/radio/receiver ) - stat_immune = 0 - base_type = /obj/machinery/power/emitter/gyrotron + base_type = /obj/machinery/emitter/gyrotron -/obj/machinery/power/emitter/gyrotron/anchored +/obj/machinery/emitter/gyrotron/anchored anchored = 1 state = 2 -/obj/machinery/power/emitter/gyrotron/Initialize() +/obj/machinery/emitter/gyrotron/Initialize() set_extension(src, /datum/extension/local_network_member) if(initial_id_tag) var/datum/extension/local_network_member/fusion = get_extension(src, /datum/extension/local_network_member) @@ -32,32 +31,32 @@ change_power_consumption(mega_energy * GYRO_POWER, POWER_USE_ACTIVE) . = ..() -/obj/machinery/power/emitter/gyrotron/modify_mapped_vars(map_hash) +/obj/machinery/emitter/gyrotron/modify_mapped_vars(map_hash) ..() ADJUST_TAG_VAR(initial_id_tag, map_hash) -/obj/machinery/power/emitter/gyrotron/Process() +/obj/machinery/emitter/gyrotron/Process() change_power_consumption(mega_energy * GYRO_POWER, POWER_USE_ACTIVE) . = ..() -/obj/machinery/power/emitter/gyrotron/get_rand_burst_delay() +/obj/machinery/emitter/gyrotron/get_rand_burst_delay() return rate*10 -/obj/machinery/power/emitter/gyrotron/get_burst_delay() +/obj/machinery/emitter/gyrotron/get_burst_delay() return rate*10 -/obj/machinery/power/emitter/gyrotron/get_emitter_beam() +/obj/machinery/emitter/gyrotron/get_emitter_beam() var/obj/item/projectile/beam/emitter/E = ..() E.damage = mega_energy * 50 return E -/obj/machinery/power/emitter/gyrotron/on_update_icon() - if (active && powernet && avail(active_power_usage)) +/obj/machinery/emitter/gyrotron/on_update_icon() + if (active && can_use_power_oneoff(active_power_usage)) icon_state = "emitter-on" else icon_state = "emitter-off" -/obj/machinery/power/emitter/gyrotron/attackby(var/obj/item/W, var/mob/user) +/obj/machinery/emitter/gyrotron/attackby(var/obj/item/W, var/mob/user) if(isMultitool(W)) var/datum/extension/local_network_member/fusion = get_extension(src, /datum/extension/local_network_member) fusion.get_new_tag(user) diff --git a/code/modules/power/fusion/kinetic_harvester.dm b/code/modules/power/fusion/kinetic_harvester.dm index a2f34d1be94..d50066a0109 100644 --- a/code/modules/power/fusion/kinetic_harvester.dm +++ b/code/modules/power/fusion/kinetic_harvester.dm @@ -13,7 +13,7 @@ var/initial_id_tag var/list/stored = list() var/list/harvesting = list() - var/obj/machinery/power/fusion_core/harvest_from + var/obj/machinery/fusion_core/harvest_from /obj/machinery/kinetic_harvester/Initialize() set_extension(src, /datum/extension/local_network_member) @@ -46,7 +46,7 @@ var/datum/local_network/lan = lanm.get_local_network() if(lan) - var/list/fusion_cores = lan.get_devices(/obj/machinery/power/fusion_core) + var/list/fusion_cores = lan.get_devices(/obj/machinery/fusion_core) if(fusion_cores && fusion_cores.len) harvest_from = fusion_cores[1] return harvest_from diff --git a/code/modules/power/generator.dm b/code/modules/power/generator.dm index 8ffaa28425b..08fa37adf9e 100644 --- a/code/modules/power/generator.dm +++ b/code/modules/power/generator.dm @@ -1,4 +1,4 @@ -/obj/machinery/power/generator +/obj/machinery/generator name = "thermoelectric generator" desc = "It's a high efficiency thermoelectric generator." icon_state = "teg-unassembled" @@ -27,7 +27,7 @@ construct_state = /decl/machine_construction/default/panel_closed stat_immune = 0 -/obj/machinery/power/generator/Initialize() +/obj/machinery/generator/Initialize() . = ..() desc = initial(desc) + " Rated for [round(max_power/1000)] kW." reconnect() @@ -37,7 +37,7 @@ //so a circulator to the NORTH of the generator connects first to the EAST, then to the WEST //and a circulator to the WEST of the generator connects first to the NORTH, then to the SOUTH //note that the circulator's outlet dir is it's always facing dir, and it's inlet is always the reverse -/obj/machinery/power/generator/proc/reconnect() +/obj/machinery/generator/proc/reconnect() if(circ1) circ1.temperature_overlay = null if(circ2) @@ -63,7 +63,7 @@ circ2 = null update_icon() -/obj/machinery/power/generator/on_update_icon() +/obj/machinery/generator/on_update_icon() icon_state = anchored ? "teg-assembled" : "teg-unassembled" overlays.Cut() if (circ1) @@ -85,7 +85,7 @@ circ2.temperature_overlay = "circ-[extreme]cold" return 1 -/obj/machinery/power/generator/Process() +/obj/machinery/generator/Process() if(!circ1 || !circ2 || !anchored || stat & (BROKEN|NOPOWER)) stored_energy = 0 return @@ -149,9 +149,10 @@ if(genlev != lastgenlev) lastgenlev = genlev update_icon() - add_avail(effective_gen) + + generate_power(effective_gen) -/obj/machinery/power/generator/attackby(obj/item/W, mob/user) +/obj/machinery/generator/attackby(obj/item/W, mob/user) if(isWrench(W)) playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) anchored = !anchored @@ -159,26 +160,22 @@ "You [anchored ? "secure" : "unsecure"] the bolts holding [src] to the floor.", \ "You hear a ratchet.") update_use_power(anchored) - if(anchored) // Powernet connection stuff. - connect_to_network() - else - disconnect_from_network() reconnect() else ..() -/obj/machinery/power/generator/CanUseTopic(mob/user) +/obj/machinery/generator/CanUseTopic(mob/user) if(!anchored) return STATUS_CLOSE return ..() -/obj/machinery/power/generator/interface_interact(mob/user) +/obj/machinery/generator/interface_interact(mob/user) if(!circ1 || !circ2) //Just incase the middle part of the TEG was not wrenched last. reconnect() ui_interact(user) return TRUE -/obj/machinery/power/generator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/generator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) // this is the data which will be sent to the ui var/vertical = 0 if (dir == NORTH || dir == SOUTH) @@ -229,7 +226,7 @@ // auto update every Master Controller tick ui.set_auto_update(1) -/obj/machinery/power/generator/verb/rotate_clock() +/obj/machinery/generator/verb/rotate_clock() set category = "Object" set name = "Rotate Generator (Clockwise)" set src in view(1) @@ -239,7 +236,7 @@ src.set_dir(turn(src.dir, 90)) -/obj/machinery/power/generator/verb/rotate_anticlock() +/obj/machinery/generator/verb/rotate_anticlock() set category = "Object" set name = "Rotate Generator (Counterclockwise)" set src in view(1) diff --git a/code/modules/power/geothermal/_geothermal.dm b/code/modules/power/geothermal/_geothermal.dm index c0c3592e08c..54193bbf856 100644 --- a/code/modules/power/geothermal/_geothermal.dm +++ b/code/modules/power/geothermal/_geothermal.dm @@ -31,33 +31,33 @@ var/global/const/MAX_GEOTHERMAL_PRESSURE = 2000 if(!QDELETED(src) && prob(100 - (25 * severity))) physically_destroyed() -/obj/machinery/power/geothermal +/obj/machinery/geothermal icon = 'icons/obj/machines/power/geothermal.dmi' icon_state = "geothermal-base" var/tmp/neighbors = 0 var/tmp/current_pressure = 0 var/efficiency = 0.5 -/obj/machinery/power/geothermal/RefreshParts() +/obj/machinery/geothermal/RefreshParts() ..() efficiency = Clamp(total_component_rating_of_type(/obj/item/stock_parts/capacitor) * GEOTHERMAL_EFFICIENCY_MOD, GEOTHERMAL_EFFICIENCY_MOD, 1) -/obj/machinery/power/geothermal/proc/add_pressure(var/pressure) +/obj/machinery/geothermal/proc/add_pressure(var/pressure) current_pressure = Clamp(current_pressure + pressure, 0, MAX_GEOTHERMAL_PRESSURE) if(!is_processing) START_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) -/obj/machinery/power/geothermal/Process() +/obj/machinery/geothermal/Process() if(anchored && !(stat & BROKEN) && loc) var/consumed_pressure = current_pressure * GEOTHERMAL_PRESSURE_CONSUMED_PER_TICK current_pressure -= consumed_pressure var/remaining_pressure = consumed_pressure consumed_pressure = round(consumed_pressure * efficiency) remaining_pressure -= consumed_pressure - if(powernet) - var/generated_power = round(consumed_pressure * GEOTHERMAL_PRESSURE_TO_POWER) - if(generated_power) - add_avail(generated_power) + + var/generated_power = round(consumed_pressure * GEOTHERMAL_PRESSURE_TO_POWER) + if(generated_power) + generate_power(generated_power) remaining_pressure = round(remaining_pressure * GEOTHERMAL_PRESSURE_LOSS) if(remaining_pressure) addtimer(CALLBACK(src, .proc/propagate_pressure, remaining_pressure), 5) @@ -65,44 +65,44 @@ var/global/const/MAX_GEOTHERMAL_PRESSURE = 2000 if(current_pressure <= 10) return PROCESS_KILL -/obj/machinery/power/geothermal/proc/propagate_pressure(var/remaining_pressure) +/obj/machinery/geothermal/proc/propagate_pressure(var/remaining_pressure) var/list/neighbors for(var/neighbordir in global.cardinal) - var/obj/machinery/power/geothermal/neighbor = (locate() in get_step(loc, neighbordir)) + var/obj/machinery/geothermal/neighbor = (locate() in get_step(loc, neighbordir)) if(neighbor?.anchored && !(neighbor.stat & BROKEN)) LAZYADD(neighbors, neighbor) if(LAZYLEN(neighbors)) remaining_pressure = round(remaining_pressure / LAZYLEN(neighbors)) if(remaining_pressure) - for(var/obj/machinery/power/geothermal/neighbor AS_ANYTHING in neighbors) + for(var/obj/machinery/geothermal/neighbor AS_ANYTHING in neighbors) neighbor.add_pressure(remaining_pressure) -/obj/machinery/power/geothermal/proc/refresh_neighbors() +/obj/machinery/geothermal/proc/refresh_neighbors() var/last_neighbors = neighbors neighbors = 0 for(var/neighbordir in global.cardinal) - if(locate(/obj/machinery/power/geothermal) in get_step(loc, neighbordir)) + if(locate(/obj/machinery/geothermal) in get_step(loc, neighbordir)) neighbors |= neighbordir if(last_neighbors != neighbors) update_icon() -/obj/machinery/power/geothermal/Initialize() +/obj/machinery/geothermal/Initialize() . = ..() refresh_neighbors() for(var/turf/T AS_ANYTHING in RANGE_TURFS(loc, 1)) - for(var/obj/machinery/power/geothermal/neighbor in T) + for(var/obj/machinery/geothermal/neighbor in T) neighbor.refresh_neighbors() STOP_PROCESSING_MACHINE(src, MACHINERY_PROCESS_SELF) -/obj/machinery/power/geothermal/Destroy() +/obj/machinery/geothermal/Destroy() var/atom/last_loc = loc . = ..() if(istype(last_loc)) for(var/turf/T AS_ANYTHING in RANGE_TURFS(last_loc, 1)) - for(var/obj/machinery/power/geothermal/neighbor in T) + for(var/obj/machinery/geothermal/neighbor in T) neighbor.refresh_neighbors() -/obj/machinery/power/geothermal/on_update_icon() +/obj/machinery/geothermal/on_update_icon() . = ..() diff --git a/code/modules/power/geothermal/geothermal_circuit.dm b/code/modules/power/geothermal/geothermal_circuit.dm index 9342f3e7929..2b34cbf2224 100644 --- a/code/modules/power/geothermal/geothermal_circuit.dm +++ b/code/modules/power/geothermal/geothermal_circuit.dm @@ -1,6 +1,6 @@ /obj/item/stock_parts/circuitboard/geothermal name = "circuitboard (geothermal turbine)" - build_path = /obj/machinery/power/geothermal + build_path = /obj/machinery/geothermal board_type = "machine" origin_tech = "{'magnets':3,'powerstorage':3}" req_components = list( diff --git a/code/modules/power/geothermal/geothermal_extension.dm b/code/modules/power/geothermal/geothermal_extension.dm index f4a25b618c0..3eef52c7075 100644 --- a/code/modules/power/geothermal/geothermal_extension.dm +++ b/code/modules/power/geothermal/geothermal_extension.dm @@ -24,7 +24,7 @@ var/turf/T = get_turf(holder) if(!istype(T)) return - var/obj/machinery/power/geothermal/geothermal = locate() in T + var/obj/machinery/geothermal/geothermal = locate() in T if(geothermal?.anchored) geothermal.add_pressure(rand(pressure_min, pressure_max)) return diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm index 6bffe1b45be..45902cdc66e 100644 --- a/code/modules/power/port_gen.dm +++ b/code/modules/power/port_gen.dm @@ -1,5 +1,5 @@ //Baseline portable generator. Has all the default handling. Not intended to be used on it's own (since it generates unlimited power). -/obj/machinery/power/port_gen +/obj/machinery/port_gen name = "Placeholder Generator" //seriously, don't use this. It can't be anchored without VV magic. desc = "A portable generator for emergency backup power." icon = 'icons/obj/power.dmi' @@ -17,26 +17,26 @@ var/sound_id var/working_sound -/obj/machinery/power/port_gen/proc/IsBroken() +/obj/machinery/port_gen/proc/IsBroken() return (stat & (BROKEN|EMPED)) -/obj/machinery/power/port_gen/proc/HasFuel() //Placeholder for fuel check. +/obj/machinery/port_gen/proc/HasFuel() //Placeholder for fuel check. return 1 -/obj/machinery/power/port_gen/proc/UseFuel() //Placeholder for fuel use. +/obj/machinery/port_gen/proc/UseFuel() //Placeholder for fuel use. return -/obj/machinery/power/port_gen/proc/DropFuel() +/obj/machinery/port_gen/proc/DropFuel() return -/obj/machinery/power/port_gen/proc/handleInactive() +/obj/machinery/port_gen/proc/handleInactive() return -/obj/machinery/power/port_gen/proc/update_sound() +/obj/machinery/port_gen/proc/update_sound() if(!working_sound) return if(!sound_id) - sound_id = "[type]_[sequential_id(/obj/machinery/power/port_gen)]" + sound_id = "[type]_[sequential_id(/obj/machinery/port_gen)]" if(active && HasFuel() && !IsBroken()) var/volume = 10 + 15*power_output if(!sound_token) @@ -47,10 +47,10 @@ QDEL_NULL(sound_token) -/obj/machinery/power/port_gen/Process() +/obj/machinery/port_gen/Process() ..() - if(active && HasFuel() && !IsBroken() && anchored && powernet) - add_avail(power_gen * power_output) + if(active && HasFuel() && !IsBroken() && anchored) + generate_power(power_gen * power_output) UseFuel() src.updateDialog() else @@ -59,20 +59,20 @@ update_icon() update_sound() -/obj/machinery/power/port_gen/on_update_icon() +/obj/machinery/port_gen/on_update_icon() if(!active) icon_state = initial(icon_state) return 1 else icon_state = "[initial(icon_state)]on" -/obj/machinery/power/port_gen/CanUseTopic(mob/user) +/obj/machinery/port_gen/CanUseTopic(mob/user) if(!anchored) to_chat(user, "The generator needs to be secured first.") return STATUS_CLOSE return ..() -/obj/machinery/power/port_gen/examine(mob/user, distance) +/obj/machinery/port_gen/examine(mob/user, distance) . = ..() if(distance > 1) return @@ -80,8 +80,7 @@ to_chat(usr, "The generator is on.") else to_chat(usr, "The generator is off.") - -/obj/machinery/power/port_gen/emp_act(severity) +/obj/machinery/port_gen/emp_act(severity) if(!active) return var/duration = 6000 //ten minutes @@ -101,7 +100,7 @@ spawn(duration) stat &= ~EMPED -/obj/machinery/power/port_gen/proc/explode() +/obj/machinery/port_gen/proc/explode() explosion(src.loc, -1, 3, 5, -1) qdel(src) @@ -109,7 +108,7 @@ #define TEMPERATURE_CHANGE_MAX 20 //A power generator that runs on solid plasma sheets. -/obj/machinery/power/port_gen/pacman +/obj/machinery/port_gen/pacman name = "portable generator" desc = "A power generator that runs on solid graphite sheets. Rated for 80 kW max safe output." @@ -141,7 +140,7 @@ var/overheating = 0 //if this gets high enough the generator explodes var/max_overheat = 150 -/obj/machinery/power/port_gen/pacman/examine(mob/user) +/obj/machinery/port_gen/pacman/examine(mob/user) . = ..() if(active) to_chat(user, "\The [src] appears to be producing [power_gen*power_output] W.") @@ -157,7 +156,7 @@ to_chat(user, "There [sheets == 1 ? "is" : "are"] [sheets] [sheets == 1 ? initial(sheet.singular_name) : initial(sheet.plural_name)] left in the hopper.") to_chat(user, SPAN_SUBTLE("\The [src] uses [mat.solid_name] [initial(sheet.plural_name)] as fuel to produce power.")) -/obj/machinery/power/port_gen/pacman/Initialize() +/obj/machinery/port_gen/pacman/Initialize() . = ..() if(isnull(sheet_path)) @@ -165,14 +164,11 @@ if(mat) sheet_path = mat.default_solid_form - if(anchored) - connect_to_network() - -/obj/machinery/power/port_gen/pacman/Destroy() +/obj/machinery/port_gen/pacman/Destroy() DropFuel() return ..() -/obj/machinery/power/port_gen/pacman/RefreshParts() +/obj/machinery/port_gen/pacman/RefreshParts() var/temp_rating = total_component_rating_of_type(/obj/item/stock_parts/micro_laser) temp_rating += total_component_rating_of_type(/obj/item/stock_parts/capacitor) @@ -181,21 +177,21 @@ power_gen = round(initial(power_gen) * Clamp(temp_rating, 0, 20) / 2) ..() -/obj/machinery/power/port_gen/pacman/proc/process_exhaust() +/obj/machinery/port_gen/pacman/proc/process_exhaust() var/decl/material/mat = GET_DECL(sheet_material) if(mat && mat.burn_product) var/datum/gas_mixture/environment = loc.return_air() if(environment) environment.adjust_gas(mat.burn_product, 0.05*power_output) -/obj/machinery/power/port_gen/pacman/HasFuel() +/obj/machinery/port_gen/pacman/HasFuel() var/needed_sheets = power_output / time_per_sheet if(sheets >= needed_sheets - sheet_left) return 1 return 0 //Removes one stack's worth of material from the generator. -/obj/machinery/power/port_gen/pacman/DropFuel() +/obj/machinery/port_gen/pacman/DropFuel() if(sheets) var/obj/item/stack/sheet_prototype = sheet_path var/dump_amount = min(sheets, initial(sheet_prototype.max_amount)) @@ -206,7 +202,7 @@ new sheet_path(loc, dump_amount) sheets -= dump_amount -/obj/machinery/power/port_gen/pacman/UseFuel() +/obj/machinery/port_gen/pacman/UseFuel() //how much material are we using this iteration? var/needed_sheets = power_output / time_per_sheet @@ -256,7 +252,7 @@ overheating-- process_exhaust() -/obj/machinery/power/port_gen/pacman/handleInactive() +/obj/machinery/port_gen/pacman/handleInactive() var/cooling_temperature = 20 var/datum/gas_mixture/environment = loc.return_air() if (environment) @@ -273,12 +269,12 @@ if(overheating) overheating-- -/obj/machinery/power/port_gen/pacman/proc/overheat() +/obj/machinery/port_gen/pacman/proc/overheat() overheating++ if (overheating > max_overheat) explode() -/obj/machinery/power/port_gen/pacman/explode() +/obj/machinery/port_gen/pacman/explode() // Vaporize all the fuel // When ground up in a grinder, 1 sheet produces 20 u of material -- Chemistry-Machinery.dm // 1 mol = 10 u? I dunno. 1 mol of carbon is definitely bigger than a pill @@ -290,7 +286,7 @@ sheet_left = 0 ..() -/obj/machinery/power/port_gen/pacman/emag_act(var/remaining_charges, var/mob/user) +/obj/machinery/port_gen/pacman/emag_act(var/remaining_charges, var/mob/user) if (active && prob(25)) explode() //if they're foolish enough to emag while it's running @@ -298,15 +294,15 @@ emagged = 1 return 1 -/obj/machinery/power/port_gen/pacman/components_are_accessible(path) +/obj/machinery/port_gen/pacman/components_are_accessible(path) return !active && ..() -/obj/machinery/power/port_gen/pacman/cannot_transition_to(state_path, mob/user) +/obj/machinery/port_gen/pacman/cannot_transition_to(state_path, mob/user) if(active) return SPAN_WARNING("You cannot do this while \the [src] is running!") return ..() -/obj/machinery/power/port_gen/pacman/attackby(var/obj/item/O, var/mob/user) +/obj/machinery/port_gen/pacman/attackby(var/obj/item/O, var/mob/user) if(istype(O, sheet_path) && (isnull(sheet_material) || sheet_material == O.get_material_type())) var/obj/item/stack/addstack = O var/amount = min((max_sheets - sheets), addstack.amount) @@ -320,26 +316,24 @@ return if(isWrench(O) && !active) if(!anchored) - connect_to_network() to_chat(user, "You secure \the [src] to the floor.") else - disconnect_from_network() to_chat(user, "You unsecure \the [src] from the floor.") playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) anchored = !anchored return component_attackby(O, user) -/obj/machinery/power/port_gen/pacman/dismantle() +/obj/machinery/port_gen/pacman/dismantle() while (sheets > 0) DropFuel() . = ..() -/obj/machinery/power/port_gen/pacman/interface_interact(mob/user) +/obj/machinery/port_gen/pacman/interface_interact(mob/user) ui_interact(user) return TRUE -/obj/machinery/power/port_gen/pacman/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/port_gen/pacman/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) if(IsBroken()) return @@ -384,7 +378,7 @@ ui.open() ui.set_auto_update(1) -/obj/machinery/power/port_gen/pacman/Topic(href, href_list) +/obj/machinery/port_gen/pacman/Topic(href, href_list) if(..()) return @@ -408,7 +402,7 @@ if (power_output < max_power_output || (emagged && power_output < round(max_power_output*2.5))) power_output++ -/obj/machinery/power/port_gen/pacman/super +/obj/machinery/port_gen/pacman/super name = "portable fission generator" desc = "A power generator that utilizes uranium sheets as fuel. Can run for much longer than the standard portabke generators. Rated for 80 kW max safe output." icon_state = "portgen1" @@ -418,16 +412,16 @@ var/rad_power = 4 //nuclear energy is green energy! -/obj/machinery/power/port_gen/pacman/super/process_exhaust() +/obj/machinery/port_gen/pacman/super/process_exhaust() return -/obj/machinery/power/port_gen/pacman/super/UseFuel() +/obj/machinery/port_gen/pacman/super/UseFuel() //produces a tiny amount of radiation when in use if (prob(rad_power*power_output)) SSradiation.radiate(src, 2*rad_power) ..() -/obj/machinery/power/port_gen/pacman/super/on_update_icon() +/obj/machinery/port_gen/pacman/super/on_update_icon() if(..()) set_light(0) return 1 @@ -442,7 +436,7 @@ set_light(0) -/obj/machinery/power/port_gen/pacman/super/explode() +/obj/machinery/port_gen/pacman/super/explode() //a nice burst of radiation var/rads = rad_power*25 + (sheets + sheet_left)*1.5 SSradiation.radiate(src, (max(40, rads))) @@ -450,7 +444,7 @@ explosion(src.loc, rad_power+1, rad_power+1, rad_power*2, 3) qdel(src) -/obj/machinery/power/port_gen/pacman/super/potato +/obj/machinery/port_gen/pacman/super/potato name = "nuclear reactor" desc = "PTTO-3, an industrial all-in-one nuclear power plant by Neo-Chernobyl GmbH. It uses uranium and vodka as a fuel source. Rated for 150 kW max safe output." power_gen = 30000 //Watts output per power_output level @@ -464,15 +458,15 @@ atom_flags = ATOM_FLAG_OPEN_CONTAINER anchored = 1 -/obj/machinery/power/port_gen/pacman/super/potato/Initialize() +/obj/machinery/port_gen/pacman/super/potato/Initialize() create_reagents(120) . = ..() -/obj/machinery/power/port_gen/pacman/super/potato/examine(mob/user) +/obj/machinery/port_gen/pacman/super/potato/examine(mob/user) . = ..() to_chat(user, "Auxilary tank shows [reagents.total_volume]u of liquid in it.") -/obj/machinery/power/port_gen/pacman/super/potato/UseFuel() +/obj/machinery/port_gen/pacman/super/potato/UseFuel() if(reagents.has_reagent(/decl/material/liquid/ethanol/vodka)) rad_power = 4 temperature_gain = 60 @@ -484,13 +478,13 @@ temperature_gain = initial(temperature_gain) ..() -/obj/machinery/power/port_gen/pacman/super/potato/on_update_icon() +/obj/machinery/port_gen/pacman/super/potato/on_update_icon() if(..()) return 1 if(power_output > max_safe_output) icon_state = "potatodanger" -/obj/machinery/power/port_gen/pacman/super/potato/attackby(var/obj/item/O, var/mob/user) +/obj/machinery/port_gen/pacman/super/potato/attackby(var/obj/item/O, var/mob/user) if(istype(O, /obj/item/chems/)) var/obj/item/chems/R = O if(R.standard_pour_into(src,user)) @@ -503,7 +497,7 @@ return ..() -/obj/machinery/power/port_gen/pacman/mrs +/obj/machinery/port_gen/pacman/mrs name = "portable fusion generator" desc = "An advanced portable fusion generator that runs on tritium. Rated for 200 kW maximum safe output!" icon_state = "portgen2" @@ -518,7 +512,7 @@ max_temperature = 800 temperature_gain = 90 -/obj/machinery/power/port_gen/pacman/mrs/explode() +/obj/machinery/port_gen/pacman/mrs/explode() //no special effects, but the explosion is pretty big (same as a supermatter shard). explosion(src.loc, 3, 6, 12, 16, 1) qdel(src) diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index 0a07537be01..9c500725487 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -1,5 +1,7 @@ ////////////////////////////// // POWER MACHINERY BASE CLASS +// This subtype is for machinery which needs to be directly referenced by its parent powernet during powernet processing. +// This subtype does not encompass all power generating machinery, or machinery that needs to draw from a powernet in general. ////////////////////////////// ///////////////////////////// @@ -42,12 +44,6 @@ powernet.trigger_warning() return powernet.draw_power(amount) -/obj/machinery/power/proc/add_avail(var/amount) - if(powernet) - powernet.newavail += amount - return 1 - return 0 - /obj/machinery/power/proc/draw_power(var/amount) if(powernet) return powernet.draw_power(amount) @@ -58,6 +54,11 @@ return powernet.avail-powernet.load else return 0 +/obj/machinery/power/proc/add_avail(var/amount) + if(powernet) + powernet.newavail += amount + return 1 + return 0 /obj/machinery/power/proc/avail() if(powernet) @@ -67,23 +68,17 @@ // connect the machine to a powernet if a node cable is present on the turf /obj/machinery/power/proc/connect_to_network() - var/turf/T = src.loc - if(!T || !istype(T)) - return 0 - - var/obj/structure/cable/C = T.get_cable_node() //check if we have a node cable on the machine turf, the first found is picked - if(!C || !C.powernet) - return 0 - - C.powernet.add_machine(src) - return 1 + var/datum/powernet/P = get_powernet() + if(P) + P.add_machine(src) + return TRUE // remove and disconnect the machine from its current powernet /obj/machinery/power/proc/disconnect_from_network() if(!powernet) - return 0 + return powernet.remove_machine(src) - return 1 + return TRUE // attach a wire to a power machine - leads from the turf you are standing on //almost never called, overwritten by all power machines but terminal and generator @@ -93,63 +88,13 @@ if(isCoil(W)) var/obj/item/stack/cable_coil/coil = W var/turf/T = user.loc - if(!istype(T) || T.density || !T.is_plating()) + if(!istype(T) || T.density || T.cannot_build_cable()) return if(get_dist(src, user) > 1) return coil.turf_place(T, user) return TRUE -/////////////////////////////////////////// -// Powernet handling helpers -////////////////////////////////////////// - -//returns all the cables WITHOUT a powernet in neighbors turfs, -//pointing towards the turf the machine is located at -/obj/machinery/power/proc/get_connections() - - . = list() - - var/cdir - var/turf/T - - for(var/card in global.cardinal) - T = get_step(loc,card) - cdir = get_dir(T,loc) - - for(var/obj/structure/cable/C in T) - if(C.powernet) continue - if(C.d1 == cdir || C.d2 == cdir) - . += C - return . - -//returns all the cables in neighbors turfs, -//pointing towards the turf the machine is located at -/obj/machinery/power/proc/get_marked_connections() - - . = list() - - var/cdir - var/turf/T - - for(var/card in global.cardinal) - T = get_step(loc,card) - cdir = get_dir(T,loc) - - for(var/obj/structure/cable/C in T) - if(C.d1 == cdir || C.d2 == cdir) - . += C - return . - -//returns all the NODES (O-X) cables WITHOUT a powernet in the turf the machine is located at -/obj/machinery/power/proc/get_indirect_connections() - . = list() - for(var/obj/structure/cable/C in loc) - if(C.powernet) continue - if(C.d1 == 0) // the cable is a node cable - . += C - return . - /////////////////////////////////////////// // GLOBAL PROCS for powernets handling ////////////////////////////////////////// diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index dfef3cf164e..7c3c2ed9b98 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -2,7 +2,7 @@ var/global/list/rad_collectors = list() // TODO: swap the hydrogen tanks out for lithium sheets or something like that. -/obj/machinery/power/rad_collector +/obj/machinery/rad_collector name = "radiation collector array" desc = "A device which uses radiation and hydrogen to produce power." icon = 'icons/obj/machines/rad_collector.dmi' @@ -29,15 +29,15 @@ var/global/list/rad_collectors = list() var/end_time = 0 var/alert_delay = 10 SECONDS -/obj/machinery/power/rad_collector/Initialize() +/obj/machinery/rad_collector/Initialize() . = ..() rad_collectors += src -/obj/machinery/power/rad_collector/Destroy() +/obj/machinery/rad_collector/Destroy() rad_collectors -= src . = ..() -/obj/machinery/power/rad_collector/Process() +/obj/machinery/rad_collector/Process() if((stat & BROKEN) || melted) return var/turf/T = get_turf(src) @@ -70,12 +70,12 @@ var/global/list/rad_collectors = list() else loaded_tank.air_adjust_gas(/decl/material/gas/hydrogen, -0.01*drainratio*min(last_rads,max_rads)/max_rads) //fuel cost increases linearly with incoming radiation -/obj/machinery/power/rad_collector/CanUseTopic(mob/user) +/obj/machinery/rad_collector/CanUseTopic(mob/user) if(!anchored) return STATUS_CLOSE return ..() -/obj/machinery/power/rad_collector/interface_interact(mob/user) +/obj/machinery/rad_collector/interface_interact(mob/user) if(!CanInteract(user, DefaultTopicState())) return FALSE . = TRUE @@ -89,7 +89,7 @@ var/global/list/rad_collectors = list() else to_chat(user, "The controls are locked!") -/obj/machinery/power/rad_collector/attackby(obj/item/W, mob/user) +/obj/machinery/rad_collector/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/tank/hydrogen)) if(!src.anchored) to_chat(user, "The [src] needs to be secured to the floor first.") @@ -110,7 +110,7 @@ var/global/list/rad_collectors = list() if(loaded_tank) to_chat(user, "Remove the tank first.") return 1 - for(var/obj/machinery/power/rad_collector/R in get_turf(src)) + for(var/obj/machinery/rad_collector/R in get_turf(src)) if(R != src) to_chat(user, "You cannot install more than one collector on the same spot.") return 1 @@ -119,10 +119,6 @@ var/global/list/rad_collectors = list() user.visible_message("[user.name] [anchored? "secures":"unsecures"] the [src.name].", \ "You [anchored? "secure":"undo"] the external bolts.", \ "You hear a ratchet.") - if(anchored && !(stat & BROKEN)) - connect_to_network() - else - disconnect_from_network() return 1 else if(istype(W, /obj/item/card/id)||istype(W, /obj/item/modular_computer)) if (src.allowed(user)) @@ -137,18 +133,18 @@ var/global/list/rad_collectors = list() return 1 return ..() -/obj/machinery/power/rad_collector/examine(mob/user, distance) +/obj/machinery/rad_collector/examine(mob/user, distance) . = ..() if (distance <= 3 && !(stat & BROKEN)) to_chat(user, "The meter indicates that \the [src] is collecting [last_power] W.") return 1 -/obj/machinery/power/rad_collector/explosion_act(severity) +/obj/machinery/rad_collector/explosion_act(severity) if(severity != 1) eject() . = ..() -/obj/machinery/power/rad_collector/proc/collector_break() +/obj/machinery/rad_collector/proc/collector_break() if(loaded_tank?.air_contents) var/turf/T = get_turf(src) if(T) @@ -157,7 +153,6 @@ var/global/list/rad_collectors = list() fragmentate(T, 2, 4, list(/obj/item/projectile/bullet/pellet/fragment/tank/small = 3, /obj/item/projectile/bullet/pellet/fragment/tank = 1)) explosion(T, -1, -1, 0) QDEL_NULL(loaded_tank) - disconnect_from_network() stat |= BROKEN melted = TRUE anchored = FALSE @@ -165,10 +160,10 @@ var/global/list/rad_collectors = list() desc += " This one is destroyed beyond repair." update_icon() -/obj/machinery/power/rad_collector/return_air() +/obj/machinery/rad_collector/return_air() . =loaded_tank?.return_air() -/obj/machinery/power/rad_collector/proc/eject() +/obj/machinery/rad_collector/proc/eject() locked = 0 var/obj/item/tank/hydrogen/Z = src.loaded_tank if (!Z) @@ -181,17 +176,17 @@ var/global/list/rad_collectors = list() else update_icon() -/obj/machinery/power/rad_collector/proc/receive_pulse(var/pulse_strength) +/obj/machinery/rad_collector/proc/receive_pulse(var/pulse_strength) if(loaded_tank && active) var/power_produced = 0 power_produced = min(100*loaded_tank.air_contents.gas[/decl/material/gas/hydrogen]*pulse_strength*pulse_coeff,max_power) - add_avail(power_produced) + generate_power(power_produced) last_power_new = power_produced return return -/obj/machinery/power/rad_collector/on_update_icon() +/obj/machinery/rad_collector/on_update_icon() if(melted) icon_state = "ca_melt" else if(active) @@ -213,7 +208,7 @@ var/global/list/rad_collectors = list() overlays += image(icon, "rads_[rad_power]") overlays += image(icon, "on") -/obj/machinery/power/rad_collector/toggle_power() +/obj/machinery/rad_collector/toggle_power() active = !active if(active) flick("ca_active", src) diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index b17ace5ee38..653ee2ebce8 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -1,6 +1,6 @@ #define EMITTER_DAMAGE_POWER_TRANSFER 450 //used to transfer power to containment field generators -/obj/machinery/power/emitter +/obj/machinery/emitter name = "emitter" desc = "A massive heavy industrial laser. This design is a fixed installation, capable of shooting in only one direction." icon = 'icons/obj/singularity.dmi' @@ -11,8 +11,8 @@ active_power_usage = 100 KILOWATTS var/efficiency = 0.3 // Energy efficiency. 30% at this time, so 100kW load means 30kW laser pulses. + var/minimum_power = 10 KILOWATTS // The minimum power the emitter will still fire at it it doesn't have enough power available. var/active = 0 - var/powered = 0 var/fire_delay = 100 var/max_burst_delay = 100 var/min_burst_delay = 20 @@ -21,11 +21,12 @@ var/shot_number = 0 var/state = 0 var/locked = 0 + var/powered = 0 core_skill = SKILL_ENGINES uncreated_component_parts = list( /obj/item/stock_parts/radio/receiver, - /obj/item/stock_parts/power/apc + /obj/item/stock_parts/power/terminal ) public_variables = list( /decl/public_access/public_variable/emitter_active, @@ -34,56 +35,48 @@ public_methods = list( /decl/public_access/public_method/toggle_emitter ) - stock_part_presets = list(/decl/stock_part_preset/radio/receiver/emitter = 1) + stock_part_presets = list(/decl/stock_part_preset/radio/receiver/emitter = 1, /decl/stock_part_preset/terminal_connect = 1) -/obj/machinery/power/emitter/anchored +/obj/machinery/emitter/anchored anchored = 1 state = 2 -/obj/machinery/power/emitter/Initialize() - . = ..() - if(state == 2 && anchored) - connect_to_network() - -/obj/machinery/power/emitter/Destroy() +/obj/machinery/emitter/Destroy() log_and_message_admins("deleted \the [src]") investigate_log("deleted at ([x],[y],[z])","singulo") return ..() -/obj/machinery/power/emitter/on_update_icon() - if (active && powernet && avail(active_power_usage)) +/obj/machinery/emitter/on_update_icon() + if (active && powered) icon_state = "emitter_+a" else icon_state = "emitter" -/obj/machinery/power/emitter/interface_interact(mob/user) +/obj/machinery/emitter/interface_interact(mob/user) if(!CanInteract(user, DefaultTopicState())) return FALSE activate(user) return TRUE -/obj/machinery/power/emitter/proc/activate(mob/user) +/obj/machinery/emitter/proc/activate(mob/user) if(!istype(user)) user = null // safety, as the proc is publicly available. if(state == 2) - if(!powernet) - to_chat(user, "\The [src] isn't connected to a wire.") - return 1 - if(!src.locked) - if(src.active==1) - src.active = 0 + if(!locked) + if(active==1) + active = 0 to_chat(user, "You turn off \the [src].") log_and_message_admins("turned off \the [src]") investigate_log("turned off by [key_name_admin(user || usr)]","singulo") else - src.active = 1 + active = 1 if(user) operator_skill = user.get_skill_value(core_skill) update_efficiency() to_chat(user, "You turn on \the [src].") - src.shot_number = 0 - src.fire_delay = get_initial_fire_delay() + shot_number = 0 + fire_delay = get_initial_fire_delay() log_and_message_admins("turned on \the [src]") investigate_log("turned on by [key_name_admin(user || usr)]","singulo") update_icon() @@ -93,59 +86,54 @@ to_chat(user, "\The [src] needs to be firmly secured to the floor first.") return 1 -/obj/machinery/power/emitter/proc/update_efficiency() +/obj/machinery/emitter/proc/update_efficiency() efficiency = initial(efficiency) if(!operator_skill) return var/skill_modifier = 0.8 * (SKILL_MAX - operator_skill)/(SKILL_MAX - SKILL_MIN) //How much randomness is added efficiency *= 1 + (rand() - 1) * skill_modifier //subtract off between 0.8 and 0, depending on skill and luck. -/obj/machinery/power/emitter/emp_act(var/severity) +/obj/machinery/emitter/emp_act(var/severity) return 1 -/obj/machinery/power/emitter/Process() +/obj/machinery/emitter/Process() if(stat & (BROKEN)) return - if(src.state != 2 || (!powernet && active_power_usage)) - src.active = 0 + if(state != 2) + active = FALSE update_icon() return - if(((src.last_shot + src.fire_delay) <= world.time) && (src.active == 1)) - - var/actual_load = draw_power(active_power_usage) - if(actual_load >= active_power_usage) //does the laser have enough power to shoot? - if(!powered) - powered = 1 - update_icon() - investigate_log("regained power and turned on","singulo") - else - if(powered) - powered = 0 - update_icon() - investigate_log("lost power and turned off","singulo") + if(((last_shot + fire_delay) <= world.time) && (active == 1)) + if(active_power_usage - can_use_power_oneoff(active_power_usage) < minimum_power) + powered = FALSE + update_icon() return - - src.last_shot = world.time - if(src.shot_number < burst_shots) - src.fire_delay = get_burst_delay() - src.shot_number ++ + var/drawn_power = min(active_power_usage, active_power_usage - use_power_oneoff(active_power_usage)) + last_shot = world.time + if(shot_number < burst_shots) + fire_delay = get_burst_delay() + shot_number ++ else - src.fire_delay = get_rand_burst_delay() - src.shot_number = 0 + fire_delay = get_rand_burst_delay() + shot_number = 0 //need to calculate the power per shot as the emitter doesn't fire continuously. var/burst_time = (min_burst_delay + max_burst_delay)/2 + 2*(burst_shots-1) - var/power_per_shot = (active_power_usage * efficiency) * (burst_time/10) / burst_shots + var/power_per_shot = (drawn_power * efficiency) * (burst_time/10) / burst_shots if(prob(35)) spark_at(src, amount=5, cardinal_only = TRUE) var/obj/item/projectile/beam/emitter/A = get_emitter_beam() - playsound(src.loc, A.fire_sound, 25, 1) + playsound(loc, A.fire_sound, 25, 1) A.damage = round(power_per_shot/EMITTER_DAMAGE_POWER_TRANSFER) - A.launch( get_step(src.loc, src.dir) ) + A.launch( get_step(loc, dir) ) + + if(!powered) + powered = TRUE + update_icon() -/obj/machinery/power/emitter/attackby(obj/item/W, mob/user) +/obj/machinery/emitter/attackby(obj/item/W, mob/user) if(isWrench(W)) if(active) @@ -154,18 +142,18 @@ switch(state) if(0) state = 1 - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) user.visible_message("[user.name] secures [src] to the floor.", \ "You secure the external reinforcing bolts to the floor.", \ "You hear a ratchet.") - src.anchored = 1 + anchored = 1 if(1) state = 0 - playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1) + playsound(loc, 'sound/items/Ratchet.ogg', 75, 1) user.visible_message("[user.name] unsecures [src] reinforcing bolts from the floor.", \ "You undo the external reinforcing bolts.", \ "You hear a ratchet.") - src.anchored = 0 + anchored = 0 if(2) to_chat(user, "\The [src] needs to be unwelded from the floor.") return @@ -180,7 +168,7 @@ to_chat(user, "\The [src] needs to be wrenched to the floor.") if(1) if (WT.remove_fuel(0,user)) - playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) + playsound(loc, 'sound/items/Welder2.ogg', 50, 1) user.visible_message("[user.name] starts to weld [src] to the floor.", \ "You start to weld [src] to the floor.", \ "You hear welding.") @@ -188,12 +176,11 @@ if(!src || !WT.isOn()) return state = 2 to_chat(user, "You weld [src] to the floor.") - connect_to_network() else to_chat(user, "You need more welding fuel to complete this task.") if(2) if (WT.remove_fuel(0,user)) - playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1) + playsound(loc, 'sound/items/Welder2.ogg', 50, 1) user.visible_message("[user.name] starts to cut [src] free from the floor.", \ "You start to cut [src] free from the floor.", \ "You hear welding.") @@ -201,7 +188,6 @@ if(!src || !WT.isOn()) return state = 1 to_chat(user, "You cut [src] free from the floor.") - disconnect_from_network() else to_chat(user, "You need more welding fuel to complete this task.") return @@ -210,16 +196,16 @@ if(emagged) to_chat(user, "The lock seems to be broken.") return - if(src.allowed(user)) - src.locked = !src.locked - to_chat(user, "The controls are now [src.locked ? "locked." : "unlocked."]") + if(allowed(user)) + locked = !locked + to_chat(user, "The controls are now [locked ? "locked." : "unlocked."]") else to_chat(user, "Access denied.") return ..() return -/obj/machinery/power/emitter/emag_act(var/remaining_charges, var/mob/user) +/obj/machinery/emitter/emag_act(var/remaining_charges, var/mob/user) if(!emagged) locked = 0 emagged = 1 @@ -227,41 +213,46 @@ user.visible_message("[user.name] emags [src].","You short out the lock.") return 1 -/obj/machinery/power/emitter/proc/get_initial_fire_delay() +/obj/machinery/emitter/components_are_accessible(var/path) + if(ispath(path, /obj/item/stock_parts/power/terminal)) + return TRUE + return ..() + +/obj/machinery/emitter/proc/get_initial_fire_delay() return 100 -/obj/machinery/power/emitter/proc/get_rand_burst_delay() +/obj/machinery/emitter/proc/get_rand_burst_delay() return rand(min_burst_delay, max_burst_delay) -/obj/machinery/power/emitter/proc/get_burst_delay() +/obj/machinery/emitter/proc/get_burst_delay() return 2 -/obj/machinery/power/emitter/proc/get_emitter_beam() +/obj/machinery/emitter/proc/get_emitter_beam() return new /obj/item/projectile/beam/emitter(get_turf(src)) /decl/public_access/public_method/toggle_emitter name = "toggle emitter" desc = "Toggles whether or not the emitter is active. It must be unlocked to work." - call_proc = /obj/machinery/power/emitter/proc/activate + call_proc = /obj/machinery/emitter/proc/activate /decl/public_access/public_variable/emitter_active - expected_type = /obj/machinery/power/emitter + expected_type = /obj/machinery/emitter name = "emitter active" desc = "Whether or not the emitter is firing." can_write = FALSE has_updates = FALSE -/decl/public_access/public_variable/emitter_active/access_var(obj/machinery/power/emitter/emitter) +/decl/public_access/public_variable/emitter_active/access_var(obj/machinery/emitter/emitter) return emitter.active /decl/public_access/public_variable/emitter_locked - expected_type = /obj/machinery/power/emitter + expected_type = /obj/machinery/emitter name = "emitter locked" desc = "Whether or not the emitter is locked. Being locked prevents one from changing the active state." can_write = FALSE has_updates = FALSE -/decl/public_access/public_variable/emitter_locked/access_var(obj/machinery/power/emitter/emitter) +/decl/public_access/public_variable/emitter_locked/access_var(obj/machinery/emitter/emitter) return emitter.locked /decl/stock_part_preset/radio/receiver/emitter diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm index e6105b7b2dd..61a1e60e8b8 100644 --- a/code/modules/power/singularity/particle_accelerator/particle.dm +++ b/code/modules/power/singularity/particle_accelerator/particle.dm @@ -37,8 +37,8 @@ toxmob(A) if((istype(A,/obj/machinery/the_singularitygen))||(istype(A,/obj/singularity/))) A:energy += energy - else if(istype(A,/obj/machinery/power/fusion_core)) - var/obj/machinery/power/fusion_core/collided_core = A + else if(istype(A,/obj/machinery/fusion_core)) + var/obj/machinery/fusion_core/collided_core = A if(particle_type && particle_type != "neutron") if(collided_core.AddParticles(particle_type, 1 + additional_particles)) collided_core.owned_field.plasma_temperature += mega_energy diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm index da595b4a36d..b5de022db27 100644 --- a/code/modules/power/singularity/singularity.dm +++ b/code/modules/power/singularity/singularity.dm @@ -38,8 +38,8 @@ var/global/list/singularities = list() if (temp) QDEL_IN(src, temp) START_PROCESSING(SSobj, src) - for(var/obj/machinery/power/singularity_beacon/singubeacon in SSmachines.machinery) - if(singubeacon.active) + for(var/obj/machinery/singularity_beacon/singubeacon in SSmachines.machinery) + if(singubeacon.use_power == POWER_USE_ACTIVE) target = singubeacon break @@ -352,10 +352,10 @@ var/global/list/singularities = list() var/dir2 = 0 var/dir3 = 0 switch(direction) - if(NORTH||SOUTH) + if(NORTH,SOUTH) dir2 = 4 dir3 = 8 - if(EAST||WEST) + if(EAST,WEST) dir2 = 1 dir3 = 2 var/turf/T2 = T @@ -461,7 +461,7 @@ var/global/list/singularities = list() return /obj/singularity/proc/pulse() - for(var/obj/machinery/power/rad_collector/R in rad_collectors) + for(var/obj/machinery/rad_collector/R in rad_collectors) if (get_dist(R, src) <= 15) //Better than using orange() every process. R.receive_pulse(energy) diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index a1b2ecc991d..52d680cddb3 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -4,7 +4,7 @@ var/global/solar_gen_rate = 1500 var/global/list/solars_list = list() /obj/machinery/power/solar - name = "solar panel" + name = "basic solar panel" desc = "A solar electrical generator." icon = 'icons/obj/power.dmi' icon_state = "sp_base" @@ -307,7 +307,7 @@ var/global/list/solars_list = list() M.unset_control() if(connected_tracker) connected_tracker.unset_control() - ..() + return ..() /obj/machinery/power/solar_control/disconnect_from_network() ..() diff --git a/code/modules/power/terminal.dm b/code/modules/power/terminal.dm index addffd960bd..c06d54c6aec 100644 --- a/code/modules/power/terminal.dm +++ b/code/modules/power/terminal.dm @@ -12,13 +12,54 @@ var/obj/item/stock_parts/power/terminal/master anchored = 1 + stat_immune = NOINPUT | NOSCREEN | NOPOWER + interact_offline = TRUE uncreated_component_parts = null - construct_state = /decl/machine_construction/noninteractive // Axiliary entity; all interactions pass through owner machine part instead. + construct_state = /decl/machine_construction/noninteractive/terminal // Auxiliary entity; all interactions pass through owner machine part instead. /obj/machinery/power/terminal/Initialize() . = ..() var/turf/T = src.loc - if(level==1) hide(!T.is_plating()) + if(level == 1 && isturf(T)) + hide(!T.is_plating()) + +/obj/machinery/power/terminal/Destroy() + master = null + . = ..() + +/obj/machinery/power/terminal/attackby(obj/item/W, mob/user) + if(isWirecutter(W)) + var/turf/T = get_turf(src) + var/obj/machinery/machine = master_machine() + + if(istype(T) && !T.is_plating()) + 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(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 + + user.visible_message(SPAN_WARNING("\The [user] dismantles the power terminal from \the [machine]."), \ + "You begin to cut the cables...") + playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1) + if(do_after(user, 50, src)) + if(!QDELETED(src) && (!master || !machine || machine.components_are_accessible(/obj/item/stock_parts/power/terminal))) + if (prob(50) && electrocute_mob(user, powernet, src)) + spark_at(machine, amount=5, cardinal_only = TRUE) + if(HAS_STATUS(user, STAT_STUN)) + return TRUE + new /obj/item/stack/cable_coil(T, 10) + to_chat(user, SPAN_NOTICE("You cut the cables and dismantle the power terminal.")) + qdel_self() + . = ..() + +/obj/machinery/power/terminal/examine(mob/user) + . = ..() + var/obj/machinery/machine = master_machine() + if(machine) + to_chat(user, "It is attached to \the [machine].") /obj/machinery/power/terminal/proc/master_machine() var/obj/machinery/machine = master && master.loc @@ -41,4 +82,15 @@ . = ..() var/obj/machinery/machine = master_machine() if(machine) - machine.power_change() \ No newline at end of file + machine.power_change() + +/obj/machinery/power/terminal/on_update_icon() + . = ..() + 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" + else + icon_state = "term" \ No newline at end of file diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 3354c690e6a..4fe0a3398c2 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -5,7 +5,7 @@ icon_state = "compressor" anchored = 1 density = 1 - var/obj/machinery/power/turbine/turbine + var/obj/machinery/turbine/turbine var/datum/gas_mixture/gas_contained var/turf/simulated/inturf var/starter = 0 @@ -17,7 +17,7 @@ uncreated_component_parts = null construct_state = /decl/machine_construction/default/panel_closed -/obj/machinery/power/turbine +/obj/machinery/turbine name = "gas turbine generator" desc = "A gas turbine used for backup power generation." icon = 'icons/obj/pipes.dmi' @@ -103,17 +103,17 @@ overlays += image('icons/obj/pipes.dmi', "comp-o1", FLY_LAYER) //TODO: DEFERRED -/obj/machinery/power/turbine/Initialize() +/obj/machinery/turbine/Initialize() ..() outturf = get_step(src, dir) return INITIALIZE_HINT_LATELOAD -/obj/machinery/power/turbine/LateInitialize() +/obj/machinery/turbine/LateInitialize() ..() if(!compressor) // It should have found us and subscribed. set_broken(TRUE) -/obj/machinery/power/turbine/Destroy() +/obj/machinery/turbine/Destroy() if(compressor) compressor.turbine = null compressor.set_broken(TRUE) @@ -124,7 +124,7 @@ #define TURBGENQ 20000 #define TURBGENG 0.8 -/obj/machinery/power/turbine/Process() +/obj/machinery/turbine/Process() if(!compressor.starter) return overlays.Cut() @@ -132,7 +132,7 @@ return lastgen = ((compressor.rpm / TURBGENQ)**TURBGENG) *TURBGENQ - add_avail(lastgen) + generate_power(lastgen) var/newrpm = ((compressor.gas_contained.temperature) * compressor.gas_contained.total_moles)/4 newrpm = max(0, newrpm) @@ -153,7 +153,7 @@ src.interact(M) AutoUpdateAI(src) -/obj/machinery/power/turbine/interact(mob/user) +/obj/machinery/turbine/interact(mob/user) if ( (get_dist(src, user) > 1 ) || (stat & (NOPOWER|BROKEN)) && (!istype(user, /mob/living/silicon/ai)) ) user.machine = null @@ -178,12 +178,12 @@ return -/obj/machinery/power/turbine/CanUseTopic(var/mob/user, href_list) +/obj/machinery/turbine/CanUseTopic(var/mob/user, href_list) if(!user.check_dexterity(DEXTERITY_KEYBOARDS)) return min(..(), STATUS_UPDATE) return ..() -/obj/machinery/power/turbine/OnTopic(user, href_list) +/obj/machinery/turbine/OnTopic(user, href_list) if(href_list["close"]) close_browser(usr, "window=turbine") return TOPIC_HANDLED diff --git a/code/modules/projectiles/projectile/change.dm b/code/modules/projectiles/projectile/change.dm index 05d5241b545..1e47bd833bc 100644 --- a/code/modules/projectiles/projectile/change.dm +++ b/code/modules/projectiles/projectile/change.dm @@ -36,7 +36,7 @@ H.set_gender(M.get_sex()) H.name = "unknown" // This will cause set_species() to randomize the mob name. H.real_name = H.name - H.set_species(choice) + H.change_species(choice) H.universal_speak = TRUE var/datum/preferences/A = new() A.randomize_appearance_and_body_for(H) diff --git a/code/modules/random_map/drop/drop_types.dm b/code/modules/random_map/drop/drop_types.dm index e464c20e026..f2778b56c02 100644 --- a/code/modules/random_map/drop/drop_types.dm +++ b/code/modules/random_map/drop/drop_types.dm @@ -188,9 +188,9 @@ var/global/list/datum/supply_drop_loot/supply_drop /datum/supply_drop_loot/power/New() ..() contents = list( - /obj/machinery/power/port_gen/pacman, - /obj/machinery/power/port_gen/pacman/super, - /obj/machinery/power/port_gen/pacman/mrs) + /obj/machinery/port_gen/pacman, + /obj/machinery/port_gen/pacman/super, + /obj/machinery/port_gen/pacman/mrs) /datum/supply_drop_loot/power/contents() return list(pick(contents)) diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index c315d5e32c4..d605125acd5 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -27,10 +27,13 @@ var/global/obj/temp_reagents_holder = new /datum/reagents/proc/get_reaction_loc() return my_atom -/datum/reagents/proc/get_primary_reagent_name() // Returns the name of the reagent with the biggest volume. +/datum/reagents/proc/get_primary_reagent_name(var/codex = FALSE) // Returns the name of the reagent with the biggest volume. var/decl/material/reagent = get_primary_reagent_decl() if(reagent) - . = reagent.name + if(codex && reagent.codex_name) + . = reagent.codex_name + else + . = reagent.name /datum/reagents/proc/get_primary_reagent_decl() . = primary_reagent && GET_DECL(primary_reagent) @@ -41,9 +44,7 @@ var/global/obj/temp_reagents_holder = new for(var/R in reagent_volumes) var/vol = reagent_volumes[R] if(vol < MINIMUM_CHEMICAL_VOLUME) - LAZYREMOVE(reagent_volumes, R) - LAZYREMOVE(reagent_data, R) - cached_color = null + clear_reagent(R, defer_update = TRUE, force = TRUE) // defer_update is important to avoid infinite recursion else total_volume += vol if(!primary_reagent || reagent_volumes[primary_reagent] < vol) @@ -203,8 +204,8 @@ var/global/obj/temp_reagents_holder = new handle_update(safety) return TRUE -/datum/reagents/proc/clear_reagent(var/reagent_type, var/defer_update = FALSE) - . = !!(REAGENT_VOLUME(src, reagent_type) || REAGENT_DATA(src, reagent_type)) +/datum/reagents/proc/clear_reagent(var/reagent_type, var/defer_update = FALSE, var/force = FALSE) + . = force || !!(REAGENT_VOLUME(src, reagent_type) || REAGENT_DATA(src, reagent_type)) if(.) var/amount = LAZYACCESS(reagent_volumes, reagent_type) LAZYREMOVE(reagent_volumes, reagent_type) @@ -239,8 +240,10 @@ var/global/obj/temp_reagents_holder = new return TRUE /datum/reagents/proc/clear_reagents() - reagent_volumes = null - reagent_data = null + for(var/reagent in reagent_volumes) + clear_reagent(reagent, TRUE) + LAZYCLEARLIST(reagent_volumes) + LAZYCLEARLIST(reagent_data) total_volume = 0 /datum/reagents/proc/get_overdose(var/decl/material/current) @@ -335,6 +338,68 @@ var/global/obj/temp_reagents_holder = new if(amount) trans_to(target, amount, multiplier, copy, defer_update = defer_update) +//Spreads the contents of this reagent holder all over the vicinity of the target turf. +/datum/reagents/proc/splash_area(var/turf/epicentre, var/range = 3, var/portion = 1.0, var/multiplier = 1, var/copy = 0) + var/list/things = list() + DVIEW(things, range, epicentre, INVISIBILITY_LIGHTING) + + var/list/turfs = list() + for (var/turf/T in things) + turfs += T + + if (!turfs.len) + return//Nowhere to splash to, somehow + + //Create a temporary holder to hold all the amount that will be spread + var/datum/reagents/R = new /datum/reagents(total_volume * portion * multiplier, global.temp_reagents_holder) + trans_to_holder(R, total_volume * portion, multiplier, copy) + + //The exact amount that will be given to each turf + var/turfportion = R.total_volume / turfs.len + for (var/turf/T in turfs) + R.splash_turf(T, amount = turfportion, multiplier = 1, copy = FALSE) + qdel(R) + +//Spreads the contents of this reagent holder all over the target turf, dividing among things in it. +//50% is divided between mobs, 20% between objects, and whatever's left on the turf itself +/datum/reagents/proc/splash_turf(var/turf/T, var/amount = null, var/multiplier = 1, var/copy = 0) + if (isnull(amount)) + amount = total_volume + else + amount = min(amount, total_volume) + if (amount <= 0) + return + + var/list/mobs = list() + for (var/mob/M in T) + mobs += M + + var/list/objs = list() + for (var/obj/O in T) + //Todo: Add some check here to not hit wires/pipes that are hidden under floor tiles. + //Maybe also not hit things under tables. + objs += O + + if (objs.len) + var/objportion = (amount * 0.2) / objs.len + for (var/o in objs) + var/obj/O = o + + trans_to(O, objportion, multiplier, copy) + + amount = min(amount, total_volume) + + if (mobs.len) + var/mobportion = (amount * 0.5) / mobs.len + for (var/m in mobs) + var/mob/M = m + trans_to(M, mobportion, multiplier, copy) + + trans_to(T, total_volume, multiplier, copy) + + if (total_volume <= 0) + qdel(src) + /datum/reagents/proc/trans_type_to(var/atom/target, var/type, var/amount = 1, var/multiplier = 1, var/defer_update = FALSE) if (!target || !target.reagents || !target.simulated) return diff --git a/code/modules/reagents/Chemistry-Metabolism.dm b/code/modules/reagents/Chemistry-Metabolism.dm index 50de9650b54..60a93a1efd2 100644 --- a/code/modules/reagents/Chemistry-Metabolism.dm +++ b/code/modules/reagents/Chemistry-Metabolism.dm @@ -3,11 +3,11 @@ var/mob/living/parent var/last_metabolize_time = 0 -/datum/reagents/metabolism/clear_reagent(var/reagent_type) - if(REAGENT_VOLUME(src, reagent_type)) +/datum/reagents/metabolism/clear_reagent(var/reagent_type, var/defer_update = FALSE, var/force = FALSE) + . = ..() + if(.) var/decl/material/current = GET_DECL(reagent_type) current.on_leaving_metabolism(parent, metabolism_class) - . = ..() /datum/reagents/metabolism/New(var/max = 100, mob/living/parent_mob, var/met_class) ..(max, parent_mob) diff --git a/code/modules/reagents/chems/chems_blood.dm b/code/modules/reagents/chems/chems_blood.dm index b3e4d742e9c..f5b5a21c4ac 100644 --- a/code/modules/reagents/chems/chems_blood.dm +++ b/code/modules/reagents/chems/chems_blood.dm @@ -1,5 +1,6 @@ /decl/material/liquid/blood name = "blood" + codex_name = "whole blood" uid = "chem_blood" lore_text = "A red (or blue) liquid commonly found inside animals, most of whom are pretty insistent about it being left where you found it." metabolism = REM * 5 diff --git a/code/modules/reagents/chems/chems_cleaner.dm b/code/modules/reagents/chems/chems_cleaner.dm index be755c43cfb..f4d682544c1 100644 --- a/code/modules/reagents/chems/chems_cleaner.dm +++ b/code/modules/reagents/chems/chems_cleaner.dm @@ -6,4 +6,5 @@ touch_met = 50 value = 0.15 // shelf price of bug spray per ml, cleaner in general is too cheap dirtiness = DIRTINESS_CLEAN + turf_touch_threshold = 0.1 uid = "chem_cleaner" diff --git a/code/modules/reagents/chems/chems_compounds.dm b/code/modules/reagents/chems/chems_compounds.dm index 0bc7fd0a212..d8768dcbd58 100644 --- a/code/modules/reagents/chems/chems_compounds.dm +++ b/code/modules/reagents/chems/chems_compounds.dm @@ -28,7 +28,7 @@ var/mob/living/carbon/human/H = M H.update_eyes() -/decl/material/liquid/glowsap/on_leaving_metabolism(mob/parent, metabolism_class) +/decl/material/liquid/glowsap/on_leaving_metabolism(atom/parent, metabolism_class) if(ishuman(parent)) var/mob/living/carbon/human/H = parent addtimer(CALLBACK(H, /mob/living/carbon/human/proc/update_eyes), 5 SECONDS) @@ -379,6 +379,7 @@ color = "#c8a5dc" touch_met = 5 dirtiness = DIRTINESS_STERILE + turf_touch_threshold = 0.1 uid = "chem_antiseptic" /decl/material/liquid/crystal_agent diff --git a/code/modules/reagents/chems/chems_drugs.dm b/code/modules/reagents/chems/chems_drugs.dm index 4adf10c06aa..ec97f58af2f 100644 --- a/code/modules/reagents/chems/chems_drugs.dm +++ b/code/modules/reagents/chems/chems_drugs.dm @@ -202,7 +202,7 @@ /decl/material/liquid/glowsap/gleam/affect_blood(var/mob/living/M, var/alien, var/removed, var/datum/reagents/holder) . = ..() - M.add_client_color(/datum/client_color/thirdeye) + M.add_client_color(/datum/client_color/noir/thirdeye) M.add_chemical_effect(CE_THIRDEYE, 1) M.add_chemical_effect(CE_MIND, -2) M.set_hallucination(50, 50) @@ -215,9 +215,11 @@ if(prob(5)) to_chat(M, SPAN_WARNING("[pick(dose_messages)]")) -/decl/material/liquid/glowsap/gleam/on_leaving_metabolism(var/mob/parent, var/metabolism_class) +/decl/material/liquid/glowsap/gleam/on_leaving_metabolism(var/atom/parent, var/metabolism_class) . = ..() - parent.remove_client_color(/datum/client_color/thirdeye) + var/mob/M = parent + if(istype(M)) + M.remove_client_color(/datum/client_color/noir/thirdeye) /decl/material/liquid/glowsap/gleam/affect_overdose(var/mob/living/M, var/alien, var/datum/reagents/holder) M.adjustBrainLoss(rand(1, 5)) diff --git a/code/modules/reagents/chems/chems_ethanol.dm b/code/modules/reagents/chems/chems_ethanol.dm index 70a1e83fcd5..ed88ce039db 100644 --- a/code/modules/reagents/chems/chems_ethanol.dm +++ b/code/modules/reagents/chems/chems_ethanol.dm @@ -99,7 +99,7 @@ uid = "chem_ethanol_absinthe" /decl/material/liquid/ethanol/ale - name = "ale" + name = "brown ale" lore_text = "A dark alchoholic beverage made by malted barley and yeast." taste_description = "hearty barley ale" color = "#4c3100" @@ -112,6 +112,7 @@ /decl/material/liquid/ethanol/beer name = "beer" + codex_name = "plain beer" lore_text = "An alcoholic beverage made from malted grains, hops, yeast, and water." taste_description = "piss water" color = "#ffd300" @@ -125,6 +126,7 @@ /decl/material/liquid/ethanol/beer/good uid = "chem_ethanol_beer_good" + codex_name = "premium beer" taste_description = "beer" /decl/material/liquid/ethanol/beer/affect_ingest(var/mob/living/M, var/alien, var/removed, var/datum/reagents/holder) @@ -160,7 +162,7 @@ glass_desc = "Damn, you feel like some kind of French aristocrat just by holding this." /decl/material/liquid/ethanol/gin - name = "gin" + name = "plain gin" lore_text = "It's gin. In space. I say, good sir." taste_description = "an alcoholic christmas tree" color = "#0064c6" @@ -173,7 +175,16 @@ //Base type for alchoholic drinks containing coffee /decl/material/liquid/ethanol/coffee - abstract_type = /decl/material/liquid/ethanol/coffee + name = "coffee liqueur" + lore_text = "A widely known, Mexican coffee-flavoured liqueur. In production since 1936!" + taste_description = "spiked coffee" + taste_mult = 1.1 + color = "#4c3100" + strength = 15 + exoplanet_rarity = MAT_RARITY_NOWHERE + uid = "chem_ethanol_coffee" + glass_name = "coffee liqueur" + glass_desc = "Guaranteed to perk you up." overdose = 45 /decl/material/liquid/ethanol/coffee/affect_ingest(var/mob/living/M, var/alien, var/removed, var/datum/reagents/holder) @@ -191,19 +202,6 @@ /decl/material/liquid/ethanol/coffee/affect_overdose(var/mob/living/M, var/alien, var/datum/reagents/holder) ADJ_STATUS(M, STAT_JITTER, 5) -/decl/material/liquid/ethanol/coffee/kahlua - name = "coffee liqueur" - lore_text = "A widely known, Mexican coffee-flavoured liqueur. In production since 1936!" - taste_description = "spiked coffee" - taste_mult = 1.1 - color = "#4c3100" - strength = 15 - exoplanet_rarity = MAT_RARITY_NOWHERE - uid = "chem_ethanol_coffee" - - glass_name = "RR coffee liquor" - glass_desc = "DAMN, THIS THING LOOKS ROBUST" - /decl/material/liquid/ethanol/melonliquor name = "melon liqueur" lore_text = "A relatively sweet and fruity 46 proof liqueur." @@ -217,7 +215,7 @@ glass_desc = "A relatively sweet and fruity 46 proof liquor." /decl/material/liquid/ethanol/rum - name = "rum" + name = "dark rum" lore_text = "Yohoho and all that." taste_description = "spiked butterscotch" taste_mult = 1.1 @@ -293,6 +291,7 @@ /decl/material/liquid/ethanol/vodka name = "vodka" + codex_name = "plain vodka" lore_text = "Number one drink AND fueling choice for Independents around the galaxy." taste_description = "grain alcohol" color = "#0064c8" // rgb: 0, 100, 200 @@ -305,6 +304,7 @@ /decl/material/liquid/ethanol/vodka/premium name = "premium vodka" + codex_name = null lore_text = "Premium distilled vodka imported directly from the Gilgamesh Colonial Confederation." taste_description = "clear kvass" color = "#aaddff" // rgb: 170, 221, 255 - very light blue. @@ -313,7 +313,7 @@ uid = "chem_ethanol_premiumvodka" /decl/material/liquid/ethanol/whiskey - name = "whiskey" + name = "malt whiskey" lore_text = "A superb and well-aged single-malt whiskey. Damn." taste_description = "molasses" color = "#4c3100" @@ -325,7 +325,7 @@ glass_desc = "The silky, smokey whiskey goodness inside the glass makes the drink look very classy." /decl/material/liquid/ethanol/wine - name = "wine" + name = "red wine" lore_text = "An premium alchoholic beverage made from distilled grape juice." taste_description = "bitter sweetness" color = "#7e4043" // rgb: 126, 64, 67 @@ -333,7 +333,7 @@ exoplanet_rarity = MAT_RARITY_NOWHERE uid = "chem_ethanol_wine" - glass_name = "wine" + glass_name = "red wine" glass_desc = "A very classy looking drink." /decl/material/liquid/ethanol/wine/premium @@ -439,7 +439,7 @@ else L.take_internal_damage(100, 0) -/decl/material/liquid/ethanol/aged_whiskey // I have no idea what this is and where it comes from. //It comes from Dinnlan now +/decl/material/liquid/ethanol/aged_whiskey // I have no idea what this is and where it comes from. //It comes from Dinnlan now name = "aged whiskey" lore_text = "A well-aged whiskey of high quality. Probably imported. Just a sip'll do it, but that burn will leave you wanting more." color = "#523600" diff --git a/code/modules/reagents/chems/random/chems_random.dm b/code/modules/reagents/chems/random/chems_random.dm index f27969acdb1..50a8ddf1da9 100644 --- a/code/modules/reagents/chems/random/chems_random.dm +++ b/code/modules/reagents/chems/random/chems_random.dm @@ -21,7 +21,7 @@ var/global/list/random_chem_interaction_blacklist = list( uid = "chem_random" var/max_effect_number = 8 var/list/data = list() - var/initialized = FALSE + var/data_initialized = FALSE /decl/material/liquid/random/proc/randomize_data(temperature) data = list() @@ -50,7 +50,7 @@ var/global/list/random_chem_interaction_blacklist = list( for(var/i in 1 to heat_num) heating_products[pick_n_take(whitelist)] = 1 / heat_num - initialized = TRUE + data_initialized = TRUE /decl/material/liquid/random/proc/stable_at_temperature(temperature) if(temperature > chilling_point && temperature < heating_point) diff --git a/code/modules/reagents/cocktails.dm b/code/modules/reagents/cocktails.dm index 0cb08f578fb..291c5394f81 100644 --- a/code/modules/reagents/cocktails.dm +++ b/code/modules/reagents/cocktails.dm @@ -67,7 +67,7 @@ ) /decl/cocktail/classic_martini - name = "martini" + name = "gin martini" description = "Vermouth with gin. The classiest of all cocktails." ratios = list( /decl/material/liquid/ethanol/gin = 0.4, @@ -120,7 +120,7 @@ description = "Similar to a white Russian, but fit for the lactose-intolerant." ratios = list( /decl/material/liquid/ethanol/vodka = 0.4, - /decl/material/liquid/ethanol/coffee/kahlua = 0.2 + /decl/material/liquid/ethanol/coffee = 0.2 ) /decl/cocktail/white_russian @@ -128,7 +128,7 @@ description = "A straightforward cocktail of coffee liqueur and vodka. Popular in a lot of places, but that's just, like, an opinion, man." ratios = list( /decl/material/liquid/ethanol/vodka = 0.3, - /decl/material/liquid/ethanol/coffee/kahlua = 0.15, + /decl/material/liquid/ethanol/coffee = 0.15, /decl/material/liquid/drink/milk/cream = 0.15 ) @@ -165,7 +165,7 @@ description = "A strong cocktail of tequila and coffee liquor." ratios = list( /decl/material/liquid/ethanol/tequila = 0.45, - /decl/material/liquid/ethanol/coffee/kahlua = 0.25 + /decl/material/liquid/ethanol/coffee = 0.25 ) /decl/cocktail/toxins_special @@ -219,7 +219,7 @@ ratios = list( /decl/material/liquid/ethanol/cognac = 0.3, /decl/material/liquid/ethanol/irish_cream = 0.2, - /decl/material/liquid/ethanol/coffee/kahlua = 0.2 + /decl/material/liquid/ethanol/coffee = 0.2 ) /decl/cocktail/atomicbomb @@ -228,7 +228,7 @@ ratios = list( /decl/material/liquid/ethanol/cognac = 0.3, /decl/material/liquid/ethanol/irish_cream = 0.2, - /decl/material/liquid/ethanol/coffee/kahlua = 0.2, + /decl/material/liquid/ethanol/coffee = 0.2, /decl/material/solid/metal/uranium ) @@ -440,7 +440,7 @@ ratios = list( /decl/material/liquid/ethanol/rum = 0.4, /decl/material/liquid/blood = 0.1, - /decl/material/liquid/ethanol/coffee/kahlua = 0.2 + /decl/material/liquid/ethanol/coffee = 0.2 ) /decl/cocktail/hippiesdelight diff --git a/code/modules/reagents/dispenser/cartridge_presets.dm b/code/modules/reagents/dispenser/cartridge_presets.dm index 20fd994a2e4..2cfeaf0cbe4 100644 --- a/code/modules/reagents/dispenser/cartridge_presets.dm +++ b/code/modules/reagents/dispenser/cartridge_presets.dm @@ -54,7 +54,7 @@ /obj/item/chems/chem_disp_cartridge/beer spawn_reagent = /decl/material/liquid/ethanol/beer /obj/item/chems/chem_disp_cartridge/kahlua - spawn_reagent = /decl/material/liquid/ethanol/coffee/kahlua + spawn_reagent = /decl/material/liquid/ethanol/coffee /obj/item/chems/chem_disp_cartridge/whiskey spawn_reagent = /decl/material/liquid/ethanol/whiskey /obj/item/chems/chem_disp_cartridge/wine 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/reactions/_reaction.dm b/code/modules/reagents/reactions/_reaction.dm index 854687aae43..54d298f7e17 100644 --- a/code/modules/reagents/reactions/_reaction.dm +++ b/code/modules/reagents/reactions/_reaction.dm @@ -37,7 +37,7 @@ /decl/chemical_reaction/proc/on_reaction(var/datum/reagents/holder, var/created_volume, var/reaction_flags) var/atom/location = holder.get_reaction_loc() - if(thermal_product && location && ATOM_IS_TEMPERATURE_SENSITIVE(location)) + if(thermal_product && location && ATOM_SHOULD_TEMPERATURE_ENQUEUE(location)) ADJUST_ATOM_TEMPERATURE(location, thermal_product) // This proc returns a list of all reagents it wants to use; if the holder has several reactions that use the same reagent, it will split the reagent evenly between them diff --git a/code/modules/reagents/reactions/reaction_alcohol.dm b/code/modules/reagents/reactions/reaction_alcohol.dm index f8127ee3edd..48e0e426967 100644 --- a/code/modules/reagents/reactions/reaction_alcohol.dm +++ b/code/modules/reagents/reactions/reaction_alcohol.dm @@ -14,7 +14,7 @@ result_amount = 10 /decl/chemical_reaction/recipe/wine - name = "Wine" + name = "Red Wine" result = /decl/material/liquid/ethanol/wine required_reagents = list(/decl/material/liquid/drink/juice/grape = 10) catalysts = list(/decl/material/liquid/enzyme = 5) @@ -46,7 +46,7 @@ mix_message = "The solution roils as it rapidly ferments into a shockingly blue liquor." /decl/chemical_reaction/recipe/beer - name = "Beer" + name = "Plain Beer" result = /decl/material/liquid/ethanol/beer required_reagents = list(/decl/material/liquid/nutriment/cornoil = 10) catalysts = list(/decl/material/liquid/enzyme = 5) @@ -79,7 +79,7 @@ /decl/chemical_reaction/recipe/kahlua name = "Kahlua" - result = /decl/material/liquid/ethanol/coffee/kahlua + result = /decl/material/liquid/ethanol/coffee required_reagents = list(/decl/material/liquid/drink/coffee = 5, /decl/material/liquid/nutriment/sugar = 5) catalysts = list(/decl/material/liquid/enzyme = 5) result_amount = 5 diff --git a/code/modules/reagents/reactions/reaction_alloys.dm b/code/modules/reagents/reactions/reaction_alloys.dm index 64156d27fb8..1d0ad029c31 100644 --- a/code/modules/reagents/reactions/reaction_alloys.dm +++ b/code/modules/reagents/reactions/reaction_alloys.dm @@ -14,7 +14,7 @@ result_amount = 3 /decl/chemical_reaction/alloy/steel - name = "Steel" + name = "Carbon Steel Alloy" result = /decl/material/solid/metal/steel required_reagents = list( /decl/material/solid/metal/iron = 1, @@ -23,7 +23,7 @@ result_amount = 2 /decl/chemical_reaction/alloy/plasteel - name = "Plasteel" + name = "Plasteel Alloy" result = /decl/material/solid/metal/plasteel required_reagents = list( /decl/material/solid/metal/steel = 2, @@ -41,7 +41,7 @@ result_amount = 3 /decl/chemical_reaction/alloy/bronze - name = "Bronze" + name = "Bronze Alloy" result = /decl/material/solid/metal/bronze required_reagents = list( /decl/material/solid/metal/copper = 4, @@ -50,7 +50,7 @@ result_amount = 5 /decl/chemical_reaction/alloy/brass - name = "Brass" + name = "Brass Alloy" result = /decl/material/solid/metal/brass required_reagents = list( /decl/material/solid/metal/copper = 2, @@ -59,7 +59,7 @@ result_amount = 3 /decl/chemical_reaction/alloy/blackbronze - name = "Black Bronze" + name = "Black Bronze Billon" result = /decl/material/solid/metal/blackbronze required_reagents = list( /decl/material/solid/metal/copper = 2, @@ -68,7 +68,7 @@ result_amount = 3 /decl/chemical_reaction/alloy/redgold - name = "Red Gold" + name = "Red Gold Billon" result = /decl/material/solid/metal/redgold required_reagents = list( /decl/material/solid/metal/copper = 2, @@ -77,7 +77,7 @@ result_amount = 3 /decl/chemical_reaction/alloy/stainlesssteel - name = "Stainless Steel" + name = "Stainless Steel Alloy" result = /decl/material/solid/metal/stainlesssteel required_reagents = list( /decl/material/solid/metal/steel = 9, diff --git a/code/modules/reagents/reactions/reaction_cafe.dm b/code/modules/reagents/reactions/reaction_cafe.dm index cb8d2771ba7..41b424be5de 100644 --- a/code/modules/reagents/reactions/reaction_cafe.dm +++ b/code/modules/reagents/reactions/reaction_cafe.dm @@ -2,36 +2,36 @@ hidden_from_codex = FALSE /decl/chemical_reaction/recipe/cafe/coffee - name = "Coffee" + name = "Brewed Coffee" result = /decl/material/liquid/drink/coffee required_reagents = list(/decl/material/liquid/water = 5, /decl/material/liquid/nutriment/coffee = 1) result_amount = 5 minimum_temperature = 70 CELSIUS maximum_temperature = (70 CELSIUS) + 100 - mix_message = "The solution thickens into a steaming dark brown beverage." + mix_message = "The solution darkens to nearly black." /decl/chemical_reaction/recipe/cafe/coffee/instant name = "Instant Coffee" required_reagents = list(/decl/material/liquid/water = 5, /decl/material/liquid/nutriment/coffee/instant = 1) maximum_temperature = INFINITY minimum_temperature = 0 - mix_message = "The solution thickens into dark brown beverage." + mix_message = "The solution darkens to nearly black." /decl/chemical_reaction/recipe/cafe/tea - name = "Black tea" + name = "Steeped Black tea" result = /decl/material/liquid/drink/tea/black required_reagents = list(/decl/material/liquid/water = 5, /decl/material/liquid/nutriment/tea = 1) result_amount = 5 minimum_temperature = 70 CELSIUS maximum_temperature = (70 CELSIUS) + 100 - mix_message = "The solution thickens into a steaming black beverage." + mix_message = "The solution darkens to a rich brown." /decl/chemical_reaction/recipe/cafe/tea/instant name = "Instant Black tea" required_reagents = list(/decl/material/liquid/water = 5, /decl/material/liquid/nutriment/tea/instant = 1) maximum_temperature = INFINITY minimum_temperature = 0 - mix_message = "The solution thickens into black beverage." + mix_message = "The solution darkens to a rich brown." /decl/chemical_reaction/recipe/cafe/hot_coco name = "Hot Coco" diff --git a/code/modules/reagents/reactions/reaction_grenade_reaction.dm b/code/modules/reagents/reactions/reaction_grenade_reaction.dm index c5d66c61625..963eb2f84b5 100644 --- a/code/modules/reagents/reactions/reaction_grenade_reaction.dm +++ b/code/modules/reagents/reactions/reaction_grenade_reaction.dm @@ -102,7 +102,7 @@ holder.clear_reagents() /decl/chemical_reaction/grenade_reaction/foam - name = "Foam" + name = "Expanding Foam" lore_text = "This mixture explodes in a burst of foam. Good for cleaning!" required_reagents = list(/decl/material/liquid/surfactant = 1, /decl/material/liquid/water = 1) result_amount = 2 diff --git a/code/modules/reagents/reactions/reaction_other.dm b/code/modules/reagents/reactions/reaction_other.dm index 78c71759697..680e3564aa1 100644 --- a/code/modules/reagents/reactions/reaction_other.dm +++ b/code/modules/reagents/reactions/reaction_other.dm @@ -32,7 +32,7 @@ mix_message = "The solution thickens slowly into a glossy liquid." /decl/chemical_reaction/anfo - name = "ANFO" + name = "Fertilizer ANFO" result = /decl/material/liquid/anfo required_reagents = list( /decl/material/liquid/fertilizer = 20, @@ -62,7 +62,7 @@ mix_message = "The solution gives off the eye-watering reek of spilled fertilizer and petroleum." /decl/chemical_reaction/crystal_agent - name = "Crystallizing agent" + name = "Crystallizing Agent" result = /decl/material/liquid/crystal_agent required_reagents = list(/decl/material/solid/silicon = 1, /decl/material/solid/metal/tungsten = 1, /decl/material/liquid/acid/polyacid = 1) minimum_temperature = 150 CELSIUS diff --git a/code/modules/reagents/reactions/reaction_recipe.dm b/code/modules/reagents/reactions/reaction_recipe.dm index 6748eb14353..13ae368813f 100644 --- a/code/modules/reagents/reactions/reaction_recipe.dm +++ b/code/modules/reagents/reactions/reaction_recipe.dm @@ -43,7 +43,7 @@ //batter reaction as food precursor, for things that don't use pliable dough precursor. /decl/chemical_reaction/recipe/batter - name = "Batter" + name = "Plain Batter" result = /decl/material/liquid/nutriment/batter required_reagents = list(/decl/material/liquid/nutriment/protein/egg = 3, /decl/material/liquid/nutriment/flour = 5, /decl/material/liquid/drink/milk = 5) result_amount = 10 diff --git a/code/modules/reagents/reactions/reaction_recipe_food.dm b/code/modules/reagents/reactions/reaction_recipe_food.dm index d4f3a62fc74..f3a74c8fbc7 100644 --- a/code/modules/reagents/reactions/reaction_recipe_food.dm +++ b/code/modules/reagents/reactions/reaction_recipe_food.dm @@ -27,7 +27,7 @@ obj_result = /obj/item/chems/food/rawmeatball /decl/chemical_reaction/recipe/food/dough - name = "Dough" + name = "Plain dough" required_reagents = list(/decl/material/liquid/nutriment/protein/egg = 3, /decl/material/liquid/nutriment/flour = 10, /decl/material/liquid/water = 10) mix_message = "The solution folds and thickens into a large ball of dough." obj_result = /obj/item/chems/food/dough diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm index 9796878f6a8..27523ae2df8 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghydro.dm @@ -121,7 +121,7 @@ possible_transfer_amounts = @"[5,10,20,30]" reagent_ids = list( /decl/material/liquid/ethanol/beer, - /decl/material/liquid/ethanol/coffee/kahlua, + /decl/material/liquid/ethanol/coffee, /decl/material/liquid/ethanol/whiskey, /decl/material/liquid/ethanol/wine, /decl/material/liquid/ethanol/vodka, @@ -151,7 +151,7 @@ /decl/material/liquid/drink/tea/green, /decl/material/liquid/drink/citrussoda, /decl/material/liquid/ethanol/beer, - /decl/material/liquid/ethanol/coffee/kahlua + /decl/material/liquid/ethanol/coffee ) /obj/item/chems/borghypo/service/attack(var/mob/M, var/mob/user) diff --git a/code/modules/reagents/reagent_containers/drinks/bottle.dm b/code/modules/reagents/reagent_containers/drinks/bottle.dm index b9fc59a144f..700eaaa907f 100644 --- a/code/modules/reagents/reagent_containers/drinks/bottle.dm +++ b/code/modules/reagents/reagent_containers/drinks/bottle.dm @@ -338,7 +338,7 @@ /obj/item/chems/drinks/bottle/kahlua/Initialize() . = ..() - reagents.add_reagent(/decl/material/liquid/ethanol/coffee/kahlua, 100) + reagents.add_reagent(/decl/material/liquid/ethanol/coffee, 100) /obj/item/chems/drinks/bottle/goldschlager name = "College Girl Goldschlager" diff --git a/code/modules/reagents/reagent_containers/food/burgers.dm b/code/modules/reagents/reagent_containers/food/burgers.dm index 36266beac77..cba952a9db0 100644 --- a/code/modules/reagents/reagent_containers/food/burgers.dm +++ b/code/modules/reagents/reagent_containers/food/burgers.dm @@ -55,21 +55,7 @@ . = ..() reagents.add_reagent(/decl/material/liquid/nutriment/protein, 2) -/obj/item/chems/food/meatburger - name = "burger" - desc = "The cornerstone of every nutritious breakfast." - icon_state = "hburger" - filling_color = "#d63c3c" - center_of_mass = @"{'x':16,'y':11}" - nutriment_desc = list("bun" = 2) - nutriment_amt = 3 - bitesize = 2 - -/obj/item/chems/food/meatburger/Initialize() - . = ..() - reagents.add_reagent(/decl/material/liquid/nutriment/protein, 3) - -/obj/item/chems/food/plainburger +/obj/item/chems/food/burger name = "burger" desc = "The cornerstone of every nutritious breakfast." icon = 'icons/obj/food_ingredients.dmi' @@ -80,7 +66,7 @@ nutriment_amt = 3 bitesize = 2 -/obj/item/chems/food/plainburger/Initialize() +/obj/item/chems/food/burger/Initialize() . = ..() reagents.add_reagent(/decl/material/liquid/nutriment/protein, 3) diff --git a/code/modules/reagents/reagent_containers/food/dough.dm b/code/modules/reagents/reagent_containers/food/dough.dm index 4a28efd26e8..2fbc32c08d6 100644 --- a/code/modules/reagents/reagent_containers/food/dough.dm +++ b/code/modules/reagents/reagent_containers/food/dough.dm @@ -56,12 +56,12 @@ nutriment_amt = 4 nutriment_type = /decl/material/liquid/nutriment/bread attack_products = list( - /obj/item/chems/food/meatball = /obj/item/chems/food/plainburger, + /obj/item/chems/food/meatball = /obj/item/chems/food/burger, /obj/item/chems/food/cutlet = /obj/item/chems/food/hamburger, /obj/item/chems/food/sausage = /obj/item/chems/food/hotdog ) -/obj/item/chems/food/plainburger/attack_products = list(/obj/item/chems/food/cheesewedge = /obj/item/chems/food/cheeseburger) +/obj/item/chems/food/burger/attack_products = list(/obj/item/chems/food/cheesewedge = /obj/item/chems/food/cheeseburger) /obj/item/chems/food/hamburger/attack_products = list(/obj/item/chems/food/cheesewedge = /obj/item/chems/food/cheeseburger) /obj/item/chems/food/human/burger/attack_products = list(/obj/item/chems/food/cheesewedge = /obj/item/chems/food/cheeseburger) diff --git a/code/modules/reagents/reagent_containers/food/junkfood.dm b/code/modules/reagents/reagent_containers/food/junkfood.dm index 19565fa3e98..826701c271a 100644 --- a/code/modules/reagents/reagent_containers/food/junkfood.dm +++ b/code/modules/reagents/reagent_containers/food/junkfood.dm @@ -295,38 +295,28 @@ desc = "Goes great with Robust Coffee." icon_state = "donut1" filling_color = "#d9c386" - var/overlay_state = "box-donut1" - center_of_mass = @"{'x':13,'y':16}" + center_of_mass = @"{'x':19,'y':16}" nutriment_desc = list("sweetness", "donut") - nutriment_type = /decl/material/liquid/nutriment/bread - -/obj/item/chems/food/donut/normal - name = "donut" - desc = "Goes great with Robust Coffee." - icon_state = "donut1" nutriment_amt = 3 bitesize = 3 nutriment_type = /decl/material/liquid/nutriment/bread + var/overlay_state = "box-donut1" + var/donut_state = "donut" /obj/item/chems/food/donut/normal/Initialize() . = ..() - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 1) - if(prob(30)) - src.icon_state = "donut2" - src.overlay_state = "box-donut2" - src.SetName("frosted donut") - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 2) - center_of_mass = @"{'x':19,'y':16}" + icon_state = "[donut_state]2" + overlay_state = "box-donut2" + SetName("frosted [name]]") + reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 3) /obj/item/chems/food/donut/chaos name = "chaos donut" desc = "Like life, it never quite tastes the same." - icon_state = "donut1" filling_color = "#ed11e6" nutriment_amt = 2 bitesize = 10 - nutriment_type = /decl/material/liquid/nutriment/bread /obj/item/chems/food/donut/chaos/proc/get_random_fillings() . = list( @@ -344,55 +334,32 @@ /obj/item/chems/food/donut/chaos/Initialize() . = ..() - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 1) reagents.add_reagent(pick(get_random_fillings()), 3) - if(prob(30)) - src.icon_state = "donut2" - src.overlay_state = "box-donut2" - src.SetName("frosted chaos donut") - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 2) /obj/item/chems/food/donut/jelly - name = "jelly donut" + name = "berry jelly donut" desc = "You jelly?" icon_state = "jdonut1" - filling_color = "#ed1169" + filling_color = "#990066" center_of_mass = @"{'x':16,'y':11}" nutriment_amt = 3 bitesize = 5 nutriment_type = /decl/material/liquid/nutriment/bread + donut_state = "jdonut" + var/jelly_type = /decl/material/liquid/drink/juice/berry /obj/item/chems/food/donut/jelly/Initialize() . = ..() - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 1) - reagents.add_reagent(/decl/material/liquid/nutriment/cherryjelly, 5) - if(prob(30)) - src.icon_state = "jdonut2" - src.overlay_state = "box-donut2" - src.SetName("frosted jelly donut") - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 2) + reagents.add_reagent(jelly_type, 5) -/obj/item/chems/food/donut/cherryjelly - name = "jelly donut" +/obj/item/chems/food/donut/jelly/cherry + name = "cherry jelly donut" desc = "You jelly?" icon_state = "jdonut1" - filling_color = "#ed1169" - center_of_mass = @"{'x':16,'y':11}" - nutriment_amt = 3 - bitesize = 5 - -/obj/item/chems/food/donut/cherryjelly/Initialize() - . = ..() - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 1) - reagents.add_reagent(/decl/material/liquid/nutriment/cherryjelly, 5) - if(prob(30)) - src.icon_state = "jdonut2" - src.overlay_state = "box-donut2" - src.SetName("frosted jelly donut") - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 2) + filling_color = "#801e28" + jelly_type = /decl/material/liquid/nutriment/cherryjelly //Sol Vendor - /obj/item/chems/food/lunacake name = "moon cake" icon_state = "lunacake_wrapped" diff --git a/code/modules/reagents/reagent_containers/food/lunch.dm b/code/modules/reagents/reagent_containers/food/lunch.dm index dcd8b49e07e..faf62483b65 100644 --- a/code/modules/reagents/reagent_containers/food/lunch.dm +++ b/code/modules/reagents/reagent_containers/food/lunch.dm @@ -15,7 +15,6 @@ var/global/list/lunchables_lunches_ = list( var/global/list/lunchables_snacks_ = list( /obj/item/chems/food/donut/jelly, - /obj/item/chems/food/donut/cherryjelly, /obj/item/chems/food/muffin, /obj/item/chems/food/popcorn, /obj/item/chems/food/sosjerky, diff --git a/code/modules/reagents/reagent_containers/food/meat/meat.dm b/code/modules/reagents/reagent_containers/food/meat/meat.dm index d9e01fb050d..e9e85d0b6a7 100644 --- a/code/modules/reagents/reagent_containers/food/meat/meat.dm +++ b/code/modules/reagents/reagent_containers/food/meat/meat.dm @@ -176,7 +176,7 @@ reagents.add_reagent(/decl/material/liquid/nutriment/protein, 6) /obj/item/chems/food/fatsausage - name = "fat sausage" + name = "spiced sausage" desc = "A piece of mixed, long meat, with some bite to it." icon_state = "sausage" filling_color = "#db0000" 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/recycling/conveyor2.dm b/code/modules/recycling/conveyor2.dm index 2f8b3f3873f..6b208f7cd99 100644 --- a/code/modules/recycling/conveyor2.dm +++ b/code/modules/recycling/conveyor2.dm @@ -264,7 +264,7 @@ var/global/list/all_conveyor_switches = list() qdel(src) /obj/item/conveyor_switch_construct - name = "conveyor switch assembly" + name = "two-way conveyor switch assembly" desc = "A conveyor control switch assembly." icon = 'icons/obj/recycling.dmi' icon_state = "switch-off" diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index b79983979b8..d6f1cc65e18 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -129,6 +129,9 @@ var/global/list/diversion_junctions = list() // Todo rewrite all of this. var/atom/movable/AM = dropping + if(AM.anchored) + return FALSE + // Determine object type and run necessary checks var/mob/M = AM var/is_dangerous // To determine css style in messages diff --git a/code/modules/research/design_console.dm b/code/modules/research/design_console.dm index d08220379ed..ed23ec3de69 100644 --- a/code/modules/research/design_console.dm +++ b/code/modules/research/design_console.dm @@ -94,7 +94,7 @@ data["tech_levels"] = show_tech_levels var/list/found_databases = list() - for(var/obj/machinery/design_database/db in network?.get_devices_by_type(/obj/machinery/design_database, user)) + for(var/obj/machinery/design_database/db in network?.get_devices_by_type(/obj/machinery/design_database, user.GetAccess())) var/list/database = list("name" = db.name, "ref" = "\ref[db]") if(db.stat & (BROKEN|NOPOWER)) database["status"] = "Offline" @@ -105,7 +105,7 @@ data["connected_databases"] = found_databases var/list/found_analyzers = list() - for(var/obj/machinery/destructive_analyzer/az in network?.get_devices_by_type(/obj/machinery/destructive_analyzer, user)) + for(var/obj/machinery/destructive_analyzer/az in network?.get_devices_by_type(/obj/machinery/destructive_analyzer, user.GetAccess())) var/list/analyzer = list("name" = az.name, "ref" = "\ref[az]") if(az.stat & (BROKEN|NOPOWER)) analyzer["status"] = "Offline" diff --git a/code/modules/shield_generators/shield.dm b/code/modules/shield_generators/shield.dm index 42317b4ada8..709d2a16371 100644 --- a/code/modules/shield_generators/shield.dm +++ b/code/modules/shield_generators/shield.dm @@ -9,7 +9,7 @@ density = 1 invisibility = 0 atmos_canpass = CANPASS_PROC - var/obj/machinery/power/shield_generator/gen = null + var/obj/machinery/shield_generator/gen = null var/disabled_for = 0 var/diffused_for = 0 @@ -267,34 +267,34 @@ explosion_resistance = 0 // Shield collision checks below -/atom/movable/proc/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/atom/movable/proc/can_pass_shield(var/obj/machinery/shield_generator/gen) return 1 // Other mobs -/mob/living/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/mob/living/can_pass_shield(var/obj/machinery/shield_generator/gen) return !gen.check_flag(MODEFLAG_NONHUMANS) // Human mobs -/mob/living/carbon/human/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/mob/living/carbon/human/can_pass_shield(var/obj/machinery/shield_generator/gen) if(isSynthetic()) return !gen.check_flag(MODEFLAG_ANORGANIC) return !gen.check_flag(MODEFLAG_HUMANOIDS) // Silicon mobs -/mob/living/silicon/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/mob/living/silicon/can_pass_shield(var/obj/machinery/shield_generator/gen) return !gen.check_flag(MODEFLAG_ANORGANIC) // Generic objects. Also applies to bullets and meteors. -/obj/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/obj/can_pass_shield(var/obj/machinery/shield_generator/gen) return !gen.check_flag(MODEFLAG_HYPERKINETIC) // Beams -/obj/item/projectile/beam/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/obj/item/projectile/beam/can_pass_shield(var/obj/machinery/shield_generator/gen) return !gen.check_flag(MODEFLAG_PHOTONIC) // Beams -/obj/item/projectile/ship_munition/energy/can_pass_shield(var/obj/machinery/power/shield_generator/gen) +/obj/item/projectile/ship_munition/energy/can_pass_shield(var/obj/machinery/shield_generator/gen) return !gen.check_flag(MODEFLAG_PHOTONIC) // Shield on-impact logic here. This is called only if the object is actually blocked by the field (can_pass_shield applies first) diff --git a/code/modules/shield_generators/shield_generator.dm b/code/modules/shield_generators/shield_generator.dm index c59441e556d..92c40c81f95 100644 --- a/code/modules/shield_generators/shield_generator.dm +++ b/code/modules/shield_generators/shield_generator.dm @@ -1,13 +1,14 @@ -/obj/machinery/power/shield_generator +/obj/machinery/shield_generator name = "advanced shield generator" desc = "A heavy-duty shield generator and capacitor, capable of generating energy shields at large distances." icon = 'icons/obj/machines/shielding.dmi' icon_state = "generator0" density = 1 - base_type = /obj/machinery/power/shield_generator + base_type = /obj/machinery/shield_generator construct_state = /decl/machine_construction/default/panel_closed wires = /datum/wires/shield_generator - uncreated_component_parts = null + uncreated_component_parts = list(/obj/item/stock_parts/power/terminal) + stock_part_presets = list(/decl/stock_part_preset/terminal_setup) stat_immune = 0 var/list/field_segments = list() // List of all shield segments owned by this generator. @@ -43,16 +44,15 @@ var/obj/effect/overmap/visitable/last_linked_overmap_object -/obj/machinery/power/shield_generator/on_update_icon() +/obj/machinery/shield_generator/on_update_icon() if(running) icon_state = "generator1" else icon_state = "generator0" -/obj/machinery/power/shield_generator/Initialize() +/obj/machinery/shield_generator/Initialize() ..() - connect_to_network() mode_list = list() for(var/st in subtypesof(/datum/shield_mode/)) var/datum/shield_mode/SM = new st() @@ -60,11 +60,11 @@ events_repository.register(/decl/observ/moved, src, src, .proc/update_overmap_shield_list) . = INITIALIZE_HINT_LATELOAD -/obj/machinery/power/shield_generator/LateInitialize() +/obj/machinery/shield_generator/LateInitialize() . = ..() update_overmap_shield_list() -/obj/machinery/power/shield_generator/Destroy() +/obj/machinery/shield_generator/Destroy() shutdown_field() field_segments = null damaged_segments = null @@ -73,16 +73,16 @@ . = ..() update_overmap_shield_list() -/obj/machinery/power/shield_generator/proc/update_overmap_shield_list() +/obj/machinery/shield_generator/proc/update_overmap_shield_list() var/obj/effect/overmap/visitable/current_overmap_object = get_owning_overmap_object() if(current_overmap_object != last_linked_overmap_object) if(last_linked_overmap_object) - last_linked_overmap_object.unregister_machine(src, /obj/machinery/power/shield_generator) + last_linked_overmap_object.unregister_machine(src, /obj/machinery/shield_generator) last_linked_overmap_object = current_overmap_object if(last_linked_overmap_object) - last_linked_overmap_object.register_machine(src, /obj/machinery/power/shield_generator) + last_linked_overmap_object.register_machine(src, /obj/machinery/shield_generator) -/obj/machinery/power/shield_generator/RefreshParts() +/obj/machinery/shield_generator/RefreshParts() max_energy = 0 full_shield_strength = 0 for(var/obj/item/stock_parts/smes_coil/S in component_parts) @@ -98,7 +98,7 @@ // Shuts down the shield, removing all shield segments and unlocking generator settings. -/obj/machinery/power/shield_generator/proc/shutdown_field() +/obj/machinery/shield_generator/proc/shutdown_field() for(var/obj/effect/shield/S in field_segments) qdel(S) @@ -111,7 +111,7 @@ // Generates the field objects. Deletes existing field, if applicable. -/obj/machinery/power/shield_generator/proc/regenerate_field() +/obj/machinery/shield_generator/proc/regenerate_field() if(field_segments.len) for(var/obj/effect/shield/S in field_segments) qdel(S) @@ -142,7 +142,7 @@ // Recalculates and updates the upkeep multiplier -/obj/machinery/power/shield_generator/proc/update_upkeep_multiplier() +/obj/machinery/shield_generator/proc/update_upkeep_multiplier() var/new_upkeep = 1.0 for(var/datum/shield_mode/SM in mode_list) if(check_flag(SM.mode_flag)) @@ -150,8 +150,7 @@ upkeep_multiplier = new_upkeep - -/obj/machinery/power/shield_generator/Process() +/obj/machinery/shield_generator/Process() upkeep_power_usage = 0 power_usage = 0 @@ -182,9 +181,10 @@ else if(running > SHIELD_RUNNING) upkeep_power_usage = round(ENERGY_UPKEEP_IDLE * idle_multiplier * (field_radius * 8) * upkeep_multiplier) // Approximates number of turfs. - if(powernet && (running >= SHIELD_RUNNING) && !input_cut) - var/energy_buffer = 0 - energy_buffer = draw_power(min(upkeep_power_usage, input_cap)) + if((running >= SHIELD_RUNNING) && !input_cut) + var/drawn_power = min(upkeep_power_usage, input_cap) + var/energy_buffer + energy_buffer = drawn_power - use_power_oneoff(drawn_power) power_usage += round(energy_buffer) if(energy_buffer < upkeep_power_usage) @@ -196,7 +196,7 @@ energy_to_demand = between(0, max_energy - current_energy, input_cap - energy_buffer) else energy_to_demand = max(0, max_energy - current_energy) - energy_buffer = draw_power(energy_to_demand) + energy_buffer = energy_to_demand - use_power_oneoff(energy_to_demand) power_usage += energy_buffer current_energy += round(energy_buffer) else @@ -211,23 +211,23 @@ else if (field_integrity() > 25) overloaded = 0 -/obj/machinery/power/shield_generator/components_are_accessible(path) +/obj/machinery/shield_generator/components_are_accessible(path) return !running && ..() -/obj/machinery/power/shield_generator/cannot_transition_to(state_path) +/obj/machinery/shield_generator/cannot_transition_to(state_path) if(running) return SPAN_NOTICE("Turn off \the [src] first!") if(offline_for) return SPAN_NOTICE("Wait until \the [src] cools down from emergency shutdown first!") return ..() -/obj/machinery/power/shield_generator/attackby(obj/item/O, mob/user) +/obj/machinery/shield_generator/attackby(obj/item/O, mob/user) if(panel_open && (isMultitool(O) || isWirecutter(O))) attack_hand(user) return TRUE return component_attackby(O, user) -/obj/machinery/power/shield_generator/proc/energy_failure() +/obj/machinery/shield_generator/proc/energy_failure() if(running == SHIELD_DISCHARGING) shutdown_field() else @@ -236,7 +236,7 @@ for(var/obj/effect/shield/S in field_segments) S.fail(1) -/obj/machinery/power/shield_generator/proc/set_idle(var/new_state) +/obj/machinery/shield_generator/proc/set_idle(var/new_state) if(new_state) if(running == SHIELD_IDLE) return @@ -249,7 +249,7 @@ running = SHIELD_SPINNING_UP spinup_counter = round(spinup_delay / idle_multiplier) -/obj/machinery/power/shield_generator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/shield_generator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) var/data[0] data["running"] = running @@ -283,16 +283,16 @@ ui.set_auto_update(1) -/obj/machinery/power/shield_generator/interface_interact(var/mob/user) +/obj/machinery/shield_generator/interface_interact(var/mob/user) ui_interact(user) return TRUE -/obj/machinery/power/shield_generator/CanUseTopic(var/mob/user) +/obj/machinery/shield_generator/CanUseTopic(var/mob/user) if(issilicon(user) && !Adjacent(user) && ai_control_disabled) return STATUS_UPDATE return ..() -/obj/machinery/power/shield_generator/OnTopic(user, href_list, datum/topic_state/state) +/obj/machinery/shield_generator/OnTopic(user, href_list, datum/topic_state/state) if(href_list["begin_shutdown"]) if(running < SHIELD_RUNNING) return @@ -372,14 +372,14 @@ idle_multiplier = new_idle return TOPIC_REFRESH -/obj/machinery/power/shield_generator/proc/field_integrity() +/obj/machinery/shield_generator/proc/field_integrity() if(full_shield_strength) return round(CLAMP01(current_energy / full_shield_strength) * 100) return 0 // Takes specific amount of damage -/obj/machinery/power/shield_generator/proc/take_shield_damage(var/damage, var/shield_damtype) +/obj/machinery/shield_generator/proc/take_shield_damage(var/damage, var/shield_damtype) var/energy_to_use = damage * ENERGY_PER_HP if(check_flag(MODEFLAG_MODULATE)) mitigation_em -= MITIGATION_HIT_LOSS @@ -418,11 +418,11 @@ // Checks whether specific flags are enabled -/obj/machinery/power/shield_generator/proc/check_flag(var/flag) +/obj/machinery/shield_generator/proc/check_flag(var/flag) return (shield_modes & flag) -/obj/machinery/power/shield_generator/proc/toggle_flag(var/flag) +/obj/machinery/shield_generator/proc/toggle_flag(var/flag) shield_modes ^= flag update_upkeep_multiplier() for(var/obj/effect/shield/S in field_segments) @@ -437,7 +437,7 @@ mitigation_heat = 0 -/obj/machinery/power/shield_generator/proc/get_flag_descriptions() +/obj/machinery/shield_generator/proc/get_flag_descriptions() var/list/all_flags = list() for(var/datum/shield_mode/SM in mode_list) if(SM.hacked_only && !hacked) @@ -455,7 +455,7 @@ // These two procs determine tiles that should be shielded given the field range. They are quite CPU intensive and may trigger BYOND infinite loop checks, therefore they are set // as background procs to prevent locking up the server. They are only called when the field is generated, or when hull mode is toggled on/off. -/obj/machinery/power/shield_generator/proc/fieldtype_square() +/obj/machinery/shield_generator/proc/fieldtype_square() set background = 1 var/list/out = list() var/list/base_turfs = get_base_turfs() @@ -480,7 +480,7 @@ return out -/obj/machinery/power/shield_generator/proc/fieldtype_hull() +/obj/machinery/shield_generator/proc/fieldtype_hull() set background = 1 . = list() var/list/base_turfs = get_base_turfs() @@ -499,7 +499,7 @@ continue // Returns a list of turfs from which a field will propagate. If multi-Z mode is enabled, this will return a "column" of turfs above and below the generator. -/obj/machinery/power/shield_generator/proc/get_base_turfs() +/obj/machinery/shield_generator/proc/get_base_turfs() var/list/turfs = list() var/turf/T = get_turf(src) diff --git a/code/modules/shuttles/shuttle_engines.dm b/code/modules/shuttles/shuttle_engines.dm index 29307ff431d..8e9af7aa60f 100644 --- a/code/modules/shuttles/shuttle_engines.dm +++ b/code/modules/shuttles/shuttle_engines.dm @@ -6,13 +6,10 @@ name = "shuttle window" icon = 'icons/obj/structures/podwindows.dmi' icon_state = "1" - density = 1 - opacity = 0 - anchored = 1 - -/obj/structure/shuttle/window/CanPass(atom/movable/mover, turf/target, height, air_group) - if(!height || air_group) return 0 - else return ..() + density = TRUE + opacity = FALSE + anchored = TRUE + atmos_canpass = CANPASS_DENSITY /obj/structure/shuttle/engine name = "engine" diff --git a/code/modules/species/outsider/random.dm b/code/modules/species/outsider/random.dm index a53f2b05030..45357b68e4e 100644 --- a/code/modules/species/outsider/random.dm +++ b/code/modules/species/outsider/random.dm @@ -170,7 +170,7 @@ to_chat(user, "You can't speak any other languages by default. You can use translator implant that spawns on top of this monolith - it will give you knowledge of any language if you hear it enough times.") var/mob/living/carbon/human/H = user new /obj/item/implanter/translator(get_turf(src)) - H.set_species(SPECIES_ALIEN) + H.change_species(SPECIES_ALIEN) var/decl/cultural_info/culture = H.get_cultural_value(TAG_CULTURE) H.fully_replace_character_name(culture.get_random_name(H, H.gender)) H.rename_self("Humanoid Alien", 1) diff --git a/code/modules/species/species.dm b/code/modules/species/species.dm index d511e0fe0fc..80a60328eda 100644 --- a/code/modules/species/species.dm +++ b/code/modules/species/species.dm @@ -13,7 +13,6 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 var/ooc_codex_information var/cyborg_noun = "Cyborg" var/hidden_from_codex = TRUE - var/is_crystalline = FALSE var/holder_icon var/list/available_bodytypes = list() @@ -26,7 +25,8 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 /decl/blood_type/bminus, /decl/blood_type/abplus, /decl/blood_type/abminus, - /decl/blood_type/oplus + /decl/blood_type/oplus, + /decl/blood_type/ominus ) var/flesh_color = "#ffc896" // Pink. @@ -189,15 +189,9 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 var/list/override_organ_types // Used for species that only need to change one or two entries in has_organ. - //List of organ tags, with the amount and type required for living by this specie + //List of organ tags, with the amount and type required for living by this species //#REMOVEME: The vital organ stuff was apparently mostly dropped, so its a bit pointless to improve it... - var/list/vital_organs = list( - BP_HEART = list("path" = /obj/item/organ/internal/heart), - BP_LUNGS = list("path" = /obj/item/organ/internal/lungs), - BP_BRAIN = list("path" = /obj/item/organ/internal/brain), - BP_CHEST = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin), - ) + var/list/vital_organs var/obj/effect/decal/cleanable/blood/tracks/move_trail = /obj/effect/decal/cleanable/blood/tracks/footprints // What marks are left when walking @@ -448,7 +442,7 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 /decl/species/proc/get_manual_dexterity(var/mob/living/carbon/human/H) . = manual_dexterity -//Checks if an existing organ is the specie's default +//Checks if an existing organ is the species default /decl/species/proc/is_default_organ(var/obj/item/organ/O) for(var/tag in has_organ) if(O.organ_tag == tag) @@ -456,8 +450,13 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 return TRUE return FALSE -//Checks if an existing limbs is the specie's default +//Checks if an existing limbs is the species default /decl/species/proc/is_default_limb(var/obj/item/organ/external/E) + // We don't have ^^ (logical XOR), so !x != !y will suffice. + if(!(species_flags & SPECIES_FLAG_CRYSTALLINE) != !BP_IS_CRYSTAL(E)) + return FALSE + if(!(species_flags & SPECIES_FLAG_SYNTHETIC) != !BP_IS_PROSTHETIC(E)) + return FALSE for(var/tag in has_limbs) if(E.organ_tag == tag) var/list/organ_data = has_limbs[tag] @@ -475,6 +474,8 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 return FALSE /decl/species/proc/is_vital_organ(var/mob/living/carbon/human/H, var/obj/item/organ/O) + if(!LAZYLEN(vital_organs)) + return FALSE //An organ organ is considered vital if there's less than the required amount of said organ type in the mob after we remove it var/list/organ_data = vital_organs[O.organ_tag] if(!organ_data || !ispath(O.type, organ_data["path"])) @@ -509,6 +510,7 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 var/limb_path = organ_data["path"] var/obj/item/organ/external/E = new limb_path(H, null, H.dna) //explicitly specify the dna H.add_organ(E, null, FALSE, FALSE) + post_organ_rejuvenate(E, H) //Create missing internal organs for(var/organ_tag in has_organ) @@ -716,11 +718,6 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 /decl/species/proc/handle_death_check(var/mob/living/carbon/human/H) return FALSE -//Mostly for toasters -/decl/species/proc/handle_limbs_setup(var/mob/living/carbon/human/H) - for(var/thing in H.get_external_organs()) - post_organ_rejuvenate(thing, H) - // Impliments different trails for species depending on if they're wearing shoes. /decl/species/proc/get_move_trail(var/mob/living/carbon/human/H) if(H.lying) @@ -863,7 +860,7 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 else . = 4 /decl/species/proc/post_organ_rejuvenate(var/obj/item/organ/org, var/mob/living/carbon/human/H) - if(org && (org.species ? org.species.is_crystalline : is_crystalline)) + if(org && (org.species ? (org.species.species_flags & SPECIES_FLAG_CRYSTALLINE) : (species_flags & SPECIES_FLAG_CRYSTALLINE))) org.status |= (ORGAN_BRITTLE|ORGAN_CRYSTAL) /decl/species/proc/check_no_slip(var/mob/living/carbon/human/H) @@ -914,18 +911,18 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 /decl/species/proc/get_holder_color(var/mob/living/carbon/human/H) return -//Called after a mob's specie is set, organs were created, and we're about to update the icon, color, and etc of the mob being created. +//Called after a mob's species is set, organs were created, and we're about to update the icon, color, and etc of the mob being created. //Consider this might be called post-init -/decl/species/proc/apply_appearence(var/mob/living/carbon/human/H) +/decl/species/proc/apply_appearance(var/mob/living/carbon/human/H) H.icon_state = lowertext(src.name) H.skin_colour = src.base_color - update_appearence_descriptors(H) + update_appearance_descriptors(H) -/decl/species/proc/update_appearence_descriptors(var/mob/living/carbon/human/H) +/decl/species/proc/update_appearance_descriptors(var/mob/living/carbon/human/H) if(!LAZYLEN(src.appearance_descriptors)) H.appearance_descriptors = null - return - + return + var/list/new_descriptors = list() //Add missing descriptors, and sanitize any existing ones for(var/desctype in src.appearance_descriptors) @@ -943,7 +940,7 @@ var/global/const/DEFAULT_SPECIES_HEALTH = 200 var/mob/living/carbon/human/dummy/mannequin/mannequin = get_mannequin("#species_[ckey(name)]") if(mannequin) - mannequin.set_species(name) + mannequin.change_species(name) customize_preview_mannequin(mannequin) preview_icon = getFlatIcon(mannequin) diff --git a/code/modules/species/station/golem.dm b/code/modules/species/station/golem.dm index eb51a3e5af5..4d540a5debf 100644 --- a/code/modules/species/station/golem.dm +++ b/code/modules/species/station/golem.dm @@ -11,7 +11,7 @@ available_bodytypes = list(/decl/bodytype/golem) unarmed_attacks = list(/decl/natural_attack/stomp, /decl/natural_attack/kick, /decl/natural_attack/punch) - species_flags = SPECIES_FLAG_NO_PAIN | SPECIES_FLAG_NO_SCAN | SPECIES_FLAG_NO_POISON + species_flags = SPECIES_FLAG_NO_PAIN | SPECIES_FLAG_NO_SCAN | SPECIES_FLAG_NO_POISON | SPECIES_FLAG_CRYSTALLINE spawn_flags = SPECIES_IS_RESTRICTED siemens_coefficient = 0 @@ -34,10 +34,6 @@ heat_level_2 = SYNTH_HEAT_LEVEL_2 heat_level_3 = SYNTH_HEAT_LEVEL_3 - vital_organs = list( - BP_BRAIN = list("path" = /obj/item/organ/internal/brain/golem), - ) - has_organ = list( BP_BRAIN = /obj/item/organ/internal/brain/golem ) @@ -45,8 +41,6 @@ death_message = "becomes completely motionless..." available_pronouns = list(/decl/pronouns/neuter) - is_crystalline = TRUE - force_cultural_info = list( TAG_CULTURE = /decl/cultural_info/culture/hidden/cultist, TAG_HOMEWORLD = /decl/cultural_info/location/stateless, diff --git a/code/modules/spells/aoe_turf/charge.dm b/code/modules/spells/aoe_turf/charge.dm index 4e45ca627b8..08776d94ddf 100644 --- a/code/modules/spells/aoe_turf/charge.dm +++ b/code/modules/spells/aoe_turf/charge.dm @@ -65,9 +65,8 @@ /spell/aoe_turf/charge/blood - name = "blood charge" + name = "Blood Infusion" desc = "This spell charges things around it using the lifeforce gained by sacrificed blood." - charge_type = Sp_HOLDVAR holder_var_type = "bruteloss" holder_var_amount = 30 \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/conjure/construct.dm b/code/modules/spells/aoe_turf/conjure/construct.dm index 1105f182d58..2d7387695d8 100644 --- a/code/modules/spells/aoe_turf/conjure/construct.dm +++ b/code/modules/spells/aoe_turf/conjure/construct.dm @@ -99,7 +99,7 @@ return /spell/aoe_turf/conjure/forcewall/lesser - name = "Shield" + name = "Force Shield" desc = "Allows you to pull up a shield to protect yourself and allies from incoming threats" charge_max = 300 diff --git a/code/modules/spells/aoe_turf/conjure/faithful_hound.dm b/code/modules/spells/aoe_turf/conjure/faithful_hound.dm index d836f9a8a46..2a2a6c8a987 100644 --- a/code/modules/spells/aoe_turf/conjure/faithful_hound.dm +++ b/code/modules/spells/aoe_turf/conjure/faithful_hound.dm @@ -19,5 +19,6 @@ newVars = list("password" = password, "allowed_mobs" = list(usr)) /spell/aoe_turf/conjure/faithful_hound/tower + desc = "This spell allows you to summon a singular spectral dog that guards the nearby area. Anyone without the password is barked at or bitten." charge_max = 1 - spell_flags = 0 \ No newline at end of file + spell_flags = 0 diff --git a/code/modules/spells/aoe_turf/conjure/force_portal.dm b/code/modules/spells/aoe_turf/conjure/force_portal.dm index 1bf7d7f075e..7a4483435cf 100644 --- a/code/modules/spells/aoe_turf/conjure/force_portal.dm +++ b/code/modules/spells/aoe_turf/conjure/force_portal.dm @@ -12,5 +12,6 @@ hud_state = "wiz_force" /spell/aoe_turf/conjure/force_portal/tower + desc = "This spell allows you to summon a force portal. Anything that hits the portal gets sucked inside and is then thrown out when the portal explodes." charge_max = 2 spell_flags = 0 \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/conjure/forcewall.dm b/code/modules/spells/aoe_turf/conjure/forcewall.dm index dd63217840c..4804923a4a0 100644 --- a/code/modules/spells/aoe_turf/conjure/forcewall.dm +++ b/code/modules/spells/aoe_turf/conjure/forcewall.dm @@ -49,4 +49,5 @@ desc = "You have a bad feeling about this." /spell/aoe_turf/conjure/forcewall/tower + desc = "A temporary invincible wall for you to summon." charge_max = 3 \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/disable_tech.dm b/code/modules/spells/aoe_turf/disable_tech.dm index 636a8c1c865..a8069b209f8 100644 --- a/code/modules/spells/aoe_turf/disable_tech.dm +++ b/code/modules/spells/aoe_turf/disable_tech.dm @@ -33,5 +33,6 @@ return "You've increased the range of [src]." /spell/aoe_turf/disable_tech/starlight + hidden_from_codex = TRUE charge_max = 600 spell_flags = 0 \ No newline at end of file diff --git a/code/modules/spells/aoe_turf/drain_blood.dm b/code/modules/spells/aoe_turf/drain_blood.dm index 23ce0e6f987..9bd46aba064 100644 --- a/code/modules/spells/aoe_turf/drain_blood.dm +++ b/code/modules/spells/aoe_turf/drain_blood.dm @@ -1,6 +1,6 @@ /spell/aoe_turf/drain_blood name = "Drain Blood" - desc = "this spell allows the caster to borrow blood from those around them. Sharing is caring!" + desc = "This spell allows the caster to borrow blood from those around them. Sharing is caring!" feedback = "DB" school = "transmutation" charge_max = 600 diff --git a/code/modules/spells/aoe_turf/exchange_wounds.dm b/code/modules/spells/aoe_turf/exchange_wounds.dm index 06a0becfff8..7a3f9b6bbea 100644 --- a/code/modules/spells/aoe_turf/exchange_wounds.dm +++ b/code/modules/spells/aoe_turf/exchange_wounds.dm @@ -1,6 +1,6 @@ /spell/aoe_turf/exchange_wounds name = "Exchange Wounds" - desc = "Syphon the wounds from your allies." + desc = "Allows you to sacrifice your own well-being for that of those around you." feedback = "EW" school = "transmutation" invocation = "Esh Yek Vai!" diff --git a/code/modules/spells/aoe_turf/knock.dm b/code/modules/spells/aoe_turf/knock.dm index 6e7f723af27..f3dfa6a1e3c 100644 --- a/code/modules/spells/aoe_turf/knock.dm +++ b/code/modules/spells/aoe_turf/knock.dm @@ -33,8 +33,9 @@ return "You've doubled the range of [src]." /spell/aoe_turf/knock/slow - name = "Slow Knock" charge_max = 200 + hidden_from_codex = TRUE /spell/aoe_turf/knock/tower - charge_max = 2 \ No newline at end of file + charge_max = 2 + hidden_from_codex = TRUE diff --git a/code/modules/spells/aoe_turf/smoke.dm b/code/modules/spells/aoe_turf/smoke.dm index d9b474d539f..5fe95415955 100644 --- a/code/modules/spells/aoe_turf/smoke.dm +++ b/code/modules/spells/aoe_turf/smoke.dm @@ -26,4 +26,5 @@ return "[src] will now create more smoke." /spell/aoe_turf/smoke/tower - charge_max = 2 \ No newline at end of file + charge_max = 2 + hidden_from_codex = TRUE diff --git a/code/modules/spells/general/acid_spray.dm b/code/modules/spells/general/acid_spray.dm index 97d5af84b0a..c39d043bfc8 100644 --- a/code/modules/spells/general/acid_spray.dm +++ b/code/modules/spells/general/acid_spray.dm @@ -27,4 +27,5 @@ chem.set_up(get_ranged_target_turf(target, angle2dir(angle+mod), 3)) /spell/acid_spray/tower + desc = "The simplest form of aggressive conjuration: acid spray is quite effective in melting both man and object." charge_max = 2 \ No newline at end of file diff --git a/code/modules/spells/general/create_air.dm b/code/modules/spells/general/create_air.dm index d051f9a152c..69e56bb9651 100644 --- a/code/modules/spells/general/create_air.dm +++ b/code/modules/spells/general/create_air.dm @@ -25,4 +25,5 @@ environment.adjust_gas(gas, air_change[gas]) /spell/create_air/tower + desc = "Allows you to generate a livable atmosphere in the area you are in." charge_max = 5 \ No newline at end of file diff --git a/code/modules/spells/general/god_construct.dm b/code/modules/spells/general/god_construct.dm index 761cb561c8f..25abda2c59e 100644 --- a/code/modules/spells/general/god_construct.dm +++ b/code/modules/spells/general/god_construct.dm @@ -2,7 +2,7 @@ #define CONSTRUCT_SPELL_TYPE 2 /spell/construction - name = "Construction" + name = "Basic Construction" desc = "This ability will let you summon a structure of your choosing." cast_delay = 10 diff --git a/code/modules/spells/general/radiant_aura.dm b/code/modules/spells/general/radiant_aura.dm index 2c243896eb9..59282fd49b5 100644 --- a/code/modules/spells/general/radiant_aura.dm +++ b/code/modules/spells/general/radiant_aura.dm @@ -22,5 +22,7 @@ QDEL_IN(A,duration) /spell/radiant_aura/starlight + name = "Starlight Aura" + desc = "This spell makes you immune to laser fire, for a short while at least." spell_flags = 0 charge_max = 400 \ No newline at end of file diff --git a/code/modules/spells/hand/blood_shards.dm b/code/modules/spells/hand/blood_shards.dm index 82ef0a9949e..69418dda0fa 100644 --- a/code/modules/spells/hand/blood_shards.dm +++ b/code/modules/spells/hand/blood_shards.dm @@ -1,7 +1,6 @@ /spell/hand/charges/blood_shard name = "Blood Shards" - desc = "Invoke a corrupted projectile forward that causes an enemy's blood to fly out in painful shards." - + desc = "Invoke a corrupted projectile forward that causes an enemy's blood to fly out in painful shards. Anyone hit by this will have their blood explode out of them in a spray of smaller shards. Stores two charges." spell_flags = 0 charge_max = 600 invocation = "opens their hand, which bursts into vicious red light." diff --git a/code/modules/spells/hand/burning_grip.dm b/code/modules/spells/hand/burning_grip.dm index d52c75d4123..1a6ff622a7c 100644 --- a/code/modules/spells/hand/burning_grip.dm +++ b/code/modules/spells/hand/burning_grip.dm @@ -39,4 +39,5 @@ to_chat(H, SPAN_WARNING("You notice that your [E] is burned.")) /spell/hand/burning_grip/tower + desc = "Allows you cause an object to heat up intensly in someone's hand, making them drop it and whatever skin is attached." charge_max = 3 \ No newline at end of file diff --git a/code/modules/spells/hand/slippery_surface.dm b/code/modules/spells/hand/slippery_surface.dm index cd7158921c0..002b4c947ff 100644 --- a/code/modules/spells/hand/slippery_surface.dm +++ b/code/modules/spells/hand/slippery_surface.dm @@ -18,4 +18,5 @@ return ..() /spell/hand/slippery_surface/tower + desc = "Allows you to slicken a small patch of floor. Anyone without sure-footing will find it hard to stay upright." charge_max = 2 \ No newline at end of file diff --git a/code/modules/spells/spell_code.dm b/code/modules/spells/spell_code.dm index 4290f1ee49e..4ca9520eac2 100644 --- a/code/modules/spells/spell_code.dm +++ b/code/modules/spells/spell_code.dm @@ -1,8 +1,8 @@ var/global/list/spells = typesof(/spell) //needed for the badmin verb for now /spell - var/name = "Spell" - var/desc = "A spell" + var/name + var/desc var/feedback = "" //what gets sent if this spell gets chosen by the spellbook. parent_type = /datum var/panel = "Spells"//What panel the proc holder needs to go on. diff --git a/code/modules/spells/targeted/blood_boil.dm b/code/modules/spells/targeted/blood_boil.dm index cecc5c17b8c..25f746daaf8 100644 --- a/code/modules/spells/targeted/blood_boil.dm +++ b/code/modules/spells/targeted/blood_boil.dm @@ -1,6 +1,6 @@ /spell/targeted/blood_boil name = "Blood Boil" - desc = "This spell allows the caster to heat up an adversary's body so much their blood boils." + desc = "Allow you to concentrate so deeply on a target that their body temperature increases, eventually setting them on fire." feedback = "BO" school = "transmutation" charge_max = 300 diff --git a/code/modules/spells/targeted/cleric_spells.dm b/code/modules/spells/targeted/cleric_spells.dm index 631b08c41ca..7a46ae23f33 100644 --- a/code/modules/spells/targeted/cleric_spells.dm +++ b/code/modules/spells/targeted/cleric_spells.dm @@ -33,6 +33,7 @@ return "[src] will now heal more." /spell/targeted/heal_target/tower + desc = "Allows you to heal yourself, or others, for a slight amount." charge_max = 2 /spell/targeted/heal_target/touch @@ -85,6 +86,7 @@ /spell/targeted/heal_target/major/tower charge_max = 1 spell_flags = INCLUDEUSER | SELECTABLE + desc = "Allows you to heal others for a great amount." /spell/targeted/heal_target/area name = "Cure Area" @@ -113,6 +115,7 @@ return "[src] now heals more in a wider area." /spell/targeted/heal_target/area/tower + desc = "Allows you to heal everyone in an area for minor damage." charge_max = 1 /spell/targeted/heal_target/area/slow diff --git a/code/modules/spells/targeted/equip/dyrnwyn.dm b/code/modules/spells/targeted/equip/dyrnwyn.dm index 305143da44c..dfbe3347ecd 100644 --- a/code/modules/spells/targeted/equip/dyrnwyn.dm +++ b/code/modules/spells/targeted/equip/dyrnwyn.dm @@ -37,4 +37,5 @@ return "Dyrnwyn has been made pure: it is now made of silver." /spell/targeted/equip_item/dyrnwyn/tower + desc = "This spell allows you to summon a golden firey sword for a short duration." charge_max = 1 \ No newline at end of file diff --git a/code/modules/spells/targeted/equip/equip.dm b/code/modules/spells/targeted/equip/equip.dm index ae4617137b0..8d105b41107 100644 --- a/code/modules/spells/targeted/equip/equip.dm +++ b/code/modules/spells/targeted/equip/equip.dm @@ -1,13 +1,9 @@ //You can set duration to 0 to have the items last forever /spell/targeted/equip_item - name = "equipment spell" cast_sound = 'sound/magic/summonitems_generic.ogg' - var/list/equipped_summons = list() //assoc list of text ids and paths to spawn - var/list/summoned_items = list() //list of items we summoned and will dispose when the spell runs out - var/delete_old = 1 //if the item previously in the slot is deleted - otherwise, it's dropped /spell/targeted/equip_item/cast(list/targets, mob/user = usr) diff --git a/code/modules/spells/targeted/equip/shield.dm b/code/modules/spells/targeted/equip/shield.dm index 4ab9102c03e..f81a75c3d62 100644 --- a/code/modules/spells/targeted/equip/shield.dm +++ b/code/modules/spells/targeted/equip/shield.dm @@ -41,4 +41,5 @@ return "Your summoned shields will now block more often." /spell/targeted/equip_item/shield/tower + desc = "This spell allows you to summon a magical shield for a short duration." charge_max = 1 \ No newline at end of file diff --git a/code/modules/spells/targeted/ethereal_jaunt.dm b/code/modules/spells/targeted/ethereal_jaunt.dm index 90000c10de6..655cf47d2e6 100644 --- a/code/modules/spells/targeted/ethereal_jaunt.dm +++ b/code/modules/spells/targeted/ethereal_jaunt.dm @@ -120,5 +120,6 @@ return /spell/targeted/ethereal_jaunt/tower + desc = "Allows you to liquify for a short duration, letting them pass through all dense objects." charge_max = 2 spell_flags = Z2NOCAST | INCLUDEUSER \ No newline at end of file diff --git a/code/modules/spells/targeted/projectile/dumbfire.dm b/code/modules/spells/targeted/projectile/dumbfire.dm index 5db69524ce9..491a5f00f99 100644 --- a/code/modules/spells/targeted/projectile/dumbfire.dm +++ b/code/modules/spells/targeted/projectile/dumbfire.dm @@ -1,5 +1,4 @@ /spell/targeted/projectile/dumbfire - name = "dumbfire spell" selection_type = "range" /spell/targeted/projectile/dumbfire/choose_targets(mob/user = usr) diff --git a/code/modules/spells/targeted/projectile/fireball.dm b/code/modules/spells/targeted/projectile/fireball.dm index 150d01d92a0..9e169694da4 100644 --- a/code/modules/spells/targeted/projectile/fireball.dm +++ b/code/modules/spells/targeted/projectile/fireball.dm @@ -1,6 +1,6 @@ /spell/targeted/projectile/dumbfire/fireball name = "Fireball" - desc = "This spell fires a fireball at a target and does not require wizard garb." + desc = "A classic spell, grants you the ability to throw an exploding ball of flame in any direction. Does not require wizard garb." feedback = "FB" proj_type = /obj/item/projectile/spell_projectile/fireball @@ -47,6 +47,7 @@ return "The spell [src] now has a larger explosion." /spell/targeted/projectile/dumbfire/fireball/tower + desc = "Imbue yourself with the power of exploding fire." charge_max = 2 //PROJECTILE diff --git a/code/modules/spells/targeted/projectile/projectile.dm b/code/modules/spells/targeted/projectile/projectile.dm index 2cc2b2a559b..6df791094be 100644 --- a/code/modules/spells/targeted/projectile/projectile.dm +++ b/code/modules/spells/targeted/projectile/projectile.dm @@ -6,12 +6,8 @@ If the spell_projectile is seeking, it will update its target every process and */ /spell/targeted/projectile - name = "projectile spell" - range = 7 - var/proj_type = /obj/item/projectile/spell_projectile //use these. They are very nice - var/proj_step_delay = 1 //lower = faster var/cast_prox_range = 1 diff --git a/code/modules/spells/targeted/shatter_mind.dm b/code/modules/spells/targeted/shatter_mind.dm index 681482ae2bc..26a3cc56892 100644 --- a/code/modules/spells/targeted/shatter_mind.dm +++ b/code/modules/spells/targeted/shatter_mind.dm @@ -1,6 +1,6 @@ /spell/targeted/shatter name = "Shatter Mind" - desc = "this spell allows the caster to literally break an enemy's mind. Permanently." + desc = "Assaults the mind of the target with fear of the unknown, shattering their sanity and causing brain damage." feedback = "SM" school = "illusion" charge_max = 300 diff --git a/code/modules/spells/targeted/torment.dm b/code/modules/spells/targeted/torment.dm index 9391e9012bb..ea1cdff12a2 100644 --- a/code/modules/spells/targeted/torment.dm +++ b/code/modules/spells/targeted/torment.dm @@ -1,6 +1,6 @@ /spell/targeted/torment name = "Torment" - desc = "this spell causes pain to all those in its radius." + desc = "Darkness stabs at the bodies of those around you. All within a medium range will suffer significant pain." feedback = "TM" school = "illusion" charge_max = 150 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/limb_reattach.dm b/code/modules/surgery/limb_reattach.dm index 9b1b9f53fc1..fdf5d454f75 100644 --- a/code/modules/surgery/limb_reattach.dm +++ b/code/modules/surgery/limb_reattach.dm @@ -91,26 +91,17 @@ /decl/surgery_step/limb/attach/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) if(!user.unEquip(tool)) return + var/obj/item/organ/external/P = target.get_organ(target_zone) var/obj/item/organ/external/E = tool user.visible_message("[user] has attached [target]'s [E.name] to the [E.amputation_point].", \ "You have attached [target]'s [E.name] to the [E.amputation_point].") - target.add_organ(E) - // Modular bodyparts (like prosthetics) do not need to be reconnected. - if(E.get_modular_limb_category() != MODULAR_BODYPART_INVALID) - E.status &= ~ORGAN_CUT_AWAY - for(var/obj/item/organ/external/child in E.children) - child.status &= ~ORGAN_CUT_AWAY - else - E.status |= ORGAN_CUT_AWAY + //Add the organ but in a detached state + target.add_organ(E, P, FALSE, TRUE, TRUE) if(BP_IS_PROSTHETIC(E) && prob(user.skill_fail_chance(SKILL_DEVICES, 50, SKILL_ADEPT))) E.add_random_ailment() - target.update_body() - target.updatehealth() - target.UpdateDamageIcon() - /decl/surgery_step/limb/attach/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = tool user.visible_message(" [user]'s hand slips, damaging [target]'s [E.amputation_point]!", \ @@ -152,15 +143,12 @@ /decl/surgery_step/limb/connect/end_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = target.get_organ(target_zone) + var/obj/item/organ/external/P = target.get_organ(E.parent_organ) user.visible_message("[user] has connected tendons and muscles in [target]'s [E.amputation_point] with [tool].", \ "You have connected tendons and muscles in [target]'s [E.amputation_point] with [tool].") - E.status &= ~ORGAN_CUT_AWAY - if(E.children) - for(var/obj/item/organ/external/C in E.children) - C.status &= ~ORGAN_CUT_AWAY - target.update_body() - target.updatehealth() - target.UpdateDamageIcon() + + //This time we call add_organ but we want it to install in a non detached state + target.add_organ(E, P, FALSE, TRUE, FALSE) /decl/surgery_step/limb/connect/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/E = target.get_organ(target_zone) 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/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index 824458f18b8..fade8fc631e 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -121,8 +121,10 @@ user.visible_message("[user] has separated [target]'s [LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone)] with \the [tool]." , \ "You have separated [target]'s [LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone)] with \the [tool].") var/obj/item/organ/I = target.get_organ(LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone)) - if(I && istype(I)) - I.cut_away(user) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(I && istype(I) && istype(affected)) + //First only detach the organ, without fully removing it + target.remove_organ(I, FALSE, TRUE) /decl/surgery_step/internal/detatch_organ/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) @@ -188,20 +190,19 @@ var/obj/item/organ/O = LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone) var/obj/item/organ/external/affected = target.get_organ(target_zone) if(istype(O) && istype(affected)) - LAZYREMOVE(affected.implants, O) - O.dropInto(target.loc) + //Now call remove again with detach = FALSE so we fully remove it + target.remove_organ(O, TRUE, FALSE) + if(!BP_IS_PROSTHETIC(affected)) playsound(target.loc, 'sound/effects/squelch1.ogg', 15, 1) else playsound(target.loc, 'sound/items/Ratchet.ogg', 50, 1) - if(istype(O, /obj/item/organ/internal/mmi_holder)) - var/obj/item/organ/internal/mmi_holder/brain = O - brain.transfer_and_delete() // Just in case somehow the organ we're extracting from an organic is an MMI if(istype(O, /obj/item/organ/internal/mmi_holder)) var/obj/item/organ/internal/mmi_holder/brain = O brain.transfer_and_delete() + log_warning("Organ removal surgery on '[target]' returned a mmi_holder '[O]' instead of a mmi!!") /decl/surgery_step/internal/remove_organ/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) @@ -275,11 +276,14 @@ "You have [robotic_surgery ? "reinstalled" : "transplanted"] \the [tool] into [target]'s [affected.name].") var/obj/item/organ/O = tool if(istype(O) && user.unEquip(O, target)) - LAZYDISTINCTADD(affected.implants, O) //move the organ into the patient. The organ is properly reattached in the next step - if(!(O.status & ORGAN_CUT_AWAY)) - log_debug("[user] ([user.ckey]) replaced organ [O], which didn't have ORGAN_CUT_AWAY set, in [target] ([target.ckey])") - O.status |= ORGAN_CUT_AWAY - playsound(target.loc, 'sound/effects/squelch1.ogg', 15, 1) + //Place the organ but don't attach it yet + target.add_organ(O, affected, detached = TRUE) + + if(!BP_IS_PROSTHETIC(affected)) + playsound(target.loc, 'sound/effects/squelch1.ogg', 15, 1) + else + playsound(target.loc, 'sound/items/Ratchet.ogg', 50, 1) + if(BP_IS_PROSTHETIC(O) && prob(user.skill_fail_chance(SKILL_DEVICES, 50, SKILL_ADEPT))) O.add_random_ailment() @@ -372,9 +376,7 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) if(istype(I) && I.parent_organ == target_zone && affected && (I in affected.implants)) - I.status &= ~ORGAN_CUT_AWAY //apply sutures - LAZYREMOVE(affected.implants, I) - target.add_organ(I, affected) + target.add_organ(I, affected, detached = FALSE) /decl/surgery_step/internal/attach_organ/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index 7ba460b4209..3a74f8f83dd 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -415,8 +415,9 @@ user.visible_message("[user] has decoupled [target]'s [LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone)] with \the [tool]." , \ "You have decoupled [target]'s [LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone)] with \the [tool].") var/obj/item/organ/internal/I = target.get_organ(LAZYACCESS(global.surgeries_in_progress["\ref[target]"], target_zone)) - if(I && istype(I)) - I.cut_away(user) + var/obj/item/organ/external/affected = target.get_organ(target_zone) + if(I && istype(I) && istype(affected)) + target.remove_organ(I, detach = TRUE) /decl/surgery_step/robotics/detatch_organ_robotic/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) user.visible_message("[user]'s hand slips, disconnecting \the [tool].", \ @@ -466,9 +467,7 @@ var/obj/item/organ/external/affected = target.get_organ(target_zone) for (var/obj/item/organ/I in affected.implants) if (I.organ_tag == current_organ) - I.status &= ~ORGAN_CUT_AWAY - LAZYREMOVE(affected.implants, I) - target.add_organ(I, affected) + target.add_organ(I, affected, detached = TRUE) break /decl/surgery_step/robotics/attach_organ_robotic/fail_step(mob/living/user, mob/living/target, target_zone, obj/item/tool) @@ -528,7 +527,7 @@ var/obj/item/mmi/M = tool var/obj/item/organ/internal/mmi_holder/holder = new(target, 1) var/mob/living/carbon/human/H = target - H.add_organ(holder, null, TRUE, TRUE) + H.add_organ(holder, affected, TRUE) tool.forceMove(holder) holder.stored_mmi = tool holder.update_from_mmi() diff --git a/code/modules/tools/tool_item.dm b/code/modules/tools/tool_item.dm index 9cdaff80664..177e5d5ef3a 100644 --- a/code/modules/tools/tool_item.dm +++ b/code/modules/tools/tool_item.dm @@ -18,8 +18,11 @@ return FALSE if(. == TOOL_USE_SUCCESS) - var/decl/tool_archetype/tool_archetype = GET_DECL(archetype) - user.visible_message(SPAN_NOTICE("\The [user] finishes [success_message || tool_archetype.use_message] \the [target] with \the [src]."), SPAN_NOTICE("You finish [success_message || tool_archetype.use_message] \the [target] with \the [src].")) + if(success_message) + user.visible_message( + SPAN_NOTICE("\The [user] finishes [success_message] \the [target] with \the [src]."), + SPAN_NOTICE("You finish [success_message] \the [target] with \the [src].") + ) return TRUE if(. == TOOL_USE_FAILURE_NOMESSAGE && failure_message) diff --git a/code/modules/vehicles/bike.dm b/code/modules/vehicles/bike.dm index 7b7ba0465dd..eae0207c323 100644 --- a/code/modules/vehicles/bike.dm +++ b/code/modules/vehicles/bike.dm @@ -95,7 +95,7 @@ /obj/vehicle/bike/load(var/atom/movable/C) var/mob/living/M = C if(!istype(M)) return 0 - if(M.buckled || M.restrained() || !Adjacent(M) || !M.Adjacent(src)) + if(M.buckled || M.anchored || M.restrained() || !Adjacent(M) || !M.Adjacent(src)) return 0 return ..(M) diff --git a/code/modules/xenoarcheaology/artifacts/standalone/autocloner.dm b/code/modules/xenoarcheaology/artifacts/standalone/autocloner.dm index 5f3a25c0c16..6e9eb5bdc46 100644 --- a/code/modules/xenoarcheaology/artifacts/standalone/autocloner.dm +++ b/code/modules/xenoarcheaology/artifacts/standalone/autocloner.dm @@ -83,7 +83,7 @@ last_process = world.time -/obj/machinery/auto_clone/get_artifact_scan_data() +/obj/machinery/auto_cloner/get_artifact_scan_data() return "Automated cloning pod - appears to rely on an artificial ecosystem formed by semi-organic nanomachines and the contained liquid.
    \ The liquid resembles protoplasmic residue supportive of unicellular organism developmental conditions.
    \ The structure is composed of a titanium alloy." diff --git a/code/modules/xenoarcheaology/boulder.dm b/code/modules/xenoarcheaology/boulder.dm index 8e3460f5e0b..90cec66a536 100644 --- a/code/modules/xenoarcheaology/boulder.dm +++ b/code/modules/xenoarcheaology/boulder.dm @@ -21,7 +21,7 @@ /obj/structure/boulder/Destroy() QDEL_NULL(artifact_find) - ..() + return ..() /obj/structure/boulder/attackby(var/obj/item/I, var/mob/user) if(istype(I, /obj/item/depth_scanner)) diff --git a/code/modules/xgm/xgm_gas_mixture.dm b/code/modules/xgm/xgm_gas_mixture.dm index 1d1ea2f3e10..953e4405332 100644 --- a/code/modules/xgm/xgm_gas_mixture.dm +++ b/code/modules/xgm/xgm_gas_mixture.dm @@ -140,7 +140,7 @@ for(var/g in gas) var/decl/material/mat = GET_DECL(g) . += mat.gas_specific_heat * gas[g] - . *= group_multiplier + . *= max(1, group_multiplier) //Adds or removes thermal energy. Returns the actual thermal energy change, as in the case of removing energy we can't go below TCMB. @@ -150,6 +150,9 @@ return 0 var/heat_capacity = heat_capacity() + if(heat_capacity <= 0) + return 0 + if (thermal_energy < 0) if (temperature < TCMB) return 0 diff --git a/code/unit_tests/atmospherics_tests.dm b/code/unit_tests/atmospherics_tests.dm index 9675d6eda99..7f9a5f49d9a 100644 --- a/code/unit_tests/atmospherics_tests.dm +++ b/code/unit_tests/atmospherics_tests.dm @@ -403,7 +403,7 @@ // then, pipes from machine datums are used to spawn machines ("player-built" behavior) for(var/type in subtypesof(/datum/fabricator_recipe/pipe)) var/datum/fabricator_recipe/pipe/recipe = new type() - var/list/stuff = recipe.build(T) + var/list/stuff = recipe.build(T, new/datum/fabricator_build_order(recipe) ) var/obj/item/pipe/pipe = locate() in stuff if(pipe) stuff -= pipe diff --git a/code/unit_tests/codex.dm b/code/unit_tests/codex.dm new file mode 100644 index 00000000000..0e63d5c04a8 --- /dev/null +++ b/code/unit_tests/codex.dm @@ -0,0 +1,38 @@ +/datum/unit_test/codex_string_uniqueness + name = "CODEX - All Codex Associated Strings Shall Be Unique" + +/datum/unit_test/codex_string_uniqueness/start_test() + var/list/failures = list() + var/list/seen_strings = list() + for(var/datum/codex_entry/entry AS_ANYTHING in SScodex.all_entries) + for(var/associated_string in entry.associated_strings) + if(seen_strings[associated_string]) + failures |= "'[associated_string]' - \ref[entry]#[entry.name] - first seen: [seen_strings[associated_string]]" + else + seen_strings[associated_string] = "\ref[entry]#[entry.name]" + + if(length(failures)) + fail("Found [length(failures)] non-unique associated strings\s:\n[jointext(failures, "\n")].") + else + pass("No non-unique associated strings.") + return TRUE + +/datum/unit_test/codex_overlap + name = "CODEX - No Codex String IDs Shall Overlap" + +/datum/unit_test/codex_overlap/start_test() + var/list/failures = list() + for(var/check_string in SScodex.entries_by_string) + var/clean_check_string = lowertext(check_string) + for(var/other_string in SScodex.entries_by_string) + var/clean_other_string = lowertext(other_string) + if(clean_other_string != clean_check_string || SScodex.entries_by_string[other_string] != SScodex.entries_by_string[check_string]) + if(findtext(clean_check_string, clean_other_string)) + failures |= "[check_string], [other_string]" + else if(findtext(clean_other_string, clean_check_string)) + failures |= "[other_string], [check_string]" + if(length(failures)) + fail("Found [length(failures)] overlapping string ID\s:\n[jointext(failures, "\n")].") + else + pass("No overlapping string IDs.") + return TRUE 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/organ_tests.dm b/code/unit_tests/organ_tests.dm index 9dccaf2af2b..23cbef7cc3f 100644 --- a/code/unit_tests/organ_tests.dm +++ b/code/unit_tests/organ_tests.dm @@ -42,6 +42,9 @@ if(I.organ_tag != organ_tag) fail("[species.name] internal organ tag mismatch. Registered as \"[organ_tag]\", actual tag was \"[I.organ_tag]\".") . = 0 + if(!isnum(I.absolute_max_damage) || I.absolute_max_damage <= 0) + fail("[species.name] internal organ has invalid absolute_max_damage value ([I.absolute_max_damage]).") + . = 0 /datum/unit_test/species_organ_creation/proc/check_external_organs(var/mob/living/carbon/human/H, var/decl/species/species) . = 1 @@ -62,7 +65,10 @@ . = 0 continue if(E.organ_tag != organ_tag) - fail("[species.name] internal organ tag mismatch. Registered as \"[organ_tag]\", actual tag was \"[E.organ_tag]\".") + fail("[species.name] external organ tag mismatch. Registered as \"[organ_tag]\", actual tag was \"[E.organ_tag]\".") + . = 0 + if(!isnum(E.absolute_max_damage) || E.absolute_max_damage <= 0) + fail("[species.name] external organ has invalid absolute_max_damage value ([E.absolute_max_damage]).") . = 0 /datum/unit_test/species_organ_creation/proc/check_organ_parents(var/mob/living/carbon/human/H, var/decl/species/species) @@ -251,4 +257,141 @@ else pass("All organs were removed and replaced correctly.") - return 1 \ No newline at end of file + return 1 + +// ============================================================================== +// Stumps shall not drop +// ============================================================================== +/datum/unit_test/stumps_shall_not_drop + name = "ORGAN: Stumps Shall Not Drop From a Gibbed Mob or Severed Limbs." + +/datum/unit_test/stumps_shall_not_drop/proc/find_stumps() + var/list/found_stumps + //Look for stumps that aren't deleted + for(var/obj/item/organ/external/stump/O in world) + if(!QDELETED(O)) + LAZYDISTINCTADD(found_stumps, O) + return found_stumps + +/datum/unit_test/stumps_shall_not_drop/proc/fill_limb_with_stumps(var/obj/item/organ/external/E) + for(var/obj/item/organ/external/C in E.children) + //Gib child limbs to create stumps on our target organ + if(C.limb_flags & ORGAN_FLAG_CAN_AMPUTATE) + C.dismember(FALSE, DISMEMBER_METHOD_BLUNT, FALSE, TRUE) + +/datum/unit_test/stumps_shall_not_drop/proc/do_cleanup(var/mob/living/carbon/human/H) + if(H && !QDELETED(H)) + qdel(H) + for(var/obj/item/organ/O in (locate(/obj/item/organ) in world)) + qdel(O) + +//Check whether using the proper tool on a removed limb to extract the contents drops any stumps +/datum/unit_test/stumps_shall_not_drop/proc/test_removed_limb_dropping_stumps_on_interact(var/mob/living/carbon/human/H, var/mob/living/carbon/human/tester, var/list/details) + . = TRUE + details.Cut() + + var/list/limbs_to_test + for(var/obj/item/organ/external/O in H.get_external_organs()) + if(isnull(O.parent_organ) || !LAZYLEN(O.children) || !(O.limb_flags & ORGAN_FLAG_CAN_AMPUTATE)) + continue //We don't want the root limb since it won't gib, or limbs with no child + fill_limb_with_stumps(O) + //Amputate the limb via edge damage so it doesn't get gibbed + O.dismember(FALSE, DISMEMBER_METHOD_EDGE, FALSE, TRUE) + LAZYDISTINCTADD(limbs_to_test, O) + + //Test every single limbs we removed + var/obj/item/scalpel/tool = tester.get_active_hand() + for(var/obj/item/organ/external/E in limbs_to_test) + //Skip to the actual part where we remove things + E.stage = 2 + + //Poke it enough times to remove everything inside at least once + for(var/i = 0, i < LAZYLEN(E.contents), i++) + E.attackby(tool, tester) + + //Look for any dropped stumps + var/list/found_stumps = find_stumps() + if(LAZYLEN(found_stumps)) + details[E.organ_tag] = "Found [LAZYLEN(found_stumps)] stumps after extracting from removed limb type '[E.type]'!" + . = FALSE + + //Cleanup + do_cleanup(H) + +//Gib a limb with stumps inside and see if the stumps were dropped +/datum/unit_test/stumps_shall_not_drop/proc/test_gibbing_limbs(var/mob/living/carbon/human/H, var/list/details) + . = TRUE + details.Cut() + + //Gib limbs that have child stumps + for(var/obj/item/organ/external/O in H.get_external_organs()) + if(isnull(O.parent_organ) || !LAZYLEN(O.children) || !(O.limb_flags & ORGAN_FLAG_CAN_AMPUTATE)) + continue //We don't want the root limb since it won't gib, or limbs with no child + fill_limb_with_stumps(O) + //Then gib the part we just placed stumps on + O.dismember(FALSE, DISMEMBER_METHOD_BLUNT, FALSE, TRUE) + + //Look for any dropped stumps + var/list/found_stumps = find_stumps() + if(LAZYLEN(found_stumps)) + . = FALSE + details[O.organ_tag] = "Found [LAZYLEN(found_stumps)] stumps after gibbing limb type '[O.type]' with child stumps!" + + //Cleanup + do_cleanup(H) + +//Gibs a mob with stumps inside and see if any were dropped +/datum/unit_test/stumps_shall_not_drop/proc/test_gibbing(var/mob/living/carbon/human/H, var/list/details) + . = TRUE + details.Cut() + + //Remove all limbs via dismember to create stumps + for(var/obj/item/organ/external/O in H.get_external_organs()) + if(isnull(O.parent_organ) || !(O.limb_flags & ORGAN_FLAG_CAN_AMPUTATE)) + continue //We don't want the root limb since it won't gib + O.dismember(FALSE, DISMEMBER_METHOD_BLUNT, FALSE, TRUE) + + //Then gib the mob, so it releases its contents + if(!QDELETED(H)) + H.gib() + + //Look for any dropped stumps + var/list/found_stumps = find_stumps() + if(LAZYLEN(found_stumps)) + details["msg"] = "Found [LAZYLEN(found_stumps)] stumps after amputating all limbs, and gibbing human of species '[H.species?.name]'!" + . = FALSE + + //Cleanup + do_cleanup(H) + +/datum/unit_test/stumps_shall_not_drop/start_test() + var/list/details = list() + + //Equip our tester + var/mob/living/carbon/human/dummy/tester = new(null, SPECIES_HUMAN) + var/obj/item/scalpel/tool = new/obj/item/scalpel(tester) + tester.put_in_active_hand(tool) + + //Run the tests + for(var/decl/species/species in get_all_species()) + + if(!test_gibbing(new/mob/living/carbon/human(null, species.name), details)) + var/failtext = "" + for(var/k in details) + failtext = "[failtext]\n[k]: [details[k]]" + fail(failtext) + + if(!test_gibbing_limbs(new/mob/living/carbon/human(null, species.name), details)) + var/failtext = "" + for(var/k in details) + failtext = "[failtext]\n[k]: [details[k]]" + fail(failtext) + + if(!test_removed_limb_dropping_stumps_on_interact(new/mob/living/carbon/human(null, species.name), tester, details)) + var/failtext = "" + for(var/k in details) + failtext = "[failtext]\n[k]: [details[k]]" + fail(failtext) + + pass("All stumps tested were not dropped!") + return TRUE diff --git a/code/unit_tests/power_tests.dm b/code/unit_tests/power_tests.dm index fabd9e26ad7..2bea3bef634 100644 --- a/code/unit_tests/power_tests.dm +++ b/code/unit_tests/power_tests.dm @@ -71,18 +71,37 @@ /datum/unit_test/area_power_tally_accuracy name = "POWER: All areas must have accurate power use values." + var/list/channel_names = list("equip", "light", "environ") +/obj/machinery/test_machine + power_channel = EQUIP + idle_power_usage = 1000 + use_power = POWER_USE_IDLE + +/datum/unit_test/area_power_tally_accuracy/proc/check_power(var/area/A) + var/list/old_values = list(A.used_equip, A.used_light, A.used_environ) + A.retally_power() + var/list/new_values = list(A.used_equip, A.used_light, A.used_environ) + for(var/i in 1 to length(old_values)) + if(abs(old_values[i] - new_values[i]) > 1) // Round because there can in fact be roundoff error here apparently. + . = TRUE + log_bad("The area [A.name] had improper power use values on the [channel_names[i]] channel: was [old_values[i]] but should be [new_values[i]].") + log_bad("This area contained the following power-using machines on this channel:") + for(var/obj/machinery/machine in A) + if(machine.power_channel == i) + log_bad(log_info_line(machine) + " with power use [machine.get_power_usage()]") /datum/unit_test/area_power_tally_accuracy/start_test() var/failed = FALSE - var/list/channel_names = list("equip", "light", "environ") + for(var/area/A in global.areas) - var/list/old_values = list(A.used_equip, A.used_light, A.used_environ) - A.retally_power() - var/list/new_values = list(A.used_equip, A.used_light, A.used_environ) - for(var/i in 1 to length(old_values)) - if(abs(old_values[i] - new_values[i]) > 1) // Round because there can in fact be roundoff error here apparently. - failed = TRUE - log_bad("The area [A.name] had improper power use values on the [channel_names[i]] channel: was [old_values[i]] but should be [new_values[i]].") + var/area_failed = check_power(A) + if(!area_failed) // spawn a "hand-made" machine and check again + var/turf/T = locate() in A.contents + if(T) + var/obj/machinery/test_machine/machine = new (T) + area_failed = check_power(A) + qdel(machine) + failed |= area_failed if(failed) fail("At least one area had improper power use values") diff --git a/code/unit_tests/zas_tests.dm b/code/unit_tests/zas_tests.dm index e1be4a07248..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. // @@ -75,7 +67,7 @@ return test_result - if(UT_NORMAL || UT_NORMAL_COLD) + if(UT_NORMAL, UT_NORMAL_COLD) if(abs(pressure - ONE_ATMOSPHERE) > 10) test_result["msg"] = "Pressure out of bounds: [pressure] | [t_msg]" return test_result @@ -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/html/changelog.html b/html/changelog.html index baa1274e648..99961dda314 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -52,59 +52,160 @@ -->
    -

    27 January 2022

    +

    13 May 2022

    +

    MistakeNot4892 updated:

    +

    tag if you want to specify another name or several people. --> updated:

    -

    24 January 2022

    -

    PsyCommando updated:

    +

    12 May 2022

    +

    tag if you want to specify another name or several people. --> updated:

    -

    11 January 2022

    -

    Gaxeer updated:

    +

    10 May 2022

    +

    tag if you want to specify another name or several people. --> updated:

    -

    03 January 2022

    +

    09 May 2022

    tag if you want to specify another name or several people. --> updated:

    -

    04 December 2021

    -

    MistakeNot4892 updated:

    +

    04 May 2022

    +

    tag if you want to specify another name or several people. --> updated:

    -

    03 December 2021

    -

    NotRanged updated:

    +

    29 April 2022

    +

    tag if you want to specify another name or several people. --> updated:

    -

    PsyCommando updated:

    + +

    25 April 2022

    +

    NataKilar updated:

    -

    02 December 2021

    -

    PsyCommando updated:

    +

    16 April 2022

    +

    NataKilar updated:

    + + +

    10 April 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    28 March 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    24 March 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    21 March 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    14 March 2022

    +

    keIgaras updated:

    + +

    tag if you want to specify another name or several people. --> updated:

    -

    01 December 2021

    +

    02 March 2022

    +

    NataKilar updated:

    + + +

    24 February 2022

    +

    SierraKomodo updated:

    + +

    tag if you want to specify another name or several people. --> updated:

    + + +

    20 February 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    18 February 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    16 February 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    14 February 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    02 February 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    27 January 2022

    +

    tag if you want to specify another name or several people. --> updated:

    + + +

    24 January 2022

    PsyCommando updated:

    27 November 2021

    diff --git a/html/changelogs/.all_changelog.yml b/html/changelogs/.all_changelog.yml index 5ab234c48be..4aa2cbbdac9 100644 --- a/html/changelogs/.all_changelog.yml +++ b/html/changelogs/.all_changelog.yml @@ -14076,3 +14076,93 @@ DO NOT EDIT THIS FILE BY HAND! AUTOMATICALLY GENERATED BY ss13_genchangelog.py. 2022-01-27: tag if you want to specify another name or several people. -->: - bugfix: Brute damage healing medications now heal brute damage again.. +2022-02-02: + tag if you want to specify another name or several people. -->: + - bugfix: Sometimes, thrown fruit smudges would fail to save. That's fixed now. +2022-02-14: + tag if you want to specify another name or several people. -->: + - balance: if you hit a person against a window while they are in a grapple, you + release them from the grapple, also increased the duration of the weakness effect + from 1 to 2 seconds + - bugfix: Network relays will now attempt reconnect on their own, so if router had + a blackout, they'll rejoin on their own without manual reboot. +2022-02-16: + tag if you want to specify another name or several people. -->: + - bugfix: You can force move mob on catwalk in passive grab +2022-02-18: + tag if you want to specify another name or several people. -->: + - bugfix: Fix bug where sprites for open doors were not displayed +2022-02-20: + tag if you want to specify another name or several people. -->: + - bugfix: Cannot phase onto low walls through solid windows anymore. + - bugfix: Shuttle chairs now display their bars in raised state too +2022-02-24: + SierraKomodo: + - rscadd: You can now remove ID cards from wallets with AltClick. This only works + for human mobs, and only if you're holding or wearing the wallet. + tag if you want to specify another name or several people. -->: + - tweak: Change the location of the ui_storage, now it is more convenient +2022-03-02: + NataKilar: + - bugfix: Fixed a bug where wall damage would not be retained when moving a shuttle +2022-03-14: + keIgaras: + - rscadd: added sofa, rounded chairs and updated armchair sprites + - tweak: fixed names of chairs in the construction panel + tag if you want to specify another name or several people. -->: + - bugfix: Deaf mobs no longer see 'You hear something about' messages while asleep. + - tweak: fixed names of chairs in the construction panel +2022-03-21: + tag if you want to specify another name or several people. -->: + - bugfix: Fixes potential index out-of-bound error when filling grown foods with + reagents. +2022-03-24: + tag if you want to specify another name or several people. -->: + - bugfix: Brains removed from someone's head now properly gets renamed to whoever + the brain owner was. + - bugfix: Organs now properly deleted in some cases where they wouldn't be. +2022-03-28: + tag if you want to specify another name or several people. -->: + - tweak: Refactored fabricator UI. +2022-04-10: + tag if you want to specify another name or several people. -->: + - bugfix: Explosion won't damage a human mob with the godmode flag on anymore. +2022-04-16: + NataKilar: + - bugfix: Fixes a bug causing windows and pipes to be constructed incorrectly. +2022-04-25: + NataKilar: + - tweak: Machinery which previously connected to cables directly for power now require + terminals, which can be added by attacking the machine with a stack of cables. +2022-04-29: + tag if you want to specify another name or several people. -->: + - bugfix: Fix organ surgery. + - bugfix: Manually spawned human mobs of the "human" species now spawn with a language. +2022-05-04: + tag if you want to specify another name or several people. -->: + - tweak: Greeting message now check if your starting loadout actually gives you + a headset before advising you on how to talk through your headset.. +2022-05-09: + tag if you want to specify another name or several people. -->: + - bugfix: Godmode human now properly ignore fall damage, shock, and dislocation + effects. + - bugfix: Fix heart not restarting on rejuv. +2022-05-10: + tag if you want to specify another name or several people. -->: + - rscadd: Adds user accounts and network groups which allows for custom access systems + tied to computer networks + - rscadd: Adds access requirements tied to computer files for read/write + - rscdel: Removes previous grant based computer network access system + - tweak: You can now hack industrial fabs to print shields, or craft a buckler from + a stool. +2022-05-12: + tag if you want to specify another name or several people. -->: + - tweak: Engine power use has been decreased substantially. +2022-05-13: + MistakeNot4892: + - tweak: You can now carry things up ladders and through space with grabs. + tag if you want to specify another name or several people. -->: + - bugfix: Dislocated limbs now show in medical scans again. + - bugfix: Dislocating someone's limb now causes pain again. + - tweak: Now get feedback when trying to use a jointlock or dislocate someone's + limbs and don't have the required skills. diff --git a/html/changelogs/AutoChangeLog-pr-2333.yml b/html/changelogs/AutoChangeLog-pr-2333.yml new file mode 100644 index 00000000000..147f170404f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-2333.yml @@ -0,0 +1,6 @@ +author: tag if you want to specify another name or several people. --> +delete-after: true +changes: + - tweak: Upward pointing cable stubs now give proper feedback, and install properly + when clicked on with a cable coil in hands. You used to need to click on the + turf instead before, which was a bit confusing. diff --git a/icons/mob/simple_animal/mouse_brown.dmi b/icons/mob/simple_animal/mouse_brown.dmi index 07342b037bb..d0d96de3b96 100644 Binary files a/icons/mob/simple_animal/mouse_brown.dmi and b/icons/mob/simple_animal/mouse_brown.dmi differ diff --git a/icons/mob/simple_animal/mouse_gray.dmi b/icons/mob/simple_animal/mouse_gray.dmi index 75ae47282b0..b654709066e 100644 Binary files a/icons/mob/simple_animal/mouse_gray.dmi and b/icons/mob/simple_animal/mouse_gray.dmi differ diff --git a/icons/mob/simple_animal/mouse_white.dmi b/icons/mob/simple_animal/mouse_white.dmi index a555c025406..2117dde5251 100644 Binary files a/icons/mob/simple_animal/mouse_white.dmi and b/icons/mob/simple_animal/mouse_white.dmi differ diff --git a/icons/obj/crafting_icons.dmi b/icons/obj/crafting_icons.dmi index 2e3ffbce3dc..59c11464010 100644 Binary files a/icons/obj/crafting_icons.dmi and b/icons/obj/crafting_icons.dmi differ diff --git a/icons/obj/furniture.dmi b/icons/obj/furniture.dmi index fb3ef87ae24..5b98322c935 100644 Binary files a/icons/obj/furniture.dmi and b/icons/obj/furniture.dmi differ diff --git a/icons/obj/power.dmi b/icons/obj/power.dmi index bb06db5de2d..14674be753d 100644 Binary files a/icons/obj/power.dmi and b/icons/obj/power.dmi differ diff --git a/icons/obj/stairs_64.dmi b/icons/obj/stairs_64.dmi new file mode 100644 index 00000000000..b0c14147e5f Binary files /dev/null and b/icons/obj/stairs_64.dmi differ diff --git a/icons/obj/vending.dmi b/icons/obj/vending.dmi index dfaf341a56e..91cf6530e50 100644 Binary files a/icons/obj/vending.dmi and b/icons/obj/vending.dmi differ diff --git a/icons/screen/radial.dmi b/icons/screen/radial.dmi index 153be625442..7c14b65935d 100644 Binary files a/icons/screen/radial.dmi and b/icons/screen/radial.dmi differ diff --git a/interface/skin.dmf b/interface/skin.dmf index 50e68f4ec05..1201b778688 100644 --- a/interface/skin.dmf +++ b/interface/skin.dmf @@ -14,7 +14,12 @@ menu "menu" command = "" saved-params = "is-checked" elem - name = "&Save screenshot as...\tShift+F2" + name = "&Save screenshot\tF2" + command = ".auto" + category = "&File" + saved-params = "is-checked" + elem + name = "&Save screenshot as..." command = ".screenshot" category = "&File" saved-params = "is-checked" diff --git a/maps/antag_spawn/ert/ert_base.dmm b/maps/antag_spawn/ert/ert_base.dmm index b75508d2d58..7bbbc6ee74b 100644 --- a/maps/antag_spawn/ert/ert_base.dmm +++ b/maps/antag_spawn/ert/ert_base.dmm @@ -2101,7 +2101,7 @@ }, /area/map_template/rescue_base/base) "eu" = ( -/obj/machinery/power/emitter, +/obj/machinery/emitter, /turf/unsimulated/floor{ dir = 1; icon_state = "vault" diff --git a/maps/antag_spawn/mercenary/mercenary_base.dmm b/maps/antag_spawn/mercenary/mercenary_base.dmm index 97fbc7c8713..e1afff1dba0 100644 --- a/maps/antag_spawn/mercenary/mercenary_base.dmm +++ b/maps/antag_spawn/mercenary/mercenary_base.dmm @@ -1588,7 +1588,7 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/map_template/merc_shuttle/rear) "cS" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/structure/cable/yellow{ icon_state = "0-8" }, @@ -2041,7 +2041,7 @@ /turf/simulated/floor/plating, /area/map_template/merc_spawn) "nN" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/structure/railing/mapped/no_density{ dir = 4 }, @@ -2163,7 +2163,7 @@ /turf/simulated/floor/tiled/airless, /area/space) "uG" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/structure/railing/mapped/no_density{ dir = 4 }, @@ -2596,7 +2596,7 @@ /turf/simulated/floor/plating, /area/map_template/merc_spawn) "PK" = ( -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /turf/simulated/floor/plating, /area/map_template/merc_spawn) "PZ" = ( @@ -2681,7 +2681,7 @@ /turf/simulated/floor/plating, /area/map_template/merc_spawn) "Tu" = ( -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /obj/structure/railing/mapped/no_density, /turf/simulated/floor/plating, /area/map_template/merc_spawn) diff --git a/maps/away/bearcat/bearcat-1.dmm b/maps/away/bearcat/bearcat-1.dmm index 1b32339b73e..7f94eb58712 100644 --- a/maps/away/bearcat/bearcat-1.dmm +++ b/maps/away/bearcat/bearcat-1.dmm @@ -1412,7 +1412,7 @@ /turf/simulated/floor/tiled/usedup, /area/ship/scrap/cargo/lower) "dc" = ( -/obj/structure/stairs/east, +/obj/structure/stairs/long/east, /obj/effect/floor_decal/industrial/warning{ dir = 1; icon_state = "warning" diff --git a/maps/away/bearcat/bearcat-2.dmm b/maps/away/bearcat/bearcat-2.dmm index 6b5e412df31..fe706b83f16 100644 --- a/maps/away/bearcat/bearcat-2.dmm +++ b/maps/away/bearcat/bearcat-2.dmm @@ -4392,7 +4392,7 @@ /turf/simulated/floor/tiled/usedup, /area/ship/scrap/maintenance/power) "in" = ( -/obj/machinery/power/shield_generator, +/obj/machinery/shield_generator, /obj/structure/cable{ icon_state = "0-2"; pixel_y = 1 @@ -5377,7 +5377,7 @@ /obj/structure/cable{ icon_state = "0-6" }, -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /turf/simulated/floor/usedup, /area/ship/scrap/hidden) "Qe" = ( diff --git a/maps/away/derelict/derelict-station.dmm b/maps/away/derelict/derelict-station.dmm index db1287f4ad6..0137b7fad12 100644 --- a/maps/away/derelict/derelict-station.dmm +++ b/maps/away/derelict/derelict-station.dmm @@ -1393,7 +1393,7 @@ /obj/structure/cable/blue{ icon_state = "0-2" }, -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/dark, /area/constructionsite/teleporter) @@ -1461,7 +1461,7 @@ /area/constructionsite/teleporter) "eW" = ( /obj/structure/cable/blue, -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/machinery/power/terminal, /obj/effect/floor_decal/industrial/outline/yellow, /turf/simulated/floor/tiled/dark, @@ -2959,7 +2959,7 @@ /turf/simulated/floor/airless, /area/constructionsite) "kr" = ( -/obj/machinery/power/shield_generator{ +/obj/machinery/shield_generator{ desc = "A heavy-duty shield generator and capacitor, capable of generating energy shields at large distances. This one seems to be in a state of disrepair."; name = "disused shield generator" }, @@ -3128,7 +3128,7 @@ /turf/simulated/floor/airless, /area/constructionsite/engineering) "le" = ( -/obj/machinery/power/rad_collector, +/obj/machinery/rad_collector, /turf/simulated/floor/airless, /area/constructionsite/engineering) "lf" = ( @@ -3379,7 +3379,7 @@ /turf/simulated/floor/airless, /area/AIsattele) "lZ" = ( -/obj/machinery/power/emitter{ +/obj/machinery/emitter{ anchored = 1; dir = 4; state = 2 @@ -3391,7 +3391,7 @@ /turf/simulated/floor/airless, /area/constructionsite/engineering) "mb" = ( -/obj/machinery/power/emitter{ +/obj/machinery/emitter{ anchored = 1; dir = 8; state = 2 diff --git a/maps/away/errant_pisces/errant_pisces.dmm b/maps/away/errant_pisces/errant_pisces.dmm index 3046e1e3fc0..82a9dcda48f 100644 --- a/maps/away/errant_pisces/errant_pisces.dmm +++ b/maps/away/errant_pisces/errant_pisces.dmm @@ -1853,7 +1853,7 @@ /turf/simulated/floor/plating, /area/errant_pisces/smes_room) "eQ" = ( -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /obj/structure/cable/yellow{ icon_state = "0-8" }, @@ -1982,7 +1982,7 @@ /turf/simulated/floor/plating, /area/errant_pisces/smes_room) "ff" = ( -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /obj/structure/cable/yellow{ icon_state = "0-8" }, @@ -2137,7 +2137,7 @@ /turf/simulated/floor/plating, /area/errant_pisces/smes_room) "fx" = ( -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /obj/structure/cable/yellow{ icon_state = "0-8" }, @@ -4878,7 +4878,7 @@ /turf/simulated/wall/r_wall, /area/errant_pisces/aux_power) "nl" = ( -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /obj/structure/cable/yellow{ icon_state = "0-4" }, @@ -5183,7 +5183,7 @@ /turf/simulated/floor/plating, /area/errant_pisces/aft_hallway) "nK" = ( -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /obj/structure/cable/yellow{ icon_state = "0-2" }, diff --git a/maps/away/liberia/liberia.dmm b/maps/away/liberia/liberia.dmm index 6c9c1c167d2..a829c69fc09 100644 --- a/maps/away/liberia/liberia.dmm +++ b/maps/away/liberia/liberia.dmm @@ -215,7 +215,7 @@ /turf/simulated/floor, /area/liberia/engineeringreactor) "aA" = ( -/obj/machinery/power/port_gen/pacman/super/potato, +/obj/machinery/port_gen/pacman/super/potato, /obj/structure/cable{ icon_state = "0-8" }, @@ -376,7 +376,7 @@ /turf/simulated/floor/tiled/techfloor, /area/liberia/merchantstorage) "aR" = ( -/obj/machinery/power/shield_generator, +/obj/machinery/shield_generator, /obj/structure/cable, /obj/structure/cable/blue{ icon_state = "1-2" @@ -4912,7 +4912,7 @@ /obj/machinery/light{ dir = 8 }, -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /obj/effect/floor_decal/industrial/warning/full, /obj/structure/cable/blue{ icon_state = "0-4" diff --git a/maps/away/magshield/magshield.dmm b/maps/away/magshield/magshield.dmm index bc93214fe4e..ed4ac1d7c4a 100644 --- a/maps/away/magshield/magshield.dmm +++ b/maps/away/magshield/magshield.dmm @@ -773,7 +773,7 @@ /obj/structure/cable/yellow{ icon_state = "0-8" }, -/obj/machinery/power/generator{ +/obj/machinery/generator{ anchored = 1 }, /turf/simulated/floor/plating, @@ -1384,7 +1384,7 @@ /turf/simulated/floor/airless, /area/magshield/north) "ea" = ( -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /turf/simulated/floor/airless, /area/magshield/north) "eb" = ( diff --git a/maps/away/slavers/slavers_base.dmm b/maps/away/slavers/slavers_base.dmm index 24cf488dcbe..a8f17ef3edd 100644 --- a/maps/away/slavers/slavers_base.dmm +++ b/maps/away/slavers/slavers_base.dmm @@ -2993,14 +2993,14 @@ /turf/simulated/floor/airless/ceiling, /area/slavers_base/powatm) "iq" = ( -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /obj/structure/cable/green{ icon_state = "0-4" }, /turf/simulated/floor/airless/ceiling, /area/slavers_base/powatm) "ir" = ( -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /obj/structure/cable/green{ icon_state = "0-4" }, @@ -3010,7 +3010,7 @@ /turf/simulated/floor/airless/ceiling, /area/slavers_base/powatm) "is" = ( -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /obj/structure/cable/green{ icon_state = "0-4" }, diff --git a/maps/away/smugglers/smugglers.dmm b/maps/away/smugglers/smugglers.dmm index 57b4550275e..85127e1fdc8 100644 --- a/maps/away/smugglers/smugglers.dmm +++ b/maps/away/smugglers/smugglers.dmm @@ -157,7 +157,7 @@ /turf/simulated/floor, /area/smugglers/base) "az" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/structure/cable{ icon_state = "0-2" }, diff --git a/maps/away/unishi/unishi-1.dmm b/maps/away/unishi/unishi-1.dmm index 050f9ded4ca..3f1470d106a 100644 --- a/maps/away/unishi/unishi-1.dmm +++ b/maps/away/unishi/unishi-1.dmm @@ -9,7 +9,7 @@ /turf/simulated/wall, /area/space) "ad" = ( -/obj/machinery/power/port_gen/pacman/super/potato, +/obj/machinery/port_gen/pacman/super/potato, /obj/structure/cable/yellow{ icon_state = "0-4" }, @@ -52,7 +52,7 @@ /turf/simulated/wall, /area/unishi/engineering) "aj" = ( -/obj/machinery/power/port_gen/pacman/super/potato, +/obj/machinery/port_gen/pacman/super/potato, /obj/structure/cable/yellow{ icon_state = "0-4" }, diff --git a/maps/away/unishi/unishi-2.dmm b/maps/away/unishi/unishi-2.dmm index 8cc5b5df540..70dd831b9a7 100644 --- a/maps/away/unishi/unishi-2.dmm +++ b/maps/away/unishi/unishi-2.dmm @@ -407,7 +407,7 @@ /turf/simulated/floor, /area/unishi/common) "bq" = ( -/obj/structure/stairs/east, +/obj/structure/stairs/long/east, /obj/structure/railing/mapped, /turf/simulated/floor, /area/unishi/common) @@ -2450,13 +2450,14 @@ /turf/simulated/floor/tiled/techfloor, /area/unishi/smresearch) "gF" = ( -/obj/machinery/power/emitter/anchored/on, +/obj/machinery/emitter/anchored/on, /obj/machinery/atmospherics/pipe/simple/visible/black{ dir = 4 }, /obj/structure/cable/yellow{ icon_state = "0-8" }, +/obj/machinery/power/terminal, /obj/machinery/atmospherics/binary/pump/high_power, /obj/effect/floor_decal/industrial/warning, /turf/simulated/floor/tiled/techfloor, diff --git a/maps/away/unishi/unishi.dm b/maps/away/unishi/unishi.dm index bd2d660731e..246ee4ca308 100644 --- a/maps/away/unishi/unishi.dm +++ b/maps/away/unishi/unishi.dm @@ -77,7 +77,7 @@ oxygen_release_modifier = 100000000000 radiation_release_modifier = 1 -/obj/machinery/power/emitter/anchored/on +/obj/machinery/emitter/anchored/on active = 1 powered = 1 diff --git a/maps/example/example-1.dmm b/maps/example/example-1.dmm index b44cab611b5..36f93e32925 100644 --- a/maps/example/example-1.dmm +++ b/maps/example/example-1.dmm @@ -1,42 +1,81 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE -"ck" = ( -/obj/structure/table/steel, -/obj/machinery/recharger, +"af" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 10 + }, +/obj/machinery/light{ + dir = 4 + }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"cS" = ( -/obj/machinery/fabricator/hacked, +/area/example/first) +"aL" = ( +/obj/machinery/computer/arcade, +/obj/effect/floor_decal/corner/orange/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"bK" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 + }, +/obj/machinery/alarm{ + pixel_y = 24 + }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"cV" = ( -/obj/machinery/computer/shuttle_control{ - shuttle_tag = "Example" +/area/example/first) +"bY" = ( +/obj/effect/floor_decal/industrial/warning/corner{ + dir = 1 }, -/turf/simulated/floor/plating, -/area/shuttle/escape) -"eg" = ( -/turf/simulated/wall, -/area/constructionsite) -"fQ" = ( -/obj/machinery/door/airlock/external/bolted{ - id_tag = "lower_level_dock_hatch_external" +/obj/effect/floor_decal/corner/orange{ + dir = 10 }, -/obj/machinery/button/access/exterior{ - id_tag = "lower_level_dock"; - pixel_y = -21 +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"ck" = ( +/obj/structure/lattice, +/turf/space, +/area/space) +"co" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 1 }, -/turf/simulated/floor, -/area/constructionsite) -"fT" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ - dir = 6 +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"cy" = ( +/obj/machinery/vending/snack, +/obj/effect/floor_decal/corner/orange/half{ + dir = 1 }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"cF" = ( +/obj/structure/rack, +/obj/item/gun/projectile/revolver, +/obj/item/gun/projectile/revolver, +/obj/item/gun/projectile/revolver, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"fU" = ( -/turf/simulated/wall/r_wall, +/area/example/first) +"cS" = ( +/obj/structure/grille, +/turf/space, /area/space) -"gO" = ( +"cV" = ( +/obj/effect/wallframe_spawn/reinforced/titanium, +/turf/simulated/floor/plating, +/area/shuttle/ferry) +"du" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 4 + }, +/obj/machinery/light_switch/on{ + dir = 1; + pixel_y = -24 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"dE" = ( /obj/structure/rack, /obj/item/stack/material/reinforced/mapped/plasteel/fifty, /obj/item/stack/material/ingot/mapped/copper/fifty, @@ -51,391 +90,1297 @@ /obj/item/storage/belt/utility/full, /obj/item/stack/material/sheet/mapped/steel/fifty, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"hw" = ( -/obj/machinery/embedded_controller/radio/airlock/docking_port{ - id_tag = "lower_level_dock"; - pixel_y = 24; - tag_airpump = "lower_level_dock_pump"; - tag_chamber_sensor = "lower_level_dock_sensor_chamber"; - tag_exterior_door = "lower_level_dock_hatch_external"; - tag_interior_door = "lower_level_dock_hatch_internal" - }, -/obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{ - dir = 4; - id_tag = "lower_level_dock_pump" - }, -/obj/machinery/airlock_sensor{ - id_tag = "lower_level_dock_sensor_chamber"; - pixel_y = -21 - }, -/turf/simulated/floor, -/area/constructionsite) -"iM" = ( +/area/example/first) +"eh" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"jg" = ( -/obj/machinery/door/airlock/external/bolted{ - id_tag = "example_shuttle_port_hatch" +/area/example/first) +"eZ" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 6 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"fa" = ( +/obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{ + dir = 8 + }, +/turf/simulated/floor, +/area/example/first) +"fb" = ( +/obj/machinery/computer/shuttle_control{ + shuttle_tag = "Testing Site Ferry" }, /turf/simulated/floor/plating, -/area/shuttle/escape) -"jB" = ( -/obj/structure/rack, -/obj/item/gun/projectile/automatic/assault_rifle, -/obj/item/gun/projectile/automatic/assault_rifle, -/obj/item/gun/projectile/automatic/assault_rifle, +/area/shuttle/ferry) +"fu" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 9 + }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"jO" = ( -/obj/effect/floor_decal/industrial/warning/corner{ +/area/example/first) +"gk" = ( +/obj/machinery/vending/cola, +/obj/machinery/light{ + dir = 1 + }, +/obj/effect/floor_decal/corner/orange/half{ dir = 1 }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"gO" = ( +/turf/simulated/wall/titanium, +/area/shuttle/ferry) +"gT" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 6 + }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"jV" = ( -/obj/structure/ladder, +/area/example/first) +"he" = ( /obj/effect/floor_decal/industrial/warning{ dir = 4 }, +/obj/machinery/atmospherics/pipe/manifold/hidden, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"kw" = ( -/obj/machinery/light_switch/on{ - dir = 8; - pixel_x = 24 - }, +/area/example/first) +"hA" = ( /obj/effect/floor_decal/corner/orange{ dir = 6 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"oy" = ( -/obj/abstract/level_data/main_level, -/turf/space, -/area/space) -"oZ" = ( -/obj/structure/cable, -/obj/machinery/power/debug_items/infinite_generator, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/constructionsite) -"pm" = ( +/area/example/first) +"ii" = ( /obj/effect/floor_decal/corner/orange{ - dir = 5 + dir = 10 }, -/obj/machinery/computer/teleporter, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"qt" = ( -/obj/effect/floor_decal/industrial/warning/corner, -/obj/effect/floor_decal/corner/orange{ - dir = 5 +/area/example/first) +"jg" = ( +/obj/machinery/embedded_controller/radio/simple_docking_controller{ + dir = 4; + id_tag = "example_shuttle_port"; + pixel_x = -19; + tag_door = "example_shuttle_port_hatch" }, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"qJ" = ( -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"rY" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ - dir = 9 +/obj/machinery/embedded_controller/radio/simple_docking_controller{ + dir = 8; + id_tag = "example_shuttle_starboard"; + pixel_x = 19; + tag_door = "example_shuttle_starboard_hatch" }, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"se" = ( -/obj/structure/rack, -/obj/item/gun/energy/laser, -/obj/item/gun/energy/laser, -/obj/item/gun/energy/laser, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"sQ" = ( -/obj/machinery/atmospherics/portables_connector, -/obj/machinery/portable_atmospherics/canister/air/airlock, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/constructionsite) -"sT" = ( -/obj/machinery/light/spot, +/obj/machinery/light/small, /turf/simulated/floor/plating, -/area/shuttle/escape) -"tg" = ( -/obj/machinery/light_switch/on{ - dir = 4; - pixel_x = -24 - }, -/obj/effect/floor_decal/corner/orange{ - dir = 9 +/area/shuttle/ferry) +"jB" = ( +/obj/machinery/door/airlock, +/turf/simulated/floor/tiled/steel_ridged, +/area/example/first) +"jP" = ( +/obj/machinery/atmospherics/portables_connector{ + dir = 8 }, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"tq" = ( -/obj/structure/rack, -/obj/item/gun/projectile/revolver, -/obj/item/gun/projectile/revolver, -/obj/item/gun/projectile/revolver, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"tE" = ( -/obj/effect/floor_decal/industrial/warning{ +/obj/machinery/portable_atmospherics/canister/air, +/obj/effect/floor_decal/industrial/outline/blue, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"kw" = ( +/obj/abstract/landmark/start, +/obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"us" = ( -/obj/abstract/landmark/latejoin, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"vd" = ( -/obj/machinery/atmospherics/unary/vent_pump/on, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"wj" = ( -/obj/item/radio/beacon, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"wB" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ +/area/example/first) +"la" = ( +/obj/machinery/door/airlock/external/bolted{ + id_tag = "lower_level_dock_hatch_internal" + }, +/obj/machinery/button/access/interior{ + id_tag = "lower_level_dock"; + pixel_x = -21 + }, +/turf/simulated/floor, +/area/example/first) +"lJ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden, +/obj/machinery/light{ dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"wF" = ( -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/constructionsite) -"xk" = ( -/turf/simulated/wall/titanium, -/area/shuttle/escape) -"xF" = ( -/obj/turbolift_map_holder/example, -/turf/simulated/floor/plating, -/area/constructionsite) -"yN" = ( -/obj/effect/floor_decal/industrial/warning/corner{ - dir = 8 +/area/example/first) +"lN" = ( +/obj/effect/floor_decal/industrial/outline/red, +/obj/machinery/atmospherics/portables_connector, +/obj/machinery/portable_atmospherics/canister/empty, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"mc" = ( +/obj/machinery/light{ + dir = 1 }, -/obj/effect/floor_decal/corner/orange{ +/obj/structure/ladder, +/obj/effect/floor_decal/industrial/warning{ + dir = 4 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"mo" = ( +/obj/effect/floor_decal/industrial/warning{ dir = 5 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Af" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Bv" = ( -/obj/abstract/landmark/start, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"BV" = ( -/obj/machinery/atmospherics/portables_connector{ +/area/example/first) +"mr" = ( +/obj/machinery/light{ dir = 8 }, -/obj/machinery/portable_atmospherics/canister/empty, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/constructionsite) -"EO" = ( -/obj/effect/floor_decal/corner/orange{ +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"mu" = ( +/obj/effect/floor_decal/industrial/outline/blue, +/obj/machinery/portable_atmospherics/canister/air, +/obj/machinery/atmospherics/portables_connector, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"nL" = ( +/obj/machinery/vending/coffee, +/obj/effect/floor_decal/corner/orange/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"on" = ( +/obj/effect/floor_decal/industrial/warning{ dir = 5 }, +/obj/machinery/atmospherics/unary/vent_pump/on, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Fe" = ( -/obj/effect/floor_decal/industrial/warning, +/area/example/first) +"ou" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden, +/turf/simulated/floor, +/area/example/first) +"oy" = ( /obj/effect/floor_decal/corner/orange{ - dir = 5 + dir = 9 + }, +/obj/machinery/light{ + dir = 8 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Gj" = ( -/obj/machinery/light/spot{ +/area/example/first) +"oz" = ( +/obj/structure/table, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"oB" = ( +/obj/effect/floor_decal/industrial/warning{ dir = 8 }, -/obj/effect/floor_decal/corner/orange{ - dir = 9 +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Hk" = ( -/turf/space, -/area/space) -"Iv" = ( -/obj/effect/floor_decal/corner/orange/mono, -/obj/machinery/teleport/hub, -/turf/simulated/floor/tiled/monotile, -/area/constructionsite) -"JG" = ( +/area/example/first) +"oI" = ( /obj/effect/floor_decal/corner/orange{ - dir = 9 - }, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"JO" = ( -/obj/effect/wallframe_spawn/reinforced/titanium, -/turf/simulated/floor/plating, -/area/shuttle/escape) -"La" = ( -/obj/structure/bed/chair{ - dir = 1 + dir = 6 }, -/obj/machinery/embedded_controller/radio/simple_docking_controller{ +/obj/machinery/light_switch/on{ dir = 8; - id_tag = "example_shuttle_starboard"; - pixel_x = 19; - tag_door = "example_shuttle_starboard_hatch" - }, -/obj/machinery/embedded_controller/radio/simple_docking_controller{ - dir = 4; - id_tag = "example_shuttle_port"; - pixel_x = -19; - tag_door = "example_shuttle_port_hatch" - }, -/obj/effect/shuttle_landmark/lower_level, -/turf/simulated/floor/plating, -/area/shuttle/escape) -"Mo" = ( -/obj/machinery/light/spot{ - dir = 4 + pixel_x = 24 }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"oS" = ( /obj/effect/floor_decal/corner/orange{ dir = 6 }, +/obj/machinery/light{ + dir = 4 + }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Nu" = ( +/area/example/first) +"oZ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden, +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"pv" = ( /obj/structure/rack, -/obj/item/gun/projectile/shotgun/pump, -/obj/item/gun/projectile/shotgun/pump, -/obj/item/gun/projectile/shotgun/pump, +/obj/item/gun/energy/laser, +/obj/item/gun/energy/laser, +/obj/item/gun/energy/laser, +/obj/machinery/light, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"NQ" = ( -/obj/effect/floor_decal/corner/orange{ +/area/example/first) +"qs" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/first) +"qB" = ( +/obj/machinery/teleport/hub, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"qR" = ( +/obj/machinery/power/debug_items/infinite_generator, +/obj/structure/cable/yellow, +/turf/simulated/floor, +/area/example/first) +"rg" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ dir = 6 }, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Pw" = ( -/obj/structure/cable{ - icon_state = "0-2" +/turf/simulated/floor, +/area/example/first) +"rH" = ( +/obj/machinery/door/airlock/external/bolted{ + id_tag = "example_first_hatch_external" }, -/obj/machinery/power/apc{ - dir = 8; - name = "west bump"; - pixel_x = -24 +/turf/simulated/floor, +/area/example/first) +"se" = ( +/obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{ + id_tag = "lower_level_dock_pump" }, -/obj/effect/floor_decal/corner/orange{ - dir = 9 +/obj/machinery/light/small{ + dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"PA" = ( +/turf/simulated/floor, +/area/example/first) +"sQ" = ( +/obj/machinery/atmospherics/portables_connector, +/obj/machinery/portable_atmospherics/canister/air/airlock, +/obj/effect/floor_decal/corner/orange/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"sT" = ( /obj/effect/floor_decal/industrial/warning/corner{ dir = 4 }, +/obj/effect/floor_decal/corner/orange/three_quarters, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"QF" = ( -/obj/machinery/atmospherics/portables_connector{ +/area/example/first) +"th" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"tl" = ( +/obj/effect/floor_decal/corner/orange{ + dir = 9 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"to" = ( +/obj/machinery/light{ dir = 8 }, -/obj/machinery/portable_atmospherics/canister/air, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/constructionsite) -"RW" = ( -/obj/machinery/light/spot{ +/turf/simulated/floor, +/area/example/first) +"tq" = ( +/obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{ + id_tag = "lower_level_dock_pump" + }, +/obj/machinery/airlock_sensor{ + id_tag = "lower_level_dock_sensor_chamber"; + pixel_x = -24 + }, +/obj/machinery/embedded_controller/radio/airlock/docking_port{ + id_tag = "lower_level_dock"; + pixel_y = 24; + tag_airpump = "lower_level_dock_pump"; + tag_chamber_sensor = "lower_level_dock_sensor_chamber"; + tag_exterior_door = "lower_level_dock_hatch_external"; + tag_interior_door = "lower_level_dock_hatch_internal" + }, +/turf/simulated/floor, +/area/example/first) +"tt" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/manifold/hidden{ dir = 1 }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"ty" = ( /obj/effect/floor_decal/corner/orange{ - dir = 5 + dir = 10 }, +/obj/machinery/light, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Su" = ( +/area/example/first) +"tA" = ( +/obj/turbolift_map_holder/example, +/turf/simulated/floor, +/area/turbolift/example/first) +"uD" = ( +/obj/machinery/light, +/turf/simulated/floor, +/area/example/first) +"uZ" = ( +/obj/structure/railing/mapped{ + dir = 8 + }, +/turf/simulated/floor, +/area/example/first) +"vg" = ( /obj/machinery/door/airlock/external/bolted{ id_tag = "example_shuttle_starboard_hatch" }, /turf/simulated/floor/plating, -/area/shuttle/escape) -"Sv" = ( -/obj/effect/floor_decal/industrial/warning{ - dir = 1 - }, -/turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Vt" = ( -/turf/simulated/floor/plating, -/area/constructionsite) -"VY" = ( +/area/shuttle/ferry) +"vi" = ( +/obj/structure/railing/mapped, +/turf/simulated/floor, +/area/example/first) +"vl" = ( /obj/machinery/atmospherics/pipe/simple/hidden{ - dir = 4 + dir = 5 }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"vK" = ( /obj/machinery/door/airlock/external/bolted{ id_tag = "lower_level_dock_hatch_internal" }, -/obj/machinery/button/access/interior{ - id_tag = "lower_level_dock"; - pixel_y = -21 +/obj/machinery/atmospherics/pipe/simple/hidden, +/turf/simulated/floor, +/area/example/first) +"vZ" = ( +/obj/structure/table, +/obj/item/card/id/captains_spare, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"wj" = ( +/obj/effect/floor_decal/industrial/warning, +/obj/effect/floor_decal/corner/orange{ + dir = 5 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"wo" = ( +/obj/structure/ladder, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"wv" = ( +/obj/machinery/door/airlock/external/glass/bolted{ + id_tag = "example_first_hatch_internal" }, /turf/simulated/floor, -/area/constructionsite) -"Wr" = ( -/obj/effect/floor_decal/industrial/warning/fulltile, +/area/example/first) +"wG" = ( +/obj/machinery/vending/cigarette, +/obj/effect/floor_decal/corner/orange/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"wP" = ( +/obj/structure/closet/firecloset/chief, +/obj/effect/floor_decal/corner/blue/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"xq" = ( +/obj/abstract/level_data/main_level, +/turf/space, +/area/space) +"xR" = ( +/obj/structure/ladder, +/turf/simulated/floor, +/area/example/first) +"xT" = ( /obj/effect/floor_decal/industrial/warning{ + dir = 4 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"ya" = ( +/obj/structure/railing/mapped{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/first) +"yc" = ( +/obj/effect/floor_decal/industrial/warning/corner{ dir = 8 }, -/turf/simulated/floor/tiled, -/area/constructionsite) -"WF" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ +/obj/effect/floor_decal/corner/orange{ dir = 5 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Yo" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden{ +/area/example/first) +"yf" = ( +/obj/machinery/atmospherics/unary/vent_pump/on, +/obj/effect/floor_decal/industrial/outline/grey, +/turf/simulated/floor, +/area/example/first) +"za" = ( +/obj/machinery/light{ dir = 1 }, +/turf/simulated/floor, +/area/example/first) +"AF" = ( +/obj/effect/floor_decal/corner/orange{ + dir = 10 + }, +/obj/machinery/alarm{ + dir = 1; + pixel_y = -19 + }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Yp" = ( +/area/example/first) +"AJ" = ( +/obj/effect/shuttle_landmark/lower_level, +/turf/simulated/floor/plating, +/area/shuttle/ferry) +"Bp" = ( +/obj/structure/stairs, +/turf/simulated/floor, +/area/example/first) +"Bs" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 1 + }, /obj/effect/floor_decal/corner/orange{ + dir = 10 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"BE" = ( +/obj/effect/wallframe_spawn/reinforced, +/turf/simulated/floor/plating, +/area/example/first) +"BS" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/simple/hidden{ dir = 5 }, -/obj/machinery/teleport/station, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"Zi" = ( -/obj/effect/floor_decal/corner/orange{ +/area/example/first) +"BV" = ( +/obj/machinery/light{ + dir = 1 + }, +/obj/machinery/suit_cycler/engineering/prepared, +/obj/effect/floor_decal/corner/orange/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"CO" = ( +/obj/effect/floor_decal/industrial/warning{ dir = 10 }, +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 1 + }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"ZF" = ( -/obj/structure/cable{ +/area/example/first) +"CQ" = ( +/obj/machinery/light, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"CU" = ( +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"DA" = ( +/obj/structure/table, +/obj/machinery/recharger, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Ed" = ( +/obj/structure/cable/yellow{ icon_state = "1-2" }, +/obj/machinery/alarm{ + dir = 8; + pixel_x = 26 + }, +/turf/simulated/floor, +/area/example/first) +"Eh" = ( +/obj/structure/table, +/obj/item/storage/backpack/holding, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Er" = ( +/obj/structure/table, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"EM" = ( +/obj/structure/closet/secure_closet/captains, +/obj/effect/floor_decal/corner/orange{ + dir = 9 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Ft" = ( +/obj/machinery/fabricator/hacked, +/obj/machinery/light, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"FZ" = ( +/obj/effect/floor_decal/industrial/warning/corner{ + dir = 1 + }, +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/first) +"Gg" = ( +/obj/structure/railing/mapped{ + dir = 1 + }, +/turf/simulated/floor, +/area/example/first) +"Hk" = ( +/turf/space, +/area/space) +"HA" = ( +/obj/machinery/atmospherics/portables_connector{ + dir = 8 + }, +/obj/machinery/portable_atmospherics/canister/empty, +/obj/effect/floor_decal/industrial/outline/red, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"HB" = ( +/obj/machinery/computer/teleporter, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"IR" = ( +/obj/effect/floor_decal/corner/orange/three_quarters{ + dir = 4 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Jc" = ( +/obj/machinery/door/airlock/glass, +/turf/simulated/floor/tiled/steel_ridged, +/area/example/first) +"Jn" = ( +/obj/machinery/door/airlock/external/bolted{ + id_tag = "lower_level_dock_hatch_external" + }, +/obj/machinery/button/access/exterior{ + id_tag = "lower_level_dock"; + pixel_y = -21 + }, +/turf/simulated/floor, +/area/example/first) +"JH" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden{ + dir = 1 + }, +/turf/simulated/floor, +/area/example/first) +"JS" = ( +/obj/structure/closet/secure_closet/hos, +/obj/effect/floor_decal/corner/blue/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"Kn" = ( +/obj/structure/stairs{ + dir = 1 + }, +/turf/simulated/floor, +/area/example/first) +"KJ" = ( +/obj/structure/cable/yellow{ + icon_state = "0-2" + }, +/obj/machinery/power/apc{ + dir = 4; + name = "west bump"; + pixel_x = 24 + }, +/turf/simulated/floor, +/area/example/first) +"KT" = ( +/obj/structure/table, +/obj/machinery/light{ + dir = 8 + }, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"La" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 4 + }, /obj/effect/floor_decal/corner/orange{ dir = 9 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) -"ZW" = ( -/obj/machinery/light/spot, +/area/example/first) +"Ly" = ( +/obj/effect/floor_decal/industrial/warning, +/obj/machinery/light{ + dir = 1 + }, /obj/effect/floor_decal/corner/orange{ - dir = 10 + dir = 5 }, /turf/simulated/floor/tiled/steel_grid, -/area/constructionsite) - -(1,1,1) = {" +/area/example/first) +"LM" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 1 + }, +/turf/simulated/floor, +/area/example/first) +"LP" = ( +/turf/simulated/floor/plating, +/area/turbolift/example/first) +"LW" = ( +/obj/machinery/teleport/station, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"Mc" = ( +/obj/machinery/alarm{ + dir = 8; + pixel_x = 26 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Mo" = ( +/obj/abstract/landmark/latejoin, +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Nu" = ( +/obj/machinery/atmospherics/portables_connector, +/obj/machinery/portable_atmospherics/canister/air/airlock, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"NC" = ( +/obj/effect/floor_decal/corner/orange{ + dir = 9 + }, +/obj/effect/floor_decal/industrial/warning, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"NL" = ( +/obj/effect/floor_decal/corner/orange/three_quarters, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"OI" = ( +/obj/machinery/atmospherics/pipe/simple/hidden, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"OZ" = ( +/obj/machinery/atmospherics/portables_connector{ + dir = 1 + }, +/obj/machinery/portable_atmospherics/canister/empty, +/obj/effect/floor_decal/industrial/outline/red, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"Pp" = ( +/obj/effect/floor_decal/industrial/warning/corner{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/first) +"Pv" = ( +/obj/structure/rack, +/obj/item/gun/projectile/shotgun/pump, +/obj/item/gun/projectile/shotgun/pump, +/obj/item/gun/projectile/shotgun/pump, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"PE" = ( +/obj/item/radio/beacon, +/obj/machinery/light{ + dir = 1 + }, +/obj/effect/floor_decal/industrial/outline/orange, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"Qc" = ( +/obj/structure/closet/emcloset, +/obj/effect/floor_decal/corner/blue/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"Rj" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 6 + }, +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 1 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"RB" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 9 + }, +/obj/machinery/atmospherics/unary/vent_pump/on, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"RF" = ( +/obj/effect/floor_decal/industrial/warning/corner, +/obj/effect/floor_decal/corner/orange/three_quarters{ + dir = 8 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"RV" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 8 + }, +/obj/machinery/light_switch/on{ + dir = 1; + pixel_y = -24 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"RW" = ( +/turf/simulated/floor, +/area/turbolift/example/first) +"Ss" = ( +/obj/structure/dispenser/oxygen, +/obj/effect/floor_decal/corner/orange/half{ + dir = 1 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"Sv" = ( +/obj/structure/table, +/turf/simulated/floor, +/area/example/first) +"Tk" = ( +/obj/structure/closet/secure_closet/hop, +/obj/effect/floor_decal/corner/orange{ + dir = 6 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Ts" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 4 + }, +/obj/effect/floor_decal/corner/orange{ + dir = 9 + }, +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Uw" = ( +/obj/machinery/atmospherics/portables_connector, +/obj/machinery/portable_atmospherics/canister/air, +/obj/effect/floor_decal/industrial/outline/blue, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/first) +"UH" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 9 + }, +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"VY" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 1 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"We" = ( +/obj/machinery/suit_cycler/security/prepared{ + initial_access = list() + }, +/turf/simulated/floor, +/area/example/first) +"Wi" = ( +/obj/effect/floor_decal/industrial/warning/fulltile, +/obj/effect/floor_decal/industrial/warning{ + dir = 8 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Wj" = ( +/obj/machinery/light{ + dir = 1 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Wr" = ( +/turf/simulated/floor/tiled/steel_ridged, +/area/example/first) +"WF" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/first) +"WK" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 9 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"WS" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/first) +"WU" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on{ + dir = 1 + }, +/obj/effect/floor_decal/industrial/outline/grey, +/turf/simulated/floor, +/area/example/first) +"WW" = ( +/obj/effect/floor_decal/industrial/warning, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"XZ" = ( +/turf/simulated/wall/r_wall/prepainted, +/area/example/first) +"Yo" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 8 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Yp" = ( +/turf/simulated/floor, +/area/example/first) +"Yt" = ( +/obj/structure/rack, +/obj/item/gun/projectile/automatic/assault_rifle, +/obj/item/gun/projectile/automatic/assault_rifle, +/obj/item/gun/projectile/automatic/assault_rifle, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"Zi" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 1 + }, +/obj/effect/floor_decal/corner/orange{ + dir = 10 + }, +/obj/machinery/light, +/turf/simulated/floor/tiled/steel_grid, +/area/example/first) +"ZF" = ( +/obj/machinery/door/airlock/external/bolted{ + id_tag = "example_shuttle_port_hatch" + }, +/turf/simulated/floor/plating, +/area/shuttle/ferry) +"ZO" = ( +/obj/machinery/door/airlock/external/glass/bolted{ + id_tag = "example_first_hatch_internal" + }, +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/first) +"ZV" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 5 + }, +/turf/simulated/floor, +/area/example/first) + +(1,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +xq +"} +(2,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +"} +(3,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +"} +(4,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +"} +(5,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +"} +(6,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +"} +(7,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk Hk Hk Hk @@ -452,6 +1397,12 @@ Hk Hk Hk Hk +"} +(8,1,1) = {" +Hk +Hk +Hk +Hk Hk Hk Hk @@ -468,8 +1419,6 @@ Hk Hk Hk Hk -"} -(2,1,1) = {" Hk Hk Hk @@ -503,7 +1452,77 @@ Hk Hk Hk "} -(3,1,1) = {" +(9,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +"} +(10,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +ck +Hk +Hk +Hk +Hk +Hk +Hk +Hk Hk Hk Hk @@ -531,19 +1550,61 @@ Hk Hk Hk Hk +ck +Hk +Hk +Hk Hk Hk Hk Hk Hk "} -(4,1,1) = {" +(11,1,1) = {" +Hk +Hk Hk Hk Hk Hk Hk Hk +ck +Hk +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +BE +BE +BE +BE +BE +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +Hk +ck Hk Hk Hk @@ -552,6 +1613,8 @@ Hk Hk Hk Hk +"} +(12,1,1) = {" Hk Hk Hk @@ -560,8 +1623,42 @@ Hk Hk Hk Hk +ck Hk +XZ +Yp +Yp +Yp +Yp +Yp +Yp +to +Yp +Yp +Yp +Yp +Yp +Yp +Yp +to +Yp +Yp +Yp +Yp +Yp +Yp +Yp +Yp +to +Yp +Yp +Yp +Yp +Yp +Yp +XZ Hk +ck Hk Hk Hk @@ -571,12 +1668,51 @@ Hk Hk Hk "} -(5,1,1) = {" +(13,1,1) = {" +Hk +Hk +Hk Hk Hk Hk Hk Hk +ck +Hk +XZ +Yp +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +BE +BE +BE +BE +BE +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +Yp +XZ +Hk +ck Hk Hk Hk @@ -585,6 +1721,8 @@ Hk Hk Hk Hk +"} +(14,1,1) = {" Hk Hk Hk @@ -593,25 +1731,150 @@ Hk Hk Hk Hk +ck Hk +XZ +Yp +XZ +Yp +Yp +Bp +Gg +Yp +XZ +tl +tl +tl +tl +tl +tl +NL +XZ +RF +La +La +Ts +La +La +La +La +Ts +La +La +sT +XZ +Yp +XZ Hk +ck Hk Hk Hk Hk -oy Hk Hk Hk Hk "} -(6,1,1) = {" +(15,1,1) = {" +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +ck +Hk +XZ +Yp +XZ +Yp +Yp +uZ +Yp +Yp +XZ +CU +CU +CU +CU +CU +CU +ii +XZ +wj +Yp +Yp +Yp +Yp +xR +Yp +Yp +Yp +Yp +Yp +Bs +XZ +Yp +XZ +Hk +ck +Hk +Hk +Hk +Hk +Hk +Hk +Hk +Hk +"} +(16,1,1) = {" +Hk +Hk +Hk Hk Hk Hk Hk Hk +ck +Hk +XZ +Yp +XZ +za +Yp +Yp +Yp +Yp +jB +CU +CU +CU +CU +CU +CU +CU +jB +WW +Yp +yf +ZV +Yp +Yp +Yp +Yp +rg +WU +Yp +Bs +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -620,6 +1883,8 @@ Hk Hk Hk Hk +"} +(17,1,1) = {" Hk Hk Hk @@ -628,8 +1893,42 @@ Hk Hk Hk Hk +ck Hk +XZ +Yp +XZ +Yp +Yp +Yp +Yp +Yp +XZ +CU +CU +CU +CU +CU +CU +ty +XZ +Ly +Yp +Yp +WF +Yp +Yp +Yp +Yp +WF +Yp +Yp +Zi +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -639,13 +1938,51 @@ Hk Hk Hk "} -(7,1,1) = {" +(18,1,1) = {" +Hk +Hk +Hk Hk Hk Hk Hk Hk +ck +Hk +XZ +za +XZ +Yp +Yp +Yp +Yp +Yp +XZ +lN +lJ +OI +eh +CU +CU +ii +XZ +wj +Yp +Yp +WF +Yp +Yp +Yp +Yp +WF +Yp +Yp +Bs +XZ +uD +XZ Hk +ck Hk Hk Hk @@ -654,6 +1991,8 @@ Hk Hk Hk Hk +"} +(19,1,1) = {" Hk Hk Hk @@ -662,8 +2001,42 @@ Hk Hk Hk Hk +ck Hk +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +Wr +Wr +Wr +XZ +wj +Yp +Yp +WF +Yp +Yp +Yp +Yp +WF +Yp +Yp +Bs +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -673,7 +2046,7 @@ Hk Hk Hk "} -(8,1,1) = {" +(20,1,1) = {" Hk Hk Hk @@ -682,7 +2055,9 @@ Hk Hk Hk Hk +ck Hk +cS Hk Hk Hk @@ -690,7 +2065,32 @@ Hk Hk Hk Hk +XZ +EM +oy +tl +XZ +Wj +CU +ii +XZ +wj +Yp +Yp +WF +Yp +Yp +Yp +Yp +WF +Yp +We +Bs +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -699,6 +2099,8 @@ Hk Hk Hk Hk +"} +(21,1,1) = {" Hk Hk Hk @@ -706,10 +2108,10 @@ Hk Hk Hk Hk -"} -(9,1,1) = {" Hk +ck Hk +cS Hk Hk Hk @@ -717,7 +2119,32 @@ Hk Hk Hk Hk +BE +Er +CU +CU +BE +CU +CU +AF +XZ +wj +Yp +Yp +WF +Yp +Yp +Yp +Yp +WF +Yp +Sv +Bs +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -726,6 +2153,8 @@ Hk Hk Hk Hk +"} +(22,1,1) = {" Hk Hk Hk @@ -734,17 +2163,42 @@ Hk Hk Hk Hk +ck Hk +cS Hk Hk Hk Hk Hk -"} -(10,1,1) = {" Hk Hk +BE +Er +CU +CU +BE +CU +CU +ii +XZ +Ly +Yp +Yp +WF +Yp +Yp +Yp +ya +WF +Yp +Sv +Zi +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -753,6 +2207,8 @@ Hk Hk Hk Hk +"} +(23,1,1) = {" Hk Hk Hk @@ -761,7 +2217,9 @@ Hk Hk Hk Hk +ck Hk +cS Hk Hk Hk @@ -769,17 +2227,42 @@ Hk Hk Hk Hk +BE +vZ +CU +CU +Jc +CU +CU +CU +Jc +WW +Yp +yf +ou +Yp +Yp +vi +Kn +JH +WU +Sv +Bs +XZ +Yp +XZ Hk +ck Hk Hk Hk Hk -"} -(11,1,1) = {" Hk Hk Hk Hk +"} +(24,1,1) = {" Hk Hk Hk @@ -788,10 +2271,42 @@ Hk Hk Hk Hk +ck Hk +cS Hk +gO +gO +ZF +gO +gO Hk +BE +Eh +CU +CU +BE +CU +CU +ii +XZ +wj +Yp +Yp +WF +Yp +Yp +Yp +uZ +WF +Yp +Sv +Bs +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -800,6 +2315,8 @@ Hk Hk Hk Hk +"} +(25,1,1) = {" Hk Hk Hk @@ -808,12 +2325,42 @@ Hk Hk Hk Hk -"} -(12,1,1) = {" +ck Hk +cS Hk +cV +fb +AJ +jg +gO Hk +BE +Er +CU +CU +BE +CU +CU +ii +XZ +yc +Yo +Yo +oB +Yo +Yo +Yo +Yo +oB +Yo +Yo +bY +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -822,6 +2369,8 @@ Hk Hk Hk Hk +"} +(26,1,1) = {" Hk Hk Hk @@ -830,10 +2379,42 @@ Hk Hk Hk Hk +ck Hk +cS Hk +gO +gO +vg +gO +gO Hk +XZ +Tk +oS +hA +XZ +Wj +CU +ii +XZ +wo +oI +Uw +UH +CU +CU +CU +CU +af +OZ +hA +IR +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -843,7 +2424,7 @@ Hk Hk Hk "} -(13,1,1) = {" +(27,1,1) = {" Hk Hk Hk @@ -852,21 +2433,52 @@ Hk Hk Hk Hk +ck Hk +XZ +XZ +XZ +XZ +Jn +XZ +BE +XZ +XZ +XZ +XZ +XZ +XZ +Wr +Wr +Wr +XZ +XZ +XZ +XZ +XZ +Wr +Wr +Wr +Wr +XZ +XZ +XZ +XZ +XZ +uD +XZ Hk +ck Hk Hk Hk Hk Hk -xk -JO -JO -jg -xk Hk Hk Hk +"} +(28,1,1) = {" Hk Hk Hk @@ -875,10 +2487,42 @@ Hk Hk Hk Hk +ck Hk -"} -(14,1,1) = {" +XZ +Yp +XZ +tq +ZV +la +CU +CU +XZ +mu +oZ +OI +co +CU +CU +ii +XZ +HB +tl +NC +to +Yp +Yp +Yp +Yp +to +Yp +Yp +Yp +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -887,21 +2531,52 @@ Hk Hk Hk Hk +"} +(29,1,1) = {" Hk Hk Hk Hk Hk Hk -xk -cV -La -sT -xk Hk Hk +ck Hk +XZ +Yp +XZ +se +qs +vK +vl +CU +jB +CU +CU +CU +CU +CU +CU +ty +XZ +LW +CU +WW +Yp +RB +BS +eZ +CO +Yp +WK +Yo +RV +XZ +Yp +XZ Hk +ck Hk Hk Hk @@ -911,9 +2586,7 @@ Hk Hk Hk "} -(15,1,1) = {" -Hk -Hk +(30,1,1) = {" Hk Hk Hk @@ -922,21 +2595,52 @@ Hk Hk Hk Hk +ck Hk +XZ +Yp +XZ +XZ +XZ +XZ +bK +CU +XZ +wG +CU +CU +CU +CU +CU +ii +XZ +qB +CU +WW +Yp +VY +kw +Mo +WW +Yp +VY +CU +dE +XZ +Yp +XZ Hk +ck Hk Hk Hk -xk -JO -JO -Su -xk Hk Hk Hk Hk Hk +"} +(31,1,1) = {" Hk Hk Hk @@ -944,110 +2648,151 @@ Hk Hk Hk Hk -"} -(16,1,1) = {" Hk +ck Hk +XZ +Yp +XZ +sQ +OI +OI +fu +CU +XZ +nL +CU +CU +CU +CU +CU +AF +XZ +PE +CU +WW +Yp +VY +kw +Mo +WW +Yp +VY +CU +Ft +XZ +Yp +XZ Hk +ck Hk Hk Hk Hk -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -eg -fQ -eg -fU -fU -fU -fU -fU -fU Hk Hk Hk Hk +"} +(32,1,1) = {" Hk Hk -"} -(17,1,1) = {" Hk Hk Hk Hk Hk Hk +ck Hk -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -eg -hw -eg -fU -fU -fU -fU -fU -fU +XZ +Yp +XZ +BV +CU +CU +CU +CQ +XZ +gk +CU +CU +CU +CU +CU +CU +Jc +CU +CU +WW +Yp +VY +kw +Mo +WW +Yp +VY +CU +DA +XZ +Yp +XZ Hk +ck Hk Hk Hk Hk Hk -"} -(18,1,1) = {" Hk Hk Hk +"} +(33,1,1) = {" Hk Hk Hk Hk -fU -fU -eg -eg -eg -eg -eg -eg -eg -eg -eg -VY -eg -eg -eg -eg -eg -fU -fU Hk Hk Hk Hk +ck Hk +XZ +Yp +XZ +Ss +CU +CU +CU +CU +XZ +cy +CU +CU +CU +CU +CU +CU +Jc +CU +CU +WW +Yp +VY +kw +Mo +WW +Yp +VY +CU +Yt +XZ +Yp +XZ Hk -"} -(19,1,1) = {" +ck Hk Hk Hk @@ -1055,59 +2800,55 @@ Hk Hk Hk Hk -fU -fU -eg -wF -JG -JG -Gj -JG -JG -tg -sQ -rY -Gj -Pw -ZF -oZ -eg -fU -fU Hk +"} +(34,1,1) = {" Hk Hk Hk Hk Hk -"} -(20,1,1) = {" Hk Hk Hk +ck Hk +XZ +Yp +XZ +XZ +Jc +XZ +Jc +XZ +XZ +aL +th +CU +CU +CU +th +CU +Jc +CU +CU +WW +Yp +VY +kw +Mo +WW +Yp +VY +CU +cF +XZ +Yp +XZ Hk +ck Hk Hk -fU -fU -eg -EO -qJ -qJ -qJ -qJ -qJ -qJ -qJ -qJ -us -us -us -Zi -eg -fU -fU Hk Hk Hk @@ -1115,41 +2856,51 @@ Hk Hk Hk "} -(21,1,1) = {" -Hk -Hk -Hk +(35,1,1) = {" Hk Hk Hk Hk -fU -fU -eg -EO -qJ -qJ -qJ -vd -WF -qJ -fT -iM -us -us -us -Zi -eg -fU -fU Hk Hk Hk Hk +ck Hk +XZ +za +XZ +Yp +Yp +Yp +Yp +Yp +XZ +XZ +XZ +XZ +Wr +XZ +XZ +XZ +XZ +mc +xT +gT +Pp +VY +kw +Mo +WW +Yp +VY +CU +pv +XZ +uD +XZ Hk -"} -(22,1,1) = {" +ck Hk Hk Hk @@ -1157,59 +2908,55 @@ Hk Hk Hk Hk -fU -fU -eg -RW -qJ -qJ -qJ -qJ -wB -qJ -wB -qJ -us -us -us -ZW -eg -fU -fU Hk +"} +(36,1,1) = {" Hk Hk Hk Hk Hk -"} -(23,1,1) = {" Hk Hk Hk +ck Hk +XZ +Yp +XZ +Yp +Yp +Yp +Yp +Yp +XZ +wP +mr +CU +CU +CU +KT +oz +XZ +LP +LP +tA +LM +VY +kw +Mo +WW +Yp +VY +CU +Pv +XZ +Yp +XZ Hk +ck Hk Hk -fU -fU -eg -qt -tE -tE -jV -PA -wB -qJ -wB -qJ -qJ -qJ -qJ -Zi -eg -fU -fU Hk Hk Hk @@ -1217,177 +2964,213 @@ Hk Hk Hk "} -(24,1,1) = {" -Hk +(37,1,1) = {" Hk Hk Hk Hk Hk Hk -fU -fU -eg -Fe -Vt -Vt -xF -Sv -wB -qJ -wB -qJ -qJ -qJ -gO -Zi -eg -fU -fU Hk Hk +ck Hk +XZ +Yp +XZ +za +Yp +Yp +Yp +Yp +XZ +JS +CU +CU +CU +CU +CU +oz +XZ +LP +LP +RW +LM +on +he +tt +Rj +Yp +mo +xT +du +XZ +Yp +XZ Hk +ck Hk Hk -"} -(25,1,1) = {" Hk Hk Hk Hk Hk Hk +"} +(38,1,1) = {" Hk -fU -fU -eg -Fe -Vt -Vt -Vt -Sv -wB -Bv -wB -qJ -qJ -qJ -cS -Zi -eg -fU -fU Hk Hk Hk Hk Hk Hk -"} -(26,1,1) = {" Hk +ck Hk +XZ +Yp +XZ +Yp +Yp +Yp +Yp +Yp +XZ +Qc +CU +CU +CU +CU +CU +oz +XZ +LP +LP +RW +LM +Yp +WF +WF +Yp +Yp +Yp +Yp +Yp +XZ +Yp +XZ Hk +ck Hk Hk Hk Hk -fU -fU -eg -Fe -Vt -Vt -Vt -Sv -wB -qJ -wB -qJ -qJ -qJ -ck -Zi -eg -fU -fU Hk Hk Hk Hk +"} +(39,1,1) = {" Hk Hk -"} -(27,1,1) = {" Hk Hk Hk Hk Hk Hk +ck Hk -fU -fU -eg -yN -Wr -Wr -Wr -jO -wB -qJ -wB -qJ -qJ -qJ -jB -Zi -eg -fU -fU +XZ +Yp +XZ +Yp +Yp +Yp +Yp +Yp +XZ +Qc +CU +Nu +vl +Mc +CU +oz +XZ +Wi +Wi +Wi +FZ +Yp +jP +HA +Yp +WS +KJ +Ed +qR +XZ +Yp +XZ Hk +ck Hk Hk Hk Hk Hk -"} -(28,1,1) = {" Hk Hk Hk +"} +(40,1,1) = {" Hk Hk Hk Hk -fU -fU -eg -RW -qJ -qJ -qJ -qJ -wB -qJ -wB -qJ -qJ -qJ -tq -ZW -eg -fU -fU Hk Hk Hk Hk +ck Hk +XZ +Yp +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +ZO +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +Yp +XZ Hk -"} -(29,1,1) = {" +ck Hk Hk Hk @@ -1395,59 +3178,55 @@ Hk Hk Hk Hk -fU -fU -eg -pm -qJ -qJ -qJ -vd -Af -qJ -Yo -iM -qJ -qJ -se -Zi -eg -fU -fU Hk +"} +(41,1,1) = {" Hk Hk Hk Hk Hk -"} -(30,1,1) = {" Hk Hk Hk +ck Hk +XZ +Yp +Yp +Yp +Yp +Yp +Yp +WS +Yp +Yp +Yp +wv +fa +wv +Yp +WS +Yp +Yp +Yp +Yp +Yp +Yp +Yp +Yp +WS +Yp +Yp +Yp +Yp +Yp +Yp +XZ Hk +ck Hk Hk -fU -fU -eg -Yp -qJ -qJ -qJ -wj -wB -qJ -wB -qJ -qJ -qJ -Nu -Zi -eg -fU -fU Hk Hk Hk @@ -1455,7 +3234,7 @@ Hk Hk Hk "} -(31,1,1) = {" +(42,1,1) = {" Hk Hk Hk @@ -1463,67 +3242,62 @@ Hk Hk Hk Hk -fU -fU -eg -Iv -NQ -kw -Mo -NQ -QF -NQ -BV -NQ -Mo -NQ -NQ -wF -eg -fU -fU Hk +ck Hk +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +rH +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ +XZ Hk +ck Hk Hk Hk -"} -(32,1,1) = {" Hk Hk Hk Hk Hk +"} +(43,1,1) = {" Hk Hk -fU -fU -eg -eg -eg -eg -eg -eg -eg -eg -eg -eg -eg -eg -eg -eg -eg -fU -fU Hk Hk Hk Hk Hk Hk -"} -(33,1,1) = {" +ck Hk Hk Hk @@ -1531,33 +3305,12 @@ Hk Hk Hk Hk -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU Hk Hk Hk Hk Hk Hk -"} -(34,1,1) = {" Hk Hk Hk @@ -1565,33 +3318,12 @@ Hk Hk Hk Hk -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU -fU Hk Hk Hk Hk Hk Hk -"} -(35,1,1) = {" Hk Hk Hk @@ -1600,6 +3332,7 @@ Hk Hk Hk Hk +ck Hk Hk Hk @@ -1608,6 +3341,8 @@ Hk Hk Hk Hk +"} +(44,1,1) = {" Hk Hk Hk @@ -1616,6 +3351,42 @@ Hk Hk Hk Hk +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck +ck Hk Hk Hk @@ -1625,7 +3396,7 @@ Hk Hk Hk "} -(36,1,1) = {" +(45,1,1) = {" Hk Hk Hk @@ -1658,8 +3429,6 @@ Hk Hk Hk Hk -"} -(37,1,1) = {" Hk Hk Hk @@ -1680,6 +3449,8 @@ Hk Hk Hk Hk +"} +(46,1,1) = {" Hk Hk Hk @@ -1692,8 +3463,6 @@ Hk Hk Hk Hk -"} -(38,1,1) = {" Hk Hk Hk @@ -1726,8 +3495,6 @@ Hk Hk Hk Hk -"} -(39,1,1) = {" Hk Hk Hk @@ -1736,6 +3503,8 @@ Hk Hk Hk Hk +"} +(47,1,1) = {" Hk Hk Hk @@ -1760,8 +3529,6 @@ Hk Hk Hk Hk -"} -(40,1,1) = {" Hk Hk Hk @@ -1790,12 +3557,12 @@ Hk Hk Hk Hk +"} +(48,1,1) = {" Hk Hk Hk Hk -"} -(41,1,1) = {" Hk Hk Hk @@ -1828,8 +3595,6 @@ Hk Hk Hk Hk -"} -(42,1,1) = {" Hk Hk Hk @@ -1846,6 +3611,8 @@ Hk Hk Hk Hk +"} +(49,1,1) = {" Hk Hk Hk @@ -1862,8 +3629,6 @@ Hk Hk Hk Hk -"} -(43,1,1) = {" Hk Hk Hk @@ -1896,12 +3661,12 @@ Hk Hk Hk Hk -"} -(44,1,1) = {" Hk Hk Hk Hk +"} +(50,1,1) = {" Hk Hk Hk @@ -1930,8 +3695,6 @@ Hk Hk Hk Hk -"} -(45,1,1) = {" Hk Hk Hk @@ -1956,6 +3719,8 @@ Hk Hk Hk Hk +"} +(51,1,1) = {" Hk Hk Hk @@ -1964,8 +3729,6 @@ Hk Hk Hk Hk -"} -(46,1,1) = {" Hk Hk Hk @@ -1998,8 +3761,6 @@ Hk Hk Hk Hk -"} -(47,1,1) = {" Hk Hk Hk @@ -2012,6 +3773,8 @@ Hk Hk Hk Hk +"} +(52,1,1) = {" Hk Hk Hk @@ -2032,8 +3795,6 @@ Hk Hk Hk Hk -"} -(48,1,1) = {" Hk Hk Hk diff --git a/maps/example/example-2.dmm b/maps/example/example-2.dmm index dd8a41a93de..5c2e5ab511b 100644 --- a/maps/example/example-2.dmm +++ b/maps/example/example-2.dmm @@ -1,435 +1,607 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE -"aa" = ( -/turf/space, -/area/space) -"ab" = ( -/turf/simulated/wall/r_wall, -/area/space) -"ac" = ( -/turf/simulated/wall, -/area/surgery) -"ad" = ( -/obj/structure/iv_drip, -/obj/item/chems/ivbag/blood/OMinus, -/obj/item/chems/ivbag/blood/OMinus, -/obj/item/chems/ivbag/blood/OMinus, -/obj/effect/floor_decal/corner/blue/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"ae" = ( -/obj/machinery/optable, -/obj/effect/floor_decal/corner/blue/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"af" = ( -/obj/machinery/computer/operating, -/obj/effect/floor_decal/corner/blue/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"ag" = ( +"aj" = ( /obj/structure/table/glass, -/obj/item/storage/box/syringes, -/obj/item/storage/box/freezer, -/obj/machinery/light/spot{ - dir = 1 - }, +/obj/item/chems/dropper, +/obj/item/stack/nanopaste, /obj/effect/floor_decal/corner/blue/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"ah" = ( -/obj/structure/table/glass, -/obj/item/storage/box/gloves, -/obj/item/storage/box/masks, -/obj/structure/railing/mapped{ +/obj/machinery/light/small{ dir = 4 }, -/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"ai" = ( -/obj/machinery/atmospherics/unary/freezer, +/area/example/second) +"bU" = ( +/obj/machinery/light{ + dir = 8 + }, +/turf/simulated/floor/tiled/steel_ridged, +/area/example/second) +"cb" = ( +/obj/effect/floor_decal/corner/mauve{ + dir = 5 + }, +/turf/simulated/floor/tiled/white, +/area/example/second) +"cH" = ( +/obj/machinery/optable, /obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aj" = ( -/obj/structure/lattice, +/area/example/second) +"dh" = ( +/obj/machinery/design_database, +/obj/effect/floor_decal/corner/mauve/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"dz" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 5 + }, +/turf/simulated/floor, +/area/example/second) +"es" = ( +/obj/machinery/light, /turf/simulated/open, -/area/surgery) -"ak" = ( -/obj/machinery/light/spot{ +/area/example/second) +"fJ" = ( +/obj/machinery/light/small{ dir = 1 }, -/obj/effect/floor_decal/corner/orange{ - dir = 5 +/obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{ + id_tag = "upper_level_dock_pump" }, -/obj/effect/floor_decal/industrial/warning{ - dir = 8 +/turf/simulated/floor, +/area/example/second) +"fS" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 4 + }, +/obj/effect/floor_decal/corner/orange{ + dir = 9 }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"al" = ( -/obj/structure/table/glass, -/obj/item/storage/firstaid/surgery, -/obj/effect/floor_decal/corner/blue/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"am" = ( -/obj/structure/railing/mapped{ - dir = 4 +/area/example/second) +"fU" = ( +/obj/effect/floor_decal/industrial/warning/dust, +/turf/simulated/floor, +/area/example/second) +"fX" = ( +/obj/machinery/alarm{ + pixel_y = 24 }, -/obj/effect/floor_decal/corner/blue{ - dir = 6 +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"gl" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 5 }, /turf/simulated/floor/tiled/white, -/area/surgery) -"an" = ( -/turf/simulated/open, -/area/surgery) -"ao" = ( -/obj/structure/table/glass, -/obj/item/chems/dropper, -/obj/item/stack/nanopaste, -/obj/machinery/light/spot{ - dir = 8 +/area/example/second) +"gs" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden{ + dir = 1 }, -/obj/effect/floor_decal/corner/blue/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"ap" = ( -/obj/machinery/body_scanconsole{ - dir = 4 +/obj/structure/railing/mapped{ + dir = 1 }, /turf/simulated/floor/tiled/white, -/area/surgery) -"aq" = ( -/obj/machinery/bodyscanner{ - dir = 4 +/area/example/second) +"hM" = ( +/obj/machinery/light{ + dir = 8 }, -/turf/simulated/floor/tiled/white, -/area/surgery) -"ar" = ( -/obj/machinery/sleeper/standard, -/obj/structure/railing/mapped{ +/turf/simulated/floor, +/area/example/second) +"ii" = ( +/obj/machinery/door/airlock/external/bolted{ + id_tag = "upper_level_dock_hatch_internal" + }, +/turf/simulated/floor, +/area/example/second) +"iS" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, -/obj/effect/floor_decal/corner/blue{ - dir = 6 +/obj/machinery/alarm{ + dir = 1; + pixel_y = -19 }, /turf/simulated/floor/tiled/white, -/area/surgery) -"as" = ( -/obj/machinery/light_switch/on{ - dir = 8; - pixel_x = 24 +/area/example/second) +"iY" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden, +/obj/structure/railing/mapped, +/turf/simulated/floor/tiled/white, +/area/example/second) +"jd" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 9 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"jr" = ( +/obj/effect/floor_decal/industrial/warning/dust/corner{ + dir = 8 }, /obj/effect/floor_decal/corner/orange{ - dir = 6 + dir = 5 }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"at" = ( -/obj/structure/table/glass, -/obj/item/scalpel/laser, -/obj/item/scalpel/laser/upgraded, -/obj/item/scalpel/laser/advanced, -/obj/item/robotanalyzer, -/obj/item/scanner/health, -/obj/item/storage/firstaid/adv, -/obj/effect/floor_decal/corner/blue/mono, +/area/example/second) +"jW" = ( +/obj/effect/floor_decal/industrial/warning{ + dir = 4 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/second) +"kf" = ( +/obj/machinery/fabricator/robotics, +/obj/effect/floor_decal/corner/mauve/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"au" = ( -/obj/structure/ladder, -/obj/structure/railing/mapped{ +/area/example/second) +"kx" = ( +/obj/machinery/door/airlock/external/bolted{ + id_tag = "upper_level_dock_hatch_external" + }, +/obj/machinery/button/access/exterior{ + id_tag = "upper_level_dock"; + pixel_y = -21 + }, +/turf/simulated/floor, +/area/example/second) +"lm" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, -/obj/effect/floor_decal/corner/blue{ - dir = 6 +/turf/simulated/floor/tiled/white, +/area/example/second) +"ln" = ( +/obj/effect/floor_decal/industrial/warning/dust/corner, +/obj/effect/floor_decal/corner/orange/three_quarters{ + dir = 8 }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"lJ" = ( +/turf/simulated/floor, +/area/example/second) +"lP" = ( /turf/simulated/floor/tiled/white, -/area/surgery) -"av" = ( -/obj/machinery/atmospherics/portables_connector, -/obj/effect/floor_decal/corner/blue/mono, +/area/example/second) +"mC" = ( +/obj/effect/floor_decal/corner/orange, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"nz" = ( +/obj/machinery/light{ + dir = 1 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"nB" = ( +/obj/effect/shuttle_landmark/upper_level, +/turf/simulated/open, +/area/space) +"nC" = ( +/obj/effect/floor_decal/corner/mauve/mono, +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aw" = ( -/obj/machinery/portable_atmospherics/canister/oxygen/prechilled, -/obj/machinery/atmospherics/portables_connector, +/area/example/second) +"nO" = ( +/obj/machinery/atmospherics/unary/vent_pump/on, +/turf/simulated/floor/tiled/white, +/area/example/second) +"oi" = ( +/obj/structure/dispenser/oxygen, +/obj/machinery/light, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/second) +"oC" = ( +/obj/machinery/suit_cycler/engineering/prepared, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/second) +"oI" = ( +/obj/machinery/light, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"oS" = ( +/turf/simulated/open, +/area/space) +"pc" = ( +/obj/effect/wallframe_spawn/reinforced, +/turf/simulated/floor/plating, +/area/example/second) +"pk" = ( +/obj/machinery/light{ + dir = 1 + }, +/turf/simulated/floor, +/area/example/second) +"pr" = ( +/obj/machinery/computer/operating{ + dir = 1 + }, /obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"ay" = ( -/obj/machinery/light/spot{ - dir = 4 +/area/example/second) +"ps" = ( +/obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{ + id_tag = "upper_level_dock_pump" }, -/obj/effect/floor_decal/corner/orange{ - dir = 6 +/obj/machinery/airlock_sensor{ + id_tag = "upper_level_dock_sensor_chamber"; + pixel_x = 24 + }, +/obj/machinery/embedded_controller/radio/airlock/docking_port{ + id_tag = "upper_level_dock"; + pixel_y = 24; + tag_airpump = "upper_level_dock_pump"; + tag_chamber_sensor = "upper_level_dock_sensor_chamber"; + tag_exterior_door = "upper_level_dock_hatch_external"; + tag_interior_door = "upper_level_dock_hatch_internal" + }, +/turf/simulated/floor, +/area/example/second) +"pt" = ( +/obj/effect/floor_decal/industrial/warning/dust/corner{ + dir = 4 }, +/obj/effect/floor_decal/corner/orange/three_quarters, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"az" = ( -/obj/structure/closet/secure_closet/freezer/meat, -/obj/effect/floor_decal/corner/blue/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aB" = ( -/obj/structure/hygiene/sink{ - dir = 8; - pixel_x = -15 +/area/example/second) +"qk" = ( +/obj/structure/railing/mapped{ + dir = 8 }, /turf/simulated/floor/tiled/white, -/area/surgery) -"aC" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/blue{ +/area/example/second) +"qA" = ( +/obj/effect/floor_decal/industrial/warning/dust, +/obj/effect/floor_decal/corner/orange{ dir = 5 }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"aD" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden/blue, +/area/example/second) +"qV" = ( +/obj/machinery/light{ + dir = 8 + }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"aE" = ( -/obj/machinery/atmospherics/unary/vent_pump/on, -/turf/simulated/floor/tiled/white, -/area/surgery) -"aF" = ( -/obj/machinery/fabricator/bioprinter, +/area/example/second) +"qW" = ( +/obj/machinery/atmospherics/portables_connector, +/obj/machinery/portable_atmospherics/canister/air, +/obj/effect/floor_decal/industrial/outline/blue, +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/second) +"rb" = ( /obj/effect/floor_decal/corner/blue/mono, +/obj/machinery/sleeper/standard, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aG" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden, +/area/example/second) +"rn" = ( +/obj/machinery/door/airlock, +/turf/simulated/floor/tiled/steel_ridged, +/area/example/second) +"sA" = ( +/turf/simulated/floor/tiled/steel_ridged, +/area/example/second) +"sO" = ( +/obj/machinery/door/airlock/external/bolted{ + id_tag = "upper_level_dock_hatch_internal" + }, +/obj/machinery/atmospherics/pipe/simple/hidden, +/turf/simulated/floor, +/area/example/second) +"sT" = ( +/obj/effect/floor_decal/corner/mauve{ + dir = 5 + }, +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 6 + }, +/turf/simulated/floor/tiled/white, +/area/example/second) +"sV" = ( +/obj/machinery/atmospherics/pipe/simple/hidden, +/obj/machinery/button/access/interior{ + id_tag = "upper_level_dock"; + pixel_x = 22; + pixel_y = 28 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"sY" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 4 + }, +/obj/effect/floor_decal/corner/orange{ + dir = 9 + }, +/obj/machinery/light/small{ + dir = 8 + }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"aH" = ( +/area/example/second) +"ug" = ( +/obj/abstract/level_data/main_level, +/turf/space, +/area/space) +"uk" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 8 + }, /obj/machinery/atmospherics/pipe/simple/hidden{ dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"aI" = ( -/obj/machinery/atmospherics/portables_connector{ +/area/example/second) +"un" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/second) +"uF" = ( +/obj/effect/floor_decal/industrial/warning/dust{ dir = 8 }, -/obj/machinery/portable_atmospherics/canister/air, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/surgery) -"aJ" = ( -/obj/effect/floor_decal/corner/blue/three_quarters{ +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"wP" = ( +/turf/simulated/open, +/area/example/second) +"xk" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/open, +/area/example/second) +"xs" = ( +/turf/space, +/area/space) +"xD" = ( +/obj/effect/floor_decal/corner/orange{ + dir = 10 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"xX" = ( +/obj/machinery/light{ + dir = 4 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"ys" = ( +/obj/structure/table/glass, +/obj/item/storage/box/syringes, +/obj/item/storage/box/freezer, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"yy" = ( +/obj/structure/railing/mapped{ dir = 4 }, /turf/simulated/floor/tiled/white, -/area/surgery) -"aK" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/blue{ - dir = 9 +/area/example/second) +"yE" = ( +/turf/simulated/floor/tiled/dark/monotile, +/area/example/second) +"zG" = ( +/obj/effect/floor_decal/industrial/warning/dust/corner{ + dir = 1 }, /obj/effect/floor_decal/corner/orange{ - dir = 6 + dir = 10 }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"aL" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden{ - dir = 1 +/area/example/second) +"Am" = ( +/obj/machinery/light, +/turf/simulated/floor, +/area/example/second) +"Ba" = ( +/obj/effect/floor_decal/corner/orange{ + dir = 9 + }, +/obj/machinery/light{ + dir = 8 }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"aM" = ( -/obj/machinery/atmospherics/portables_connector{ +/area/example/second) +"BG" = ( +/obj/machinery/light/small{ dir = 8 }, -/obj/machinery/portable_atmospherics/canister/empty, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/surgery) -"aO" = ( -/obj/machinery/atmospherics/unary/vent_scrubber/on{ +/turf/simulated/floor/tiled/white, +/area/example/second) +"BY" = ( +/obj/effect/floor_decal/industrial/warning/dust{ dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"aP" = ( -/obj/machinery/light/spot{ - dir = 8 +/obj/effect/floor_decal/corner/orange{ + dir = 10 }, -/obj/machinery/design_database, -/obj/effect/floor_decal/corner/mauve/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aQ" = ( -/obj/machinery/light/spot, -/obj/machinery/fabricator/protolathe, -/obj/effect/floor_decal/corner/mauve/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aR" = ( -/obj/structure/cable{ - icon_state = "0-2" +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"Ci" = ( +/obj/machinery/power/debug_items/infinite_generator, +/obj/structure/cable/yellow, +/turf/simulated/floor/tiled/white, +/area/example/second) +"Cq" = ( +/obj/machinery/light_switch/on{ + dir = 1; + pixel_y = -20 }, -/obj/machinery/power/apc{ - dir = 8; - name = "west bump"; - pixel_x = -24 +/turf/simulated/floor/tiled/white, +/area/example/second) +"Cu" = ( +/obj/effect/floor_decal/corner/orange{ + dir = 6 }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"CN" = ( /obj/effect/floor_decal/corner/mauve{ dir = 5 }, +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 + }, /turf/simulated/floor/tiled/white, -/area/surgery) -"aS" = ( -/obj/structure/cable{ +/area/example/second) +"CW" = ( +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"CX" = ( +/obj/structure/cable/yellow{ icon_state = "1-2" }, /turf/simulated/floor/tiled/white, -/area/surgery) -"aT" = ( -/obj/structure/cable, -/obj/machinery/power/debug_items/infinite_generator, -/obj/effect/floor_decal/corner/mauve/mono, +/area/example/second) +"Dt" = ( +/obj/effect/floor_decal/corner/blue/mono, +/obj/structure/closet/secure_closet/freezer/meat, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aU" = ( -/obj/machinery/light/spot, -/obj/effect/floor_decal/corner/orange{ - dir = 10 +/area/example/second) +"En" = ( +/obj/machinery/bodyscanner{ + dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"aW" = ( -/obj/machinery/fabricator/imprinter, -/obj/effect/floor_decal/corner/mauve/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aX" = ( -/obj/machinery/destructive_analyzer, -/obj/effect/floor_decal/corner/mauve/mono, +/obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"aZ" = ( -/obj/effect/floor_decal/industrial/warning{ - dir = 8 - }, -/turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"ba" = ( -/obj/machinery/fabricator/industrial, +/area/example/second) +"EO" = ( +/obj/machinery/fabricator/protolathe, /obj/effect/floor_decal/corner/mauve/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"eY" = ( +/area/example/second) +"FE" = ( /obj/effect/floor_decal/corner/orange{ dir = 10 }, -/turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"hN" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ +/obj/effect/floor_decal/industrial/warning{ dir = 4 }, -/turf/simulated/floor/tiled/white, -/area/surgery) -"iE" = ( -/obj/machinery/atmospherics/unary/vent_pump/on, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"kL" = ( -/turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"lr" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ - dir = 6 +/area/example/second) +"Gn" = ( +/obj/effect/wallframe_spawn/reinforced, +/turf/simulated/floor, +/area/example/second) +"Gy" = ( +/obj/effect/floor_decal/corner/orange/three_quarters{ + dir = 8 }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"qL" = ( -/obj/machinery/computer/design_console, -/obj/effect/floor_decal/corner/mauve/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"qY" = ( -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/surgery) -"tL" = ( -/obj/machinery/fabricator/robotics, -/obj/effect/floor_decal/corner/mauve/mono, -/turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"zv" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ +/area/example/second) +"GG" = ( +/obj/structure/railing/mapped, +/turf/simulated/open, +/area/example/second) +"GP" = ( +/obj/machinery/light{ dir = 4 }, -/obj/effect/floor_decal/corner/blue{ - dir = 6 +/obj/machinery/atmospherics/portables_connector{ + dir = 1 }, -/turf/simulated/floor/tiled/white, -/area/surgery) -"zS" = ( -/obj/effect/floor_decal/corner/orange{ - dir = 6 +/obj/machinery/portable_atmospherics/canister/empty, +/obj/effect/floor_decal/industrial/outline/red, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/second) +"Hn" = ( +/obj/structure/table/glass, +/obj/item/storage/box/gloves, +/obj/item/storage/box/masks, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"Hp" = ( +/obj/structure/grille, +/turf/space, +/area/space) +"JU" = ( +/obj/structure/table/glass, +/obj/item/scalpel/laser, +/obj/item/scalpel/laser/upgraded, +/obj/item/scalpel/laser/advanced, +/obj/item/robotanalyzer, +/obj/item/scanner/health, +/obj/item/storage/firstaid/adv, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"Lq" = ( +/obj/structure/stairs{ + dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"Dg" = ( -/obj/effect/floor_decal/industrial/warning{ +/turf/simulated/floor/tiled/white, +/area/example/second) +"Lz" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on{ dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"Em" = ( /turf/simulated/floor/tiled/white, -/area/surgery) -"FJ" = ( +/area/example/second) +"LK" = ( +/obj/structure/table/glass, +/obj/item/storage/firstaid/surgery, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"LX" = ( +/obj/machinery/fabricator/imprinter, /obj/effect/floor_decal/corner/mauve/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"HZ" = ( -/obj/effect/floor_decal/industrial/warning/corner{ +/area/example/second) +"Nj" = ( +/obj/effect/floor_decal/industrial/warning/dust{ dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"KB" = ( -/obj/effect/floor_decal/corner/blue{ - dir = 6 +/obj/effect/floor_decal/corner/orange{ + dir = 10 }, -/obj/effect/floor_decal/industrial/warning/corner{ +/obj/machinery/light/small, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"Ny" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden{ dir = 4 }, -/turf/simulated/floor/tiled/white, -/area/surgery) -"LO" = ( -/obj/abstract/level_data/main_level, -/turf/space, -/area/space) -"Ry" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ +/turf/simulated/floor, +/area/example/second) +"NY" = ( +/obj/structure/ladder, +/turf/simulated/open, +/area/example/second) +"Pc" = ( +/obj/effect/floor_decal/corner/orange{ dir = 5 }, -/turf/simulated/floor/tiled/white, -/area/surgery) -"SE" = ( -/obj/effect/floor_decal/corner/blue{ - dir = 10 - }, -/turf/simulated/floor/tiled/white, -/area/surgery) -"Tt" = ( -/obj/machinery/design_database, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"Qn" = ( +/obj/machinery/destructive_analyzer, /obj/effect/floor_decal/corner/mauve/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"Vl" = ( +/area/example/second) +"QJ" = ( /obj/effect/floor_decal/corner/orange{ - dir = 9 + dir = 6 + }, +/obj/machinery/light{ + dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/surgery) -"WO" = ( +/area/example/second) +"QU" = ( /obj/structure/rack, /obj/item/stack/material/reinforced/mapped/plasteel/fifty, /obj/item/stack/material/ingot/mapped/copper/fifty, @@ -443,1649 +615,2909 @@ /obj/item/stack/material/gemstone/mapped/diamond/ten, /obj/item/storage/belt/utility/full, /obj/item/stack/material/sheet/mapped/steel/fifty, -/obj/machinery/light_switch/on{ - dir = 4; - pixel_x = -24 +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"SQ" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 10 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"Uf" = ( +/obj/effect/floor_decal/corner/orange/three_quarters{ + dir = 4 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"UN" = ( +/obj/machinery/computer/design_console{ + dir = 1 }, +/obj/effect/floor_decal/corner/mauve/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"Vm" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 4 + }, +/turf/simulated/floor, +/area/example/second) +"VG" = ( +/obj/machinery/fabricator/bioprinter, /obj/effect/floor_decal/corner/blue/mono, /turf/simulated/floor/tiled/white/monotile, -/area/surgery) -"Zk" = ( +/area/example/second) +"VL" = ( +/turf/simulated/open, +/area/turbolift/example/second) +"Xm" = ( +/obj/structure/iv_drip, +/obj/item/chems/ivbag/blood/OMinus, +/obj/item/chems/ivbag/blood/OMinus, +/obj/item/chems/ivbag/blood/OMinus, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"Xw" = ( +/obj/machinery/light/small{ + dir = 4 + }, +/turf/simulated/open, +/area/example/second) +"XE" = ( +/turf/simulated/wall/r_wall/prepainted, +/area/example/second) +"XR" = ( +/obj/effect/floor_decal/corner/orange{ + dir = 9 + }, +/turf/simulated/floor/tiled/steel_grid, +/area/example/second) +"Yn" = ( +/obj/machinery/fabricator/industrial, +/obj/effect/floor_decal/corner/mauve/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"Yy" = ( +/turf/simulated/floor/shuttle_ceiling, +/area/space) +"YT" = ( /obj/effect/floor_decal/corner/mauve{ - dir = 5 + dir = 1 + }, +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 + }, +/obj/machinery/power/apc{ + dir = 1; + pixel_y = 21 + }, +/obj/structure/cable/yellow{ + icon_state = "0-2" }, /turf/simulated/floor/tiled/white, -/area/surgery) +/area/example/second) +"ZD" = ( +/obj/machinery/body_scanconsole{ + dir = 1 + }, +/obj/effect/floor_decal/corner/blue/mono, +/turf/simulated/floor/tiled/white/monotile, +/area/example/second) +"ZI" = ( +/obj/machinery/atmospherics/portables_connector{ + dir = 1 + }, +/obj/machinery/portable_atmospherics/canister/air/airlock, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/second) (1,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +ug "} (2,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (3,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (4,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (5,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -LO -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (6,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (7,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (8,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (9,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (10,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (11,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +pc +pc +pc +pc +pc +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (12,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +lJ +lJ +lJ +lJ +lJ +hM +lJ +lJ +lJ +lJ +lJ +lJ +lJ +hM +lJ +lJ +lJ +lJ +lJ +lJ +lJ +lJ +hM +lJ +lJ +lJ +lJ +lJ +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (13,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +XE +XE +XE +XE +XE +XE +XE +sA +sA +sA +sA +sA +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (14,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +wP +wP +wP +CW +CW +rn +CW +CW +CW +CW +CW +CW +qV +sA +ln +fS +fS +sY +fS +fS +fS +fS +sY +fS +fS +pt +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (15,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +wP +xk +GG +CW +CW +XE +CW +CW +CW +CW +CW +CW +CW +sA +qA +En +ZD +lP +Xm +NY +lP +LX +cb +lP +dh +BY +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (16,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +XE +XE +XE +fX +oi +XE +CW +CW +CW +CW +CW +CW +CW +sA +qA +lP +nO +gl +cH +lP +lP +Qn +sT +Lz +dh +BY +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (17,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +fJ +dz +ii +CW +oC +XE +CW +CW +CW +CW +CW +CW +CW +sA +qA +LK +lP +lm +pr +lP +lP +EO +CN +lP +UN +Nj +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (18,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +pk +XE +ps +Ny +sO +sV +ZI +XE +CW +xX +CW +CW +CW +CW +CW +sA +qA +Hn +lP +lm +JU +lP +lP +Yn +CN +lP +kf +BY +XE +Am +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (19,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -ad -al -ao -at -az -aF -WO -Vl -Vl -aP -aR -aS -aT -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +XE +XE +XE +kx +XE +pc +XE +XE +XE +XE +XE +XE +sA +sA +sA +XE +qA +ys +lP +lm +aj +lP +lP +nC +CN +lP +kf +BY +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (20,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -ae -aB -ap -Em -Em -Em -SE -kL -kL -Tt -Zk -Em -aW -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +Hp +oS +oS +oS +oS +oS +oS +oS +XE +XR +Ba +XR +XE +nz +CW +CW +sA +qA +rb +lP +iS +XE +lP +Cq +XE +YT +CX +Ci +BY +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (21,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -af -Em -aq -Em -aE -Ry -SE -lr -aO -qL -Zk -Em -aX -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +Hp +oS +oS +oS +nB +oS +oS +oS +pc +CW +CW +CW +pc +CW +CW +CW +sA +qA +Dt +lP +lm +BG +lP +lP +BG +lm +lP +lP +BY +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (22,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -ag -Em -Em -Em -Em -hN -SE -aH -kL -Em -Em -Em -aQ -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +Hp +oS +oS +oS +oS +oS +oS +oS +pc +CW +CW +CW +sA +CW +CW +CW +sA +qA +QU +lP +lm +yy +lP +lP +yy +lm +lP +lP +Nj +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (23,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -ah -am -ar -au -KB -zv -aJ -aH -kL -tL -Zk -Em -ba -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +Hp +oS +oS +oS +oS +oS +oS +oS +pc +CW +CW +CW +sA +CW +CW +CW +sA +qA +VG +nO +iY +Lq +lP +lP +wP +gs +Lz +lP +BY +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (24,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -aj -an -an -an -Dg -aH -kL -aH -kL -tL -Zk -Em -FJ -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +Hp +oS +Yy +Yy +Yy +Yy +Yy +oS +pc +CW +CW +CW +sA +CW +CW +CW +sA +qA +lP +lP +lm +qk +lP +lP +qk +lm +lP +lP +BY +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (25,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -aj -an -an -an -Dg -aH -kL -aH -kL -kL -kL -kL -eY -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +Hp +oS +Yy +Yy +Yy +Yy +Yy +oS +pc +CW +CW +CW +pc +CW +CW +CW +sA +jr +uF +uF +uk +uF +uF +uF +uF +uk +uF +uF +zG +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (26,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -aj -an -an -an -Dg -aH -kL -aH -kL -kL -kL -kL -eY -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +Hp +oS +Yy +Yy +Yy +Yy +Yy +oS +XE +Cu +QJ +Cu +XE +nz +CW +CW +sA +NY +Cu +qW +jd +CW +CW +CW +CW +SQ +GP +Cu +Uf +XE +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (27,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -aj -an -an -an -Dg -aH -kL -aH -kL -kL -kL -kL -eY -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +XE +XE +pc +pc +pc +pc +XE +XE +XE +XE +XE +XE +sA +sA +sA +XE +XE +XE +XE +sA +sA +sA +sA +sA +sA +XE +XE +XE +XE +Am +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (28,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -ak -aZ -aZ -aZ -HZ -aH -kL -aH -kL -kL -kL -kL -aU -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +sA +CW +qV +CW +CW +CW +CW +CW +bU +Gy +XR +XR +CW +CW +CW +CW +CW +CW +XR +XR +XR +sA +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (29,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -ai -aC -kL -kL -iE -aG -kL -aL -aO -kL -kL -kL -eY -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +sA +CW +CW +CW +CW +CW +CW +CW +sA +Pc +yE +yE +yE +yE +yE +yE +yE +yE +yE +yE +yE +sA +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (30,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -av -aD -kL -kL -kL -aH -kL -aH -kL -kL -kL -kL -eY -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +sA +CW +CW +CW +CW +CW +CW +CW +sA +Pc +yE +mC +Cu +Cu +Cu +Cu +Cu +Cu +Cu +Cu +Cu +sA +lJ +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (31,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -aw -aK -as -ay -zS -aI -zS -aM -zS -zS -ay -zS -qY -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +sA +CW +CW +CW +CW +CW +CW +CW +sA +Pc +yE +xD +Gn +Gn +Gn +Gn +Gn +Gn +Gn +Gn +Gn +XE +Vm +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (32,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ac -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +nz +CW +CW +CW +oI +XE +CW +CW +CW +CW +CW +CW +CW +sA +Pc +yE +xD +Gn +wP +wP +wP +wP +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (33,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +XE +CW +CW +CW +CW +CW +CW +CW +sA +Pc +yE +xD +Gn +wP +wP +wP +wP +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (34,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -ab -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +XE +CW +xX +CW +CW +CW +xX +CW +sA +Pc +yE +xD +Gn +wP +wP +wP +wP +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (35,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +pk +XE +CW +CW +CW +CW +CW +XE +XE +XE +sA +sA +sA +XE +XE +XE +NY +jW +FE +Gn +wP +wP +wP +wP +wP +wP +wP +wP +wP +es +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (36,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +XE +CW +qV +CW +CW +CW +qV +CW +XE +VL +VL +VL +Gn +wP +wP +wP +wP +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (37,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +nz +CW +CW +CW +oI +XE +CW +CW +CW +CW +CW +CW +CW +XE +VL +VL +VL +Gn +wP +wP +wP +wP +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (38,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +XE +CW +CW +CW +CW +CW +CW +CW +XE +VL +VL +VL +Gn +wP +wP +wP +wP +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (39,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +CW +CW +CW +CW +CW +XE +CW +CW +CW +CW +CW +CW +CW +XE +wP +Xw +wP +Gn +wP +wP +wP +wP +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (40,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +XE +XE +XE +XE +XE +XE +XE +XE +sA +sA +sA +sA +sA +XE +XE +XE +XE +XE +XE +wP +wP +wP +wP +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (41,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +lJ +lJ +lJ +lJ +lJ +lJ +un +lJ +lJ +lJ +lJ +lJ +lJ +lJ +un +lJ +lJ +lJ +lJ +fU +wP +wP +wP +xk +wP +wP +wP +wP +wP +wP +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (42,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +XE +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (43,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (44,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +oS +xs +xs +xs +xs +xs +xs +xs +xs "} (45,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (46,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (47,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} (48,1,1) = {" -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa -aa +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +"} +(49,1,1) = {" +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +"} +(50,1,1) = {" +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +"} +(51,1,1) = {" +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +"} +(52,1,1) = {" +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs +xs "} diff --git a/maps/example/example-3.dmm b/maps/example/example-3.dmm index 4cdf9f783f0..ac8499fa29a 100644 --- a/maps/example/example-3.dmm +++ b/maps/example/example-3.dmm @@ -1,1946 +1,3140 @@ //MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE -"a" = ( +"bE" = ( +/obj/abstract/map_data/example, /turf/space, /area/space) -"b" = ( -/turf/simulated/wall/r_wall, -/area/space) -"c" = ( -/turf/simulated/wall, -/area/maintenance/fsmaint2) -"d" = ( -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/maintenance/fsmaint2) -"e" = ( -/obj/machinery/light/spot{ - dir = 1 - }, -/obj/effect/floor_decal/corner/orange{ - dir = 5 +"cd" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 4 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"f" = ( -/obj/structure/lattice, -/turf/simulated/open, -/area/maintenance/fsmaint2) -"g" = ( +/obj/effect/floor_decal/industrial/warning/dust, +/turf/simulated/floor, +/area/example/third) +"cj" = ( /turf/simulated/open, -/area/maintenance/fsmaint2) -"h" = ( -/obj/machinery/light_switch/on{ - dir = 8; - pixel_x = 24 +/area/turbolift/example/third) +"cw" = ( +/obj/machinery/atmospherics/portables_connector, +/obj/machinery/portable_atmospherics/canister/empty, +/obj/effect/floor_decal/industrial/outline/red, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/third) +"dx" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 5 }, -/obj/machinery/atmospherics/pipe/simple/hidden, -/obj/effect/floor_decal/corner/orange{ - dir = 6 +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 10 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"i" = ( -/obj/effect/shuttle_landmark/upper_level, -/turf/space, -/area/space) -"j" = ( -/obj/machinery/light/spot{ +/turf/simulated/floor, +/area/example/third) +"eg" = ( +/obj/machinery/light{ dir = 8 }, -/obj/effect/floor_decal/corner/orange{ - dir = 9 - }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"k" = ( -/obj/structure/ladder, -/obj/structure/railing/mapped{ +/obj/effect/floor_decal/industrial/warning/dust{ dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"l" = ( +/area/example/third) +"eD" = ( /obj/machinery/atmospherics/pipe/simple/hidden{ - dir = 4 - }, -/obj/machinery/door/airlock/external/bolted{ - id_tag = "upper_level_dock_hatch_internal" + dir = 6 }, -/obj/machinery/button/access/interior{ - id_tag = "upper_level_dock"; - pixel_y = -21 +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 8 }, /turf/simulated/floor, -/area/maintenance/fsmaint2) -"m" = ( -/obj/machinery/door/airlock/external/bolted{ - id_tag = "upper_level_dock_hatch_external" - }, -/obj/machinery/button/access/exterior{ - id_tag = "upper_level_dock"; +/area/example/third) +"hv" = ( +/obj/machinery/alarm{ + dir = 1; pixel_y = -21 }, /turf/simulated/floor, -/area/maintenance/fsmaint2) -"n" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ - dir = 6 +/area/example/third) +"iI" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 9 }, /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"q" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"s" = ( -/obj/machinery/atmospherics/portables_connector{ +/area/example/third) +"jb" = ( +/obj/machinery/light, +/turf/simulated/open, +/area/example/third) +"lK" = ( +/turf/simulated/floor/glass, +/area/example/third) +"lY" = ( +/obj/machinery/atmospherics/unary/vent_pump/on, +/turf/simulated/floor, +/area/example/third) +"nG" = ( +/turf/simulated/open, +/area/space) +"nN" = ( +/obj/effect/floor_decal/industrial/warning/dust{ dir = 8 }, -/obj/machinery/portable_atmospherics/canister/air, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/maintenance/fsmaint2) -"t" = ( -/obj/machinery/atmospherics/pipe/manifold/hidden{ +/turf/simulated/floor/tiled/steel_grid, +/area/example/third) +"pB" = ( +/obj/effect/floor_decal/industrial/warning/dust, +/turf/simulated/floor/tiled/steel_grid, +/area/example/third) +"pH" = ( +/obj/machinery/light{ dir = 1 }, +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 9 + }, /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"u" = ( -/obj/machinery/atmospherics/portables_connector{ +/area/example/third) +"rX" = ( +/obj/structure/railing/mapped{ dir = 8 }, -/obj/machinery/portable_atmospherics/canister/empty, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/maintenance/fsmaint2) -"v" = ( -/obj/machinery/light/spot{ - dir = 1 +/obj/machinery/atmospherics/pipe/simple/hidden, +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 4 }, -/obj/effect/floor_decal/corner/orange{ +/turf/simulated/floor, +/area/example/third) +"sH" = ( +/obj/effect/wallframe_spawn/reinforced, +/turf/simulated/floor/plating, +/area/example/third) +"uC" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ dir = 5 }, -/obj/effect/floor_decal/industrial/warning{ - dir = 8 +/obj/effect/floor_decal/industrial/warning/dust, +/turf/simulated/floor, +/area/example/third) +"uK" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ + dir = 9 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"w" = ( -/obj/abstract/level_data/main_level, -/turf/space, -/area/space) -"x" = ( -/obj/machinery/light/spot, -/obj/effect/floor_decal/corner/orange{ - dir = 10 +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 6 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"y" = ( -/obj/structure/cable{ - icon_state = "0-2" +/turf/simulated/floor, +/area/example/third) +"vo" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 5 }, -/obj/machinery/power/apc{ - dir = 8; - name = "west bump"; - pixel_x = -24 +/turf/simulated/floor, +/area/example/third) +"ww" = ( +/obj/machinery/light{ + dir = 8 }, -/obj/effect/floor_decal/corner/orange{ - dir = 9 +/turf/simulated/open, +/area/example/third) +"wD" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"z" = ( -/obj/structure/cable{ - icon_state = "1-2" - }, -/obj/effect/floor_decal/corner/orange{ +/area/example/third) +"xk" = ( +/turf/space, +/area/space) +"xD" = ( +/obj/machinery/atmospherics/pipe/manifold/hidden, +/obj/effect/floor_decal/industrial/warning/dust, +/turf/simulated/floor, +/area/example/third) +"yv" = ( +/obj/effect/floor_decal/industrial/warning/dust{ dir = 9 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"A" = ( -/obj/structure/cable, -/obj/machinery/power/debug_items/infinite_generator, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/maintenance/fsmaint2) -"B" = ( -/obj/effect/floor_decal/corner/orange{ - dir = 10 - }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"C" = ( +/turf/simulated/floor, +/area/example/third) +"Al" = ( /obj/machinery/atmospherics/pipe/simple/hidden, -/obj/effect/floor_decal/corner/orange{ - dir = 6 +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 8 }, +/turf/simulated/floor, +/area/example/third) +"AD" = ( /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"E" = ( +/area/example/third) +"Em" = ( /obj/machinery/atmospherics/pipe/simple/hidden{ - dir = 5 + dir = 10 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"F" = ( -/obj/machinery/atmospherics/portables_connector, -/obj/machinery/portable_atmospherics/canister/air/airlock, -/obj/effect/floor_decal/corner/orange/mono, -/turf/simulated/floor/tiled/monotile, -/area/maintenance/fsmaint2) -"G" = ( -/obj/machinery/atmospherics/pipe/simple/hidden{ +/obj/effect/floor_decal/industrial/warning/dust{ dir = 4 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"H" = ( -/obj/effect/floor_decal/industrial/warning/corner{ +/turf/simulated/floor, +/area/example/third) +"Es" = ( +/turf/simulated/floor, +/area/example/third) +"EO" = ( +/obj/structure/railing/mapped{ dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"I" = ( -/obj/effect/floor_decal/industrial/warning{ +/turf/simulated/floor, +/area/example/third) +"Ff" = ( +/obj/machinery/atmospherics/portables_connector, +/obj/machinery/portable_atmospherics/canister/air, +/obj/effect/floor_decal/industrial/outline/blue, +/turf/simulated/floor/tiled/dark/monotile, +/area/example/third) +"Fk" = ( +/obj/machinery/light{ dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"J" = ( -/obj/effect/floor_decal/industrial/warning{ - dir = 8 +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 5 }, /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"K" = ( -/obj/effect/floor_decal/corner/orange{ - dir = 6 +/area/example/third) +"GA" = ( +/obj/machinery/power/debug_items/infinite_generator, +/obj/structure/cable/yellow{ + icon_state = "0-2" + }, +/turf/simulated/floor, +/area/example/third) +"HC" = ( +/obj/effect/floor_decal/industrial/warning/dust, +/turf/simulated/floor, +/area/example/third) +"Ke" = ( +/turf/simulated/open, +/area/example/third) +"KS" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 1 }, /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"L" = ( -/obj/machinery/light_switch/on{ - dir = 4; - pixel_x = -24 +/area/example/third) +"LP" = ( +/turf/simulated/wall/r_wall/prepainted, +/area/example/third) +"MG" = ( +/obj/structure/cable/yellow{ + icon_state = "1-4" }, -/obj/effect/floor_decal/corner/orange{ +/turf/simulated/floor, +/area/example/third) +"MO" = ( +/obj/abstract/level_data/main_level, +/turf/space, +/area/space) +"Nz" = ( +/obj/machinery/atmospherics/pipe/simple/hidden{ dir = 9 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"M" = ( -/obj/structure/railing/mapped{ - dir = 4 - }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"P" = ( -/obj/structure/railing/mapped{ - dir = 4 +/obj/effect/floor_decal/industrial/warning/dust, +/turf/simulated/floor, +/area/example/third) +"NO" = ( +/obj/machinery/light, +/turf/simulated/floor, +/area/example/third) +"NP" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 1 }, -/obj/effect/floor_decal/corner/orange{ - dir = 5 +/turf/simulated/floor, +/area/example/third) +"OJ" = ( +/obj/machinery/door/airlock/external/glass, +/turf/simulated/floor/tiled/steel_ridged, +/area/example/third) +"Qg" = ( +/obj/machinery/light{ + dir = 1 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"Q" = ( -/obj/machinery/atmospherics/unary/vent_pump/on, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"S" = ( -/obj/machinery/atmospherics/unary/vent_pump/high_volume/external_air{ - dir = 8; - id_tag = "upper_level_dock_pump" +/turf/simulated/floor, +/area/example/third) +"QH" = ( +/obj/machinery/power/apc{ + dir = 1; + pixel_y = 21 }, -/obj/machinery/embedded_controller/radio/airlock/docking_port{ - id_tag = "upper_level_dock"; - pixel_y = 24; - tag_airpump = "upper_level_dock_pump"; - tag_chamber_sensor = "upper_level_dock_sensor_chamber"; - tag_exterior_door = "upper_level_dock_hatch_external"; - tag_interior_door = "upper_level_dock_hatch_internal" +/obj/structure/cable/yellow{ + icon_state = "0-8" }, -/obj/machinery/airlock_sensor{ - id_tag = "upper_level_dock_sensor_chamber"; - pixel_y = -21 +/obj/machinery/light_switch/on{ + pixel_x = 11; + pixel_y = 31 }, /turf/simulated/floor, -/area/maintenance/fsmaint2) -"T" = ( -/obj/machinery/light/spot{ +/area/example/third) +"TM" = ( +/obj/structure/catwalk, +/turf/simulated/open, +/area/example/third) +"TO" = ( +/obj/machinery/atmospherics/pipe/simple/hidden, +/obj/effect/floor_decal/industrial/warning/dust{ dir = 4 }, -/obj/effect/floor_decal/corner/orange{ - dir = 6 - }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"U" = ( +/turf/simulated/floor, +/area/example/third) +"UC" = ( +/obj/structure/ladder, +/turf/simulated/open, +/area/example/third) +"UM" = ( /obj/machinery/atmospherics/unary/vent_scrubber/on{ - dir = 1 + dir = 8 + }, +/turf/simulated/floor, +/area/example/third) +"UV" = ( +/obj/machinery/light{ + dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"V" = ( -/obj/abstract/map_data{ - height = 3 +/area/example/third) +"Wn" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on, +/turf/simulated/floor, +/area/example/third) +"WU" = ( +/obj/effect/floor_decal/industrial/warning/dust{ + dir = 6 }, -/turf/space, -/area/space) -"W" = ( /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"X" = ( -/obj/effect/floor_decal/industrial/warning/corner{ +/area/example/third) +"Xg" = ( +/obj/structure/railing/mapped{ dir = 4 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"Y" = ( -/obj/effect/floor_decal/corner/orange{ - dir = 5 +/turf/simulated/floor, +/area/example/third) +"Ye" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 4 }, -/turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) -"Z" = ( -/obj/effect/floor_decal/corner/orange{ - dir = 9 +/turf/simulated/floor, +/area/example/third) +"Zr" = ( +/obj/effect/floor_decal/industrial/warning/dust/corner{ + dir = 1 + }, +/obj/effect/floor_decal/industrial/warning/dust/corner{ + dir = 4 }, /turf/simulated/floor/tiled/steel_grid, -/area/maintenance/fsmaint2) +/area/example/third) (1,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -V +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +MO "} (2,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +bE "} (3,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (4,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (5,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -w -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (6,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (7,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (8,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (9,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (10,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (11,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (12,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +ww +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (13,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (14,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (15,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +yv +eD +Al +Al +Al +Al +Al +Al +Al +dx +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (16,1,1) = {" -a -a -a -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +NP +UM +Es +Es +Es +Es +Es +Es +Wn +xD +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (17,1,1) = {" -a -a -a -a -a -a -a -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +NP +Es +Es +Es +Es +Es +Es +Es +Es +cd +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (18,1,1) = {" -a -a -a -a -a -a -a -b -b -c -c -c -c -c -c -c -c -c -c -c -c -c -c -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +NP +GA +MG +Es +Es +Es +Es +Es +cw +Nz +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (19,1,1) = {" -a -a -a -a -a -a -a -b -b -c -d -Z -Z -j -Z -Z -L -Z -Z -j -y -z -A -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +LP +LP +LP +LP +LP +LP +LP +LP +pH +nN +nN +nN +nN +nN +nN +nN +nN +NP +LP +QH +Es +Es +Es +Es +hv +LP +HC +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (20,1,1) = {" -a -a -a -a -a -a -a -b -b -c -Y -W -W -W -W -W -W -W -W -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +lK +lK +lK +lK +lK +lK +lK +sH +KS +AD +AD +AD +AD +AD +AD +AD +AD +NP +LP +Qg +Es +Es +Es +Es +NO +LP +HC +Ke +Ke +jb +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (21,1,1) = {" -a -a -a -a -a -a -a -b -b -c -Y -W -W -W -Q -E -W -n -U -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +lK +lK +lK +lK +lK +lK +lK +sH +KS +AD +AD +AD +AD +AD +AD +AD +AD +NP +Es +Es +Es +Es +Es +Es +Es +Ff +uC +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (22,1,1) = {" -a -a -a -a -a -a -a -b -b -c -e -W -W -W -W -G -W -G -W -W -W -W -x -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +lK +lK +lK +lK +lK +lK +lK +sH +KS +AD +AD +AD +AD +AD +AD +AD +AD +NP +Es +Es +Xg +Es +Es +Es +Es +Es +cd +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (23,1,1) = {" -a -a -a -a -a -a -a -b -b -c -P -M -M -k -X -G -W -G -W -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +lK +lK +lK +lK +lK +lK +lK +sH +KS +AD +AD +AD +AD +AD +AD +AD +AD +NP +Ye +Es +Ke +EO +Es +Es +Es +lY +xD +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (24,1,1) = {" -a -a -a -a -a -a -a -b -b -c -f -g -g -g -I -G -W -G -W -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +lK +lK +lK +lK +lK +lK +lK +sH +KS +AD +AD +AD +AD +AD +AD +AD +AD +vo +Em +TO +rX +TO +TO +TO +TO +TO +uK +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (25,1,1) = {" -a -a -a -a -a -a -a -b -b -c -f -g -g -g -I -G -W -G -W -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +lK +lK +lK +lK +lK +lK +lK +sH +KS +AD +AD +AD +AD +AD +AD +UV +AD +AD +pB +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (26,1,1) = {" -a -a -a -a -a -a -a -b -b -c -f -g -g -g -I -G -W -G -W -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +lK +lK +lK +lK +lK +lK +iI +OJ +Zr +AD +AD +AD +AD +AD +AD +LP +UC +AD +pB +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (27,1,1) = {" -a -a -a -a -a -a -a -b -b -c -f -g -g -g -I -G -W -G -W -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +LP +LP +LP +LP +LP +LP +LP +LP +Fk +wD +wD +wD +wD +wD +wD +eg +wD +wD +WU +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (28,1,1) = {" -a -a -a -a -a -a -a -b -b -c -v -J -J -J -H -G -W -G -W -W -W -W -x -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (29,1,1) = {" -a -a -a -a -a -a -a -b -b -c -Y -W -W -W -Q -q -W -t -U -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (30,1,1) = {" -a -a -a -a -a -a -a -b -b -c -Y -W -W -W -W -G -W -G -W -W -W -W -B -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (31,1,1) = {" -a -a -a -a -a -a -a -b -b -c -F -C -h -E -T -s -K -u -K -K -T -K -d -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (32,1,1) = {" -a -a -a -a -a -a -a -b -b -c -c -c -c -l -c -c -c -c -c -c -c -c -c -c -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (33,1,1) = {" -a -a -a -a -a -a -a -b -b -b -b -b -c -S -c -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (34,1,1) = {" -a -a -a -a -a -a -a -b -b -b -b -b -c -m -c -b -b -b -b -b -b -b -b -b -b -b -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (35,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (36,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -i -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +cj +cj +cj +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (37,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +cj +cj +cj +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (38,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +cj +cj +cj +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (39,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +Ke +Ke +Ke +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (40,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +TM +TM +TM +TM +TM +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (41,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +Ke +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (42,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +LP +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (43,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (44,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +nG +xk +xk +xk +xk +xk +xk +xk +xk "} (45,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (46,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (47,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} (48,1,1) = {" -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a -a +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +"} +(49,1,1) = {" +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +"} +(50,1,1) = {" +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +"} +(51,1,1) = {" +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +"} +(52,1,1) = {" +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk +xk "} diff --git a/maps/example/example.dm b/maps/example/example.dm index 84d1fed74c8..72a8676b951 100644 --- a/maps/example/example.dm +++ b/maps/example/example.dm @@ -14,6 +14,6 @@ #elif !defined(MAP_OVERRIDE) - #warn A map has already been included, ignoring Example + #warn A map has already been included, ignoring Testing Site #endif diff --git a/maps/example/example_areas.dm b/maps/example/example_areas.dm index e734a12e3de..4850ad5ed1e 100644 --- a/maps/example/example_areas.dm +++ b/maps/example/example_areas.dm @@ -1,18 +1,17 @@ -/area/constructionsite - name = "\improper Construction Site" - icon_state = "storage" - -/area/maintenance/fsmaint2 - name = "\improper Fore Starboard Maintenance - 2" +/area/example/first + name = "\improper Testing Site First Floor" icon_state = "fsmaint" -/area/surgery - name = "\improper Operating Theatre" +/area/example/second + name = "\improper Testing Site Second Floor" icon_state = "surgery" - req_access = list(access_medical) -/area/turbolift - name = "\improper Example Elevator" +/area/example/third + name = "\improper Testing Site Third Floor" + icon_state = "storage" + +/area/turbolift/example + name = "\improper Testing Site Elevator" icon_state = "shuttle" requires_power = FALSE dynamic_lighting = TRUE @@ -33,23 +32,18 @@ arrival_sound = null lift_announce_str = null -/area/turbolift/alert_on_fall(var/mob/living/carbon/human/H) - if(H.client && SSpersistence.elevator_fall_shifts > 0) - SSwebhooks.send(WEBHOOK_ELEVATOR_FALL, list("text" = "We managed to make it [SSpersistence.elevator_fall_shifts] shift\s without someone falling down an elevator shaft.")) - SSpersistence.elevator_fall_shifts = -1 + base_turf = /turf/simulated/open -/area/turbolift/first - name = "Lower Level" +/area/turbolift/example/first + name = "Testing Site First Floor Lift" base_turf = /turf/simulated/floor -/area/turbolift/second - name = "Surgery Level" - base_turf = /turf/simulated/open +/area/turbolift/example/second + name = "Testing Site Second Floor Lift" -/area/turbolift/third - name = "Upper Level" - base_turf = /turf/simulated/open +/area/turbolift/example/third + name = "Testing Site Third Floor Lift" -/area/shuttle/escape - name = "\improper Emergency Shuttle" +/area/shuttle/ferry + name = "\improper Testing Site Ferry" icon_state = "shuttle" diff --git a/maps/example/example_define.dm b/maps/example/example_define.dm index c319509b81d..3665b6c140a 100644 --- a/maps/example/example_define.dm +++ b/maps/example/example_define.dm @@ -1,6 +1,6 @@ /datum/map/example - name = "Example" - full_name = "The Example" + name = "Testing" + full_name = "Testing Site" path = "example" lobby_screens = list( @@ -11,7 +11,9 @@ /decl/music_track/absconditus ) - allowed_spawns = list(/decl/spawnpoint/arrivals) + allowed_spawns = list( + /decl/spawnpoint/arrivals + ) shuttle_docked_message = "The shuttle has docked." shuttle_leaving_dock = "The shuttle has departed from home dock." diff --git a/maps/example/example_jobs.dm b/maps/example/example_jobs.dm index 0fd999de7bd..311b1195ffb 100644 --- a/maps/example/example_jobs.dm +++ b/maps/example/example_jobs.dm @@ -11,8 +11,10 @@ economic_power = 1 access = list() minimal_access = list() - outfit_type = /decl/hierarchy/outfit/job/example_tourist - department_types = list(/decl/department/example) + outfit_type = /decl/hierarchy/outfit/job/tourist + department_types = list( + /decl/department/example + ) -/decl/hierarchy/outfit/job/example_tourist - name = "Job - Example Tourist" +/decl/hierarchy/outfit/job/tourist + name = "Job - Testing Site Tourist" diff --git a/maps/example/example_lobby.png b/maps/example/example_lobby.png index 06d81c56d9f..a744adebfb6 100644 Binary files a/maps/example/example_lobby.png and b/maps/example/example_lobby.png differ diff --git a/maps/example/example_shuttles.dm b/maps/example/example_shuttles.dm index debe72c7e22..6c04021d998 100644 --- a/maps/example/example_shuttles.dm +++ b/maps/example/example_shuttles.dm @@ -15,8 +15,8 @@ ) /datum/shuttle/autodock/ferry/example - name = "Example" - shuttle_area = /area/shuttle/escape + name = "Testing Site Ferry" + shuttle_area = /area/shuttle/ferry dock_target = "example_shuttle_starboard" warmup_time = 10 @@ -27,9 +27,10 @@ "STARBOARD" = "example_shuttle_starboard", "PORT" = "example_shuttle_port" ) + ceiling_type = /turf/simulated/floor/shuttle_ceiling /obj/turbolift_map_holder/example - name = "Example elevator placeholder" + name = "Testing Site elevator placeholder" icon = 'icons/obj/turbolift_preview_nowalls_3x3.dmi' depth = 3 lift_size_x = 2 @@ -42,9 +43,9 @@ button_type = /obj/structure/lift/button/standalone panel_type = /obj/structure/lift/panel/standalone areas_to_use = list( - /area/turbolift/first, - /area/turbolift/second, - /area/turbolift/third + /area/turbolift/example/first, + /area/turbolift/example/second, + /area/turbolift/example/third ) floor_departure_sound = 'sound/effects/lift_heavy_start.ogg' - floor_arrival_sound = 'sound/effects/lift_heavy_stop.ogg' + floor_arrival_sound = 'sound/effects/lift_heavy_stop.ogg' diff --git a/maps/example/example_unit_testing.dm b/maps/example/example_unit_testing.dm index 263157ae9de..6c4c9380ab3 100644 --- a/maps/example/example_unit_testing.dm +++ b/maps/example/example_unit_testing.dm @@ -3,6 +3,9 @@ apc_test_exempt_areas = list( /area/space = NO_SCRUBBER|NO_VENT|NO_APC, /area/exoplanet = NO_SCRUBBER|NO_VENT|NO_APC, - /area/turbolift = NO_SCRUBBER|NO_VENT|NO_APC, - /area/shuttle/escape = NO_SCRUBBER|NO_VENT|NO_APC + /area/turbolift/example = NO_SCRUBBER|NO_VENT|NO_APC, + /area/shuttle/ferry = NO_SCRUBBER|NO_VENT|NO_APC ) + +/obj/abstract/map_data/example + height = 3 diff --git a/maps/exodus/exodus-2.dmm b/maps/exodus/exodus-2.dmm index dac9270ce15..595a495a67d 100644 --- a/maps/exodus/exodus-2.dmm +++ b/maps/exodus/exodus-2.dmm @@ -46572,7 +46572,7 @@ /turf/simulated/floor/tiled/white, /area/exodus/medical/medbay) "bSp" = ( -/obj/machinery/power/port_gen/pacman{ +/obj/machinery/port_gen/pacman{ sheets = 25 }, /obj/effect/floor_decal/industrial/warning{ @@ -57522,7 +57522,7 @@ /turf/simulated/floor/airless, /area/exodus/solar/starboard) "cpn" = ( -/obj/machinery/power/rad_collector, +/obj/machinery/rad_collector, /turf/simulated/floor/plating, /area/exodus/engineering/storage) "cpo" = ( @@ -59423,7 +59423,7 @@ /turf/simulated/floor/tiled/techfloor, /area/exodus/engineering/storage) "cuB" = ( -/obj/machinery/power/emitter, +/obj/machinery/emitter, /turf/simulated/floor/plating, /area/exodus/engineering/storage) "cuC" = ( @@ -59510,7 +59510,7 @@ /obj/structure/cable/yellow{ icon_state = "0-2" }, -/obj/machinery/power/generator{ +/obj/machinery/generator{ anchored = 1; dir = 4 }, @@ -62224,12 +62224,15 @@ /turf/simulated/floor/plating, /area/exodus/engineering/engine_room) "cIa" = ( -/obj/machinery/power/emitter{ +/obj/machinery/emitter{ anchored = 1; id_tag = "EngineEmitter"; state = 2 }, /obj/structure/cable/cyan, +/obj/machinery/power/terminal{ + dir = 1 + }, /turf/simulated/floor/plating, /area/exodus/engineering/engine_room) "cIb" = ( @@ -62756,7 +62759,7 @@ /turf/simulated/floor/plating, /area/exodus/maintenance/engi_engine) "cJZ" = ( -/obj/machinery/power/generator{ +/obj/machinery/generator{ anchored = 1; dir = 4 }, @@ -63996,7 +63999,7 @@ /turf/simulated/floor/tiled/steel_grid, /area/exodus/quartermaster/miningdock) "jko" = ( -/obj/machinery/power/rad_collector, +/obj/machinery/rad_collector, /obj/effect/floor_decal/industrial/warning/corner{ dir = 8 }, @@ -64310,7 +64313,7 @@ /turf/simulated/floor/tiled/steel_grid, /area/exodus/research/chargebay) "ohn" = ( -/obj/machinery/power/emitter, +/obj/machinery/emitter, /obj/effect/floor_decal/industrial/warning{ dir = 8 }, diff --git a/maps/ministation/ministation.dmm b/maps/ministation/ministation.dmm index ca0af295ae0..fd395e2dcb6 100644 --- a/maps/ministation/ministation.dmm +++ b/maps/ministation/ministation.dmm @@ -11786,7 +11786,7 @@ /turf/simulated/floor/tiled, /area/ministation/engine) "FJ" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /turf/simulated/floor/tiled, /area/ministation/engine) "FK" = ( @@ -13853,7 +13853,7 @@ /turf/simulated/floor/tiled, /area/ministation/ai_sat) "LG" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/structure/cable, /turf/simulated/floor/tiled, /area/ministation/ai_sat) diff --git a/maps/nexus/nexus-1.dmm b/maps/nexus/nexus-1.dmm index ea308eb0ad1..dcc8e89048e 100644 --- a/maps/nexus/nexus-1.dmm +++ b/maps/nexus/nexus-1.dmm @@ -778,7 +778,7 @@ /obj/structure/window/borosilicate_reinforced{ dir = 1 }, -/obj/machinery/power/emitter/gyrotron/anchored{ +/obj/machinery/emitter/gyrotron/anchored{ dir = 1; initial_id_tag = "main" }, @@ -789,6 +789,7 @@ icon_state = "0-8" }, /obj/structure/catwalk, +/obj/machinery/power/terminal, /turf/simulated/floor/plating, /area/nexus/engineering/engine) "hn" = ( @@ -2606,7 +2607,7 @@ /turf/simulated/floor/tiled, /area/nexus/hallway/starboard/aft) "yE" = ( -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /obj/structure/cable, /obj/effect/floor_decal/industrial/warning{ dir = 1 @@ -2761,7 +2762,7 @@ /turf/simulated/floor/tiled/white, /area/nexus/civilian) "Ay" = ( -/obj/structure/stairs/west, +/obj/structure/stairs/long/west, /turf/simulated/floor/plating, /area/nexus/civilian) "AA" = ( @@ -3780,7 +3781,7 @@ /turf/simulated/floor/tiled/white, /area/nexus/civilian) "JR" = ( -/obj/machinery/power/fusion_core/mapped{ +/obj/machinery/fusion_core/mapped{ initial_id_tag = "main" }, /obj/structure/cable{ @@ -4884,7 +4885,7 @@ /turf/simulated/floor/tiled, /area/nexus/hallway/port) "Ug" = ( -/obj/structure/stairs/east, +/obj/structure/stairs/long/east, /turf/simulated/floor/plating, /area/nexus/civilian) "Uk" = ( diff --git a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm index 4964e071869..4ac8a2734bb 100644 --- a/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm +++ b/maps/random_ruins/exoplanet_ruins/crashed_pod/crashed_pod.dmm @@ -971,7 +971,7 @@ /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 1 }, -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /obj/machinery/light/small, /turf/simulated/floor/tiled/techfloor, /area/map_template/crashed_pod) diff --git a/maps/random_ruins/exoplanet_ruins/lodge/lodge.dmm b/maps/random_ruins/exoplanet_ruins/lodge/lodge.dmm index 0d4bb6d39ce..1f63596b5c4 100644 --- a/maps/random_ruins/exoplanet_ruins/lodge/lodge.dmm +++ b/maps/random_ruins/exoplanet_ruins/lodge/lodge.dmm @@ -6,7 +6,7 @@ /turf/simulated/wall/wood, /area/template_noop) "c" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /turf/simulated/floor/wood, /area/template_noop) "d" = ( diff --git a/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm b/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm index c779f034d81..bb33f799726 100644 --- a/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm +++ b/maps/random_ruins/exoplanet_ruins/marooned/marooned.dmm @@ -216,7 +216,7 @@ "aI" = ( /obj/item/wirecutters, /obj/item/stack/cable_coil, -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /obj/structure/cable{ dir = 8; icon_state = "0-5" diff --git a/maps/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm b/maps/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm index 365c597a517..e2c5d115b68 100644 --- a/maps/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm +++ b/maps/random_ruins/exoplanet_ruins/oldpod/oldpod.dmm @@ -450,7 +450,7 @@ /turf/simulated/floor/plating, /area/map_template/oldpod) "bc" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/structure/cable/cyan, /obj/effect/floor_decal/industrial/warning{ dir = 4 diff --git a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm index acc73879858..e221e45c337 100644 --- a/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm +++ b/maps/random_ruins/exoplanet_ruins/playablecolony/colony.dmm @@ -886,7 +886,7 @@ /obj/machinery/light{ dir = 1 }, -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /turf/simulated/floor/tiled/techfloor/grid, /area/map_template/colony/engineering) "cb" = ( @@ -922,7 +922,7 @@ "cf" = ( /obj/structure/catwalk, /obj/effect/decal/cleanable/dirt, -/obj/machinery/power/emitter/gyrotron, +/obj/machinery/emitter/gyrotron, /turf/exterior/concrete, /area/template_noop) "cg" = ( @@ -1411,7 +1411,7 @@ /area/template_noop) "dn" = ( /obj/structure/catwalk, -/obj/machinery/power/rad_collector, +/obj/machinery/rad_collector, /obj/effect/decal/cleanable/dirt, /turf/exterior/concrete, /area/template_noop) diff --git a/maps/torch/datums/reports/command.dm b/maps/torch/datums/reports/command.dm index 9e1380eb72b..50e83ce5be7 100644 --- a/maps/torch/datums/reports/command.dm +++ b/maps/torch/datums/reports/command.dm @@ -23,7 +23,7 @@ xo_fields += add_field(/datum/report_field/number, "Number of personnel in requested position") xo_fields += add_field(/datum/report_field/options/yes_no, "Approved") for(var/datum/report_field/field in xo_fields) - field.set_access(access_edit = access_hop) + field.set_access(write_access = access_hop) /datum/computer_file/report/recipient/access_modification form_name = "AMA-SGF-02" @@ -49,4 +49,4 @@ xo_fields += add_field(/datum/report_field/number, "Number of personnel in relevant position") xo_fields += add_field(/datum/report_field/options/yes_no, "Approved") for(var/datum/report_field/field in xo_fields) - field.set_access(access_edit = access_hop) \ No newline at end of file + field.set_access(write_access = access_hop) \ No newline at end of file diff --git a/maps/torch/datums/reports/corporate.dm b/maps/torch/datums/reports/corporate.dm index 42684c7f2b2..5b7afc09ba9 100644 --- a/maps/torch/datums/reports/corporate.dm +++ b/maps/torch/datums/reports/corporate.dm @@ -34,7 +34,7 @@ /datum/computer_file/report/recipient/corp/memo/external/New() ..() - set_access(access_edit = access_nanotrasen) + set_access(write_access = access_nanotrasen) //No access restrictions for easier use. /datum/computer_file/report/recipient/corp/sales @@ -71,7 +71,7 @@ add_field(/datum/report_field/text_label, "To be shipped and delivered directly to the employee's next of kin without delay.") add_field(/datum/report_field/signature, "Signature") add_field(/datum/report_field/options/yes_no, "Approved") - set_access(access_edit = access_nanotrasen) + set_access(write_access = access_nanotrasen) /datum/computer_file/report/recipient/corp/fire form_name = "C-0102" @@ -80,8 +80,7 @@ /datum/computer_file/report/recipient/corp/fire/New() ..() - set_access(access_heads, access_heads) - set_access(access_nanotrasen, override = 0) + set_access(list(list(access_heads, access_nanotrasen)), access_heads) /datum/computer_file/report/recipient/corp/fire/generate_fields() ..() @@ -95,7 +94,7 @@ /datum/computer_file/report/recipient/corp/incident/New() ..() - set_access(access_edit = access_nanotrasen) + set_access(write_access = access_nanotrasen) /datum/computer_file/report/recipient/corp/incident/generate_fields() ..() @@ -134,7 +133,7 @@ temp_fields += add_field(/datum/report_field/signature, "Corporate Representative's Signature") temp_fields += add_field(/datum/report_field/options/yes_no, "Approved") for(var/datum/report_field/temp_field in temp_fields) - temp_field.set_access(access_edit = access_nanotrasen) + temp_field.set_access(write_access = access_nanotrasen) /datum/computer_file/report/recipient/corp/deny form_name = "C-1443D" @@ -152,4 +151,4 @@ add_field(/datum/report_field/options/yes_no, "Project Cancellation") add_field(/datum/report_field/simple_text, "Other") add_field(/datum/report_field/options/yes_no, "Report Approved") - set_access(access_edit = access_nanotrasen) \ No newline at end of file + set_access(write_access = access_nanotrasen) \ No newline at end of file diff --git a/maps/torch/datums/reports/deck.dm b/maps/torch/datums/reports/deck.dm index 0df46023a9a..f795ce5ded7 100644 --- a/maps/torch/datums/reports/deck.dm +++ b/maps/torch/datums/reports/deck.dm @@ -10,8 +10,7 @@ /datum/computer_file/report/recipient/docked/New() ..() - set_access(access_cargo, access_cargo) - set_access(access_heads, override = 0) + set_access(list(list(access_cargo, access_heads)), access_cargo) /datum/computer_file/report/recipient/docked/generate_fields() ..() diff --git a/maps/torch/datums/reports/medical.dm b/maps/torch/datums/reports/medical.dm index 23c0a5c2842..0eea8fbb490 100644 --- a/maps/torch/datums/reports/medical.dm +++ b/maps/torch/datums/reports/medical.dm @@ -44,7 +44,7 @@ add_field(/datum/report_field/simple_text, "Ask about any recent sickness", "NOT CHECKED") add_field(/datum/report_field/pencode_text, "Other Notes") add_field(/datum/report_field/signature, "Doctor's Signature") - set_access(access_edit = access_medical_equip) + set_access(write_access = access_medical_equip) /datum/computer_file/report/recipient/medical/autopsy form_name = "SCG-MED-015" diff --git a/maps/torch/datums/reports/robotics.dm b/maps/torch/datums/reports/robotics.dm index 6a82e0cdce8..615db7c77cf 100644 --- a/maps/torch/datums/reports/robotics.dm +++ b/maps/torch/datums/reports/robotics.dm @@ -17,4 +17,4 @@ xo_fields += add_field(/datum/report_field/signature, "Executive Officer's signature") xo_fields += add_field(/datum/report_field/options/yes_no, "Approved") for(var/datum/report_field/field in xo_fields) - field.set_access(access_edit = access_hop) \ No newline at end of file + field.set_access(write_access = access_hop) \ No newline at end of file diff --git a/maps/torch/datums/reports/security.dm b/maps/torch/datums/reports/security.dm index 6365e334923..416710fdc34 100644 --- a/maps/torch/datums/reports/security.dm +++ b/maps/torch/datums/reports/security.dm @@ -4,8 +4,7 @@ /datum/computer_file/report/recipient/sec/New() ..() - set_access(access_security) - set_access(access_heads, override = 0) + set_access(list(access_security, access_heads)) /datum/computer_file/report/recipient/sec/incident form_name = "SCG-SEC-01" @@ -28,7 +27,7 @@ add_field(/datum/report_field/pencode_text, "Description of Items/Property") add_field(/datum/report_field/pencode_text, "Narrative") add_field(/datum/report_field/signature, "Reporting Officer's signature") - set_access(access_edit = access_security) + set_access(write_access = access_security) /datum/computer_file/report/recipient/sec/investigation form_name = "SCG-SEC-02" @@ -46,7 +45,7 @@ add_field(/datum/report_field/pencode_text, "Summary") add_field(/datum/report_field/pencode_text, "Observations") add_field(/datum/report_field/signature, "Signature") - set_access(access_edit = access_security) + set_access(write_access = access_security) /datum/computer_file/report/recipient/sec/evidence form_name = "SCG-SEC-02b" @@ -61,11 +60,11 @@ add_field(/datum/report_field/time, "Time") add_field(/datum/report_field/people/from_manifest, "Confiscated from") add_field(/datum/report_field/pencode_text, "List of items in custody/evidence lockup") - set_access(access_edit = access_security) + set_access(write_access = access_security) temp_field = add_field(/datum/report_field/signature, "Brig Chief's signature") - temp_field.set_access(access_edit = list(access_security, access_armory)) + temp_field.set_access(write_access = list(access_security, access_armory)) temp_field = add_field(/datum/report_field/signature, "Forensic Technician's signature") - temp_field.set_access(access_edit = list(access_security, access_forensics_lockers)) + temp_field.set_access(write_access = list(access_security, access_forensics_lockers)) /datum/computer_file/report/recipient/sec/statement form_name = "SCG-SEC-02c" @@ -88,7 +87,7 @@ add_field(/datum/report_field/pencode_text, "Narrative") add_field(/datum/report_field/text_label/instruction, "By submitting this form, I understand this is considered a formal police report. I understand that all information written above is truthful and accurate. I understand that intentionally filing a fraudulent police report is a criminal offense that will be prosecuted to the fullest extent of the law. As this is a binding legal document, I understand that by filing this form that any intentionally false information may warrant disciplinary action against myself. This statement was given on my own volition to assist with documenting the above summarized incident.") add_field(/datum/report_field/signature, "Signature") - set_access(access_edit = access_security) + set_access(write_access = access_security) /datum/computer_file/report/recipient/sec/arrest form_name = "SCG-SEC-03" @@ -120,7 +119,7 @@ add_field(/datum/report_field/simple_text, "IF YES, what injuries are pre-existing?") add_field(/datum/report_field/text_label/instruction, "This document MUST be submitted to, and reviwed by, the Chief of Security or Brig Chief.") add_field(/datum/report_field/signature, "Reporting Officer's signature") - set_access(access_edit = access_security) + set_access(write_access = access_security) /datum/computer_file/report/recipient/sec/restraining form_name = "SCG-SEC-04" @@ -137,7 +136,7 @@ add_field(/datum/report_field/time, "Time Effective") add_field(/datum/report_field/text_label/instruction, "THE DEFENDANT IS ORDERED TO: 1) Not to abuse Plaintiff(s) by physically harming them, attempting to physically harm them, place them in fear of imminent physical harm; 2) Stop harassing them by any wilfull and malicious conduct aimed at them and intended to cause fear, intimidation, abuse, or damage to property; 3) Not to contact Plaintiff(s) unless authorized to do so by the CO, XO, COS or their appointee; 4) Remain out of the Plaintiff(s) workplace, 5) Remain no less than 20M away from Plaintiff. Violation of this legal order will result in arrest for Endangerment and any other applicable charges, including any applicable SCUJ violations.") add_field(/datum/report_field/signature, "Submitting Officer's signature") - set_access(access_edit = access_hos) + set_access(write_access = access_hos) /datum/computer_file/report/recipient/sec/ltc form_name = "SCG-SEC-05" @@ -155,4 +154,4 @@ add_field(/datum/report_field/simple_text, "Authorized for Possession Of") add_field(/datum/report_field/text_label/instruction, "THIS LICENSE IS ISSUED 'AT-WILL' AND MAY BE REVOKED AT ANY TIME FOR ANY REASON BY THE COMMANDING OFFICER, EXECUTIVE OFFICER, OR THE CHIEF OF SECURITY. IN THE EVENT OF ILLEGAL CONDUCT, THIS LICENSE MAY BE REVOKED BY ANY LAW ENFORCEMENT OFFICER ACTING IN THE COURSE OF THEIR NORMAL DUTIES. ALL LICENSEES ARE REQUIRED TO ABIDE BY LOCAL LAWS AND REGULATIONS AT ALL TIMES. OPEN CARRY OF LICENSED ITEMS IS GENERALLY NOT PERMITTED UNLESS EXPLICITLY DENOTED. THIS DOCUMENT MUST BE CARRIED BY THE LICENSED PARTY WHEN THEY ARE IN DIRECT OR CONSTRUCTIVE POSSESSION OF THE AFORMENTIONED ITEMS OR WEAPONS THAT THEY ARE AUTHORIZED FOR. COPIES OF THIS DOCUMENT WILL BE FORWARDED TO THE COMMANDING OFFICER, EXECUTIVE OFFICER, CHIEF OF SECURITY, AND BRIG OFFICER FOR REFERENCE.") add_field(/datum/report_field/signature, "Submitting Officer's signature") - set_access(access_edit = access_hos) \ No newline at end of file + set_access(write_access = access_hos) \ No newline at end of file diff --git a/maps/torch/datums/reports/solgov.dm b/maps/torch/datums/reports/solgov.dm index 6150ea8b065..385eb9985dc 100644 --- a/maps/torch/datums/reports/solgov.dm +++ b/maps/torch/datums/reports/solgov.dm @@ -17,8 +17,7 @@ add_field(/datum/report_field/pencode_text, "Other Notes") add_field(/datum/report_field/signature, "Signature") add_field(/datum/report_field/options/yes_no, "Approved") - set_access(access_edit = access_representative, override = 0) - set_access(access_edit = access_nanotrasen, override = 0) + set_access(write_access = write_access | list(list(access_representative, access_nanotrasen))) ..() /datum/computer_file/report/recipient/sol/crewman_incident @@ -34,7 +33,7 @@ add_field(/datum/report_field/pencode_text, "Description of incident") add_field(/datum/report_field/signature, "Signature") add_field(/datum/report_field/options/yes_no, "Approved") - set_access(access_edit = list(access_heads, access_solgov_crew)) + set_access(write_access = list(list(access_heads, access_solgov_crew))) ..() /datum/computer_file/report/recipient/sol/work_visa @@ -51,5 +50,5 @@ temp_field = add_field(/datum/report_field/signature, "Issuer of Work Visa Signature") add_field(/datum/report_field/signature, "Recipient of Work Visa Signature") add_field(/datum/report_field/options/yes_no, "Approved") - temp_field.set_access(access_edit = access_representative) + temp_field.set_access(write_access = access_representative) ..() diff --git a/maps/torch/torch1_deck5.dmm b/maps/torch/torch1_deck5.dmm index 3ac7de3b9dc..c95a64b510a 100644 --- a/maps/torch/torch1_deck5.dmm +++ b/maps/torch/torch1_deck5.dmm @@ -1228,7 +1228,7 @@ /turf/simulated/floor/tiled/dark/monotile, /area/bridge/ai/ai) "db" = ( -/obj/structure/stairs/west, +/obj/structure/stairs/long/west, /obj/structure/railing/mapped, /turf/simulated/floor/tiled/monotile, /area/hallway/primary/fifthdeck/fore) @@ -2528,7 +2528,7 @@ /turf/simulated/floor/tiled/dark/monotile, /area/quartermaster/expedition/storage) "gq" = ( -/obj/structure/stairs/east, +/obj/structure/stairs/long/east, /turf/simulated/floor/tiled/monotile, /area/quartermaster/storage) "gr" = ( @@ -8694,7 +8694,7 @@ dir = 4; pixel_x = -24 }, -/obj/structure/stairs/south, +/obj/structure/stairs/long, /turf/simulated/floor/tiled, /area/hallway/primary/fifthdeck/aft) "wA" = ( @@ -9709,7 +9709,7 @@ /turf/simulated/floor/tiled/monotile, /area/quartermaster/expedition/eva) "Dr" = ( -/obj/machinery/power/port_gen/pacman{ +/obj/machinery/port_gen/pacman{ anchored = 1 }, /obj/effect/floor_decal/industrial/hatch/yellow, @@ -10780,7 +10780,7 @@ /area/crew_quarters/observation) "Ko" = ( /obj/effect/floor_decal/industrial/hatch/yellow, -/obj/machinery/power/port_gen/pacman{ +/obj/machinery/port_gen/pacman{ anchored = 1 }, /obj/structure/cable/yellow{ @@ -12258,7 +12258,7 @@ /turf/simulated/floor/tiled/dark/monotile, /area/bridge/ai/ai) "Vz" = ( -/obj/structure/stairs/south, +/obj/structure/stairs/long, /turf/simulated/floor/tiled, /area/hallway/primary/fifthdeck/aft) "VA" = ( diff --git a/maps/torch/torch2_deck4.dmm b/maps/torch/torch2_deck4.dmm index 79429821c13..9a12ee83c20 100644 --- a/maps/torch/torch2_deck4.dmm +++ b/maps/torch/torch2_deck4.dmm @@ -2783,7 +2783,7 @@ /obj/machinery/light{ dir = 4 }, -/obj/structure/stairs/north, +/obj/structure/stairs/long/north, /turf/simulated/floor/tiled, /area/hallway/primary/fourthdeck/aft) "kZ" = ( @@ -4119,7 +4119,7 @@ /turf/simulated/floor/tiled/monotile, /area/hallway/primary/fourthdeck/fore) "pV" = ( -/obj/structure/stairs/east, +/obj/structure/stairs/long/east, /turf/simulated/floor/tiled/monotile, /area/hallway/primary/fourthdeck/fore) "pW" = ( @@ -8096,7 +8096,7 @@ /turf/simulated/floor/tiled, /area/quartermaster/storage/upper) "Er" = ( -/obj/structure/stairs/north, +/obj/structure/stairs/long/north, /turf/simulated/floor/tiled, /area/hallway/primary/fourthdeck/aft) "Et" = ( @@ -8943,7 +8943,7 @@ /turf/simulated/floor/tiled/monotile, /area/shuttle/escape_pod9/station) "Hv" = ( -/obj/structure/stairs/north, +/obj/structure/stairs/long/north, /obj/structure/railing/mapped{ dir = 4; icon_state = "railing0-1" @@ -13451,7 +13451,7 @@ /turf/simulated/floor/tiled/dark, /area/eva) "Vq" = ( -/obj/structure/stairs/north, +/obj/structure/stairs/long/north, /turf/simulated/floor/wood/walnut, /area/crew_quarters/lounge) "Vr" = ( diff --git a/maps/torch/torch3_deck3.dmm b/maps/torch/torch3_deck3.dmm index d9e87023020..5e4f7391f13 100644 --- a/maps/torch/torch3_deck3.dmm +++ b/maps/torch/torch3_deck3.dmm @@ -5745,7 +5745,7 @@ /turf/simulated/floor/tiled/monotile, /area/hydroponics) "oa" = ( -/obj/structure/stairs/west, +/obj/structure/stairs/long/west, /turf/simulated/floor/tiled, /area/hallway/primary/thirddeck/fore) "ob" = ( @@ -11894,7 +11894,7 @@ /turf/simulated/floor/carpet/purple, /area/chapel/main) "Fm" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/effect/floor_decal/industrial/outline/grey, /turf/simulated/floor/tiled/techfloor, /area/maintenance/thirddeck/aftport) @@ -12050,7 +12050,7 @@ /turf/simulated/floor/tiled/dark, /area/vacant/cabin) "FO" = ( -/obj/machinery/power/rad_collector, +/obj/machinery/rad_collector, /obj/effect/floor_decal/industrial/outline/grey, /turf/simulated/floor/tiled/techfloor, /area/maintenance/thirddeck/aftport) @@ -12063,7 +12063,7 @@ dir = 8 }, /obj/effect/floor_decal/industrial/outline/yellow, -/obj/machinery/power/emitter, +/obj/machinery/emitter, /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 }, @@ -15060,7 +15060,7 @@ /turf/simulated/floor/tiled/white, /area/crew_quarters/galley) "Pr" = ( -/obj/structure/stairs/south, +/obj/structure/stairs/long, /turf/simulated/floor/tiled, /area/hallway/primary/thirddeck/aft) "Ps" = ( @@ -15739,7 +15739,7 @@ dir = 4; pixel_x = -23 }, -/obj/structure/stairs/south, +/obj/structure/stairs/long, /obj/effect/floor_decal/corner/green{ dir = 9 }, @@ -16851,7 +16851,7 @@ /turf/simulated/floor/tiled/dark, /area/engineering/hardstorage) "Ve" = ( -/obj/machinery/power/port_gen/pacman{ +/obj/machinery/port_gen/pacman{ sheets = 25 }, /obj/effect/floor_decal/corner/yellow/half{ @@ -17174,7 +17174,7 @@ /turf/simulated/floor/tiled/steel_grid, /area/hallway/primary/thirddeck/center) "VY" = ( -/obj/structure/stairs/west, +/obj/structure/stairs/long/west, /obj/effect/floor_decal/corner/yellow/half{ dir = 8 }, diff --git a/maps/torch/torch4_deck2.dmm b/maps/torch/torch4_deck2.dmm index f2af4a382e7..7b557e1fa73 100644 --- a/maps/torch/torch4_deck2.dmm +++ b/maps/torch/torch4_deck2.dmm @@ -602,7 +602,7 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/seconddeck/aftstarboard) "bw" = ( -/obj/machinery/power/rad_collector, +/obj/machinery/rad_collector, /turf/simulated/floor/plating, /area/maintenance/seconddeck/central) "bx" = ( @@ -1868,7 +1868,7 @@ /turf/simulated/floor/reinforced/airless, /area/engineering/fuelbay/aux) "eh" = ( -/obj/machinery/power/fusion_core/mapped{ +/obj/machinery/fusion_core/mapped{ initial_id_tag = "aux_fusion_plant" }, /obj/structure/cable/yellow{ @@ -1984,7 +1984,7 @@ /obj/structure/cable/green{ icon_state = "0-4" }, -/obj/machinery/power/emitter/gyrotron/anchored{ +/obj/machinery/emitter/gyrotron/anchored{ dir = 8; initial_id_tag = "aux_fusion_plant" }, @@ -2012,7 +2012,7 @@ /obj/structure/cable/yellow{ icon_state = "0-2" }, -/obj/machinery/power/port_gen/pacman/mrs{ +/obj/machinery/port_gen/pacman/mrs{ anchored = 1 }, /turf/simulated/floor/reinforced/airless, @@ -3908,7 +3908,7 @@ /obj/structure/cable/yellow{ icon_state = "0-8" }, -/obj/machinery/power/generator{ +/obj/machinery/generator{ anchored = 1 }, /turf/simulated/floor/tiled/techfloor/grid, @@ -4161,7 +4161,7 @@ /turf/simulated/floor/plating, /area/engineering/auxpowergen) "jU" = ( -/obj/structure/stairs/north, +/obj/structure/stairs/long/north, /turf/simulated/floor/tiled, /area/engineering/foyer) "jV" = ( @@ -4684,7 +4684,7 @@ /obj/structure/extinguisher_cabinet{ pixel_x = 32 }, -/obj/structure/stairs/north, +/obj/structure/stairs/long/north, /turf/simulated/floor/tiled, /area/engineering/foyer) "ln" = ( @@ -4926,7 +4926,7 @@ /obj/structure/cable/yellow{ icon_state = "0-4" }, -/obj/machinery/power/generator{ +/obj/machinery/generator{ anchored = 1 }, /turf/simulated/floor/tiled/techfloor/grid, @@ -5336,7 +5336,7 @@ /turf/simulated/floor/tiled, /area/hallway/primary/seconddeck/center) "mZ" = ( -/obj/structure/stairs/east, +/obj/structure/stairs/long/east, /turf/simulated/floor/tiled, /area/hallway/primary/seconddeck/center) "na" = ( @@ -6701,7 +6701,7 @@ /turf/simulated/wall/r_wall/map_preset/tan, /area/vacant/prototype/engine) "qy" = ( -/obj/machinery/power/shield_generator, +/obj/machinery/shield_generator, /obj/structure/cable{ icon_state = "0-2" }, @@ -11737,7 +11737,7 @@ /area/engineering/wastetank) "El" = ( /obj/effect/floor_decal/industrial/hatch/yellow, -/obj/machinery/power/emitter{ +/obj/machinery/emitter{ anchored = 1; dir = 4; id_tag = "EngineEmitter"; @@ -13058,7 +13058,7 @@ /turf/simulated/floor/plating, /area/maintenance/seconddeck/aftstarboard) "HV" = ( -/obj/machinery/power/generator{ +/obj/machinery/generator{ anchored = 1; dir = 4 }, diff --git a/maps/torch/torch5_deck1.dmm b/maps/torch/torch5_deck1.dmm index c60a3003492..5f05d072b62 100644 --- a/maps/torch/torch5_deck1.dmm +++ b/maps/torch/torch5_deck1.dmm @@ -3731,7 +3731,7 @@ /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/forestarboard) "aiZ" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/effect/floor_decal/industrial/outline/grey, /turf/simulated/floor/tiled/techfloor, /area/maintenance/firstdeck/forestarboard) @@ -5339,7 +5339,7 @@ /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/fore) "atC" = ( -/obj/structure/stairs/west, +/obj/structure/stairs/long/west, /obj/structure/railing/mapped, /turf/simulated/floor/tiled, /area/hallway/primary/firstdeck/fore) @@ -7920,7 +7920,7 @@ /obj/structure/cable/green{ icon_state = "4-8" }, -/obj/machinery/power/emitter/anchored, +/obj/machinery/emitter/anchored, /obj/structure/cable/green{ icon_state = "0-4" }, @@ -13933,7 +13933,7 @@ /turf/simulated/floor/tiled/white/monotile, /area/medical/exam_room) "gab" = ( -/obj/machinery/power/port_gen/pacman{ +/obj/machinery/port_gen/pacman{ anchored = 1; sheets = 25 }, @@ -24099,7 +24099,7 @@ /turf/simulated/floor/tiled/monotile, /area/security/brig) "tTS" = ( -/obj/machinery/power/port_gen/pacman{ +/obj/machinery/port_gen/pacman{ anchored = 1 }, /obj/effect/floor_decal/industrial/outline/grey, @@ -25998,7 +25998,7 @@ /area/security/wing) "ylp" = ( /obj/effect/floor_decal/industrial/outline/grey, -/obj/machinery/power/port_gen/pacman{ +/obj/machinery/port_gen/pacman{ anchored = 1 }, /turf/simulated/floor/tiled/techfloor/grid, diff --git a/maps/torch/torch6_bridge.dmm b/maps/torch/torch6_bridge.dmm index 547db40ff48..87f3d0a24d3 100644 --- a/maps/torch/torch6_bridge.dmm +++ b/maps/torch/torch6_bridge.dmm @@ -799,7 +799,7 @@ /turf/simulated/floor/tiled/dark, /area/bridge/hallway/starboard) "cb" = ( -/obj/machinery/power/port_gen/pacman, +/obj/machinery/port_gen/pacman, /obj/effect/floor_decal/industrial/outline/grey, /turf/simulated/floor/tiled/techfloor, /area/maintenance/bridge/aftstarboard) @@ -6218,7 +6218,7 @@ /turf/simulated/floor/reinforced/airless, /area/space) "sp" = ( -/obj/machinery/power/port_gen/pacman{ +/obj/machinery/port_gen/pacman{ anchored = 1; sheets = 25 }, diff --git a/maps/torch/torch_define.dm b/maps/torch/torch_define.dm index 88eddfe77c6..6380b67a4bf 100644 --- a/maps/torch/torch_define.dm +++ b/maps/torch/torch_define.dm @@ -9,7 +9,8 @@ admin_levels = list(7) accessible_z_levels = list("1"=1,"2"=3,"3"=1,"4"=1,"5"=1,"6"=1,"9"=30) overmap_ids = list(OVERMAP_ID_SPACE = /datum/overmap/torch) - usable_email_tlds = list("endeavour.issc.iseo", "endeavour.espatier.mil", "freemail.net") + // TODO: REIMPLEMENT + //usable_email_tlds = list("endeavour.issc.iseo", "endeavour.espatier.mil", "freemail.net") allowed_spawns = list(/decl/spawnpoint/cryo) default_spawn = /decl/spawnpoint/cryo diff --git a/maps/torch/torch_ranks.dm b/maps/torch/torch_ranks.dm index a919b6eb603..cf38c3613fa 100644 --- a/maps/torch/torch_ranks.dm +++ b/maps/torch/torch_ranks.dm @@ -1,3 +1,6 @@ +// TODO: +// - Find some way to reimplement branch-specific email TLDs + /datum/job/submap branch = /datum/mil_branch/civilian rank = /datum/mil_rank/civ/civ @@ -133,7 +136,7 @@ /datum/mil_branch/iseo_issc name = "International Stellar Surveyor Corps" name_short = "ISSC" - email_domain = "issc.iseo" + // email_domain = "issc.iseo" rank_types = list( /datum/mil_rank/sc/t1, @@ -168,7 +171,7 @@ /datum/mil_branch/espatier_corps name = "Espatier Corps" name_short = "EC" - email_domain = "espatier.mil" + // email_domain = "espatier.mil" rank_types = list( /datum/mil_rank/espatier/e1, @@ -222,7 +225,7 @@ /datum/mil_branch/civilian name = "Civilian" name_short = "Civ" - email_domain = "freemail.net" + // email_domain = "freemail.net" // allow_custom_email = TRUE rank_types = list( @@ -240,7 +243,7 @@ /datum/mil_branch/government name = "ISEO Employee" name_short = "ISEO" - email_domain = "gov.iseo" + // email_domain = "gov.iseo" rank_types = list( /datum/mil_rank/government/gov, diff --git a/maps/torch/z1_admin.dmm b/maps/torch/z1_admin.dmm index 706842fcd4b..aea0a7d4324 100644 --- a/maps/torch/z1_admin.dmm +++ b/maps/torch/z1_admin.dmm @@ -3575,7 +3575,7 @@ }, /area/space) "axp" = ( -/obj/machinery/power/emitter, +/obj/machinery/emitter, /turf/unsimulated/floor{ dir = 1; icon_state = "vault" diff --git a/maps/tradeship/tradeship-0.dmm b/maps/tradeship/tradeship-0.dmm index 6319feb5e95..2ba3c59e2e1 100644 --- a/maps/tradeship/tradeship-0.dmm +++ b/maps/tradeship/tradeship-0.dmm @@ -1744,7 +1744,7 @@ /turf/simulated/floor/wood/walnut, /area/ship/trade/disused) "II" = ( -/obj/structure/stairs/west, +/obj/structure/stairs/long/west, /turf/simulated/floor/tiled/steel_grid, /area/ship/trade/loading_bay) "IQ" = ( diff --git a/maps/tradeship/tradeship-1.dmm b/maps/tradeship/tradeship-1.dmm index da4b13a7af9..5429efb6c46 100644 --- a/maps/tradeship/tradeship-1.dmm +++ b/maps/tradeship/tradeship-1.dmm @@ -1115,7 +1115,7 @@ /turf/simulated/floor/tiled/steel_grid, /area/ship/trade/cargo/lower) "dh" = ( -/obj/structure/stairs/east, +/obj/structure/stairs/long/east, /obj/effect/floor_decal/industrial/warning{ dir = 1; icon_state = "warning" diff --git a/maps/tradeship/tradeship-2.dmm b/maps/tradeship/tradeship-2.dmm index 1451ff10f0a..c23412d9b43 100644 --- a/maps/tradeship/tradeship-2.dmm +++ b/maps/tradeship/tradeship-2.dmm @@ -1056,7 +1056,7 @@ /area/ship/trade/shuttle/outgoing) "cI" = ( /obj/structure/cable, -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /turf/simulated/floor/plating, /area/ship/trade/shuttle/outgoing) "cL" = ( @@ -3528,7 +3528,7 @@ /turf/simulated/floor/tiled/techfloor/grid, /area/ship/trade/maintenance/power) "iU" = ( -/obj/machinery/power/shield_generator, +/obj/machinery/shield_generator, /obj/structure/cable{ icon_state = "0-2"; pixel_y = 1 @@ -4035,7 +4035,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/fusion_core/mapped{ +/obj/machinery/fusion_core/mapped{ initial_id_tag = "main_drive" }, /turf/simulated/floor/reinforced/airless, @@ -4059,7 +4059,7 @@ "kc" = ( /obj/structure/cable, /obj/machinery/light, -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /obj/item/wrench, /turf/simulated/floor/tiled/techfloor/grid, /area/ship/trade/maintenance/power) @@ -5889,7 +5889,7 @@ /obj/structure/cable{ icon_state = "0-4" }, -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /turf/simulated/floor/plating, /area/ship/trade/shieldbay) "IM" = ( @@ -6140,7 +6140,7 @@ /turf/simulated/floor/plating, /area/ship/trade/crew/hallway/starboard) "Lm" = ( -/obj/machinery/power/shield_generator, +/obj/machinery/shield_generator, /obj/structure/cable{ icon_state = "0-8" }, @@ -6366,12 +6366,13 @@ /obj/machinery/meter, /obj/machinery/atmospherics/pipe/manifold/visible/fuel, /obj/structure/window/borosilicate_reinforced, -/obj/machinery/power/emitter/gyrotron/anchored{ +/obj/machinery/emitter/gyrotron/anchored{ initial_id_tag = "main_drive" }, /obj/structure/cable{ icon_state = "0-4" }, +/obj/machinery/power/terminal, /turf/simulated/floor/plating, /area/ship/trade/maintenance/engine/aft) "OP" = ( diff --git a/maps/tradeship/tradeship_jobs.dm b/maps/tradeship/tradeship_jobs.dm index bbe5be6b118..b40527159e8 100644 --- a/maps/tradeship/tradeship_jobs.dm +++ b/maps/tradeship/tradeship_jobs.dm @@ -19,6 +19,8 @@ /obj/machinery/suit_cycler/tradeship boots = /obj/item/clothing/shoes/magboots req_access = list() + initial_access = list() + locked = FALSE /obj/machinery/suit_cycler/tradeship/Initialize() if(prob(75)) diff --git a/maps/tradeship/tradeship_overrides.dm b/maps/tradeship/tradeship_overrides.dm index c1fc00b7dbd..78171f334ad 100644 --- a/maps/tradeship/tradeship_overrides.dm +++ b/maps/tradeship/tradeship_overrides.dm @@ -1,5 +1,5 @@ /datum/computer_file/program/merchant/tradeship - required_access = list() + read_access = list() /obj/machinery/computer/modular/preset/merchant/tradeship default_software = list( diff --git a/maps/~mapsystem/maps.dm b/maps/~mapsystem/maps.dm index 072afd0579b..b3b4399bce5 100644 --- a/maps/~mapsystem/maps.dm +++ b/maps/~mapsystem/maps.dm @@ -32,7 +32,6 @@ var/global/const/MAP_HAS_RANK = 2 //Rank system, also togglable var/list/map_levels // Z-levels available to various consoles, such as the crew monitor. Defaults to station_levels if unset. var/list/base_turf_by_z = list() // Custom base turf by Z-level. Defaults to world.turf for unlisted Z-levels - var/list/usable_email_tlds = list("freemail.net") var/base_floor_type = /turf/simulated/floor/airless // The turf type used when generating floors between Z-levels at startup. var/base_floor_area // Replacement area, if a base_floor_type is generated. Leave blank to skip. 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/content/bigpharma/extension.dm b/mods/content/bigpharma/extension.dm index eba9e5fa382..87239c5aeb6 100644 --- a/mods/content/bigpharma/extension.dm +++ b/mods/content/bigpharma/extension.dm @@ -12,7 +12,7 @@ return /datum/extension/obfuscated_medication/proc/get_original_reagent(var/obj/item/donor) - return donor?.reagents?.get_primary_reagent_name() + return donor?.reagents?.get_primary_reagent_name(codex = TRUE) /datum/extension/obfuscated_medication/bottle container_name = "bottle" @@ -41,7 +41,7 @@ /datum/extension/obfuscated_medication/pill_bottle/get_original_reagent(var/obj/item/donor) for(var/obj/item/chems/pill/pill in donor?.contents) if(pill.reagents?.total_volume) - return pill.reagents.get_primary_reagent_name() + return pill.reagents.get_primary_reagent_name(codex = TRUE) /datum/extension/obfuscated_medication/pill_bottle/update_appearance() var/obj/item/storage/pill_bottle/bottle = holder @@ -55,7 +55,7 @@ /datum/extension/obfuscated_medication/foil_pack/get_original_reagent(var/obj/item/donor) for(var/obj/item/chems/pill/pill in donor?.contents) if(pill.reagents?.total_volume) - return pill.reagents.get_primary_reagent_name() + return pill.reagents.get_primary_reagent_name(codex = TRUE) /datum/extension/obfuscated_medication/foil_pack/update_appearance() var/obj/item/storage/pill_bottle/foil_pack/foil_pack = holder diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm b/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm index 83007caa482..9b21eb5e9bb 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria-1.dmm @@ -2576,7 +2576,7 @@ /turf/simulated/floor/plating, /area/lar_maria/sec_wing) "gJ" = ( -/obj/machinery/power/port_gen/pacman/mrs, +/obj/machinery/port_gen/pacman/mrs, /obj/structure/cable/yellow{ icon_state = "0-8" }, @@ -2721,7 +2721,7 @@ /turf/simulated/floor/plating, /area/lar_maria/cells) "hd" = ( -/obj/structure/stairs/south, +/obj/structure/stairs/long, /turf/simulated/floor/tiled, /area/lar_maria/vir_access) "he" = ( diff --git a/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm b/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm index 414092e0fa7..f91c098201e 100644 --- a/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm +++ b/mods/content/corporate/away_sites/lar_maria/lar_maria-2.dmm @@ -108,7 +108,7 @@ /turf/simulated/floor/reinforced, /area/space) "au" = ( -/obj/machinery/power/port_gen/pacman/super, +/obj/machinery/port_gen/pacman/super, /turf/simulated/floor/plating, /area/lar_maria/solar_control) "av" = ( diff --git a/mods/content/hearth_culture/humanity.dm b/mods/content/hearth_culture/humanity.dm index 66f6dbd3b1e..8478d55ee6a 100644 --- a/mods/content/hearth_culture/humanity.dm +++ b/mods/content/hearth_culture/humanity.dm @@ -65,7 +65,7 @@ //FACTIONS START /decl/cultural_info/faction/humanity - name = "Other Faction" + name = "Other Human Faction" description = "You belong to one of the many other factions that operate in the galaxy. Numerous, too numerous to list, these factions represent a variety of interests, purposes, intents and goals." subversive_potential = 25 diff --git a/mods/content/hearthdrinks/cocktails.dm b/mods/content/hearthdrinks/cocktails.dm index 5e02a6de700..40cb31c3a38 100644 --- a/mods/content/hearthdrinks/cocktails.dm +++ b/mods/content/hearthdrinks/cocktails.dm @@ -107,7 +107,7 @@ description = "A blend of coffee and kahula." ratios = list( /decl/material/liquid/drink/coffee = 0.4, - /decl/material/liquid/ethanol/coffee/kahlua = 0.2 + /decl/material/liquid/ethanol/coffee = 0.2 ) /decl/cocktail/qiiboxi diff --git a/mods/content/hearthfoods/food/fried.dm b/mods/content/hearthfoods/food/fried.dm index 0e67129ffbd..ff0d3d302da 100644 --- a/mods/content/hearthfoods/food/fried.dm +++ b/mods/content/hearthfoods/food/fried.dm @@ -171,7 +171,7 @@ batter_coating = /decl/material/liquid/nutriment/batter /obj/item/chems/food/pizzacrunchslice - name = "pizza crunch" + name = "pizza crunch slice" desc = "A little piece of a heart attack. Its toppings are a mystery, hidden under batter." icon = 'mods/content/hearthfoods/icons/obj/food.dmi' icon_state = "pizzacrunchslice" diff --git a/mods/content/hearthfoods/recipes/recipes_baked.dm b/mods/content/hearthfoods/recipes/recipes_baked.dm index ec98c273b0d..73ae5312d45 100644 --- a/mods/content/hearthfoods/recipes/recipes_baked.dm +++ b/mods/content/hearthfoods/recipes/recipes_baked.dm @@ -1,4 +1,5 @@ /decl/recipe/brownies + display_name = "chocolate brownies" appliance = APPLIANCE_OVEN reagents = list(/decl/material/liquid/nutriment/browniemix = 10, /decl/material/liquid/nutriment/protein/egg = 3) reagent_mix = REAGENT_REPLACE //No egg or mix in final recipe @@ -35,6 +36,7 @@ reagent_mix = REAGENT_REPLACE //No raw egg in finished product, protein after cooking causes magic meatballs otherwise /decl/recipe/muffin/berry + display_name = null // autoset fruit = list("berries" = 1) result = /obj/item/chems/food/berrymuffin diff --git a/mods/content/hearthfoods/recipes/recipes_fried.dm b/mods/content/hearthfoods/recipes/recipes_fried.dm index 9e5ed4a77f0..b686d5ecf0f 100644 --- a/mods/content/hearthfoods/recipes/recipes_fried.dm +++ b/mods/content/hearthfoods/recipes/recipes_fried.dm @@ -48,6 +48,7 @@ coating = /decl/material/liquid/nutriment/batter/beerbatter /decl/recipe/pizzacrunch_1 + display_name = "premade pizza crunch" appliance = APPLIANCE_FRYER items = list( /obj/item/chems/food/sliceable/pizza = 1 @@ -57,6 +58,7 @@ //Alternate pizza crunch recipe for combination pizzas made in oven /decl/recipe/pizzacrunch_2 + display_name = "custom pizza crunch" appliance = APPLIANCE_FRYER items = list( /obj/item/chems/food/variable/pizza = 1 diff --git a/mods/content/hearthfoods/recipes/recipes_meat.dm b/mods/content/hearthfoods/recipes/recipes_meat.dm index f937a2a3c7d..8a65682a36f 100644 --- a/mods/content/hearthfoods/recipes/recipes_meat.dm +++ b/mods/content/hearthfoods/recipes/recipes_meat.dm @@ -1,5 +1,6 @@ //Bacon /decl/recipe/bacon + display_name = "deep-fried bacon" appliance = APPLIANCE_FRYER items = list( /obj/item/chems/food/rawbacon = 1 diff --git a/mods/content/hearthfoods/recipes/recipes_tortillas_chips.dm b/mods/content/hearthfoods/recipes/recipes_tortillas_chips.dm index 0ce6c4bcee5..87857baf528 100644 --- a/mods/content/hearthfoods/recipes/recipes_tortillas_chips.dm +++ b/mods/content/hearthfoods/recipes/recipes_tortillas_chips.dm @@ -62,6 +62,7 @@ result = /obj/item/chems/food/enchiladas /decl/recipe/burrito + display_name = "plain meat burrito" appliance = APPLIANCE_MIX|APPLIANCE_MICROWAVE items = list( /obj/item/chems/food/tortilla = 1, diff --git a/mods/content/psionics/datum/codex.dm b/mods/content/psionics/datum/codex.dm index fc52f35a6a5..cac5a264042 100644 --- a/mods/content/psionics/datum/codex.dm +++ b/mods/content/psionics/datum/codex.dm @@ -1,6 +1,5 @@ /datum/codex_entry/cuchulain_foundation name = "Cuchulain Foundation" - associated_strings = list("Cuchulain", "Foundation") associated_paths = list( /obj/item/storage/briefcase/foundation, /obj/item/gun/projectile/revolver/foundation, @@ -25,7 +24,7 @@ /datum/codex_entry/psionics name = "Psionics" - associated_strings = list("Psychic", "Psychic Powers", "Psi") + associated_strings = list("psychic powers") associated_paths = list( /obj/item/book/manual/psionics, /obj/item/clothing/head/helmet/space/psi_amp, diff --git a/mods/content/psionics/system/psionics/complexus/complexus_latency.dm b/mods/content/psionics/system/psionics/complexus/complexus_latency.dm index 482c3c2d05d..373c2283129 100644 --- a/mods/content/psionics/system/psionics/complexus/complexus_latency.dm +++ b/mods/content/psionics/system/psionics/complexus/complexus_latency.dm @@ -8,6 +8,7 @@ return FALSE var/faculty = pick(latencies) + LAZYREMOVE(latencies, faculty) var/new_rank = rand(2,5) owner.set_psi_rank(faculty, new_rank) var/decl/psionic_faculty/faculty_decl = SSpsi.get_faculty(faculty) diff --git a/mods/content/psionics/system/psionics/faculties/_power.dm b/mods/content/psionics/system/psionics/faculties/_power.dm index b47924df28c..8dc77d3e1be 100644 --- a/mods/content/psionics/system/psionics/faculties/_power.dm +++ b/mods/content/psionics/system/psionics/faculties/_power.dm @@ -1,4 +1,6 @@ /decl/psionic_power + abstract_type = /decl/psionic_power + var/name // Name. If null, psipower won't be generated on roundstart. var/faculty // Associated psi faculty. var/min_rank // Minimum psi rank to use this power. @@ -15,6 +17,9 @@ /decl/psionic_power/proc/invoke(var/mob/living/user, var/atom/target) + if(is_abstract()) + return FALSE + if(!user.psi) return FALSE @@ -38,6 +43,8 @@ return TRUE /decl/psionic_power/proc/handle_post_power(var/mob/living/user, var/atom/target) + if(is_abstract()) + return if(cooldown) user.psi.set_cooldown(cooldown) if(admin_log && ismob(user) && ismob(target)) diff --git a/mods/content/psionics/system/psionics/faculties/coercion.dm b/mods/content/psionics/system/psionics/faculties/coercion.dm index f509fb29631..e3abb4b55f4 100644 --- a/mods/content/psionics/system/psionics/faculties/coercion.dm +++ b/mods/content/psionics/system/psionics/faculties/coercion.dm @@ -6,6 +6,7 @@ /decl/psionic_power/coercion faculty = PSI_COERCION + abstract_type = /decl/psionic_power/coercion /decl/psionic_power/coercion/invoke(var/mob/living/user, var/mob/living/target) if (!istype(target)) diff --git a/mods/content/psionics/system/psionics/faculties/energistics.dm b/mods/content/psionics/system/psionics/faculties/energistics.dm index 85aad9dba21..299e1faa7b8 100644 --- a/mods/content/psionics/system/psionics/faculties/energistics.dm +++ b/mods/content/psionics/system/psionics/faculties/energistics.dm @@ -6,6 +6,7 @@ /decl/psionic_power/energistics faculty = PSI_ENERGISTICS + abstract_type = /decl/psionic_power/energistics /decl/psionic_power/energistics/disrupt name = "Disrupt" @@ -88,7 +89,7 @@ pew.current = target pew.starting = get_turf(user) pew.shot_from = user - pew.launch(target, user.zone_sel.selecting, (target.x-user.x), (target.y-user.y)) + pew.launch(target, user.zone_sel.selecting, user) return TRUE /decl/psionic_power/energistics/spark diff --git a/mods/content/psionics/system/psionics/faculties/psychokinesis.dm b/mods/content/psionics/system/psionics/faculties/psychokinesis.dm index 500282f4bcb..f5627a91772 100644 --- a/mods/content/psionics/system/psionics/faculties/psychokinesis.dm +++ b/mods/content/psionics/system/psionics/faculties/psychokinesis.dm @@ -8,6 +8,7 @@ faculty = PSI_PSYCHOKINESIS use_manifest = TRUE use_sound = null + abstract_type = /decl/psionic_power/psychokinesis /decl/psionic_power/psychokinesis/psiblade name = "Psiblade" diff --git a/mods/content/psionics/system/psionics/faculties/redaction.dm b/mods/content/psionics/system/psionics/faculties/redaction.dm index b1ea64128f2..86dbac472d8 100644 --- a/mods/content/psionics/system/psionics/faculties/redaction.dm +++ b/mods/content/psionics/system/psionics/faculties/redaction.dm @@ -7,6 +7,7 @@ /decl/psionic_power/redaction faculty = PSI_REDACTION admin_log = FALSE + abstract_type = /decl/psionic_power/redaction /decl/psionic_power/redaction/proc/check_dead(var/mob/living/target) if(!istype(target)) diff --git a/mods/content/psionics/system/subsystem_psi.dm b/mods/content/psionics/system/subsystem_psi.dm index 9e66f7861f1..8277a72b071 100644 --- a/mods/content/psionics/system/subsystem_psi.dm +++ b/mods/content/psionics/system/subsystem_psi.dm @@ -30,7 +30,7 @@ PROCESSING_SUBSYSTEM_DEF(psi) var/list/powers = decls_repository.get_decls_of_subtype(/decl/psionic_power) for(var/ptype in powers) var/decl/psionic_power/power = powers[ptype] - if(power.faculty) + if(!power.is_abstract() && power.faculty) var/decl/psionic_faculty/faculty = get_faculty(power.faculty) if(faculty) faculty.powers |= power diff --git a/mods/content/xenobiology/food.dm b/mods/content/xenobiology/food.dm index d3c0aec1433..0d854b6df7b 100644 --- a/mods/content/xenobiology/food.dm +++ b/mods/content/xenobiology/food.dm @@ -1,4 +1,5 @@ /decl/recipe/slimetoast + display_name = "Slime Toast" appliance = APPLIANCE_MIX | APPLIANCE_SKILLET reagents = list(/decl/material/liquid/slimejelly = 5) items = list( @@ -7,6 +8,7 @@ result = /obj/item/chems/food/jelliedtoast/slime /decl/recipe/jellydonut/slime + display_name = "Slime Jelly Donut" appliance = APPLIANCE_FRYER reagents = list(/decl/material/liquid/slimejelly = 5, /decl/material/liquid/nutriment/sugar = 5) items = list( @@ -15,6 +17,7 @@ result = /obj/item/chems/food/donut/slimejelly /decl/recipe/slimeburger + display_name = "Slime Burger" appliance = APPLIANCE_MIX reagents = list(/decl/material/liquid/slimejelly = 5) items = list( @@ -23,6 +26,7 @@ result = /obj/item/chems/food/jellyburger/slime /decl/recipe/slimesandwich + display_name = "Slime Sandwich" appliance = APPLIANCE_MIX reagents = list(/decl/material/liquid/slimejelly = 5) items = list( @@ -48,7 +52,6 @@ .=..() reagents.add_reagent(/decl/material/liquid/slimejelly, 5) - /obj/item/chems/food/slimesoup name = "slime soup" desc = "If no water is available, you may substitute tears." @@ -70,16 +73,11 @@ center_of_mass = @"{'x':16,'y':11}" nutriment_amt = 3 bitesize = 5 + donut_state = "jdonut" /obj/item/chems/food/donut/slimejelly/Initialize() .=..() - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 1) reagents.add_reagent(/decl/material/liquid/slimejelly, 5) - if(prob(30)) - src.icon_state = "jdonut2" - src.overlay_state = "box-donut2" - src.SetName("frosted jelly donut") - reagents.add_reagent(/decl/material/liquid/nutriment/sprinkles, 2) /obj/item/chems/food/mysterysoup/get_random_fillings() . = ..() + list(list( diff --git a/mods/content/xenobiology/slime/items.dm b/mods/content/xenobiology/slime/items.dm index 441b4ccd859..4944cd6c14d 100644 --- a/mods/content/xenobiology/slime/items.dm +++ b/mods/content/xenobiology/slime/items.dm @@ -1,5 +1,5 @@ /obj/item/slime_extract - name = "slime extract" + name = "slime core extract" desc = "Goo extracted from a slime. Legends claim these to have \"magical powers\"." icon = 'mods/content/xenobiology/icons/slimes/slime_extract.dmi' icon_state = ICON_STATE_WORLD diff --git a/mods/content/xenobiology/slime/slime_codex.dm b/mods/content/xenobiology/slime/slime_codex.dm index fec8638170a..ca16e9f8c58 100644 --- a/mods/content/xenobiology/slime/slime_codex.dm +++ b/mods/content/xenobiology/slime/slime_codex.dm @@ -42,6 +42,6 @@ var/list/reagent_strings = list() for(var/reagent_id in slime_data.reaction_strings) var/decl/material/mat = GET_DECL(reagent_id) - reagent_strings += "[capitalize(mat.name)]- [slime_data.reaction_strings[reagent_id]]" + reagent_strings += "[capitalize(mat.name)]- [slime_data.reaction_strings[reagent_id]]" extra_mechanics_text += "[length(reagent_strings) ? jointext(reagent_strings, "
    ") : "None."]" mechanics_text = "[mechanics_text]
    [jointext(extra_mechanics_text, "")]" diff --git a/mods/species/adherent/datum/culture.dm b/mods/species/adherent/datum/culture.dm index c3643adbb09..e963eb5131b 100644 --- a/mods/species/adherent/datum/culture.dm +++ b/mods/species/adherent/datum/culture.dm @@ -12,7 +12,7 @@ ) /decl/cultural_info/culture/adherent/get_random_name(gender) - return "[uppertext("[pick(global.full_alphabet)][pick(global.full_alphabet)]-[pick(global.full_alphabet)] [rand(1000,9999)]")]" + return "[uppertext("[pick(global.alphabet)][pick(global.alphabet)]-[pick(global.alphabet)] [rand(1000,9999)]")]" /decl/cultural_info/culture/adherent/sanitize_cultural_name(name) return sanitize_name(name, allow_numbers = TRUE) diff --git a/mods/species/adherent/datum/species.dm b/mods/species/adherent/datum/species.dm index 18a38fccb17..c959f9ed080 100644 --- a/mods/species/adherent/datum/species.dm +++ b/mods/species/adherent/datum/species.dm @@ -59,7 +59,7 @@ heat_level_2 = SYNTH_HEAT_LEVEL_2 heat_level_3 = SYNTH_HEAT_LEVEL_3 - species_flags = SPECIES_FLAG_NO_SCAN | SPECIES_FLAG_NO_PAIN | SPECIES_FLAG_NO_POISON | SPECIES_FLAG_NO_MINOR_CUT + species_flags = SPECIES_FLAG_NO_SCAN | SPECIES_FLAG_NO_PAIN | SPECIES_FLAG_NO_POISON | SPECIES_FLAG_NO_MINOR_CUT | SPECIES_FLAG_CRYSTALLINE spawn_flags = SPECIES_CAN_JOIN appearance_flags = HAS_EYE_COLOR @@ -106,11 +106,6 @@ BP_COOLING_FINS = /obj/item/organ/internal/powered/cooling_fins ) - vital_organs = list( - BP_BRAIN = list("path" = /obj/item/organ/internal/brain/adherent), - BP_GROIN = list("path" = /obj/item/organ/external/groin/crystal), - BP_CHEST = list("path" = /obj/item/organ/external/chest/crystal), - ) move_trail = /obj/effect/decal/cleanable/blood/tracks/snake max_players = 3 diff --git a/mods/species/adherent/organs/organs_external.dm b/mods/species/adherent/organs/organs_external.dm index c5d67893880..17d3e78e627 100644 --- a/mods/species/adherent/organs/organs_external.dm +++ b/mods/species/adherent/organs/organs_external.dm @@ -7,7 +7,6 @@ body_part = SLOT_LOWER_BODY organ_tag = BP_CHEST parent_organ = null - dislocated = -1 max_damage = 50 min_broken_damage = 25 arterial_bleed_severity = 0 @@ -23,7 +22,6 @@ name = "trailing tendrils" joint = "base" arterial_bleed_severity = 0 - dislocated = -1 max_damage = 50 min_broken_damage = 25 encased = "ceramic hull" @@ -40,7 +38,6 @@ joint = "connector socket" glowing_eyes = TRUE arterial_bleed_severity = 0 - dislocated = -1 max_damage = 50 min_broken_damage = 25 cavity_max_w_class = ITEM_SIZE_NORMAL // Apparently their brains change w_class to this. @@ -57,7 +54,6 @@ amputation_point = "midpoint" joint = "base" arterial_bleed_severity = 0 - dislocated = -1 max_damage = 20 min_broken_damage = 10 status = ORGAN_PROSTHETIC @@ -71,7 +67,6 @@ amputation_point = "midpoint" joint = "base" arterial_bleed_severity = 0 - dislocated = -1 max_damage = 20 min_broken_damage = 10 icon = 'mods/species/adherent/icons/body_turquoise.dmi' @@ -86,7 +81,6 @@ amputation_point = "midpoint" joint = "base" arterial_bleed_severity = 0 - dislocated = -1 max_damage = 20 min_broken_damage = 10 icon = 'mods/species/adherent/icons/body_turquoise.dmi' @@ -101,7 +95,6 @@ amputation_point = "midpoint" joint = "base" arterial_bleed_severity = 0 - dislocated = -1 max_damage = 20 min_broken_damage = 10 icon = 'mods/species/adherent/icons/body_turquoise.dmi' @@ -119,7 +112,6 @@ organ_tag = BP_L_LEG parent_organ = BP_CHEST arterial_bleed_severity = 0 - dislocated = -1 max_damage = 20 min_broken_damage = 10 icon = 'mods/species/adherent/icons/body_turquoise.dmi' diff --git a/mods/species/adherent/organs/organs_internal.dm b/mods/species/adherent/organs/organs_internal.dm index 55eef5f2201..e2a4c73ced2 100644 --- a/mods/species/adherent/organs/organs_internal.dm +++ b/mods/species/adherent/organs/organs_internal.dm @@ -1,4 +1,4 @@ -#define PROTOCOL_ARTICLE "Protocol article [rand(100,999)]-[uppertext(pick(global.full_alphabet))] subsection #[rand(10,99)]" +#define PROTOCOL_ARTICLE "Protocol article [rand(100,999)]-[uppertext(pick(global.alphabet))] subsection #[rand(10,99)]" /obj/item/organ/internal/brain/adherent name = "mentality matrix" diff --git a/mods/species/ascent/datum/codex.dm b/mods/species/ascent/datum/codex.dm index 9c7b409def5..2aad34a2114 100644 --- a/mods/species/ascent/datum/codex.dm +++ b/mods/species/ascent/datum/codex.dm @@ -1,6 +1,5 @@ /datum/codex_entry/ascent name = "The Ascent" - associated_strings = list("ascent") associated_paths = list( /mob/living/silicon/robot/flying/ascent, /obj/item/multitool/mantid, diff --git a/mods/species/ascent/datum/culture.dm b/mods/species/ascent/datum/culture.dm index 241db72ce68..99fe5cdaa6a 100644 --- a/mods/species/ascent/datum/culture.dm +++ b/mods/species/ascent/datum/culture.dm @@ -2,8 +2,7 @@ return dna?.lineage || create_gyne_name() /proc/create_gyne_name() - var/gynename = "[capitalize(pick(global.gyne_architecture))] [capitalize(pick(global.gyne_geoforms))]" - return gynename + return "[capitalize(pick(global.gyne_architecture))] [capitalize(pick(global.gyne_geoforms))]" //Thanks to: // - https://en.wikipedia.org/wiki/List_of_landforms @@ -63,8 +62,8 @@ var/global/list/gyne_architecture = list( ) /decl/cultural_info/culture/ascent - name = "The Ascent" - language = /decl/language/mantid/nonvocal + name = "Ascent Milieu" + language = /decl/language/mantid/nonvocal default_language = /decl/language/mantid additional_langs = list(/decl/language/mantid/worldnet, /decl/language/mantid) hidden = TRUE @@ -86,8 +85,8 @@ var/global/list/gyne_architecture = list( return "[random_id(/decl/species/mantid, 1, 99)] [lineage]" /decl/cultural_info/location/kharmaani - name = "Core" - language = /decl/language/mantid/nonvocal + name = "Ascent Core" + language = /decl/language/mantid/nonvocal description = "The Kharmaani are not terribly imaginative when it comes to naming their worlds. Core, \ their birth star, supports the humid greenhouse-gas-choked giant called Home, which the majority of the \ populace call their motherland. While the planet's orbit is thickly populated with habitats, factories \ @@ -96,8 +95,8 @@ var/global/list/gyne_architecture = list( hidden = TRUE /decl/cultural_info/faction/ascent_serpentid - name = "Ascent Serpentid" - language = /decl/language/mantid/nonvocal + name = "Ascent Serpentid" + language = /decl/language/mantid/nonvocal description = "Members of the Ascent tend to be organized along the natural lines of their respective species. \ For Kharmaani, this is oriented around individual gynes and their power structures. Serpentids have a slightly less \ manipulative approach, as well as more numerous and less self-absorbed queens. They tend to cluster in broad social groups, \ @@ -106,8 +105,8 @@ var/global/list/gyne_architecture = list( hidden = TRUE /decl/cultural_info/faction/ascent_alate - name = "Ascent Alate" - language = /decl/language/mantid/nonvocal + name = "Ascent Alate" + language = /decl/language/mantid/nonvocal description = "The life of an alate is a difficult and frequently short one. Those who survive \ to maturity have had the violent and uncompromising culture of the Ascent beaten into them with \ bladed forelimbs for their entire lives. There is no formal schooling within the Kharmaani \ @@ -120,8 +119,8 @@ var/global/list/gyne_architecture = list( hidden = TRUE /decl/cultural_info/faction/ascent_gyne - name = "Ascent Gyne" - language = /decl/language/mantid/nonvocal + name = "Ascent Gyne" + language = /decl/language/mantid/nonvocal description = "By the time a gyne has survived her 'childhood' and shed the exoskeleton of an \ alate during a breeding frenzy, she has obtained a master class education in murdering and eating \ her rivals at the first opportunity, as well as a sideline in a technical or practical field. The \ diff --git a/mods/species/ascent/datum/species.dm b/mods/species/ascent/datum/species.dm index 3b6cee8b250..c4be4544a11 100644 --- a/mods/species/ascent/datum/species.dm +++ b/mods/species/ascent/datum/species.dm @@ -130,14 +130,6 @@ BP_SYSTEM_CONTROLLER = /obj/item/organ/internal/controller ) - vital_organs = list( - BP_HEART = list("path" = /obj/item/organ/internal/heart/insectoid), - BP_LUNGS = list("path" = /obj/item/organ/internal/lungs/insectoid), - BP_BRAIN = list("path" = /obj/item/organ/internal/brain/insectoid), - BP_CHEST = list("path" = /obj/item/organ/external/chest/insectoid), - BP_GROIN = list("path" = /obj/item/organ/external/groin/insectoid/mantid), - ) - limb_mapping = list(BP_CHEST = list(BP_CHEST, BP_M_HAND)) force_cultural_info = list( diff --git a/mods/species/ascent/items/id_control.dm b/mods/species/ascent/items/id_control.dm index dd52074677b..a8bbdab1c59 100644 --- a/mods/species/ascent/items/id_control.dm +++ b/mods/species/ascent/items/id_control.dm @@ -37,9 +37,9 @@ status = ORGAN_PROSTHETIC var/obj/item/card/id/id_card = /obj/item/card/id/ascent -/obj/item/organ/internal/controller/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected) +/obj/item/organ/internal/controller/do_install(mob/living/carbon/human/target, obj/item/organ/external/affected, in_place, update_icon, detached) . = ..() - if(owner) + if(!detached && owner) owner.set_id_info(id_card) owner.add_language(/decl/language/mantid/worldnet) diff --git a/mods/species/ascent/machines/magnetotron.dm b/mods/species/ascent/machines/magnetotron.dm index aa072d6c7a0..3affc3825d4 100644 --- a/mods/species/ascent/machines/magnetotron.dm +++ b/mods/species/ascent/machines/magnetotron.dm @@ -43,7 +43,7 @@ target.visible_message(SPAN_NOTICE("[target] molts away their shell, emerging as a new gyne.")) spark_at(src, cardinal_only = TRUE) ADJ_STATUS(target, STAT_STUN, 6) - target.set_species(SPECIES_MANTID_GYNE) + target.change_species(SPECIES_MANTID_GYNE) new /obj/effect/temp_visual/emp_burst(loc) for(var/obj/item/organ/external/E in target.get_external_organs()) if(prob(60)) diff --git a/mods/species/ascent/mobs/drone.dm b/mods/species/ascent/mobs/drone.dm index ace63251459..69a4b10e365 100644 --- a/mods/species/ascent/mobs/drone.dm +++ b/mods/species/ascent/mobs/drone.dm @@ -137,6 +137,8 @@ N.charge_costs = list(1000) /mob/living/silicon/robot/flying/ascent + name = "\improper Ascent drone" + real_name = "\improper Ascent drone" desc = "A small, sleek, dangerous-looking hover-drone." speak_statement = "clicks" speak_exclamation = "rasps" diff --git a/mods/species/ascent/mobs/insectoid_egg.dm b/mods/species/ascent/mobs/insectoid_egg.dm index c302c3b4ed1..dad0583a7fe 100644 --- a/mods/species/ascent/mobs/insectoid_egg.dm +++ b/mods/species/ascent/mobs/insectoid_egg.dm @@ -10,7 +10,6 @@ var/global/default_gyne /obj/structure/insectoid_egg name = "alien egg" - breakable = TRUE desc = "A semi-translucent alien egg." health = 100 maxhealth = 100 diff --git a/mods/species/lizard/datum/faction.dm b/mods/species/lizard/datum/faction.dm index f5550126abd..4e4ac40956a 100644 --- a/mods/species/lizard/datum/faction.dm +++ b/mods/species/lizard/datum/faction.dm @@ -8,20 +8,20 @@ /decl/cultural_info/faction/lizard/sstrak name = "Sstrak-Yuk'har-V'vrenskan" - description = "You are part of the Sstrak-Yuk’har-V’vrenskan, the interstellar navy of the Coalition. They are the only branch of the military to have never fought in a single war, \ - and many Unathi consider their role to be purely ceremonial, or serving as ‘space police’. This lack of interstellar combat is a testament to the Unathi’s determination for mutual peace, \ + description = "You are part of the Sstrak-Yuk�har-V�vrenskan, the interstellar navy of the Coalition. They are the only branch of the military to have never fought in a single war, \ + and many Unathi consider their role to be purely ceremonial, or serving as �space police�. This lack of interstellar combat is a testament to the Unathi�s determination for mutual peace, \ but a concern to its military staff. Many members of the Sstrak are considered reserves, and have recently found themselves loaned out to civilian branches of the Coalition, serving as \ security attachments, such as onboard the Endeavour, protecting the Unathi personnel and their human counterparts." /decl/cultural_info/faction/lizard/kruz name = "Kruz Institute" description = "You are from the Kruz Institute, the scientific powerhouse of Moghes. Either from the Institute itself, with its home in the Tizegi Mountains, or from its colonial holdings \ - on Oe’erg, you are likely dedicated to the pursuit of science. The Kruz Institute maintains a significant presence in Unathi space, dedicating resources to interstellar exploration and research. \ + on Oe�erg, you are likely dedicated to the pursuit of science. The Kruz Institute maintains a significant presence in Unathi space, dedicating resources to interstellar exploration and research. \ While they have ties to the Vasakkad, they are a largely independent body, and have negotiated separate contracts to work alongside the Human ISEO. They are extremely picky when it comes to students, \ - and those that graduate from the Kruz Institute are considered some of the Unathi’s brightest minds." + and those that graduate from the Kruz Institute are considered some of the Unathi�s brightest minds." /decl/cultural_info/faction/lizard/other - name = "Other Faction" + name = "Unathi - Other Faction" description = "You do not consider yourself a part of any of the major factions of the Unathi. Wherever you are from, there are many reasons for an unaligned Unathi to travel to space, \ either for trade, employment, or adventure. It's likely you were born on Moghes, or Off-World as part of the new Spacer generation of Unathi, and you decided to leave the Vasakkad in search \ of something new. It is not unheard of for Unathi to seek employment in Human space, in a wide variety of roles and places, mostly serving contractor and civilian niches." 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/mods/species/skrell/datum/skrell_ranks.dm b/mods/species/skrell/datum/skrell_ranks.dm index 3485cbc8517..38f8aeb9d6f 100644 --- a/mods/species/skrell/datum/skrell_ranks.dm +++ b/mods/species/skrell/datum/skrell_ranks.dm @@ -1,7 +1,8 @@ /datum/mil_branch/skrell_fleet name = "Skrellian Defense Task Force" name_short = "SDTF" - email_domain = "sdtf.qb" + // See todo in torch_ranks.dm + // email_domain = "sdtf.qb" rank_types = list( /datum/mil_rank/skrell_fleet/zuumqix, /datum/mil_rank/skrell_fleet/vuxix diff --git a/mods/species/utility_frames/species.dm b/mods/species/utility_frames/species.dm index ec0bc9e7b8a..67390c3ba37 100644 --- a/mods/species/utility_frames/species.dm +++ b/mods/species/utility_frames/species.dm @@ -20,7 +20,7 @@ available_bodytypes = list(/decl/bodytype/utility_frame) age_descriptor = /datum/appearance_descriptor/age/utility_frame hidden_from_codex = FALSE - species_flags = SPECIES_FLAG_NO_PAIN | SPECIES_FLAG_NO_SCAN | SPECIES_FLAG_NO_POISON + species_flags = SPECIES_FLAG_NO_PAIN | SPECIES_FLAG_NO_SCAN | SPECIES_FLAG_NO_POISON | SPECIES_FLAG_SYNTHETIC spawn_flags = SPECIES_CAN_JOIN appearance_flags = HAS_SKIN_COLOR | HAS_EYE_COLOR strength = STR_HIGH @@ -64,10 +64,6 @@ BP_POSIBRAIN = /obj/item/organ/internal/posibrain, BP_EYES = /obj/item/organ/internal/eyes/robot ) - vital_organs = list( - BP_POSIBRAIN = list("path" = /obj/item/organ/internal/posibrain), - BP_CHEST = list("path" = /obj/item/organ/external/chest), - ) exertion_effect_chance = 10 exertion_charge_scale = 1 diff --git a/mods/species/vox/datum/species.dm b/mods/species/vox/datum/species.dm index b365a3f184b..95498a0f5b6 100644 --- a/mods/species/vox/datum/species.dm +++ b/mods/species/vox/datum/species.dm @@ -98,15 +98,6 @@ BP_HINDTONGUE = /obj/item/organ/internal/hindtongue ) - vital_organs = list( - BP_HEART = list("path" = /obj/item/organ/internal/heart/vox), - BP_LUNGS = list("path" = /obj/item/organ/internal/lungs/vox), - BP_BRAIN = list("path" = /obj/item/organ/internal/brain), - BP_STACK = list("path" = /obj/item/organ/internal/voxstack), - BP_CHEST = list("path" = /obj/item/organ/external/chest), - BP_GROIN = list("path" = /obj/item/organ/external/groin/vox), - ) - override_limb_types = list(BP_TAIL = /obj/item/organ/external/tail/vox) available_pronouns = list(/decl/pronouns/neuter) diff --git a/nano/css/shared.css b/nano/css/shared.css index 5e22c05b339..787fd71b78c 100644 --- a/nano/css/shared.css +++ b/nano/css/shared.css @@ -41,8 +41,18 @@ hr { border: 1px solid #00ff00; padding: 0px 4px 4px 4px; margin: 0 2px 2px 0; - cursor: default; white-space: nowrap; + cursor: pointer; +} + +.link:hover, .greenButton:hover{ + background: #009c00 !important; +} +.redButton:hover{ + background: #9c0202 !important; +} +.yellowButton:hover{ + background: #848601 !important; } .hasIcon { @@ -86,10 +96,11 @@ a.white:hover { background: #2f943c; } -.linkOff, a.linkOff:link, a.linkOff:visited, a.linkOff:active, a.linkOff:hover, .disabled, a.disabled:link, a.disabled:visited, a.disabled:active, a.disabled:hover { +.linkOff, a.linkOff:link, a.linkOff:visited, a.linkOff:active, a.linkOff:hover, .disabled, .disabled:hover, a.disabled:link, a.disabled:visited, a.disabled:active, a.disabled:hover { color: #ffffff; - background: #999999; + background: #999999 !important; border-color: #666666; + cursor: default; } a.icon, .linkOn.icon, .linkOff.icon, .selected.icon, .disabled.icon { diff --git a/nano/images/example/example-1.png b/nano/images/example/example-1.png index d3f74bc2397..e69de29bb2d 100644 Binary files a/nano/images/example/example-1.png and b/nano/images/example/example-1.png differ diff --git a/nano/images/example/example-2.png b/nano/images/example/example-2.png index a623ad868b6..e69de29bb2d 100644 Binary files a/nano/images/example/example-2.png and b/nano/images/example/example-2.png differ diff --git a/nano/images/example/example-3.png b/nano/images/example/example-3.png index 905c3714a4b..e69de29bb2d 100644 Binary files a/nano/images/example/example-3.png and b/nano/images/example/example-3.png differ diff --git a/nano/js/nano_template.js b/nano/js/nano_template.js index 1e984c78349..bac5ff5cea3 100644 --- a/nano/js/nano_template.js +++ b/nano/js/nano_template.js @@ -60,7 +60,7 @@ var NanoTemplate = function () { _compiledTemplates[key] = doT.template(_templates[key], null, _templates) } catch (error) { - alert(error.message); + alert('ERROR: Compiling template key "' + key + '" ("' + _templateData[key] + '") failed with error: ' + error); } } }; @@ -84,8 +84,6 @@ var NanoTemplate = function () { compileTemplates(); } if (typeof _compiledTemplates[templateKey] != 'function') { - alert(_compiledTemplates[templateKey]); - alert('ERROR: Template "' + templateKey + '" failed to compile!'); return '

    Template error (failed to compile)

    '; } return _compiledTemplates[templateKey].call(this, data['data'], data['config'], _helpers); diff --git a/nano/templates/TemplatesGuide.txt b/nano/templates/TemplatesGuide.txt index f333e454676..60f58d31f5b 100644 --- a/nano/templates/TemplatesGuide.txt +++ b/nano/templates/TemplatesGuide.txt @@ -7,4 +7,4 @@ to easily add conditionals (if statements), loops (for loops) and custom formatt Templates are stored in the /nano/templates folder and the file extension is .tmpl. -This guide is being replaced with a wiki entry, found here: http://wiki.baystation12.net/NanoUI \ No newline at end of file +This guide is being replaced with a wiki entry, found here: https://bay.ss13.me/en/Guides/coderbus/nanoui \ No newline at end of file diff --git a/nano/templates/account_management.tmpl b/nano/templates/account_management.tmpl new file mode 100644 index 00000000000..0b512fa10ce --- /dev/null +++ b/nano/templates/account_management.tmpl @@ -0,0 +1,87 @@ +{{if data.prog_state == -1}} +
    +
    + Error Encountered: +
    +
    + {{:data.error}} +
    +
    + {{:helper.link('Go back', 'arrowthickstop-1-w', {'back' : 1}, null)}} +{{else data.prog_state == 0}} +

    Welcome to the network account management utility:

    + {{:helper.link('Self account management', null, {'self_mode' : 1}, null)}} + {{:helper.link('Manage other accounts', null, {'other_mode' : 1}, null)}} +{{else data.prog_state == 1}} +

    Welcome, {{:data.account_fullname}}

    +
    +
    + Login: +
    +
    + {{:data.account_name}} +
    +
    +
    +
    + Password: +
    +
    + {{:helper.link('*******', null, {'change_password' : 1}, null)}} +
    +
    +
    +
    +
    + Groups: +
    +
    + {{for data.account_groups}} + {{:value}}
    + {{/for}} +
    +
    +
    + {{:helper.link('Change name', null, {'change_fullname' : 1}, null)}} + {{:helper.link('Go back', 'arrowthickstop-1-w', {'back' : 1}, null)}} +{{else data.prog_state == 2}} + {{if data.accounts}} +

    Network Accounts:

    +

    Some accounts may not be visible depending on your access to account servers.

    +
    + +
    Account LoginReal Name + {{for data.accounts}} +
    {{:helper.link(value.account, '', {'select_account' : value.account})}} + {{:value.fullname}} + {{/for}} +
    + {{:helper.link('Create account', '', {'create_account' : 1})}} + {{:helper.link('Recover account from backup', '', {'recover_account' : 1})}} + {{:helper.link('Go back', 'arrowthickstop-1-w', {'back' : 1}, null)}} + {{else data.parent_groups}} +

    Modifying account for {{:data.account_name}}:

    +

    Viewing parent groups. Group sub management is {{:data.sub_management ? 'enabled' : 'disabled'}}.

    + + +
    Parent GroupMembership + {{for data.parent_groups}} +
    {{:helper.link(value.name, '', {'select_parent_group' : value.name})}} + {{:helper.link(value.member ? 'Member' : 'Not a member', '', {'mod_group' : value.name})}} + {{/for}} +
    + {{:helper.link('Go back', 'arrowthickstop-1-w', {'back' : 1}, null)}} + {{else data.child_groups}} +

    Modifying account for {{:data.account_name}}:

    +

    Viewing child groups of {{:data.parent_group}}. Group sub management is {{:data.sub_management ? 'enabled' : 'disabled'}}.

    + + +
    Child GroupMembership + {{for data.child_groups}} +
    {{:value.name}} + {{:helper.link(value.member ? 'Member' : 'Not a member', '', {'mod_group' : value.name})}} + {{/for}} +
    + {{:helper.link('Go back', 'arrowthickstop-1-w', {'back' : 1}, null)}} + {{/if}} +{{/if}} diff --git a/nano/templates/crew_records.tmpl b/nano/templates/crew_records.tmpl index 4d19a8ba844..f78b2128974 100644 --- a/nano/templates/crew_records.tmpl +++ b/nano/templates/crew_records.tmpl @@ -37,16 +37,10 @@ {{/for}} {{else}} -{{if data.creation}} - {{:helper.link('New Record', 'document', {'new_record' : 1}, null)}} -{{/if}} -{{:helper.link('Name Search', 'search', {'search' : 'Name'}, null)}} -{{if data.dnasearch}} - {{:helper.link('DNA Search', 'search', {'search' : 'DNA'}, null)}} -{{/if}} -{{if data.fingersearch}} - {{:helper.link('Fingerprint Search', 'search', {'search' : 'Fingerprint'}, null)}} -{{/if}} +{{:helper.link('New Record', 'document', {'new_record' : 1}, null)}} +{{for data.searchable}} + {{:helper.link(value + ' Search', 'search', {'search' : value}, null)}} +{{/for}}

    Available records:

    diff --git a/nano/templates/email_administration.tmpl b/nano/templates/email_administration.tmpl index 39cad285ab0..48a3ae0fe24 100644 --- a/nano/templates/email_administration.tmpl +++ b/nano/templates/email_administration.tmpl @@ -63,7 +63,6 @@
    {{:helper.link(data.cur_suspended ? 'Unsuspend' : 'Suspend', null, {'ban' : data.cur_uid})}} - {{:helper.link('Set Password', null, {'changepass' : data.cur_uid})}} {{:helper.link('Return', null, {'back' : 1})}}
    @@ -92,15 +91,10 @@ {{else}}

    Welcome to Email Administration System

    SECURE SYSTEM - Have your identification ready

    - {{:helper.link('Create New Account', null, {'newaccount' : 1})}}

    Select account which you wish to administrate:
    {{for data.accounts}} -
    {{:helper.link(value.login, null, {'viewaccount' : value.uid})}} +
    {{:helper.link(value.login, null, {'viewaccount' : value.login})}} {{/for}}
    -{{/if}} -{{if data.terminal}} -

    MANUAL SYSTEM ACCESS

    -{{:helper.link('ACTIVATE TERMINAL', null, {'PC_terminal' : 1})}} {{/if}} \ No newline at end of file diff --git a/nano/templates/email_client.tmpl b/nano/templates/email_client.tmpl index a6c5f4da88f..8f25f4a2630 100644 --- a/nano/templates/email_client.tmpl +++ b/nano/templates/email_client.tmpl @@ -27,14 +27,12 @@ {{else data.current_account}} Welcome to your account, {{:data.current_account}}
    {{:helper.link('New Message', 'mail-closed', {'new_message' : 1})}} - {{:helper.link('Change Password', 'locked', {'changepassword' : 1})}} {{:helper.link('Set notification', 'alert', {'set_notification' : 1})}} {{if data.notification_mute}} - {{:helper.link('Unmute', 'volume-on', {'mute' : 1})}} + {{:helper.link('Unmute', 'volume-off', {'mute' : 1})}} {{else}} - {{:helper.link('Mute', 'volume-off', {'mute' : 1})}} - {{/if}} - {{:helper.link('Log Out', 'key', {'logout' : 1})}}

    + {{:helper.link('Mute', 'volume-on', {'mute' : 1})}} + {{/if}}

    {{if data.addressbook}} {{:helper.link('Back', null, {'close_addressbook' : 1})}} {{:helper.link('Enter Email', 'pencil', {'edit_recipient' : 1})}} @@ -42,7 +40,7 @@
    Address book: {{for data.accounts}} -
    {{:value.name}}{{:value.job}}{{:helper.link(value.login, null, {'set_recipient' : value.login})}} +
    {{:value.name}}{{:value.job}}{{:helper.link(value.address, null, {'set_recipient' : value.address})}} {{/for}}
    {{else data.new_message}} @@ -173,28 +171,4 @@ No messages found in selected folder {{/if}} {{/if}} -{{else}} - Welcome to Email System. Please log in. -
    -
    - Email address: -
    -
    - {{:data.stored_login}}  -
    -
    - Password: -
    -
    - {{:data.stored_password}}  -
    -
    - Options: -
    -
    - {{:helper.link('Enter Login', null, {'edit_login' : 1})}} - {{:helper.link('Enter Password', null, {'edit_password' : 1})}} - {{:helper.link('Log In', null, {'login' : 1})}} -
    -
    {{/if}} \ No newline at end of file diff --git a/nano/templates/fabricator.tmpl b/nano/templates/fabricator.tmpl index 8d9ec5549fb..bdca3f6087a 100644 --- a/nano/templates/fabricator.tmpl +++ b/nano/templates/fabricator.tmpl @@ -1,132 +1,27 @@ -{{if data.functional}} - - {{if data.color_selectable}} - - - - - {{/if}} - {{if data.network}} - - - {{:helper.link(data.network, null, { 'settings': 1 }, null)}} - - - {{/if}} - - - - - - - - - - - -
    - Color - - {{:helper.link('(Set)', null, {'color_select' : 1})}} -
    - Connected to local network. - -
    - - - - - - - {{for data.material_storage}} - - - - - - {{/for}} -
    ResourceStorageOptions
    {{:value.name}}{{:value.stored}}/{{:value.max}}{{:helper.link(value.eject_label, null, {'eject_mat' : value.eject_key})}}
    -
    - - - - - - - - - - - -
    Current BuildUnitsProgress
    {{:data.current_build.name}}{{:data.current_build.multiplier}}{{:data.current_build.progress}}
    -
    - - - - - - - {{for data.build_queue}} - - - - {{if value.reference}} - - {{else}} - - {{/if}} - - {{/for}} -
    PendingUnitsOptions
    {{:value.name}}{{:value.multiplier}}{{:helper.link('Cancel', null, {'cancel' : value.reference})}}-
    -
    + +{{#def.fab_shared}} + + + +{{ fab_header( data.network, data.network_id ); }} -
    -
    - Category -
    -
    - {{:helper.link(data.category, null, {'change_category' : 1})}} -
    -
    +{{if data.functional}} + +
    + {{ fab_resources_table(data.expand_resources, data.material_storage); }} + {{ fab_construction_queue(data.expand_queue, data.build_queue, data.current_build); }} + {{ fab_configuration( data.skip_config, data.expand_config, data); }} + {{ fab_display_filter( data.category, data.filtering, data.hide_categories); }} +
    -
    -
    - Filter -
    -
    - {{:helper.link(data.filtering, null, {'set_filter' : 1})}} -
    -
    + + {{ fab_build_options( data.build_options ); }} -
    - - - - - - - {{for data.build_options}} - - {{if value.illegal}} - - {{else}} - - {{/if}} - - - - {{/for}} -
    DesignCostOptions
    {{:value.name}}{{:value.name}}{{:value.cost}} - {{if !value.unavailable}} - {{:helper.link('Queue', null, {'make' : value.reference, 'multiplier' : 1})}} - {{for value.multiplier :multValue:multIndex}} - {{:helper.link(multValue.label, null, {'make' : value.reference, 'multiplier' : multValue.multiplier})}} - {{/for}} - {{else}} - Insufficient resources. - {{/if}} -
    -
    {{else}} -

    FABRICATOR OFFLINE. CONTACT SYSTEM ADMINISTRATOR.

    +

    FABRICATOR OFFLINE. CONTACT SYSTEM ADMINISTRATOR.

    + {{/if}} diff --git a/nano/templates/fabricator_bioprinter.tmpl b/nano/templates/fabricator_bioprinter.tmpl new file mode 100644 index 00000000000..a6937ffe9f9 --- /dev/null +++ b/nano/templates/fabricator_bioprinter.tmpl @@ -0,0 +1,93 @@ + +{{#def.fab_shared}} + + + + + +{{function display_dna(dna) { }} + +{{ } }} + + +{{function fab_organ_config_summary(dna) { + let summary = " ["; + + if(dna != undefined) { + summary += "DNA: " + dna.real_name + ", " + dna.species; + } else { + summary += "!! No DNA !! "; + }; + + return summary + "]"; +} }} + + {{function fab_organ_config( dna, expand_config) { }} +
    + {{:helper.link(make_list_toggle_prefix(expand_config, 'Configuration') + (expand_config ? '' : fab_organ_config_summary(dna) ), null, {'toggle_config' : 1}, null, 'linkSubtle') }} +
    + {{if expand_config}} +
    + +
    + {{if dna}} +
    {{:helper.link('DNA:', 'trash', {'flush_dna' : 1} ) }}
    +
    {{display_dna(dna);}}
    + {{else}} +
    DNA:
    +

    No DNA sample loaded.

    + {{/if}} +
    +
    + {{/if}} + {{ } }} + + + +{{fab_header( data.network, data.network_id ); }} + +{{if data.functional}} + +
    + {{ fab_resources_table( data.expand_resources, data.material_storage ); }} + {{ fab_construction_queue( data.expand_queue, data.build_queue, data.current_build ); }} + {{ fab_organ_config( data.dna, data.expand_config ); }} +
    + + + {{ fab_build_options(data.build_options); }} + +{{else}} +

    FABRICATOR OFFLINE. CONTACT SYSTEM ADMINISTRATOR.

    +{{/if}} diff --git a/nano/templates/fabricator_robot.tmpl b/nano/templates/fabricator_robot.tmpl new file mode 100644 index 00000000000..644cb5c9ffe --- /dev/null +++ b/nano/templates/fabricator_robot.tmpl @@ -0,0 +1,62 @@ + +{{#def.fab_shared}} + + + + + {{function fab_limb_config_summary(species) { + let summary = " ["; + + if(species != undefined) { + summary += species; + } else { + summary += "!! No species !! "; + }; + + return summary + "]"; +} }} + +{{function fab_limb_config( species, expand_config) { }} +
    + {{:helper.link(make_list_toggle_prefix(expand_config, 'Configuration') + (expand_config ? '' : fab_limb_config_summary(species) ), null, {'toggle_config' : 1}, null, 'linkSubtle') }} +
    + {{if expand_config}} +
    + +
    +
    Current Species:
    +
    {{:helper.link( species, null, {'pick_species' : 1} ) }}
    +
    +
    + {{/if}} +{{ } }} + + + +{{fab_header( data.network, data.network_id ); }} + +{{if data.functional}} + +
    + {{ fab_resources_table( data.expand_resources, data.material_storage ); }} + {{ fab_construction_queue( data.expand_queue, data.build_queue, data.current_build ); }} + {{ fab_limb_config( data.species, data.expand_config ); }} + {{ fab_display_filter( data.category, data.filtering, data.hide_categories ); }} +
    + + + {{ fab_build_options(data.build_options); }} + +{{else}} +

    FABRICATOR OFFLINE. CONTACT SYSTEM ADMINISTRATOR.

    +{{/if}} \ No newline at end of file diff --git a/nano/templates/fabricator_shared.tmpl b/nano/templates/fabricator_shared.tmpl new file mode 100644 index 00000000000..404024b2061 --- /dev/null +++ b/nano/templates/fabricator_shared.tmpl @@ -0,0 +1,414 @@ + +{{#def.net_shared}} + + + + + + + +{{function get_longest_material_name(material_storage){ + let longest = 0; + for(let item in material_storage){ + if(material_storage[item].name.length > longest){ + longest = material_storage[item].name.length; + }; + }; + return longest; +} }} + + +{{function shorten_number(amount, fixed) { + var billions = amount / 1000000000; + var millions = amount / 1000000; + var thousands = amount / 1000; + + if(billions >= 1){ + return String(fixed? billions.toFixed(3) : billions) + 'G'; + } + else if(millions >= 1){ + return String(fixed? millions.toFixed(3) : millions) + 'M'; + } + else if(thousands >= 1){ + return String(fixed? thousands.toFixed(3) : thousands) + 'K'; + } + + return String((fixed? amount.toFixed(3) : amount)); +} }} + + +{{function pad_string(str, padlength, padchar) { + let padding = ''; + let strlen = str.length; + if(padlength > strlen){ + for(let i = 0; i < (padlength - strlen); i++) { + padding += padchar; + }; + }; + return str + padding; +} }} + + +{{function make_list_toggle_prefix(shown, list_name) { + return (shown? '-' : '+') + ' ' + list_name; +} }} + + +{{function fab_header(network, network_id) { }} + {{net_connection_settings(network, network_id);}} +{{ } }} + + + + + {{function make_material_list_summary(material_storage) { + let summary = ''; + if(material_storage){ + let nbempty = 0; + for(let item in material_storage){ + let current = material_storage[item].name.substr(0,2) + ':' + shorten_number(material_storage[item].stored, true) + ' '; + if(material_storage[item].stored <= 0){ + nbempty += 1; + }; + }; + if(nbempty >= material_storage.length){ + summary = "Materials Depleted!"; + } + else if(nbempty > 0) { + summary = '' + nbempty + ' depleted material(s)'; + }; + } + if(summary.length){ + summary = ' [ ' + summary + ']'; + } + return summary; +} }} + + +{{function fab_resources_table_entry(name, amount, max, unit, longestNameLen, ejectKey) { }} +
    + {{:helper.link('', 'eject', {'eject_mat' : ejectKey}, null, 'link') }} {{:pad_string(name, longestNameLen, ' ') }} {{:pad_string(amount, 8, ' ') }}/{{:pad_string(max, 8, ' ')}} {{:unit}} +
    +{{ } }} + + +{{function fab_resources_table(expand_resources, material_storage) { }} + +
    + {{: helper.link( make_list_toggle_prefix(expand_resources, 'Materials') + make_material_list_summary(material_storage), null, {'toggle_resources' : 1}, null, 'linkSubtle') }} +
    + + {{if expand_resources}} + {{longest_material_name = get_longest_material_name(material_storage);}} + {{for material_storage}} + {{fab_resources_table_entry(value.name, shorten_number(value.stored, true), shorten_number(value.max, true), value.unit, longest_material_name, value.eject_key);}} + {{/for}} + {{/if}} +{{ } }} + + + + {{function make_queue_summary(current_build, build_queue) { + let summary = ''; + if(current_build){ + summary += current_build.name + ' (' + current_build.multiplier + ') ' + current_build.progress; + } + if(build_queue && build_queue.length > 0){ + summary += ' (' + build_queue.length + ' queued)'; + } + if(summary.length > 0){ + summary = ' [' + summary + ']' + } + return summary; +} }} + + +{{function fab_construction_queue(expand_queue, build_queue, current_build) { }} + +
    + {{: helper.link( (make_list_toggle_prefix(expand_queue, 'Queue') + make_queue_summary(current_build, build_queue)), null, {'toggle_queue' : 1}, null, 'linkSubtle') }} +
    + + {{if expand_queue}} +
    Current:
    +
    + {{if current_build}} + {{:current_build.name}} {{:current_build.multiplier}} {{:current_build.progress}} + {{else}} + -- + {{/if}} +
    +
    Queued:
    +
    + {{if build_queue && build_queue.length > 0}} + {{for build_queue}} + {{if value.reference}}{{:helper.link((value.name + ' x' + value.multiplier), 'cancel', {'cancel' : value.reference}, null, 'linkCancel')}}{{else}} {{:(value.name + ' x' + value.multiplier)}} {{/if}} + {{/for}} + {{else}} + -- + {{/if}} +
    + {{/if}} +{{ } }} + + + +{{function draw_colored_square(color) { }} + + +{{ } }} + +{{function fab_configuration_summary_color(expand_config, color) { + if(!expand_config){ + return ' [' + 'Color] '; + } + return ''; +} }} + +{{function fab_configuration_color(color_selectable, color) { }} + {{if color_selectable}} +
    +
    + Color: +
    +
    + {{draw_colored_square(color);}} {{:helper.link( 'set', null, {'color_select' : 1}) }} +
    +
    + {{/if}} +{{ } }} + +{{function fab_configuration(skip_config, expand_config, data) { }} + {{if !skip_config}} +
    + {{: helper.link( make_list_toggle_prefix(expand_config, 'Configuration') + fab_configuration_summary_color(expand_config, data.color), null, {'toggle_config' : 1}, null, 'linkSubtle') }} +
    + {{if expand_config}} +
    + {{fab_configuration_color( data.color_selectable, data.color );}} +
    + {{/if}} + {{/if}} +{{ } }} + + +{{ function fab_display_filter( category, filtering, hide_categories ) { }} +
    Filtering
    +
    + {{if !hide_categories}} +
    +
    + Category +
    +
    + {{:helper.link(category, null, {'change_category' : 1})}} +
    +
    + {{/if}} +
    +
    + Filter +
    +
    + {{:helper.link(filtering, null, {'set_filter' : 1})}} +
    +
    +
    +{{ } }} + + +{{function fab_make_cost_list( materials ) { }} + +{{ } }} + + +{{function fab_make_multiplier_buttons( unavailable, reference, multiplier ) { }} + {{:helper.link('x1', null, {'make' : reference, 'multiplier' : 1}, (unavailable ? 'disabled' : null))}} + {{for multiplier :multValue :multIndex}} + {{:helper.link(multValue.label, null, {'make' : reference, 'multiplier' : multValue.multiplier}, (unavailable ? 'disabled' : null) )}} + {{/for}} +{{ } }} + + +{{function fab_make_option_title(name, illegal) { }} + {{if illegal}}{{/if}} + {{:name}} + {{if illegal}}{{/if}} +{{ } }} + + +{{function fab_build_options( build_options ) { }} +
    + + + + + + + {{for build_options}} + + + + + + + + + {{/for}} +
    DesignCost
    {{ fab_make_option_title(value.name, value.illegal); }}{{ fab_make_cost_list(value.materials); }}{{ fab_make_multiplier_buttons(value.unavailable, value.reference, value.multiplier); }}
    +{{ } }} + diff --git a/nano/templates/identification_computer.tmpl b/nano/templates/identification_computer.tmpl index 8f60d625de6..72277b1ea7d 100644 --- a/nano/templates/identification_computer.tmpl +++ b/nano/templates/identification_computer.tmpl @@ -63,19 +63,19 @@
    - Email login: + Network account login:
    - {{:helper.link(data.id_email_login, 'pencil', {'action' : 'edit', 'elogin' : 1})}} + {{:helper.link(data.network_account_login, 'pencil', {'action' : 'edit', 'alogin' : 1})}}
    - Email password: + Network account password:
    - {{:helper.link(data.id_email_password, 'pencil', {'action' : 'edit', 'epswd' : 1})}} + {{:helper.link(data.network_account_password, 'pencil', {'action' : 'edit', 'apswd' : 1})}}
    diff --git a/nano/templates/layout_default_header.tmpl b/nano/templates/layout_default_header.tmpl index 0608c0938b4..6f5dc6f4e28 100644 --- a/nano/templates/layout_default_header.tmpl +++ b/nano/templates/layout_default_header.tmpl @@ -17,6 +17,11 @@ {{if data.PC_stationtime}} {{:data.PC_stationtime}} {{/if}} + {{if data.PC_loggedin}} + {{:helper.link('Account:' + data.PC_loggedin, null, {'PC_login' : 1})}}{{:helper.link('X', null, {'PC_logout' : 1})}} + {{else}} + {{:helper.link('Not logged in', null, {'PC_login' : 1})}} + {{/if}} {{for data.PC_programheaders}} {{/for}} diff --git a/nano/templates/network_acl.tmpl b/nano/templates/network_acl.tmpl index ff3db28d5c2..19e60e2ddd9 100644 --- a/nano/templates/network_acl.tmpl +++ b/nano/templates/network_acl.tmpl @@ -7,112 +7,46 @@ {{:helper.link("NETWORK SETTINGS", null, { "settings" : 1 })}} {{else}} - Welcome to the Network Firewall Controller system. Please consult your system administrator if you have any questions about your device.
    + Welcome to the Network Access Controller system. Please consult your system administrator if you have any questions about your device.
    {{:helper.link("NETWORK SETTINGS", null, { "settings" : 1 })}}
    {{if !data.connected}}

    Disconnected from network.

    + {{else !data.current_group}} +

    Network Group Settings:

    +
    SettingToggleInfo +
    PARENT GROUP SUBMANAGEMENT + {{:helper.link(data.allow_submanagement ? 'ON' : 'OFF', null, { "toggle_submanagement" : 1 })}} + {{:helper.link('?', null, { "info" : "submanagement"})}} +
    PARENT ACCOUNT CREATION + {{:helper.link(data.parent_account_creation ? 'ON' : 'OFF', null, { "toggle_parent_account_creation" : 1 })}} + {{:helper.link('?', null, { "info" : "parent_account_creation"})}} +
    +

    Viewing Parent Groups:

    + +
    Group + Operations + {{for data.parent_groups}} +
    {{:helper.link(value.group_name, null, {"view_child_groups" : value.group_name})}} + + {{:helper.link('Remove', null, { "remove_group" : value.group_name})}} + {{/for}} +
    + {{:helper.link('Create Group', null, { "create_group" : 1})}} {{else}} -

    Viewing File System:

    -
    - {{:helper.link(data.file_server, null, { "change_file_server" : 1 })}} -

    - {{if data.editing_user}} -

    Viewing User Record: {{:data.desired_name}}

    -
    -
    NAME:
    -
    {{:data.desired_name}}
    -
    -
    -
    USER ID:
    -
    {{:data.user_id}}
    -
    -
    -
    GRANT COUNT:
    -
    {{:data.grant_count}}
    -
    -
    -
    RECORD SIZE:
    -
    {{:data.size}}GQ
    -
    -
    - {{:helper.link('CREATE GRANT', null, { "create_grant" : data.user_id })}} - {{if data.is_admin}} - {{:helper.link('REMOVE ADMIN', null, { "remove_admin": 1})}} - {{else}} - {{:helper.link('GRANT ADMIN', null, { "add_admin": 1})}} - {{/if}} - {{:helper.link('WRITE ID', null, { "write_id": 1}, !data.card_inserted ? 'disabled' : null)}} - {{:helper.link('EJECT ID', null, { "eject_id": 1}, !data.card_inserted ? 'disabled' : null)}} -
    -
    - -
    Grant - Operations - {{for data.grants}} -
    {{:value.grant_name}} - - {{if value.assigned}} - {{:helper.link('REMOVE', null, { "remove_grant" : value.grant_name })}} - {{else}} - {{:helper.link('ASSIGN', null, { "assign_grant" : value.grant_name })}} - {{/if}} - {{:helper.link('DELETE', null, { "delete_grant" : value.grant_name })}} - {{/for}} -
    - {{:helper.link('Back to menu', null, { "back" : 1 })}} - {{else data.editing_program}} -

    Editing Program: {{:data.program_name}}

    -
    - - {{:helper.link('REMOVE ACCESS REQUIREMENT', null, { "clear_program_access" : 1}, data.cleared_control ? 'selected' : null)}} -
    Grant - Operations - {{for data.grants}} -
    {{:value.grant_name}} - - {{if value.assigned}} - {{:helper.link('REMOVE', null, { "remove_grant" : value.grant_name })}} - {{else}} - {{:helper.link('ASSIGN', null, { "assign_grant" : value.grant_name })}} - {{/if}} - {{/for}} -
    - {{:helper.link('Back to menu', null, { "back" : 1 })}} - {{else}} -

    Available Users ({{:data.file_server}}):

    - -
    User ID - Grants - File size (GQ) - Operations - {{for data.users}} -
    {{:value.desired_name}} - {{:value.grant_count}} - {{:value.size}}GQ - - {{:helper.link('VIEW', null, { "view_user" : value.user_id })}} - {{/for}} -
    -
    -

    Available Programs:

    - {{if data.program_control}} - {{:helper.link('DISABLE PROGRAM ACCESS CONTROL', null, { "toggle_program_control" : 1})}} - -
    Program - Grants - Access - {{for data.programs}} -
    {{:value.name}} - {{:value.grants}} - - {{:helper.link('EDIT', null, { "view_program" : value.name})}} - {{/for}} -
    - {{else}} - {{:helper.link('ENABLE PROGRAM ACCESS CONTROL', null, { "toggle_program_control" : 1})}} - {{/if}} - {{/if}} +

    Parent Group: {{:data.current_group}}

    +
    + +
    Group + Operations + {{for data.child_groups}} +
    {{:value.group_name}} + + {{:helper.link('Remove', null, { "remove_group" : value.group_name})}} + {{/for}} +
    + {{:helper.link('Create Group', null, { "create_group" : 1})}} + {{:helper.link('Back to parent group listing', null, { "view_child_groups" : null })}} {{/if}} {{/if}} \ No newline at end of file diff --git a/nano/templates/network_id.tmpl b/nano/templates/network_id.tmpl new file mode 100644 index 00000000000..52613e05998 --- /dev/null +++ b/nano/templates/network_id.tmpl @@ -0,0 +1,19 @@ +
    + {{:helper.link("NETWORK SETTINGS", null, { "settings" : 1 })}} +
    +
    +
    +
    Login:
    +
    + {{:helper.link(data.login, null, { "change_login" : 1 })}} +
    +
    +
    +
    Password:
    +
    + {{:helper.link(data.password, null, { "change_password" : 1 })}} +
    +
    +
    + {{:helper.link('Login', null, { "login_account" : 1 })}} +
    \ No newline at end of file diff --git a/nano/templates/network_lock.tmpl b/nano/templates/network_lock.tmpl index e6e8ce1d56f..319618960fc 100644 --- a/nano/templates/network_lock.tmpl +++ b/nano/templates/network_lock.tmpl @@ -20,19 +20,63 @@
    -

    Grants:

    -
    Grant +
    Pattern + Groups Operations - {{for data.grants}} -
    {{:value.grant_name}} - - {{if value.assigned}} - {{:helper.link('REMOVE', null, { "remove_grant" : value.grant_name })}} - {{else}} - {{:helper.link('ASSIGN', null, { "assign_grant" : value.grant_name })}} - {{/if}} + {{for data.patterns}} +
    {{:helper.link("Pattern " + value.index, null, { "select_pattern" : value.index}, (data.selected_pattern == value.index) ? 'selected' : null)}} + {{:value.groups}} + {{:helper.link('Delete pattern', null, { "remove_pattern" : value.index})}} {{/for}}
    +
    + {{:helper.link('Add pattern', null, { "add_pattern" : 1}, null)}} + {{:helper.link('?', null, { "info" : "pattern" }, null)}} +
    + {{if data.parent_groups}} +

    Parent Groups:

    + {{:helper.link('?', null, { "info" : "parent_groups" }, null)}} + +
    Group + Operations + {{for data.parent_groups}} +
    {{:helper.link(value.parent_group, null, { "select_parent_group" : value.parent_group })}} + + {{if value.assigned}} + {{:helper.link('REMOVE', null, { "remove_group" : value.parent_group })}} + {{else}} + {{:helper.link('ASSIGN', null, { "assign_group" : value.parent_group })}} + {{/if}} + {{/for}} +
    + {{else data.selected_parent_group}} +

    Viewing Child Groups for: {{:data.selected_parent_group}}

    + {{if data.child_groups}} +

    Child Groups:

    + +
    Group + Operations + {{for data.child_groups}} +
    {{:value.child_group}} + + {{if value.assigned}} + {{:helper.link('REMOVE', null, { "remove_group" : value.child_group })}} + {{else}} + {{:helper.link('ASSIGN', null, { "assign_group" : value.child_group })}} + {{/if}} + {{/for}} +
    + {{else}} + No child groups found! + {{/if}} +
    + {{:helper.link('Back to parent group listing', null, { "select_parent_group" : null })}} +
    + {{else}} + {{if data.selected_pattern}} + No groups found on network! + {{/if}} + {{/if}} {{/if}} {{/if}} \ No newline at end of file diff --git a/nano/templates/network_router.tmpl b/nano/templates/network_router.tmpl index 740382aa17f..464b4e51451 100644 --- a/nano/templates/network_router.tmpl +++ b/nano/templates/network_router.tmpl @@ -30,6 +30,14 @@ BACKUP ROUTER / RELAY {{/if}} + {{if data.reconnect}} +
    + CONNECTION LOST, ATTEMPTING RECONNECT IN {{:data.reconnect}}s +
    +
    + {{:helper.link("RECONNECT NOW", null, { "reconnect" : 1 })}} +
    + {{/if}}
    Wi-Fi Connections:
    diff --git a/nano/templates/network_shared.tmpl b/nano/templates/network_shared.tmpl new file mode 100644 index 00000000000..3be2c10a429 --- /dev/null +++ b/nano/templates/network_shared.tmpl @@ -0,0 +1,25 @@ + + + + +{{function net_connection_settings(network, network_id) { }} +
    +
    {{:helper.link(network ? (network + '.' + network_id) : '---', network? 'signal-diag': 'close', { 'network_settings': 1 }, null)}}
    +
    +
    +{{ } }} \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index 7cef8cb42c8..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" @@ -1276,10 +1276,11 @@ #include "code\game\objects\structures\crates_lockers\closets\secure\security.dm" #include "code\game\objects\structures\crates_lockers\closets\secure\service.dm" #include "code\game\objects\structures\doors\_door.dm" -#include "code\game\objects\structures\stool_bed_chair_nest\bed.dm" -#include "code\game\objects\structures\stool_bed_chair_nest\chairs.dm" -#include "code\game\objects\structures\stool_bed_chair_nest\stools.dm" -#include "code\game\objects\structures\stool_bed_chair_nest\wheelchair.dm" +#include "code\game\objects\structures\stool_bed_chair_nest_sofa\bed.dm" +#include "code\game\objects\structures\stool_bed_chair_nest_sofa\chairs.dm" +#include "code\game\objects\structures\stool_bed_chair_nest_sofa\sofa.dm" +#include "code\game\objects\structures\stool_bed_chair_nest_sofa\stools.dm" +#include "code\game\objects\structures\stool_bed_chair_nest_sofa\wheelchair.dm" #include "code\game\turfs\simulated.dm" #include "code\game\turfs\turf.dm" #include "code\game\turfs\turf_ao.dm" @@ -1727,6 +1728,7 @@ #include "code\modules\codex\categories\category_cultures.dm" #include "code\modules\codex\categories\category_fusion_reaction.dm" #include "code\modules\codex\categories\category_languages.dm" +#include "code\modules\codex\categories\category_phenomena.dm" #include "code\modules\codex\categories\category_reactions.dm" #include "code\modules\codex\categories\category_recipes.dm" #include "code\modules\codex\categories\category_skills.dm" @@ -1747,7 +1749,6 @@ #include "code\modules\codex\entries\misc.dm" #include "code\modules\codex\entries\mobs.dm" #include "code\modules\codex\entries\paperwork.dm" -#include "code\modules\codex\entries\spells.dm" #include "code\modules\codex\entries\stacks.dm" #include "code\modules\codex\entries\storage.dm" #include "code\modules\codex\entries\structures.dm" @@ -1765,6 +1766,7 @@ #include "code\modules\crafting\crafting_recipes\gun_crafting\crafting_cannon.dm" #include "code\modules\crafting\crafting_recipes\gun_crafting\crafting_coilgun.dm" #include "code\modules\crafting\crafting_recipes\gun_crafting\crafting_zipgun.dm" +#include "code\modules\crafting\crafting_recipes\improvised_crafting\crafting_buckler.dm" #include "code\modules\crafting\crafting_recipes\improvised_crafting\crafting_butterflyknife.dm" #include "code\modules\crafting\crafting_recipes\improvised_crafting\crafting_crossbow.dm" #include "code\modules\crafting\crafting_recipes\improvised_crafting\crafting_spear_prod.dm" @@ -1908,6 +1910,7 @@ #include "code\modules\fabrication\designs\imprinter\designs_exosuit_software.dm" #include "code\modules\fabrication\designs\imprinter\designs_misc_circuits.dm" #include "code\modules\fabrication\designs\industrial\_designs_industrial.dm" +#include "code\modules\fabrication\designs\industrial\designs_armour.dm" #include "code\modules\fabrication\designs\industrial\designs_exosuit_components.dm" #include "code\modules\fabrication\designs\meat\_designs_meat.dm" #include "code\modules\fabrication\designs\meat\designs_organs.dm" @@ -2059,7 +2062,6 @@ #include "code\modules\keybindings\admin.dm" #include "code\modules\keybindings\bindings_atom.dm" #include "code\modules\keybindings\bindings_client.dm" -#include "code\modules\keybindings\carbon.dm" #include "code\modules\keybindings\client.dm" #include "code\modules\keybindings\communication.dm" #include "code\modules\keybindings\human.dm" @@ -2533,7 +2535,6 @@ #include "code\modules\modular_computers\computers\subtypes\preset_telescreen.dm" #include "code\modules\modular_computers\file_system\computer_file.dm" #include "code\modules\modular_computers\file_system\data.dm" -#include "code\modules\modular_computers\file_system\grant.dm" #include "code\modules\modular_computers\file_system\manifest.dm" #include "code\modules\modular_computers\file_system\program.dm" #include "code\modules\modular_computers\file_system\program_events.dm" @@ -2541,6 +2542,7 @@ #include "code\modules\modular_computers\file_system\programs\antagonist\hacked_camera.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\revelation.dm" #include "code\modules\modular_computers\file_system\programs\antagonist\uplink.dm" +#include "code\modules\modular_computers\file_system\programs\command\accounts.dm" #include "code\modules\modular_computers\file_system\programs\command\card.dm" #include "code\modules\modular_computers\file_system\programs\command\comm.dm" #include "code\modules\modular_computers\file_system\programs\engineering\alarm_monitor.dm" @@ -2599,6 +2601,9 @@ #include "code\modules\modular_computers\networking\_network.dm" #include "code\modules\modular_computers\networking\network_cable.dm" #include "code\modules\modular_computers\networking\network_files.dm" +#include "code\modules\modular_computers\networking\accounts\_network_accounts.dm" +#include "code\modules\modular_computers\networking\accounts\account.dm" +#include "code\modules\modular_computers\networking\accounts\email_message.dm" #include "code\modules\modular_computers\networking\device_types\_network_device.dm" #include "code\modules\modular_computers\networking\device_types\acl.dm" #include "code\modules\modular_computers\networking\device_types\broadcaster.dm" @@ -2606,9 +2611,6 @@ #include "code\modules\modular_computers\networking\device_types\relay.dm" #include "code\modules\modular_computers\networking\device_types\router.dm" #include "code\modules\modular_computers\networking\device_types\stock_part.dm" -#include "code\modules\modular_computers\networking\emails\_network_email.dm" -#include "code\modules\modular_computers\networking\emails\email_account.dm" -#include "code\modules\modular_computers\networking\emails\email_message.dm" #include "code\modules\modular_computers\networking\machinery\_network_machine.dm" #include "code\modules\modular_computers\networking\machinery\acl.dm" #include "code\modules\modular_computers\networking\machinery\mainframe.dm" @@ -2631,12 +2633,13 @@ #include "code\modules\modular_computers\terminal\terminal_skill_fail.dm" #include "code\modules\multiz\basic.dm" #include "code\modules\multiz\hoist.dm" +#include "code\modules\multiz\ladder.dm" #include "code\modules\multiz\level_data.dm" #include "code\modules\multiz\map_data.dm" #include "code\modules\multiz\mobile_ladder.dm" #include "code\modules\multiz\movement.dm" #include "code\modules\multiz\pipes.dm" -#include "code\modules\multiz\structures.dm" +#include "code\modules\multiz\stairs.dm" #include "code\modules\multiz\turf.dm" #include "code\modules\multiz\zmimic\mimic_common.dm" #include "code\modules\multiz\zmimic\mimic_movable.dm" @@ -3386,6 +3389,7 @@ #include "code\unit_tests\chemistry_tests.dm" #include "code\unit_tests\closets.dm" #include "code\unit_tests\clothing.dm" +#include "code\unit_tests\codex.dm" #include "code\unit_tests\culture.dm" #include "code\unit_tests\decls.dm" #include "code\unit_tests\economics.dm" diff --git a/test/check-paths.sh b/test/check-paths.sh index 5e2265ea09a..f92a2f59f91 100755 --- a/test/check-paths.sh +++ b/test/check-paths.sh @@ -23,7 +23,7 @@ exactly() { # exactly N name search [mode] [filter] # With the potential exception of << if you increase any of these numbers you're probably doing it wrong # Additional exception August 2020: \b is a regex symbol as well as a BYOND macro. -exactly 1 "escapes" '\\\\(red|blue|green|black|b|i[^mc])' +exactly 4 "escapes" '\\\\(red|blue|green|black|b|i[^mc])' "-Eo" exactly 4 "Del()s" '\WDel\(' exactly 2 "/atom text paths" '"/atom' exactly 2 "/area text paths" '"/area' @@ -32,12 +32,12 @@ exactly 2 "/mob text paths" '"/mob' exactly 6 "/obj text paths" '"/obj' exactly 8 "/turf text paths" '"/turf' exactly 1 "world<< uses" 'world<<|world[[:space:]]<<' -exactly 91 "'in world' uses" 'in world' +exactly 93 "'in world' uses" 'in world' exactly 1 "world.log<< uses" 'world.log<<|world.log[[:space:]]<<' exactly 18 "<< uses" '(?> uses" '>>(?!>)' -P exactly 0 "incorrect indentations" '^( {4,})' -P -exactly 20 "text2path uses" 'text2path' +exactly 22 "text2path uses" 'text2path' exactly 4 "update_icon() override" '/update_icon\((.*)\)' -P exactly 0 "goto uses" 'goto ' exactly 6 "atom/New uses" '^/(obj|atom|area|mob|turf).*/New\('