diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index b60d02c79d2..aa7b5ef0c55 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -24,8 +24,8 @@ _build_dependencies.sh @AffectedArc07 # Executables that need to be security-cleared dreamchecker.exe @AffectedArc07 +milla.dll @AffectedArc07 rust_g.dll @AffectedArc07 -librust_g.so @AffectedArc07 ### S34NW diff --git a/.github/workflows/build_milla.yml b/.github/workflows/build_milla.yml new file mode 100644 index 00000000000..df1856c59df --- /dev/null +++ b/.github/workflows/build_milla.yml @@ -0,0 +1,93 @@ +name: Build MILLA +on: + issue_comment: + types: created + +jobs: + build-milla: + if: | + github.event.issue.pull_request && + (github.event.comment.body == '!build_milla') && + ((github.event.sender.id == github.event.issue.user.id) || + (github.event.comment.author_association == 'COLLABORATOR') || + (github.event.comment.author_association == 'MEMBER') || + (github.event.comment.author_association == 'OWNER')) + + runs-on: ubuntu-latest + steps: + - name: Like the comment + env: + BASE_REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + run: | + gh api \ + --method POST \ + -H "Accept: application/vnd.github+json" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + /repos/$BASE_REPOSITORY/issues/comments/${{ github.event.comment.id }}/reactions \ + -f content='+1' + + - name: PR Data + run: | + pr_json=$(curl -L -s --fail-with-body -H "Authorization: token ${{ github.token }}" ${{ github.event.issue.pull_request.url }}) + if [ `jq -r '.maintainer_can_modify' <<<$pr_json` == "false" ] ; then + gh pr comment ${{ github.event.issue.html_url }} --body 'GitHub Actions can not push to the repository without "Allow edits and access to secrets by maintainers" checked.' + echo "FAIL_NOTIFIED=true" >> "$GITHUB_ENV" + exit 1 + fi + echo "PR_REPO=`jq -r '.head.repo.full_name' <<<$pr_json`" >> $GITHUB_ENV + echo "PR_BRANCH=`jq -r '.head.ref' <<<$pr_json`" >> $GITHUB_ENV + echo "PR_HEAD_LABEL=`jq -r '.head.label' <<<$pr_json`" >> $GITHUB_ENV + + - uses: actions/checkout@v4 + with: + repository: ${{ env.PR_REPO }} + ref: ${{ env.PR_BRANCH }} + token: ${{ github.token }} + + - name: Build MILLA + env: + BASE_BRANCH: ${{ github.event.repository.default_branch }} + BASE_REPOSITORY: ${{ github.repository }} + GH_TOKEN: ${{ github.token }} + run: | + # Get the code. + git config user.name paradisess13[bot] + git config user.email "165046124+paradisess13[bot]@users.noreply.github.com" + git pull origin "$PR_BRANCH" --depth=$((ahead_by + 1)) + git remote add upstream "https://github.com/$BASE_REPOSITORY.git" + git fetch upstream "$BASE_BRANCH" --depth=$((behind_by + 1)) + cd milla + + # Get dependencies. + rustup target add i686-unknown-linux-gnu + rustup target add i686-pc-windows-gnu + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install zlib1g-dev:i386 lib32gcc-11-dev mingw-w64 mingw-w64-i686-dev + + # Build it. + cargo build --release --target i686-unknown-linux-gnu + cargo build --release --target i686-pc-windows-gnu + + # Copy the built targets to their checked-in locations. + cp target/i686-unknown-linux-gnu/release/libmilla.so ../tools/ci/libmilla_ci.so + cp target/i686-pc-windows-gnu/release/milla.dll ../milla.dll + + # Check if a workflow file would be modified by the merge (permissions prevent pushes if so) + latest_workflow_commit=$(git log -n 1 --pretty=format:"%H" upstream/$BASE_BRANCH -- .github/workflows) + if ! git branch --contains $latest_workflow_commit | grep -q "$(git rev-parse --abbrev-ref HEAD)"; then + gh pr comment ${{ github.event.issue.html_url }} --body "GitHub Actions can not push to this branch as workflow files have been changed since your branch was last updated. Please update your branch past https://github.com/$BASE_REPOSITORY/commit/$latest_workflow_commit before using this command again." + echo "FAIL_NOTIFIED=true" >> "$GITHUB_ENV" + exit 1 + fi + + git commit -a -m "Build MILLA" --allow-empty + git push origin + + - name: Notify Failure + if: failure() && env.FAIL_NOTIFIED != 'true' + env: + GH_TOKEN: ${{ github.token }} + run: | + gh pr comment ${{ github.event.issue.html_url }} -b 'Building MILLA failed, see the action run log for details: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}' diff --git a/.github/workflows/merge_upstream_master.yml b/.github/workflows/merge_upstream_master.yml index c5032c411a8..9806851fa7c 100644 --- a/.github/workflows/merge_upstream_master.yml +++ b/.github/workflows/merge_upstream_master.yml @@ -109,6 +109,16 @@ jobs: git merge FETCH_HEAD git push origin + - name: Rebuild TGUI + run: | + if git diff-tree --name-only -r $(git rev-parse HEAD~2) | grep "tgui/public/" + bash tgui/bin/tgui + git commit -m "Rebuild TGUI" + git push origin + else + echo "No changes to rebuild TGUI" + fi + - name: Notify Failure if: failure() && env.FAIL_NOTIFIED != 'true' run: | diff --git a/code/__DEFINES/hud_locations.dm b/code/__DEFINES/hud_locations.dm index 3c907ddf9b5..c475b4d7a4d 100644 --- a/code/__DEFINES/hud_locations.dm +++ b/code/__DEFINES/hud_locations.dm @@ -171,9 +171,11 @@ #define HUD_STYLE_STANDARD 1 #define HUD_STYLE_REDUCED 2 #define HUD_STYLE_NOHUD 3 +#define HUD_STYLE_ACTIONHUD 4 -#define HUD_VERSIONS 3 //used in show_hud() +#define HUD_VERSIONS 4 //used in show_hud() //1 = standard hud //2 = reduced hud (just hands and intent switcher) //3 = no hud (for screenshots) +//4 = Only action buttons (so vampires / changelings and such can use abilities while asleep) diff --git a/code/_onclick/hud/fullscreen.dm b/code/_onclick/hud/fullscreen.dm index e93a0a206bd..afc30ca3422 100644 --- a/code/_onclick/hud/fullscreen.dm +++ b/code/_onclick/hud/fullscreen.dm @@ -1,21 +1,30 @@ -/mob/proc/overlay_fullscreen(category, type, severity) +/mob/proc/overlay_fullscreen(category, type, severity, animated = 0) var/atom/movable/screen/fullscreen/screen = screens[category] + var/is_centered = istype(screen, /atom/movable/screen/fullscreen/center) + if(!screen || screen.type != type) // needs to be recreated clear_fullscreen(category, FALSE) screens[category] = screen = new type() - else if((!severity || severity == screen.severity) && (!client || screen.screen_loc != "CENTER-7,CENTER-7" || screen.view == client.view)) + else if((!severity || severity == screen.severity) && (!is_centered && (!client || screen.screen_loc != "CENTER-7,CENTER-7" || screen.view == client.view))) // doesn't need to be updated return screen screen.icon_state = "[initial(screen.icon_state)][severity]" screen.severity = severity if(client && screen.should_show_to(src)) - screen.update_for_view(client.view) + if(animated) + INVOKE_ASYNC(src, PROC_REF(apply_screen_animation), animated, screen) + if(!is_centered) + screen.update_for_view(client.view) client.screen += screen return screen +/mob/proc/apply_screen_animation(animated = 0, atom/movable/screen/fullscreen/screen) + screen.alpha = 0 + animate(screen, alpha = 255, time = animated) + /mob/proc/clear_fullscreen(category, animated = 10) var/atom/movable/screen/fullscreen/screen = screens[category] if(!screen) @@ -30,10 +39,11 @@ if(client) client.screen -= screen qdel(screen) - else - if(client) - client.screen -= screen - qdel(screen) + return + + if(client) + client.screen -= screen + qdel(screen) /mob/proc/clear_fullscreens() for(var/category in screens) @@ -46,28 +56,21 @@ for(var/category in screens) screen = screens[category] if(screen.should_show_to(mymob)) - screen.update_for_view(mymob.client.view) mymob.client.screen |= screen - else - mymob.client.screen -= screen + continue + mymob.client.screen -= screen /atom/movable/screen/fullscreen icon = 'icons/mob/screen_full.dmi' icon_state = "default" screen_loc = "CENTER-7,CENTER-7" + var/view layer = FULLSCREEN_LAYER plane = FULLSCREEN_PLANE mouse_opacity = MOUSE_OPACITY_TRANSPARENT - var/view = 7 var/severity = 0 var/show_when_dead = FALSE -/atom/movable/screen/fullscreen/proc/update_for_view(client_view) - if(screen_loc == "CENTER-7,CENTER-7" && view != client_view) - var/list/actualview = getviewsize(client_view) - view = client_view - transform = matrix(actualview[1]/FULLSCREEN_OVERLAY_RESOLUTION_X, 0, 0, 0, actualview[2]/FULLSCREEN_OVERLAY_RESOLUTION_Y, 0) - /atom/movable/screen/fullscreen/proc/should_show_to(mob/mymob) if(!show_when_dead && mymob.stat == DEAD) return FALSE @@ -77,51 +80,62 @@ severity = 0 return ..() -/atom/movable/screen/fullscreen/brute +/// Stretch version is suitable for images that stick to screen edges, as they scale with user screen size +/// For centrally located screen objects, use /atom/movable/screen/fullscreen/center +/atom/movable/screen/fullscreen/stretch + view = 7 + +/atom/movable/screen/fullscreen/proc/update_for_view(client_view) + if(screen_loc == "CENTER-7,CENTER-7" && view != client_view) + var/list/actualview = getviewsize(client_view) + view = client_view + transform = matrix(actualview[1]/FULLSCREEN_OVERLAY_RESOLUTION_X, 0, 0, 0, actualview[2]/FULLSCREEN_OVERLAY_RESOLUTION_Y, 0) + +/atom/movable/screen/fullscreen/stretch/brute icon_state = "brutedamageoverlay" layer = UI_DAMAGE_LAYER -/atom/movable/screen/fullscreen/oxy +/atom/movable/screen/fullscreen/stretch/oxy icon_state = "oxydamageoverlay" layer = UI_DAMAGE_LAYER -/atom/movable/screen/fullscreen/crit +/atom/movable/screen/fullscreen/stretch/crit icon_state = "passage" layer = CRIT_LAYER -/atom/movable/screen/fullscreen/blind +/atom/movable/screen/fullscreen/stretch/blind icon_state = "blackimageoverlay" layer = BLIND_LAYER -/atom/movable/screen/fullscreen/impaired +/atom/movable/screen/fullscreen/stretch/impaired icon_state = "impairedoverlay" -/atom/movable/screen/fullscreen/payback +/atom/movable/screen/fullscreen/stretch/payback icon = 'icons/mob/screen_payback.dmi' icon_state = "payback" show_when_dead = TRUE -/atom/movable/screen/fullscreen/fog +/atom/movable/screen/fullscreen/stretch/fog icon = 'icons/mob/screen_fog.dmi' icon_state = "fog" color = "#FF0000" -/atom/movable/screen/fullscreen/flash +/atom/movable/screen/fullscreen/stretch/flash icon = 'icons/mob/screen_gen.dmi' screen_loc = "WEST,SOUTH to EAST,NORTH" icon_state = "flash" -/atom/movable/screen/fullscreen/flash/noise +/atom/movable/screen/fullscreen/stretch/flash/noise icon = 'icons/mob/screen_gen.dmi' screen_loc = "WEST,SOUTH to EAST,NORTH" icon_state = "noise" -/atom/movable/screen/fullscreen/high +/atom/movable/screen/fullscreen/stretch/high icon = 'icons/mob/screen_gen.dmi' screen_loc = "WEST,SOUTH to EAST,NORTH" icon_state = "druggy" -/atom/movable/screen/fullscreen/lighting_backdrop +/atom/movable/screen/fullscreen/stretch/lighting_backdrop icon = 'icons/mob/screen_gen.dmi' icon_state = "flash" transform = matrix(200, 0, 0, 0, 200, 0) @@ -130,18 +144,18 @@ show_when_dead = TRUE //Provides darkness to the back of the lighting plane -/atom/movable/screen/fullscreen/lighting_backdrop/lit +/atom/movable/screen/fullscreen/stretch/lighting_backdrop/lit invisibility = INVISIBILITY_LIGHTING layer = BACKGROUND_LAYER+21 color = "#000" show_when_dead = TRUE //Provides whiteness in case you don't see lights so everything is still visible -/atom/movable/screen/fullscreen/lighting_backdrop/unlit +/atom/movable/screen/fullscreen/stretch/lighting_backdrop/unlit layer = BACKGROUND_LAYER+20 show_when_dead = TRUE -/atom/movable/screen/fullscreen/see_through_darkness +/atom/movable/screen/fullscreen/stretch/see_through_darkness icon_state = "nightvision" plane = LIGHTING_PLANE layer = LIGHTING_LAYER @@ -149,7 +163,7 @@ show_when_dead = TRUE /// An effect which tracks the cursor's location on the screen -/atom/movable/screen/fullscreen/cursor_catcher +/atom/movable/screen/fullscreen/stretch/cursor_catcher icon_state = "fullscreen_blocker" // Fullscreen semi transparent icon plane = HUD_PLANE mouse_opacity = MOUSE_OPACITY_ICON @@ -167,14 +181,14 @@ var/mouse_params /// Links this up with a mob -/atom/movable/screen/fullscreen/cursor_catcher/proc/assign_to_mob(mob/owner) +/atom/movable/screen/fullscreen/stretch/cursor_catcher/proc/assign_to_mob(mob/owner) src.owner = owner view_list = getviewsize(owner.client.view) RegisterSignal(owner, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) calculate_params() /// Update when the mob we're assigned to has moved -/atom/movable/screen/fullscreen/cursor_catcher/proc/on_move(atom/source, atom/oldloc, dir, forced) +/atom/movable/screen/fullscreen/stretch/cursor_catcher/proc/on_move(atom/source, atom/oldloc, dir, forced) SIGNAL_HANDLER if(!given_turf) @@ -183,19 +197,18 @@ var/y_offset = source.loc.y - oldloc.y given_turf = locate(given_turf.x + x_offset, given_turf.y + y_offset, given_turf.z) - -/atom/movable/screen/fullscreen/cursor_catcher/MouseEntered(location, control, params) +/atom/movable/screen/fullscreen/stretch/cursor_catcher/MouseEntered(location, control, params) . = ..() MouseMove(location, control, params) if(usr == owner) calculate_params() -/atom/movable/screen/fullscreen/cursor_catcher/MouseMove(location, control, params) +/atom/movable/screen/fullscreen/stretch/cursor_catcher/MouseMove(location, control, params) if(usr != owner) return mouse_params = params -/atom/movable/screen/fullscreen/cursor_catcher/proc/calculate_params() +/atom/movable/screen/fullscreen/stretch/cursor_catcher/proc/calculate_params() var/list/modifiers = params2list(mouse_params) var/icon_x = text2num(LAZYACCESS(modifiers, "vis-x")) if(isnull(icon_x)) @@ -209,6 +222,28 @@ given_x = round(icon_x - world.icon_size * our_x, 1) given_y = round(icon_y - world.icon_size * our_y, 1) +/// Centered fullscreen atoms +/// These are used to center a widescreen image so it scales properly across screen sizes, not suitable for screen images that eminate from the edge of the screen +/atom/movable/screen/fullscreen/center + appearance_flags = TILE_BOUND + +/atom/movable/screen/fullscreen/center/Initialize(mapload) + . = ..() + var/icon/I = new(icon) + var/x_offset = -((I.Width() - world.icon_size) / 2) + var/y_offset = -((I.Height() - world.icon_size) / 2) + qdel(I) + screen_loc = "CENTER:[x_offset],CENTER:[y_offset]" + +/atom/movable/screen/fullscreen/center/blind/sleeping + icon = 'icons/mob/screen_sleeping.dmi' + icon_state = "sleepblind" + layer = BLIND_LAYER + +/atom/movable/screen/fullscreen/center/blind/disky + icon = 'icons/mob/screen_tight.dmi' + icon_state = "disky" + layer = BLIND_LAYER #undef FULLSCREEN_LAYER #undef BLIND_LAYER diff --git a/code/_onclick/hud/hud_datum.dm b/code/_onclick/hud/hud_datum.dm index 804e0c0f90f..566e07c7bb9 100644 --- a/code/_onclick/hud/hud_datum.dm +++ b/code/_onclick/hud/hud_datum.dm @@ -210,6 +210,15 @@ if(length(infodisplay)) mymob.client.screen -= infodisplay + if(HUD_STYLE_ACTIONHUD) //No HUD + hud_shown = TRUE //Governs behavior of other procs + if(static_inventory.len) + mymob.client.screen -= static_inventory + if(toggleable_inventory.len) + mymob.client.screen -= toggleable_inventory + if(infodisplay.len) + mymob.client.screen -= infodisplay + hud_version = display_hud_version persistent_inventory_update() mymob.update_action_buttons(1) @@ -244,6 +253,9 @@ return /mob/proc/hide_hud() + if(HAS_TRAIT(src, TRAIT_KNOCKEDOUT) && isliving(src)) + to_chat(src, "You can not change huds while asleep!") + return if(hud_used && client) hud_used.show_hud() //Shows the next hud preset to_chat(src, "Switched HUD mode. Press the key you just pressed to toggle the HUD mode again.") diff --git a/code/_onclick/hud/human_hud.dm b/code/_onclick/hud/human_hud.dm index 848fd46b2b9..68eb065069f 100644 --- a/code/_onclick/hud/human_hud.dm +++ b/code/_onclick/hud/human_hud.dm @@ -59,7 +59,7 @@ /datum/hud/human/New(mob/living/carbon/human/owner, ui_style = 'icons/mob/screen_white.dmi', ui_color = "#ffffff", ui_alpha = 255) ..() - owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness) + owner.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/stretch/see_through_darkness) var/atom/movable/screen/using var/atom/movable/screen/inventory/inv_box @@ -393,7 +393,7 @@ if(!mymob) return var/mob/living/carbon/human/H = mymob - if(inventory_shown && hud_shown) + if(inventory_shown && hud_version == HUD_STYLE_STANDARD) if(H.shoes) H.shoes.screen_loc = ui_shoes H.client.screen += H.shoes @@ -436,7 +436,7 @@ if(!mymob) return var/mob/living/carbon/human/H = mymob - if(hud_shown) + if(hud_version == HUD_STYLE_STANDARD) if(H.s_store) H.s_store.screen_loc = ui_sstore1 H.client.screen += H.s_store diff --git a/code/_onclick/hud/other_mobs_hud.dm b/code/_onclick/hud/other_mobs_hud.dm index 39fe3a31552..51e1562a73c 100644 --- a/code/_onclick/hud/other_mobs_hud.dm +++ b/code/_onclick/hud/other_mobs_hud.dm @@ -10,7 +10,7 @@ static_inventory += using action_intent = using - user.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/see_through_darkness) + user.overlay_fullscreen("see_through_darkness", /atom/movable/screen/fullscreen/stretch/see_through_darkness) /datum/hud/corgi/New(mob/user) ..() diff --git a/code/_onclick/hud/plane_master.dm b/code/_onclick/hud/plane_master.dm index 321239833a4..bc7ae0dca0f 100644 --- a/code/_onclick/hud/plane_master.dm +++ b/code/_onclick/hud/plane_master.dm @@ -44,8 +44,8 @@ /atom/movable/screen/plane_master/lighting/backdrop(mob/mymob) . = ..() - mymob.overlay_fullscreen("lighting_backdrop_lit", /atom/movable/screen/fullscreen/lighting_backdrop/lit) - mymob.overlay_fullscreen("lighting_backdrop_unlit", /atom/movable/screen/fullscreen/lighting_backdrop/unlit) + mymob.overlay_fullscreen("lighting_backdrop_lit", /atom/movable/screen/fullscreen/stretch/lighting_backdrop/lit) + mymob.overlay_fullscreen("lighting_backdrop_unlit", /atom/movable/screen/fullscreen/stretch/lighting_backdrop/unlit) /atom/movable/screen/plane_master/lighting/Initialize() . = ..() diff --git a/code/_onclick/hud/robot_hud.dm b/code/_onclick/hud/robot_hud.dm index b2c692dfc32..30fae75f816 100644 --- a/code/_onclick/hud/robot_hud.dm +++ b/code/_onclick/hud/robot_hud.dm @@ -205,7 +205,7 @@ if(!R.module) return - if(R.shown_robot_modules && hud_shown) + if(R.shown_robot_modules && hud_version == HUD_STYLE_STANDARD) //Modules display is shown R.client.screen += module_store_icon //"store" icon diff --git a/code/controllers/subsystem/SStgui.dm b/code/controllers/subsystem/SStgui.dm index d84c486c615..0f0392941d3 100644 --- a/code/controllers/subsystem/SStgui.dm +++ b/code/controllers/subsystem/SStgui.dm @@ -187,7 +187,7 @@ SUBSYSTEM_DEF(tgui) * * return int The number of UIs updated. */ -/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object) +/datum/controller/subsystem/tgui/proc/update_uis(datum/src_object, full_update = FALSE) var/count = 0 var/key = "[src_object.UID()]" // No UIs opened for this src_object @@ -196,7 +196,10 @@ SUBSYSTEM_DEF(tgui) for(var/datum/tgui/ui in open_uis_by_src[key]) // Check if UI is valid. if(ui && ui.src_object && ui.user && ui.src_object.ui_host(ui.user)) - ui.process(force = TRUE) + if(full_update) + ui.send_full_update(null, TRUE) + else + ui.process(force = TRUE) count++ return count diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm index 38f94e4ce89..4e976e9e5d6 100644 --- a/code/datums/status_effects/buffs.dm +++ b/code/datums/status_effects/buffs.dm @@ -670,13 +670,13 @@ return ..() /datum/status_effect/drill_payback/on_apply() - owner.overlay_fullscreen("payback", /atom/movable/screen/fullscreen/payback, 0) + owner.overlay_fullscreen("payback", /atom/movable/screen/fullscreen/stretch/payback, 0) addtimer(CALLBACK(src, PROC_REF(payback_phase_2)), 2.7 SECONDS) return TRUE /datum/status_effect/drill_payback/proc/payback_phase_2() owner.clear_fullscreen("payback") - owner.overlay_fullscreen("payback", /atom/movable/screen/fullscreen/payback, 1) + owner.overlay_fullscreen("payback", /atom/movable/screen/fullscreen/stretch/payback, 1) /datum/status_effect/drill_payback/tick() //They are not staying down. This will be a fight. if(!drilled_successfully && (get_dist(owner, drilled) >= 9)) //We don't want someone drilling the safe at arivals then raiding bridge with the buff diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm index e65da3535cd..1a93201a1d9 100644 --- a/code/datums/status_effects/debuffs.dm +++ b/code/datums/status_effects/debuffs.dm @@ -1023,7 +1023,7 @@ /datum/status_effect/bubblegum_curse/on_creation(mob/living/new_owner, mob/living/source) . = ..() source_UID = source.UID() - owner.overlay_fullscreen("Bubblegum", /atom/movable/screen/fullscreen/fog, 1) + owner.overlay_fullscreen("Bubblegum", /atom/movable/screen/fullscreen/stretch/fog, 1) /datum/status_effect/bubblegum_curse/tick() var/mob/living/simple_animal/hostile/megafauna/bubblegum/attacker = locateUID(source_UID) @@ -1031,7 +1031,7 @@ qdel(src) if(attacker.health <= attacker.maxHealth / 2) owner.clear_fullscreen("Bubblegum") - owner.overlay_fullscreen("Bubblegum", /atom/movable/screen/fullscreen/fog, 2) + owner.overlay_fullscreen("Bubblegum", /atom/movable/screen/fullscreen/stretch/fog, 2) if(!coward_checking) if(owner.z != attacker.z) addtimer(CALLBACK(src, PROC_REF(onstation_coward_callback)), 12 SECONDS) diff --git a/code/datums/uplink_items/uplink_general.dm b/code/datums/uplink_items/uplink_general.dm index 21bea123b93..a58bbf324dd 100644 --- a/code/datums/uplink_items/uplink_general.dm +++ b/code/datums/uplink_items/uplink_general.dm @@ -26,7 +26,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) uplink_items[I.category] = list() uplink_items[I.category] += I - if(I.limited_stock < 0 && !I.cant_discount && I.item && I.cost > 5) + if(I.limited_stock < 0 && I.can_discount && I.item && I.cost > 5) sales_items += I for(var/datum/uplink_item/I in last) @@ -79,7 +79,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /// This makes an item on the uplink only show up to the specified species var/list/species = null var/surplus = 100 //Chance of being included in the surplus crate (when pick() selects it) - var/cant_discount = FALSE + var/can_discount = TRUE var/limited_stock = -1 // Can you only buy so many? -1 allows for infinite purchases var/hijack_only = FALSE //can this item be purchased only during hijackings? var/refundable = FALSE @@ -379,7 +379,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) reference = "SKD" item = /obj/item/melee/knuckleduster/syndie cost = 10 - cant_discount = TRUE + can_discount = FALSE // GRENADES AND EXPLOSIVES @@ -751,7 +751,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 10 surplus = 0 hijack_only = TRUE //This is an item only useful for a hijack traitor, as such, it should only be available in those scenarios. - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/device_tools/advpinpointer name = "Advanced Pinpointer" @@ -887,7 +887,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) reference = "BABA" item = /obj/item/toy/syndicateballoon cost = 100 - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/badass/bomber name = "Syndicate Bomber Jacket" @@ -906,7 +906,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/bundles_TC category = "Bundles and Telecrystals" surplus = 0 - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/bundles_TC/telecrystal name = "Raw Telecrystal" diff --git a/code/datums/uplink_items/uplink_nuclear.dm b/code/datums/uplink_items/uplink_nuclear.dm index 5fbd96465e2..bb9fd71fe62 100644 --- a/code/datums/uplink_items/uplink_nuclear.dm +++ b/code/datums/uplink_items/uplink_nuclear.dm @@ -120,7 +120,7 @@ refund_path = /obj/item/antag_spawner/nuke_ops cost = 100 refundable = TRUE - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/support/reinforcement/assault_borg name = "Syndicate Assault Cyborg" @@ -347,7 +347,7 @@ reference = "C4B" item = /obj/item/storage/backpack/duffel/syndie/c4 cost = 40 //20% discount! - cant_discount = TRUE + can_discount = FALSE uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) /datum/uplink_item/explosives/breaching_charge @@ -366,7 +366,7 @@ reference = "X4B" item = /obj/item/storage/backpack/duffel/syndie/x4 cost = 20 - cant_discount = TRUE + can_discount = FALSE uplinktypes = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) /datum/uplink_item/explosives/grenadier @@ -643,7 +643,7 @@ cost = 55 excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT) surplus = 0 - cant_discount = TRUE + can_discount = FALSE hijack_only = FALSE /datum/uplink_item/explosives/emp_bomb/nuke @@ -651,7 +651,7 @@ cost = 50 excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT) surplus = 0 - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/explosives/atmosfiregrenades/nuke reference = "NAPG" @@ -659,7 +659,7 @@ cost = 60 excludefrom = list(UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT) surplus = 0 - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/stealthy_tools/chameleon/nuke reference = "NCHAM" diff --git a/code/datums/uplink_items/uplink_special.dm b/code/datums/uplink_items/uplink_special.dm index ae5e97944dc..c98f3a1ed6e 100644 --- a/code/datums/uplink_items/uplink_special.dm +++ b/code/datums/uplink_items/uplink_special.dm @@ -1,7 +1,7 @@ /// This uplink catagory is for uplink items avalible by special circumstances. Think station traits, or if some event rolling in a round gave traitors special items, or an objective. /datum/uplink_item/special category = "Special Offers" - cant_discount = TRUE + can_discount = FALSE surplus = 0 excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST, UPLINK_TYPE_TRAITOR, UPLINK_TYPE_SIT) diff --git a/code/datums/uplink_items/uplink_traitor.dm b/code/datums/uplink_items/uplink_traitor.dm index ff2cdec2b6d..99ce40fe217 100644 --- a/code/datums/uplink_items/uplink_traitor.dm +++ b/code/datums/uplink_items/uplink_traitor.dm @@ -4,7 +4,7 @@ /datum/uplink_item/jobspecific category = "Job Specific Tools" - cant_discount = TRUE + can_discount = FALSE excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) // Stops the job specific category appearing for nukies //Clown @@ -333,7 +333,7 @@ /datum/uplink_item/species_restricted category = "Species Specific Gear" - cant_discount = TRUE + can_discount = FALSE excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) // Stops the job specific category appearing for nukies //skrell @@ -420,7 +420,7 @@ refund_path = /obj/item/guardiancreator/tech/choose refundable = TRUE surplus = 0 // This being refundable makes this a big no no in my mind. - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/stealthy_weapons/martialarts name = "Martial Arts Scroll" @@ -431,7 +431,7 @@ item = /obj/item/sleeping_carp_scroll cost = 65 excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/stealthy_weapons/bearserk name = "Bearserker Pelt" @@ -548,7 +548,7 @@ cost = 70 excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) surplus = 0 - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/cyber_implants/sensory_enhancer name = "Qani-Laaca Sensory Computer Autoimplanter" @@ -659,7 +659,7 @@ cost = 40 excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) surplus = 0 - cant_discount = TRUE + can_discount = FALSE hijack_only = TRUE /datum/uplink_item/explosives/emp_bomb @@ -671,7 +671,7 @@ cost = 40 excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) surplus = 0 - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/explosives/emp_bomb/New() ..() @@ -687,7 +687,7 @@ cost = 50 excludefrom = list(UPLINK_TYPE_NUCLEAR, UPLINK_TYPE_SST) surplus = 0 - cant_discount = TRUE + can_discount = FALSE /datum/uplink_item/stealthy_tools/chameleon name = "Chameleon Kit" diff --git a/code/game/gamemodes/autotraitor/autotraitor.dm b/code/game/gamemodes/autotraitor/autotraitor.dm index 371a90bdfab..9752d190b91 100644 --- a/code/game/gamemodes/autotraitor/autotraitor.dm +++ b/code/game/gamemodes/autotraitor/autotraitor.dm @@ -1,7 +1,3 @@ -//This is a beta game mode to test ways to implement an "infinite" traitor round in which more traitors are automatically added in as needed. -//Automatic traitor adding is complete pending the inevitable bug fixes. Need to add a respawn system to let dead people respawn after 30 minutes or so. - - /datum/game_mode/traitor/autotraitor name = "AutoTraitor" config_tag = "extend-a-traitormongous" @@ -11,10 +7,9 @@ /datum/game_mode/traitor/autotraitor/announce() ..() - to_chat(world, "Game mode is AutoTraitor. Traitors will be added to the round automagically as needed.") + to_chat(world, "Game mode is AutoTraitor. Traitors will be added to the round automagically as needed.") /datum/game_mode/traitor/autotraitor/pre_setup() - if(GLOB.configuration.gamemode.prevent_mindshield_antags) restricted_jobs += protected_jobs @@ -24,7 +19,6 @@ if(P.client && P.ready) num_players++ - //var/r = rand(5) var/num_traitors = 1 var/max_traitors = 1 var/traitor_prob = 0 @@ -33,7 +27,7 @@ // Stop setup if no possible traitors if(!length(possible_traitors)) - return 0 + return FALSE if(GLOB.configuration.gamemode.traitor_scaling) num_traitors = max_traitors - 1 + prob(traitor_prob) @@ -42,8 +36,7 @@ else num_traitors = max(1, min(num_players(), traitors_possible)) - - for(var/i = 0, i < num_traitors, i++) + for(var/i in 1 to num_traitors) var/datum/mind/traitor = pick(possible_traitors) pre_traitors += traitor possible_traitors.Remove(traitor) @@ -56,128 +49,91 @@ traitor.special_role = SPECIAL_ROLE_TRAITOR traitor.restricted_roles = restricted_jobs -// if(!length(traitors)) -// return 0 - return 1 - - - + return TRUE /datum/game_mode/traitor/autotraitor/post_setup() ..() - traitorcheckloop() + addtimer(CALLBACK(src, PROC_REF(traitor_check_loop)), 15 MINUTES) -/datum/game_mode/traitor/autotraitor/proc/traitorcheckloop() - spawn(9000) - if(SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) - return - //message_admins("Performing AutoTraitor Check") - var/playercount = 0 - var/traitorcount = 0 - var/possible_traitors[0] - for(var/mob/living/player in GLOB.mob_list) - if(player.client && player.stat != DEAD) - playercount += 1 - if(!player.mind) - continue - if(player.mind.special_role) - traitorcount += 1 - continue - if(ishuman(player) || isAI(player)) - if((ROLE_TRAITOR in player.client.prefs.be_special) && !player.client.skip_antag && !jobban_isbanned(player, ROLE_TRAITOR) && !jobban_isbanned(player, ROLE_SYNDICATE)) - possible_traitors += player.mind - for(var/datum/mind/player in possible_traitors) - for(var/job in restricted_jobs) - if(player.assigned_role == job) - possible_traitors -= player - if(!player.current || !ishuman(player.current)) // Remove mindshield-implanted mobs from the list +/datum/game_mode/traitor/autotraitor/proc/traitor_check_loop() + if(SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) + return + + var/player_count = 0 + var/traitor_count = 0 + var/list/possible_traitors = list() + for(var/mob/living/player in GLOB.mob_list) + if(player.client && player.stat != DEAD) + player_count += 1 + if(!player.mind) continue - var/mob/living/carbon/human/H = player.current - for(var/obj/item/bio_chip/mindshield/I in H.contents) - if(I && I.implanted) - possible_traitors -= player - if(!H.job || H.mind.offstation_role) //Golems, special events stuff, etc. + if(player.mind.special_role) + traitor_count += 1 + continue + if(ishuman(player) || isAI(player)) + if((ROLE_TRAITOR in player.client.prefs.be_special) && !player.client.skip_antag && !jobban_isbanned(player, ROLE_TRAITOR) && !jobban_isbanned(player, ROLE_SYNDICATE)) + possible_traitors += player.mind + for(var/datum/mind/player in possible_traitors) + for(var/job in restricted_jobs) + if(player.assigned_role == job) possible_traitors -= player - //message_admins("Live Players: [playercount]") - //message_admins("Live Traitors: [traitorcount]") -// message_admins("Potential Traitors:") -// for(var/mob/living/traitorlist in possible_traitors) -// message_admins("[traitorlist.real_name]") + if(!player.current || !ishuman(player.current)) // Remove mindshield-implanted mobs from the list + continue + var/mob/living/carbon/human/H = player.current + for(var/obj/item/bio_chip/mindshield/I in H.contents) + if(I && I.implanted) + possible_traitors -= player + if(!H.job || H.mind.offstation_role) //Golems, special events stuff, etc. + possible_traitors -= player -// var/r = rand(5) -// var/target_traitors = 1 - var/max_traitors = 1 - var/traitor_prob = 0 - max_traitors = round(playercount / 10) + 1 - traitor_prob = (playercount - (max_traitors - 1) * 10) * 5 - if(traitorcount < max_traitors - 1) - traitor_prob += 50 + var/max_traitors = 1 + var/traitor_prob = 0 + max_traitors = round(player_count / 10) + 1 + traitor_prob = (player_count - (max_traitors - 1) * 10) * 5 + if(traitor_count < max_traitors - 1) + traitor_prob += 50 + if(traitor_count < max_traitors) + if(prob(traitor_prob)) + message_admins("Making a new Traitor.") + if(!length(possible_traitors)) + message_admins("No potential traitors. Cancelling new traitor.") + addtimer(CALLBACK(src, PROC_REF(traitor_check_loop)), 15 MINUTES) + return + var/datum/mind/new_traitor_mind = pick(possible_traitors) + var/mob/living/new_traitor = new_traitor_mind.current - if(traitorcount < max_traitors) - //message_admins("Number of Traitors is below maximum. Rolling for new Traitor.") - //message_admins("The probability of a new traitor is [traitor_prob]%") - - if(prob(traitor_prob)) - message_admins("Making a new Traitor.") - if(!length(possible_traitors)) - message_admins("No potential traitors. Cancelling new traitor.") - traitorcheckloop() - return - var/datum/mind/newtraitormind = pick(possible_traitors) - var/mob/living/newtraitor = newtraitormind.current - //message_admins("[newtraitor.real_name] is the new Traitor.") - - to_chat(newtraitor, "ATTENTION: It is time to pay your debt to the Syndicate...") - newtraitor.mind.add_antag_datum(/datum/antagonist/traitor) - //else - //message_admins("No new traitor being added.") - //else - //message_admins("Number of Traitors is at maximum. Not making a new Traitor.") - - traitorcheckloop() - + to_chat(new_traitor, "ATTENTION: It is time to pay your debt to the Syndicate...") + new_traitor.mind.add_antag_datum(/datum/antagonist/traitor) + addtimer(CALLBACK(src, PROC_REF(traitor_check_loop)), 15 MINUTES) /datum/game_mode/traitor/autotraitor/latespawn(mob/living/carbon/human/character) ..() if(SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) return - //message_admins("Late Join Check") + if(character.client && (ROLE_TRAITOR in character.client.prefs.be_special) && !character.client.skip_antag && !jobban_isbanned(character, ROLE_TRAITOR) && !jobban_isbanned(character, ROLE_SYNDICATE)) - //message_admins("Late Joiner has Be Syndicate") - //message_admins("Checking number of players") - var/playercount = 0 - var/traitorcount = 0 + var/player_count = 0 + var/traitor_count = 0 for(var/mob/living/player in GLOB.mob_list) if(player.client && player.stat != DEAD) - playercount += 1 + player_count += 1 if(player.mind && player.mind.special_role) - traitorcount += 1 - //message_admins("Live Players: [playercount]") - //message_admins("Live Traitors: [traitorcount]") + traitor_count += 1 - //var/r = rand(5) - //var/target_traitors = 1 var/max_traitors = 2 var/traitor_prob = 0 - max_traitors = round(playercount / 10) + 1 - traitor_prob = (playercount - (max_traitors - 1) * 10) * 5 - if(traitorcount < max_traitors - 1) + max_traitors = round(player_count / 10) + 1 + traitor_prob = (player_count - (max_traitors - 1) * 10) * 5 + if(traitor_count < max_traitors - 1) traitor_prob += 50 - //target_traitors = max(1, min(round((playercount + r) / 10, 1), traitors_possible)) - //message_admins("Target Traitor Count is: [target_traitors]") - if(traitorcount < max_traitors) + if(traitor_count < max_traitors) for(var/job in restricted_jobs) if(character.mind.assigned_role == job || !ishuman(character)) return - //message_admins("Number of Traitors is below maximum. Rolling for New Arrival Traitor.") - //message_admins("The probability of a new traitor is [traitor_prob]%") + if(prob(traitor_prob)) - message_admins("New traitor roll passed. Making a new Traitor.") - character.mind.make_Traitor() //TEMP: Add proper checks for loyalty here. uc_guy - //else - //message_admins("New traitor roll failed. No new traitor.") - //else - //message_admins("Late Joiner does not have Be Syndicate") + message_admins("New traitor roll passed. Making a new Traitor.") + character.mind.make_Traitor() diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 071cf52415f..d440e7624d3 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -398,7 +398,7 @@ /obj/machinery/camera/get_remote_view_fullscreens(mob/user) if(view_range == short_range) //unfocused - user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2) + user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/stretch/impaired, 2) /obj/machinery/camera/update_remote_sight(mob/living/user) if(isXRay() && isAI(user)) diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 8c5a22d2544..ee045ecb935 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -208,7 +208,7 @@ if(final) playsound(origin, 'sound/machines/terminal_prompt_confirm.ogg', 25, FALSE) remote_eye.setLoc(get_turf(final)) - C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/flash/noise) + C.overlay_fullscreen("flash", /atom/movable/screen/fullscreen/stretch/flash/noise) C.clear_fullscreen("flash", 3) //Shorter flash than normal since it's an ~~advanced~~ console! else playsound(origin, 'sound/machines/terminal_prompt_deny.ogg', 25, FALSE) diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index e5d1d70d4a4..59e1a926d84 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -35,6 +35,28 @@ Possible to do for anyone motivated enough: GLOBAL_LIST_EMPTY(holopads) +/** + * A stationary holopad for projecting hologram and making and receiving holocalls. + * + * Holopads are floor-plane machines similar, in appearance and interactive function, to quantum pads. They can be + * used by crew members to make and answer holocalls, or by the AI to project holograms autonomously. + * + * Holopads are machines which can project a hologram up to `holo_range` tiles away. They do this in one of two modes: + * holocalls, and AI holograms. Holocalls require a user to stand on a holopad, use its menu to select a remote holopad, + * and make a call. Holocalls must be answered by the receiving holopad, or they will fail. Holopads can be configured + * globally to auto-accept instead of failing, with the debug static variable `force_answer_call`. They can also be + * individually configured to auto-accept calls immediately by setting their public mode option (`public_mode`), + * which can be done in-game by using a screwdriver and then a multitool on a holopad. Holocalls will automatically end + * if power goes out, the caller moves off the calling holopad, the caller projects outside the receiving holopad's + * range, the caller is killed or incapacitated, the caller ghosts or disconnects, or the caller is QDELETED. AI + * holograms require the AI to focus on the originating holopad and click on it; no answer is required. If the AI moves + * out of a holopad's range and into the range of another, it will attempt to transfer its hologram to the next holopad. + * Otherwise, AI holograms will stop projecting if power goes out for the originating holopad, the AI clicks on its + * origin holopad, or, like holocalls, if the AI is killed, incapacitated, disconnected, QDELETED, or ghosts. + * Holopads relay speech from the caller made with the :h radio key, and relay all speech on the answering end to the + * caller or AI. + */ + /obj/machinery/hologram/holopad name = "holopad" desc = "It's a floor-mounted device for projecting holographic images." @@ -47,17 +69,29 @@ GLOBAL_LIST_EMPTY(holopads) max_integrity = 300 armor = list(melee = 50, bullet = 20, laser = 20, energy = 20, bomb = 0, rad = 0, fire = 50, acid = 0) - var/list/masters = list()//List of living mobs that use the holopad - var/list/holorays = list()//Holoray-mob link. - var/last_request = 0 //to prevent request spam. ~Carn - var/holo_range = 5 // Change to change how far the AI can move away from the holopad before deactivating. + /// List of living mobs currently using the holopad + var/list/masters = list() + /// Holoray-mob link. + var/list/holorays = list() + /// Last request time, to prevent request spam. ~Carn + var/last_request = 0 + /// The range, in tiles, that a holopad can project a hologram. + var/holo_range = 5 var/temp = "" - var/list/holo_calls //array of /datum/holocalls - var/datum/holocall/outgoing_call //do not modify the datums only check and call the public procs - var/static/force_answer_call = FALSE //Calls will be automatically answered after a couple rings, here for debugging + /// A list of holocalls associated with this holopad. + var/list/holo_calls + /// The outgoing holocall currently being processed by this holopad. + var/datum/holocall/outgoing_call + /// Universal debug toggle for whether holopads will automatically answer calls after a few rings. + var/static/force_answer_call = FALSE + /// Toggle for auto-answering calls immediately, set via multitool. + var/public_mode = FALSE + /// The ray effect emanating from this holopad to the produced hologram. var/obj/effect/overlay/holoray/ray + /// Whether or not this holopad is currently ringing, from being called by another pad. var/ringing = FALSE - var/dialling_input = FALSE //The user is currently selecting where to send their call + /// Whether or not the user is currently selecting where to send their call. + var/dialling_input = FALSE /obj/machinery/hologram/holopad/Initialize(mapload) . = ..() @@ -120,6 +154,13 @@ GLOBAL_LIST_EMPTY(holopads) return return ..() +/obj/machinery/hologram/holopad/multitool_act(mob/living/user, obj/item/I) + . = TRUE + if(!I.use_tool(src, user, 0, volume = I.tool_volume)) + return + public_mode = !public_mode + to_chat(user, "You [public_mode ? "enable" : "disable"] the holopad's public mode setting.") + /obj/machinery/hologram/holopad/screwdriver_act(mob/user, obj/item/I) . = TRUE default_deconstruction_screwdriver(user, "holopad_open", "holopad0", I) @@ -143,6 +184,11 @@ GLOBAL_LIST_EMPTY(holopads) user.set_machine(src) interact(user) +/obj/machinery/hologram/holopad/examine(mob/user) + . = ..() + . += "Its maintenance panel can be screwed [panel_open ? "closed" : "open"]." + . += "Its public mode indicator reads [public_mode ? "on" : "off"]. It can be turned [public_mode ? "off" : "on"] by using a multitool while the maintenance panel is open." + /obj/machinery/hologram/holopad/AltClick(mob/living/carbon/human/user) if(..()) return @@ -300,7 +346,7 @@ GLOBAL_LIST_EMPTY(holopads) continue if(HC.connected_holopad != src) - if(force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2))) + if((force_answer_call && world.time > (HC.call_start_time + (HOLOPAD_MAX_DIAL_TIME / 2))) || public_mode) HC.Answer(src) break if(outgoing_call) diff --git a/code/game/objects/items/weapons/RCD.dm b/code/game/objects/items/weapons/RCD.dm index 1457413d046..481ca66e692 100644 --- a/code/game/objects/items/weapons/RCD.dm +++ b/code/game/objects/items/weapons/RCD.dm @@ -1,7 +1,3 @@ - -#define MATTER_100 100 -#define MATTER_500 500 - #define TAB_AIRLOCK_TYPE 1 #define TAB_AIRLOCK_ACCESS 2 @@ -10,6 +6,204 @@ #define MODE_WINDOW "Windows" #define MODE_DECON "Deconstruction" +/// A generic action for an RCD. +/datum/rcd_act + /// How much compressed matter this action costs. + var/cost = 0 + /// How long this action takes. + var/delay = 0 + /// The message (if any) to send the user when the action starts. + var/start_message + /// The effect (if any) to create when the action starts. + var/obj/effect/start_effect_type + /// The effect (if any) to create when the action completes. + var/obj/effect/end_effect_type + /// The mode the RCD must be in. + var/mode + +/// Attempt the action. This should not need to be overridden. +/datum/rcd_act/proc/try_act(atom/A, obj/item/rcd/rcd, mob/user) + if(!can_act(A, rcd)) + return FALSE + // We don't use the sound effect from use_tool because RCDs have a different sound effect for the start and end. + playsound(get_turf(rcd), 'sound/machines/click.ogg', 50, TRUE) + if(!rcd.tool_use_check(user, cost)) + return FALSE + if(start_message) + to_chat(user, start_message) + var/obj/effect/start_effect + if(start_effect_type) + start_effect = new start_effect_type(get_turf(A)) + if(!rcd.use_tool(A, user, delay, cost)) + if(!QDELETED(start_effect)) + qdel(start_effect) + return FALSE + if(start_effect) + qdel(start_effect) + // If time elapsed, check our preconditions again. + if(delay && !can_act(A, rcd)) + return FALSE + if(end_effect_type) + new end_effect_type(get_turf(A)) + playsound(get_turf(rcd), 'sound/items/deconstruct.ogg', 50, TRUE) + act(A, rcd, user) + return TRUE + +/// Test to see if the act is possible. You should usually override this. +/datum/rcd_act/proc/can_act(atom/A, obj/item/rcd/rcd) + SHOULD_CALL_PARENT(TRUE) + return rcd.mode == mode + +/// Perform the act. You should usually override this. +/datum/rcd_act/proc/act(atom/A, obj/item/rcd/rcd, mob/user) + return + +/datum/rcd_act/place_floor + mode = MODE_TURF + cost = 1 + start_message = "Building floor..." + end_effect_type = /obj/effect/temp_visual/rcd_effect/end + +/datum/rcd_act/place_floor/can_act(atom/A, obj/item/rcd/rcd) + if(!..()) + return FALSE + return isspaceturf(A) || istype(A, /obj/structure/lattice) + +/datum/rcd_act/place_floor/act(atom/A, obj/item/rcd/rcd, mob/user) + var/turf/act_on = get_turf(A) + act_on.ChangeTurf(/turf/simulated/floor/plating) + +/datum/rcd_act/place_wall + mode = MODE_TURF + cost = 3 + start_message = "Building wall..." + delay = 2 SECONDS + start_effect_type = /obj/effect/temp_visual/rcd_effect/short + end_effect_type = /obj/effect/temp_visual/rcd_effect/end + +/datum/rcd_act/place_wall/can_act(atom/A, obj/item/rcd/rcd) + if(!..()) + return FALSE + return isfloorturf(A) + +/datum/rcd_act/place_wall/act(atom/A, obj/item/rcd/rcd, mob/user) + var/turf/act_on = get_turf(A) + act_on.ChangeTurf(/turf/simulated/wall) + +/datum/rcd_act/place_airlock + mode = MODE_AIRLOCK + cost = 10 + start_message = "Building airlock..." + delay = 5 SECONDS + start_effect_type = /obj/effect/temp_visual/rcd_effect + end_effect_type = /obj/effect/temp_visual/rcd_effect/end + +/datum/rcd_act/place_airlock/can_act(atom/A, obj/item/rcd/rcd) + if(!..()) + return FALSE + return isfloorturf(A) && !(/obj/machinery/door/airlock in A.contents) + +/datum/rcd_act/place_airlock/act(atom/A, obj/item/rcd/rcd, mob/user) + var/obj/machinery/door/airlock/T = new rcd.door_type(A) + if(T.glass) + T.polarized_glass = rcd.electrochromic + T.name = rcd.door_name + T.autoclose = TRUE + if(rcd.one_access) + T.req_one_access = rcd.selected_accesses.Copy() + else + T.req_access = rcd.selected_accesses.Copy() + +/datum/rcd_act/place_window + mode = MODE_WINDOW + cost = 2 + start_message = "Building window..." + delay = 2 SECONDS + start_effect_type = /obj/effect/temp_visual/rcd_effect/short + end_effect_type = /obj/effect/temp_visual/rcd_effect/end + +/datum/rcd_act/place_window/can_act(atom/A, obj/item/rcd/rcd) + if(!..()) + return FALSE + return isfloorturf(A) && !(/obj/structure/grille in A.contents) + +/datum/rcd_act/place_window/act(atom/A, obj/item/rcd/rcd, mob/user) + var/turf/act_on= A + for(var/obj/structure/window/window_to_delete in act_on) + qdel(window_to_delete) + new /obj/structure/grille(act_on) + new /obj/structure/window/full/reinforced(act_on) + act_on.ChangeTurf(/turf/simulated/floor/plating) // Platings go under windows. + +/datum/rcd_act/remove_floor + mode = MODE_DECON + cost = 5 + start_message = "Deconstructing floor..." + delay = 5 SECONDS + start_effect_type = /obj/effect/temp_visual/rcd_effect/reverse + +/datum/rcd_act/remove_floor/can_act(atom/A, obj/item/rcd/rcd) + if(!..()) + return FALSE + return isfloorturf(A) + +/datum/rcd_act/remove_floor/act(atom/A, obj/item/rcd/rcd, mob/user) + var/turf/act_on = get_turf(A) + act_on.ChangeTurf(act_on.baseturf) + +/datum/rcd_act/remove_wall + mode = MODE_DECON + cost = 5 + start_message = "Deonstructing wall..." + delay = 5 SECONDS + start_effect_type = /obj/effect/temp_visual/rcd_effect/reverse + +/datum/rcd_act/remove_wall/can_act(atom/A, obj/item/rcd/rcd) + if(!..()) + return FALSE + if(isreinforcedwallturf(A) && !rcd.canRwall) + return FALSE + if(istype(A, /turf/simulated/wall/indestructible)) + return FALSE + return iswallturf(A) + +/datum/rcd_act/remove_wall/act(atom/A, obj/item/rcd/rcd, mob/user) + var/turf/act_on = get_turf(A) + act_on.ChangeTurf(/turf/simulated/floor/plating) + +/datum/rcd_act/remove_airlock + mode = MODE_DECON + cost = 20 + start_message = "Deconstructing airlock..." + delay = 5 SECONDS + start_effect_type = /obj/effect/temp_visual/rcd_effect/reverse + +/datum/rcd_act/remove_airlock/can_act(atom/A, obj/item/rcd/rcd) + if(!..()) + return FALSE + return istype(A, /obj/machinery/door/airlock) + +/datum/rcd_act/remove_airlock/act(atom/A, obj/item/rcd/rcd, mob/user) + qdel(A) + +/datum/rcd_act/remove_window + mode = MODE_DECON + cost = 2 + start_message = "Deconstructing window..." + delay = 2 SECONDS + start_effect_type = /obj/effect/temp_visual/rcd_effect/reverse_short + +/datum/rcd_act/remove_window/can_act(atom/A, obj/item/rcd/rcd) + if(!..()) + return FALSE + return istype(A, /obj/structure/window) + +/datum/rcd_act/remove_window/act(atom/A, obj/item/rcd/rcd, mob/user) + var/turf/act_on = get_turf(A) + qdel(A) + for(var/obj/structure/grille/grill_to_destroy in act_on) + qdel(grill_to_destroy) + /obj/item/rcd name = "rapid-construction-device (RCD)" desc = "A device used to rapidly build and deconstruct walls, floors and airlocks." @@ -25,7 +219,6 @@ materials = list(MAT_METAL = 30000) origin_tech = "engineering=4;materials=2" toolspeed = 1 - usesound = 'sound/items/deconstruct.ogg' flags_2 = NO_MAT_REDEMPTION_2 req_access = list(ACCESS_ENGINE) armor = list(MELEE = 0, BULLET = 0, LASER = 0, ENERGY = 0, BOMB = 0, RAD = 0, FIRE = 100, ACID = 50) @@ -37,7 +230,7 @@ /// The current amount of matter stored. var/matter = NONE /// The max amount of matter that can be stored. - var/max_matter = MATTER_100 + var/max_matter = 100 /// The RCD's current build mode. var/mode = MODE_TURF /// If the RCD can deconstruct reinforced walls. @@ -65,9 +258,16 @@ var/static/list/door_types_ui_list = list() /// An associative list containing all station accesses. Includes their name and access number. For use with the UI. var/static/list/door_accesses_list = list() + /// The list of potential RCD actions. + var/static/list/possible_actions /obj/item/rcd/Initialize() . = ..() + if(!length(possible_actions)) + possible_actions = list() + for(var/action_type in subtypesof(/datum/rcd_act)) + possible_actions += new action_type() + spark_system = new /datum/effect_system/spark_spread spark_system.set_up(5, 0, src) spark_system.attach(src) @@ -364,214 +564,6 @@ else return FALSE -/** - * Called in `afterattack()` if `mode` is set to `MODE_TURF`. - * - * Creates either a plating, or a wall, depending on the turf that already exists at the location. - * - * Arguments: - * * A - the location we're trying to build at. - * * user - the mob using the RCD. - */ -/obj/item/rcd/proc/mode_turf(atom/A, mob/user) - if(isspaceturf(A) || istype(A, /obj/structure/lattice)) - if(useResource(1, user)) - to_chat(user, "Building Floor...") - playsound(loc, usesound, 50, 1) - var/turf/AT = get_turf(A) - new/obj/effect/temp_visual/rcd_effect/end(get_turf(A)) - AT.ChangeTurf(/turf/simulated/floor/plating) - return TRUE - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - - if(isfloorturf(A)) - if(checkResource(3, user)) - to_chat(user, "Building Wall...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - var/obj/effect/temp_visual/rcd_effect/short/E = new(get_turf(A)) - if(do_after(user, 2 SECONDS * toolspeed, target = A)) - if(!isfloorturf(A)) - return FALSE - if(!useResource(3, user)) - return FALSE - playsound(loc, usesound, 50, 1) - var/turf/AT = A - new/obj/effect/temp_visual/rcd_effect/end(get_turf(A)) - AT.ChangeTurf(/turf/simulated/wall) - return TRUE - qdel(E) - return FALSE - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - to_chat(user, "ERROR! Location unsuitable for wall construction!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - -/** - * Called in `afterattack()` if `mode` is set to `MODE_AIRLOCK`. - * - * Creates an `door_type` airlock at the given location `A`, and assigns it accesses from `selected_accesses`. - * - * Arguments: - * * A - the location we're trying to build at. - * * user - the mob using the RCD. - */ -/obj/item/rcd/proc/mode_airlock(atom/A, mob/user) - if(isfloorturf(A)) - if(checkResource(10, user)) - to_chat(user, "Building Airlock...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - var/obj/effect/temp_visual/rcd_effect/E = new(get_turf(A)) - if(do_after(user, 5 SECONDS * toolspeed, target = A)) - if(locate(/obj/machinery/door/airlock) in A.contents) - return FALSE - if(!useResource(10, user)) - return FALSE - playsound(loc, usesound, 50, 1) - new/obj/effect/temp_visual/rcd_effect/end(get_turf(A)) - var/obj/machinery/door/airlock/T = new door_type(A) - if(T.glass) - T.polarized_glass = electrochromic - T.name = door_name - T.autoclose = TRUE - if(one_access) - T.req_one_access = selected_accesses.Copy() - else - T.req_access = selected_accesses.Copy() - return FALSE - qdel(E) - return FALSE - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - to_chat(user, "ERROR! Location unsuitable for airlock construction!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - -/** - * Called in `afterattack()` if `mode` is set to `MODE_DECON`. - * - * Deconstrcts the target atom `A`. - * Valid atoms are: basic walls, reinforced walls (if `canRwall` is `TRUE`), airlocks, and windows. - * - * Arguments: - * * A - the location we're trying to build at. - * * user - the mob using the RCD. - */ -/obj/item/rcd/proc/mode_decon(atom/A, mob/user) - if(iswallturf(A)) - if(isreinforcedwallturf(A) && !canRwall) - return FALSE - if(istype(A, /turf/simulated/wall/indestructible)) - return FALSE - if(checkResource(5, user)) - to_chat(user, "Deconstructing Wall...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - var/obj/effect/temp_visual/rcd_effect/reverse/E = new(get_turf(A)) - if(do_after(user, 5 SECONDS * toolspeed, target = A)) - if(!useResource(5, user)) - return FALSE - playsound(loc, usesound, 50, 1) - var/turf/AT = A - AT.ChangeTurf(/turf/simulated/floor/plating) - return TRUE - qdel(E) - return FALSE - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - - if(isfloorturf(A)) - if(checkResource(5, user)) - to_chat(user, "Deconstructing Floor...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - var/obj/effect/temp_visual/rcd_effect/reverse/E = new(get_turf(A)) - if(do_after(user, 5 SECONDS * toolspeed, target = A)) - if(!useResource(5, user)) - return FALSE - playsound(loc, usesound, 50, 1) - var/turf/AT = A - AT.ChangeTurf(AT.baseturf) - return TRUE - qdel(E) - return FALSE - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - - if(istype(A, /obj/machinery/door/airlock)) - if(checkResource(20, user)) - to_chat(user, "Deconstructing Airlock...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - var/obj/effect/temp_visual/rcd_effect/reverse/E = new(get_turf(A)) - if(do_after(user, 5 SECONDS * toolspeed, target = A)) - if(!useResource(20, user)) - return FALSE - playsound(loc, usesound, 50, 1) - qdel(A) - return TRUE - qdel(E) - return FALSE - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - - if(istype(A, /obj/structure/window)) - if(!checkResource(2, user)) - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - to_chat(user, "Deconstructing window...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - var/obj/effect/temp_visual/rcd_effect/reverse_short/E = new(get_turf(A)) - if(!do_after(user, 20 * toolspeed, target = A)) - qdel(E) - return FALSE - if(!useResource(2, user)) - return FALSE - playsound(loc, usesound, 50, 1) - var/turf/T1 = get_turf(A) - QDEL_NULL(A) - for(var/obj/structure/grille/G in T1.contents) - qdel(G) - return TRUE - return FALSE - -/** - * Called in `afterattack()` if `mode` is set to `MODE_WINDOW`. - * - * Constructs a grille and 4 reinforced window panes at the given location `A`. - * - * Arguments: - * * A - the location we're trying to build at. - * * user - the mob using the RCD. - */ -/obj/item/rcd/proc/mode_window(atom/A, mob/user) - if(isfloorturf(A)) - if(locate(/obj/structure/grille) in A) - return FALSE // We already have window - if(!checkResource(2, user)) - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - to_chat(user, "Constructing window...") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - var/obj/effect/temp_visual/rcd_effect/short/E = new(get_turf(A)) - if(!do_after(user, 20 * toolspeed, target = A)) - qdel(E) - return FALSE - if(locate(/obj/structure/grille) in A) - return FALSE // We already have window - if(!useResource(2, user)) - return FALSE - playsound(loc, usesound, 50, 1) - new/obj/effect/temp_visual/rcd_effect/end(get_turf(A)) - new /obj/structure/grille(A) - for(var/obj/structure/window/W in A) - qdel(W) - new /obj/structure/window/full/reinforced(A) - var/turf/AT = A - AT.ChangeTurf(/turf/simulated/floor/plating) // Platings go under windows. - return TRUE - to_chat(user, "ERROR! Location unsuitable for window construction!") - playsound(loc, 'sound/machines/click.ogg', 50, 1) - return FALSE - /obj/item/rcd/afterattack(atom/A, mob/user, proximity) if(!proximity) return FALSE @@ -580,31 +572,29 @@ if(!is_type_in_list(A, allowed_targets)) return FALSE - switch(mode) - if(MODE_TURF) - . = mode_turf(A, user) - if(MODE_AIRLOCK) - . = mode_airlock(A, user) - if(MODE_DECON) - . = mode_decon(A, user) - if(MODE_WINDOW) - . = mode_window(A, user) - else - to_chat(user, "ERROR: RCD in MODE: [mode] attempted use by [user]. Send this text #coderbus or an admin.") - . = 0 + for(var/datum/rcd_act/act in possible_actions) + if(act.can_act(A, src)) + . = act.try_act(A, src, user) + update_icon(UPDATE_OVERLAYS) + SStgui.update_uis(src) + return + + if(mode == MODE_DECON) + to_chat(user, "You can't deconstruct that!") + else + to_chat(user, "Location unsuitable for construction.") update_icon(UPDATE_OVERLAYS) SStgui.update_uis(src) + return FALSE /** - * Called in each of the four build modes after an object is successfully built. - * - * Subtracts the amount of matter used from `matter`. + * Attempts to use matter from the RCD. * * Arguments: - * * amount - the amount of matter that was used. + * * amount - the amount of matter to use */ -/obj/item/rcd/proc/useResource(amount, mob/user) +/obj/item/rcd/use(amount) if(matter < amount) return FALSE matter -= amount @@ -617,7 +607,7 @@ * Arguments: * * amount - an amount of matter to check for */ -/obj/item/rcd/proc/checkResource(amount, mob/user) +/obj/item/rcd/tool_use_check(mob/user, amount) . = matter >= amount if(!. && user) to_chat(user, no_ammo_message) @@ -639,13 +629,13 @@ /obj/item/rcd/borg/syndicate power_use_multiplier = 80 -/obj/item/rcd/borg/useResource(amount, mob/user) - if(!isrobot(user)) +/obj/item/rcd/borg/use(amount) + var/mob/living/silicon/robot/R = usr + if(!istype(R)) return FALSE - var/mob/living/silicon/robot/R = user return R.cell.use(amount * power_use_multiplier) -/obj/item/rcd/borg/checkResource(amount, mob/user) +/obj/item/rcd/borg/tool_use_check(mob/user, amount) if(!isrobot(user)) return FALSE var/mob/living/silicon/robot/R = user @@ -675,8 +665,8 @@ name = "combat RCD" icon_state = "crcd" item_state = "crcd" - max_matter = MATTER_500 - matter = MATTER_500 + max_matter = 500 + matter = 500 canRwall = TRUE /obj/item/rcd_ammo @@ -701,9 +691,6 @@ /obj/item/rcd_ammo/large ammoamt = 100 -#undef MATTER_100 -#undef MATTER_500 - #undef TAB_AIRLOCK_TYPE #undef TAB_AIRLOCK_ACCESS #undef MODE_TURF diff --git a/code/game/objects/items/weapons/alien_specific.dm b/code/game/objects/items/weapons/alien_specific.dm index 2b57806d0e0..ed58a1f0a5f 100644 --- a/code/game/objects/items/weapons/alien_specific.dm +++ b/code/game/objects/items/weapons/alien_specific.dm @@ -19,14 +19,14 @@ //Bottles for borg liquid squirters. PSSH PSSH /obj/item/reagent_containers/spray/alien - name = "liquid synthesizer" - desc = "squirts alien liquids." + name = "generic chemical synthesizer" + desc = "If you can see this, make an issue report on GitHub. Something has gone wrong!" icon = 'icons/mob/alien.dmi' icon_state = "borg-default" /obj/item/reagent_containers/spray/alien/smoke - name = "smoke synthesizer" - desc = "squirts smokey liquids." + name = "smokescreen module" + desc = "Releases a dense smoke cloud that cannot be seen through. Your thermal vision is still able to see targets through it." icon = 'icons/mob/alien.dmi' icon_state = "borg-spray-smoke" list_reagents = list("water" = 50) @@ -50,8 +50,8 @@ reagents.check_and_add("water", volume, 2 * coeff) /obj/item/reagent_containers/spray/alien/acid - name = "acid synthesizer" - desc = "squirts burny liquids." + name = "polyacid synthesizer" + desc = "Sprays concentrated polyacid." icon = 'icons/mob/alien.dmi' icon_state = "borg-spray-acid" list_reagents = list("facid" = 125, "sacid" = 125) @@ -60,20 +60,14 @@ reagents.check_and_add("facid", volume / 2, 2 * coeff) // Volume / 2 here becuase there should be an even amount of both chems. reagents.check_and_add("sacid", volume / 2, 2 * coeff) -/obj/item/reagent_containers/spray/alien/stun - name = "paralytic toxin synthesizer" - desc = "squirts ether." - icon = 'icons/mob/alien.dmi' - icon_state = "borg-spray-stun" - list_reagents = list("ether" = 250) - -/obj/item/reagent_containers/spray/alien/stun/cyborg_recharge(coeff, emagged) - reagents.check_and_add("ether", volume, 2 * coeff) - -//SKREEEEEEEEEEEE tool - /obj/item/flash/cyborg/alien name = "eye flash" desc = "Useful for taking pictures, making friends and flash-frying chips." icon = 'icons/mob/alien.dmi' icon_state = "borg-flash" + +/obj/item/flash/cyborg/alien/cyborg_recharge(coeff, emagged) + if(broken) + broken = FALSE + times_used = 0 + icon_state = "borg-flash" diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 3dde46e83a2..151e2c22692 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -374,7 +374,7 @@ /obj/structure/closet/get_remote_view_fullscreens(mob/user) if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1) + user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/stretch/impaired, 1) /obj/structure/closet/ex_act(severity) for(var/atom/A in contents) diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 5d80c18fe1a..194ad6dd440 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -241,7 +241,7 @@ /obj/structure/morgue/get_remote_view_fullscreens(mob/user) if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2) + user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/stretch/impaired, 2) /* * Morgue tray @@ -534,7 +534,7 @@ GLOBAL_LIST_EMPTY(crematoriums) /obj/structure/crematorium/get_remote_view_fullscreens(mob/user) if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2) + user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/stretch/impaired, 2) /* * Crematorium tray diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index bb77fd7f3e4..aedb6c68765 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -359,13 +359,13 @@ GLOBAL_LIST_EMPTY(safes) drill.song.start_playing(driller) notify_ghosts("Security assault in progress in [get_area(src)]!", enter_link="(Click to jump to!)", source = src, action = NOTIFY_FOLLOW) for(var/mob/dead/observer/O in GLOB.player_list) - O.overlay_fullscreen("payback", /atom/movable/screen/fullscreen/payback, 0) + O.overlay_fullscreen("payback", /atom/movable/screen/fullscreen/stretch/payback, 0) addtimer(CALLBACK(src, PROC_REF(ghost_payback_phase_2)), 2.7 SECONDS) /obj/structure/safe/proc/ghost_payback_phase_2() for(var/mob/dead/observer/O in GLOB.player_list) O.clear_fullscreen("payback") - O.overlay_fullscreen("payback", /atom/movable/screen/fullscreen/payback, 1) + O.overlay_fullscreen("payback", /atom/movable/screen/fullscreen/stretch/payback, 1) addtimer(CALLBACK(src, PROC_REF(clear_payback)), 2 MINUTES) /obj/structure/safe/proc/clear_payback() diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 2f89291bc38..b945dca68e4 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -2149,18 +2149,20 @@ if(ismindshielded(player.current)) possible_traitors -= player if(length(possible_traitors)) - var/datum/mind/newtraitormind = pick(possible_traitors) + var/datum/mind/new_traitor_mind = pick(possible_traitors) var/datum/objective/assassinate/kill_objective = new() kill_objective.target = H.mind kill_objective.explanation_text = "Assassinate [H.mind.name], the [H.mind.assigned_role]" - newtraitormind.add_mind_objective(kill_objective) + new_traitor_mind.add_mind_objective(kill_objective) var/datum/antagonist/traitor/T = new() T.give_objectives = FALSE - to_chat(newtraitormind.current, "ATTENTION: It is time to pay your debt to the Syndicate...") - to_chat(newtraitormind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") - newtraitormind.add_antag_datum(T) + to_chat(new_traitor_mind.current, "ATTENTION: It is time to pay your debt to the Syndicate...") + to_chat(new_traitor_mind.current, "Goal: KILL [H.real_name], currently in [get_area(H.loc)]") + new_traitor_mind.add_antag_datum(T) + message_admins("[key_name_admin(new_traitor_mind)] was chosen to be the traitor for a smite!") + log_admin("[key_name(new_traitor_mind)] was made into a traitor to hunt [key_name(H)] for 'Crew Traitor' smite.") else to_chat(usr, "ERROR: Unable to find any valid candidate to send after [H].") return diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 4e696bc6025..0fac7c4668c 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -482,7 +482,7 @@ return /obj/machinery/atmospherics/unary/cryo_cell/get_remote_view_fullscreens(mob/user) - user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1) + user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/stretch/impaired, 1) /obj/machinery/atmospherics/unary/cryo_cell/update_remote_sight(mob/living/user) return //we don't see the pipe network while inside cryo. diff --git a/code/modules/food_and_drinks/drinks/bottler/bottler.dm b/code/modules/food_and_drinks/drinks/bottler/bottler.dm index d9c7654729b..13c709e557d 100644 --- a/code/modules/food_and_drinks/drinks/bottler/bottler.dm +++ b/code/modules/food_and_drinks/drinks/bottler/bottler.dm @@ -270,7 +270,7 @@ //bad recipe, ruins the drink var/contents = pick("thick goop", "pungent sludge", "unspeakable slurry", "gross-looking concoction", "eldritch abomination of liquids") visible_message("The [con_type] fills with \an [contents]...") - drink_container.reagents.add_reagent(pick("????", "toxic_slurry", "meatslurry", "glowing_slury", "fishwater"), pick(30, 50)) + drink_container.reagents.add_reagent(pick("????", "toxic_slurry", "meatslurry", "glowing_slurry", "fishwater"), pick(30, 50)) drink_container.name = "Liquid Mistakes" drink_container.desc = "WARNING: CONTENTS MAY BE AWFUL, DRINK AT OWN RISK." else diff --git a/code/modules/hallucinations/effects/moderate.dm b/code/modules/hallucinations/effects/moderate.dm index 9e197e3ed68..b381a82d4a4 100644 --- a/code/modules/hallucinations/effects/moderate.dm +++ b/code/modules/hallucinations/effects/moderate.dm @@ -234,7 +234,7 @@ /obj/effect/hallucination/delamination_alarm/Initialize(mapload, mob/living/carbon/target) . = ..() target.playsound_local(target, 'sound/machines/engine_alert2.ogg', 25, FALSE, 30, 30) - target.hear_radio(message_to_multilingual("Danger! Crystal hyperstructure integrity faltering! Integrity: [rand(30, 50)]%"), vname = "supermatter crystal", part_a = "\[[get_frequency_name(PUB_FREQ)]\] ", part_b = " ") + target.hear_radio(message_to_multilingual("Danger! Crystal hyperstructure integrity faltering! Integrity: [rand(30, 50)]%"), vname = "supermatter crystal", part_a = "\[[get_frequency_name(PUB_FREQ)]\] ", part_b = " ") /** * # Hallucination - Plasma Flood diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid_update_icons.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid_update_icons.dm index 5732c200ee6..ac13fd82732 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid_update_icons.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid_update_icons.dm @@ -72,7 +72,7 @@ inv.update_icon() if(wear_suit) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) //if the inventory is open ... wear_suit.screen_loc = ui_oclothing //TODO //...draw the item in the inventory screen client.screen += wear_suit //Either way, add the item to the HUD diff --git a/code/modules/mob/living/carbon/carbon_life.dm b/code/modules/mob/living/carbon/carbon_life.dm index 4f393cbe8ee..381443e3e9a 100644 --- a/code/modules/mob/living/carbon/carbon_life.dm +++ b/code/modules/mob/living/carbon/carbon_life.dm @@ -319,7 +319,7 @@ severity = 9 if(-INFINITY to -95) severity = 10 - overlay_fullscreen("crit", /atom/movable/screen/fullscreen/crit, severity) + overlay_fullscreen("crit", /atom/movable/screen/fullscreen/stretch/crit, severity) else if(stat == CONSCIOUS) if(check_death_method()) clear_fullscreen("crit") @@ -340,7 +340,7 @@ severity = 6 if(45 to INFINITY) severity = 7 - overlay_fullscreen("oxy", /atom/movable/screen/fullscreen/oxy, severity) + overlay_fullscreen("oxy", /atom/movable/screen/fullscreen/stretch/oxy, severity) else clear_fullscreen("oxy") @@ -356,7 +356,7 @@ if(45 to 70) severity = 4 if(70 to 85) severity = 5 if(85 to INFINITY) severity = 6 - overlay_fullscreen("brute", /atom/movable/screen/fullscreen/brute, severity) + overlay_fullscreen("brute", /atom/movable/screen/fullscreen/stretch/brute, severity) else clear_fullscreen("brute") diff --git a/code/modules/mob/living/carbon/carbon_procs.dm b/code/modules/mob/living/carbon/carbon_procs.dm index 1807cdb3533..6cbdbd9d46f 100644 --- a/code/modules/mob/living/carbon/carbon_procs.dm +++ b/code/modules/mob/living/carbon/carbon_procs.dm @@ -353,7 +353,7 @@ if((E && (E.status & ORGAN_DEAD)) || !.) return FALSE -/mob/living/carbon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, laser_pointer = FALSE, type = /atom/movable/screen/fullscreen/flash) +/mob/living/carbon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, laser_pointer = FALSE, type = /atom/movable/screen/fullscreen/stretch/flash) //Parent proc checks if a mob can_be_flashed() . = ..() @@ -1201,9 +1201,9 @@ so that different stomachs can handle things in different ways VB*/ /mob/living/carbon/proc/update_tint() var/tinttotal = get_total_tint() if(tinttotal >= TINT_BLIND) - overlay_fullscreen("tint", /atom/movable/screen/fullscreen/blind) + overlay_fullscreen("tint", /atom/movable/screen/fullscreen/stretch/blind) else if(tinttotal >= TINT_IMPAIR) - overlay_fullscreen("tint", /atom/movable/screen/fullscreen/impaired, 2) + overlay_fullscreen("tint", /atom/movable/screen/fullscreen/stretch/impaired, 2) else clear_fullscreen("tint", 0) diff --git a/code/modules/mob/living/carbon/human/human_update_icons.dm b/code/modules/mob/living/carbon/human/human_update_icons.dm index 78100d015d6..feaad4085f9 100644 --- a/code/modules/mob/living/carbon/human/human_update_icons.dm +++ b/code/modules/mob/living/carbon/human/human_update_icons.dm @@ -567,7 +567,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) inv.update_icon() if(w_uniform && istype(w_uniform, /obj/item/clothing/under)) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) //if the inventory is open ... w_uniform.screen_loc = ui_iclothing //...draw the item in the inventory screen client.screen += w_uniform //Either way, add the item to the HUD @@ -644,7 +644,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) inv.update_icon() if(wear_id) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) wear_id.screen_loc = ui_id client.screen += wear_id @@ -660,7 +660,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) inv.update_icon() if(gloves) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) //if the inventory is open ... gloves.screen_loc = ui_gloves //...draw the item in the inventory screen client.screen += gloves //Either way, add the item to the HUD @@ -702,7 +702,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) if(glasses) var/mutable_appearance/new_glasses var/obj/item/organ/external/head/head_organ = get_organ("head") - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) //if the inventory is open ... glasses.screen_loc = ui_glasses //...draw the item in the inventory screen client.screen += glasses //Either way, add the item to the HUD @@ -744,7 +744,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) if(l_ear || r_ear) if(l_ear) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) //if the inventory is open ... l_ear.screen_loc = ui_l_ear //...draw the item in the inventory screen client.screen += l_ear //Either way, add the item to the HUD @@ -761,7 +761,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) overlays_standing[EARS_LAYER] = mutable_appearance('icons/mob/clothing/ears.dmi', "[t_type]", layer = -EARS_LAYER) if(r_ear) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) //if the inventory is open ... r_ear.screen_loc = ui_r_ear //...draw the item in the inventory screen client.screen += r_ear //Either way, add the item to the HUD @@ -786,7 +786,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) inv.update_icon() if(shoes) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) //if the inventory is open ... shoes.screen_loc = ui_shoes //...draw the item in the inventory screen client.screen += shoes //Either way, add the item to the HUD @@ -822,7 +822,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) inv.update_icon() if(s_store) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) s_store.screen_loc = ui_sstore1 client.screen += s_store @@ -872,7 +872,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) if(inv) inv.update_icon() - if(hud_used.hud_shown && belt) + if(hud_used.hud_version == HUD_STYLE_STANDARD && belt) client.screen += belt belt.screen_loc = ui_belt @@ -899,7 +899,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) inv.update_icon() if(wear_suit && istype(wear_suit, /obj/item/clothing/suit)) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) //if the inventory is open ... wear_suit.screen_loc = ui_oclothing //TODO //...draw the item in the inventory screen client.screen += wear_suit //Either way, add the item to the HUD @@ -948,7 +948,7 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) if(inv) inv.update_icon() - if(hud_used.hud_shown) + if(hud_used.hud_version == HUD_STYLE_STANDARD) if(l_store) client.screen += l_store l_store.screen_loc = ui_storage1 @@ -1082,21 +1082,21 @@ GLOBAL_LIST_EMPTY(damage_icon_parts) //update whether our head item appears on our hud. /mob/living/carbon/human/update_hud_head(obj/item/I) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) I.screen_loc = ui_head client.screen += I //update whether our mask item appears on our hud. /mob/living/carbon/human/update_hud_wear_mask(obj/item/I) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) if(hud_used.inventory_shown) I.screen_loc = ui_mask client.screen += I //update whether our back item appears on our hud. /mob/living/carbon/human/update_hud_back(obj/item/I) - if(client && hud_used && hud_used.hud_shown) + if(client && hud_used && hud_used.hud_version == HUD_STYLE_STANDARD) I.screen_loc = ui_back client.screen += I diff --git a/code/modules/mob/living/carbon/human/human_update_status.dm b/code/modules/mob/living/carbon/human/human_update_status.dm index 855a3d39e74..ee7fe70d3ee 100644 --- a/code/modules/mob/living/carbon/human/human_update_status.dm +++ b/code/modules/mob/living/carbon/human/human_update_status.dm @@ -13,7 +13,7 @@ /mob/living/carbon/human/update_nearsighted_effects() var/obj/item/clothing/glasses/G = glasses if(HAS_TRAIT(src, TRAIT_NEARSIGHT) && (!istype(G) || !G.prescription)) - overlay_fullscreen("nearsighted", /atom/movable/screen/fullscreen/impaired, 1) + overlay_fullscreen("nearsighted", /atom/movable/screen/fullscreen/stretch/impaired, 1) else clear_fullscreen("nearsighted") diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7f083bd1755..b7bb510dc0f 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -828,7 +828,7 @@ return TRUE //called when the mob receives a bright flash -/mob/living/proc/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, laser_pointer = FALSE, type = /atom/movable/screen/fullscreen/flash) +/mob/living/proc/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, laser_pointer = FALSE, type = /atom/movable/screen/fullscreen/stretch/flash) if(can_be_flashed(intensity, override_blindness_check)) overlay_fullscreen("flash", type) addtimer(CALLBACK(src, PROC_REF(clear_fullscreen), "flash", 25), 25) diff --git a/code/modules/mob/living/living_life.dm b/code/modules/mob/living/living_life.dm index 1611a0a09c8..fad7b238c42 100644 --- a/code/modules/mob/living/living_life.dm +++ b/code/modules/mob/living/living_life.dm @@ -173,7 +173,7 @@ healths.icon_state = "health7" severity = 6 if(severity > 0) - overlay_fullscreen("brute", /atom/movable/screen/fullscreen/brute, severity) + overlay_fullscreen("brute", /atom/movable/screen/fullscreen/stretch/brute, severity) else clear_fullscreen("brute") if(health <= HEALTH_THRESHOLD_CRIT) diff --git a/code/modules/mob/living/living_update_status.dm b/code/modules/mob/living/living_update_status.dm index 041d28afdf3..5f729221cf5 100644 --- a/code/modules/mob/living/living_update_status.dm +++ b/code/modules/mob/living/living_update_status.dm @@ -1,12 +1,22 @@ -/mob/living/update_blind_effects() +/mob/living/update_blind_effects(sleeping = FALSE, force_clear_sleeping = FALSE) + if(force_clear_sleeping) //We force it on waking up, as if you are blind from other methods it will refuse to clear it. + clear_fullscreen("sleepblind") + clear_fullscreen("disky") if(!has_vision(information_only=TRUE)) - overlay_fullscreen("blind", /atom/movable/screen/fullscreen/blind) + if(sleeping) + overlay_fullscreen("sleepblind", /atom/movable/screen/fullscreen/center/blind/sleeping, animated = 2 SECONDS) + overlay_fullscreen("disky", /atom/movable/screen/fullscreen/center/blind/disky, animated = 7 SECONDS) + throw_alert("blind", /atom/movable/screen/alert/blind) + return TRUE + overlay_fullscreen("blind", /atom/movable/screen/fullscreen/stretch/blind) throw_alert("blind", /atom/movable/screen/alert/blind) return TRUE else clear_fullscreen("blind") + clear_fullscreen("sleepblind") + clear_fullscreen("disky") clear_alert("blind") - return 0 + return FALSE /mob/living/update_blurry_effects() var/atom/movable/plane_master_controller/game_plane_master_controller = hud_used?.plane_master_controllers[PLANE_MASTERS_GAME] @@ -19,7 +29,7 @@ /mob/living/update_druggy_effects() if(AmountDruggy()) - overlay_fullscreen("high", /atom/movable/screen/fullscreen/high) + overlay_fullscreen("high", /atom/movable/screen/fullscreen/stretch/high) throw_alert("high", /atom/movable/screen/alert/high) sound_environment_override = SOUND_ENVIRONMENT_DRUGGED else @@ -29,7 +39,7 @@ /mob/living/update_nearsighted_effects() if(HAS_TRAIT(src, TRAIT_NEARSIGHT)) - overlay_fullscreen("nearsighted", /atom/movable/screen/fullscreen/impaired, 1) + overlay_fullscreen("nearsighted", /atom/movable/screen/fullscreen/stretch/impaired, 1) else clear_fullscreen("nearsighted") diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 5e8d534d65a..1b20fa56aa4 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -821,13 +821,11 @@ /obj/item/crowbar/cyborg/red, /obj/item/melee/energy/alien/claws, /obj/item/flash/cyborg/alien, - /obj/item/reagent_containers/spray/alien/stun, /obj/item/reagent_containers/spray/alien/smoke, ) emag_override_modules = list(/obj/item/reagent_containers/spray/alien/acid) special_rechargables = list( /obj/item/reagent_containers/spray/alien/acid, - /obj/item/reagent_containers/spray/alien/stun, /obj/item/reagent_containers/spray/alien/smoke ) diff --git a/code/modules/mob/living/silicon/silicon_mob.dm b/code/modules/mob/living/silicon/silicon_mob.dm index a487def354f..42d8bf19b98 100644 --- a/code/modules/mob/living/silicon/silicon_mob.dm +++ b/code/modules/mob/living/silicon/silicon_mob.dm @@ -447,7 +447,7 @@ /mob/living/silicon/get_access() return IGNORE_ACCESS //silicons always have access -/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/flash/noise) +/mob/living/silicon/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0, visual = 0, type = /atom/movable/screen/fullscreen/stretch/flash/noise) if(affect_silicon) return ..() diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index b96a046710e..c9d4feb15aa 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -285,12 +285,11 @@ back_to_hunt() return - if(no_handcuffs) // should we not cuff? + if(!(iscarbon(target) && target.canBeHandcuffed())) back_to_idle() return - if(!(iscarbon(target) && target.canBeHandcuffed())) - back_to_idle() + if(no_handcuffs) // should we not cuff? return if(currently_cuffing) diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index 2b4821ddc54..79f7c56c449 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -347,14 +347,14 @@ back_to_hunt() return // target is stunned and nearby - if(no_handcuffs) // should we not cuff? - back_to_idle() - return if(!(iscarbon(target) && target.canBeHandcuffed())) back_to_idle() return + if(no_handcuffs) // should we not cuff? + return + if(currently_cuffing) return diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm index bbc52f1b752..676b64b2a2d 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm @@ -103,9 +103,21 @@ Difficulty: Hard BL = new /mob/living/simple_animal/hostile/ancient_robot_leg(loc, src, BOTTOM_LEFT) beam = new /obj/effect/abstract(loc) mode = pick(BLUESPACE, GRAV, PYRO, FLUX, VORTEX, CRYO) //picks one of the 6 cores - if(mode == FLUX) // Main attack is shock, so flux makes it stronger - melee_damage_lower = 25 - melee_damage_upper = 25 + switch(mode) + if(BLUESPACE) + desc += " It emits sparks of blue energy." + if(GRAV) + desc += " Gravity seems to distort around it." + if(PYRO) + desc += " You see flames burning around it." + if(FLUX) // Main attack is shock, so flux makes it stronger + melee_damage_lower = 25 + melee_damage_upper = 25 + desc += " It seems to overflow with energy." + if(VORTEX) + desc += " You see space bend and distort around it." + if(CRYO) + desc += " The air surrounding it is cold and listless." body_shield() add_overlay("[mode]") add_overlay("eyes") diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 94af1932245..09585012a39 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -141,7 +141,7 @@ Difficulty: Hard H.apply_status_effect(STATUS_EFFECT_BUBBLEGUM_CURSE, src) if(second_life) H.clear_fullscreen("Bubblegum") - H.overlay_fullscreen("Bubblegum", /atom/movable/screen/fullscreen/fog, 2) + H.overlay_fullscreen("Bubblegum", /atom/movable/screen/fullscreen/stretch/fog, 2) /mob/living/simple_animal/hostile/megafauna/bubblegum/death(gibbed) diff --git a/code/modules/mob/living/simple_animal/slime/slime_mob.dm b/code/modules/mob/living/simple_animal/slime/slime_mob.dm index 4e880cfa7f7..16e4a8773f9 100644 --- a/code/modules/mob/living/simple_animal/slime/slime_mob.dm +++ b/code/modules/mob/living/simple_animal/slime/slime_mob.dm @@ -194,7 +194,7 @@ healths.icon_state = "slime_health7" severity = 6 if(severity > 0) - overlay_fullscreen("brute", /atom/movable/screen/fullscreen/brute, severity) + overlay_fullscreen("brute", /atom/movable/screen/fullscreen/stretch/brute, severity) else clear_fullscreen("brute") diff --git a/code/modules/mob/living/stat_states.dm b/code/modules/mob/living/stat_states.dm index 8048e4cb75a..551eac92d1d 100644 --- a/code/modules/mob/living/stat_states.dm +++ b/code/modules/mob/living/stat_states.dm @@ -19,8 +19,10 @@ if(updating) update_sight() - update_blind_effects() + update_blind_effects(TRUE) set_typing_indicator(FALSE) + if(hud_used && client) + hud_used.show_hud(HUD_STYLE_ACTIONHUD) return TRUE @@ -40,7 +42,9 @@ if(updating) update_sight() - update_blind_effects() + update_blind_effects(force_clear_sleeping = TRUE) + if(hud_used && client) + hud_used.show_hud(HUD_STYLE_STANDARD) return TRUE diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 5bf5402ba26..b89bb7ec75a 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1254,7 +1254,7 @@ GLOBAL_LIST_INIT(slot_equipment_priority, list( \ sync_nightvision_screen() //Sync up the overlay used for nightvision to the amount of see_in_dark a mob has. This needs to be called everywhere sync_lighting_plane_alpha() is. /mob/proc/sync_nightvision_screen() - var/atom/movable/screen/fullscreen/see_through_darkness/S = screens["see_through_darkness"] + var/atom/movable/screen/fullscreen/stretch/see_through_darkness/S = screens["see_through_darkness"] if(S) var/suffix = "" switch(see_in_dark) diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 1b1dfe10392..f0ede914893 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -363,8 +363,8 @@ if(!Process_Spacemove(get_dir(pulling.loc, A))) return var/target_turf = get_step(pulling, get_dir(pulling.loc, A)) - if(!Adjacent(target_turf)) //Make sure the turf we are trying to pull to is adjacent to the user. - return + if(get_dist(target_turf, loc) > 1) // Make sure the turf we are trying to pull to is adjacent to the user. + return // We do not use Adjacent() here because it checks if there are dense objects in the way, making it impossible to move an object to the side if we're blocked on both sides. if(ismob(pulling)) var/mob/M = pulling var/atom/movable/t = M.pulling diff --git a/code/modules/mod/modules/module_kinesis.dm b/code/modules/mod/modules/module_kinesis.dm index dcacecc1737..c2c9242fb40 100644 --- a/code/modules/mod/modules/module_kinesis.dm +++ b/code/modules/mod/modules/module_kinesis.dm @@ -30,7 +30,7 @@ /// Overlay we add to each grabbed atom. var/image/kinesis_icon /// Our mouse movement catcher. - var/atom/movable/screen/fullscreen/cursor_catcher/kinesis/kinesis_catcher + var/atom/movable/screen/fullscreen/stretch/cursor_catcher/kinesis/kinesis_catcher /// The sounds playing while we grabbed an object. var/datum/looping_sound/kinesis/soundloop ///The pixel_X of whatever we were grabbing before hand. @@ -83,7 +83,7 @@ pre_pixel_x = grabbed_atom.pixel_x pre_pixel_y = grabbed_atom.pixel_y beam.chain = beam.Beam(grabbed_atom, icon_state = "kinesis", icon='icons/effects/beam.dmi', time = 100 SECONDS, maxdistance = 15, beam_type = /obj/effect/ebeam, beam_sleep_time = 3) - kinesis_catcher = mod.wearer.overlay_fullscreen("kinesis", /atom/movable/screen/fullscreen/cursor_catcher/kinesis, 0) + kinesis_catcher = mod.wearer.overlay_fullscreen("kinesis", /atom/movable/screen/fullscreen/stretch/cursor_catcher/kinesis, 0) kinesis_catcher.assign_to_mob(mod.wearer) soundloop.start() START_PROCESSING(SSfastprocess, src) @@ -249,7 +249,7 @@ complexity = 0 use_power_cost = DEFAULT_CHARGE_DRAIN * 5 -/atom/movable/screen/fullscreen/cursor_catcher/kinesis +/atom/movable/screen/fullscreen/stretch/cursor_catcher/kinesis icon = 'icons/mob/screen_kinesis.dmi' icon_state = "kinesis" diff --git a/code/modules/power/engines/singularity/singularity.dm b/code/modules/power/engines/singularity/singularity.dm index f9da157db0d..b00a030a16e 100644 --- a/code/modules/power/engines/singularity/singularity.dm +++ b/code/modules/power/engines/singularity/singularity.dm @@ -348,7 +348,7 @@ if(STAGE_ONE) steps = 1 if(STAGE_TWO) - steps = 3//Yes this is right + steps = 2 if(STAGE_THREE) steps = 3 if(STAGE_FOUR) diff --git a/code/modules/power/engines/supermatter/supermatter.dm b/code/modules/power/engines/supermatter/supermatter.dm index 3fc0b2826da..97b41d4c8ed 100644 --- a/code/modules/power/engines/supermatter/supermatter.dm +++ b/code/modules/power/engines/supermatter/supermatter.dm @@ -323,7 +323,7 @@ var/image/causality_field = image(icon, null, "causality_field") add_overlay(causality_field) - var/speaking = "[emergency_alert] The supermatter has reached critical integrity failure. Emergency causality destabilization field has been activated." + var/speaking = "[emergency_alert] The supermatter has reached critical integrity failure. Emergency causality destabilization field has been activated." for(var/mob/M in GLOB.player_list) // for all players var/turf/T = get_turf(M) if(istype(T) && atoms_share_level(T, src)) // if the player is on the same zlevel as the SM shared @@ -336,7 +336,7 @@ damage = explosion_point - 1 // One point below exploding, so it will re-start the countdown once unfrozen return if(damage < explosion_point) // Cutting it a bit close there engineers - radio.autosay("[safe_alert] Failsafe has been disengaged.", name, null) + radio.autosay("[safe_alert] Failsafe has been disengaged.", name, null) cut_overlay(causality_field, TRUE) final_countdown = FALSE remove_filter(list("outline", "icon")) @@ -345,9 +345,9 @@ sleep(10) continue else if(i > 50) - speaking = "[DisplayTimeText(i, TRUE)] remain before causality stabilization." + speaking = "[DisplayTimeText(i, TRUE)] remain before causality stabilization." else - speaking = "[i*0.1]..." + speaking = "[i * 0.1]..." radio.autosay(speaking, name, null) sleep(10) @@ -648,27 +648,27 @@ //Oh shit it's bad, time to freak out if(damage > emergency_point) - radio.autosay("[emergency_alert] Integrity: [get_integrity()]%", name, null) + radio.autosay("[emergency_alert] Integrity: [get_integrity()]%", name, null) lastwarning = REALTIMEOFDAY if(!has_reached_emergency) investigate_log("has reached the emergency point for the first time.", "supermatter") message_admins("[src] has reached the emergency point [ADMIN_JMP(src)].") has_reached_emergency = TRUE else if(damage >= damage_archived) // The damage is still going up - radio.autosay("[warning_alert] Integrity: [get_integrity()]%", name, "Engineering") + radio.autosay("[warning_alert] Integrity: [get_integrity()]%", name, "Engineering") lastwarning = REALTIMEOFDAY - (WARNING_DELAY * 5) else // Phew, we're safe - radio.autosay("[safe_alert] Integrity: [get_integrity()]%", name, "Engineering") + radio.autosay("[safe_alert] Integrity: [get_integrity()]%", name, "Engineering") lastwarning = REALTIMEOFDAY if(power > POWER_PENALTY_THRESHOLD) - radio.autosay("Warning: Hyperstructure has reached dangerous power level.", name, "Engineering") + radio.autosay("Warning: Hyperstructure has reached dangerous power level.", name, "Engineering") if(powerloss_inhibitor < 0.5) - radio.autosay("DANGER: CHARGE INERTIA CHAIN REACTION IN PROGRESS.", name, "Engineering") + radio.autosay("DANGER: CHARGE INERTIA CHAIN REACTION IN PROGRESS.", name, "Engineering") if(combined_gas > MOLE_CRUNCH_THRESHOLD) - radio.autosay("Warning: Critical coolant mass reached.", name, "Engineering") + radio.autosay("Warning: Critical coolant mass reached.", name, "Engineering") //Boom (Mind blown) if(damage > explosion_point) countdown() diff --git a/code/modules/power/engines/supermatter/supermatter_event.dm b/code/modules/power/engines/supermatter/supermatter_event.dm index 8af8c5ef9b3..1d7bae80edc 100644 --- a/code/modules/power/engines/supermatter/supermatter_event.dm +++ b/code/modules/power/engines/supermatter/supermatter_event.dm @@ -33,7 +33,7 @@ return /datum/supermatter_event/proc/on_end() - sm_radio_say("Anomalous crystal activity has ended.") + sm_radio_say("Anomalous crystal activity has ended.") supermatter.heat_penalty_threshold = SUPERMATTER_HEAT_PENALTY_THRESHOLD supermatter.gas_multiplier = 1 supermatter.power_additive = 0 @@ -120,7 +120,7 @@ duration = 1 MINUTES /datum/supermatter_event/bravo_tier/alert_engi() - sm_radio_say("Anomalous crystal activity detected! Activity class: [name]. Operator intervention is required!") + sm_radio_say("Anomalous crystal activity detected! Activity class: [name]. Operator intervention is required!") // more gas @@ -150,7 +150,7 @@ duration = 10 SECONDS /datum/supermatter_event/alpha_tier/alert_engi() - sm_radio_say("ALERT: Critical anomalous crystal activity detected! Activity class: [name]. IMMEDIATE Operator intervention is REQUIRED!") + sm_radio_say("ALERT: Critical anomalous crystal activity detected! Activity class: [name]. IMMEDIATE Operator intervention is REQUIRED!") /datum/supermatter_event/alpha_tier/apc_short name = "A-1" @@ -181,16 +181,16 @@ duration = 7 MINUTES // 2 MINUTES of s-tier anomaly /datum/supermatter_event/sierra_tier/alert_engi() - general_radio_say("ALERT: Anomalous supermatter state expected in: 5 minutes.") - sm_radio_say("EMERGENCY ALERT: 5 MINUTES UNTIL [supermatter] EXHIBITS [name] CLASS ANOMALOUS ACTIVITY!") + general_radio_say("ALERT: Anomalous supermatter state expected in: 5 minutes.") + sm_radio_say("EMERGENCY ALERT: 5 MINUTES UNTIL [supermatter] EXHIBITS [name] CLASS ANOMALOUS ACTIVITY!") /datum/supermatter_event/sierra_tier/on_start() addtimer(CALLBACK(src, PROC_REF(start_sierra_event)), 5 MINUTES) supermatter.has_run_sclass = TRUE /datum/supermatter_event/sierra_tier/proc/start_sierra_event() - general_radio_say("ALERT: ANOMALOUS SUPERMATTER STATE DETECTED!") - sm_radio_say("EMERGENCY ALERT: Class [name] anomalous behavior in progress!") + general_radio_say("ALERT: ANOMALOUS SUPERMATTER STATE DETECTED!") + sm_radio_say("EMERGENCY ALERT: Class [name] anomalous behavior in progress!") //S class events //Arc-type diff --git a/code/modules/reagents/chemistry/machinery/pandemic.dm b/code/modules/reagents/chemistry/machinery/pandemic.dm index 51862c1a89c..1c07afabb50 100644 --- a/code/modules/reagents/chemistry/machinery/pandemic.dm +++ b/code/modules/reagents/chemistry/machinery/pandemic.dm @@ -11,6 +11,7 @@ var/temp_html = "" var/printing = null var/wait = null + var/selected_strain_index = 1 var/obj/item/reagent_containers/beaker = null /obj/machinery/computer/pandemic/Initialize(mapload) @@ -26,23 +27,33 @@ stat |= BROKEN update_icon() -/obj/machinery/computer/pandemic/proc/GetVirusByIndex(index) +/obj/machinery/computer/pandemic/proc/GetViruses() if(beaker && beaker.reagents) if(length(beaker.reagents.reagent_list)) var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list if(BL) if(BL.data && BL.data["viruses"]) var/list/viruses = BL.data["viruses"] - return viruses[index] + return viruses -/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(index) +/obj/machinery/computer/pandemic/proc/GetVirusByIndex(index) + var/list/viruses = GetViruses() + if(viruses && index > 0 && index <= length(viruses)) + return viruses[index] + +/obj/machinery/computer/pandemic/proc/GetResistances() if(beaker && beaker.reagents) if(length(beaker.reagents.reagent_list)) var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list if(BL) if(BL.data && BL.data["resistances"]) var/list/resistances = BL.data["resistances"] - return resistances[index] + return resistances + +/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(index) + var/list/resistances = GetResistances() + if(resistances && index > 0 && index <= length(resistances)) + return resistances[index] /obj/machinery/computer/pandemic/proc/GetVirusTypeByIndex(index) var/datum/disease/D = GetVirusByIndex(index) @@ -68,117 +79,233 @@ if(!wait) . += "waitlight" -/obj/machinery/computer/pandemic/Topic(href, href_list) +/obj/machinery/computer/pandemic/proc/create_culture(name, bottle_type = "culture", cooldown = 50) + var/obj/item/reagent_containers/glass/bottle/B = new/obj/item/reagent_containers/glass/bottle(loc) + B.icon_state = "round_bottle" + B.pixel_x = rand(-3, 3) + B.pixel_y = rand(-3, 3) + replicator_cooldown(cooldown) + B.name = "[name] [bottle_type] bottle" + return B + +/obj/machinery/computer/pandemic/ui_act(action, params, datum/tgui/ui, datum/ui_state/state) if(..()) return - - usr.set_machine(src) - if(!beaker) return - - if(href_list["create_vaccine"]) - if(!wait) - var/obj/item/reagent_containers/glass/bottle/B = new/obj/item/reagent_containers/glass/bottle(loc) - if(B) - B.pixel_x = rand(-3, 3) - B.pixel_y = rand(-3, 3) - var/path = GetResistancesByIndex(text2num(href_list["create_vaccine"])) - var/vaccine_type = path - var/vaccine_name = "Unknown" - - if(!ispath(vaccine_type)) - if(GLOB.archive_diseases[path]) - var/datum/disease/D = GLOB.archive_diseases[path] - if(D) - vaccine_name = D.name - vaccine_type = path - else if(vaccine_type) - var/datum/disease/D = new vaccine_type(0, null) - if(D) - vaccine_name = D.name - - if(vaccine_type) - - B.name = "[vaccine_name] vaccine bottle" - B.reagents.add_reagent("vaccine", 15, list(vaccine_type)) - replicator_cooldown(200) - else - temp_html = "The replicator is not ready yet." - updateUsrDialog() + if(inoperable()) return - else if(href_list["create_virus_culture"]) - if(!wait) - var/type = GetVirusTypeByIndex(text2num(href_list["create_virus_culture"]))//the path is received as string - converting + + + . = TRUE + + switch(action) + if("clone_strain") + if(wait) + atom_say("The replicator is not ready yet.") + return + + var/strain_index = text2num(params["strain_index"]) + if(isnull(strain_index)) + atom_say("Unable to respond to command.") + return + var/datum/disease/virus = GetVirusByIndex(strain_index) var/datum/disease/D = null + if(!virus) + atom_say("Unable to find requested strain.") + return + var/type = virus.GetDiseaseID() if(!ispath(type)) - D = GetVirusByIndex(text2num(href_list["create_virus_culture"])) - var/datum/disease/advance/A = GLOB.archive_diseases[D.GetDiseaseID()] + var/datum/disease/advance/A = GLOB.archive_diseases[type] if(A) D = new A.type(0, A) else if(type) if(type in GLOB.diseases) // Make sure this is a disease D = new type(0, null) if(!D) + atom_say("Unable to synthesize requested strain.") return - var/name = tgui_input_text(usr, "Name:", "Name the culture", D.name, MAX_NAME_LEN) + var/default_name = "" + if(D.name == "Unknown" || D.name == "") + default_name = replacetext(beaker.name, new/regex(" culture bottle\\Z", "g"), "") + else + default_name = D.name + var/name = tgui_input_text(usr, "Name:", "Name the culture", default_name, MAX_NAME_LEN) if(name == null || wait) return - var/obj/item/reagent_containers/glass/bottle/B = new/obj/item/reagent_containers/glass/bottle(loc) - B.icon_state = "round_bottle" - B.pixel_x = rand(-3, 3) - B.pixel_y = rand(-3, 3) - replicator_cooldown(50) - var/list/data = list("viruses"=list(D)) - B.name = "[name] culture bottle" + var/obj/item/reagent_containers/glass/bottle/B = create_culture(name) B.desc = "A small bottle. Contains [D.agent] culture in synthblood medium." - B.reagents.add_reagent("blood",20,data) - updateUsrDialog() - else - temp_html = "The replicator is not ready yet." - updateUsrDialog() - return - else if(href_list["empty_beaker"]) - beaker.reagents.clear_reagents() - eject_beaker() - updateUsrDialog() - return - else if(href_list["eject"]) - eject_beaker() - updateUsrDialog() - return - else if(href_list["clear"]) - temp_html = "" - updateUsrDialog() - return - else if(href_list["name_disease"]) - var/new_name = tgui_input_text(usr, "Name the Disease", "New Name", max_length = MAX_NAME_LEN) - if(!new_name) - return - if(..()) - return - var/id = GetVirusTypeByIndex(text2num(href_list["name_disease"])) - if(GLOB.archive_diseases[id]) - var/datum/disease/advance/A = GLOB.archive_diseases[id] + B.reagents.add_reagent("blood", 20, list("viruses" = list(D))) + if("clone_vaccine") + if(wait) + atom_say("The replicator is not ready yet.") + return + + var/resistance_index = text2num(params["resistance_index"]) + if(isnull(resistance_index)) + atom_say("Unable to find requested antibody.") + return + var/vaccine_type = GetResistancesByIndex(resistance_index) + var/vaccine_name = "Unknown" + if(!ispath(vaccine_type)) + if(GLOB.archive_diseases[vaccine_type]) + var/datum/disease/D = GLOB.archive_diseases[vaccine_type] + if(D) + vaccine_name = D.name + else if(vaccine_type) + var/datum/disease/D = new vaccine_type(0, null) + if(D) + vaccine_name = D.name + + if(!vaccine_type) + atom_say("Unable to synthesize requested antibody.") + return + + var/obj/item/reagent_containers/glass/bottle/B = create_culture(vaccine_name, "vaccine", 200) + B.reagents.add_reagent("vaccine", 15, list(vaccine_type)) + if("eject_beaker") + eject_beaker() + if("destroy_eject_beaker") + beaker.reagents.clear_reagents() + eject_beaker() + if("print_release_forms") + var/strain_index = text2num(params["strain_index"]) + if(isnull(strain_index)) + atom_say("Unable to respond to command.") + return + var/type = GetVirusTypeByIndex(strain_index) + if(!type) + atom_say("Unable to find requested strain.") + return + var/datum/disease/advance/A = GLOB.archive_diseases[type] + if(!A) + atom_say("Unable to find requested strain.") + return + print_form(A, usr) + if("name_strain") + var/strain_index = text2num(params["strain_index"]) + if(isnull(strain_index)) + atom_say("Unable to respond to command.") + return + var/type = GetVirusTypeByIndex(strain_index) + if(!type) + atom_say("Unable to find requested strain.") + return + var/datum/disease/advance/A = GLOB.archive_diseases[type] + if(!A) + atom_say("Unable to find requested strain.") + return + if(A.name != "Unknown") + atom_say("Request rejected. Strain already has a name.") + return + var/new_name = tgui_input_text(usr, "Name the Strain", "New Name", max_length = MAX_NAME_LEN) + if(!new_name) + return A.AssignName(new_name) for(var/datum/disease/advance/AD in GLOB.active_diseases) AD.Refresh() - updateUsrDialog() - else if(href_list["print_form"]) - var/datum/disease/D = GetVirusByIndex(text2num(href_list["print_form"])) - D = GLOB.archive_diseases[D.GetDiseaseID()]//We know it's advanced no need to check - print_form(D, usr) + if("switch_strain") + var/strain_index = text2num(params["strain_index"]) + if(isnull(strain_index) || strain_index < 1) + atom_say("Unable to respond to command.") + return + var/list/viruses = GetViruses() + if(strain_index > length(viruses)) + atom_say("Unable to find requested strain.") + return + selected_strain_index = strain_index; + else + return FALSE +/obj/machinery/computer/pandemic/ui_state(mob/user) + return GLOB.default_state - else - usr << browse(null, "window=pandemic") - updateUsrDialog() - return +/obj/machinery/computer/pandemic/ui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PanDEMIC", name) + ui.open() - add_fingerprint(usr) +/obj/machinery/computer/pandemic/ui_data(mob/user) + var/datum/reagent/blood/Blood = null + if(beaker) + var/datum/reagents/R = beaker.reagents + for(var/datum/reagent/blood/B in R.reagent_list) + if(B) + Blood = B + break + + var/list/data = list( + "synthesisCooldown" = wait ? TRUE : FALSE, + "beakerLoaded" = beaker ? TRUE : FALSE, + "beakerContainsBlood" = Blood ? TRUE : FALSE, + "beakerContainsVirus" = length(Blood?.data["viruses"]) != 0, + "selectedStrainIndex" = selected_strain_index, + ) + + return data + +/obj/machinery/computer/pandemic/ui_static_data(mob/user) + var/list/data = list() + . = data + + var/datum/reagent/blood/Blood = null + if(beaker) + var/datum/reagents/R = beaker.reagents + for(var/datum/reagent/blood/B in R.reagent_list) + if(B) + Blood = B + break + + var/list/strains = list() + for(var/datum/disease/D in GetViruses()) + if(D.visibility_flags & HIDDEN_PANDEMIC) + continue + + var/list/symptoms = list() + if(istype(D, /datum/disease/advance)) + var/datum/disease/advance/A = D + D = GLOB.archive_diseases[A.GetDiseaseID()] + if(!D) + CRASH("We weren't able to get the advance disease from the archive.") + for(var/datum/symptom/S in A.symptoms) + symptoms += list(list( + "name" = S.name, + "stealth" = S.stealth, + "resistance" = S.resistance, + "stageSpeed" = S.stage_speed, + "transmissibility" = S.transmittable, + "complexity" = S.level, + )) + + strains += list(list( + "commonName" = D.name, + "description" = D.desc, + "bloodDNA" = Blood.data["blood_DNA"], + "bloodType" = Blood.data["blood_type"], + "diseaseAgent" = D.agent, + "possibleTreatments" = D.cure_text, + "transmissionRoute" = D.spread_text, + "symptoms" = symptoms, + "isAdvanced" = istype(D, /datum/disease/advance), + )) + data["strains"] = strains + + var/list/resistances = list() + for(var/resistance in GetResistances()) + if(!ispath(resistance)) + var/datum/disease/D = GLOB.archive_diseases[resistance] + if(D) + resistances += list(D.name) + else if(resistance) + var/datum/disease/D = new resistance(0, null) + if(D) + resistances += list(D.name) + data["resistances"] = resistances /obj/machinery/computer/pandemic/proc/eject_beaker() beaker.forceMove(loc) beaker = null icon_state = "pandemic0" + selected_strain_index = 1 //Prints a nice virus release form. Props to Urbanliner for the layout /obj/machinery/computer/pandemic/proc/print_form(datum/disease/advance/D, mob/living/user) @@ -241,108 +368,16 @@ P.info = info_text.Join("") P.update_icon() +/obj/machinery/computer/pandemic/attack_ai(mob/user) + return attack_hand(user) + /obj/machinery/computer/pandemic/attack_hand(mob/user) if(..()) return - user.set_machine(src) - var/dat = "" - if(temp_html) - dat = "[temp_html]

Main Menu" - else if(!beaker) - dat += "Please insert beaker.
" - dat += "Close" - else - var/datum/reagents/R = beaker.reagents - var/datum/reagent/blood/Blood = null - for(var/datum/reagent/blood/B in R.reagent_list) - if(B) - Blood = B - break - if(!R.total_volume||!length(R.reagent_list)) - dat += "The beaker is empty
" - else if(!Blood) - dat += "No blood sample found in beaker." - else if(!Blood.data) - dat += "No blood data found in beaker." - else - dat += "

Blood sample data:

" - dat += "Blood DNA: [(Blood.data["blood_DNA"]||"none")]
" - dat += "Blood Type: [(Blood.data["blood_type"]||"none")]
" - - - if(Blood.data["viruses"]) - var/list/vir = Blood.data["viruses"] - if(length(vir)) - var/i = 0 - for(var/thing in Blood.data["viruses"]) - var/datum/disease/D = thing - i++ - if(!(D.visibility_flags & HIDDEN_PANDEMIC)) - - if(istype(D, /datum/disease/advance)) - - var/datum/disease/advance/A = D - D = GLOB.archive_diseases[A.GetDiseaseID()] - if(D) - if(D.name == "Unknown") - dat += "Name Disease
" - else - dat += "Print release form
" - - if(!D) - CRASH("We weren't able to get the advance disease from the archive.") - - dat += "Disease Agent: [D?"[D.agent] - Create virus culture bottle":"none"]
" - dat += "Common name: [(D.name||"none")]
" - dat += "Description: [(D.desc||"none")]
" - dat += "Spread: [(D.spread_text||"none")]
" - dat += "Possible cure: [(D.cure_text||"none")]

" - - if(istype(D, /datum/disease/advance)) - var/datum/disease/advance/A = D - dat += "Symptoms: " - var/english_symptoms = list() - for(var/datum/symptom/S in A.symptoms) - english_symptoms += S.name - dat += english_list(english_symptoms) - - else - dat += "No detectable virus in the sample." - else - dat += "No detectable virus in the sample." - - dat += "
Contains antibodies to: " - if(Blood.data["resistances"]) - var/list/res = Blood.data["resistances"] - if(length(res)) - dat += "
    " - var/i = 0 - for(var/type in Blood.data["resistances"]) - i++ - var/disease_name = "Unknown" - - if(!ispath(type)) - var/datum/disease/advance/A = GLOB.archive_diseases[type] - if(A) - disease_name = A.name - else - var/datum/disease/D = new type(0, null) - disease_name = D.name - - dat += "
  • [disease_name] - Create vaccine bottle
  • " - dat += "

" - else - dat += "nothing
" - else - dat += "nothing
" - dat += "
Eject beaker[((R.total_volume&&length(R.reagent_list)) ? "-- Empty and eject beaker":"")]
" - dat += "Close" - - var/datum/browser/popup = new(user, "pandemic", name, 575, 400) - popup.set_content(dat) - popup.open(0) - onclose(user, "pandemic") + ui_interact(user) +/obj/machinery/computer/pandemic/attack_ghost(mob/user) + ui_interact(user) /obj/machinery/computer/pandemic/attackby(obj/item/I, mob/user, params) if(default_unfasten_wrench(user, I, time = 4 SECONDS)) @@ -361,6 +396,7 @@ beaker.loc = src to_chat(user, "You add the beaker to the machine.") updateUsrDialog() + SStgui.update_uis(src, TRUE) icon_state = "pandemic1" else return ..() diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index f273631c1f5..76740a9a988 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -545,7 +545,7 @@ /obj/machinery/disposal/get_remote_view_fullscreens(mob/user) if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS))) - user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 2) + user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/stretch/impaired, 2) /obj/machinery/disposal/force_eject_occupant(mob/target) target.forceMove(get_turf(src)) diff --git a/code/modules/shuttle/dock_generator.dm b/code/modules/shuttle/dock_generator.dm new file mode 100644 index 00000000000..ab53133315b --- /dev/null +++ b/code/modules/shuttle/dock_generator.dm @@ -0,0 +1,103 @@ +/obj/item/whiteship_port_generator + name = "docking area signaller" + desc = "A signaling device for the NT expeditionary vessel, used to designate new docking areas." + icon = 'icons/obj/device.dmi' + icon_state = "gangtool-white" + + var/list/placed_docks = list() + var/max_docks = 3 + +/obj/item/whiteship_port_generator/examine(mob/user) + . = ..() + var/count = max_docks - length(placed_docks) + var/plural = count > 1 + . += "There [plural ? "are" : "is"] [count] use[plural ? "s" : ""] left." + +/obj/item/whiteship_port_generator/attack_self(mob/living/user) + if(is_station_level(user.z)) + log_admin("[key_name(user)] attempted to create a whiteship dock in the station's sector at [COORD(user)].") + to_chat(user, "New docking areas cannot be designated within the station's sector!") + return + + if(is_admin_level(user.z)) + log_admin("[key_name(user)] attempted to create a whiteship dock on Centcomm level at [COORD(user)].") + to_chat(user, "New docking areas cannot be designated in this sector!") + return + + if(is_mining_level(user.z)) + log_admin("[key_name(user)] attempted to create a whiteship dock on Lavaland at [COORD(user)].") + to_chat(user, "New docking areas cannot be designated planet side!") + return + + for(var/obj/placed_dock in placed_docks) + if(placed_dock.z == user.z) + to_chat(user, "A docking area has already been placed in this sector!") + return + + var/list/dir_choices = list("North" = NORTH, "East" = EAST, "South" = SOUTH, "West" = WEST) + var/dir_choice = tgui_input_list(user, "Select the new docking area orientation.", "Dock Orientation", dir_choices) + if(!dir_choice) + return + + var/dest_dir = dir_choices[dir_choice] + var/turf/destination = get_step(user.loc, dest_dir) + var/obj/docking_port/stationary/whiteship/port = new(destination) + port.dir = dest_dir + + var/min_x = -1 + var/min_y = -1 + var/max_x = -1 + var/max_y = -1 + + var/list/ordered_turfs = port.return_ordered_turfs() + for(var/turf/T in ordered_turfs) + min_x = min_x < 0 ? T.x : min(min_x, T.x) + min_y = min_y < 0 ? T.y : min(min_y, T.y) + max_x = max_x < 0 ? T.x : max(max_x, T.x) + max_y = max_y < 0 ? T.y : max(max_y, T.y) + if(!isspaceturf(T)) + to_chat(user, "Obstruction found in docking space area, aborting!") + qdel(port, force = TRUE) + return + for(var/obj/O in T.contents) + if(O == port) + continue + else + to_chat(user, "Objects found in docking space area, aborting!") + qdel(port, force = TRUE) + return + + if(min_x <= TRANSITION_BORDER_WEST + 1 || max_x >= TRANSITION_BORDER_EAST - 1) + to_chat(user, "Docking space area too close to edge of sector, aborting!") + qdel(port, force = TRUE) + return + if(min_y <= TRANSITION_BORDER_SOUTH + 1 || max_y >= TRANSITION_BORDER_NORTH - 1) + to_chat(user, "Docking space area too close to edge of sector, aborting!") + qdel(port, force = TRUE) + return + + placed_docks += port + var/dock_count = length(placed_docks) + + var/basename = "Custom Dock #[dock_count]" + var/name = tgui_input_text(user, + message = "Select the new docking area name.", + title = "New Dock Name", + default = basename, + max_length = 20 + ) + port.name = name ? name : basename + port.id = "whiteship_custom_[dock_count]" + port.register() + + for(var/obj/machinery/computer/shuttle/white_ship/S in GLOB.machines) + S.possible_destinations = null + S.connect() + + log_admin("[key_name(user)] created a whiteship dock named '[name]' at [COORD(port)].") + + if(dock_count < max_docks) + to_chat(user, "Landing zone set.") + else + to_chat(user, "Landing zone set. The signaller vanishes!") + qdel(src) diff --git a/code/modules/station_goals/secondary/secondary_goal.dm b/code/modules/station_goals/secondary/secondary_goal.dm index de3a9e15024..2c25b9b95dd 100644 --- a/code/modules/station_goals/secondary/secondary_goal.dm +++ b/code/modules/station_goals/secondary/secondary_goal.dm @@ -98,10 +98,12 @@ SSticker.mode.secondary_goals -= src message_admins("[key_name_admin(usr)] removed secondary goal [src] ([admin_desc])") log_admin("[key_name_admin(usr)] removed secondary goal [src] ([admin_desc])") + tracker.unregister(SSshuttle.supply) qdel(src) usr.client.modify_goals() else if(href_list["mark_complete"]) completed = 1 + tracker.unregister(SSshuttle.supply) usr.client.modify_goals() message_admins("[key_name_admin(usr)] marked secondary goal [src] ([admin_desc]) as complete") log_admin("[key_name_admin(usr)] marked secondary goal [src] ([admin_desc]) as complete") diff --git a/code/modules/station_goals/secondary/secondary_goal_tracker.dm b/code/modules/station_goals/secondary/secondary_goal_tracker.dm index db31ce322bf..10327763f7b 100644 --- a/code/modules/station_goals/secondary/secondary_goal_tracker.dm +++ b/code/modules/station_goals/secondary/secondary_goal_tracker.dm @@ -2,6 +2,7 @@ var/datum/secondary_goal_progress/real_progress var/datum/secondary_goal_progress/temporary_progress var/datum/station_goal/secondary/goal + var/registered = FALSE /datum/secondary_goal_tracker/New(datum/station_goal/secondary/goal_in, datum/secondary_goal_progress/progress) goal = goal_in @@ -11,6 +12,8 @@ /datum/secondary_goal_tracker/proc/reset() real_progress = new real_progress.type(goal) temporary_progress = real_progress.Copy() + if(!registered) + register(SSshuttle.supply) /datum/secondary_goal_tracker/proc/register(shuttle) RegisterSignal(shuttle, COMSIG_CARGO_BEGIN_SCAN, PROC_REF(reset_tempporary_progress)) @@ -19,6 +22,7 @@ RegisterSignal(shuttle, COMSIG_CARGO_DO_PRIORITY_SELL, PROC_REF(update_progress)) RegisterSignal(shuttle, COMSIG_CARGO_SEND_ERROR, PROC_REF(update_progress)) RegisterSignal(shuttle, COMSIG_CARGO_END_SELL, PROC_REF(check_for_completion)) + registered = TRUE /datum/secondary_goal_tracker/proc/unregister(shuttle) UnregisterSignal(shuttle, COMSIG_CARGO_BEGIN_SCAN) @@ -27,6 +31,7 @@ UnregisterSignal(shuttle, COMSIG_CARGO_DO_PRIORITY_SELL) UnregisterSignal(shuttle, COMSIG_CARGO_SEND_ERROR) UnregisterSignal(shuttle, COMSIG_CARGO_END_SELL) + registered = FALSE // Resets the temporary porgress to match the real progress. /datum/secondary_goal_tracker/proc/reset_tempporary_progress(obj/docking_port/mobile/supply/shuttle) diff --git a/icons/mob/alien.dmi b/icons/mob/alien.dmi index aa921ea1343..e9fd3d3c594 100644 Binary files a/icons/mob/alien.dmi and b/icons/mob/alien.dmi differ diff --git a/icons/mob/screen_sleeping.dmi b/icons/mob/screen_sleeping.dmi new file mode 100644 index 00000000000..c86eff475d5 Binary files /dev/null and b/icons/mob/screen_sleeping.dmi differ diff --git a/icons/mob/screen_tight.dmi b/icons/mob/screen_tight.dmi new file mode 100644 index 00000000000..2bf7de62e4b Binary files /dev/null and b/icons/mob/screen_tight.dmi differ diff --git a/icons/obj/bureaucracy.dmi b/icons/obj/bureaucracy.dmi index 30a7fc4f732..9f0a699bf6d 100644 Binary files a/icons/obj/bureaucracy.dmi and b/icons/obj/bureaucracy.dmi differ diff --git a/paradise.dme b/paradise.dme index 9c97b387faa..b8bc61904dc 100644 --- a/paradise.dme +++ b/paradise.dme @@ -2713,6 +2713,7 @@ #include "code\modules\server_commands\servermsg.dm" #include "code\modules\server_commands\commands\newroundannounce.dm" #include "code\modules\shuttle\assault_pod.dm" +#include "code\modules\shuttle\dock_generator.dm" #include "code\modules\shuttle\emergency.dm" #include "code\modules\shuttle\ert_shuttle.dm" #include "code\modules\shuttle\navigation_computer.dm" diff --git a/tgui/packages/tgui/components/Flex.tsx b/tgui/packages/tgui/components/Flex.tsx index 0762d01b3c9..2ac49f67a08 100644 --- a/tgui/packages/tgui/components/Flex.tsx +++ b/tgui/packages/tgui/components/Flex.tsx @@ -24,10 +24,11 @@ export const computeFlexClassName = (props: FlexProps) => { }; export const computeFlexProps = (props: FlexProps) => { - const { className, direction, wrap, align, justify, inline, ...rest } = props; + const { className, direction, wrap, align, justify, inline, style, ...rest } = + props; return computeBoxProps({ style: { - ...rest.style, + ...style, 'flex-direction': direction, 'flex-wrap': wrap === true ? 'wrap' : wrap, 'align-items': align, diff --git a/tgui/packages/tgui/interfaces/PanDEMIC.tsx b/tgui/packages/tgui/interfaces/PanDEMIC.tsx new file mode 100644 index 00000000000..a255f8892b4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PanDEMIC.tsx @@ -0,0 +1,374 @@ +import { BooleanLike } from '../../common/react'; +import { InfernoNode } from 'inferno'; +import { useBackend } from '../backend'; +import { + Button, + Flex, + LabeledList, + Section, + Stack, + Table, + Tabs, +} from '../components'; +import { Window } from '../layouts'; + +interface PathogenSymptom { + name: string; + stealth: number; + resistance: number; + stageSpeed: number; + transmissibility: number; +} + +interface PathogenStrain { + commonName?: string; + description?: string; + bloodDNA?: string; + bloodType?: string; + diseaseAgent: string; + possibleTreatments?: string; + transmissionRoute?: string; + symptoms?: PathogenSymptom[]; + isAdvanced: BooleanLike; +} + +interface PanDEMICData { + synthesisCooldown: BooleanLike; + beakerLoaded: BooleanLike; + beakerContainsBlood: BooleanLike; + beakerContainsVirus: BooleanLike; + selectedStrainIndex: number; + strains?: PathogenStrain[]; + resistances?: string[]; +} + +export const PanDEMIC = (props, context) => { + const { data } = useBackend(context); + const { + beakerLoaded, + beakerContainsBlood, + beakerContainsVirus, + resistances = [], + } = data; + + let emptyPlaceholder; + if (!beakerLoaded) { + emptyPlaceholder = <>No container loaded.; + } else if (!beakerContainsBlood) { + emptyPlaceholder = <>No blood sample found in the loaded container.; + } + + return ( + + + + {emptyPlaceholder && ( +
} + > + {emptyPlaceholder} +
+ )} + {!!beakerContainsVirus && } + {resistances?.length > 0 && } +
+
+
+ ); +}; + +const CommonCultureActions = (props, context) => { + const { act, data } = useBackend(context); + const { beakerLoaded } = data; + return ( + <> +