diff --git a/.github/workflows/render_nanomaps.yml b/.github/workflows/render_nanomaps.yml new file mode 100644 index 0000000000..6b58c9e8e0 --- /dev/null +++ b/.github/workflows/render_nanomaps.yml @@ -0,0 +1,35 @@ +# GitHub action to autorender nanomaps outside the game +# This kills off the awful verb we have that takes a full 50 seconds and hangs the whole server +# The file names and locations are VERY important here +# DO NOT EDIT THIS UNLESS YOU KNOW WHAT YOU ARE DOING +# -aa +name: 'Render Nanomaps' +on: + push: + branches: master + paths: + - 'maps/**' + +jobs: + generate_maps: + name: 'Generate NanoMaps' + runs-on: ubuntu-18.04 + steps: + - name: 'Update Branch' + uses: actions/checkout@v2 + with: + fetch-depth: 1 + + - name: 'Generate Maps' + run: './tools/github-actions/nanomap-renderer-invoker.sh' + + - name: 'Commit Maps' + run: | + git config --local user.email "action@github.com" + git config --local user.name "NanoMap Generation" + git pull origin master + git commit -m "NanoMap Auto-Update (`date`)" -a || true + - name: 'Push Maps' + uses: ad-m/github-push-action@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index 2780275b94..51ceb23af6 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Going to make a Pull Request? Make sure you read the [CONTRIBUTING.md](.github/C VOREStation is a fork of the Polaris code branch, itself a fork of the Baystation12 code branch, for the game Space Station 13. +![Render Nanomaps](https://github.com/VOREStation/VOREStation/workflows/Render%20Nanomaps/badge.svg) + --- ### LICENSE diff --git a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm index dd38dae3ca..e960d36fa7 100644 --- a/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm +++ b/code/ATMOSPHERICS/components/binary_devices/passive_gate.dm @@ -13,6 +13,7 @@ desc = "A one-way air valve that can be used to regulate input or output pressure, and flow rate. Does not require power." use_power = USE_POWER_OFF + interact_offline = TRUE var/unlocked = 0 //If 0, then the valve is locked closed, otherwise it is open(-able, it's a one-way valve so it closes if gas would flow backwards). var/target_pressure = ONE_ATMOSPHERE @@ -216,7 +217,7 @@ tgui_interact(user) /obj/machinery/atmospherics/binary/passive_gate/tgui_interact(mob/user, datum/tgui/ui) - if(stat & (BROKEN|NOPOWER)) + if(stat & BROKEN) return FALSE ui = SStgui.try_update_ui(user, src, ui) if(!ui) diff --git a/code/ATMOSPHERICS/components/omni_devices/filter.dm b/code/ATMOSPHERICS/components/omni_devices/filter.dm index 56171a0880..00e2f9d5dc 100644 --- a/code/ATMOSPHERICS/components/omni_devices/filter.dm +++ b/code/ATMOSPHERICS/components/omni_devices/filter.dm @@ -33,8 +33,10 @@ return ..() /obj/machinery/atmospherics/omni/atmos_filter/sort_ports() + var/any_updated = FALSE for(var/datum/omni_port/P in ports) if(P.update) + any_updated = TRUE if(output == P) output = null if(input == P) @@ -50,6 +52,8 @@ output = P if(ATM_O2 to ATM_N2O) atmos_filters += P + if(any_updated) + rebuild_filtering_list() /obj/machinery/atmospherics/omni/atmos_filter/error_check() if(!input || !output || !atmos_filters) @@ -231,7 +235,6 @@ target_port.mode = mode if(target_port.mode != previous_mode) handle_port_change(target_port) - rebuild_filtering_list() else return else diff --git a/code/ATMOSPHERICS/pipes/tank.dm b/code/ATMOSPHERICS/pipes/tank.dm index 54f90d9521..a893facff6 100644 --- a/code/ATMOSPHERICS/pipes/tank.dm +++ b/code/ATMOSPHERICS/pipes/tank.dm @@ -70,10 +70,6 @@ if(istype(W, /obj/item/device/pipe_painter)) return - if(istype(W, /obj/item/device/analyzer) && in_range(user, src)) - var/obj/item/device/analyzer/A = W - A.analyze_gases(src, user) - /obj/machinery/atmospherics/pipe/tank/air name = "Pressure Tank (Air)" icon_state = "air_map" diff --git a/code/__defines/pda.dm b/code/__defines/pda.dm new file mode 100644 index 0000000000..c5d32f03ba --- /dev/null +++ b/code/__defines/pda.dm @@ -0,0 +1,3 @@ +#define PDA_APP_UPDATE 0 +#define PDA_APP_NOUPDATE 1 +#define PDA_APP_UPDATE_SLOW 2 diff --git a/code/_helpers/text.dm b/code/_helpers/text.dm index 02a8806089..a9aac1ffcf 100644 --- a/code/_helpers/text.dm +++ b/code/_helpers/text.dm @@ -21,6 +21,27 @@ /* * Text sanitization */ +// Can be used almost the same way as normal input for text +/proc/clean_input(Message, Title, Default, mob/user=usr) + var/txt = input(user, Message, Title, Default) as text | null + if(txt) + return html_encode(txt) + +//Simply removes < and > and limits the length of the message +/proc/strip_html_simple(var/t,var/limit=MAX_MESSAGE_LEN) + var/list/strip_chars = list("<",">") + t = copytext(t,1,limit) + for(var/char in strip_chars) + var/index = findtext(t, char) + while(index) + t = copytext(t, 1, index) + copytext(t, index+1) + index = findtext(t, char) + return t + +//Runs byond's sanitization proc along-side strip_html_simple +//I believe strip_html_simple() is required to run first to prevent '<' from displaying as '<' that html_encode() would cause +/proc/adminscrub(var/t,var/limit=MAX_MESSAGE_LEN) + return copytext((html_encode(strip_html_simple(t))),1,limit) //Used for preprocessing entered text /proc/sanitize(var/input, var/max_length = MAX_MESSAGE_LEN, var/encode = 1, var/trim = 1, var/extra = 1) diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 859884a8b7..76b18f7222 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -141,8 +141,9 @@ return 1 /obj/machinery/turretid/AICtrlClick() //turns off/on Turrets - Topic(src, list("command"="enable", "value"="[!enabled]")) - return 1 + enabled = !enabled + updateTurrets() + return TRUE /atom/proc/AIAltClick(var/atom/A) return AltClick(A) @@ -156,8 +157,10 @@ return 1 /obj/machinery/turretid/AIAltClick() //toggles lethal on turrets - Topic(src, list("command"="lethal", "value"="[!lethal]")) - return 1 + if(lethal_is_configurable) + lethal = !lethal + updateTurrets() + return TRUE /atom/proc/AIMiddleClick(var/mob/living/silicon/user) return 0 diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm index 66cf3d1c12..5e5f044ac2 100644 --- a/code/_onclick/hud/screen_objects.dm +++ b/code/_onclick/hud/screen_objects.dm @@ -515,7 +515,7 @@ if("Show Crew Manifest") if(isAI(usr)) var/mob/living/silicon/ai/AI = usr - AI.ai_roster() + AI.subsystem_crew_manifest() if("Show Alerts") if(isAI(usr)) @@ -540,12 +540,14 @@ if("PDA - Send Message") if(isAI(usr)) var/mob/living/silicon/ai/AI = usr - AI.aiPDA.cmd_send_pdamesg(usr) + AI.aiPDA.start_program(AI.aiPDA.find_program(/datum/data/pda/app/messenger)) + AI.aiPDA.cmd_pda_open_ui(usr) if("PDA - Show Message Log") if(isAI(usr)) var/mob/living/silicon/ai/AI = usr - AI.aiPDA.cmd_show_message_log(usr) + AI.aiPDA.start_program(AI.aiPDA.find_program(/datum/data/pda/app/messenger)) + AI.aiPDA.cmd_pda_open_ui(usr) if("Take Image") if(isAI(usr)) diff --git a/code/controllers/subsystems/nanoui.dm b/code/controllers/subsystems/nanoui.dm deleted file mode 100644 index c916eb2739..0000000000 --- a/code/controllers/subsystems/nanoui.dm +++ /dev/null @@ -1,22 +0,0 @@ -SUBSYSTEM_DEF(nanoui) - name = "NanoUI" - wait = 5 - flags = SS_NO_INIT - // a list of current open /nanoui UIs, grouped by src_object and ui_key - var/list/open_uis = list() - // a list of current open /nanoui UIs, not grouped, for use in processing - var/list/processing_uis = list() - -/datum/controller/subsystem/nanoui/Recover() - if(SSnanoui.open_uis) - open_uis |= SSnanoui.open_uis - if(SSnanoui.processing_uis) - processing_uis |= SSnanoui.processing_uis - -/datum/controller/subsystem/nanoui/stat_entry() - return ..("[processing_uis.len] UIs") - -/datum/controller/subsystem/nanoui/fire(resumed) - for(var/thing in processing_uis) - var/datum/nanoui/UI = thing - UI.process() diff --git a/code/controllers/subsystems/persistence.dm b/code/controllers/subsystems/persistence.dm index c2724c2d60..00c08363de 100644 --- a/code/controllers/subsystems/persistence.dm +++ b/code/controllers/subsystems/persistence.dm @@ -31,7 +31,7 @@ SUBSYSTEM_DEF(persistence) return // if((!T.z in GLOB.using_map.station_levels) || !initialized) - if(!T.z in using_map.station_levels) + if(!(T.z in using_map.station_levels)) return if(!tracking_values[track_type]) diff --git a/code/datums/datum.dm b/code/datums/datum.dm index 61eb4c6953..d806728f5d 100644 --- a/code/datums/datum.dm +++ b/code/datums/datum.dm @@ -31,5 +31,5 @@ weakref = null // Clear this reference to ensure it's kept for as brief duration as possible. tag = null - SSnanoui.close_uis(src) + SStgui.close_uis(src) return QDEL_HINT_QUEUE diff --git a/code/datums/mind.dm b/code/datums/mind.dm index 10a7b4b7b9..bca9df5ae1 100644 --- a/code/datums/mind.dm +++ b/code/datums/mind.dm @@ -85,7 +85,6 @@ current.verbs -= /datum/changeling/proc/EvolutionMenu current.mind = null - SSnanoui.user_transferred(current, new_character) // transfer active NanoUI instances to new user if(new_character.mind) //remove any mind currently in our new body's mind variable new_character.mind.current = null diff --git a/code/datums/repositories/crew.dm b/code/datums/repositories/crew.dm index 7a748049bc..c45202194b 100644 --- a/code/datums/repositories/crew.dm +++ b/code/datums/repositories/crew.dm @@ -38,9 +38,10 @@ var/global/datum/repository/crew/crew_repository = new() crewmemberData["assignment"] = H.get_assignment(if_no_id="Unknown", if_no_job="No Job") if(C.sensor_mode >= SUIT_SENSOR_BINARY) - crewmemberData["dead"] = H.stat > UNCONSCIOUS + crewmemberData["dead"] = H.stat == DEAD if(C.sensor_mode >= SUIT_SENSOR_VITAL) + crewmemberData["stat"] = H.stat crewmemberData["oxy"] = round(H.getOxyLoss(), 1) crewmemberData["tox"] = round(H.getToxLoss(), 1) crewmemberData["fire"] = round(H.getFireLoss(), 1) diff --git a/code/datums/uplink/announcements.dm b/code/datums/uplink/announcements.dm index 1c4a448cee..e85541a55d 100644 --- a/code/datums/uplink/announcements.dm +++ b/code/datums/uplink/announcements.dm @@ -25,16 +25,7 @@ return list("title" = title, "message" = message) /datum/uplink_item/abstract/announcements/fake_centcom/get_goods(var/obj/item/device/uplink/U, var/loc, var/mob/user, var/list/args) - for (var/obj/machinery/computer/communications/C in machines) - if(! (C.stat & (BROKEN|NOPOWER) ) ) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( C.loc ) - P.name = "'[command_name()] Update.'" - P.info = replacetext(args["message"], "\n", "
") - P.update_space(P.info) - P.update_icon() - C.messagetitle.Add(args["title"]) - C.messagetext.Add(P.info) - + post_comm_message(args["title"], replacetext(args["message"], "\n", "
")) command_announcement.Announce(args["message"], args["title"]) return 1 diff --git a/code/datums/uplink/implants.dm b/code/datums/uplink/implants.dm index bbbeaf9e7e..d1bb93acdc 100644 --- a/code/datums/uplink/implants.dm +++ b/code/datums/uplink/implants.dm @@ -73,3 +73,13 @@ name = "Integrated Surge Implant" item_cost = 40 path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/surge + +/datum/uplink_item/item/implants/imp_armblade + name = "Integrated Armblade Implant" + item_cost = 40 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/armblade + +/datum/uplink_item/item/implants/imp_handblade + name = "Integrated Handblade Implant" + item_cost = 25 + path = /obj/item/weapon/storage/box/syndie_kit/imp_aug/handblade diff --git a/code/datums/uplink/uplink_items.dm b/code/datums/uplink/uplink_items.dm index cca1933c9c..8d9de8f732 100644 --- a/code/datums/uplink/uplink_items.dm +++ b/code/datums/uplink/uplink_items.dm @@ -51,7 +51,7 @@ var/datum/uplink/uplink = new() if(!can_buy(U)) return - if(U.CanUseTopic(user, inventory_state) != STATUS_INTERACTIVE) + if(U.CanUseTopic(user, GLOB.tgui_inventory_state) != STATUS_INTERACTIVE) return var/cost = cost(U.uses, U) diff --git a/code/defines/obj/weapon.dm b/code/defines/obj/weapon.dm index 6c05e65438..8be56acbf3 100644 --- a/code/defines/obj/weapon.dm +++ b/code/defines/obj/weapon.dm @@ -10,6 +10,8 @@ w_class = ITEMSIZE_SMALL attack_verb = list("called", "rang") hitsound = 'sound/weapons/ring.ogg' + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' /obj/item/weapon/rsp name = "\improper Rapid-Seed-Producer (RSP)" @@ -22,6 +24,8 @@ var/stored_matter = 0 var/mode = 1 w_class = ITEMSIZE_NORMAL + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' /obj/item/weapon/soap name = "soap" @@ -35,7 +39,6 @@ throwforce = 0 throw_speed = 4 throw_range = 20 - drop_sound = 'sound/misc/slip.ogg' /obj/item/weapon/soap/nanotrasen desc = "A NanoTrasen-brand bar of soap. Smells of phoron." @@ -149,6 +152,8 @@ throw_range = 20 matter = list(DEFAULT_WALL_MATERIAL = 100) origin_tech = list(TECH_MAGNET = 1) + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' /obj/item/weapon/staff name = "wizards staff" @@ -196,6 +201,8 @@ item_state = "std_mod" w_class = ITEMSIZE_SMALL var/mtype = 1 // 1=electronic 2=hardware + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' /obj/item/weapon/module/card_reader name = "card reader module" @@ -227,7 +234,6 @@ item_state = "std_mod" desc = "Charging circuits for power cells." - /obj/item/device/camera_bug name = "camera bug" icon = 'icons/obj/device.dmi' @@ -303,6 +309,8 @@ display_contents_with_number = 1 max_w_class = ITEMSIZE_NORMAL max_storage_space = 100 + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' /obj/item/weapon/storage/part_replacer/adv name = "advanced rapid part exchange device" @@ -326,7 +334,8 @@ icon = 'icons/obj/stock_parts.dmi' w_class = ITEMSIZE_SMALL var/rating = 1 - drop_sound = 'sound/items/drop/glass.ogg' + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' /obj/item/weapon/stock_parts/New() src.pixel_x = rand(-5.0, 5) diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm index 1d48349cb5..03185d31d3 100644 --- a/code/game/dna/dna2.dm +++ b/code/game/dna/dna2.dm @@ -276,7 +276,7 @@ var/global/list/datum/dna/gene/dna_genes[0] // Set a DNA UI block's raw value. /datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0) if (block<=0) return - ASSERT(value>0) + ASSERT(value>=0) ASSERT(value<=4095) UI[block]=value dirtyUI=1 @@ -292,7 +292,6 @@ var/global/list/datum/dna/gene/dna_genes[0] // Used in hair and facial styles (value being the index and maxvalue being the len of the hairstyle list) /datum/dna/proc/SetUIValueRange(var/block,var/value,var/maxvalue,var/defer=0) if (block<=0) return - if (value==0) value = 1 // FIXME: hair/beard/eye RGB values if they are 0 are not set, this is a work around we'll encode it in the DNA to be 1 instead. ASSERT(maxvalue<=4095) var/range = (4095 / maxvalue) if(value) @@ -302,7 +301,7 @@ var/global/list/datum/dna/gene/dna_genes[0] /datum/dna/proc/GetUIValueRange(var/block,var/maxvalue) if (block<=0) return 0 var/value = GetUIValue(block) - return round(0.5 + (value / 4096) * maxvalue) + return round(0.5 + (value / 4095) * maxvalue) // Is the UI gene "on" or "off"? // For UI, this is simply a check of if the value is > 2050. @@ -388,7 +387,7 @@ var/global/list/datum/dna/gene/dna_genes[0] /datum/dna/proc/GetSEValueRange(var/block,var/maxvalue) if (block<=0) return 0 var/value = GetSEValue(block) - return round(1 +(value / 4096)*maxvalue) + return round(1 +(value / 4095)*maxvalue) // Is the block "on" (1) or "off" (0)? (Un-assigned genes are always off.) /datum/dna/proc/GetSEState(var/block) diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm index f4fcffbd34..cd3ec6a44d 100644 --- a/code/game/dna/dna2_helpers.dm +++ b/code/game/dna/dna2_helpers.dm @@ -177,7 +177,7 @@ // Ears var/ears = dna.GetUIValueRange(DNA_UI_EAR_STYLE, ear_styles_list.len + 1) - 1 - if(ears <= 1) + if(ears < 1) H.ear_style = null else if((0 < ears) && (ears <= ear_styles_list.len)) H.ear_style = ear_styles_list[ear_styles_list[ears]] @@ -192,14 +192,14 @@ //Tail var/tail = dna.GetUIValueRange(DNA_UI_TAIL_STYLE, tail_styles_list.len + 1) - 1 - if(tail <= 1) + if(tail < 1) H.tail_style = null else if((0 < tail) && (tail <= tail_styles_list.len)) H.tail_style = tail_styles_list[tail_styles_list[tail]] //Wing var/wing = dna.GetUIValueRange(DNA_UI_WING_STYLE, wing_styles_list.len + 1) - 1 - if(wing <= 1) + if(wing < 1) H.wing_style = null else if((0 < wing) && (wing <= wing_styles_list.len)) H.wing_style = wing_styles_list[wing_styles_list[wing]] diff --git a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm index b90b402e78..6ec87a64d4 100644 --- a/code/game/gamemodes/changeling/powers/fabricate_clothing.dm +++ b/code/game/gamemodes/changeling/powers/fabricate_clothing.dm @@ -286,5 +286,5 @@ var/global/list/changeling_fabricated_clothing = list( if(!registered_user) registered_user = usr usr.set_id_info(src) - ui_interact(registered_user) + tgui_interact(registered_user) ..() \ No newline at end of file diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm index 4ee64999fa..b49c41f30f 100644 --- a/code/game/jobs/access.dm +++ b/code/game/jobs/access.dm @@ -180,7 +180,7 @@ /proc/get_access_by_id(id) var/list/AS = get_all_access_datums_by_id() - return AS[id] + return AS["[id]"] /proc/get_all_jobs() var/list/all_jobs = list() diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm index 403efbf4fe..3aec6c8c37 100644 --- a/code/game/jobs/job_controller.dm +++ b/code/game/jobs/job_controller.dm @@ -540,6 +540,8 @@ var/global/datum/controller/occupations/job_master // EMAIL GENERATION // Email addresses will be created under this domain name. Mostly for the looks. var/domain = "freemail.nt" + if(using_map && LAZYLEN(using_map.usable_email_tlds)) + domain = using_map.usable_email_tlds[1] var/sanitized_name = sanitize(replacetext(replacetext(lowertext(H.real_name), " ", "."), "'", "")) var/complete_login = "[sanitized_name]@[domain]" diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 6fe30aeaab..2d5e39b1b7 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -260,7 +260,7 @@ update_flag ..() - SSnanoui.update_uis(src) // Update all NanoUIs attached to src + SStgui.update_uis(src) // Update all NanoUIs attached to src /obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob) return src.attack_hand(user) diff --git a/code/game/machinery/atmoalter/portable_atmospherics.dm b/code/game/machinery/atmoalter/portable_atmospherics.dm index 915fb8fac1..dc530ecc2f 100644 --- a/code/game/machinery/atmoalter/portable_atmospherics.dm +++ b/code/game/machinery/atmoalter/portable_atmospherics.dm @@ -136,12 +136,6 @@ else to_chat(user, "Nothing happens.") return - - else if ((istype(W, /obj/item/device/analyzer)) && Adjacent(user)) - var/obj/item/device/analyzer/A = W - A.analyze_gases(src, user) - return - return diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 73b78cb148..955ccf596a 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -204,7 +204,9 @@ else P = W itemname = P.name - info = P.notehtml + var/datum/data/pda/app/notekeeper/N = P.find_program(/datum/data/pda/app/notekeeper) + if(N) + info = N.notehtml to_chat(U, "You hold \a [itemname] up to the camera ...") for(var/mob/living/silicon/ai/O in living_mob_list) if(!O.client) diff --git a/code/game/machinery/computer/arcade.dm b/code/game/machinery/computer/arcade.dm index 8ef01c9b64..6921cf989c 100644 --- a/code/game/machinery/computer/arcade.dm +++ b/code/game/machinery/computer/arcade.dm @@ -92,8 +92,11 @@ var/blocked = 0 //Player cannot attack/heal while set var/turtle = 0 -/obj/machinery/computer/arcade/battle/New() - ..() +/obj/machinery/computer/arcade/battle/Initialize() + . = ..() + randomize_characters() + +/obj/machinery/computer/arcade/battle/proc/randomize_characters() var/name_action var/name_part1 var/name_part2 @@ -111,17 +114,17 @@ if(..()) return user.set_machine(src) - ui_interact(user) + tgui_interact(user) -/** - * Display the NanoUI window for the arcade machine. - * - * See NanoUI documentation for details. - */ -/obj/machinery/computer/arcade/battle/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) +/obj/machinery/computer/arcade/battle/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ArcadeBattle", name) + ui.open() - var/list/data = list() +/obj/machinery/computer/arcade/battle/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + data["name"] = name data["temp"] = temp data["enemyAction"] = enemy_action data["enemyName"] = enemy_name @@ -129,59 +132,54 @@ data["playerMP"] = player_mp data["enemyHP"] = enemy_hp data["gameOver"] = gameover + return data - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "arcade_battle.tmpl", src.name, 400, 300) - ui.set_initial_data(data) - ui.open() - //ui.set_auto_update(2) - -/obj/machinery/computer/arcade/battle/Topic(href, href_list) +/obj/machinery/computer/arcade/battle/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 + return TRUE - if (!blocked && !gameover) - if (href_list["attack"]) - blocked = 1 - var/attackamt = rand(2,6) - temp = "You attack for [attackamt] damage!" - playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - if(turtle > 0) - turtle-- + if(!blocked && !gameover) + switch(action) + if("attack") + blocked = 1 + var/attackamt = rand(2,6) + temp = "You attack for [attackamt] damage!" + playsound(src, 'sound/arcade/hit.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + if(turtle > 0) + turtle-- - sleep(10) - enemy_hp -= attackamt - arcade_action() + sleep(10) + enemy_hp -= attackamt + arcade_action() - else if (href_list["heal"]) - blocked = 1 - var/pointamt = rand(1,3) - var/healamt = rand(6,8) - temp = "You use [pointamt] magic to heal for [healamt] damage!" - playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - turtle++ + if("heal") + blocked = 1 + var/pointamt = rand(1,3) + var/healamt = rand(6,8) + temp = "You use [pointamt] magic to heal for [healamt] damage!" + playsound(src, 'sound/arcade/heal.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + turtle++ - sleep(10) - player_mp -= pointamt - player_hp += healamt - blocked = 1 - arcade_action() + sleep(10) + player_mp -= pointamt + player_hp += healamt + blocked = 1 + arcade_action() - else if (href_list["charge"]) - blocked = 1 - var/chargeamt = rand(4,7) - temp = "You regain [chargeamt] points" - playsound(src, 'sound/arcade/mana.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) - player_mp += chargeamt - if(turtle > 0) - turtle-- + if("charge") + blocked = 1 + var/chargeamt = rand(4,7) + temp = "You regain [chargeamt] points" + playsound(src, 'sound/arcade/mana.ogg', 50, 1, extrarange = -3, falloff = 0.1, ignore_walls = FALSE) + player_mp += chargeamt + if(turtle > 0) + turtle-- - sleep(10) - arcade_action() + sleep(10) + arcade_action() - else if (href_list["newgame"]) //Reset everything + if(action == "newgame") //Reset everything temp = "New Round" player_hp = 30 player_mp = 10 @@ -191,12 +189,11 @@ turtle = 0 if(emagged) - src.New() + randomize_characters() emagged = 0 - src.add_fingerprint(usr) - SSnanoui.update_uis(src) - return + add_fingerprint(usr) + return TRUE /obj/machinery/computer/arcade/battle/proc/arcade_action() if ((enemy_mp <= 0) || (enemy_hp <= 0)) @@ -211,7 +208,7 @@ new /obj/item/clothing/head/collectable/petehat(src.loc) message_admins("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") log_game("[key_name_admin(usr)] has outbombed Cuban Pete and been awarded a bomb.") - src.New() + randomize_characters() emagged = 0 else if(!contents.len) feedback_inc("arcade_win_normal") diff --git a/code/game/machinery/computer/camera.dm b/code/game/machinery/computer/camera.dm index 97d68e190a..29783c4c9c 100644 --- a/code/game/machinery/computer/camera.dm +++ b/code/game/machinery/computer/camera.dm @@ -36,6 +36,13 @@ return tgui_interact(user) +/obj/machinery/computer/security/attack_robot(mob/user) + if(isrobot(user)) + var/mob/living/silicon/robot/R = user + if(!R.shell) + return attack_hand(user) + ..() + /obj/machinery/computer/security/attack_ai(mob/user) if(isAI(user)) to_chat(user, "You realise its kind of stupid to access a camera console when you have the entire camera network at your metaphorical fingertips") diff --git a/code/game/machinery/computer/card.dm b/code/game/machinery/computer/card.dm index 7833a58d72..0f52e67b8c 100644 --- a/code/game/machinery/computer/card.dm +++ b/code/game/machinery/computer/card.dm @@ -26,7 +26,7 @@ var/list/formatted = list() for(var/job in jobs) formatted.Add(list(list( - "display_name" = replacetext(job, " ", " "), + "display_name" = replacetext(job, " ", " "), "target_rank" = get_target_rank(), "job" = job))) @@ -68,7 +68,7 @@ id_card.forceMove(src) modify = id_card - SSnanoui.update_uis(src) + SStgui.update_uis(src) attack_hand(user) /obj/machinery/computer/card/attack_ai(var/mob/user as mob) @@ -77,20 +77,27 @@ /obj/machinery/computer/card/attack_hand(mob/user as mob) if(..()) return if(stat & (NOPOWER|BROKEN)) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/card/ui_interact(mob/user, ui_key="main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) +/obj/machinery/computer/card/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "IdentificationComputer", name) + ui.open() +/obj/machinery/computer/card/tgui_static_data(mob/user) + var/list/data = ..() if(data_core) data_core.get_manifest_list() + data["manifest"] = PDA_Manifest + return data + +/obj/machinery/computer/card/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() - var/data[0] - data["src"] = "\ref[src]" data["station_name"] = station_name() data["mode"] = mode data["printing"] = printing - data["manifest"] = PDA_Manifest data["target_name"] = modify ? modify.name : "-----" data["target_owner"] = modify && modify.registered_name ? modify.registered_name : "-----" data["target_rank"] = get_target_rank() @@ -110,27 +117,27 @@ continue if(dept.centcom_only && !is_centcom()) continue - departments[++departments.len] = list("department_name" = dept.name, "jobs" = format_jobs(SSjob.get_job_titles_in_department(dept.name)) ) - + departments.Add(list(list( + "department_name" = dept.name, + "jobs" = format_jobs(SSjob.get_job_titles_in_department(dept.name)) + ))) data["departments"] = departments - if (modify && is_centcom()) - var/list/all_centcom_access = list() + var/list/all_centcom_access = list() + var/list/regions = list() + if(modify && is_centcom()) for(var/access in get_all_centcom_access()) all_centcom_access.Add(list(list( - "desc" = replacetext(get_centcom_access_desc(access), " ", " "), + "desc" = replacetext(get_centcom_access_desc(access), " ", " "), "ref" = access, "allowed" = (access in modify.access) ? 1 : 0))) - - data["all_centcom_access"] = all_centcom_access - else if (modify) - var/list/regions = list() - for(var/i = 1; i <= 7; i++) + else if(modify) + for(var/i in ACCESS_REGION_SECURITY to ACCESS_REGION_SUPPLY) var/list/accesses = list() for(var/access in get_region_accesses(i)) if (get_access_desc(access)) accesses.Add(list(list( - "desc" = replacetext(get_access_desc(access), " ", " "), + "desc" = replacetext(get_access_desc(access), " ", " "), "ref" = access, "allowed" = (access in modify.access) ? 1 : 0))) @@ -138,23 +145,20 @@ "name" = get_region_accesses_name(i), "accesses" = accesses))) - data["regions"] = regions + data["regions"] = regions + data["all_centcom_access"] = all_centcom_access - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "identification_computer.tmpl", src.name, 600, 700) - ui.set_initial_data(data) - ui.open() + return data -/obj/machinery/computer/card/Topic(href, href_list) +/obj/machinery/computer/card/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 + return TRUE - switch(href_list["choice"]) - if ("modify") - if (modify) + switch(action) + if("modify") + if(modify) data_core.manifest_modify(modify.registered_name, modify.assignment) - modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") + modify.name = "[modify.registered_name]'s ID Card ([modify.assignment])" if(ishuman(usr)) modify.forceMove(get_turf(src)) if(!usr.get_active_hand()) @@ -165,12 +169,13 @@ modify = null else var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/weapon/card/id) && usr.unEquip(I)) + if(istype(I, /obj/item/weapon/card/id) && usr.unEquip(I)) I.forceMove(src) modify = I + . = TRUE - if ("scan") - if (scan) + if("scan") + if(scan) if(ishuman(usr)) scan.forceMove(get_turf(src)) if(!usr.get_active_hand()) @@ -181,25 +186,26 @@ scan = null else var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/weapon/card/id)) + if(istype(I, /obj/item/weapon/card/id)) usr.drop_item() I.forceMove(src) scan = I + . = TRUE if("access") - if(href_list["allowed"]) - if(is_authenticated()) - var/access_type = text2num(href_list["access_target"]) - var/access_allowed = text2num(href_list["allowed"]) - if(access_type in (is_centcom() ? get_all_centcom_access() : get_all_station_access())) - modify.access -= access_type - if(!access_allowed) - modify.access += access_type - modify.lost_access = list() //VOREStation addition: reset the lost access upon any modifications + if(is_authenticated()) + var/access_type = text2num(params["access_target"]) + var/access_allowed = text2num(params["allowed"]) + if(access_type in (is_centcom() ? get_all_centcom_access() : get_all_station_access())) + modify.access -= access_type + if(!access_allowed) + modify.access += access_type + modify.lost_access = list() //VOREStation addition: reset the lost access upon any modifications + . = TRUE - if ("assign") - if (is_authenticated() && modify) - var/t1 = href_list["assign_target"] + if("assign") + if(is_authenticated() && modify) + var/t1 = params["assign_target"] if(t1 == "Custom") var/temp_t = sanitize(input("Enter a custom job assignment.","Assignment"), 45) //let custom jobs function as an impromptu alt title, mainly for sechuds @@ -222,44 +228,42 @@ modify.lost_access = list() //VOREStation addition: reset the lost access upon any modifications callHook("reassign_employee", list(modify)) + . = TRUE - if ("reg") - if (is_authenticated()) - var/t2 = modify - if ((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) - var/temp_name = sanitizeName(href_list["reg"]) - if(temp_name) - modify.registered_name = temp_name - else - src.visible_message("[src] buzzes rudely.") - SSnanoui.update_uis(src) + if("reg") + if(is_authenticated()) + var/temp_name = sanitizeName(params["reg"]) + if(temp_name) + modify.registered_name = temp_name + else + visible_message("[src] buzzes rudely.") + . = TRUE - if ("account") - if (is_authenticated()) - var/t2 = modify - if ((modify == t2 && (in_range(src, usr) || (istype(usr, /mob/living/silicon))) && istype(loc, /turf))) - var/account_num = text2num(href_list["account"]) - modify.associated_account_number = account_num - SSnanoui.update_uis(src) + if("account") + if(is_authenticated()) + var/account_num = text2num(params["account"]) + modify.associated_account_number = account_num + . = TRUE - if ("mode") - mode = text2num(href_list["mode_target"]) + if("mode") + mode = text2num(params["mode_target"]) + . = TRUE - if ("print") - if (!printing) + if("print") + if(!printing) printing = 1 spawn(50) printing = null - SSnanoui.update_uis(src) + SStgui.update_uis(src) var/obj/item/weapon/paper/P = new(loc) - if (mode) + if(mode) P.name = text("crew manifest ([])", stationtime2text()) P.info = {"

Crew Manifest


[data_core ? data_core.get_manifest(0) : ""] "} - else if (modify) + else if(modify) P.name = "access report" P.info = {"

Access Report

Prepared By: [scan.registered_name ? scan.registered_name : "Unknown"]
@@ -273,19 +277,20 @@ for(var/A in modify.access) P.info += " [get_access_desc(A)]" + . = TRUE - if ("terminate") - if (is_authenticated()) + if("terminate") + if(is_authenticated()) modify.assignment = "Dismissed" //VOREStation Edit: setting adjustment modify.access = list() modify.lost_access = list() //VOREStation addition: reset the lost access upon any modifications callHook("terminate_employee", list(modify)) - if (modify) - modify.name = text("[modify.registered_name]'s ID Card ([modify.assignment])") + . = TRUE - return 1 + if(modify) + modify.name = "[modify.registered_name]'s ID Card ([modify.assignment])" /obj/machinery/computer/card/centcom name = "\improper CentCom ID card modification console" diff --git a/code/game/machinery/computer/communications.dm b/code/game/machinery/computer/communications.dm index 9333e32556..e878aaaf8a 100644 --- a/code/game/machinery/computer/communications.dm +++ b/code/game/machinery/computer/communications.dm @@ -9,553 +9,24 @@ light_color = "#0099ff" req_access = list(access_heads) circuit = /obj/item/weapon/circuitboard/communications - var/prints_intercept = 1 - var/authenticated = 0 - var/list/messagetitle = list() - var/list/messagetext = list() - var/currmsg = 0 - var/aicurrmsg = 0 - var/state = STATE_DEFAULT - var/aistate = STATE_DEFAULT - var/message_cooldown = 0 - var/centcomm_message_cooldown = 0 - var/tmp_alertlevel = 0 - var/const/STATE_DEFAULT = 1 - var/const/STATE_CALLSHUTTLE = 2 - var/const/STATE_CANCELSHUTTLE = 3 - var/const/STATE_MESSAGELIST = 4 - var/const/STATE_VIEWMESSAGE = 5 - var/const/STATE_DELMESSAGE = 6 - var/const/STATE_STATUSDISPLAY = 7 - var/const/STATE_ALERT_LEVEL = 8 - var/const/STATE_CONFIRM_LEVEL = 9 - var/const/STATE_CREWTRANSFER = 10 - var/status_display_freq = "1435" - var/stat_msg1 - var/stat_msg2 + var/datum/tgui_module/communications/communications - var/datum/lore/atc_controller/ATC - var/datum/announcement/priority/crew_announcement = new - -/obj/machinery/computer/communications/New() - ..() - ATC = atc - crew_announcement.newscast = 1 - -/obj/machinery/computer/communications/process() - if(..()) - if(state != STATE_STATUSDISPLAY) - src.updateDialog() - - -/obj/machinery/computer/communications/Topic(href, href_list) - if(..()) - return 1 - if (using_map && !(src.z in using_map.contact_levels)) - to_chat(usr, "Unable to establish a connection: You're too far away from the station!") - return - usr.set_machine(src) - - if(!href_list["operation"]) - return - switch(href_list["operation"]) - // main interface - if("main") - src.state = STATE_DEFAULT - if("login") - var/mob/M = usr - var/obj/item/weapon/card/id/I = M.GetIdCard() - if (I && istype(I)) - if(src.check_access(I)) - authenticated = 1 - if(access_captain in I.access) - authenticated = 2 - crew_announcement.announcer = GetNameAndAssignmentFromId(I) - if("logout") - authenticated = 0 - crew_announcement.announcer = "" - - if("swipeidseclevel") - if(src.authenticated) //Let heads change the alert level. - var/old_level = security_level - if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN - if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this - set_security_level(tmp_alertlevel) - if(security_level != old_level) - //Only notify the admins if an actual change happened - log_game("[key_name(usr)] has changed the security level to [get_security_level()].") - message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") - switch(security_level) - if(SEC_LEVEL_GREEN) - feedback_inc("alert_comms_green",1) - if(SEC_LEVEL_YELLOW) - feedback_inc("alert_comms_yellow",1) - if(SEC_LEVEL_VIOLET) - feedback_inc("alert_comms_violet",1) - if(SEC_LEVEL_ORANGE) - feedback_inc("alert_comms_orange",1) - if(SEC_LEVEL_BLUE) - feedback_inc("alert_comms_blue",1) - tmp_alertlevel = 0 - state = STATE_DEFAULT - - if("announce") - if(src.authenticated==2) - if(message_cooldown) - to_chat(usr, "Please allow at least one minute to pass between announcements") - return - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|message - if(!input || !(usr in view(1,src))) - return - crew_announcement.Announce(input) - message_cooldown = 1 - spawn(600)//One minute cooldown - message_cooldown = 0 - - if("callshuttle") - src.state = STATE_DEFAULT - if(src.authenticated) - src.state = STATE_CALLSHUTTLE - if("callshuttle2") - if(src.authenticated) - call_shuttle_proc(usr) - if(emergency_shuttle.online()) - post_status("shuttle") - src.state = STATE_DEFAULT - if("cancelshuttle") - src.state = STATE_DEFAULT - if(src.authenticated) - src.state = STATE_CANCELSHUTTLE - if("cancelshuttle2") - if(src.authenticated) - cancel_call_proc(usr) - src.state = STATE_DEFAULT - if("messagelist") - src.currmsg = 0 - src.state = STATE_MESSAGELIST - if("toggleatc") - src.ATC.squelched = !src.ATC.squelched - if("viewmessage") - src.state = STATE_VIEWMESSAGE - if (!src.currmsg) - if(href_list["message-num"]) - src.currmsg = text2num(href_list["message-num"]) - else - src.state = STATE_MESSAGELIST - if("delmessage") - src.state = (src.currmsg) ? STATE_DELMESSAGE : STATE_MESSAGELIST - if("delmessage2") - if(src.authenticated) - if(src.currmsg) - var/title = src.messagetitle[src.currmsg] - var/text = src.messagetext[src.currmsg] - src.messagetitle.Remove(title) - src.messagetext.Remove(text) - if(src.currmsg == src.aicurrmsg) - src.aicurrmsg = 0 - src.currmsg = 0 - src.state = STATE_MESSAGELIST - else - src.state = STATE_VIEWMESSAGE - if("status") - src.state = STATE_STATUSDISPLAY - - // Status display stuff - if("setstat") - switch(href_list["statdisp"]) - if("message") - post_status("message", stat_msg1, stat_msg2) - if("alert") - post_status("alert", href_list["alert"]) - else - post_status(href_list["statdisp"]) - - if("setmsg1") - stat_msg1 = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", stat_msg1) as text|null, 40), 40) - src.updateDialog() - if("setmsg2") - stat_msg2 = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", stat_msg2) as text|null, 40), 40) - src.updateDialog() - - // OMG CENTCOMM LETTERHEAD - if("MessageCentCom") - if(src.authenticated==2) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - return - var/input = sanitize(input("Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. \ - Please be aware that this process is very expensive, and abuse will lead to... termination. \ - Transmission does not guarantee a response. \ - There is a 30 second delay before you may send another message, be clear, full and concise.", "Central Command Quantum Messaging") as null|message) - if(!input || !(usr in view(1,src))) - return - CentCom_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made an IA [using_map.boss_short] announcement: [input]") - centcomm_message_cooldown = 1 - spawn(300)//10 minute cooldown - centcomm_message_cooldown = 0 - - - // OMG SYNDICATE ...LETTERHEAD - if("MessageSyndicate") - if((src.authenticated==2) && (src.emagged)) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - return - var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "")) - if(!input || !(usr in view(1,src))) - return - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_game("[key_name(usr)] has made an illegal announcement: [input]") - centcomm_message_cooldown = 1 - spawn(300)//10 minute cooldown - centcomm_message_cooldown = 0 - - if("RestoreBackup") - to_chat(usr, "Backup routing data restored!") - src.emagged = 0 - src.updateDialog() - - - - // AI interface - if("ai-main") - src.aicurrmsg = 0 - src.aistate = STATE_DEFAULT - if("ai-callshuttle") - src.aistate = STATE_CALLSHUTTLE - if("ai-callshuttle2") - call_shuttle_proc(usr) - src.aistate = STATE_DEFAULT - if("ai-messagelist") - src.aicurrmsg = 0 - src.aistate = STATE_MESSAGELIST - if("ai-viewmessage") - src.aistate = STATE_VIEWMESSAGE - if (!src.aicurrmsg) - if(href_list["message-num"]) - src.aicurrmsg = text2num(href_list["message-num"]) - else - src.aistate = STATE_MESSAGELIST - if("ai-delmessage") - src.aistate = (src.aicurrmsg) ? STATE_DELMESSAGE : STATE_MESSAGELIST - if("ai-delmessage2") - if(src.aicurrmsg) - var/title = src.messagetitle[src.aicurrmsg] - var/text = src.messagetext[src.aicurrmsg] - src.messagetitle.Remove(title) - src.messagetext.Remove(text) - if(src.currmsg == src.aicurrmsg) - src.currmsg = 0 - src.aicurrmsg = 0 - src.aistate = STATE_MESSAGELIST - if("ai-status") - src.aistate = STATE_STATUSDISPLAY - - if("securitylevel") - src.tmp_alertlevel = text2num( href_list["newalertlevel"] ) - if(!tmp_alertlevel) tmp_alertlevel = 0 - state = STATE_CONFIRM_LEVEL - - if("changeseclevel") - state = STATE_ALERT_LEVEL - - - - src.updateUsrDialog() +/obj/machinery/computer/communications/Initialize() + . = ..() + communications = new(src) /obj/machinery/computer/communications/emag_act(var/remaining_charges, var/mob/user) if(!emagged) - src.emagged = 1 + emagged = TRUE + communications.emagged = TRUE to_chat(user, "You scramble the communication routing circuits!") - return 1 + return TRUE -/obj/machinery/computer/communications/attack_ai(var/mob/user as mob) - return src.attack_hand(user) +/obj/machinery/computer/communications/attack_ai(mob/user) + return attack_hand(user) -/obj/machinery/computer/communications/attack_hand(var/mob/user as mob) +/obj/machinery/computer/communications/attack_hand(mob/user) if(..()) return - if (using_map && !(src.z in using_map.contact_levels)) - to_chat(user, "Unable to establish a connection: You're too far away from the station!") - return - - user.set_machine(src) - var/dat = "Communications Console" - if (emergency_shuttle.has_eta()) - var/timeleft = emergency_shuttle.estimate_arrival_time() - dat += "Emergency shuttle\n
\nETA: [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]
" - - if (istype(user, /mob/living/silicon)) - var/dat2 = src.interact_ai(user) // give the AI a different interact proc to limit its access - if(dat2) - dat += dat2 - user << browse(dat, "window=communications;size=400x500") - onclose(user, "communications") - return - - switch(src.state) - if(STATE_DEFAULT) - if (src.authenticated) - dat += "
\[ Log Out \]" - if (src.authenticated==2) - dat += "
\[ Make An Announcement \]" - if(src.emagged == 0) - dat += "
\[ Send an emergency message to [using_map.boss_short] \]" - else - dat += "
\[ Send an emergency message to \[UNKNOWN\] \]" - dat += "
\[ Restore Backup Routing Data \]" - - dat += "
\[ Change alert level \]" - if(emergency_shuttle.location()) - if (emergency_shuttle.online()) - dat += "
\[ Cancel Shuttle Call \]" - else - dat += "
\[ Call Emergency Shuttle \]" - - dat += "
\[ Set Status Display \]" - else - dat += "
\[ Log In \]" - dat += "
\[ Message List \]" - dat += "
\[ [ATC.squelched ? "Enable" : "Disable"] ATC Relay \]" - if(STATE_CALLSHUTTLE) - dat += "Are you sure you want to call the shuttle? \[ OK | Cancel \]" - if(STATE_CANCELSHUTTLE) - dat += "Are you sure you want to cancel the shuttle? \[ OK | Cancel \]" - if(STATE_MESSAGELIST) - dat += "Messages:" - for(var/i = 1; i<=src.messagetitle.len; i++) - dat += "
[src.messagetitle[i]]" - if(STATE_VIEWMESSAGE) - if (src.currmsg) - dat += "[src.messagetitle[src.currmsg]]

[src.messagetext[src.currmsg]]" - if (src.authenticated) - dat += "

\[ Delete \]" - else - src.state = STATE_MESSAGELIST - src.attack_hand(user) - return - if(STATE_DELMESSAGE) - if (src.currmsg) - dat += "Are you sure you want to delete this message? \[ OK | Cancel \]" - else - src.state = STATE_MESSAGELIST - src.attack_hand(user) - return - if(STATE_STATUSDISPLAY) - dat += "Set Status Displays
" - dat += "\[ Clear \]
" - dat += "\[ Station Time \]
" - dat += "\[ Shuttle ETA \]
" - dat += "\[ Message \]" - dat += "
" - dat += "\[ Alert: None |" - dat += " Red Alert |" - dat += " Lockdown |" - dat += " Biohazard \]

" - if(STATE_ALERT_LEVEL) - dat += "Current alert level: [get_security_level()]
" - if(security_level == SEC_LEVEL_DELTA) - dat += "The self-destruct mechanism is active. Find a way to deactivate the mechanism to lower the alert level or evacuate." - else - dat += "Blue
" - dat += "Orange
" - dat += "Violet
" - dat += "Yellow
" - dat += "Green" - if(STATE_CONFIRM_LEVEL) - dat += "Current alert level: [get_security_level()]
" - dat += "Confirm the change to: [num2seclevel(tmp_alertlevel)]
" - dat += "OK to confirm change.
" - - dat += "
\[ [(src.state != STATE_DEFAULT) ? "Main Menu | " : ""]Close \]" - user << browse(dat, "window=communications;size=400x500") - onclose(user, "communications") - - - - -/obj/machinery/computer/communications/proc/interact_ai(var/mob/living/silicon/ai/user as mob) - var/dat = "" - switch(src.aistate) - if(STATE_DEFAULT) - if(emergency_shuttle.location() && !emergency_shuttle.online()) - dat += "
\[ Call Emergency Shuttle \]" - dat += "
\[ Message List \]" - dat += "
\[ Set Status Display \]" - dat += "
\[ [ATC.squelched ? "Enable" : "Disable"] ATC Relay \]" - if(STATE_CALLSHUTTLE) - dat += "Are you sure you want to call the shuttle? \[ OK | Cancel \]" - if(STATE_MESSAGELIST) - dat += "Messages:" - for(var/i = 1; i<=src.messagetitle.len; i++) - dat += "
[src.messagetitle[i]]" - if(STATE_VIEWMESSAGE) - if (src.aicurrmsg) - dat += "[src.messagetitle[src.aicurrmsg]]

[src.messagetext[src.aicurrmsg]]" - dat += "

\[ Delete \]" - else - src.aistate = STATE_MESSAGELIST - src.attack_hand(user) - return null - if(STATE_DELMESSAGE) - if(src.aicurrmsg) - dat += "Are you sure you want to delete this message? \[ OK | Cancel \]" - else - src.aistate = STATE_MESSAGELIST - src.attack_hand(user) - return - - if(STATE_STATUSDISPLAY) - dat += "Set Status Displays
" - dat += "\[ Clear \]
" - dat += "\[ Station Time \]
" - dat += "\[ Shuttle ETA \]
" - dat += "\[ Message \]" - dat += "
" - dat += "\[ Alert: None |" - dat += " Red Alert |" - dat += " Lockdown |" - dat += " Biohazard \]

" - - - dat += "
\[ [(src.aistate != STATE_DEFAULT) ? "Main Menu | " : ""]Close \]" - return dat - -/proc/enable_prison_shuttle(var/mob/user) - for(var/obj/machinery/computer/prison_shuttle/PS in machines) - PS.allowedtocall = !(PS.allowedtocall) - -/proc/call_shuttle_proc(var/mob/user) - if ((!( ticker ) || !emergency_shuttle.location())) - return - - if(!universe.OnShuttleCall(usr)) - to_chat(user, "Cannot establish a bluespace connection.") - return - - if(deathsquad.deployed) - to_chat(user, "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(emergency_shuttle.deny_shuttle) - to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") - return - - if(world.time < 6000) // Ten minute grace period to let the game get going without lolmetagaming. -- TLE - to_chat(user, "The emergency shuttle is refueling. Please wait another [round((6000-world.time)/600)] minute\s before trying again.") - return - - if(emergency_shuttle.going_to_centcom()) - to_chat(user, "The emergency shuttle may not be called while returning to [using_map.boss_short].") - return - - if(emergency_shuttle.online()) - to_chat(user, "The emergency shuttle is already on its way.") - return - - if(ticker.mode.name == "blob") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return - - emergency_shuttle.call_evac() - log_game("[key_name(user)] has called the shuttle.") - message_admins("[key_name_admin(user)] has called the shuttle.", 1) - admin_chat_message(message = "Emergency evac beginning! Called by [key_name(user)]!", color = "#CC2222") //VOREStation Add - - - return - -/proc/init_shift_change(var/mob/user, var/force = 0) - if ((!( ticker ) || !emergency_shuttle.location())) - return - - if(emergency_shuttle.going_to_centcom()) - to_chat(user, "The shuttle may not be called while returning to [using_map.boss_short].") - return - - if(emergency_shuttle.online()) - to_chat(user, "The shuttle is already on its way.") - return - - // if force is 0, some things may stop the shuttle call - if(!force) - if(emergency_shuttle.deny_shuttle) - to_chat(user, "[using_map.boss_short] does not currently have a shuttle available in your sector. Please try again later.") - return - - if(deathsquad.deployed == 1) - to_chat(user, "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated.") - return - - if(world.time < 54000) // 30 minute grace period to let the game get going - to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/60)] minutes before trying again.") - return - - if(ticker.mode.auto_recall_shuttle) - //New version pretends to call the shuttle but cause the shuttle to return after a random duration. - emergency_shuttle.auto_recall = 1 - - if(ticker.mode.name == "blob" || ticker.mode.name == "epidemic") - to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") - return - - emergency_shuttle.call_transfer() - - //delay events in case of an autotransfer - if (isnull(user)) - SSevents.delay_events(EVENT_LEVEL_MODERATE, 9000) //15 minutes - SSevents.delay_events(EVENT_LEVEL_MAJOR, 9000) - - log_game("[user? key_name(user) : "Autotransfer"] has called the shuttle.") - message_admins("[user? key_name_admin(user) : "Autotransfer"] has called the shuttle.", 1) - admin_chat_message(message = "Autotransfer shuttle dispatched, shift ending soon.", color = "#2277BB") //VOREStation Add - - return - -/proc/cancel_call_proc(var/mob/user) - if (!( ticker ) || !emergency_shuttle.can_recall()) - return - if((ticker.mode.name == "blob")||(ticker.mode.name == "Meteor")) - return - - if(!emergency_shuttle.going_to_centcom()) //check that shuttle isn't already heading to CentCom - emergency_shuttle.recall() - log_game("[key_name(user)] has recalled the shuttle.") - message_admins("[key_name_admin(user)] has recalled the shuttle.", 1) - return - - -/proc/is_relay_online() - for(var/obj/machinery/telecomms/relay/M in world) - if(M.stat == 0) - return 1 - return 0 - -/obj/machinery/computer/communications/proc/post_status(var/command, var/data1, var/data2) - - var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) - - if(!frequency) return - - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = TRANSMISSION_RADIO - status_signal.data["command"] = command - - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - log_admin("STATUS: [src.fingerprintslast] set status screen message with [src]: [data1] [data2]") - //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") - if("alert") - status_signal.data["picture_state"] = data1 - - frequency.post_signal(src, status_signal) + communications.tgui_interact(user) diff --git a/code/game/machinery/computer/guestpass.dm b/code/game/machinery/computer/guestpass.dm index eb634c249f..1a4ec083df 100644 --- a/code/game/machinery/computer/guestpass.dm +++ b/code/game/machinery/computer/guestpass.dm @@ -13,14 +13,14 @@ var/reason = "NOT SPECIFIED" /obj/item/weapon/card/id/guest/GetAccess() - if (world.time > expiration_time) + if(world.time > expiration_time) return access else return temp_access /obj/item/weapon/card/id/guest/examine(mob/user) . = ..() - if (world.time < expiration_time) + if(world.time < expiration_time) . += "This pass expires at [worldtime2stationtime(expiration_time)]." else . += "It expired at [worldtime2stationtime(expiration_time)]." @@ -28,7 +28,7 @@ /obj/item/weapon/card/id/guest/read() if(!Adjacent(usr)) return //Too far to read - if (world.time > expiration_time) + if(world.time > expiration_time) to_chat(usr, "This pass expired at [worldtime2stationtime(expiration_time)].") else to_chat(usr, "This pass expires at [worldtime2stationtime(expiration_time)].") @@ -105,7 +105,7 @@ if(!giver && user.unEquip(I)) I.forceMove(src) giver = I - SSnanoui.update_uis(src) + SStgui.update_uis(src) else if(giver) to_chat(user, "There is already ID card inside.") return @@ -119,128 +119,119 @@ return user.set_machine(src) + tgui_interact(user) - ui_interact(user) +/obj/machinery/computer/guestpass/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "GuestPass", name) + ui.open() -/** - * Display the NanoUI window for the guest pass console. - * - * See NanoUI documentation for details. - */ -/obj/machinery/computer/guestpass/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) +/obj/machinery/computer/guestpass/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() - var/list/data = list() + var/list/area_list = list() - var/area_list[0] - - if (giver && giver.access) + data["access"] = null + if(giver && giver.access) data["access"] = giver.access for (var/A in giver.access) if(A in accesses) - area_list[++area_list.len] = list("area" = A, "area_name" = get_access_desc(A), "on" = 1) + area_list.Add(list(list("area" = A, "area_name" = get_access_desc(A), "on" = 1))) else - area_list[++area_list.len] = list("area" = A, "area_name" = get_access_desc(A), "on" = null) + area_list.Add(list(list("area" = A, "area_name" = get_access_desc(A), "on" = null))) + data["area"] = area_list data["giver"] = giver data["giveName"] = giv_name data["reason"] = reason data["duration"] = duration - data["area"] = area_list data["mode"] = mode data["log"] = internal_log data["uid"] = uid - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "guest_pass.tmpl", src.name, 400, 520) - ui.set_initial_data(data) - ui.open() - //ui.set_auto_update(5) + return data -/obj/machinery/computer/guestpass/Topic(href, href_list) +/obj/machinery/computer/guestpass/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 - usr.set_machine(src) - if (href_list["mode"]) - mode = href_list["mode"] + return TRUE - if (href_list["choice"]) - switch(href_list["choice"]) - if ("giv_name") - var/nam = sanitizeName(input("Person pass is issued to", "Name", giv_name) as text|null) - if (nam) - giv_name = nam - if ("reason") - var/reas = sanitize(input("Reason why pass is issued", "Reason", reason) as text|null) - if(reas) - reason = reas - if ("duration") - var/dur = input("Duration (in minutes) during which pass is valid (up to 120 minutes).", "Duration") as num|null - if (dur) - if (dur > 0 && dur <= 120) - duration = dur - else - to_chat(usr, "Invalid duration.") - if ("access") - var/A = text2num(href_list["access"]) - if (A in accesses) - accesses.Remove(A) + switch(action) + if("mode") + mode = params["mode"] + + if("giv_name") + var/nam = sanitizeName(input("Person pass is issued to", "Name", giv_name) as text|null) + if(nam) + giv_name = nam + if("reason") + var/reas = sanitize(input("Reason why pass is issued", "Reason", reason) as text|null) + if(reas) + reason = reas + if("duration") + var/dur = input("Duration (in minutes) during which pass is valid (up to 120 minutes).", "Duration") as num|null + if(dur) + if(dur > 0 && dur <= 120) + duration = dur else - if(A in giver.access) //Let's make sure the ID card actually has the access. - accesses.Add(A) - else - to_chat(usr, "Invalid selection, please consult technical support if there are any issues.") - log_debug("[key_name_admin(usr)] tried selecting an invalid guest pass terminal option.") - if (href_list["action"]) - switch(href_list["action"]) - if ("id") - if (giver) - if(ishuman(usr)) - giver.loc = usr.loc - if(!usr.get_active_hand()) - usr.put_in_hands(giver) - giver = null - else - giver.loc = src.loc - giver = null - accesses.Cut() + to_chat(usr, "Invalid duration.") + if("access") + var/A = text2num(params["access"]) + if(A in accesses) + accesses.Remove(A) + else + if(A in giver.access) //Let's make sure the ID card actually has the access. + accesses.Add(A) else - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/weapon/card/id) && usr.unEquip(I)) - I.loc = src - giver = I - - if ("print") - var/dat = "

Activity log of guest pass terminal #[uid]


" - for (var/entry in internal_log) - dat += "[entry]

" - //to_chat(usr, "Printing the log, standby...") - //sleep(50) - var/obj/item/weapon/paper/P = new/obj/item/weapon/paper( loc ) - P.name = "activity log" - P.info = dat - - if ("issue") - if (giver) - var/number = add_zero("[rand(0,9999)]", 4) - var/entry = "\[[stationtime2text()]\] Pass #[number] issued by [giver.registered_name] ([giver.assignment]) to [giv_name]. Reason: [reason]. Grants access to following areas: " - for (var/i=1 to accesses.len) - var/A = accesses[i] - if (A) - var/area = get_access_desc(A) - entry += "[i > 1 ? ", [area]" : "[area]"]" - entry += ". Expires at [worldtime2stationtime(world.time + duration*10*60)]." - internal_log.Add(entry) - - var/obj/item/weapon/card/id/guest/pass = new(src.loc) - pass.temp_access = accesses.Copy() - pass.registered_name = giv_name - pass.expiration_time = world.time + duration*10*60 - pass.reason = reason - pass.name = "guest pass #[number]" + to_chat(usr, "Invalid selection, please consult technical support if there are any issues.") + log_debug("[key_name_admin(usr)] tried selecting an invalid guest pass terminal option.") + if("id") + if(giver) + if(ishuman(usr)) + giver.loc = usr.loc + if(!usr.get_active_hand()) + usr.put_in_hands(giver) + giver = null else - to_chat(usr, "Cannot issue pass without issuing ID.") + giver.loc = src.loc + giver = null + accesses.Cut() + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id) && usr.unEquip(I)) + I.loc = src + giver = I - src.add_fingerprint(usr) - SSnanoui.update_uis(src) \ No newline at end of file + if("print") + var/dat = "

Activity log of guest pass terminal #[uid]


" + for (var/entry in internal_log) + dat += "[entry]

" + //to_chat(usr, "Printing the log, standby...") + //sleep(50) + var/obj/item/weapon/paper/P = new/obj/item/weapon/paper( loc ) + P.name = "activity log" + P.info = dat + + if("issue") + if(giver) + var/number = add_zero("[rand(0,9999)]", 4) + var/entry = "\[[stationtime2text()]\] Pass #[number] issued by [giver.registered_name] ([giver.assignment]) to [giv_name]. Reason: [reason]. Grants access to following areas: " + for (var/i=1 to accesses.len) + var/A = accesses[i] + if(A) + var/area = get_access_desc(A) + entry += "[i > 1 ? ", [area]" : "[area]"]" + entry += ". Expires at [worldtime2stationtime(world.time + duration*10*60)]." + internal_log.Add(entry) + + var/obj/item/weapon/card/id/guest/pass = new(src.loc) + pass.temp_access = accesses.Copy() + pass.registered_name = giv_name + pass.expiration_time = world.time + duration*10*60 + pass.reason = reason + pass.name = "guest pass #[number]" + else + to_chat(usr, "Cannot issue pass without issuing ID.") + + add_fingerprint(usr) + return TRUE \ No newline at end of file diff --git a/code/game/machinery/computer/message.dm b/code/game/machinery/computer/message.dm index 1dc0709ed3..1caa7b61a4 100644 --- a/code/game/machinery/computer/message.dm +++ b/code/game/machinery/computer/message.dm @@ -131,7 +131,10 @@ //Get out list of viable PDAs var/list/obj/item/device/pda/sendPDAs = list() for(var/obj/item/device/pda/P in PDAs) - if(!P.owner || P.toff || P.hidden) + if(!P.owner || P.hidden) + continue + var/datum/data/pda/app/messenger/M = P.find_program(/datum/data/pda/app/messenger) + if(!M || M.toff) continue sendPDAs["[P.name]"] = "\ref[P]" data["possibleRecipients"] = sendPDAs @@ -265,7 +268,11 @@ if("set_recipient") var/ref = params["val"] var/obj/item/device/pda/P = locate(ref) - if(!istype(P) || !P.owner || P.toff || P.hidden) + if(!istype(P) || !P.owner || P.hidden) + return FALSE + + var/datum/data/pda/app/messenger/M = P.find_program(/datum/data/pda/app/messenger) + if(!M || M.toff) return FALSE customrecepient = P . = TRUE @@ -286,22 +293,26 @@ var/obj/item/device/pda/PDARec = null for(var/obj/item/device/pda/P in PDAs) - if(!P.owner || P.toff || P.hidden) continue + if(!P.owner || P.hidden) + continue + var/datum/data/pda/app/messenger/M = P.find_program(/datum/data/pda/app/messenger) + if(!M || M.toff) + continue if(P.owner == customsender) PDARec = P //Sender isn't faking as someone who exists if(isnull(PDARec)) linkedServer.send_pda_message("[customrecepient.owner]", "[customsender]","[custommessage]") - customrecepient.new_message(customsender, customsender, customjob, custommessage) + var/datum/data/pda/app/messenger/M = customrecepient.find_program(/datum/data/pda/app/messenger) + if(M) + M.receive_message(list("sent" = 0, "owner" = customsender, "job" = customjob, "message" = custommessage), null) //Sender is faking as someone who exists else linkedServer.send_pda_message("[customrecepient.owner]", "[PDARec.owner]","[custommessage]") - customrecepient.tnote.Add(list(list("sent" = 0, "owner" = "[PDARec.owner]", "job" = "[customjob]", "message" = "[custommessage]", "target" ="\ref[PDARec]"))) - - if(!customrecepient.conversations.Find("\ref[PDARec]")) - customrecepient.conversations.Add("\ref[PDARec]") - - customrecepient.new_message(PDARec, custommessage) + + var/datum/data/pda/app/messenger/M = customrecepient.find_program(/datum/data/pda/app/messenger) + if(M) + M.receive_message(list("sent" = 0, "owner" = "[PDARec.owner]", "job" = "[customjob]", "message" = "[custommessage]", "target" = "\ref[PDARec]"), "\ref[PDARec]") //Finally.. ResetMessage() . = TRUE diff --git a/code/game/machinery/computer/timeclock_vr.dm b/code/game/machinery/computer/timeclock_vr.dm index a94b338320..8cfc711c18 100644 --- a/code/game/machinery/computer/timeclock_vr.dm +++ b/code/game/machinery/computer/timeclock_vr.dm @@ -55,7 +55,7 @@ if(!card && user.unEquip(I)) I.forceMove(src) card = I - SSnanoui.update_uis(src) + SStgui.update_uis(src) update_icon() else if(card) to_chat(user, "There is already ID card inside.") diff --git a/code/game/machinery/doors/airlock.dm b/code/game/machinery/doors/airlock.dm index 7eb9e4a342..014efcb581 100644 --- a/code/game/machinery/doors/airlock.dm +++ b/code/game/machinery/doors/airlock.dm @@ -851,11 +851,13 @@ About the new airlock wires panel: /obj/machinery/door/airlock/attack_ghost(mob/user) tgui_interact(user) -/obj/machinery/door/airlock/tgui_interact(mob/user, datum/tgui/ui) +/obj/machinery/door/airlock/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/custom_state) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "AiAirlock", name) ui.open() + if(custom_state) + ui.set_state(custom_state) return TRUE /obj/machinery/door/airlock/tgui_data(mob/user) diff --git a/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm b/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm index ef6367758f..9e65c06bcd 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers_dummy.dm @@ -7,7 +7,6 @@ layer = ABOVE_OBJ_LAYER var/id_tag - var/datum/topic_state/remote/remote_state var/obj/machinery/embedded_controller/radio/airlock/master_controller /obj/machinery/dummy_airlock_controller/process() @@ -25,15 +24,10 @@ break if(!master_controller) qdel(src) - else - remote_state = new /datum/topic_state/remote(src, master_controller) /obj/machinery/dummy_airlock_controller/Destroy() if(master_controller) master_controller.dummy_terminals -= src - if(remote_state) - qdel(remote_state) - remote_state = null return ..() /obj/machinery/dummy_airlock_controller/interface_interact(var/mob/user) diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 00a8e68760..e8e741b578 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -192,7 +192,6 @@ if(inoperable()) to_chat(usr, "\The [src] doesn't appear to function.") return - ui_interact(user) tgui_interact(user) /obj/machinery/media/jukebox/tgui_status(mob/user) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index 05e2256d9d..de85f95e9a 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -101,22 +101,22 @@ NEWSCASTER.newsAlert(annoncement) NEWSCASTER.update_icon() - var/list/receiving_pdas = new - for (var/obj/item/device/pda/P in PDAs) - if(!P.owner) - continue - if(P.toff) - continue - receiving_pdas += P + // var/list/receiving_pdas = new + // for (var/obj/item/device/pda/P in PDAs) + // if(!P.owner) + // continue + // if(P.toff) + // continue + // receiving_pdas += P - spawn(0) // get_receptions sleeps further down the line, spawn of elsewhere - var/datum/receptions/receptions = get_receptions(null, receiving_pdas) // datums are not atoms, thus we have to assume the newscast network always has reception + // spawn(0) // get_receptions sleeps further down the line, spawn of elsewhere + // var/datum/receptions/receptions = get_receptions(null, receiving_pdas) // datums are not atoms, thus we have to assume the newscast network always has reception - for(var/obj/item/device/pda/PDA in receiving_pdas) - if(!(receptions.receiver_reception[PDA] & TELECOMMS_RECEPTION_RECEIVER)) - continue + // for(var/obj/item/device/pda/PDA in receiving_pdas) + // if(!(receptions.receiver_reception[PDA] & TELECOMMS_RECEPTION_RECEIVER)) + // continue - PDA.new_news(annoncement) + // PDA.new_news(annoncement) var/datum/feed_network/news_network = new /datum/feed_network //The global news-network, which is coincidentally a global list. diff --git a/code/game/machinery/oxygen_pump.dm b/code/game/machinery/oxygen_pump.dm index 37c75957be..f7821a4e84 100644 --- a/code/game/machinery/oxygen_pump.dm +++ b/code/game/machinery/oxygen_pump.dm @@ -79,7 +79,7 @@ update_use_power(USE_POWER_IDLE) /obj/machinery/oxygen_pump/attack_ai(mob/user as mob) - ui_interact(user) + tgui_interact(user) /obj/machinery/oxygen_pump/proc/attach_mask(var/mob/living/carbon/C) if(C && istype(C)) @@ -176,61 +176,60 @@ set src in oview(1) set category = "Object" set name = "Show Tank Settings" - ui_interact(usr) + tgui_interact(usr) -//GUI Tank Setup -/obj/machinery/oxygen_pump/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] +/obj/machinery/oxygen_pump/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) if(!tank) - to_chat(usr, "It is missing a tank!") - data["tankPressure"] = 0 - data["releasePressure"] = 0 - data["defaultReleasePressure"] = 0 - data["maxReleasePressure"] = 0 - data["maskConnected"] = 0 - data["tankInstalled"] = 0 - // this is the data which will be sent to the ui + to_chat(user, "[src] is missing a tank.") + if(ui) + ui.close() + return + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Tank", name) + ui.open() + +/obj/machinery/oxygen_pump/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["showToggle"] = FALSE + data["maskConnected"] = !!breather + + data["tankPressure"] = 0 + data["releasePressure"] = 0 + data["defaultReleasePressure"] = 0 + data["minReleasePressure"] = 0 + data["releasePressure"] = round(tank.distribute_pressure ? tank.distribute_pressure : 0) + data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) + if(tank) data["tankPressure"] = round(tank.air_contents.return_pressure() ? tank.air_contents.return_pressure() : 0) - data["releasePressure"] = round(tank.distribute_pressure ? tank.distribute_pressure : 0) data["defaultReleasePressure"] = round(TANK_DEFAULT_RELEASE_PRESSURE) - data["maxReleasePressure"] = round(TANK_MAX_RELEASE_PRESSURE) - data["maskConnected"] = 0 - data["tankInstalled"] = 1 - if(!breather) - data["maskConnected"] = 0 - if(breather) - data["maskConnected"] = 1 + return data - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "Oxygen_pump.tmpl", "Tank", 500, 300) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(1) - -/obj/machinery/oxygen_pump/Topic(href, href_list) +/obj/machinery/oxygen_pump/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 + return TRUE - if (href_list["dist_p"]) - if (href_list["dist_p"] == "reset") - tank.distribute_pressure = TANK_DEFAULT_RELEASE_PRESSURE - else if (href_list["dist_p"] == "max") - tank.distribute_pressure = TANK_MAX_RELEASE_PRESSURE - else - var/cp = text2num(href_list["dist_p"]) - tank.distribute_pressure += cp - tank.distribute_pressure = min(max(round(tank.distribute_pressure), 0), TANK_MAX_RELEASE_PRESSURE) - return 1 + switch(action) + if("pressure") + var/pressure = params["pressure"] + if(pressure == "reset") + pressure = TANK_DEFAULT_RELEASE_PRESSURE + . = TRUE + else if(pressure == "min") + pressure = 0 + . = TRUE + else if(pressure == "max") + pressure = TANK_MAX_RELEASE_PRESSURE + . = TRUE + else if(text2num(pressure) != null) + pressure = text2num(pressure) + . = TRUE + if(.) + tank.distribute_pressure = clamp(round(pressure), 0, TANK_MAX_RELEASE_PRESSURE) /obj/machinery/oxygen_pump/anesthetic name = "anesthetic pump" diff --git a/code/game/machinery/partslathe_vr.dm b/code/game/machinery/partslathe_vr.dm index aab15f40f9..c5ddcff900 100644 --- a/code/game/machinery/partslathe_vr.dm +++ b/code/game/machinery/partslathe_vr.dm @@ -233,7 +233,6 @@ /obj/machinery/partslathe/attack_hand(mob/user) if(..()) return - ui_interact(user) tgui_interact(user) /obj/machinery/partslathe/ui_assets(mob/user) diff --git a/code/game/machinery/pda_multicaster.dm b/code/game/machinery/pda_multicaster.dm index 20e870d3d0..a70f1ede37 100644 --- a/code/game/machinery/pda_multicaster.dm +++ b/code/game/machinery/pda_multicaster.dm @@ -63,7 +63,9 @@ /obj/machinery/pda_multicaster/proc/update_PDAs(var/turn_off) for(var/obj/item/device/pda/pda in contents) - pda.toff = turn_off + var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger/multicast) + if(M) + M.toff = turn_off /obj/machinery/pda_multicaster/proc/update_power() if(toggle) diff --git a/code/game/machinery/pointdefense.dm b/code/game/machinery/pointdefense.dm index 8eeb73fbc5..5b10e398a3 100644 --- a/code/game/machinery/pointdefense.dm +++ b/code/game/machinery/pointdefense.dm @@ -95,7 +95,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) /obj/machinery/pointdefense_control/attackby(var/obj/item/W, var/mob/user) if(W?.is_multitool()) var/new_ident = input(user, "Enter a new ident tag.", "[src]", id_tag) as null|text - if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, physical_state)) + if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, GLOB.tgui_physical_state)) // Check for duplicate controllers with this ID for(var/thing in pointdefense_controllers) var/obj/machinery/pointdefense_control/PC = thing @@ -211,7 +211,7 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense) /obj/machinery/power/pointdefense/attackby(var/obj/item/W, var/mob/user) if(W?.is_multitool()) var/new_ident = input(user, "Enter a new ident tag.", "[src]", id_tag) as null|text - if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, physical_state)) + if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, GLOB.tgui_physical_state)) to_chat(user, "You register [src] with the [new_ident] network.") id_tag = new_ident return diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm index 1ed44c61bc..01a890bd79 100644 --- a/code/game/machinery/portable_turret.dm +++ b/code/game/machinery/portable_turret.dm @@ -66,6 +66,7 @@ var/last_fired = FALSE //TRUE: if the turret is cooling down from a shot, FALSE: turret is ready to fire var/shot_delay = 1.5 SECONDS //1.5 seconds between each shot + var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI var/check_arrest = TRUE //checks if the perp is set to arrest var/check_records = TRUE //checks if a security record exists at all var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have @@ -81,6 +82,7 @@ var/enabled = TRUE //determines if the turret is on var/lethal = FALSE //whether in lethal or stun mode + var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed var/disabled = FALSE var/shot_sound //what sound should play when the turret fires @@ -214,6 +216,9 @@ req_one_access = list() installation = /obj/item/weapon/gun/energy/lasertag/omni + targetting_is_configurable = FALSE + lethal_is_configurable = FALSE + locked = FALSE enabled = FALSE anchored = FALSE @@ -262,43 +267,14 @@ if(istype(M.wear_suit, /obj/item/clothing/suit/bluetag) && check_weapons) // Checks if they are a blue player return TURRET_PRIORITY_TARGET -/obj/machinery/porta_turret/lasertag/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - data["access"] = !isLocked(user) - data["locked"] = locked - data["enabled"] = enabled - //data["is_lethal"] = 1 // VOREStation Removal of "Lethal" setting - it does nothing. Rykka did dis. - //data["lethal"] = lethal // VOREStation Removal of "Lethal" setting - it does nothing. Rykka did dis. - - if(data["access"]) - var/settings[0] - settings[++settings.len] = list("category" = "Target Red", "setting" = "check_synth", "value" = check_synth) // Could not get the UI to work with new vars specifically for lasertag turrets -Nalarac - settings[++settings.len] = list("category" = "Target Blue", "setting" = "check_weapons", "value" = check_weapons) // So I'm using these variables since they don't do anything else in this case - data["settings"] = settings - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/porta_turret/lasertag/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["command"] && href_list["value"]) - var/value = text2num(href_list["value"]) - if(href_list["command"] == "enable") - enabled = value - //else if(href_list["command"] == "lethal") // VOREStation Removal of "Lethal" setting - it does nothing. Rykka did dis. - //lethal = value // VOREStation Removal of "Lethal" setting - it does nothing. Rykka did dis. - else if(href_list["command"] == "check_synth") - check_synth = value - else if(href_list["command"] == "check_weapons") - check_weapons = value - - return 1 +/obj/machinery/porta_turret/lasertag/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, // is turret turned on? + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable + ) + return data /obj/machinery/porta_turret/Initialize() //Sets up a spark system @@ -401,102 +377,99 @@ lethal_shot_sound = 'sound/weapons/eluger.ogg' shot_sound = 'sound/weapons/Taser.ogg' -/obj/machinery/porta_turret/proc/isLocked(mob/user) - if(ailock && issilicon(user)) - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") - return 1 - - if(locked && !issilicon(user)) - to_chat(user, "Controls locked.") - return 1 - - return 0 - -/obj/machinery/porta_turret/attack_ai(mob/user) - if(isLocked(user)) - return - - ui_interact(user) - -/obj/machinery/porta_turret/attack_hand(mob/user) - if(isLocked(user)) - return - - ui_interact(user) - -/obj/machinery/porta_turret/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - data["access"] = !isLocked(user) - data["locked"] = locked - data["enabled"] = enabled - data["is_lethal"] = 1 - data["lethal"] = lethal - - if(data["access"]) - var/settings[0] - settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth) - settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons) - settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records) - settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest) - settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access) - settings[++settings.len] = list("category" = "Check misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies) - settings[++settings.len] = list("category" = "Neutralize All Entities", "setting" = "check_all", "value" = check_all) - settings[++settings.len] = list("category" = "Neutralize Downed Entities", "setting" = "check_down", "value" = check_down) - data["settings"] = settings - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - /obj/machinery/porta_turret/proc/HasController() var/area/A = get_area(src) return A && A.turret_controls.len > 0 -/obj/machinery/porta_turret/CanUseTopic(var/mob/user) +/obj/machinery/porta_turret/proc/isLocked(mob/user) if(HasController()) - to_chat(user, "Turrets can only be controlled using the assigned turret controller.") - return STATUS_CLOSE + return TRUE + if(isrobot(user) || isAI(user)) + if(ailock) + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + return TRUE + else + return FALSE + if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return FALSE + else + return TRUE + if(locked) + return TRUE + return FALSE - if(isLocked(user)) - return STATUS_CLOSE +/obj/machinery/porta_turret/attack_ai(mob/user) + tgui_interact(user) +/obj/machinery/porta_turret/attack_ghost(mob/user) + tgui_interact(user) + +/obj/machinery/porta_turret/attack_hand(mob/user) + tgui_interact(user) + +/obj/machinery/porta_turret/tgui_interact(mob/user, datum/tgui/ui = null) + if(HasController()) + to_chat(user, "[src] can only be controlled using the assigned turret controller.") + return if(!anchored) - to_chat(user, "\The [src] has to be secured first!") - return STATUS_CLOSE + to_chat(user, "[src] has to be secured first!") + return + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortableTurret", name, 500, 400) + ui.open() - return ..() +/obj/machinery/porta_turret/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, + "targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable, + "check_weapons" = check_weapons, + "neutralize_noaccess" = check_access, + "neutralize_norecord" = check_records, + "neutralize_criminals" = check_arrest, + "neutralize_all" = check_all, + "neutralize_nonsynth" = check_synth, + "neutralize_unidentified" = check_anomalies, + "neutralize_down" = check_down, + ) + return data -/obj/machinery/porta_turret/Topic(href, href_list) +/obj/machinery/porta_turret/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 + return TRUE + if(isLocked(usr)) + return TRUE + . = TRUE - if(href_list["command"] && href_list["value"]) - var/value = text2num(href_list["value"]) - if(href_list["command"] == "enable") - enabled = value - else if(href_list["command"] == "lethal") - lethal = value - else if(href_list["command"] == "check_synth") - check_synth = value - else if(href_list["command"] == "check_weapons") - check_weapons = value - else if(href_list["command"] == "check_records") - check_records = value - else if(href_list["command"] == "check_arrest") - check_arrest = value - else if(href_list["command"] == "check_access") - check_access = value - else if(href_list["command"] == "check_anomalies") - check_anomalies = value - else if(href_list["command"] == "check_all") - check_all = value - else if(href_list["command"] == "check_down") - check_down = value - - return 1 + switch(action) + if("power") + enabled = !enabled + if("lethal") + if(lethal_is_configurable) + lethal = !lethal + if(targetting_is_configurable) + switch(action) + if("authweapon") + check_weapons = !check_weapons + if("authaccess") + check_access = !check_access + if("authnorecord") + check_records = !check_records + if("autharrest") + check_arrest = !check_arrest + if("authxeno") + check_anomalies = !check_anomalies + if("authsynth") + check_synth = !check_synth + if("authall") + check_all = !check_all + if("authdown") + check_down = !check_down /obj/machinery/porta_turret/power_change() if(powered()) @@ -929,6 +902,7 @@ var/check_weapons var/check_anomalies var/check_all + var/check_down var/ailock /obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC) @@ -944,6 +918,7 @@ check_weapons = TC.check_weapons check_anomalies = TC.check_anomalies check_all = TC.check_all + check_down = TC.check_down ailock = TC.ailock power_change() diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 75b78d4114..c205395199 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -93,6 +93,8 @@ return /obj/machinery/recharger/attack_hand(mob/user as mob) + if(!Adjacent(user)) + return FALSE add_fingerprint(user) if(charging) diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index e87de196d9..86b339e6d5 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -104,7 +104,6 @@ var/list/obj/machinery/requests_console/allConsoles = list() /obj/machinery/requests_console/attack_hand(user as mob) if(..(user)) return - ui_interact(user) tgui_interact(user) /obj/machinery/requests_console/tgui_interact(mob/user, datum/tgui/ui) diff --git a/code/game/machinery/suit_storage_unit.dm b/code/game/machinery/suit_storage_unit.dm index 4e30affc1a..87d2ca9b44 100644 --- a/code/game/machinery/suit_storage_unit.dm +++ b/code/game/machinery/suit_storage_unit.dm @@ -602,37 +602,37 @@ name = "Engineering suit cycler" model_text = "Engineering" req_access = list(access_construction) - departments = list("Engineering","Atmospherics","HAZMAT","Construction") + departments = list("Engineering","Atmospherics","HAZMAT","Construction","No Change") /obj/machinery/suit_cycler/mining name = "Mining suit cycler" model_text = "Mining" req_access = list(access_mining) - departments = list("Mining") + departments = list("Mining","No Change") /obj/machinery/suit_cycler/security name = "Security suit cycler" model_text = "Security" req_access = list(access_security) - departments = list("Security","Crowd Control","Security EVA") + departments = list("Security","Crowd Control","Security EVA","No Change") /obj/machinery/suit_cycler/medical name = "Medical suit cycler" model_text = "Medical" req_access = list(access_medical) - departments = list("Medical","Biohazard","Emergency Medical Response") + departments = list("Medical","Biohazard","Emergency Medical Response","No Change") /obj/machinery/suit_cycler/syndicate name = "Nonstandard suit cycler" model_text = "Nonstandard" req_access = list(access_syndicate) - departments = list("Mercenary", "Charring") + departments = list("Mercenary", "Charring","No Change") can_repair = 1 /obj/machinery/suit_cycler/exploration name = "Explorer suit cycler" model_text = "Exploration" - departments = list("Exploration","Old Exploration") + departments = list("Exploration","Old Exploration","No Change") /obj/machinery/suit_cycler/exploration/Initialize() species -= SPECIES_TESHARI @@ -641,33 +641,33 @@ /obj/machinery/suit_cycler/pilot name = "Pilot suit cycler" model_text = "Pilot" - departments = list("Pilot Blue","Pilot") + departments = list("Pilot Blue","Pilot","No Change") /obj/machinery/suit_cycler/vintage name = "Vintage Crew suit cycler" model_text = "Vintage" - departments = list("Vintage Crew") + departments = list("Vintage Crew","No Change") req_access = null /obj/machinery/suit_cycler/vintage/pilot name = "Vintage Pilot suit cycler" model_text = "Vintage Pilot" - departments = list("Vintage Pilot (Bubble Helm)","Vintage Pilot (Closed Helm)") + departments = list("Vintage Pilot (Bubble Helm)","Vintage Pilot (Closed Helm)","No Change") /obj/machinery/suit_cycler/vintage/medsci name = "Vintage MedSci suit cycler" model_text = "Vintage MedSci" - departments = list("Vintage Medical (Bubble Helm)","Vintage Medical (Closed Helm)","Vintage Research (Bubble Helm)","Vintage Research (Closed Helm)") + departments = list("Vintage Medical (Bubble Helm)","Vintage Medical (Closed Helm)","Vintage Research (Bubble Helm)","Vintage Research (Closed Helm)","No Change") /obj/machinery/suit_cycler/vintage/rugged name = "Vintage Ruggedized suit cycler" model_text = "Vintage Ruggedized" - departments = list("Vintage Engineering","Vintage Marine","Vintage Officer","Vintage Mercenary") + departments = list("Vintage Engineering","Vintage Marine","Vintage Officer","Vintage Mercenary","No Change") /obj/machinery/suit_cycler/vintage/omni name = "Vintage Master suit cycler" model_text = "Vintage Master" - departments = list("Vintage Crew","Vintage Engineering","Vintage Pilot (Bubble Helm)","Vintage Pilot (Closed Helm)","Vintage Medical (Bubble Helm)","Vintage Medical (Closed Helm)","Vintage Research (Bubble Helm)","Vintage Research (Closed Helm)","Vintage Marine","Vintage Officer","Vintage Mercenary") + departments = list("Vintage Crew","Vintage Engineering","Vintage Pilot (Bubble Helm)","Vintage Pilot (Closed Helm)","Vintage Medical (Bubble Helm)","Vintage Medical (Closed Helm)","Vintage Research (Bubble Helm)","Vintage Research (Closed Helm)","Vintage Marine","Vintage Officer","Vintage Mercenary","No Change") /obj/machinery/suit_cycler/vintage/Initialize() species -= SPECIES_TESHARI @@ -1049,17 +1049,9 @@ /obj/machinery/suit_cycler/proc/apply_paintjob() var/obj/item/clothing/head/helmet/parent_helmet var/obj/item/clothing/suit/space/parent_suit - + var/turf/T = get_turf(src) if(!target_species || !target_department) return - - if(target_species) - if(helmet) helmet.refit_for_species(target_species) - if(suit) - suit.refit_for_species(target_species) - if(suit.helmet) - suit.helmet.refit_for_species(target_species) - //Now "Complete" with most departmental and variant suits, and sorted by department. These aren't available in the standard or emagged cycler lists because they're incomplete for most species. switch(target_department) if("No Change") @@ -1217,7 +1209,32 @@ parent_suit = /obj/item/clothing/suit/space/void/refurb/mercenary/talon //VOREStation Addition End //END: downstream variant space - + if(target_species) + //Only run these checks if they have a sprite sheet defined, otherwise they use human's anyways, and there is almost definitely a sprite. + if((helmet!=null&&(target_species in helmet.sprite_sheets_obj))||(suit!=null&&(target_species in suit.sprite_sheets_obj))) + //Making sure all of our items have the sprites to be refitted. + var/helmet_check = ((helmet!=null && (initial(parent_helmet.icon_state) in icon_states(helmet.sprite_sheets_obj[target_species],1))) || helmet==null) + //If the helmet exists, only return true if there's also sprites for it. If the helmet doesn't exist, return true. + var/suit_check = ((suit!=null && (initial(parent_suit.icon_state) in icon_states(suit.sprite_sheets_obj[target_species],1))) || suit==null) + var/suit_helmet_check = ((suit!=null && suit.helmet!=null && (initial(parent_helmet.icon_state) in icon_states(suit.helmet.sprite_sheets_obj[target_species],1))) || suit==null || suit.helmet==null) + if(helmet_check && suit_check && suit_helmet_check) + if(helmet) + helmet.refit_for_species(target_species) + if(suit) + suit.refit_for_species(target_species) + if(suit.helmet) + suit.helmet.refit_for_species(target_species) + else + //If they don't, alert the user and stop here. + T.visible_message("[bicon(src)]Unable to apply specified cosmetics with specified species. Please try again with a different species or cosmetic option selected.") + return + else + if(helmet) + helmet.refit_for_species(target_species) + if(suit) + suit.refit_for_species(target_species) + if(suit.helmet) + suit.helmet.refit_for_species(target_species) //look at this! isn't it beautiful? -KK (well ok not beautiful but it's a lot cleaner) if(helmet && target_department != "No Change") var/obj/item/clothing/H = new parent_helmet diff --git a/code/game/machinery/suit_storage_unit_vr.dm b/code/game/machinery/suit_storage_unit_vr.dm index 3702914e32..168114844d 100644 --- a/code/game/machinery/suit_storage_unit_vr.dm +++ b/code/game/machinery/suit_storage_unit_vr.dm @@ -1,11 +1,11 @@ /obj/machinery/suit_cycler - departments = list("Engineering","Mining","Medical","Security","Atmos","HAZMAT","Construction","Biohazard","Emergency Medical Response","Crowd Control","Exploration","Pilot Blue","Pilot","Manager","Prototype") + departments = list("Engineering","Mining","Medical","Security","Atmos","HAZMAT","Construction","Biohazard","Emergency Medical Response","Crowd Control","Exploration","Pilot Blue","Pilot","Manager","Prototype","No Change") species = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_VULPKANIN) // Old Exploration is too WIP to use right now /obj/machinery/suit_cycler/exploration req_access = list(access_explorer) - departments = list("Exploration") + departments = list("Exploration","No Change") /obj/machinery/suit_cycler/pilot req_access = list(access_pilot) @@ -14,7 +14,7 @@ name = "Manager suit cycler" model_text = "Manager" req_access = list(access_captain) - departments = list("Manager") + departments = list("Manager","No Change") /obj/machinery/suit_cycler/captain/Initialize() //No Teshari Sprites species -= SPECIES_TESHARI @@ -24,7 +24,7 @@ name = "Prototype suit cycler" model_text = "Prototype" req_access = list(access_hos) - departments = list("Prototype") + departments = list("Prototype","No Change") /obj/machinery/suit_cycler/prototype/Initialize() //No Teshari Sprites species -= SPECIES_TESHARI @@ -34,34 +34,34 @@ name = "Talon crew suit cycler" model_text = "Talon crew" req_access = list(access_talon) - departments = list("Talon Crew") + departments = list("Talon Crew","No Change") /obj/machinery/suit_cycler/vintage/tpilot name = "Talon pilot suit cycler" model_text = "Talon pilot" req_access = list(access_talon) - departments = list("Talon Pilot (Bubble Helm)","Talon Pilot (Closed Helm)") + departments = list("Talon Pilot (Bubble Helm)","Talon Pilot (Closed Helm)","No Change") /obj/machinery/suit_cycler/vintage/tengi name = "Talon engineer suit cycler" model_text = "Talon engineer" req_access = list(access_talon) - departments = list("Talon Engineering") + departments = list("Talon Engineering","No Change") /obj/machinery/suit_cycler/vintage/tguard name = "Talon guard suit cycler" model_text = "Talon guard" req_access = list(access_talon) - departments = list("Talon Marine","Talon Mercenary") + departments = list("Talon Marine","Talon Mercenary","No Change") /obj/machinery/suit_cycler/vintage/tmedic name = "Talon doctor suit cycler" model_text = "Talon doctor" req_access = list(access_talon) - departments = list("Talon Medical (Bubble Helm)","Talon Medical (Closed Helm)") + departments = list("Talon Medical (Bubble Helm)","Talon Medical (Closed Helm)","No Change") /obj/machinery/suit_cycler/vintage/tcaptain name = "Talon captain suit cycler" model_text = "Talon captain" req_access = list(access_talon) - departments = list("Talon Officer") \ No newline at end of file + departments = list("Talon Officer","No Change") \ No newline at end of file diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index 31dd9d2e13..cc8c262803 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -11,7 +11,7 @@ var/id = null var/one_time_use = 0 //Used for one-time-use teleport cards (such as clown planet coordinates.) //Setting this to 1 will set locked to null after a player enters the portal and will not allow hand-teles to open portals to that location. - var/datum/nano_module/program/teleport_control/teleport_control + var/datum/tgui_module/teleport_control/teleport_control /obj/machinery/computer/teleporter/New() id = "[rand(1000, 9999)]" @@ -44,7 +44,7 @@ teleport_control.station = station /obj/machinery/computer/teleporter/Destroy() - qdel_null(teleport_control) + QDEL_NULL(teleport_control) return ..() /obj/machinery/computer/teleporter/attackby(I as obj, mob/living/user as mob) @@ -96,108 +96,13 @@ attack_hand() /obj/machinery/computer/teleporter/attack_ai(mob/user) - ui_interact(user) + teleport_control.tgui_interact(user) /obj/machinery/computer/teleporter/attack_hand(mob/user) add_fingerprint(user) if(stat & (BROKEN|NOPOWER)) return - ui_interact(user) - -/obj/machinery/computer/teleporter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - teleport_control.ui_interact(user, ui_key, ui, force_open) - -/obj/machinery/computer/teleporter/interact(mob/user) - teleport_control.ui_interact(user) - -////// -////// Nano-module for teleporter -////// -/datum/nano_module/program/teleport_control - name = "Teleporter Control" - var/locked_name = "Not Locked" - var/obj/item/locked = null - var/obj/machinery/teleport/station/station = null - var/obj/machinery/teleport/hub/hub = null - -/datum/nano_module/program/teleport_control/Topic(href, href_list) - if(..()) return 1 - - if(href_list["select_target"]) - var/list/L = list() - var/list/areaindex = list() - - for(var/obj/item/device/radio/beacon/R in all_beacons) - var/turf/T = get_turf(R) - if(!T) - continue - if(!(T.z in using_map.player_levels)) - continue - var/tmpname = T.loc.name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = R - - for (var/obj/item/weapon/implant/tracking/I in all_tracking_implants) - if(!I.implanted || !ismob(I.loc)) - continue - else - var/mob/M = I.loc - if(M.stat == 2) - if(M.timeofdeath + 6000 < world.time) - continue - var/turf/T = get_turf(M) - if(T) continue - if(T.z == 2) continue - var/tmpname = M.real_name - if(areaindex[tmpname]) - tmpname = "[tmpname] ([++areaindex[tmpname]])" - else - areaindex[tmpname] = 1 - L[tmpname] = I - - var/desc = input("Please select a location to lock in.", "Locking Menu") in L|null - if(!desc) - return 0 - if(get_dist(host, usr) > 1 && !issilicon(usr)) - return 0 - - locked = L[desc] - locked_name = desc - return 1 - - if(href_list["test_fire"]) - station?.testfire() - return 1 - - if(href_list["toggle_on"]) - if(!station) - return 0 - - if(station.engaged) - station.disengage() - else - station.engage() - - return 1 - -/datum/nano_module/program/teleport_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() - - data["locked_name"] = locked_name ? locked_name : "No Target" - data["station_connected"] = station ? 1 : 0 - data["hub_connected"] = hub ? 1 : 0 - data["calibrated"] = hub ? hub.accurate : 0 - data["teleporter_on"] = station ? station.engaged : 0 - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "teleport_control.tmpl", "Teleport Control Console", 400, 500, state = state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + teleport_control.tgui_interact(user) /obj/machinery/computer/teleporter/verb/set_id(t as text) set category = "Object" @@ -211,14 +116,6 @@ id = t return -/proc/find_loc(obj/R as obj) - if(!R) return null - var/turf/T = R.loc - while(!istype(T, /turf)) - T = T.loc - if(!T || istype(T, /area)) return null - return T - ////// ////// Root of all the machinery ////// diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm index a3d10db557..ad55fe14a1 100644 --- a/code/game/machinery/turret_control.dm +++ b/code/game/machinery/turret_control.dm @@ -11,31 +11,35 @@ desc = "Used to control a room's automated defenses." icon = 'icons/obj/machines/turret_control.dmi' icon_state = "control_standby" - anchored = 1 - density = 0 - var/enabled = 0 - var/lethal = 0 - var/locked = 1 + anchored = TRUE + density = FALSE + var/enabled = FALSE + var/lethal = FALSE + var/lethal_is_configurable = TRUE + var/locked = TRUE var/area/control_area //can be area name, path or nothing. - var/check_arrest = 1 //checks if the perp is set to arrest - var/check_records = 1 //checks if a security record exists at all - var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have - var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements - var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos) - var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg - var/check_all = 0 //If active, will shoot at anything. - var/ailock = 0 //Silicons cannot use this + var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI + var/check_arrest = TRUE //checks if the perp is set to arrest + var/check_records = TRUE //checks if a security record exists at all + var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have + var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements + var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos) + var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg + var/check_all = FALSE //If active, will shoot at anything. + var/check_down = TRUE //If active, won't shoot laying targets. + var/ailock = FALSE //Silicons cannot use this + var/syndicate = FALSE req_access = list(access_ai_upload) /obj/machinery/turretid/stun - enabled = 1 + enabled = TRUE icon_state = "control_stun" /obj/machinery/turretid/lethal - enabled = 1 - lethal = 1 + enabled = TRUE + lethal = TRUE icon_state = "control_kill" /obj/machinery/turretid/Destroy() @@ -67,21 +71,24 @@ . = ..() /obj/machinery/turretid/proc/isLocked(mob/user) - if(ailock && issilicon(user)) - to_chat(user, "There seems to be a firewall preventing you from accessing this device.") - return 1 + if(isrobot(user) || isAI(user)) + if(ailock) + to_chat(user, "There seems to be a firewall preventing you from accessing this device.") + return TRUE + else + return FALSE - if(locked && !issilicon(user)) - to_chat(user, "Access denied.") - return 1 + if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return FALSE + else + return TRUE - return 0 + if(locked) + return TRUE -/obj/machinery/turretid/CanUseTopic(mob/user) - if(isLocked(user)) - return STATUS_CLOSE - - return ..() + return FALSE /obj/machinery/turretid/attackby(obj/item/weapon/W, mob/user) if(stat & BROKEN) @@ -100,77 +107,80 @@ /obj/machinery/turretid/emag_act(var/remaining_charges, var/mob/user) if(!emagged) to_chat(user, "You short out the turret controls' access analysis module.") - emagged = 1 - locked = 0 - ailock = 0 - return 1 + emagged = TRUE + locked = FALSE + ailock = FALSE + return TRUE /obj/machinery/turretid/attack_ai(mob/user as mob) - if(isLocked(user)) - return + tgui_interact(user) - ui_interact(user) +/obj/machinery/turretid/attack_ghost(mob/user as mob) + tgui_interact(user) /obj/machinery/turretid/attack_hand(mob/user as mob) - if(isLocked(user)) + tgui_interact(user) + +/obj/machinery/turretid/tgui_interact(mob/user, datum/tgui/ui = null) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "PortableTurret", name) // 500, 400 + ui.open() + +/obj/machinery/turretid/tgui_data(mob/user) + var/list/data = list( + "locked" = isLocked(user), // does the current user have access? + "on" = enabled, + "targetting_is_configurable" = targetting_is_configurable, + "lethal" = lethal, + "lethal_is_configurable" = lethal_is_configurable, + "check_weapons" = check_weapons, + "neutralize_noaccess" = check_access, + "one_access" = FALSE, + "selectedAccess" = list(), + "access_is_configurable" = FALSE, + "neutralize_norecord" = check_records, + "neutralize_criminals" = check_arrest, + "neutralize_nonsynth" = check_synth, + "neutralize_all" = check_all, + "neutralize_unidentified" = check_anomalies, + "neutralize_down" = check_down, + ) + return data + +/obj/machinery/turretid/tgui_act(action, params) + if(..()) + return + if(isLocked(usr)) return - ui_interact(user) + . = TRUE + switch(action) + if("power") + enabled = !enabled + if("lethal") + if(lethal_is_configurable) + lethal = !lethal + if(targetting_is_configurable) + switch(action) + if("authweapon") + check_weapons = !check_weapons + if("authaccess") + check_access = !check_access + if("authnorecord") + check_records = !check_records + if("autharrest") + check_arrest = !check_arrest + if("authxeno") + check_anomalies = !check_anomalies + if("authsynth") + check_synth = !check_synth + if("authall") + check_all = !check_all + if("authdown") + check_down = !check_down -/obj/machinery/turretid/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - data["access"] = !isLocked(user) - data["locked"] = locked - data["enabled"] = enabled - data["is_lethal"] = 1 - data["lethal"] = lethal - - if(data["access"]) - var/settings[0] - settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth) - settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons) - settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records) - settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest) - settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access) - settings[++settings.len] = list("category" = "Check misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies) - settings[++settings.len] = list("category" = "Neutralize All Entities", "setting" = "check_all", "value" = check_all) - - data["settings"] = settings - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/turretid/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["command"] && href_list["value"]) - var/value = text2num(href_list["value"]) - if(href_list["command"] == "enable") - enabled = value - else if(href_list["command"] == "lethal") - lethal = value - else if(href_list["command"] == "check_synth") - check_synth = value - else if(href_list["command"] == "check_weapons") - check_weapons = value - else if(href_list["command"] == "check_records") - check_records = value - else if(href_list["command"] == "check_arrest") - check_arrest = value - else if(href_list["command"] == "check_access") - check_access = value - else if(href_list["command"] == "check_anomalies") - check_anomalies = value - else if(href_list["command"] == "check_all") - check_all = value - - updateTurrets() - return 1 + updateTurrets() /obj/machinery/turretid/proc/updateTurrets() var/datum/turret_checks/TC = new @@ -183,10 +193,11 @@ TC.check_weapons = check_weapons TC.check_anomalies = check_anomalies TC.check_all = check_all + TC.check_down = check_down TC.ailock = ailock if(istype(control_area)) - for (var/obj/machinery/porta_turret/aTurret in control_area) + for(var/obj/machinery/porta_turret/aTurret in control_area) aTurret.setState(TC) update_icon() diff --git a/code/game/machinery/vitals_monitor.dm b/code/game/machinery/vitals_monitor.dm index a26c3ff706..e6d1ba5811 100644 --- a/code/game/machinery/vitals_monitor.dm +++ b/code/game/machinery/vitals_monitor.dm @@ -144,6 +144,6 @@ if(!istype(user)) return - if(CanInteract(user, physical_state)) + if(CanInteract(user, GLOB.tgui_physical_state)) beep = !beep to_chat(user, "You turn the sound on \the [src] [beep ? "on" : "off"].") diff --git a/code/game/mecha/combat/combat.dm b/code/game/mecha/combat/combat.dm index d618d276c3..7661bb3255 100644 --- a/code/game/mecha/combat/combat.dm +++ b/code/game/mecha/combat/combat.dm @@ -17,6 +17,8 @@ max_special_equip = 1 cargo_capacity = 1 + encumbrance_gap = 1.5 + starting_components = list( /obj/item/mecha_parts/component/hull/durable, /obj/item/mecha_parts/component/actuator, diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm index 17aea92345..3ae3e41e13 100644 --- a/code/game/mecha/combat/phazon.dm +++ b/code/game/mecha/combat/phazon.dm @@ -25,6 +25,8 @@ max_universal_equip = 3 max_special_equip = 4 + encumbrance_gap = 2 + starting_components = list( /obj/item/mecha_parts/component/hull/durable, /obj/item/mecha_parts/component/actuator, diff --git a/code/game/mecha/equipment/tools/drill.dm b/code/game/mecha/equipment/tools/drill.dm index 346481eb66..7a3587e454 100644 --- a/code/game/mecha/equipment/tools/drill.dm +++ b/code/game/mecha/equipment/tools/drill.dm @@ -49,11 +49,34 @@ for(var/obj/item/weapon/ore/ore in range(chassis,1)) if(get_dir(chassis,ore)&chassis.dir) ore.forceMove(ore_box) + else if(isliving(target)) + drill_mob(target, chassis.occupant) + return 1 else if(target.loc == C) log_message("Drilled through [target]") target.ex_act(2) return 1 +/obj/item/mecha_parts/mecha_equipment/tool/drill/proc/drill_mob(mob/living/target, mob/user) + add_attack_logs(user, target, "attacked", "[name]", "(INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") + var/drill_force = force //Couldn't manage it otherwise. + if(ishuman(target)) + target.apply_damage(drill_force, BRUTE) + return + + else if(istype(target, /mob/living/simple_mob)) + var/mob/living/simple_mob/S = target + if(target.stat == DEAD) + if(S.meat_amount > 0) + S.harvest(user) + return + else + S.gib() + return + else + S.apply_damage(drill_force) + return + /obj/item/mecha_parts/mecha_equipment/tool/drill/diamonddrill name = "diamond drill" desc = "This is an upgraded version of the drill that'll pierce the heavens!" diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index a6aafb577e..e9e3397bba 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -31,7 +31,10 @@ var/initial_icon = null //Mech type for resetting icon. Only used for reskinning kits (see custom items) var/can_move = 1 var/mob/living/carbon/occupant = null + var/step_in = 10 //Make a step in step_in/10 sec. + var/encumbrance_gap = 1 //How many points of slowdown are negated from equipment? Added to the mech's base step_in. + var/dir_in = 2 //What direction will the mech face when entered/powered on? Defaults to South. var/step_energy_drain = 10 var/health = 300 //Health is health @@ -193,6 +196,7 @@ var/datum/action/innate/mecha/mech_toggle_cloaking/cloak_action = new var/weapons_only_cycle = FALSE //So combat mechs don't switch to their equipment at times. + /obj/mecha/Initialize() ..() @@ -559,12 +563,12 @@ target.attack_hand(src.occupant) return 1 if(istype(target, /obj/machinery/embedded_controller)) - target.ui_interact(src.occupant) + target.tgui_interact(src.occupant) return 1 return 0 -/obj/mecha/contents_nano_distance(var/src_object, var/mob/living/user) - . = user.shared_living_nano_distance(src_object) //allow them to interact with anything they can interact with normally. +/obj/mecha/contents_tgui_distance(var/src_object, var/mob/living/user) + . = user.shared_living_tgui_distance(src_object) //allow them to interact with anything they can interact with normally. if(. != STATUS_INTERACTIVE) //Allow interaction with the mecha or anything that is part of the mecha if(src_object == src || (src_object in src)) @@ -641,18 +645,21 @@ /obj/mecha/proc/get_step_delay() var/tally = 0 - if(overload) - tally = min(1, round(step_in/2)) + if(LAZYLEN(equipment)) + for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) + if(ME.get_step_delay()) + tally += ME.get_step_delay() + + if(tally <= encumbrance_gap) // If the total is less than our encumbrance gap, ignore equipment weight. + tally = 0 + else // Otherwise, start the tally after cutting that gap out. + tally -= encumbrance_gap for(var/slot in internal_components) var/obj/item/mecha_parts/component/C = internal_components[slot] if(C && C.get_step_delay()) tally += C.get_step_delay() - for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) - if(ME.get_step_delay()) - tally += ME.get_step_delay() - var/obj/item/mecha_parts/component/actuator/actuator = internal_components[MECH_ACTUATOR] if(!actuator) // Relying purely on hydraulic pumps. You're going nowhere fast. @@ -674,7 +681,10 @@ break break - return max(1, round(tally, 0.1)) + if(overload) // At the end, because this would normally just make the mech *slower* since tally wasn't starting at 0. + tally = min(1, round(tally/2)) + + return max(1, round(tally, 0.1)) // Round the total to the nearest 10th. Can't go lower than 1 tick. Even humans have a delay longer than that. /obj/mecha/proc/dyndomove(direction) if(!can_move) diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm index 86405b6934..aff19e301d 100644 --- a/code/game/mecha/working/ripley.dm +++ b/code/game/mecha/working/ripley.dm @@ -14,6 +14,8 @@ minimum_penetration = 10 + encumbrance_gap = 2 + starting_components = list( /obj/item/mecha_parts/component/hull/durable, /obj/item/mecha_parts/component/actuator, diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 9812704a75..e90cf3aaea 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -91,16 +91,16 @@ var/icon/default_worn_icon //Default on-mob icon var/worn_layer //Default on-mob layer - + // Pickup/Drop/Equip/Throw Sounds ///Used when thrown into a mob var/mob_throw_hit_sound // Sound used when equipping the items into a valid slot. - var/equip_sound + var/equip_sound // pickup sound - this is the default - var/pickup_sound = 'sound/items/pickup/device.ogg' + var/pickup_sound = "generic_pickup" // drop sound - this is the default - var/drop_sound = 'sound/items/drop/device.ogg' + var/drop_sound = "generic_drop" var/tip_timer // reference to timer id for a tooltip we might open soon @@ -468,12 +468,12 @@ var/list/global/slot_flags_enumeration = list( if(!canremove) return 0 - + if(!slot) if(issilicon(M)) return 1 // for stuff in grippers return 0 - + if(!M.slot_is_accessible(slot, src, disable_warning? null : M)) return 0 return 1 @@ -785,6 +785,8 @@ modules/mob/living/carbon/human/life.dm if you die, you will be zoomed out. // My best guess as to why this is here would be that it does so little. Still, keep it under all the procs, for sanity's sake. /obj/item/device icon = 'icons/obj/device.dmi' + pickup_sound = 'sound/items/pickup/device.ogg' + drop_sound = 'sound/items/drop/device.ogg' //Worn icon generation for on-mob sprites /obj/item/proc/make_worn_icon(var/body_type,var/slot_name,var/inhands,var/default_icon,var/default_layer,var/icon/clip_mask = null) //VOREStation edit - add 'clip mask' argument. diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm deleted file mode 100644 index 91784b5de6..0000000000 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ /dev/null @@ -1,1608 +0,0 @@ - -//The advanced pea-green monochrome lcd of tomorrow. - -var/global/list/obj/item/device/pda/PDAs = list() - -/obj/item/device/pda - name = "\improper PDA" - desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." - icon = 'icons/obj/pda.dmi' - icon_state = "pda" - item_state = "electronic" - w_class = ITEMSIZE_SMALL - slot_flags = SLOT_ID | SLOT_BELT - sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/seromi/id.dmi') - - //Main variables - var/pdachoice = 1 - var/owner = null - var/default_cartridge = 0 // Access level defined by cartridge - var/obj/item/weapon/cartridge/cartridge = null //current cartridge - var/mode = 0 //Controls what menu the PDA will display. 0 is hub; the rest are either built in or based on cartridge. - - var/lastmode = 0 - var/ui_tick = 0 - var/nanoUI[0] - - //Secondary variables - var/scanmode = 0 //1 is medical scanner, 2 is forensics, 3 is reagent scanner. - var/fon = 0 //Is the flashlight function on? - var/f_lum = 2 //Luminosity for the flashlight function - var/message_silent = 0 //To beep or not to beep, that is the question - var/news_silent = 1 //To beep or not to beep, that is the question. The answer is No. - var/toff = 0 //If 1, messenger disabled - var/tnote[0] //Current Texts - var/last_text //No text spamming - var/last_honk //Also no honk spamming that's bad too - var/ttone = "beep" //The PDA ringtone! - var/newstone = "beep, beep" //The news ringtone! - var/lock_code = "" // Lockcode to unlock uplink - var/honkamt = 0 //How many honks left when infected with honk.exe - var/mimeamt = 0 //How many silence left when infected with mime.exe - var/note = "Congratulations, your station has chosen the Thinktronic 5230 Personal Data Assistant!" //Current note in the notepad function - var/notehtml = "" - var/cart = "" //A place to stick cartridge menu information - var/detonate = 1 // Can the PDA be blown up? - var/hidden = 0 // Is the PDA hidden from the PDA list? - var/active_conversation = null // New variable that allows us to only view a single conversation. - var/list/conversations = list() // For keeping up with who we have PDA messsages from. - var/new_message = 0 //To remove hackish overlay check - var/new_news = 0 - var/touch_silent = 0 //If 1, no beeps on interacting. - - var/active_feed // The selected feed - var/list/warrant // The warrant as we last knew it - var/list/feeds = list() // The list of feeds as we last knew them - var/list/feed_info = list() // The data and contents of each feed as we last knew them - - var/list/cartmodes = list(40, 42, 43, 433, 44, 441, 45, 451, 46, 48, 47, 49) // If you add more cartridge modes add them to this list as well. - var/list/no_auto_update = list(1, 40, 43, 44, 441, 45, 451) // These modes we turn off autoupdate - var/list/update_every_five = list(3, 41, 433, 46, 47, 48, 49) // These we update every 5 ticks - - var/obj/item/weapon/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. - var/ownjob = null //related to above - this is assignment (potentially alt title) - var/ownrank = null // this one is rank, never alt title - - var/obj/item/device/paicard/pai = null // A slot for a personal AI device - - var/spam_proof = FALSE // If true, it can't be spammed by random events. - -/obj/item/device/pda/examine(mob/user) - . = ..() - if(Adjacent(user)) - . += "The time [stationtime2text()] is displayed in the corner of the screen." - -/obj/item/device/pda/CtrlClick() - if(issilicon(usr)) - return - - if(can_use(usr)) - remove_pen() - return - ..() - -/obj/item/device/pda/AltClick() - if(issilicon(usr)) - return - - if ( can_use(usr) ) - if(id) - remove_id() - else - to_chat(usr, "This PDA does not have an ID in it.") - -//Bloop when using: -/obj/item/device/pda/CouldUseTopic(var/mob/user) - ..() - if(iscarbon(user) && !touch_silent) - playsound(src, 'sound/machines/pda_click.ogg', 20) - -/obj/item/device/pda/medical - default_cartridge = /obj/item/weapon/cartridge/medical - icon_state = "pda-m" - -/obj/item/device/pda/viro - default_cartridge = /obj/item/weapon/cartridge/medical - icon_state = "pda-v" - -/obj/item/device/pda/engineering - default_cartridge = /obj/item/weapon/cartridge/engineering - icon_state = "pda-e" - -/obj/item/device/pda/security - default_cartridge = /obj/item/weapon/cartridge/security - icon_state = "pda-s" - -/obj/item/device/pda/detective - default_cartridge = /obj/item/weapon/cartridge/detective - icon_state = "pda-det" - -/obj/item/device/pda/warden - default_cartridge = /obj/item/weapon/cartridge/security - icon_state = "pda-warden" - -/obj/item/device/pda/janitor - default_cartridge = /obj/item/weapon/cartridge/janitor - icon_state = "pda-j" - ttone = "slip" - -/obj/item/device/pda/science - default_cartridge = /obj/item/weapon/cartridge/signal/science - icon_state = "pda-tox" - ttone = "boom" - -/obj/item/device/pda/clown - default_cartridge = /obj/item/weapon/cartridge/clown - icon_state = "pda-clown" - desc = "A portable microcomputer by Thinktronic Systems, LTD. The surface is coated with polytetrafluoroethylene and banana drippings." - ttone = "honk" - -/obj/item/device/pda/mime - default_cartridge = /obj/item/weapon/cartridge/mime - icon_state = "pda-mime" - message_silent = 1 - news_silent = 1 - ttone = "silence" - newstone = "silence" - -/obj/item/device/pda/heads - default_cartridge = /obj/item/weapon/cartridge/head - icon_state = "pda-h" - news_silent = 1 - -/obj/item/device/pda/heads/hop - default_cartridge = /obj/item/weapon/cartridge/hop - icon_state = "pda-hop" - -/obj/item/device/pda/heads/hos - default_cartridge = /obj/item/weapon/cartridge/hos - icon_state = "pda-hos" - -/obj/item/device/pda/heads/ce - default_cartridge = /obj/item/weapon/cartridge/ce - icon_state = "pda-ce" - -/obj/item/device/pda/heads/cmo - default_cartridge = /obj/item/weapon/cartridge/cmo - icon_state = "pda-cmo" - -/obj/item/device/pda/heads/rd - default_cartridge = /obj/item/weapon/cartridge/rd - icon_state = "pda-rd" - -/obj/item/device/pda/captain - default_cartridge = /obj/item/weapon/cartridge/captain - icon_state = "pda-c" - detonate = 0 - //toff = 1 - -/obj/item/device/pda/ert - default_cartridge = /obj/item/weapon/cartridge/captain - icon_state = "pda-h" - detonate = 0 -// hidden = 1 - -/obj/item/device/pda/cargo - default_cartridge = /obj/item/weapon/cartridge/quartermaster - icon_state = "pda-cargo" - -/obj/item/device/pda/quartermaster - default_cartridge = /obj/item/weapon/cartridge/quartermaster - icon_state = "pda-q" - -/obj/item/device/pda/shaftminer - icon_state = "pda-miner" - default_cartridge = /obj/item/weapon/cartridge/miner - -/obj/item/device/pda/syndicate - default_cartridge = /obj/item/weapon/cartridge/syndicate - icon_state = "pda-syn" -// name = "Military PDA" // Vorestation Edit -// owner = "John Doe" - hidden = 1 - -/obj/item/device/pda/chaplain - default_cartridge = /obj/item/weapon/cartridge/service - icon_state = "pda-holy" - ttone = "holy" - -/obj/item/device/pda/lawyer - default_cartridge = /obj/item/weapon/cartridge/lawyer - icon_state = "pda-lawyer" - ttone = "..." - -/obj/item/device/pda/botanist - default_cartridge = /obj/item/weapon/cartridge/service - icon_state = "pda-hydro" - -/obj/item/device/pda/roboticist - default_cartridge = /obj/item/weapon/cartridge/signal/science - icon_state = "pda-robot" - -/obj/item/device/pda/librarian - default_cartridge = /obj/item/weapon/cartridge/service - icon_state = "pda-libb" - desc = "A portable microcomputer by Thinktronic Systems, LTD. This is model is a WGW-11 series e-reader." - note = "Congratulations, your station has chosen the Thinktronic 5290 WGW-11 Series E-reader and Personal Data Assistant!" - message_silent = 1 //Quiet in the library! - news_silent = 0 // Librarian is above the law! (That and alt job title is reporter) - -/obj/item/device/pda/clear - icon_state = "pda-transp" - desc = "A portable microcomputer by Thinktronic Systems, LTD. This is model is a special edition with a transparent case." - note = "Congratulations, you have chosen the Thinktronic 5230 Personal Data Assistant Deluxe Special Max Turbo Limited Edition!" - -/obj/item/device/pda/chef - default_cartridge = /obj/item/weapon/cartridge/service - icon_state = "pda-chef" - -/obj/item/device/pda/bar - default_cartridge = /obj/item/weapon/cartridge/service - icon_state = "pda-bar" - -/obj/item/device/pda/atmos - default_cartridge = /obj/item/weapon/cartridge/atmos - icon_state = "pda-atmo" - -/obj/item/device/pda/chemist - default_cartridge = /obj/item/weapon/cartridge/chemistry - icon_state = "pda-chem" - -/obj/item/device/pda/geneticist - default_cartridge = /obj/item/weapon/cartridge/medical - icon_state = "pda-gene" - - -// Special AI/pAI PDAs that cannot explode. -/obj/item/device/pda/ai - icon_state = "NONE" - ttone = "data" - newstone = "news" - detonate = 0 - - -/obj/item/device/pda/ai/proc/set_name_and_job(newname as text, newjob as text, newrank as null|text) - owner = newname - ownjob = newjob - if(newrank) - ownrank = newrank - else - ownrank = ownjob - name = newname + " (" + ownjob + ")" - -//AI verb and proc for sending PDA messages. -/obj/item/device/pda/ai/verb/cmd_send_pdamesg() - set category = "AI IM" - set name = "Send Message" - set src in usr - if(usr.stat == 2) - to_chat(usr, "You can't send PDA messages because you are dead!") - return - var/list/plist = available_pdas() - if (plist) - var/c = input(usr, "Please select a PDA") as null|anything in sortList(plist) - if (!c) // if the user hasn't selected a PDA file we can't send a message - return - var/selected = plist[c] - create_message(usr, selected, 0) - -/obj/item/device/pda/ai/verb/cmd_toggle_pda_receiver() - set category = "AI IM" - set name = "Toggle Sender/Receiver" - set src in usr - if(usr.stat == 2) - to_chat(usr, "You can't send PDA messages because you are dead!") - return - toff = !toff - to_chat(usr, "PDA sender/receiver toggled [(toff ? "Off" : "On")]!") - -/obj/item/device/pda/ai/verb/cmd_toggle_pda_silent() - set category = "AI IM" - set name = "Toggle Ringer" - set src in usr - if(usr.stat == 2) - to_chat(usr, "You can't send PDA messages because you are dead!") - return - message_silent=!message_silent - to_chat(usr, "PDA ringer toggled [(message_silent ? "Off" : "On")]!") - -/obj/item/device/pda/ai/verb/cmd_show_message_log() - set category = "AI IM" - set name = "Show Message Log" - set src in usr - if(usr.stat == 2) - to_chat(usr, "You can't send PDA messages because you are dead!") - return - var/HTML = "AI PDA Message Log" - for(var/index in tnote) - if(index["sent"]) - HTML += addtext("→ To ", index["owner"],":
", index["message"], "
") - else - HTML += addtext("← From ", index["owner"],":
", index["message"], "
") - HTML +="" - usr << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0") - - -/obj/item/device/pda/ai/can_use() - return 1 - - -/obj/item/device/pda/ai/attack_self(mob/user as mob) - if ((honkamt > 0) && (prob(60)))//For clown virus. - honkamt-- - playsound(src, 'sound/items/bikehorn.ogg', 30, 1) - return - - -/obj/item/device/pda/ai/pai - ttone = "assist" - -/obj/item/device/pda/ai/shell - spam_proof = TRUE // Since empty shells get a functional PDA. - -// Used for the PDA multicaster, which mirrors messages sent to it to a specific department, -/obj/item/device/pda/multicaster - ownjob = "Relay" - icon_state = "NONE" - ttone = "data" - detonate = 0 - news_silent = 1 - spam_proof = TRUE // Spam messages don't actually work and its difficult to disable these. - var/list/cartridges_to_send_to = list() - -// This is what actually mirrors the message, -/obj/item/device/pda/multicaster/new_message(var/sending_unit, var/sender, var/sender_job, var/message) - if(sender) - var/list/targets = list() - for(var/obj/item/device/pda/pda in PDAs) - if(pda.cartridge && pda.owner && is_type_in_list(pda.cartridge, cartridges_to_send_to)) - targets |= pda - if(targets.len) - for(var/obj/item/device/pda/target in targets) - create_message(target, sender, sender_job, message) - -// This has so much copypasta, -/obj/item/device/pda/multicaster/create_message(var/obj/item/device/pda/P, var/original_sender, var/original_job, var/t) - t = sanitize(t, MAX_MESSAGE_LEN, 0) - t = replace_characters(t, list(""" = "\"")) - if (!t || !istype(P)) - return - - if (isnull(P)||P.toff || toff) - return - - last_text = world.time - var/datum/reception/reception = get_reception(src, P, t) - t = reception.message - - if(reception.message_server && (reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER)) // only send the message if it's stable, - if(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER == 0) // Does our recipient have a broadcaster on their level?, - return - var/send_result = reception.message_server.send_pda_message("[P.owner]","[owner]","[t]") - if (send_result) - return - - P.tnote.Add(list(list("sent" = 0, "owner" = "[owner]", "job" = "[ownjob]", "message" = "[t]", "target" = "\ref[src]"))) - - if(!P.conversations.Find("\ref[src]")) - P.conversations.Add("\ref[src]") - - P.new_message(src, "[original_sender] \[Relayed\]", original_job, t, 0) - - else - return - -/obj/item/device/pda/multicaster/command/New() - ..() - owner = "Command Department" - name = "Command Department (Relay)" - cartridges_to_send_to = command_cartridges - -/obj/item/device/pda/multicaster/security/New() - ..() - owner = "Security Department" - name = "Security Department (Relay)" - cartridges_to_send_to = security_cartridges - -/obj/item/device/pda/multicaster/engineering/New() - ..() - owner = "Engineering Department" - name = "Engineering Department (Relay)" - cartridges_to_send_to = engineering_cartridges - -/obj/item/device/pda/multicaster/medical/New() - ..() - owner = "Medical Department" - name = "Medical Department (Relay)" - cartridges_to_send_to = medical_cartridges - -/obj/item/device/pda/multicaster/research/New() - ..() - owner = "Research Department" - name = "Research Department (Relay)" - cartridges_to_send_to = research_cartridges - -/obj/item/device/pda/multicaster/cargo/New() - ..() - owner = "Cargo Department" - name = "Cargo Department (Relay)" - cartridges_to_send_to = cargo_cartridges - -/obj/item/device/pda/multicaster/civilian/New() - ..() - owner = "Civilian Services Department" - name = "Civilian Services Department (Relay)" - cartridges_to_send_to = civilian_cartridges - -/* - * The Actual PDA - */ - -/obj/item/device/pda/New(var/mob/living/carbon/human/H) - ..() - PDAs += src - PDAs = sortAtom(PDAs) - if(default_cartridge) - cartridge = new default_cartridge(src) - new /obj/item/weapon/pen(src) - pdachoice = isnull(H) ? 1 : (ishuman(H) ? H.pdachoice : 1) - switch(pdachoice) - if(1) icon = 'icons/obj/pda.dmi' - if(2) icon = 'icons/obj/pda_slim.dmi' - if(3) icon = 'icons/obj/pda_old.dmi' - if(4) icon = 'icons/obj/pda_rugged.dmi' - if(5) icon = 'icons/obj/pda_holo.dmi' - if(6) - icon = 'icons/obj/pda_wrist.dmi' - item_state = icon_state - item_icons = list( - slot_belt_str = 'icons/mob/pda_wrist.dmi', - slot_wear_id_str = 'icons/mob/pda_wrist.dmi', - slot_gloves_str = 'icons/mob/pda_wrist.dmi' - ) - desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a wrist-bound version." - slot_flags = SLOT_ID | SLOT_BELT | SLOT_GLOVES - sprite_sheets = list( - SPECIES_TESHARI = 'icons/mob/species/seromi/pda_wrist.dmi', - SPECIES_VR_TESHARI = 'icons/mob/species/seromi/pda_wrist.dmi', - ) - else - icon = 'icons/obj/pda_old.dmi' - log_debug("Invalid switch for PDA, defaulting to old PDA icons. [pdachoice] chosen.") - - -/obj/item/device/pda/proc/can_use() - - if(!ismob(loc)) - return 0 - - var/mob/M = loc - if(M.stat || M.restrained() || M.paralysis || M.stunned || M.weakened) - return 0 - if((src in M.contents) || ( istype(loc, /turf) && in_range(src, M) )) - return 1 - else - return 0 - -/obj/item/device/pda/GetAccess() - if(id) - return id.GetAccess() - else - return ..() - -/obj/item/device/pda/GetID() - return id - -/obj/item/device/pda/MouseDrop(obj/over_object as obj, src_location, over_location) - var/mob/M = usr - if((!istype(over_object, /obj/screen)) && can_use()) - return attack_self(M) - return - - -/obj/item/device/pda/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - ui_tick++ - var/datum/nanoui/old_ui = SSnanoui.get_open_ui(user, src, "main") - var/auto_update = 1 - if(mode in no_auto_update) - auto_update = 0 - if(old_ui && (mode == lastmode && ui_tick % 5 && mode in update_every_five)) - return - - lastmode = mode - - var/title = "Personal Data Assistant" - - var/data[0] // This is the data that will be sent to the PDA - - data["owner"] = owner // Who is your daddy... - data["ownjob"] = ownjob // ...and what does he do? - - data["mode"] = mode // The current view - data["scanmode"] = scanmode // Scanners - data["fon"] = fon // Flashlight on? - data["pai"] = (isnull(pai) ? 0 : 1) // pAI inserted? - data["note"] = note // current pda notes - data["message_silent"] = message_silent // does the pda make noise when it receives a message? - data["news_silent"] = news_silent // does the pda make noise when it receives news? - data["touch_silent"] = touch_silent // does the pda make noise when it receives news? - data["toff"] = toff // is the messenger function turned off? - data["active_conversation"] = active_conversation // Which conversation are we following right now? - - - data["idInserted"] = (id ? 1 : 0) - data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") - - data["cart_loaded"] = cartridge ? 1:0 - if(cartridge) - var/cartdata[0] - cartdata["access"] = list(\ - "access_security" = cartridge.access_security,\ - "access_engine" = cartridge.access_engine,\ - "access_atmos" = cartridge.access_atmos,\ - "access_medical" = cartridge.access_medical,\ - "access_clown" = cartridge.access_clown,\ - "access_mime" = cartridge.access_mime,\ - "access_janitor" = cartridge.access_janitor,\ - "access_quartermaster" = cartridge.access_quartermaster,\ - "access_hydroponics" = cartridge.access_hydroponics,\ - "access_reagent_scanner" = cartridge.access_reagent_scanner,\ - "access_remote_door" = cartridge.access_remote_door,\ - "access_status_display" = cartridge.access_status_display,\ - "access_detonate_pda" = cartridge.access_detonate_pda\ - ) - - if(mode in cartmodes) - data["records"] = cartridge.create_NanoUI_values() - - if(mode == 0) - cartdata["name"] = cartridge.name - if(isnull(cartridge.radio)) - cartdata["radio"] = 0 - else - if(istype(cartridge.radio, /obj/item/radio/integrated/beepsky)) - cartdata["radio"] = 1 - if(istype(cartridge.radio, /obj/item/radio/integrated/signal)) - cartdata["radio"] = 2 - //if(istype(cartridge.radio, /obj/item/radio/integrated/mule)) - // cartdata["radio"] = 3 - - if(mode == 2) - cartdata["charges"] = cartridge.charges ? cartridge.charges : 0 - data["cartridge"] = cartdata - - data["stationTime"] = stationtime2text() - data["new_Message"] = new_message - data["new_News"] = new_news - - var/datum/reception/reception = get_reception(src, do_sleep = 0) - var/has_reception = reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER - data["reception"] = has_reception - - if(mode==2) - var/convopdas[0] - var/pdas[0] - var/count = 0 - for (var/obj/item/device/pda/P in PDAs) - if (!P.owner||P.toff||P == src||P.hidden) continue - if(conversations.Find("\ref[P]")) - convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1"))) - else - pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0"))) - count++ - - data["convopdas"] = convopdas - data["pdas"] = pdas - data["pda_count"] = count - - if(mode==21) - data["messagescount"] = tnote.len - data["messages"] = tnote - else - data["messagescount"] = null - data["messages"] = null - - if(active_conversation) - for(var/c in tnote) - if(c["target"] == active_conversation) - data["convo_name"] = sanitize(c["owner"]) - data["convo_job"] = sanitize(c["job"]) - break - if(mode==41) - data_core.get_manifest_list() - - - if(mode==3) - data["aircontents"] = src.analyze_air() - if(mode==6) - if(has_reception) - feeds.Cut() - for(var/datum/feed_channel/channel in news_network.network_channels) - feeds[++feeds.len] = list("name" = channel.channel_name, "censored" = channel.censored) - data["feedChannels"] = feeds - if(mode==61) - var/datum/feed_channel/FC - for(FC in news_network.network_channels) - if(FC.channel_name == active_feed["name"]) - break - - var/list/feed = feed_info[active_feed] - if(!feed) - feed = list() - feed["channel"] = FC.channel_name - feed["author"] = "Unknown" - feed["censored"]= 0 - feed["updated"] = -1 - feed_info[active_feed] = feed - - if(FC.updated > feed["updated"] && has_reception) - feed["author"] = FC.author - feed["updated"] = FC.updated - feed["censored"] = FC.censored - - var/list/messages = list() - if(!FC.censored) - var/index = 0 - for(var/datum/feed_message/FM in FC.messages) - index++ - if(FM.img) - usr << browse_rsc(FM.img, "pda_news_tmp_photo_[feed["channel"]]_[index].png") - // News stories are HTML-stripped but require newline replacement to be properly displayed in NanoUI - var/body = replacetext(FM.body, "\n", "
") - messages[++messages.len] = list("author" = FM.author, "body" = body, "message_type" = FM.message_type, "time_stamp" = FM.time_stamp, "has_image" = (FM.img != null), "caption" = FM.caption, "index" = index) - feed["messages"] = messages - - data["feed"] = feed - - data["manifest"] = PDA_Manifest - - nanoUI = data - // update the ui if it exists, returns null if no ui is passed/found - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - - if (!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "pda.tmpl", title, 520, 400, state = inventory_state) - // add templates for screens in common with communicator. - ui.add_template("atmosphericScan", "atmospheric_scan.tmpl") - ui.add_template("crewManifest", "crew_manifest.tmpl") - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(auto_update) - -/obj/item/device/pda/attack_self(mob/user as mob) - user.set_machine(src) - - if(active_uplink_check(user)) - return - - ui_interact(user) //NanoUI requires this proc - return - -/obj/item/device/pda/Topic(href, href_list) - if(href_list["cartmenu"] && !isnull(cartridge)) - cartridge.Topic(href, href_list) - return 1 - if(href_list["radiomenu"] && !isnull(cartridge) && !isnull(cartridge.radio)) - cartridge.radio.Topic(href, href_list) - return 1 - - - ..() - var/mob/user = usr - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - var/mob/living/U = usr - //Looking for master was kind of pointless since PDAs don't appear to have one. - //if ((src in U.contents) || ( istype(loc, /turf) && in_range(src, U) ) ) - if (usr.stat == DEAD) - return 0 - if(!can_use()) //Why reinvent the wheel? There's a proc that does exactly that. - U.unset_machine() - if(ui) - ui.close() - return 0 - - add_fingerprint(U) - U.set_machine(src) - - switch(href_list["choice"]) - -//BASIC FUNCTIONS=================================== - - if("Close")//Self explanatory - U.unset_machine() - ui.close() - return 0 - if("Refresh")//Refresh, goes to the end of the proc. - if("Return")//Return - if(mode<=9) - mode = 0 - else - mode = round(mode/10) - if(mode==2) - active_conversation = null - if(mode==4)//Fix for cartridges. Redirects to hub. - mode = 0 - else if(mode >= 40 && mode <= 49)//Fix for cartridges. Redirects to refresh the menu. - cartridge.mode = mode - if ("Authenticate")//Checks for ID - id_check(U, 1) - if("UpdateInfo") - ownjob = id.assignment - ownrank = id.rank - name = "PDA-[owner] ([ownjob])" - if("Eject")//Ejects the cart, only done from hub. - verb_remove_cartridge() - -//MENU FUNCTIONS=================================== - - if("0")//Hub - mode = 0 - if("1")//Notes - mode = 1 - if("2")//Messenger - mode = 2 - if("21")//Read messages - mode = 21 - if("3")//Atmos scan - mode = 3 - if("4")//Redirects to hub - mode = 0 - if("chatroom") // chatroom hub - mode = 5 - if("41") //Manifest - mode = 41 - - -//MAIN FUNCTIONS=================================== - - if("Light") - if(fon) - fon = 0 - set_light(0) - else - fon = 1 - set_light(f_lum) - if("Medical Scan") - if(scanmode == 1) - scanmode = 0 - else if((!isnull(cartridge)) && (cartridge.access_medical)) - scanmode = 1 - if("Reagent Scan") - if(scanmode == 3) - scanmode = 0 - else if((!isnull(cartridge)) && (cartridge.access_reagent_scanner)) - scanmode = 3 - if("Halogen Counter") - if(scanmode == 4) - scanmode = 0 - else if((!isnull(cartridge)) && (cartridge.access_engine)) - scanmode = 4 - if("Honk") - if ( !(last_honk && world.time < last_honk + 20) ) - playsound(src, 'sound/items/bikehorn.ogg', 50, 1) - last_honk = world.time - if("Gas Scan") - if(scanmode == 5) - scanmode = 0 - else if((!isnull(cartridge)) && (cartridge.access_atmos)) - scanmode = 5 - if("Toggle Beeping") - touch_silent = !touch_silent - -//MESSENGER/NOTE FUNCTIONS=================================== - - if ("Edit") - var/n = input(U, "Please enter message", name, notehtml) as message - if (in_range(src, U) && loc == U) - n = sanitizeSafe(n, extra = 0) - if (mode == 1) - note = html_decode(n) - notehtml = note - note = replacetext(note, "\n", "
") - else - ui.close() - if("Toggle Messenger") - toff = !toff - if("Toggle Ringer")//If viewing texts then erase them, if not then toggle silent status - message_silent = !message_silent - if("Toggle News") - news_silent = !news_silent - if("Clear")//Clears messages - if(href_list["option"] == "All") - tnote.Cut() - conversations.Cut() - if(href_list["option"] == "Convo") - var/new_tnote[0] - for(var/i in tnote) - if(i["target"] != active_conversation) - new_tnote[++new_tnote.len] = i - tnote = new_tnote - conversations.Remove(active_conversation) - - active_conversation = null - if(mode==21) - mode=2 - - if("Ringtone") - var/t = input(U, "Please enter new ringtone", name, ttone) as text - if (in_range(src, U) && loc == U) - if (t) - if(src.hidden_uplink && hidden_uplink.check_trigger(U, lowertext(t), lowertext(lock_code))) - to_chat(U, "The PDA softly beeps.") - ui.close() - else - t = sanitize(t, 20) - ttone = t - else - ui.close() - return 0 - if("Newstone") - var/t = input(U, "Please enter new news tone", name, newstone) as text - if (in_range(src, U) && loc == U) - if (t) - t = sanitize(t, 20) - newstone = t - else - ui.close() - return 0 - if("Message") - - var/obj/item/device/pda/P = locate(href_list["target"]) - src.create_message(U, P, !href_list["notap"]) - if(mode == 2) - if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp. - active_conversation = href_list["target"] - mode = 21 - - if("Select Conversation") - var/P = href_list["convo"] - for(var/n in conversations) - if(P == n) - active_conversation=P - mode=21 - if("Select Feed") - var/n = href_list["name"] - for(var/f in feeds) - if(f["name"] == n) - active_feed = f - mode=61 - if("Send Honk")//Honk virus - if(cartridge && cartridge.access_clown)//Cartridge checks are kind of unnecessary since everything is done through switch. - var/obj/item/device/pda/P = locate(href_list["target"])//Leaving it alone in case it may do something useful, I guess. - if(!isnull(P)) - if (!P.toff && cartridge.charges > 0) - cartridge.charges-- - U.show_message("Virus sent!", 1) - P.honkamt = (rand(15,20)) - else - to_chat(U, "PDA not found.") - else - ui.close() - return 0 - if("Send Silence")//Silent virus - if(cartridge && cartridge.access_mime) - var/obj/item/device/pda/P = locate(href_list["target"]) - if(!isnull(P)) - if (!P.toff && cartridge.charges > 0) - cartridge.charges-- - U.show_message("Virus sent!", 1) - P.message_silent = 1 - P.news_silent = 1 - P.ttone = "silence" - P.newstone = "silence" - else - to_chat(U, "PDA not found.") - else - ui.close() - return 0 - - -//SYNDICATE FUNCTIONS=================================== - - if("Toggle Door") - if(cartridge && cartridge.access_remote_door) - for(var/obj/machinery/door/blast/M in machines) - if(M.id == cartridge.remote_door_id) - if(M.density) - M.open() - else - M.close() - - if("Detonate")//Detonate PDA... maybe - if(cartridge && cartridge.access_detonate_pda) - var/obj/item/device/pda/P = locate(href_list["target"]) - var/datum/reception/reception = get_reception(src, P, "", do_sleep = 0) - if(!(reception.message_server && reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER)) - U.show_message("An error flashes on your [src]: Connection unavailable", 1) - return - if(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER == 0) // Does our recepient have a broadcaster on their level? - U.show_message("An error flashes on your [src]: Recipient unavailable", 1) - return - if(!isnull(P)) - if (!P.toff && cartridge.charges > 0) - cartridge.charges-- - - var/difficulty = 2 - - if(P.cartridge) - difficulty += P.cartridge.access_medical - difficulty += P.cartridge.access_security - difficulty += P.cartridge.access_engine - difficulty += P.cartridge.access_clown - difficulty += P.cartridge.access_janitor - if(P.hidden_uplink) - difficulty += 3 - - if(prob(difficulty)) - U.show_message("An error flashes on your [src].", 1) - else if (prob(difficulty * 7)) - U.show_message("Energy feeds back into your [src]!", 1) - ui.close() - detonate_act(src) - log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up") - message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge but failed.", 1) - else - U.show_message("Success!", 1) - log_admin("[key_name(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded") - message_admins("[key_name_admin(U)] just attempted to blow up [P] with the Detomatix cartridge and succeeded.", 1) - detonate_act(P) - else - to_chat(U, "No charges left.") - - else - to_chat(U, "PDA not found.") - else - U.unset_machine() - ui.close() - return 0 - -//pAI FUNCTIONS=================================== - if("pai") - if(pai) - if(pai.loc != src) - pai = null - else - switch(href_list["option"]) - if("1") // Configure pAI device - pai.attack_self(U) - if("2") // Eject pAI device - var/turf/T = get_turf_or_move(src.loc) - if(T) - pai.loc = T - pai = null - - else - mode = text2num(href_list["choice"]) - if(cartridge) - cartridge.mode = mode - -//EXTRA FUNCTIONS=================================== - - if (mode == 2||mode == 21)//To clear message overlays. - new_message = 0 - update_icon() - - if (mode == 6||mode == 61)//To clear news overlays. - new_news = 0 - update_icon() - - if ((honkamt > 0) && (prob(60)))//For clown virus. - honkamt-- - playsound(src, 'sound/items/bikehorn.ogg', 30, 1) - - return 1 // return 1 tells it to refresh the UI in NanoUI - -/obj/item/device/pda/update_icon() - ..() - - overlays.Cut() - if(new_message || new_news) - overlays += image(icon, "pda-r") - -/obj/item/device/pda/proc/detonate_act(var/obj/item/device/pda/P) - //TODO: sometimes these attacks show up on the message server - var/i = rand(1,100) - var/j = rand(0,1) //Possibility of losing the PDA after the detonation - var/message = "" - var/mob/living/M = null - if(ismob(P.loc)) - M = P.loc - - //switch(i) //Yes, the overlapping cases are intended. - if(i<=10) //The traditional explosion - P.explode() - j=1 - message += "Your [P] suddenly explodes!" - if(i>=10 && i<= 20) //The PDA burns a hole in the holder. - j=1 - if(M && isliving(M)) - M.apply_damage( rand(30,60) , BURN) - message += "You feel a searing heat! Your [P] is burning!" - if(i>=20 && i<=25) //EMP - empulse(P.loc, 1, 2, 4, 6, 1) - message += "Your [P] emits a wave of electromagnetic energy!" - if(i>=25 && i<=40) //Smoke - var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem - S.attach(P.loc) - S.set_up(P, 10, 0, P.loc) - playsound(P, 'sound/effects/smoke.ogg', 50, 1, -3) - S.start() - message += "Large clouds of smoke billow forth from your [P]!" - if(i>=40 && i<=45) //Bad smoke - var/datum/effect/effect/system/smoke_spread/bad/B = new /datum/effect/effect/system/smoke_spread/bad - B.attach(P.loc) - B.set_up(P, 10, 0, P.loc) - playsound(P, 'sound/effects/smoke.ogg', 50, 1, -3) - B.start() - message += "Large clouds of noxious smoke billow forth from your [P]!" - if(i>=65 && i<=75) //Weaken - if(M && isliving(M)) - M.apply_effects(0,1) - message += "Your [P] flashes with a blinding white light! You feel weaker." - if(i>=75 && i<=85) //Stun and stutter - if(M && isliving(M)) - M.apply_effects(1,0,0,0,1) - message += "Your [P] flashes with a blinding white light! You feel weaker." - if(i>=85) //Sparks - var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread - s.set_up(2, 1, P.loc) - s.start() - message += "Your [P] begins to spark violently!" - if(i>45 && i<65 && prob(50)) //Nothing happens - message += "Your [P] bleeps loudly." - j = prob(10) - - if(j && detonate) //This kills the PDA - qdel(P) - if(message) - message += "It melts in a puddle of plastic." - else - message += "Your [P] shatters in a thousand pieces!" - - if(M && isliving(M)) - message = "[message]" - M.show_message(message, 1) - -/obj/item/device/pda/proc/remove_id() - if (id) - if (ismob(loc)) - var/mob/M = loc - M.put_in_hands(id) - to_chat(usr, "You remove the ID from the [name].") - playsound(src, 'sound/machines/id_swipe.ogg', 100, 1) - else - id.loc = get_turf(src) - id = null - -/obj/item/device/pda/proc/remove_pen() - var/obj/item/weapon/pen/O = locate() in src - if(O) - if(istype(loc, /mob)) - var/mob/M = loc - if(M.get_active_hand() == null) - M.put_in_hands(O) - to_chat(usr, "You remove \the [O] from \the [src].") - return - O.loc = get_turf(src) - else - to_chat(usr, "This PDA does not have a pen in it.") - -/obj/item/device/pda/proc/create_message(var/mob/living/U = usr, var/obj/item/device/pda/P, var/tap = 1) - if(tap) - U.visible_message("\The [U] taps on their PDA's screen.") - var/t = input(U, "Please enter message", P.name, null) as text - t = sanitize(t) - //t = readd_quotes(t) - t = replace_characters(t, list(""" = "\"")) - if (!t || !istype(P)) - return - if (!in_range(src, U) && loc != U) - return - - if (isnull(P)||P.toff || toff) - return - - if (last_text && world.time < last_text + 5) - return - - if (!can_use()) - return - - if (is_jammed(src)) - return - - last_text = world.time - var/datum/reception/reception = get_reception(src, P, t) - t = reception.message - - if(reception.message_server && (reception.telecomms_reception & TELECOMMS_RECEPTION_SENDER)) // only send the message if it's stable - if(reception.telecomms_reception & TELECOMMS_RECEPTION_RECEIVER == 0) // Does our recipient have a broadcaster on their level? - to_chat(U, "ERROR: Cannot reach recipient.") - return - var/send_result = reception.message_server.send_pda_message("[P.owner]","[owner]","[t]") - if (send_result) - to_chat(U, "ERROR: Messaging server rejected your message. Reason: contains '[send_result]'.") - return - - tnote.Add(list(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]"))) - P.tnote.Add(list(list("sent" = 0, "owner" = "[owner]", "job" = "[ownjob]", "message" = "[t]", "target" = "\ref[src]"))) - for(var/mob/M in player_list) - if(M.stat == DEAD && M.client && (M.is_preference_enabled(/datum/client_preference/ghost_ears))) // src.client is so that ghosts don't have to listen to mice - if(istype(M, /mob/new_player)) - continue - if(M.forbid_seeing_deadchat) - continue - M.show_message("PDA Message - [owner] -> [P.owner]: [t]") - - if(!conversations.Find("\ref[P]")) - conversations.Add("\ref[P]") - if(!P.conversations.Find("\ref[src]")) - P.conversations.Add("\ref[src]") - to_chat(U, "[bicon(src)] Sent message to [P.owner] ([P.ownjob]), \"[t]\"") - - if (prob(5) && security_level >= SEC_LEVEL_BLUE) //Give the AI a chance of intercepting the message //VOREStation Edit: no spam interception on lower codes + lower interception chance - var/who = src.owner - if(prob(50)) - who = P.owner - for(var/mob/living/silicon/ai/ai in mob_list) - // Allows other AIs to intercept the message but the AI won't intercept their own message. - if(ai.aiPDA != P && ai.aiPDA != src) - ai.show_message("Intercepted message from [who]: [t]") - - P.new_message_from_pda(src, t) - SSnanoui.update_user_uis(U, src) // Update the sending user's PDA UI so that they can see the new message - else - to_chat(U, "ERROR: Messaging server is not responding.") - -/obj/item/device/pda/proc/new_info(var/beep_silent, var/message_tone, var/reception_message) - if (!beep_silent) - playsound(src, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(2, loc)) - O.show_message(text("[bicon(src)] *[message_tone]*")) - //Search for holder of the PDA. - var/mob/living/L = null - if(loc && isliving(loc)) - L = loc - //Maybe they are a pAI! - else - L = get(src, /mob/living/silicon) - - if(L) - if(reception_message) - to_chat(L,reception_message) - SSnanoui.update_user_uis(L, src) // Update the receiving user's PDA UI so that they can see the new message - -/obj/item/device/pda/proc/new_news(var/message) - new_info(news_silent, newstone, news_silent ? "" : "[bicon(src)] [message]") - - if(!news_silent) - new_news = 1 - update_icon() - -/obj/item/device/pda/ai/new_news(var/message) - // Do nothing - -/obj/item/device/pda/proc/new_message_from_pda(var/obj/item/device/pda/sending_device, var/message) - if (is_jammed(src)) - return - new_message(sending_device, sending_device.owner, sending_device.ownjob, message) - -/obj/item/device/pda/proc/new_message(var/sending_unit, var/sender, var/sender_job, var/message, var/reply = 1) - var/reception_message = "[bicon(src)] Message from [sender] ([sender_job]), \"[message]\" ([reply ? "Reply" : "Unable to Reply"])" - new_info(message_silent, ttone, reception_message) - - log_pda("(PDA: [sending_unit]) sent \"[message]\" to [name]", usr) - new_message = 1 - update_icon() - -/obj/item/device/pda/ai/new_message(var/atom/movable/sending_unit, var/sender, var/sender_job, var/message) - var/track = "" - if(ismob(sending_unit.loc) && isAI(loc)) - track = "(Follow)" - - var/reception_message = "[bicon(src)] Message from [sender] ([sender_job]), \"[message]\" (Reply) [track]" - new_info(message_silent, newstone, reception_message) - - log_pda("(PDA: [sending_unit]) sent \"[message]\" to [name]",usr) - new_message = 1 - -/obj/item/device/pda/proc/spam_message(sender, message) - var/reception_message = "\icon[src] Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)" - new_info(message_silent, ttone, reception_message) - - if(prob(50)) // Give the AI an increased chance to intercept the message - for(var/mob/living/silicon/ai/ai in mob_list) - if(ai.aiPDA != src) - ai.show_message("Intercepted message from [sender] (Unknown / spam?) to [owner]: [message]") - -/obj/item/device/pda/verb/verb_reset_pda() - set category = "Object" - set name = "Reset PDA" - set src in usr - - if(issilicon(usr)) - return - - if(can_use(usr)) - mode = 0 - SSnanoui.update_uis(src) - to_chat(usr, "You press the reset button on \the [src].") - else - to_chat(usr, "You cannot do this while restrained.") - -/obj/item/device/pda/verb/verb_remove_id() - set category = "Object" - set name = "Remove id" - set src in usr - - if(issilicon(usr)) - return - - if ( can_use(usr) ) - if(id) - remove_id() - else - to_chat(usr, "This PDA does not have an ID in it.") - else - to_chat(usr, "You cannot do this while restrained.") - - -/obj/item/device/pda/verb/verb_remove_pen() - set category = "Object" - set name = "Remove pen" - set src in usr - - if(issilicon(usr)) - return - - if ( can_use(usr) ) - remove_pen() - else - to_chat(usr, "You cannot do this while restrained.") - -/obj/item/device/pda/verb/verb_remove_cartridge() - set category = "Object" - set name = "Remove cartridge" - set src in usr - - if(issilicon(usr)) - return - - if(!can_use(usr)) - to_chat(usr, "You cannot do this while restrained.") - return - - if(isnull(cartridge)) - to_chat(usr, "There's no cartridge to eject.") - return - - cartridge.forceMove(get_turf(src)) - if(ismob(loc)) - var/mob/M = loc - M.put_in_hands(cartridge) - mode = 0 - scanmode = 0 - if (cartridge.radio) - cartridge.radio.hostpda = null - to_chat(usr, "You remove \the [cartridge] from the [name].") - playsound(src, 'sound/machines/id_swipe.ogg', 100, 1) - cartridge = null - -/obj/item/device/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use. - if(choice == 1) - if (id) - remove_id() - return 1 - else - var/obj/item/I = user.get_active_hand() - if (istype(I, /obj/item/weapon/card/id) && user.unEquip(I)) - I.loc = src - id = I - return 1 - else - var/obj/item/weapon/card/I = user.get_active_hand() - if (istype(I, /obj/item/weapon/card/id) && I:registered_name && user.unEquip(I)) - var/obj/old_id = id - I.loc = src - id = I - user.put_in_hands(old_id) - return 1 - return 0 - -// access to status display signals -/obj/item/device/pda/attackby(obj/item/C as obj, mob/user as mob) - ..() - if(istype(C, /obj/item/weapon/cartridge) && !cartridge) - cartridge = C - user.drop_item() - cartridge.loc = src - to_chat(usr, "You insert [cartridge] into [src].") - SSnanoui.update_uis(src) // update all UIs attached to src - if(cartridge.radio) - cartridge.radio.hostpda = src - - else if(istype(C, /obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/idcard = C - if(!idcard.registered_name) - to_chat(user, "\The [src] rejects the ID.") - return - if(!owner) - owner = idcard.registered_name - ownjob = idcard.assignment - ownrank = idcard.rank - name = "PDA-[owner] ([ownjob])" - to_chat(user, "Card scanned.") - else - //Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand. - if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) ) - if(id_check(user, 2)) - to_chat(user, "You put the ID into \the [src]'s slot.") - updateSelfDialog()//Update self dialog on success. - return //Return in case of failed check or when successful. - updateSelfDialog()//For the non-input related code. - else if(istype(C, /obj/item/device/paicard) && !src.pai) - user.drop_item() - C.loc = src - pai = C - to_chat(user, "You slot \the [C] into \the [src].") - SSnanoui.update_uis(src) // update all UIs attached to src - else if(istype(C, /obj/item/weapon/pen)) - var/obj/item/weapon/pen/O = locate() in src - if(O) - to_chat(user, "There is already a pen in \the [src].") - else - user.drop_item() - C.loc = src - to_chat(user, "You slot \the [C] into \the [src].") - return - -/obj/item/device/pda/attack(mob/living/C as mob, mob/living/user as mob) - if (istype(C, /mob/living/carbon)) - switch(scanmode) - if(1) - - for (var/mob/O in viewers(C, null)) - O.show_message("\The [user] has analyzed [C]'s vitals!", 1) - - user.show_message("Analyzing Results for [C]:") - user.show_message(" Overall Status: [C.stat > 1 ? "dead" : "[C.health - C.halloss]% healthy"]", 1) - user.show_message(text(" Damage Specifics: []-[]-[]-[]", - (C.getOxyLoss() > 50) ? "warning" : "", C.getOxyLoss(), - (C.getToxLoss() > 50) ? "warning" : "", C.getToxLoss(), - (C.getFireLoss() > 50) ? "warning" : "", C.getFireLoss(), - (C.getBruteLoss() > 50) ? "warning" : "", C.getBruteLoss() - ), 1) - user.show_message(" Key: Suffocation/Toxin/Burns/Brute", 1) - user.show_message(" Body Temperature: [C.bodytemperature-T0C]°C ([C.bodytemperature*1.8-459.67]°F)", 1) - if(C.tod && (C.stat == DEAD || (C.status_flags & FAKEDEATH))) - user.show_message(" Time of Death: [C.tod]") - if(istype(C, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = C - var/list/damaged = H.get_damaged_organs(1,1) - user.show_message("Localized Damage, Brute/Burn:",1) - if(length(damaged)>0) - for(var/obj/item/organ/external/org in damaged) - user.show_message(text(" []: []-[]", - capitalize(org.name), (org.brute_dam > 0) ? "warning" : "notice", org.brute_dam, (org.burn_dam > 0) ? "warning" : "notice", org.burn_dam),1) - else - user.show_message(" Limbs are OK.",1) - - if(2) - if (!istype(C:dna, /datum/dna)) - to_chat(user, "No fingerprints found on [C]") - else - to_chat(user, text("\The [C]'s Fingerprints: [md5(C:dna.uni_identity)]")) - if ( !(C:blood_DNA) ) - to_chat(user, "No blood found on [C]") - if(C:blood_DNA) - qdel(C:blood_DNA) - else - to_chat(user, "Blood found on [C]. Analysing...") - spawn(15) - for(var/blood in C:blood_DNA) - to_chat(user, "Blood type: [C:blood_DNA[blood]]\nDNA: [blood]") - - if(4) - user.visible_message("\The [user] has analyzed [C]'s radiation levels!", "You have analyzed [C]'s radiation levels!") - to_chat(user, "Analyzing Results for [C]:") - if(C.radiation) - to_chat(user, "Radiation Level: [C.radiation]") - else - to_chat(user, "No radiation detected.") - -/obj/item/device/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) - if(!proximity) return - switch(scanmode) - - if(3) - if(!isobj(A)) - return - if(!isnull(A.reagents)) - if(A.reagents.reagent_list.len > 0) - var/reagents_length = A.reagents.reagent_list.len - to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.") - for (var/re in A.reagents.reagent_list) - to_chat(user, " [re]") - else - to_chat(user, "No active chemical agents found in [A].") - else - to_chat(user, "No significantchemical agents found in [A].") - - if(5) - analyze_gases(A, user) - - if (!scanmode && istype(A, /obj/item/weapon/paper) && owner) - // JMO 20140705: Makes scanned document show up properly in the notes. Not pretty for formatted documents, - // as this will clobber the HTML, but at least it lets you scan a document. You can restore the original - // notes by editing the note again. (Was going to allow you to edit, but scanned documents are too long.) - var/raw_scan = (A:info) - var/formatted_scan = "" - // Scrub out the tags (replacing a few formatting ones along the way) - - // Find the beginning and end of the first tag. - var/tag_start = findtext(raw_scan,"<") - var/tag_stop = findtext(raw_scan,">") - - // Until we run out of complete tags... - while(tag_start&&tag_stop) - var/pre = copytext(raw_scan,1,tag_start) // Get the stuff that comes before the tag - var/tag = lowertext(copytext(raw_scan,tag_start+1,tag_stop)) // Get the tag so we can do intellegent replacement - var/tagend = findtext(tag," ") // Find the first space in the tag if there is one. - - // Anything that's before the tag can just be added as is. - formatted_scan = formatted_scan+pre - - // If we have a space after the tag (and presumably attributes) just crop that off. - if (tagend) - tag=copytext(tag,1,tagend) - - if (tag=="p"||tag=="/p"||tag=="br") // Check if it's I vertical space tag. - formatted_scan=formatted_scan+"
" // If so, add some padding in. - - raw_scan = copytext(raw_scan,tag_stop+1) // continue on with the stuff after the tag - - // Look for the next tag in what's left - tag_start = findtext(raw_scan,"<") - tag_stop = findtext(raw_scan,">") - - // Anything that is left in the page. just tack it on to the end as is - formatted_scan=formatted_scan+raw_scan - - // If there is something in there already, pad it out. - if (length(note)>0) - note = note + "

" - - // Store the scanned document to the notes - note = "Scanned Document. Edit to restore previous notes/delete scan.
----------
" + formatted_scan + "
" - // notehtml ISN'T set to allow user to get their old notes back. A better implementation would add a "scanned documents" - // feature to the PDA, which would better convey the availability of the feature, but this will work for now. - - // Inform the user - to_chat(user, "Paper scanned and OCRed to notekeeper.") //concept of scanning paper copyright brainoblivion 2009 - - -/obj/item/device/pda/proc/explode() //This needs tuning. //Sure did. - if(!src.detonate) return - var/turf/T = get_turf(src.loc) - if(T) - T.hotspot_expose(700,125) - explosion(T, 0, 0, 1, rand(1,2)) - return - -/obj/item/device/pda/Destroy() - PDAs -= src - if (src.id && prob(100) && !delete_id) //IDs are kept in 90% of the cases //VOREStation Edit - 100% of the cases, excpet when specified otherwise - src.id.forceMove(get_turf(src.loc)) - else - QDEL_NULL(src.id) - QDEL_NULL(src.cartridge) - QDEL_NULL(src.pai) - return ..() - -/obj/item/device/pda/clown/Crossed(atom/movable/AM as mob|obj) //Clown PDA is slippery. - if(AM.is_incorporeal()) - return - if (istype(AM, /mob/living)) - var/mob/living/M = AM - - if(M.slip("the PDA",8) && M.real_name != src.owner && istype(src.cartridge, /obj/item/weapon/cartridge/clown)) - if(src.cartridge.charges < 5) - src.cartridge.charges++ - -/obj/item/device/pda/proc/available_pdas() - var/list/names = list() - var/list/plist = list() - var/list/namecounts = list() - - if (toff) - to_chat(usr, "Turn on your receiver in order to send messages.") - return - - for (var/obj/item/device/pda/P in PDAs) - if (!P.owner) - continue - else if(P.hidden) - continue - else if (P == src) - continue - else if (P.toff) - continue - - var/name = P.owner - if (name in names) - namecounts[name]++ - name = text("[name] ([namecounts[name]])") - else - names.Add(name) - namecounts[name] = 1 - - plist[text("[name]")] = P - return plist - - -//Some spare PDAs in a box -/obj/item/weapon/storage/box/PDAs - name = "box of spare PDAs" - desc = "A box of spare PDA microcomputers." - icon = 'icons/obj/pda.dmi' - icon_state = "pdabox" - - New() - ..() - new /obj/item/device/pda(src) - new /obj/item/device/pda(src) - new /obj/item/device/pda(src) - new /obj/item/device/pda(src) - new /obj/item/weapon/cartridge/head(src) - - var/newcart = pick( /obj/item/weapon/cartridge/engineering, - /obj/item/weapon/cartridge/security, - /obj/item/weapon/cartridge/medical, - /obj/item/weapon/cartridge/signal/science, - /obj/item/weapon/cartridge/quartermaster) - new newcart(src) - -// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP -/obj/item/device/pda/emp_act(severity) - for(var/atom/A in src) - A.emp_act(severity) - -/obj/item/device/pda/proc/analyze_air() - var/list/results = list() - var/turf/T = get_turf(src.loc) - if(!isnull(T)) - var/datum/gas_mixture/environment = T.return_air() - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles - if (total_moles) - var/o2_level = environment.gas["oxygen"]/total_moles - var/n2_level = environment.gas["nitrogen"]/total_moles - var/co2_level = environment.gas["carbon_dioxide"]/total_moles - var/phoron_level = environment.gas["phoron"]/total_moles - var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) - - // entry is what the element is describing - // Type identifies which unit or other special characters to use - // Val is the information reported - // Bad_high/_low are the values outside of which the entry reports as dangerous - // Poor_high/_low are the values outside of which the entry reports as unideal - // Values were extracted from the template itself - results = list( - list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), - list("entry" = "Temperature", "units" = "°C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), - list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), - list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), - list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), - list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), - list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) - ) - - if(isnull(results)) - results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) - return results diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm deleted file mode 100644 index 5a404288e3..0000000000 --- a/code/game/objects/items/devices/PDA/cart.dm +++ /dev/null @@ -1,630 +0,0 @@ -var/list/command_cartridges = list( - /obj/item/weapon/cartridge/captain, - /obj/item/weapon/cartridge/hop, - /obj/item/weapon/cartridge/hos, - /obj/item/weapon/cartridge/ce, - /obj/item/weapon/cartridge/rd, - /obj/item/weapon/cartridge/cmo, - /obj/item/weapon/cartridge/head, - /obj/item/weapon/cartridge/lawyer // Internal Affaris, - ) - -var/list/security_cartridges = list( - /obj/item/weapon/cartridge/security, - /obj/item/weapon/cartridge/detective, - /obj/item/weapon/cartridge/hos - ) - -var/list/engineering_cartridges = list( - /obj/item/weapon/cartridge/engineering, - /obj/item/weapon/cartridge/atmos, - /obj/item/weapon/cartridge/ce - ) - -var/list/medical_cartridges = list( - /obj/item/weapon/cartridge/medical, - /obj/item/weapon/cartridge/chemistry, - /obj/item/weapon/cartridge/cmo - ) - -var/list/research_cartridges = list( - /obj/item/weapon/cartridge/signal/science, - /obj/item/weapon/cartridge/rd - ) - -var/list/cargo_cartridges = list( - /obj/item/weapon/cartridge/quartermaster, // This also covers cargo-techs, apparently, - /obj/item/weapon/cartridge/miner, - /obj/item/weapon/cartridge/hop - ) - -var/list/civilian_cartridges = list( - /obj/item/weapon/cartridge/janitor, - /obj/item/weapon/cartridge/service, - /obj/item/weapon/cartridge/hop - ) - -/obj/item/weapon/cartridge - name = "generic cartridge" - desc = "A data cartridge for portable microcomputers." - icon = 'icons/obj/pda.dmi' - icon_state = "cart" - item_state = "electronic" - w_class = ITEMSIZE_TINY - drop_sound = 'sound/items/drop/component.ogg' - pickup_sound = 'sound/items/pickup/component.ogg' - - var/obj/item/radio/integrated/radio = null - var/access_security = 0 - var/access_engine = 0 - var/access_atmos = 0 - var/access_medical = 0 - var/access_clown = 0 - var/access_mime = 0 - var/access_janitor = 0 -// var/access_flora = 0 - var/access_reagent_scanner = 0 - var/access_remote_door = 0 // Control some blast doors remotely!! - var/remote_door_id = "" - var/access_status_display = 0 - var/access_quartermaster = 0 - var/access_detonate_pda = 0 - var/access_hydroponics = 0 - var/charges = 0 - var/mode = null - var/menu - var/datum/data/record/active1 = null //General - var/datum/data/record/active2 = null //Medical - var/datum/data/record/active3 = null //Security - var/selected_sensor = null // Power Sensor - var/message1 // used for status_displays - var/message2 - var/list/stored_data = list() - -/obj/item/weapon/cartridge/Destroy() - QDEL_NULL(radio) - return ..() - -/obj/item/weapon/cartridge/engineering - name = "\improper Power-ON cartridge" - icon_state = "cart-e" - access_engine = 1 - -/obj/item/weapon/cartridge/atmos - name = "\improper BreatheDeep cartridge" - icon_state = "cart-a" - access_atmos = 1 - -/obj/item/weapon/cartridge/medical - name = "\improper Med-U cartridge" - icon_state = "cart-m" - access_medical = 1 - -/obj/item/weapon/cartridge/chemistry - name = "\improper ChemWhiz cartridge" - icon_state = "cart-chem" - access_reagent_scanner = 1 - access_medical = 1 - -/obj/item/weapon/cartridge/security - name = "\improper R.O.B.U.S.T. cartridge" - icon_state = "cart-s" - access_security = 1 - -/obj/item/weapon/cartridge/security/Initialize() - radio = new /obj/item/radio/integrated/beepsky(src) - . = ..() - -/obj/item/weapon/cartridge/detective - name = "\improper D.E.T.E.C.T. cartridge" - icon_state = "cart-s" - access_security = 1 - access_medical = 1 - - -/obj/item/weapon/cartridge/janitor - name = "\improper CustodiPRO cartridge" - desc = "The ultimate in clean-room design." - icon_state = "cart-j" - access_janitor = 1 - -/obj/item/weapon/cartridge/lawyer - name = "\improper P.R.O.V.E. cartridge" - icon_state = "cart-s" - access_security = 1 - -/obj/item/weapon/cartridge/clown - name = "\improper Honkworks 5.0 cartridge" - icon_state = "cart-clown" - access_clown = 1 - charges = 5 - -/obj/item/weapon/cartridge/mime - name = "\improper Gestur-O 1000 cartridge" - icon_state = "cart-mi" - access_mime = 1 - charges = 5 -/* -/obj/item/weapon/cartridge/botanist - name = "Green Thumb v4.20" - icon_state = "cart-b" - access_flora = 1 -*/ - -/obj/item/weapon/cartridge/service - name = "\improper Serv-U Pro cartridge" - desc = "A data cartridge designed to serve YOU!" - -/obj/item/weapon/cartridge/signal - name = "generic signaler cartridge" - desc = "A data cartridge with an integrated radio signaler module." - var/qdeled = 0 - -/obj/item/weapon/cartridge/signal/science - name = "\improper Signal Ace 2 cartridge" - desc = "Complete with integrated radio signaler!" - icon_state = "cart-tox" - access_reagent_scanner = 1 - access_atmos = 1 - -/obj/item/weapon/cartridge/signal/Initialize() - radio = new /obj/item/radio/integrated/signal(src) - . = ..() - -/obj/item/weapon/cartridge/quartermaster - name = "\improper Space Parts & Space Vendors cartridge" - desc = "Perfect for the Quartermaster on the go!" - icon_state = "cart-q" - access_quartermaster = 1 - -/obj/item/weapon/cartridge/miner - name = "\improper Drill-Jockey 4.5 cartridge" - desc = "It's covered in some sort of sand." - icon_state = "cart-q" - -/obj/item/weapon/cartridge/head - name = "\improper Easy-Record DELUXE cartridge" - icon_state = "cart-h" - access_status_display = 1 - -/obj/item/weapon/cartridge/hop - name = "\improper HumanResources9001 cartridge" - icon_state = "cart-h" - access_status_display = 1 - access_quartermaster = 1 - access_janitor = 1 - access_security = 1 - -/obj/item/weapon/cartridge/hos - name = "\improper R.O.B.U.S.T. DELUXE cartridge" - icon_state = "cart-hos" - access_status_display = 1 - access_security = 1 - -/obj/item/weapon/cartridge/hos/Initialize() - radio = new /obj/item/radio/integrated/beepsky(src) - . = ..() - -/obj/item/weapon/cartridge/ce - name = "\improper Power-On DELUXE cartridge" - icon_state = "cart-ce" - access_status_display = 1 - access_engine = 1 - access_atmos = 1 - -/obj/item/weapon/cartridge/cmo - name = "\improper Med-U DELUXE cartridge" - icon_state = "cart-cmo" - access_status_display = 1 - access_reagent_scanner = 1 - access_medical = 1 - -/obj/item/weapon/cartridge/rd - name = "\improper Signal Ace DELUXE cartridge" - icon_state = "cart-rd" - access_status_display = 1 - access_reagent_scanner = 1 - access_atmos = 1 - -/obj/item/weapon/cartridge/rd/Initialize() - radio = new /obj/item/radio/integrated/signal(src) - . = ..() - -/obj/item/weapon/cartridge/captain - name = "\improper Value-PAK cartridge" - desc = "Now with 200% more value!" - icon_state = "cart-c" - access_quartermaster = 1 - access_janitor = 1 - access_engine = 1 - access_security = 1 - access_medical = 1 - access_reagent_scanner = 1 - access_status_display = 1 - access_atmos = 1 - -/obj/item/weapon/cartridge/syndicate - name = "\improper Detomatix cartridge" - icon_state = "cart" - access_remote_door = 1 - access_detonate_pda = 1 - remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. - charges = 4 - -/obj/item/weapon/cartridge/proc/post_status(var/command, var/data1, var/data2) - - var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) - if(!frequency) return - - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = TRANSMISSION_RADIO - status_signal.data["command"] = command - - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - if(loc) - var/obj/item/PDA = loc - var/mob/user = PDA.fingerprintslast - log_admin("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") - message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") - - if("alert") - status_signal.data["picture_state"] = data1 - - frequency.post_signal(src, status_signal) - - -/* - This generates the nano values of the cart menus. - Because we close the UI when we insert a new cart - we don't have to worry about null values on items - the user can't access. Well, unless they are href hacking. - But in that case their UI will just lock up. -*/ - - -/obj/item/weapon/cartridge/proc/create_NanoUI_values(mob/user as mob) - var/values[0] - - /* Signaler (Mode: 40) */ - - - if(istype(radio,/obj/item/radio/integrated/signal) && (mode==40)) - var/obj/item/radio/integrated/signal/R = radio - values["signal_freq"] = format_frequency(R.frequency) - values["signal_code"] = R.code - - - /* Station Display (Mode: 42) */ - - if(mode==42) - values["message1"] = message1 ? message1 : "(none)" - values["message2"] = message2 ? message2 : "(none)" - - - - /* Power Monitor (Mode: 43 / 433) */ - - if(mode==43 || mode==433) - var/list/sensors = list() - var/obj/machinery/power/sensor/MS = null - var/my_z = get_z(user) - var/list/levels = using_map.get_map_levels(my_z) - - for(var/obj/machinery/power/sensor/S in machines) - if(!(get_z(S) in levels)) - continue - sensors.Add(list(list("name_tag" = S.name_tag))) - if(S.name_tag == selected_sensor) - MS = S - values["power_sensors"] = sensors - if(selected_sensor && MS) - values["sensor_reading"] = MS.return_reading_data() - - - /* General Records (Mode: 44 / 441 / 45 / 451) */ - if(mode == 44 || mode == 441 || mode == 45 || mode ==451) - if(istype(active1, /datum/data/record) && (active1 in data_core.general)) - values["general"] = active1.fields - values["general_exists"] = 1 - - else - values["general_exists"] = 0 - - - - /* Medical Records (Mode: 44 / 441) */ - - if(mode == 44 || mode == 441) - var/medData[0] - for(var/datum/data/record/R in sortRecord(data_core.general)) - medData[++medData.len] = list(Name = R.fields["name"],"ref" = "\ref[R]") - values["medical_records"] = medData - - if(istype(active2, /datum/data/record) && (active2 in data_core.medical)) - values["medical"] = active2.fields - values["medical_exists"] = 1 - else - values["medical_exists"] = 0 - - /* Security Records (Mode:45 / 451) */ - - if(mode == 45 || mode == 451) - var/secData[0] - for (var/datum/data/record/R in sortRecord(data_core.general)) - secData[++secData.len] = list(Name = R.fields["name"], "ref" = "\ref[R]") - values["security_records"] = secData - - if(istype(active3, /datum/data/record) && (active3 in data_core.security)) - values["security"] = active3.fields - values["security_exists"] = 1 - else - values["security_exists"] = 0 - - /* Security Bot Control (Mode: 46) */ - - if(mode==46) - var/botsData[0] - var/beepskyData[0] - if(istype(radio,/obj/item/radio/integrated/beepsky)) - var/obj/item/radio/integrated/beepsky/SC = radio - beepskyData["active"] = SC.active - if(SC.active && !isnull(SC.botstatus)) - var/area/loca = SC.botstatus["loca"] - var/loca_name = sanitize(loca.name) - beepskyData["botstatus"] = list("loca" = loca_name, "mode" = SC.botstatus["mode"]) - else - beepskyData["botstatus"] = list("loca" = null, "mode" = -1) - var/botsCount=0 - if(SC.botlist && SC.botlist.len) - for(var/mob/living/bot/B in SC.botlist) - botsCount++ - if(B.loc) - botsData[++botsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]") - - if(!botsData.len) - botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) - - beepskyData["bots"] = botsData - beepskyData["count"] = botsCount - - else - beepskyData["active"] = 0 - botsData[++botsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null) - beepskyData["botstatus"] = list("loca" = null, "mode" = null) - beepskyData["bots"] = botsData - beepskyData["count"] = 0 - - values["beepsky"] = beepskyData - - - /* MULEBOT Control (Mode: 48) */ - - if(mode==48) - var/mulebotsData[0] - var/count = 0 - - for(var/mob/living/bot/mulebot/M in living_mob_list) - if(!M.on) - continue - ++count - var/muleData[0] - muleData["name"] = M.suffix - muleData["location"] = get_area(M) - muleData["paused"] = M.paused - muleData["home"] = M.homeName - muleData["target"] = M.targetName - muleData["ref"] = "\ref[M]" - muleData["load"] = M.load ? M.load.name : "Nothing" - - mulebotsData[++mulebotsData.len] = muleData.Copy() - - values["mulebotcount"] = count - values["mulebots"] = mulebotsData - - - - /* Supply Shuttle Requests Menu (Mode: 47) */ - - if(mode==47) - var/supplyData[0] - var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle - if (shuttle) - supplyData["shuttle_moving"] = shuttle.has_arrive_time() - supplyData["shuttle_eta"] = shuttle.eta_minutes() - supplyData["shuttle_loc"] = shuttle.at_station() ? "Station" : "Dock" - var/supplyOrderCount = 0 - var/supplyOrderData[0] - for(var/S in SSsupply.shoppinglist) - var/datum/supply_order/SO = S - - supplyOrderData[++supplyOrderData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "ApprovedBy" = SO.ordered_by, "Comment" = html_encode(SO.comment)) - if(!supplyOrderData.len) - supplyOrderData[++supplyOrderData.len] = list("Number" = null, "Name" = null, "OrderedBy"=null) - - supplyData["approved"] = supplyOrderData - supplyData["approved_count"] = supplyOrderCount - - var/requestCount = 0 - var/requestData[0] - for(var/S in SSsupply.order_history) - var/datum/supply_order/SO = S - if(SO.status != SUP_ORDER_REQUESTED) - continue - - requestCount++ - requestData[++requestData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "OrderedBy" = SO.ordered_by, "Comment" = html_encode(SO.comment)) - if(!requestData.len) - requestData[++requestData.len] = list("Number" = null, "Name" = null, "orderedBy" = null, "Comment" = null) - - supplyData["requests"] = requestData - supplyData["requests_count"] = requestCount - - - values["supply"] = supplyData - - - - /* Janitor Supplies Locator (Mode: 49) */ - if(mode==49) - var/JaniData[0] - var/turf/cl = get_turf(src) - - if(cl) - JaniData["user_loc"] = list("x" = cl.x, "y" = cl.y) - else - JaniData["user_loc"] = list("x" = 0, "y" = 0) - var/MopData[0] - for(var/obj/item/weapon/mop/M in all_mops) - var/turf/ml = get_turf(M) - if(ml) - if(ml.z != cl.z) - continue - var/direction = get_dir(src, M) - MopData[++MopData.len] = list ("x" = ml.x, "y" = ml.y, "dir" = uppertext(dir2text(direction)), "status" = M.reagents.total_volume ? "Wet" : "Dry") - - if(!MopData.len) - MopData[++MopData.len] = list("x" = 0, "y" = 0, dir=null, status = null) - - - var/BucketData[0] - for(var/obj/structure/mopbucket/B in all_mopbuckets) - var/turf/bl = get_turf(B) - if(bl) - if(bl.z != cl.z) - continue - var/direction = get_dir(src,B) - BucketData[++BucketData.len] = list ("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.reagents.total_volume/100) - - if(!BucketData.len) - BucketData[++BucketData.len] = list("x" = 0, "y" = 0, dir=null, status = null) - - var/CbotData[0] - for(var/mob/living/bot/cleanbot/B in mob_list) - var/turf/bl = get_turf(B) - if(bl) - if(bl.z != cl.z) - continue - var/direction = get_dir(src,B) - CbotData[++CbotData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.on ? "Online" : "Offline") - - - if(!CbotData.len) - CbotData[++CbotData.len] = list("x" = 0, "y" = 0, dir=null, status = null) - var/CartData[0] - for(var/obj/structure/janitorialcart/B in all_janitorial_carts) - var/turf/bl = get_turf(B) - if(bl) - if(bl.z != cl.z) - continue - var/direction = get_dir(src,B) - var/status = "No Bucket" - if(B.mybucket) - status = B.mybucket.reagents.total_volume / 100 - CartData[++CartData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = status) - if(!CartData.len) - CartData[++CartData.len] = list("x" = 0, "y" = 0, dir=null, status = null) - - - - - JaniData["mops"] = MopData - JaniData["buckets"] = BucketData - JaniData["cleanbots"] = CbotData - JaniData["carts"] = CartData - values["janitor"] = JaniData - - return values - - - - - -/obj/item/weapon/cartridge/Topic(href, href_list) - ..() - - if (!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr)) - usr.unset_machine() - usr << browse(null, "window=pda") - return - - - - - switch(href_list["choice"]) - if("Medical Records") - var/datum/data/record/R = locate(href_list["target"]) - var/datum/data/record/M = locate(href_list["target"]) - loc:mode = 441 - mode = 441 - if (R in data_core.general) - for (var/datum/data/record/E in data_core.medical) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - M = E - break - active1 = R - active2 = M - - if("Security Records") - var/datum/data/record/R = locate(href_list["target"]) - var/datum/data/record/S = locate(href_list["target"]) - loc:mode = 451 - mode = 451 - if (R in data_core.general) - for (var/datum/data/record/E in data_core.security) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - S = E - break - active1 = R - active3 = S - - if("Send Signal") - if(is_jammed(src)) - return - spawn( 0 ) - radio:send_signal("ACTIVATE") - return - - if("Signal Frequency") - var/new_frequency = sanitize_frequency(radio:frequency + text2num(href_list["sfreq"])) - radio:set_frequency(new_frequency) - - if("Signal Code") - radio:code += text2num(href_list["scode"]) - radio:code = round(radio:code) - radio:code = min(100, radio:code) - radio:code = max(1, radio:code) - - if("Status") - switch(href_list["statdisp"]) - if("message") - post_status("message", message1, message2) - if("alert") - post_status("alert", href_list["alert"]) - if("setmsg1") - message1 = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", message1) as text|null, 40), 40) - updateSelfDialog() - if("setmsg2") - message2 = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", message2) as text|null, 40), 40) - updateSelfDialog() - else - post_status(href_list["statdisp"]) - - if("Power Select") - selected_sensor = href_list["target"] - loc:mode = 433 - mode = 433 - if("Power Clear") - selected_sensor = null - loc:mode = 43 - mode = 43 - - if("MULEbot") - var/mob/living/bot/mulebot/M = locate(href_list["ref"]) - if(istype(M)) - M.obeyCommand(href_list["command"]) - - return 1 diff --git a/code/game/objects/items/devices/PDA/cart_vr.dm b/code/game/objects/items/devices/PDA/cart_vr.dm deleted file mode 100644 index fc4e3d098a..0000000000 --- a/code/game/objects/items/devices/PDA/cart_vr.dm +++ /dev/null @@ -1,17 +0,0 @@ -var/list/exploration_cartridges = list( - /obj/item/weapon/cartridge/explorer, - /obj/item/weapon/cartridge/sar - ) - -/obj/item/weapon/cartridge/explorer - name = "\improper Explorator cartridge" - icon_state = "cart-e" - access_reagent_scanner = 1 - access_atmos = 1 - -/obj/item/weapon/cartridge/sar - name = "\improper Med-Exp cartridge" - icon_state = "cart-m" - access_medical = 1 - access_reagent_scanner = 1 - access_atmos = 1 diff --git a/code/game/objects/items/devices/PDA/chatroom.dm b/code/game/objects/items/devices/PDA/chatroom.dm deleted file mode 100644 index 008dab6414..0000000000 --- a/code/game/objects/items/devices/PDA/chatroom.dm +++ /dev/null @@ -1,16 +0,0 @@ -var/list/chatrooms = list() - -/datum/chatroom - var/name = "Generic Chatroom" - var/list/logged_in = list() - var/list/logs = list() // chat logs - var/list/banned = list() // banned users - var/list/whitelist = list() // whitelisted users - var/list/muted = list() - var/topic = "" // topic message for the chatroom - var/password = "" // blank for no password. - var/operator = "" // name of the operator - -/datum/chatroom/proc/attempt_connect(var/obj/item/device/pda/device, var/obj/password) - if(!device) - return diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm deleted file mode 100644 index 133798e4b1..0000000000 --- a/code/game/objects/items/devices/PDA/radio.dm +++ /dev/null @@ -1,153 +0,0 @@ -/obj/item/radio/integrated - name = "\improper PDA radio module" - desc = "An electronic radio system." - icon = 'icons/obj/module.dmi' - icon_state = "power_mod" - var/obj/item/device/pda/hostpda = null - - var/on = 0 //Are we currently active?? - var/menu_message = "" - - New() - ..() - if (istype(loc.loc, /obj/item/device/pda)) - hostpda = loc.loc - - proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3, s_filter) - - //to_world("Post: [freq]: [key]=[value], [key2]=[value2]") - var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) - - if(!frequency) return - - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = TRANSMISSION_RADIO - signal.data[key] = value - if(key2) - signal.data[key2] = value2 - if(key3) - signal.data[key3] = value3 - - frequency.post_signal(src, signal, radio_filter = s_filter) - - return - - proc/generate_menu() - -/obj/item/radio/integrated/beepsky - var/list/botlist = null // list of bots - var/mob/living/bot/secbot/active // the active bot; if null, show bot list - var/list/botstatus // the status signal sent by the bot - - var/control_freq = BOT_FREQ - - // create a new QM cartridge, and register to receive bot control & beacon message - New() - ..() - spawn(5) - if(radio_controller) - radio_controller.add_object(src, control_freq, radio_filter = RADIO_SECBOT) - - // receive radio signals - // can detect bot status signals - // create/populate list as they are recvd - - receive_signal(datum/signal/signal) -// var/obj/item/device/pda/P = src.loc - - /* - to_world("recvd:[P] : [signal.source]") - for(var/d in signal.data) - to_world("- [d] = [signal.data[d]]") - */ - if (signal.data["type"] == "secbot") - if(!botlist) - botlist = new() - - if(!(signal.source in botlist)) - botlist += signal.source - - if(active == signal.source) - var/list/b = signal.data - botstatus = b.Copy() - -// if (istype(P)) P.updateSelfDialog() - - Topic(href, href_list) - ..() - var/obj/item/device/pda/PDA = src.hostpda - - switch(href_list["op"]) - - if("control") - active = locate(href_list["bot"]) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_SECBOT) - - if("scanbots") // find all bots - botlist = null - post_signal(control_freq, "command", "bot_status", s_filter = RADIO_SECBOT) - - if("botlist") - active = null - - if("stop", "go") - post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = RADIO_SECBOT) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_SECBOT) - - if("summon") - post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(PDA) , s_filter = RADIO_SECBOT) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_SECBOT) - - -/obj/item/radio/integrated/beepsky/Destroy() - if(radio_controller) - radio_controller.remove_object(src, control_freq) - return ..() - -/* - * Radio Cartridge, essentially a signaler. - */ - - -/obj/item/radio/integrated/signal - var/frequency = 1457 - var/code = 30.0 - var/last_transmission - var/datum/radio_frequency/radio_connection - -/obj/item/radio/integrated/signal/Initialize() - if(!radio_controller) - return - - if (src.frequency < PUBLIC_LOW_FREQ || src.frequency > PUBLIC_HIGH_FREQ) - src.frequency = sanitize_frequency(src.frequency) - - set_frequency(frequency) - -/obj/item/radio/integrated/signal/proc/set_frequency(new_frequency) - radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency) - -/obj/item/radio/integrated/signal/proc/send_signal(message="ACTIVATE") - - if(last_transmission && world.time < (last_transmission + 5)) - return - last_transmission = world.time - - var/time = time2text(world.realtime,"hh:mm:ss") - var/turf/T = get_turf(src) - lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") - - var/datum/signal/signal = new - signal.source = src - signal.encryption = code - signal.data["message"] = message - - radio_connection.post_signal(src, signal) - -/obj/item/radio/integrated/signal/Destroy() - if(radio_controller) - radio_controller.remove_object(src, frequency) - return ..() diff --git a/code/game/objects/items/devices/communicator/UI.dm b/code/game/objects/items/devices/communicator/UI.dm deleted file mode 100644 index a4686e39de..0000000000 --- a/code/game/objects/items/devices/communicator/UI.dm +++ /dev/null @@ -1,292 +0,0 @@ -// Proc: ui_interact() -// Parameters: 4 (standard NanoUI arguments) -// Description: Uses a bunch of for loops to turn lists into lists of lists, so they can be displayed in nanoUI, then displays various buttons to the user. -/obj/item/device/communicator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/key_state = null) - // this is the data which will be sent to the ui - var/data[0] //General nanoUI information - var/communicators[0] //List of communicators - var/invites[0] //Communicators and ghosts we've invited to our communicator. - var/requests[0] //Communicators and ghosts wanting to go in our communicator. - var/voices[0] //Current /mob/living/voice s inside the device. - var/connected_communicators[0] //Current communicators connected to the device. - - var/im_contacts_ui[0] //List of communicators that have been messaged. - var/im_list_ui[0] //List of messages. - - var/weather[0] - var/modules_ui[0] //Home screen info. - - //First we add other 'local' communicators. - for(var/obj/item/device/communicator/comm in known_devices) - if(comm.network_visibility && comm.exonet) - communicators[++communicators.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address) - - //Now for ghosts who we pretend have communicators. - for(var/mob/observer/dead/O in known_devices) - if(O.client && O.client.prefs.communicator_visibility == 1 && O.exonet) - communicators[++communicators.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") - - //Lists all the other communicators that we invited. - for(var/obj/item/device/communicator/comm in voice_invites) - if(comm.exonet) - invites[++invites.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") - - //Ghosts we invited. - for(var/mob/observer/dead/O in voice_invites) - if(O.exonet && O.client) - invites[++invites.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") - - //Communicators that want to talk to us. - for(var/obj/item/device/communicator/comm in voice_requests) - if(comm.exonet) - requests[++requests.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") - - //Ghosts that want to talk to us. - for(var/mob/observer/dead/O in voice_requests) - if(O.exonet && O.client) - requests[++requests.len] = list("name" = sanitize("[O.client.prefs.real_name]'s communicator"), "address" = O.exonet.address, "ref" = "\ref[O]") - - //Now for all the voice mobs inside the communicator. - for(var/mob/living/voice/voice in contents) - voices[++voices.len] = list("name" = sanitize("[voice.name]'s communicator"), "true_name" = sanitize(voice.name)) - - //Finally, all the communicators linked to this one. - for(var/obj/item/device/communicator/comm in communicating) - connected_communicators[++connected_communicators.len] = list("name" = sanitize(comm.name), "true_name" = sanitize(comm.name), "ref" = "\ref[comm]") - - //Devices that have been messaged or recieved messages from. - for(var/obj/item/device/communicator/comm in im_contacts) - if(comm.exonet) - im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(comm.name), "address" = comm.exonet.address, "ref" = "\ref[comm]") - - for(var/mob/observer/dead/ghost in im_contacts) - if(ghost.exonet) - im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(ghost.name), "address" = ghost.exonet.address, "ref" = "\ref[ghost]") - - for(var/obj/item/integrated_circuit/input/EPv2/CIRC in im_contacts) - if(CIRC.exonet && CIRC.assembly) - im_contacts_ui[++im_contacts_ui.len] = list("name" = sanitize(CIRC.assembly.name), "address" = CIRC.exonet.address, "ref" = "\ref[CIRC]") - - - //Actual messages. - for(var/I in im_list) - im_list_ui[++im_list_ui.len] = list("address" = I["address"], "to_address" = I["to_address"], "im" = I["im"]) - - //Weather reports. - for(var/datum/planet/planet in SSplanets.planets) - if(planet.weather_holder && planet.weather_holder.current_weather) - var/list/W = list( - "Planet" = planet.name, - "Time" = planet.current_time.show_time("hh:mm"), - "Weather" = planet.weather_holder.current_weather.name, - "Temperature" = planet.weather_holder.temperature - T0C, - "High" = planet.weather_holder.current_weather.temp_high - T0C, - "Low" = planet.weather_holder.current_weather.temp_low - T0C, - "WindDir" = planet.weather_holder.wind_dir ? dir2text(planet.weather_holder.wind_dir) : "None", - "WindSpeed" = planet.weather_holder.wind_speed ? "[planet.weather_holder.wind_speed > 2 ? "Severe" : "Normal"]" : "None", - "Forecast" = english_list(planet.weather_holder.forecast, and_text = "→", comma_text = "→", final_comma_text = "→") // Unicode RIGHTWARDS ARROW. - ) - weather[++weather.len] = W - - // Update manifest - data_core.get_manifest_list() - - //Modules for homescreen. - for(var/list/R in modules) - modules_ui[++modules_ui.len] = R - - data["user"] = "\ref[user]" // For receiving input() via topic, because input(usr,...) wasn't working on cartridges - data["owner"] = owner ? owner : "Unset" - data["occupation"] = occupation ? occupation : "Swipe ID to set." - data["connectionStatus"] = get_connection_to_tcomms() - data["visible"] = network_visibility - data["address"] = exonet.address ? exonet.address : "Unallocated" - data["targetAddress"] = target_address - data["targetAddressName"] = target_address_name - data["currentTab"] = selected_tab - data["knownDevices"] = communicators - data["invitesSent"] = invites - data["requestsReceived"] = requests - data["voice_mobs"] = voices - data["communicating"] = connected_communicators - data["video_comm"] = video_source ? "\ref[video_source.loc]" : null - data["imContacts"] = im_contacts_ui - data["imList"] = im_list_ui - data["time"] = stationtime2text() - data["ring"] = ringer - data["homeScreen"] = modules_ui - data["note"] = note // current notes - data["weather"] = weather - data["aircontents"] = src.analyze_air() - data["flashlight"] = fon - data["manifest"] = PDA_Manifest - data["feeds"] = compile_news() - data["latest_news"] = get_recent_news() - if(newsfeed_channel) - data["target_feed"] = data["feeds"][newsfeed_channel] - if(cartridge) // If there's a cartridge, we need to grab the information from it - data["cart_devices"] = cartridge.get_device_status() - data["cart_templates"] = cartridge.ui_templates - for(var/list/L in cartridge.get_data()) - data[L["field"]] = L["value"] - // cartridge.get_data() returns a list of tuples: - // The field element is the tag used to access the information by the template - // The value element is the actual data, and can take any form necessary for the template - - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - data["currentTab"] = 1 // Reset the current tab, because we're going to home page - ui = new(user, src, ui_key, "communicator_header.tmpl", "Communicator", 475, 700, state = key_state) - // add templates for screens in common with communicator. - ui.add_template("atmosphericScan", "atmospheric_scan.tmpl") - ui.add_template("crewManifest", "crew_manifest.tmpl") - ui.add_template("Body", "communicator.tmpl") // Main body - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every five Master Controller tick - ui.set_auto_update(5) - -// Proc: Topic() -// Parameters: 2 (standard Topic arguments) -// Description: Responds to NanoUI button presses. -/obj/item/device/communicator/Topic(href, href_list) - if(..()) - return 1 - if(href_list["rename"]) - var/new_name = sanitizeSafe(input(usr,"Please enter your name.","Communicator",usr.name) ) - if(new_name) - register_device(new_name) - - if(href_list["toggle_visibility"]) - switch(network_visibility) - if(1) //Visible, becoming invisbile - network_visibility = 0 - if(camera) - camera.remove_network(NETWORK_COMMUNICATORS) - if(0) //Invisible, becoming visible - network_visibility = 1 - if(camera) - camera.add_network(NETWORK_COMMUNICATORS) - - if(href_list["toggle_ringer"]) - ringer = !ringer - - if(href_list["add_hex"]) - var/hex = href_list["add_hex"] - add_to_EPv2(hex) - - if(href_list["write_target_address"]) - var/new_address = sanitizeSafe(input(usr,"Please enter the desired target EPv2 address. Note that you must write the colons \ - yourself.","Communicator",src.target_address) ) - if(new_address) - target_address = new_address - - if(href_list["clear_target_address"]) - target_address = "" - - if(href_list["dial"]) - if(!get_connection_to_tcomms()) - to_chat(usr, "Error: Cannot connect to Exonet node.") - return - var/their_address = href_list["dial"] - exonet.send_message(their_address, "voice") - - if(href_list["decline"]) - var/ref_to_remove = href_list["decline"] - var/atom/decline = locate(ref_to_remove) - if(decline) - del_request(decline) - - if(href_list["message"]) - if(!get_connection_to_tcomms()) - to_chat(usr, "Error: Cannot connect to Exonet node.") - return - var/their_address = href_list["message"] - var/text = sanitizeSafe(input(usr,"Enter your message.","Text Message")) - if(text) - exonet.send_message(their_address, "text", text) - im_list += list(list("address" = exonet.address, "to_address" = their_address, "im" = text)) - log_pda("(COMM: [src]) sent \"[text]\" to [exonet.get_atom_from_address(their_address)]", usr) - for(var/mob/M in player_list) - if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) - if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) - continue - if(exonet.get_atom_from_address(their_address) == M) - continue - M.show_message("Comm IM - [src] -> [exonet.get_atom_from_address(their_address)]: [text]") - - if(href_list["disconnect"]) - var/name_to_disconnect = href_list["disconnect"] - for(var/mob/living/voice/V in contents) - if(name_to_disconnect == V.name) - close_connection(usr, V, "[usr] hung up") - for(var/obj/item/device/communicator/comm in communicating) - if(name_to_disconnect == comm.name) - close_connection(usr, comm, "[usr] hung up") - - if(href_list["startvideo"]) - var/ref_to_video = href_list["startvideo"] - var/obj/item/device/communicator/comm = locate(ref_to_video) - if(comm) - connect_video(usr, comm) - - if(href_list["endvideo"]) - if(video_source) - end_video() - - if(href_list["watchvideo"]) - if(video_source) - watch_video(usr,video_source.loc) - - if(href_list["copy"]) - target_address = href_list["copy"] - - if(href_list["copy_name"]) - target_address_name = href_list["copy_name"] - - if(href_list["hang_up"]) - for(var/mob/living/voice/V in contents) - close_connection(usr, V, "[usr] hung up") - for(var/obj/item/device/communicator/comm in communicating) - close_connection(usr, comm, "[usr] hung up") - - if(href_list["switch_tab"]) - selected_tab = href_list["switch_tab"] - - if(href_list["edit"]) - var/n = input(usr, "Please enter message", name, notehtml) - n = sanitizeSafe(n, extra = 0) - if(n) - note = html_decode(n) - notehtml = note - note = replacetext(note, "\n", "
") - else - note = "" - notehtml = note - - if(href_list["switch_template"]) - var/datum/nanoui/ui = SSnanoui.get_open_ui(usr, src, "main") - if(ui) - ui.add_template("Body", href_list["switch_template"]) - - if(href_list["Light"]) - fon = !fon - set_light(fon * flum) - - if(href_list["toggle_device"]) - var/obj/O = cartridge.internal_devices[text2num(href_list["toggle_device"])] - cartridge.active_devices ^= list(O) // Exclusive or, will toggle its presence - - if(href_list["newsfeed"]) - newsfeed_channel = text2num(href_list["newsfeed"]) - - if(href_list["cartridge_topic"] && cartridge) // Has to have a cartridge to perform these functions - cartridge.Topic(href, href_list) - - SSnanoui.update_uis(src) - add_fingerprint(usr) diff --git a/code/game/objects/items/devices/communicator/UI_tgui.dm b/code/game/objects/items/devices/communicator/UI_tgui.dm new file mode 100644 index 0000000000..8dabf77f42 --- /dev/null +++ b/code/game/objects/items/devices/communicator/UI_tgui.dm @@ -0,0 +1,326 @@ +// Proc: tgui_state() +// Parameters: User +// Description: This tells TGUI to only allow us to be interacted with while in a mob inventory. +/obj/item/device/communicator/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +// Proc: tgui_interact() +// Parameters: User, UI, Parent UI +// Description: This proc handles opening the UI. It's basically just a standard stub. +/obj/item/device/communicator/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/custom_state) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Communicator", name) + if(custom_state) + ui.set_state(custom_state) + ui.open() + if(custom_state) // Just in case + ui.set_state(custom_state) + +// Proc: tgui_data() +// Parameters: User, UI, State +// Description: Uses a bunch of for loops to turn lists into lists of lists, so they can be displayed in nanoUI, then displays various buttons to the user. +/obj/item/device/communicator/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + // this is the data which will be sent to the ui + var/list/data = list() //General nanoUI information + var/list/communicators = list() //List of communicators + var/list/invites = list() //Communicators and ghosts we've invited to our communicator. + var/list/requests = list() //Communicators and ghosts wanting to go in our communicator. + var/list/voices = list() //Current /mob/living/voice s inside the device. + var/list/connected_communicators = list() //Current communicators connected to the device. + + var/list/im_contacts_ui = list() //List of communicators that have been messaged. + var/list/im_list_ui = list() //List of messages. + + var/list/weather = list() + var/list/modules_ui = list() //Home screen info. + + //First we add other 'local' communicators. + for(var/obj/item/device/communicator/comm in known_devices) + if(comm.network_visibility && comm.exonet) + communicators.Add(list(list( + "name" = sanitize(comm.name), + "address" = comm.exonet.address + ))) + + //Now for ghosts who we pretend have communicators. + for(var/mob/observer/dead/O in known_devices) + if(O.client && O.client.prefs.communicator_visibility == 1 && O.exonet) + communicators.Add(list(list( + "name" = sanitize("[O.client.prefs.real_name]'s communicator"), + "address" = O.exonet.address, + "ref" = "\ref[O]" + ))) + + //Lists all the other communicators that we invited. + for(var/obj/item/device/communicator/comm in voice_invites) + if(comm.exonet) + invites.Add(list(list( + "name" = sanitize(comm.name), + "address" = comm.exonet.address, + "ref" = "\ref[comm]" + ))) + + //Ghosts we invited. + for(var/mob/observer/dead/O in voice_invites) + if(O.exonet && O.client) + invites.Add(list(list( + "name" = sanitize("[O.client.prefs.real_name]'s communicator"), + "address" = O.exonet.address, + "ref" = "\ref[O]" + ))) + + //Communicators that want to talk to us. + for(var/obj/item/device/communicator/comm in voice_requests) + if(comm.exonet) + requests.Add(list(list( + "name" = sanitize(comm.name), + "address" = comm.exonet.address, + "ref" = "\ref[comm]" + ))) + + //Ghosts that want to talk to us. + for(var/mob/observer/dead/O in voice_requests) + if(O.exonet && O.client) + requests.Add(list(list( + "name" = sanitize("[O.client.prefs.real_name]'s communicator"), + "address" = O.exonet.address, + "ref" = "\ref[O]" + ))) + + //Now for all the voice mobs inside the communicator. + for(var/mob/living/voice/voice in contents) + voices.Add(list(list( + "name" = sanitize("[voice.name]'s communicator"), + "true_name" = sanitize(voice.name), + ))) + + //Finally, all the communicators linked to this one. + for(var/obj/item/device/communicator/comm in communicating) + connected_communicators.Add(list(list( + "name" = sanitize(comm.name), + "true_name" = sanitize(comm.name), + "ref" = "\ref[comm]", + ))) + + //Devices that have been messaged or recieved messages from. + for(var/obj/item/device/communicator/comm in im_contacts) + if(comm.exonet) + im_contacts_ui.Add(list(list( + "name" = sanitize(comm.name), + "address" = comm.exonet.address, + "ref" = "\ref[comm]" + ))) + + for(var/mob/observer/dead/ghost in im_contacts) + if(ghost.exonet) + im_contacts_ui.Add(list(list( + "name" = sanitize(ghost.name), + "address" = ghost.exonet.address, + "ref" = "\ref[ghost]" + ))) + + for(var/obj/item/integrated_circuit/input/EPv2/CIRC in im_contacts) + if(CIRC.exonet && CIRC.assembly) + im_contacts_ui.Add(list(list( + "name" = sanitize(CIRC.assembly.name), + "address" = CIRC.exonet.address, + "ref" = "\ref[CIRC]" + ))) + + + //Actual messages. + for(var/I in im_list) + im_list_ui.Add(list(list( + "address" = I["address"], + "to_address" = I["to_address"], + "im" = I["im"] + ))) + + //Weather reports. + for(var/datum/planet/planet in SSplanets.planets) + if(planet.weather_holder && planet.weather_holder.current_weather) + var/list/W = list( + "Planet" = planet.name, + "Time" = planet.current_time.show_time("hh:mm"), + "Weather" = planet.weather_holder.current_weather.name, + "Temperature" = planet.weather_holder.temperature - T0C, + "High" = planet.weather_holder.current_weather.temp_high - T0C, + "Low" = planet.weather_holder.current_weather.temp_low - T0C, + "WindDir" = planet.weather_holder.wind_dir ? dir2text(planet.weather_holder.wind_dir) : "None", + "WindSpeed" = planet.weather_holder.wind_speed ? "[planet.weather_holder.wind_speed > 2 ? "Severe" : "Normal"]" : "None", + "Forecast" = english_list(planet.weather_holder.forecast, and_text = "→", comma_text = "→", final_comma_text = "→") // Unicode RIGHTWARDS ARROW. + ) + weather.Add(list(W)) + + + //Modules for homescreen. + for(var/list/R in modules) + modules_ui.Add(list(R)) + + data["user"] = "\ref[user]" // For receiving input() via topic, because input(usr,...) wasn't working on cartridges + data["owner"] = owner ? owner : "Unset" + data["occupation"] = occupation ? occupation : "Swipe ID to set." + data["connectionStatus"] = get_connection_to_tcomms() + data["visible"] = network_visibility + data["address"] = exonet.address ? exonet.address : "Unallocated" + data["targetAddress"] = target_address + data["targetAddressName"] = target_address_name + data["currentTab"] = selected_tab + data["knownDevices"] = communicators + data["invitesSent"] = invites + data["requestsReceived"] = requests + data["voice_mobs"] = voices + data["communicating"] = connected_communicators + data["video_comm"] = video_source ? "\ref[video_source.loc]" : null + data["imContacts"] = im_contacts_ui + data["imList"] = im_list_ui + data["time"] = stationtime2text() + data["ring"] = ringer + data["homeScreen"] = modules_ui + data["note"] = note // current notes + data["weather"] = weather + data["aircontents"] = src.analyze_air() + data["flashlight"] = fon + data["feeds"] = compile_news() + data["latest_news"] = get_recent_news() + if(newsfeed_channel) + data["target_feed"] = data["feeds"][newsfeed_channel] + else + data["target_feed"] = null + + return data + +// Proc: tgui_static_data() +// Parameters: User, UI, State +// Description: Just like tgui_data, except it only gets called once when the user opens the UI, not every tick. +/obj/item/device/communicator/tgui_static_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + // Update manifest' + if(data_core) + data_core.get_manifest_list() + data["manifest"] = PDA_Manifest + return data + +// Proc: tgui-act() +// Parameters: 4 (standard tgui_act arguments) +// Description: Responds to UI button presses. +/obj/item/device/communicator/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + add_fingerprint(usr) + . = TRUE + switch(action) + if("rename") + var/new_name = sanitizeSafe(input(usr,"Please enter your name.","Communicator",usr.name) ) + if(new_name) + register_device(new_name) + + if("toggle_visibility") + switch(network_visibility) + if(1) //Visible, becoming invisbile + network_visibility = 0 + if(camera) + camera.remove_network(NETWORK_COMMUNICATORS) + if(0) //Invisible, becoming visible + network_visibility = 1 + if(camera) + camera.add_network(NETWORK_COMMUNICATORS) + + if("toggle_ringer") + ringer = !ringer + + if("add_hex") + var/hex = params["add_hex"] + add_to_EPv2(hex) + + if("write_target_address") + target_address = sanitizeSafe(params["val"]) + + if("clear_target_address") + target_address = "" + + if("dial") + if(!get_connection_to_tcomms()) + to_chat(usr, "Error: Cannot connect to Exonet node.") + return FALSE + var/their_address = params["dial"] + exonet.send_message(their_address, "voice") + + if("decline") + var/ref_to_remove = params["decline"] + var/atom/decline = locate(ref_to_remove) + if(decline) + del_request(decline) + + if("message") + if(!get_connection_to_tcomms()) + to_chat(usr, "Error: Cannot connect to Exonet node.") + return FALSE + var/their_address = params["message"] + var/text = sanitizeSafe(input(usr,"Enter your message.","Text Message")) + if(text) + exonet.send_message(their_address, "text", text) + im_list += list(list("address" = exonet.address, "to_address" = their_address, "im" = text)) + log_pda("(COMM: [src]) sent \"[text]\" to [exonet.get_atom_from_address(their_address)]", usr) + for(var/mob/M in player_list) + if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears)) + if(istype(M, /mob/new_player) || M.forbid_seeing_deadchat) + continue + if(exonet.get_atom_from_address(their_address) == M) + continue + M.show_message("Comm IM - [src] -> [exonet.get_atom_from_address(their_address)]: [text]") + + if("disconnect") + var/name_to_disconnect = params["disconnect"] + for(var/mob/living/voice/V in contents) + if(name_to_disconnect == sanitize(V.name)) + close_connection(usr, V, "[usr] hung up") + for(var/obj/item/device/communicator/comm in communicating) + if(name_to_disconnect == sanitize(comm.name)) + close_connection(usr, comm, "[usr] hung up") + + if("startvideo") + var/ref_to_video = params["startvideo"] + var/obj/item/device/communicator/comm = locate(ref_to_video) + if(comm) + connect_video(usr, comm) + + if("endvideo") + if(video_source) + end_video() + + if("copy") + target_address = params["copy"] + + if("copy_name") + target_address_name = params["copy_name"] + + if("hang_up") + for(var/mob/living/voice/V in contents) + close_connection(usr, V, "[usr] hung up") + for(var/obj/item/device/communicator/comm in communicating) + close_connection(usr, comm, "[usr] hung up") + + if("switch_tab") + selected_tab = params["switch_tab"] + + if("edit") + var/n = input(usr, "Please enter message", name, notehtml) as message|null + n = sanitizeSafe(n, extra = 0) + if(n) + note = html_decode(n) + notehtml = note + note = replacetext(note, "\n", "
") + else + note = "" + notehtml = note + + if("Light") + fon = !fon + set_light(fon * flum) + + if("newsfeed") + newsfeed_channel = text2num(params["newsfeed"]) + diff --git a/code/game/objects/items/devices/communicator/cartridge.dm b/code/game/objects/items/devices/communicator/cartridge.dm deleted file mode 100644 index 120df593e9..0000000000 --- a/code/game/objects/items/devices/communicator/cartridge.dm +++ /dev/null @@ -1,952 +0,0 @@ -// Communicator peripheral devices -// Internal devices that attack() can be relayed to -// Additional UI menus for added functionality -/obj/item/weapon/commcard - name = "generic commcard" - desc = "A peripheral plug-in for personal communicators." - icon = 'icons/obj/pda.dmi' - icon_state = "cart" - item_state = "electronic" - w_class = ITEMSIZE_TINY - - var/list/internal_devices = list() // Devices that can be toggled on to trigger on attack() - var/list/active_devices = list() // Devices that will be triggered on attack() - var/list/ui_templates = list() // List of ui templates the commcard can access - var/list/internal_data = list() // Data that shouldn't be updated every time nanoUI updates, or needs to persist between updates - - -/obj/item/weapon/commcard/proc/get_device_status() - var/list/L = list() - var/i = 1 - for(var/obj/I in internal_devices) - if(I in active_devices) - L[++L.len] = list("name" = "\proper[I.name]", "active" = 1, "index" = i++) - else - L[++L.len] = list("name" = I.name, "active" = 0, "index" = i++) - return L - - -// cartridge.get_data() returns a list of tuples: -// The field element is the tag used to access the information by the template -// The value element is the actual data, and can take any form necessary for the template -/obj/item/weapon/commcard/proc/get_data() - return list() - -// Handles cartridge-specific functions -// The helper.link() MUST HAVE 'cartridge_topic' passed into the href in order for cartridge functions to be processed. -// Doesn't matter what the value of it is for now, it's just a flag to say, "Hey, there's cartridge data to change!" -/obj/item/weapon/commcard/Topic(href, href_list) - - // Signalers - if(href_list["signaler_target"]) - - var/obj/item/device/assembly/signaler/S = locate(href_list["signaler_target"]) // Should locate the correct signaler - - if(!istype(S)) // Ref is no longer valid - return - - if(S.loc != src) // No longer within the cartridge - return - - switch(href_list["signaler_action"]) - if("Pulse") - S.activate() - - if("Edit") - var/mob/user = locate(href_list["user"]) - if(!istype(user)) // Ref no longer valid - return - - var/newVal = input(user, "Input a new [href_list["signaler_value"]].", href_list["signaler_value"], (href_list["signaler_value"] == "Code" ? S.code : S.frequency)) as num|null - if(newVal) - switch(href_list["signaler_value"]) - if("Code") - S.code = newVal - - if("Frequency") - S.frequency = newVal - - // Refresh list of powernet sensors - if(href_list["powernet_refresh"]) - internal_data["grid_sensors"] = find_powernet_sensors() - - // Load apc's on targeted powernet - if(href_list["powernet_target"]) - internal_data["powernet_target"] = href_list["powernet_target"] - - // GPS units - if(href_list["gps_target"]) - var/obj/item/device/gps/G = locate(href_list["gps_target"]) - - if(!istype(G)) // Ref is no longer valid - return - - if(G.loc != src) // No longer within the cartridge - return - - switch(href_list["gps_action"]) - if("Power") - G.tracking = text2num(href_list["value"]) - - if("Long_Range") - G.local_mode = text2num(href_list["value"]) - - if("Hide_Signal") - G.hide_signal = text2num(href_list["value"]) - - if("Tag") - var/mob/user = locate(href_list["user"]) - if(!istype(user)) // Ref no longer valid - return - - var/newTag = input(user, "Please enter desired tag.", G.tag) as text|null - - if(newTag) - G.tag = newTag - - if(href_list["active_category"]) - internal_data["supply_category"] = href_list["active_category"] - - // Supply topic - // Copied from /obj/machinery/computer/supplycomp/Topic() - // code\game\machinery\computer\supply.dm, line 188 - // Unfortunately, in order to support complete functionality, the whole thing is necessary - if(href_list["pack_ref"]) - var/datum/supply_pack/S = locate(href_list["pack_ref"]) - - // Invalid ref - if(!istype(S)) - return - - // Expand the supply pack's contents - if(href_list["expand"]) - internal_data["supply_pack_expanded"] ^= S - - // Make a request for the pack - if(href_list["request"]) - var/mob/user = locate(href_list["user"]) - if(!istype(user)) // Invalid ref - return - - if(world.time < internal_data["supply_reqtime"]) - visible_message("[src] flashes, \"[internal_data["supply_reqtime"] - world.time] seconds remaining until another requisition form may be printed.\"") - return - - var/timeout = world.time + 600 - var/reason = sanitize(input(user, "Reason:","Why do you require this item?","") as null|text) - if(world.time > timeout) - to_chat(user, "Error. Request timed out.") - return - if(!reason) - return - - SSsupply.create_order(S, user, reason) - internal_data["supply_reqtime"] = (world.time + 5) % 1e5 - - if(href_list["order_ref"]) - var/datum/supply_order/O = locate(href_list["order_ref"]) - - // Invalid ref - if(!istype(O)) - return - - var/mob/user = locate(href_list["user"]) - if(!istype(user)) // Invalid ref - return - - if(href_list["edit"]) - var/new_val = sanitize(input(user, href_list["edit"], "Enter the new value for this field:", href_list["default"]) as null|text) - if(!new_val) - return - - switch(href_list["edit"]) - if("Supply Pack") - O.name = new_val - - if("Cost") - var/num = text2num(new_val) - if(num) - O.cost = num - - if("Index") - var/num = text2num(new_val) - if(num) - O.index = num - - if("Reason") - O.comment = new_val - - if("Ordered by") - O.ordered_by = new_val - - if("Ordered at") - O.ordered_at = new_val - - if("Approved by") - O.approved_by = new_val - - if("Approved at") - O.approved_at = new_val - - if(href_list["approve"]) - SSsupply.approve_order(O, user) - - if(href_list["deny"]) - SSsupply.deny_order(O, user) - - if(href_list["delete"]) - SSsupply.delete_order(O, user) - - if(href_list["clear_all_requests"]) - var/mob/user = locate(href_list["user"]) - if(!istype(user)) // Invalid ref - return - - SSsupply.deny_all_pending(user) - - if(href_list["export_ref"]) - var/datum/exported_crate/E = locate(href_list["export_ref"]) - - // Invalid ref - if(!istype(E)) - return - - var/mob/user = locate(href_list["user"]) - if(!istype(user)) // Invalid ref - return - - if(href_list["index"]) - var/list/L = E.contents[href_list["index"]] - - if(href_list["edit"]) - var/field = alert(user, "Select which field to edit", , "Name", "Quantity", "Value") - - var/new_val = sanitize(input(user, href_list["edit"], "Enter the new value for this field:", href_list["default"]) as null|text) - if(!new_val) - return - - switch(field) - if("Name") - L["object"] = new_val - - if("Quantity") - var/num = text2num(new_val) - if(num) - L["quantity"] = num - - if("Value") - var/num = text2num(new_val) - if(num) - L["value"] = num - - if(href_list["delete"]) - E.contents.Cut(href_list["index"], href_list["index"] + 1) - - // Else clause means they're editing/deleting the whole export report, rather than a specific item in it - else if(href_list["edit"]) - var/new_val = sanitize(input(user, href_list["edit"], "Enter the new value for this field:", href_list["default"]) as null|text) - if(!new_val) - return - - switch(href_list["edit"]) - if("Name") - E.name = new_val - - if("Value") - var/num = text2num(new_val) - if(num) - E.value = num - - else if(href_list["delete"]) - SSsupply.delete_export(E, user) - - else if(href_list["add_item"]) - SSsupply.add_export_item(E, user) - - if(SSsupply && SSsupply.shuttle) - switch(href_list["send_shuttle"]) - if("send_away") - if(SSsupply.shuttle.forbidden_atoms_check()) - to_chat(usr, "For safety reasons the automated supply shuttle cannot transport live organisms, classified nuclear weaponry or homing beacons.") - else - SSsupply.shuttle.launch(src) - to_chat(usr, "Initiating launch sequence.") - - if("send_to_station") - SSsupply.shuttle.launch(src) - to_chat(usr, "The supply shuttle has been called and will arrive in approximately [round(SSsupply.movetime/600,1)] minutes.") - - if("cancel_shuttle") - SSsupply.shuttle.cancel_launch(src) - - if("force_shuttle") - SSsupply.shuttle.force_launch(src) - - // Status display - switch(href_list["stat_display"]) - if("message") - post_status("message", internal_data["stat_display_line1"], internal_data["stat_display_line2"]) - internal_data["stat_display_special"] = "message" - if("alert") - post_status("alert", href_list["alert"]) - internal_data["stat_display_special"] = href_list["alert"] - if("setmsg") - internal_data["stat_display_line[href_list["line"]]"] = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", internal_data["stat_display_line[href_list["line"]]"]) as text|null, 40), 40) - else - post_status(href_list["stat_display"]) - internal_data["stat_display_special"] = href_list["stat_display"] - - // Merc shuttle blast door controls - switch(href_list["all_blast_doors"]) - if("open") - for(var/obj/machinery/door/blast/B in internal_data["shuttle_doors"]) - B.open() - if("close") - for(var/obj/machinery/door/blast/B in internal_data["shuttle_doors"]) - B.close() - - if(href_list["scan_blast_doors"]) - internal_data["shuttle_doors"] = find_blast_doors() - - if(href_list["toggle_blast_door"]) - var/obj/machinery/door/blast/B = locate(href_list["toggle_blast_door"]) - if(!B) - return - spawn(0) - if(B.density) - B.open() - else - B.close() - - -// Updates status displays with a new message -// Copied from /obj/item/weapon/cartridge/proc/post_status(), -// code/game/objects/items/devices/PDA/cart.dm, line 251 -/obj/item/weapon/commcard/proc/post_status(var/command, var/data1, var/data2) - var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) - if(!frequency) - return - - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = TRANSMISSION_RADIO - status_signal.data["command"] = command - - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - internal_data["stat_display_active1"] = data1 // Update the internally stored message, we won't get receive_signal if we're the sender - internal_data["stat_display_active2"] = data2 - if(loc) - var/obj/item/PDA = loc - var/mob/user = PDA.fingerprintslast - log_admin("STATUS: [user] set status screen with [src]. Message: [data1] [data2]") - message_admins("STATUS: [user] set status screen with [src]. Message: [data1] [data2]") - - if("alert") - status_signal.data["picture_state"] = data1 - - frequency.post_signal(src, status_signal) - -// Receives updates by external devices to the status displays -/obj/item/weapon/commcard/receive_signal(var/datum/signal/signal, var/receive_method, var/receive_param) - internal_data["stat_display_special"] = signal.data["command"] - switch(signal.data["command"]) - if("message") - internal_data["stat_display_active1"] = signal.data["msg1"] - internal_data["stat_display_active2"] = signal.data["msg2"] - if("alert") - internal_data["stat_display_special"] = signal.data["picture_state"] - - -/////////////////////////// -// SUBTYPES -/////////////////////////// - - -// Engineering Cartridge: -// Devices -// *- Halogen Counter -// Templates -// *- Power Monitor -/obj/item/weapon/commcard/engineering - name = "\improper Power-ON cartridge" - icon_state = "cart-e" - ui_templates = list(list("name" = "Power Monitor", "template" = "comm_power_monitor.tmpl")) - -/obj/item/weapon/commcard/engineering/New() - ..() - internal_devices |= new /obj/item/device/halogen_counter(src) - -/obj/item/weapon/commcard/engineering/Initialize() - internal_data["grid_sensors"] = find_powernet_sensors() - internal_data["powernet_target"] = "" - -/obj/item/weapon/commcard/engineering/get_data() - return list( - list("field" = "powernet_monitoring", "value" = get_powernet_monitoring_list()), - list("field" = "powernet_target", "value" = get_powernet_target(internal_data["powernet_target"])) - ) - -// Atmospherics Cartridge: -// Devices -// *- Gas scanner -/obj/item/weapon/commcard/atmos - name = "\improper BreatheDeep cartridge" - icon_state = "cart-a" - -/obj/item/weapon/commcard/atmos/New() - ..() - internal_devices |= new /obj/item/device/analyzer(src) - - -// Medical Cartridge: -// Devices -// *- Halogen Counter -// *- Health Analyzer -// Templates -// *- Medical Records -/obj/item/weapon/commcard/medical - name = "\improper Med-U cartridge" - icon_state = "cart-m" - ui_templates = list(list("name" = "Medical Records", "template" = "med_records.tmpl")) - -/obj/item/weapon/commcard/medical/New() - ..() - internal_devices |= new /obj/item/device/healthanalyzer(src) - internal_devices |= new /obj/item/device/halogen_counter(src) - -/obj/item/weapon/commcard/medical/get_data() - return list(list("field" = "med_records", "value" = get_med_records())) - - -// Chemistry Cartridge: -// Devices -// *- Halogen Counter -// *- Health Analyzer -// *- Reagent Scanner -// Templates -// *- Medical Records -/obj/item/weapon/commcard/medical/chemistry - name = "\improper ChemWhiz cartridge" - icon_state = "cart-chem" - -/obj/item/weapon/commcard/medical/chemistry/New() - ..() - internal_devices |= new /obj/item/device/reagent_scanner(src) - - -// Detective Cartridge: -// Devices -// *- Halogen Counter -// *- Health Analyzer -// Templates -// *- Medical Records -// *- Security Records -/obj/item/weapon/commcard/medical/detective - name = "\improper D.E.T.E.C.T. cartridge" - icon_state = "cart-s" - ui_templates = list( - list("name" = "Medical Records", "template" = "med_records.tmpl"), - list("name" = "Security Records", "template" = "sec_records.tmpl") - ) - -/obj/item/weapon/commcard/medical/detective/get_data() - var/list/data = ..() - data[++data.len] = list("field" = "sec_records", "value" = get_sec_records()) - return data - - -// Internal Affairs Cartridge: -// Templates -// *- Security Records -// *- Employment Records -/obj/item/weapon/commcard/int_aff - name = "\improper P.R.O.V.E. cartridge" - icon_state = "cart-s" - ui_templates = list( - list("name" = "Employment Records", "template" = "emp_records.tmpl"), - list("name" = "Security Records", "template" = "sec_records.tmpl") - ) - -/obj/item/weapon/commcard/int_aff/get_data() - return list( - list("field" = "emp_records", "value" = get_emp_records()), - list("field" = "sec_records", "value" = get_sec_records()) - ) - - -// Security Cartridge: -// Templates -// *- Security Records -// *- Security Bot Access -/obj/item/weapon/commcard/security - name = "\improper R.O.B.U.S.T. cartridge" - icon_state = "cart-s" - ui_templates = list( - list("name" = "Security Records", "template" = "sec_records.tmpl"), - list("name" = "Security Bot Control", "template" = "sec_bot_access.tmpl") - ) - -/obj/item/weapon/commcard/security/get_data() - return list( - list("field" = "sec_records", "value" = get_sec_records()), - list("field" = "sec_bot_access", "value" = get_sec_bot_access()) - ) - - -// Janitor Cartridge: -// Templates -// *- Janitorial Locator Magicbox -/obj/item/weapon/commcard/janitor - name = "\improper CustodiPRO cartridge" - desc = "The ultimate in clean-room design." - ui_templates = list( - list("name" = "Janitorial Supply Locator", "template" = "janitorialLocator.tmpl") - ) - -/obj/item/weapon/commcard/janitor/get_data() - return list( - list("field" = "janidata", "value" = get_janitorial_locations()) - ) - - -// Signal Cartridge: -// Devices -// *- Signaler -// Templates -// *- Signaler Access -/obj/item/weapon/commcard/signal - name = "generic signaler cartridge" - desc = "A data cartridge with an integrated radio signaler module." - ui_templates = list( - list("name" = "Integrated Signaler Control", "template" = "signaler_access.tmpl") - ) - -/obj/item/weapon/commcard/signal/New() - ..() - internal_devices |= new /obj/item/device/assembly/signaler(src) - -/obj/item/weapon/commcard/signal/get_data() - return list( - list("field" = "signaler_access", "value" = get_int_signalers()) - ) - - -// Science Cartridge: -// Devices -// *- Signaler -// *- Reagent Scanner -// *- Gas Scanner -// Templates -// *- Signaler Access -/obj/item/weapon/commcard/signal/science - name = "\improper Signal Ace 2 cartridge" - desc = "Complete with integrated radio signaler!" - icon_state = "cart-tox" - // UI templates inherited - -/obj/item/weapon/commcard/signal/science/New() - ..() - internal_devices |= new /obj/item/device/reagent_scanner(src) - internal_devices |= new /obj/item/device/analyzer(src) - - -// Supply Cartridge: -// Templates -// *- Supply Records -/obj/item/weapon/commcard/supply - name = "\improper Space Parts & Space Vendors cartridge" - desc = "Perfect for the Quartermaster on the go!" - icon_state = "cart-q" - ui_templates = list( - list("name" = "Supply Records", "template" = "supply_records.tmpl") - ) - -/obj/item/weapon/commcard/supply/New() - ..() - internal_data["supply_category"] = null - internal_data["supply_controls"] = FALSE // Cannot control the supply shuttle, cannot accept orders - internal_data["supply_pack_expanded"] = list() - internal_data["supply_reqtime"] = -1 - -/obj/item/weapon/commcard/supply/get_data() - // Supply records data - var/list/shuttle_status = get_supply_shuttle_status() - var/list/orders = get_supply_orders() - var/list/receipts = get_supply_receipts() - var/list/misc_supply_data = get_misc_supply_data() // Packaging this stuff externally so it's less hardcoded into the specific cartridge - var/list/pack_list = list() // List of supply packs within the currently selected category - - if(internal_data["supply_category"]) - pack_list = get_supply_pack_list() - - return list( - list("field" = "shuttle_auth", "value" = misc_supply_data["shuttle_auth"]), - list("field" = "order_auth", "value" = misc_supply_data["order_auth"]), - list("field" = "supply_points", "value" = misc_supply_data["supply_points"]), - list("field" = "categories", "value" = misc_supply_data["supply_categories"]), - list("field" = "contraband", "value" = misc_supply_data["contraband"]), - list("field" = "active_category", "value" = internal_data["supply_category"]), - list("field" = "shuttle", "value" = shuttle_status), - list("field" = "orders", "value" = orders), - list("field" = "receipts", "value" = receipts), - list("field" = "supply_packs", "value" = pack_list) - ) - - -// Command Cartridge: -// Templates -// *- Status Display Access -// *- Employment Records -/obj/item/weapon/commcard/head - name = "\improper Easy-Record DELUXE" - icon_state = "cart-h" - ui_templates = list( - list("name" = "Status Display Access", "template" = "stat_display_access.tmpl"), - list("name" = "Employment Records", "template" = "emp_records.tmpl") - ) - -/obj/item/weapon/commcard/head/New() - ..() - internal_data["stat_display_line1"] = null - internal_data["stat_display_line2"] = null - internal_data["stat_display_active1"] = null - internal_data["stat_display_active2"] = null - internal_data["stat_display_special"] = null - -/obj/item/weapon/commcard/head/Initialize() - // Have to register the commcard with the Radio controller to receive updates to the status displays - radio_controller.add_object(src, 1435) - ..() - -/obj/item/weapon/commcard/head/Destroy() - // Have to unregister the commcard for proper bookkeeping - radio_controller.remove_object(src, 1435) - ..() - -/obj/item/weapon/commcard/head/get_data() - return list( - list("field" = "emp_records", "value" = get_emp_records()), - list("field" = "stat_display", "value" = get_status_display()) - ) - -// Head of Personnel Cartridge: -// Templates -// *- Status Display Access -// *- Employment Records -// *- Security Records -// *- Supply Records -// ?- Supply Bot Access -// *- Janitorial Locator Magicbox -/obj/item/weapon/commcard/head/hop - name = "\improper HumanResources9001 cartridge" - icon_state = "cart-h" - ui_templates = list( - list("name" = "Status Display Access", "template" = "stat_display_access.tmpl"), - list("name" = "Employment Records", "template" = "emp_records.tmpl"), - list("name" = "Security Records", "template" = "sec_records.tmpl"), - list("name" = "Supply Records", "template" = "supply_records.tmpl"), - list("name" = "Janitorial Supply Locator", "template" = "janitorialLocator.tmpl") - ) - - -/obj/item/weapon/commcard/head/hop/get_data() - var/list/data = ..() - - // Sec records - data[++data.len] = list("field" = "sec_records", "value" = get_sec_records()) - - // Supply records data - var/list/shuttle_status = get_supply_shuttle_status() - var/list/orders = get_supply_orders() - var/list/receipts = get_supply_receipts() - var/list/misc_supply_data = get_misc_supply_data() // Packaging this stuff externally so it's less hardcoded into the specific cartridge - var/list/pack_list = list() // List of supply packs within the currently selected category - - if(internal_data["supply_category"]) - pack_list = get_supply_pack_list() - - data[++data.len] = list("field" = "shuttle_auth", "value" = misc_supply_data["shuttle_auth"]) - data[++data.len] = list("field" = "order_auth", "value" = misc_supply_data["order_auth"]) - data[++data.len] = list("field" = "supply_points", "value" = misc_supply_data["supply_points"]) - data[++data.len] = list("field" = "categories", "value" = misc_supply_data["supply_categories"]) - data[++data.len] = list("field" = "contraband", "value" = misc_supply_data["contraband"]) - data[++data.len] = list("field" = "active_category", "value" = internal_data["supply_category"]) - data[++data.len] = list("field" = "shuttle", "value" = shuttle_status) - data[++data.len] = list("field" = "orders", "value" = orders) - data[++data.len] = list("field" = "receipts", "value" = receipts) - data[++data.len] = list("field" = "supply_packs", "value" = pack_list) - - // Janitorial locator magicbox - data[++data.len] = list("field" = "janidata", "value" = get_janitorial_locations()) - - return data - - -// Head of Security Cartridge: -// Templates -// *- Status Display Access -// *- Employment Records -// *- Security Records -// *- Security Bot Access -/obj/item/weapon/commcard/head/hos - name = "\improper R.O.B.U.S.T. DELUXE" - icon_state = "cart-hos" - ui_templates = list( - list("name" = "Status Display Access", "template" = "stat_display_access.tmpl"), - list("name" = "Employment Records", "template" = "emp_records.tmpl"), - list("name" = "Security Records", "template" = "sec_records.tmpl"), - list("name" = "Security Bot Control", "template" = "sec_bot_access.tmpl") - ) - -/obj/item/weapon/commcard/head/hos/get_data() - var/list/data = ..() - // Sec records - data[++data.len] = list("field" = "sec_records", "value" = get_sec_records()) - // Sec bot access - data[++data.len] = list("field" = "sec_bot_access", "value" = get_sec_bot_access()) - return data - - -// Research Director Cartridge: -// Devices -// *- Signaler -// *- Gas Scanner -// *- Reagent Scanner -// Templates -// *- Status Display Access -// *- Employment Records -// *- Signaler Access -/obj/item/weapon/commcard/head/rd - name = "\improper Signal Ace DELUXE" - icon_state = "cart-rd" - ui_templates = list( - list("name" = "Status Display Access", "template" = "stat_display_access.tmpl"), - list("name" = "Employment Records", "template" = "emp_records.tmpl"), - list("name" = "Integrated Signaler Control", "template" = "signaler_access.tmpl") - ) - -/obj/item/weapon/commcard/head/rd/New() - ..() - internal_devices |= new /obj/item/device/analyzer(src) - internal_devices |= new /obj/item/device/reagent_scanner(src) - internal_devices |= new /obj/item/device/assembly/signaler(src) - -/obj/item/weapon/commcard/head/rd/get_data() - var/list/data = ..() - // Signaler access - data[++data.len] = list("field" = "signaler_access", "value" = get_int_signalers()) - return data - - -// Chief Medical Officer Cartridge: -// Devices -// *- Health Analyzer -// *- Reagent Scanner -// *- Halogen Counter -// Templates -// *- Status Display Access -// *- Employment Records -// *- Medical Records -/obj/item/weapon/commcard/head/cmo - name = "\improper Med-U DELUXE" - icon_state = "cart-cmo" - ui_templates = list( - list("name" = "Status Display Access", "template" = "stat_display_access.tmpl"), - list("name" = "Employment Records", "template" = "emp_records.tmpl"), - list("name" = "Medical Records", "template" = "med_records.tmpl") - ) - -/obj/item/weapon/commcard/head/cmo/New() - ..() - internal_devices |= new /obj/item/device/healthanalyzer(src) - internal_devices |= new /obj/item/device/reagent_scanner(src) - internal_devices |= new /obj/item/device/halogen_counter(src) - -/obj/item/weapon/commcard/head/cmo/get_data() - var/list/data = ..() - // Med records - data[++data.len] = list("field" = "med_records", "value" = get_med_records()) - return data - -// Chief Engineer Cartridge: -// Devices -// *- Gas Scanner -// *- Halogen Counter -// Templates -// *- Status Display Access -// *- Employment Records -// *- Power Monitoring -/obj/item/weapon/commcard/head/ce - name = "\improper Power-On DELUXE" - icon_state = "cart-ce" - ui_templates = list( - list("name" = "Status Display Access", "template" = "stat_display_access.tmpl"), - list("name" = "Employment Records", "template" = "emp_records.tmpl"), - list("name" = "Power Monitor", "template" = "comm_power_monitor.tmpl") - ) - -/obj/item/weapon/commcard/head/ce/New() - ..() - internal_devices |= new /obj.item/device/analyzer(src) - internal_devices |= new /obj/item/device/halogen_counter(src) - -/obj/item/weapon/commcard/head/ce/Initialize() - internal_data["grid_sensors"] = find_powernet_sensors() - internal_data["powernet_target"] = "" - -/obj/item/weapon/commcard/head/ce/get_data() - var/list/data = ..() - // Add power monitoring data - data[++data.len] = list("field" = "powernet_monitoring", "value" = get_powernet_monitoring_list()) - data[++data.len] = list("field" = "powernet_target", "value" = get_powernet_target(internal_data["powernet_target"])) - return data - - -// Captain Cartridge: -// Devices -// *- Health analyzer -// *- Gas Scanner -// *- Reagent Scanner -// *- Halogen Counter -// X- GPS - Balance -// *- Signaler -// Templates -// *- Status Display Access -// *- Employment Records -// *- Medical Records -// *- Security Records -// *- Power Monitoring -// *- Supply Records -// X- Supply Bot Access - Mulebots usually break when used -// *- Security Bot Access -// *- Janitorial Locator Magicbox -// X- GPS Access - Balance -// *- Signaler Access -/obj/item/weapon/commcard/head/captain - name = "\improper Value-PAK cartridge" - desc = "Now with 200% more value!" - icon_state = "cart-c" - ui_templates = list( - list("name" = "Status Display Access", "template" = "stat_display_access.tmpl"), - list("name" = "Employment Records", "template" = "emp_records.tmpl"), - list("name" = "Medical Records", "template" = "med_records.tmpl"), - list("name" = "Security Records", "template" = "sec_records.tmpl"), - list("name" = "Security Bot Control", "template" = "sec_bot_access.tmpl"), - list("name" = "Power Monitor", "template" = "comm_power_monitor.tmpl"), - list("name" = "Supply Records", "template" = "supply_records.tmpl"), - list("name" = "Janitorial Supply Locator", "template" = "janitorialLocator.tmpl"), - list("name" = "Integrated Signaler Control", "template" = "signaler_access.tmpl") - ) - -/obj/item/weapon/commcard/head/captain/New() - ..() - internal_devices |= new /obj.item/device/analyzer(src) - internal_devices |= new /obj/item/device/healthanalyzer(src) - internal_devices |= new /obj/item/device/reagent_scanner(src) - internal_devices |= new /obj/item/device/halogen_counter(src) - internal_devices |= new /obj/item/device/assembly/signaler(src) - -/obj/item/weapon/commcard/head/captain/get_data() - var/list/data = ..() - //Med records - data[++data.len] = list("field" = "med_records", "value" = get_med_records()) - - // Sec records - data[++data.len] = list("field" = "sec_records", "value" = get_sec_records()) - - // Sec bot access - data[++data.len] = list("field" = "sec_bot_access", "value" = get_sec_bot_access()) - - // Power Monitoring - data[++data.len] = list("field" = "powernet_monitoring", "value" = get_powernet_monitoring_list()) - data[++data.len] = list("field" = "powernet_target", "value" = get_powernet_target(internal_data["powernet_target"])) - - // Supply records data - var/list/shuttle_status = get_supply_shuttle_status() - var/list/orders = get_supply_orders() - var/list/receipts = get_supply_receipts() - var/list/misc_supply_data = get_misc_supply_data() // Packaging this stuff externally so it's less hardcoded into the specific cartridge - var/list/pack_list = list() // List of supply packs within the currently selected category - - if(internal_data["supply_category"]) - pack_list = get_supply_pack_list() - - data[++data.len] = list("field" = "shuttle_auth", "value" = misc_supply_data["shuttle_auth"]) - data[++data.len] = list("field" = "order_auth", "value" = misc_supply_data["order_auth"]) - data[++data.len] = list("field" = "supply_points", "value" = misc_supply_data["supply_points"]) - data[++data.len] = list("field" = "categories", "value" = misc_supply_data["supply_categories"]) - data[++data.len] = list("field" = "contraband", "value" = misc_supply_data["contraband"]) - data[++data.len] = list("field" = "active_category", "value" = internal_data["supply_category"]) - data[++data.len] = list("field" = "shuttle", "value" = shuttle_status) - data[++data.len] = list("field" = "orders", "value" = orders) - data[++data.len] = list("field" = "receipts", "value" = receipts) - data[++data.len] = list("field" = "supply_packs", "value" = pack_list) - - // Janitorial locator magicbox - data[++data.len] = list("field" = "janidata", "value" = get_janitorial_locations()) - - // Signaler access - data[++data.len] = list("field" = "signaler_access", "value" = get_int_signalers()) - - return data - - -// Mercenary Cartridge -// Templates -// *- Merc Shuttle Door Controller -/obj/item/weapon/commcard/mercenary - name = "\improper Detomatix cartridge" - icon_state = "cart" - ui_templates = list( - list("name" = "Shuttle Blast Door Control", "template" = "merc_blast_door_control.tmpl") - ) - -/obj/item/weapon/commcard/mercenary/Initialize() - internal_data["shuttle_door_code"] = "smindicate" // Copied from PDA code - internal_data["shuttle_doors"] = find_blast_doors() - -/obj/item/weapon/commcard/mercenary/get_data() - var/door_status[0] - for(var/obj/machinery/door/blast/B in internal_data["shuttle_doors"]) - door_status[++door_status.len] += list( - "open" = B.density, - "name" = B.name, - "ref" = "\ref[B]" - ) - - return list( - list("field" = "blast_door", "value" = door_status) - ) - - -// Explorer Cartridge -// Devices -// *- GPS -// Templates -// *- GPS Access - -// IMPORTANT: NOT MAPPED IN DUE TO BALANCE CONCERNS RE: FINDING THE VICTIMS OF ANTAGS. -// See suit sensors, specifically ease of turning them off, and variable level of settings which may or may not give location -// A GPS in your phone that is either broadcasting position or totally off, and can be hidden in pockets, coats, bags, boxes, etc, is much harder to disable -/obj/item/weapon/commcard/explorer - name = "\improper Explorator cartridge" - icon_state = "cart-tox" - ui_templates = list( - list("name" = "Integrated GPS", "template" = "gps_access.tmpl") - ) - -/obj/item/weapon/commcard/explorer/New() - ..() - internal_devices |= new /obj/item/device/gps/explorer(src) - -/obj/item/weapon/commcard/explorer/get_data() - var/list/GPS = get_GPS_lists() - - return list( - list("field" = "gps_access", "value" = GPS[1]), - list("field" = "gps_signal", "value" = GPS[2]), - list("field" = "gps_status", "value" = GPS[3]) - ) \ No newline at end of file diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm index 392af1b870..358ef3b61b 100644 --- a/code/game/objects/items/devices/communicator/communicator.dm +++ b/code/game/objects/items/devices/communicator/communicator.dm @@ -14,7 +14,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() #define WTHRTAB 7 #define MANITAB 8 #define SETTTAB 9 -#define EXTRTAB 10 /obj/item/device/communicator name = "communicator" @@ -43,19 +42,18 @@ var/global/list/obj/item/device/communicator/all_communicators = list() var/note = "Thank you for choosing the T-14.2 Communicator, this is your notepad!" //Current note in the notepad function var/notehtml = "" - var/obj/item/weapon/commcard/cartridge = null //current cartridge var/fon = 0 // Internal light var/flum = 2 // Brightness var/list/modules = list( - list("module" = "Phone", "icon" = "phone64", "number" = PHONTAB), - list("module" = "Contacts", "icon" = "person64", "number" = CONTTAB), - list("module" = "Messaging", "icon" = "comment64", "number" = MESSTAB), - list("module" = "News", "icon" = "note64", "number" = NEWSTAB), // Need a different icon, - list("module" = "Note", "icon" = "note64", "number" = NOTETAB), - list("module" = "Weather", "icon" = "sun64", "number" = WTHRTAB), - list("module" = "Crew Manifest", "icon" = "note64", "number" = MANITAB), // Need a different icon, - list("module" = "Settings", "icon" = "gear64", "number" = SETTTAB), + list("module" = "Phone", "icon" = "phone", "number" = PHONTAB), + list("module" = "Contacts", "icon" = "user", "number" = CONTTAB), + list("module" = "Messaging", "icon" = "comment-alt", "number" = MESSTAB), + list("module" = "News", "icon" = "newspaper", "number" = NEWSTAB), // Need a different icon, + list("module" = "Note", "icon" = "sticky-note", "number" = NOTETAB), + list("module" = "Weather", "icon" = "sun", "number" = WTHRTAB), + list("module" = "Crew Manifest", "icon" = "crown", "number" = MANITAB), // Need a different icon, + list("module" = "Settings", "icon" = "cog", "number" = SETTTAB), ) //list("module" = "Name of Module", "icon" = "icon name64", "number" = "what tab is the module") var/selected_tab = HOMETAB @@ -104,13 +102,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() register_device(S.loc.name) initialize_exonet(S.loc) -// Proc: examine() -// Parameters: user - the user doing the examining -// Description: Allows the user to click a link when examining to look at video if one is going. -/obj/item/device/communicator/examine(mob/user) - . = ..() - if(Adjacent(user) && video_source) - . += "It looks like it's on a video call: \[view\]" // Proc: initialize_exonet() // Parameters: 1 (user - the person the communicator belongs to) @@ -132,6 +123,9 @@ var/global/list/obj/item/device/communicator/all_communicators = list() // Description: Shows all the voice mobs inside the device, and their status. /obj/item/device/communicator/examine(mob/user) . = ..() + + if(Adjacent(user) && video_source) + . += "It looks like it's on a video call: \[view\]" for(var/mob/living/voice/voice in contents) . += "On the screen, you can see a image feed of [voice]." @@ -148,6 +142,17 @@ var/global/list/obj/item/device/communicator/all_communicators = list() else . += "The device doesn't appear to be transmitting any data." +// Proc: Topic() +// Parameters: href, href_list - Data from a link +// Description: Used by the above examine. +/obj/item/device/communicator/Topic(href, href_list) + if(..()) + return 1 + + if(href_list["watchvideo"]) + if(video_source) + watch_video(usr) + // Proc: emp_act() // Parameters: None // Description: Drops all calls when EMPed, so the holder can then get murdered by the antagonist. @@ -203,17 +208,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() if(!get_connection_to_tcomms()) close_connection(reason = "Connection timed out") -// Proc: attack() -// Parameters: 2 (M - what is being attacked. user - the mob that has the communicator) -// Description: When the communicator has an attached commcard with internal devices, relay the attack() through to those devices. -// Contents of the for loop are copied from gripper code, because that does approximately what we want to do. -/obj/item/device/communicator/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(cartridge && cartridge.active_devices) - for(var/obj/item/wrapped in cartridge.active_devices) - if(wrapped) //The force of the wrapped obj gets set to zero during the attack() and afterattack(). - wrapped.attack(M,user) - return 0 - // Proc: attackby() // Parameters: 2 (C - what is used on the communicator. user - the mob that has the communicator) // Description: When an ID is swiped on the communicator, the communicator reads the job and checks it against the Owner name, if success, the occupation is added. @@ -229,13 +223,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() occupation = idcard.assignment to_chat(user, "Occupation updated.") - if(istype(C, /obj/item/weapon/commcard) && !cartridge) - cartridge = C - user.drop_item() - cartridge.forceMove(src) - to_chat(usr, "You slot \the [cartridge] into \the [src].") - modules[++modules.len] = list("module" = "External Device", "icon" = "external64", "number" = EXTRTAB) - SSnanoui.update_uis(src) // update all UIs attached to src return // Proc: attack_self() @@ -246,7 +233,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list() initialize_exonet(user) alert_called = 0 update_icon() - ui_interact(user) + tgui_interact(user) if(video_source) watch_video(user) @@ -358,38 +345,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list() client_huds |= global_hud.whitense client_huds |= global_hud.darkMask -/obj/item/device/communicator/verb/verb_remove_cartridge() - set category = "Object" - set name = "Remove commcard" - set src in usr - - // Can't remove what isn't there - if(!cartridge) - to_chat(usr, "There isn't a commcard to remove!") - return - - // Can't remove if you're physically unable to - if(usr.stat || usr.restrained() || usr.paralysis || usr.stunned || usr.weakened) - to_chat(usr, "You cannot do this while restrained.") - return - - var/turf/T = get_turf(src) - cartridge.loc = T - // If it's in someone, put the cartridge in their hands - if (ismob(loc)) - var/mob/M = loc - M.put_in_hands(cartridge) - // Else just set it on the ground - else - cartridge.loc = get_turf(src) - cartridge = null - // We have to iterate through the modules to find EXTRTAB, because list procs don't play nice with a list of lists - for(var/i = 1, i <= modules.len, i++) - if(modules[i]["number"] == EXTRTAB) - modules.Cut(i, i+1) - break - to_chat(usr, "You remove \the [cartridge] from the [name].") - //It's the 26th century. We should have smart watches by now. /obj/item/device/communicator/watch name = "communicator watch" diff --git a/code/game/objects/items/devices/communicator/helper.dm b/code/game/objects/items/devices/communicator/helper.dm index ec67686668..b406100456 100644 --- a/code/game/objects/items/devices/communicator/helper.dm +++ b/code/game/objects/items/devices/communicator/helper.dm @@ -20,7 +20,7 @@ // Values were extracted from the template itself results = list( list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), - list("entry" = "Temperature", "units" = "°C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Temperature", "units" = "°C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), @@ -44,19 +44,18 @@ var/index = 0 for(var/datum/feed_message/FM in channel.messages) index++ + var/list/msgdata = list( + "author" = FM.author, + "body" = FM.body, + "img" = null, + "message_type" = FM.message_type, + "time_stamp" = FM.time_stamp, + "caption" = FM.caption, + "index" = index + ) if(FM.img) - usr << browse_rsc(FM.img, "pda_news_tmp_photo_[feeds["channel"]]_[index].png") - // News stories are HTML-stripped but require newline replacement to be properly displayed in NanoUI - var/body = replacetext(FM.body, "\n", "
") - messages[++messages.len] = list( - "author" = FM.author, - "body" = body, - "message_type" = FM.message_type, - "time_stamp" = FM.time_stamp, - "has_image" = (FM.img != null), - "caption" = FM.caption, - "index" = index - ) + msgdata["img"] = icon2base64(FM.img) + messages[++messages.len] = msgdata feeds[++feeds.len] = list( "name" = channel.channel_name, @@ -96,448 +95,3 @@ news.Swap(1, 3) // List is sorted in ascending order of timestamp, we want descending return news - - - -// Putting the commcard data harvesting helpers here -// Not ideal to put all the procs on the base type -// but it may open options for adminbus, -// And it saves duplicated code - - -// Medical records -/obj/item/weapon/commcard/proc/get_med_records() - var/med_records[0] - for(var/datum/data/record/M in sortRecord(data_core.medical)) - var/record[0] - record[++record.len] = list("tab" = "Name", "val" = M.fields["name"]) - record[++record.len] = list("tab" = "ID", "val" = M.fields["id"]) - record[++record.len] = list("tab" = "Blood Type", "val" = M.fields["b_type"]) - record[++record.len] = list("tab" = "DNA #", "val" = M.fields["b_dna"]) - record[++record.len] = list("tab" = "Gender", "val" = M.fields["id_gender"]) - record[++record.len] = list("tab" = "Entity Classification", "val" = M.fields["brain_type"]) - record[++record.len] = list("tab" = "Minor Disorders", "val" = M.fields["mi_dis"]) - record[++record.len] = list("tab" = "Major Disorders", "val" = M.fields["ma_dis"]) - record[++record.len] = list("tab" = "Allergies", "val" = M.fields["alg"]) - record[++record.len] = list("tab" = "Condition", "val" = M.fields["cdi"]) - record[++record.len] = list("tab" = "Notes", "val" = M.fields["notes"]) - - med_records[++med_records.len] = list("name" = M.fields["name"], "record" = record) - return med_records - - -// Employment records -/obj/item/weapon/commcard/proc/get_emp_records() - var/emp_records[0] - for(var/datum/data/record/G in sortRecord(data_core.general)) - var/record[0] - record[++record.len] = list("tab" = "Name", "val" = G.fields["name"]) - record[++record.len] = list("tab" = "ID", "val" = G.fields["id"]) - record[++record.len] = list("tab" = "Rank", "val" = G.fields["rank"]) - record[++record.len] = list("tab" = "Fingerprint", "val" = G.fields["fingerprint"]) - record[++record.len] = list("tab" = "Entity Classification", "val" = G.fields["brain_type"]) - record[++record.len] = list("tab" = "Sex", "val" = G.fields["sex"]) - record[++record.len] = list("tab" = "Species", "val" = G.fields["species"]) - record[++record.len] = list("tab" = "Age", "val" = G.fields["age"]) - record[++record.len] = list("tab" = "Notes", "val" = G.fields["notes"]) - - emp_records[++emp_records.len] = list("name" = G.fields["name"], "record" = record) - return emp_records - - -// Security records -/obj/item/weapon/commcard/proc/get_sec_records() - var/sec_records[0] - for(var/datum/data/record/G in sortRecord(data_core.general)) - var/record[0] - record[++record.len] = list("tab" = "Name", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Sex", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Species", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Age", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Rank", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Fingerprint", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Physical Status", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Mental Status", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Criminal Status", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Major Crimes", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Minor Crimes", "val" = G.fields[""]) - record[++record.len] = list("tab" = "Notes", "val" = G.fields["notes"]) - - sec_records[++sec_records.len] = list("name" = G.fields["name"], "record" = record) - return sec_records - - -// Status of all secbots -// Weaker than what PDAs appear to do, but as of 7/1/2018 PDA secbot access is nonfunctional -/obj/item/weapon/commcard/proc/get_sec_bot_access() - var/sec_bots[0] - for(var/mob/living/bot/secbot/S in mob_list) - // Get new bot - var/status[0] - status[++status.len] = list("tab" = "Name", "val" = sanitize(S.name)) - - // If it's turned off, then it shouldn't be broadcasting any further info - if(!S.on) - status[++status.len] = list("tab" = "Power", "val" = "Off") // Encoding the span classes here so I don't have to do complicated switches in the ui template - continue - status[++status.len] = list("tab" = "Power", "val" = "On") - - // -- What it's doing - // If it's engaged, then say who it thinks it's engaging - if(S.target) - status[++status.len] = list("tab" = "Status", "val" = "Apprehending Target") - status[++status.len] = list("tab" = "Target", "val" = S.target_name(S.target)) - // Else if it's patrolling - else if(S.will_patrol) - status[++status.len] = list("tab" = "Status", "val" = "Patrolling") - // Otherwise we don't know what it's doing - else - status[++status.len] = list("tab" = "Status", "val" = "Idle") - - // Where it is - status[++status.len] = list("tab" = "Location", "val" = sanitize("[get_area(S.loc)]")) - - // Append bot to the list - sec_bots[++sec_bots.len] = list("bot" = S.name, "status" = status) - return sec_bots - - -// Code and frequency of stored signalers -// Supports multiple signalers within the device -/obj/item/weapon/commcard/proc/get_int_signalers() - var/signalers[0] - for(var/obj/item/device/assembly/signaler/S in internal_devices) - var/unit[0] - unit[++unit.len] = list("tab" = "Code", "val" = S.code) - unit[++unit.len] = list("tab" = "Frequency", "val" = S.frequency) - - signalers[++signalers.len] = list("ref" = "\ref[S]", "status" = unit) - - return signalers - -// Returns list of all powernet sensors currently visible to the commcard -/obj/item/weapon/commcard/proc/find_powernet_sensors() - var/grid_sensors[0] - - // Find all the powernet sensors we need to pull data from - // Copied from /datum/nano_module/power_monitor/proc/refresh_sensors(), - // located in '/code/modules/nano/modules/power_monitor.dm' - // Minor tweaks for efficiency and cleanliness - var/turf/T = get_turf(src) - if(T) - var/list/levels = using_map.get_map_levels(T.z, FALSE) - for(var/obj/machinery/power/sensor/S in machines) - if((S.long_range) || (S.loc.z in levels) || (S.loc.z == T.z)) // Consoles have range on their Z-Level. Sensors with long_range var will work between Z levels. - if(S.name_tag == "#UNKN#") // Default name. Shouldn't happen! - warning("Powernet sensor with unset ID Tag! [S.x]X [S.y]Y [S.z]Z") - else - grid_sensors += S - return grid_sensors - -// List of powernets -/obj/item/weapon/commcard/proc/get_powernet_monitoring_list() - // Fetch power monitor data - var/sensors[0] - - for(var/obj/machinery/power/sensor/S in internal_data["grid_sensors"]) - var/list/focus = S.return_reading_data() - - sensors[++sensors.len] = list( - "name" = S.name_tag, - "alarm" = focus["alarm"] - ) - - return sensors - -// Information about the targeted powernet -/obj/item/weapon/commcard/proc/get_powernet_target(var/target_sensor) - if(!target_sensor) - return - - var/powernet_target[0] - - for(var/obj/machinery/power/sensor/S in internal_data["grid_sensors"]) - var/list/focus = S.return_reading_data() - - // Packages the span class here so it doesn't need to be interpreted w/in the for loop in the ui template - var/load_stat = "Optimal" - if(focus["load_percentage"] >= 95) - load_stat = "DANGER: Overload" - else if(focus["load_percentage"] >= 85) - load_stat = "WARNING: High Load" - - var/alarm_stat = focus["alarm"] ? "WARNING: Abnormal activity detected!" : "Secure" - - if(target_sensor == S.name_tag) - powernet_target = list( - "name" = S.name_tag, - "alarm" = focus["alarm"], - "error" = focus["error"], - "apc_data" = focus["apc_data"], - "status" = list( - list("field" = "Network Load Status", "statval" = load_stat), - list("field" = "Network Security Status", "statval" = alarm_stat), - list("field" = "Load Percentage", "statval" = focus["load_percentage"]), - list("field" = "Available Power", "statval" = focus["total_avail"]), - list("field" = "APC Power Usage", "statval" = focus["total_used_apc"]), - list("field" = "Other Power Usage", "statval" = focus["total_used_other"]), - list("field" = "Total Usage", "statval" = focus["total_used_all"]) - ) - ) - - return powernet_target - -// Compiles the locations of all janitorial paraphernalia, as used by janitorialLocator.tmpl -/obj/item/weapon/commcard/proc/get_janitorial_locations() - // Fetch janitorial locator - var/janidata[0] - var/list/cleaningList = list() - cleaningList += all_mops + all_mopbuckets + all_janitorial_carts - - // User's location - var/turf/userloc = get_turf(src) - if(isturf(userloc)) - janidata[++janidata.len] = list("field" = "Current Location", "val" = "[userloc.x], [userloc.y], [using_map.get_zlevel_name(userloc.z)]") - else - janidata[++janidata.len] = list("field" = "Current Location", "val" = "Unknown") - return janidata // If the user isn't on a valid turf, then it shouldn't be able to find anything anyways - - // Mops, mop buckets, janitorial carts. - for(var/obj/C in cleaningList) - var/turf/T = get_turf(C) - if(isturf(T) )//&& T.z in using_map.get_map_levels(userloc, FALSE)) - if(T.z == userloc.z) - janidata[++janidata.len] = list("field" = apply_text_macros("\proper [C.name]"), "val" = "[T.x], [T.y], [using_map.get_zlevel_name(T.z)]") - else - janidata[++janidata.len] = list("field" = apply_text_macros("\proper [C.name]"), "val" = "[T.x], [T.y], [using_map.get_zlevel_name(T.z)]") - - // Cleanbots - for(var/mob/living/bot/cleanbot/B in living_mob_list) - var/turf/T = get_turf(B) - if(isturf(T) )//&& T.z in using_map.get_map_levels(userloc, FALSE)) - var/textout = "" - if(B.on) - textout += "Status: Online
" - if(T.z == userloc.z) - textout += "[T.x], [T.y], [using_map.get_zlevel_name(T.z)]" - else - textout += "[T.x], [T.y], [using_map.get_zlevel_name(T.z)]" - else - textout += "Status: Offline" - - janidata[++janidata.len] = list("field" = "[B.name]", "val" = textout) - - return janidata - -// Compiles the three lists used by GPS_access.tmpl -// The contents of the three lists are inherently related, so separating them into different procs would be largely redundant -/obj/item/weapon/commcard/proc/get_GPS_lists() - // GPS Access - var/intgps[0] // Gps devices within the commcard -- Allow tag edits, turning on/off, etc - var/extgps[0] // Gps devices not inside the commcard -- Print locations if a gps is on - var/stagps[0] // Gps net status, location, whether it's on, if it's got long range - var/obj/item/device/gps/cumulative = new(src) - cumulative.tracking = FALSE - cumulative.local_mode = TRUE // Won't detect long-range signals automatically - cumulative.long_range = FALSE - var/list/toggled_gps = list() // List of GPS units that are turned off before display_list() is called - - for(var/obj/item/device/gps/G in internal_devices) - var/gpsdata[0] - if(G.tracking && !G.emped) - cumulative.tracking = TRUE // Turn it on - if(G.long_range) - cumulative.long_range = TRUE // It can detect long-range - if(!G.local_mode) - cumulative.local_mode = FALSE // It is detecting long-range - - gpsdata["ref"] = "\ref[G]" - gpsdata["tag"] = G.gps_tag - gpsdata["power"] = G.tracking - gpsdata["local_mode"] = G.local_mode - gpsdata["long_range"] = G.long_range - gpsdata["hide_signal"] = G.hide_signal - gpsdata["can_hide"] = G.can_hide_signal - - intgps[++intgps.len] = gpsdata // Add it to the list - - if(G.tracking) - G.tracking = FALSE // Disable the internal gps units so they don't show up in the report - toggled_gps += G - - var/list/remote_gps = cumulative.display_list() // Fetch information for all units except the ones inside of this device - - for(var/obj/item/device/gps/G in toggled_gps) // Reenable any internal GPS units - G.tracking = TRUE - - stagps["enabled"] = cumulative.tracking - stagps["long_range_en"] = (cumulative.long_range && !cumulative.local_mode) - - stagps["my_area_name"] = remote_gps["my_area_name"] - stagps["curr_x"] = remote_gps["curr_x"] - stagps["curr_y"] = remote_gps["curr_y"] - stagps["curr_z"] = remote_gps["curr_z"] - stagps["curr_z_name"] = remote_gps["curr_z_name"] - - extgps = remote_gps["gps_list"] // Compiled by the GPS - - qdel(cumulative) // Don't want spare GPS units building up in the contents - - return list( - intgps, - extgps, - stagps - ) - -// Collects the current status of the supply shuttle -// Copied from /obj/machinery/computer/supplycomp/ui_interact(), -// code\game\machinery\computer\supply.dm, starting at line 55 -/obj/item/weapon/commcard/proc/get_supply_shuttle_status() - var/shuttle_status[0] - var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle - if(shuttle) - if(shuttle.has_arrive_time()) - shuttle_status["location"] = "In transit" - shuttle_status["mode"] = SUP_SHUTTLE_TRANSIT - shuttle_status["time"] = shuttle.eta_minutes() - - else - shuttle_status["time"] = 0 - if(shuttle.at_station()) - if(shuttle.shuttle_docking_controller) - switch(shuttle.shuttle_docking_controller.get_docking_status()) - if("docked") - shuttle_status["location"] = "Docked" - shuttle_status["mode"] = SUP_SHUTTLE_DOCKED - if("undocked") - shuttle_status["location"] = "Undocked" - shuttle_status["mode"] = SUP_SHUTTLE_UNDOCKED - if("docking") - shuttle_status["location"] = "Docking" - shuttle_status["mode"] = SUP_SHUTTLE_DOCKING - shuttle_status["force"] = shuttle.can_force() - if("undocking") - shuttle_status["location"] = "Undocking" - shuttle_status["mode"] = SUP_SHUTTLE_UNDOCKING - shuttle_status["force"] = shuttle.can_force() - - else - shuttle_status["location"] = "Station" - shuttle_status["mode"] = SUP_SHUTTLE_DOCKED - - else - shuttle_status["location"] = "Away" - shuttle_status["mode"] = SUP_SHUTTLE_AWAY - - if(shuttle.can_launch()) - shuttle_status["launch"] = 1 - else if(shuttle.can_cancel()) - shuttle_status["launch"] = 2 - else - shuttle_status["launch"] = 0 - - switch(shuttle.moving_status) - if(SHUTTLE_IDLE) - shuttle_status["engine"] = "Idle" - if(SHUTTLE_WARMUP) - shuttle_status["engine"] = "Warming up" - if(SHUTTLE_INTRANSIT) - shuttle_status["engine"] = "Engaged" - - else - shuttle["mode"] = SUP_SHUTTLE_ERROR - - return shuttle_status - -// Compiles the list of supply orders -// Copied from /obj/machinery/computer/supplycomp/ui_interact(), -// code\game\machinery\computer\supply.dm, starting at line 130 -/obj/item/weapon/commcard/proc/get_supply_orders() - var/orders[0] - for(var/datum/supply_order/S in SSsupply.order_history) - orders[++orders.len] = list( - "ref" = "\ref[S]", - "status" = S.status, - "entries" = list( - list("field" = "Supply Pack", "entry" = S.name), - list("field" = "Cost", "entry" = S.cost), - list("field" = "Index", "entry" = S.index), - list("field" = "Reason", "entry" = S.comment), - list("field" = "Ordered by", "entry" = S.ordered_by), - list("field" = "Ordered at", "entry" = S.ordered_at), - list("field" = "Approved by", "entry" = S.approved_by), - list("field" = "Approved at", "entry" = S.approved_at) - ) - ) - - return orders - -// Compiles the list of supply export receipts -// Copied from /obj/machinery/computer/supplycomp/ui_interact(), -// code\game\machinery\computer\supply.dm, starting at line 147 -/obj/item/weapon/commcard/proc/get_supply_receipts() - var/receipts[0] - for(var/datum/exported_crate/E in SSsupply.exported_crates) - receipts[++receipts.len] = list( - "ref" = "\ref[E]", - "contents" = E.contents, - "error" = E.contents["error"], - "title" = list( - list("field" = "Name", "entry" = E.name), - list("field" = "Value", "entry" = E.value) - ) - ) - return receipts - - -// Compiles the list of supply packs for the category currently stored in internal_data["supply_category"] -// Copied from /obj/machinery/computer/supplycomp/ui_interact(), -// code\game\machinery\computer\supply.dm, starting at line 147 -/obj/item/weapon/commcard/proc/get_supply_pack_list() - var/supply_packs[0] - for(var/pack_name in SSsupply.supply_pack) - var/datum/supply_pack/P = SSsupply.supply_pack[pack_name] - if(P.group == internal_data["supply_category"]) - var/list/pack = list( - "name" = P.name, - "cost" = P.cost, - "contraband" = P.contraband, - "manifest" = uniquelist(P.manifest), - "random" = P.num_contained, - "expand" = 0, - "ref" = "\ref[P]" - ) - - if(P in internal_data["supply_pack_expanded"]) - pack["expand"] = 1 - - supply_packs[++supply_packs.len] = pack - - return supply_packs - - -// Compiles miscellaneous data and permissions used by the supply template -/obj/item/weapon/commcard/proc/get_misc_supply_data() - return list( - "shuttle_auth" = (internal_data["supply_controls"] & SUP_SEND_SHUTTLE), - "order_auth" = (internal_data["supply_controls"] & SUP_ACCEPT_ORDERS), - "supply_points" = SSsupply.points, - "supply_categories" = all_supply_groups - ) - -/obj/item/weapon/commcard/proc/get_status_display() - return list( - "line1" = internal_data["stat_display_line1"], - "line2" = internal_data["stat_display_line2"], - "active_line1" = internal_data["stat_display_active1"], - "active_line2" = internal_data["stat_display_active2"], - "active" = internal_data["stat_display_special"] - ) - -/obj/item/weapon/commcard/proc/find_blast_doors() - var/target_doors[0] - for(var/obj/machinery/door/blast/B in machines) - if(B.id == internal_data["shuttle_door_code"]) - target_doors += B - - return target_doors \ No newline at end of file diff --git a/code/game/objects/items/devices/hacktool.dm b/code/game/objects/items/devices/hacktool.dm index a48a3da024..b221ff875c 100644 --- a/code/game/objects/items/devices/hacktool.dm +++ b/code/game/objects/items/devices/hacktool.dm @@ -5,7 +5,7 @@ var/in_hack_mode = 0 var/list/known_targets var/list/supported_types - var/datum/topic_state/default/must_hack/hack_state + var/datum/tgui_state/default/must_hack/hack_state /obj/item/device/multitool/hacktool/New() ..() @@ -30,7 +30,7 @@ else ..() -/obj/item/device/multitool/hacktool/resolve_attackby(atom/A, mob/user) +/obj/item/device/multitool/hacktool/afterattack(atom/A, mob/user) sanity_check() if(!in_hack_mode) @@ -39,7 +39,8 @@ if(!attempt_hack(user, A)) return 0 - A.ui_interact(user, state = hack_state) + // Note, if you ever want to expand supported_types, you must manually add the custom state argument to their tgui_interact + A.tgui_interact(user, custom_state = hack_state) return 1 /obj/item/device/multitool/hacktool/proc/attempt_hack(var/mob/user, var/atom/target) @@ -83,18 +84,18 @@ /obj/item/device/multitool/hacktool/proc/on_target_destroy(var/target) known_targets -= target -/datum/topic_state/default/must_hack +/datum/tgui_state/default/must_hack var/obj/item/device/multitool/hacktool/hacktool -/datum/topic_state/default/must_hack/New(var/hacktool) +/datum/tgui_state/default/must_hack/New(var/hacktool) src.hacktool = hacktool ..() -/datum/topic_state/default/must_hack/Destroy() +/datum/tgui_state/default/must_hack/Destroy() hacktool = null return ..() -/datum/topic_state/default/must_hack/can_use_topic(var/src_object, var/mob/user) +/datum/tgui_state/default/must_hack/can_use_topic(src_object, mob/user) if(!hacktool || !hacktool.in_hack_mode || !(src_object in hacktool.known_targets)) return STATUS_CLOSE return ..() diff --git a/code/game/objects/items/devices/paicard.dm b/code/game/objects/items/devices/paicard.dm index 8b7f55e0a9..77e463c0d0 100644 --- a/code/game/objects/items/devices/paicard.dm +++ b/code/game/objects/items/devices/paicard.dm @@ -33,6 +33,24 @@ GLOBAL_LIST_BOILERPLATE(all_pai_cards, /obj/item/device/paicard) QDEL_NULL(radio) return ..() +/obj/item/device/paicard/attack_ghost(mob/observer/dead/user) + if(istype(user) && user.can_admin_interact()) + switch(alert(user, "Would you like to become a pAI by force? (Admin)", "pAI Creation", "Yes", "No")) + if("Yes") + // Copied from paiController/Topic + var/mob/living/silicon/pai/pai = new(src) + pai.name = user.name + pai.real_name = pai.name + pai.key = user.key + + setPersonality(pai) + looking_for_personality = FALSE + + if(pai.mind) + update_antag_icons(pai.mind) + return ..() + + /obj/item/device/paicard/attack_self(mob/user) if (!in_range(src, user)) return diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index f1990c7fe2..d2511038dc 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -151,18 +151,10 @@ var/global/list/default_medbay_channels = list( return tgui_interact(user) -/obj/item/device/radio/ui_interact(mob/user, ui_key, datum/nanoui/ui, force_open, datum/nano_ui/master_ui, datum/topic_state/state) - log_runtime(EXCEPTION("Warning: [user] attempted to call ui_interact on radio [src] [type]. This is deprecated. Please update the caller to tgui_interact.")) - -/obj/item/device/radio/Topic(href, href_list) - if(href_list["track"]) - log_runtime(EXCEPTION("Warning: Topic() was improperly called on radio [src] [type], with the track href and \[[href] [json_encode(href_list)]]. Please update the caller to use tgui_act.")) - . = ..() - -/obj/item/device/radio/tgui_interact(mob/user, datum/tgui/ui) +/obj/item/device/radio/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "Radio", name) + ui = new(user, src, "Radio", name, parent_ui) ui.open() /obj/item/device/radio/tgui_data(mob/user) diff --git a/code/game/objects/items/devices/tvcamera.dm b/code/game/objects/items/devices/tvcamera.dm index e8b6df0b98..acf4b7cc3a 100644 --- a/code/game/objects/items/devices/tvcamera.dm +++ b/code/game/objects/items/devices/tvcamera.dm @@ -54,7 +54,7 @@ popup.set_content(jointext(dat,null)) popup.open() -/obj/item/device/tvcamera/Topic(bred, href_list, state = physical_state) +/obj/item/device/tvcamera/Topic(bred, href_list, state = GLOB.tgui_physical_state) if(..()) return 1 if(href_list["channel"]) diff --git a/code/game/objects/items/devices/uplink.dm b/code/game/objects/items/devices/uplink.dm index 5a19c72742..5b6e55290e 100644 --- a/code/game/objects/items/devices/uplink.dm +++ b/code/game/objects/items/devices/uplink.dm @@ -1,13 +1,23 @@ GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink) +// I placed this here because of how relevant it is. +// You place this in your uplinkable item to check if an uplink is active or not. +// If it is, it will display the uplink menu and return 1, else it'll return false. +// If it returns true, I recommend closing the item's normal menu with "user << browse(null, "window=name")" +/obj/item/proc/active_uplink_check(mob/user as mob) + // Activates the uplink if it's active + if(hidden_uplink) + if(hidden_uplink.active) + hidden_uplink.trigger(user) + return TRUE + return FALSE + /obj/item/device/uplink var/welcome = "Welcome, Operative" // Welcoming menu message var/uses // Numbers of crystals var/list/ItemsCategory // List of categories with lists of items var/list/ItemsReference // List of references with an associated item var/list/nanoui_items // List of items for NanoUI use - var/nanoui_menu = 0 // The current menu we are in - var/list/nanoui_data = new // Additional data for NanoUI use var/faction = "" //Antag faction holder. var/list/purchase_log = new @@ -17,9 +27,7 @@ GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink) var/next_offer_time var/datum/uplink_item/discount_item //The item to be discounted var/discount_amount //The amount as a percent the item will be discounted by - -/obj/item/device/uplink/nano_host() - return loc + var/compact_mode = FALSE /obj/item/device/uplink/Initialize(var/mapload, var/datum/mind/owner = null, var/telecrystals = DEFAULT_TELECRYSTAL_AMOUNT) . = ..() @@ -53,23 +61,20 @@ GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink) name = "hidden uplink" desc = "There is something wrong if you're examining this." var/active = 0 - var/datum/uplink_category/category = 0 // The current category we are in var/exploit_id // Id of the current exploit record we are viewing + var/selected_cat // The hidden uplink MUST be inside an obj/item's contents. /obj/item/device/uplink/hidden/Initialize() . = ..() if(!isitem(loc)) return INITIALIZE_HINT_QDEL - nanoui_data = list() - update_nano_data() /obj/item/device/uplink/hidden/next_offer() discount_item = default_uplink_selection.get_random_item(INFINITY) discount_amount = pick(90;0.9, 80;0.8, 70;0.7, 60;0.6, 50;0.5, 40;0.4, 30;0.3, 20;0.2, 10;0.1) - update_nano_data() - SSnanoui.update_uis(src) next_offer_time = world.time + offer_time + SStgui.update_uis(src) addtimer(CALLBACK(src, .proc/next_offer), offer_time) // Toggles the uplink on and off. Normally this will bypass the item's normal functions and go to the uplink menu, if activated. @@ -91,136 +96,137 @@ GLOBAL_LIST_BOILERPLATE(world_uplinks, /obj/item/device/uplink) return 1 return 0 -/* - NANO UI FOR UPLINK WOOP WOOP -*/ -/obj/item/device/uplink/hidden/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/title = "Remote Uplink" - var/data[0] - uses = user.mind.tcrystals - if(ishuman(user)) - var/mob/living/carbon/human/H = user - faction = H.antag_faction +// Legacy +/obj/item/device/uplink/hidden/interact(mob/user) + tgui_interact(user) - data["welcome"] = welcome - data["crystals"] = uses - data["menu"] = nanoui_menu - data += nanoui_data +/***************** + * Uplink TGUI + *****************/ +/obj/item/device/uplink/tgui_host() + return loc - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) // No auto-refresh - ui = new(user, src, ui_key, "uplink.tmpl", title, 630, 700, state = inventory_state) - data["menu"] = 0 - ui.set_initial_data(data) +/obj/item/device/uplink/hidden/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/uplink/hidden/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + if(!active) + toggle() + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Uplink", "Remote Uplink") + // This UI is only ever opened by one person, + // and never is updated outside of user input. + ui.set_autoupdate(FALSE) ui.open() +/obj/item/device/uplink/hidden/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + if(!user.mind) + return -// Interaction code. Gathers a list of items purchasable from the paren't uplink and displays it. It also adds a lock button. -/obj/item/device/uplink/hidden/interact(mob/user) - ui_interact(user) + var/list/data = ..() -/obj/item/device/uplink/hidden/CanUseTopic() + data["telecrystals"] = uses + data["lockable"] = TRUE + data["compactMode"] = compact_mode + + data["discount_name"] = discount_item ? discount_item.name : "" + data["discount_amount"] = (1-discount_amount)*100 + data["offer_expiry"] = worldtime2stationtime(next_offer_time) + + data["exploit"] = null + data["locked_records"] = null + + if(exploit_id) + for(var/datum/data/record/L in data_core.locked) + if(L.fields["id"] == exploit_id) + data["exploit"] = list() // Setting this to equal L.fields passes it's variables that are lists as reference instead of value. + // We trade off being able to automatically add shit for more control over what gets passed to json + // and if it's sanitized for html. + data["exploit"]["nanoui_exploit_record"] = html_encode(L.fields["exploit_record"]) // Change stuff into html + data["exploit"]["nanoui_exploit_record"] = replacetext(data["exploit"]["nanoui_exploit_record"], "\n", "
") // change line breaks into
+ data["exploit"]["name"] = html_encode(L.fields["name"]) + data["exploit"]["sex"] = html_encode(L.fields["sex"]) + data["exploit"]["age"] = html_encode(L.fields["age"]) + data["exploit"]["species"] = html_encode(L.fields["species"]) + data["exploit"]["rank"] = html_encode(L.fields["rank"]) + data["exploit"]["home_system"] = html_encode(L.fields["home_system"]) + data["exploit"]["citizenship"] = html_encode(L.fields["citizenship"]) + data["exploit"]["faction"] = html_encode(L.fields["faction"]) + data["exploit"]["religion"] = html_encode(L.fields["religion"]) + data["exploit"]["fingerprint"] = html_encode(L.fields["fingerprint"]) + if(L.fields["antagvis"] == ANTAG_KNOWN || (faction == L.fields["antagfac"] && (L.fields["antagvis"] == ANTAG_SHARED))) + data["exploit"]["antagfaction"] = html_encode(L.fields["antagfac"]) + else + data["exploit"]["antagfaction"] = html_encode("None") + break + else + var/list/permanentData = list() + for(var/datum/data/record/L in sortRecord(data_core.locked)) + permanentData.Add(list(list( + "name" = L.fields["name"], + "id" = L.fields["id"] + ))) + data["locked_records"] = permanentData + + return data + +/obj/item/device/uplink/hidden/tgui_static_data(mob/user) + var/list/data = ..() + + data["categories"] = list() + for(var/datum/uplink_category/category in uplink.categories) + if(category.can_view(src)) + var/list/cat = list( + "name" = category.name, + "items" = (category == selected_cat ? list() : null) + ) + for(var/datum/uplink_item/item in category.items) + if(!item.can_view(src)) + continue + var/cost = item.cost(uses, src) || "???" + cat["items"] += list(list( + "name" = item.name, + "cost" = cost, + "desc" = item.description(), + "ref" = REF(item), + )) + data["categories"] += list(cat) + + return data + +/obj/item/device/uplink/hidden/tgui_status(mob/user, datum/tgui_state/state) if(!active) return STATUS_CLOSE return ..() -// The purchasing code. -/obj/item/device/uplink/hidden/Topic(href, href_list) +/obj/item/device/uplink/hidden/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 + return TRUE - var/mob/user = usr - if(href_list["buy_item"]) - var/datum/uplink_item/UI = (locate(href_list["buy_item"]) in uplink.items) - UI.buy(src, usr) - else if(href_list["lock"]) - toggle() - var/datum/nanoui/ui = SSnanoui.get_open_ui(user, src, "main") - ui.close() - else if(href_list["return"]) - nanoui_menu = round(nanoui_menu/10) - else if(href_list["menu"]) - nanoui_menu = text2num(href_list["menu"]) - if(href_list["id"]) - exploit_id = href_list["id"] - else if(href_list["category"]) - category = locate(href_list["category"]) in uplink.categories - - update_nano_data() - return 1 - -/obj/item/device/uplink/hidden/proc/update_nano_data() - if(nanoui_menu == 0) - var/categories[0] - for(var/datum/uplink_category/category in uplink.categories) - if(category.can_view(src)) - categories[++categories.len] = list("name" = category.name, "ref" = "\ref[category]") - nanoui_data["categories"] = categories - nanoui_data["discount_name"] = discount_item ? discount_item.name : "" - nanoui_data["discount_amount"] = (1-discount_amount)*100 - nanoui_data["offer_expiry"] = worldtime2stationtime(next_offer_time) - - if(category) - nanoui_data["current_category"] = category.name - var/items[0] - for(var/datum/uplink_item/item in category.items) - if(item.can_view(src)) - var/cost = item.cost(uses, src) - if(!cost) cost = "???" - items[++items.len] = list("name" = item.name, "description" = replacetext(item.description(), "\n", "
"), "can_buy" = item.can_buy(src), "cost" = cost, "ref" = "\ref[item]") - nanoui_data["items"] = items - - else if(nanoui_menu == 2) - var/permanentData[0] - for(var/datum/data/record/L in sortRecord(data_core.locked)) - permanentData[++permanentData.len] = list(Name = L.fields["name"],"id" = L.fields["id"]) - nanoui_data["exploit_records"] = permanentData - else if(nanoui_menu == 21) - nanoui_data["exploit_exists"] = 0 - - for(var/datum/data/record/L in data_core.locked) - if(L.fields["id"] == exploit_id) - nanoui_data["exploit"] = list() // Setting this to equal L.fields passes it's variables that are lists as reference instead of value. - // We trade off being able to automatically add shit for more control over what gets passed to json - // and if it's sanitized for html. - nanoui_data["exploit"]["nanoui_exploit_record"] = html_encode(L.fields["exploit_record"]) // Change stuff into html - nanoui_data["exploit"]["nanoui_exploit_record"] = replacetext(nanoui_data["exploit"]["nanoui_exploit_record"], "\n", "
") // change line breaks into
- nanoui_data["exploit"]["name"] = html_encode(L.fields["name"]) - nanoui_data["exploit"]["sex"] = html_encode(L.fields["sex"]) - nanoui_data["exploit"]["age"] = html_encode(L.fields["age"]) - nanoui_data["exploit"]["species"] = html_encode(L.fields["species"]) - nanoui_data["exploit"]["rank"] = html_encode(L.fields["rank"]) - nanoui_data["exploit"]["home_system"] = html_encode(L.fields["home_system"]) - nanoui_data["exploit"]["citizenship"] = html_encode(L.fields["citizenship"]) - nanoui_data["exploit"]["faction"] = html_encode(L.fields["faction"]) - nanoui_data["exploit"]["religion"] = html_encode(L.fields["religion"]) - nanoui_data["exploit"]["fingerprint"] = html_encode(L.fields["fingerprint"]) - if(L.fields["antagvis"] == ANTAG_KNOWN || (faction == L.fields["antagfac"] && (L.fields["antagvis"] == ANTAG_SHARED))) - nanoui_data["exploit"]["antagfaction"] = html_encode(L.fields["antagfac"]) - else - nanoui_data["exploit"]["antagfaction"] = html_encode("None") - nanoui_data["exploit_exists"] = 1 - break - -// I placed this here because of how relevant it is. -// You place this in your uplinkable item to check if an uplink is active or not. -// If it is, it will display the uplink menu and return 1, else it'll return false. -// If it returns true, I recommend closing the item's normal menu with "user << browse(null, "window=name")" -/obj/item/proc/active_uplink_check(mob/user as mob) - // Activates the uplink if it's active - if(src.hidden_uplink) - if(src.hidden_uplink.active) - src.hidden_uplink.trigger(user) - return 1 - return 0 + switch(action) + if("buy") + var/datum/uplink_item/UI = (locate(params["ref"]) in uplink.items) + UI.buy(src, usr) + return TRUE + if("lock") + toggle() + SStgui.close_uis(src) + if("select") + selected_cat = params["category"] + return TRUE + if("compact_toggle") + compact_mode = !compact_mode + return TRUE + if("view_exploits") + exploit_id = params["id"] + return TRUE // PRESET UPLINKS // A collection of preset uplinks. // // Includes normal radio uplink, multitool uplink, // implant uplink (not the implant tool) and a preset headset uplink. - /obj/item/device/radio/uplink/New(atom/loc, datum/mind/target_mind, telecrystals) ..(loc) hidden_uplink = new(src, target_mind, telecrystals) diff --git a/code/game/objects/items/stacks/sheets/leather.dm b/code/game/objects/items/stacks/sheets/leather.dm index 782df54346..8bf366268a 100644 --- a/code/game/objects/items/stacks/sheets/leather.dm +++ b/code/game/objects/items/stacks/sheets/leather.dm @@ -4,8 +4,8 @@ singular_name = "human skin piece" icon_state = "sheet-hide" no_variants = FALSE - drop_sound = 'sound/items/drop/cloth.ogg' - pickup_sound = 'sound/items/pickup/cloth.ogg' + drop_sound = 'sound/items/drop/leather.ogg' + pickup_sound = 'sound/items/pickup/leather.ogg' /obj/item/stack/animalhide/human amount = 50 diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index ad187265c2..0c2cf53961 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -66,63 +66,81 @@ else . += "There is enough charge for [get_amount()]." -/obj/item/stack/attack_self(mob/user as mob) - list_recipes(user) +/obj/item/stack/attack_self(mob/user) + tgui_interact(user) -/obj/item/stack/proc/list_recipes(mob/user as mob, recipes_sublist) - if (!recipes) - return - if (!src || get_amount() <= 0) - user << browse(null, "window=stack") - user.set_machine(src) //for correct work of onclose - var/list/recipe_list = recipes - if (recipes_sublist && recipe_list[recipes_sublist] && istype(recipe_list[recipes_sublist], /datum/stack_recipe_list)) - var/datum/stack_recipe_list/srl = recipe_list[recipes_sublist] - recipe_list = srl.recipes - var/t1 = text("Constructions from []Amount Left: []
", src, src.get_amount()) - for(var/i=1;i<=recipe_list.len,i++) - var/E = recipe_list[i] - if (isnull(E)) - t1 += "
" - continue +/obj/item/stack/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Stack", name) + ui.open() - if (i>1 && !isnull(recipe_list[i-1])) - t1+="
" +/obj/item/stack/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["amount"] = get_amount() - if (istype(E, /datum/stack_recipe_list)) - var/datum/stack_recipe_list/srl = E - t1 += "[srl.title]" + return data - if (istype(E, /datum/stack_recipe)) - var/datum/stack_recipe/R = E - var/max_multiplier = round(src.get_amount() / R.req_amount) - var/title as text - var/can_build = 1 - can_build = can_build && (max_multiplier>0) - if (R.res_amount>1) - title+= "[R.res_amount]x [R.title]\s" - else - title+= "[R.title]" - title+= " ([R.req_amount] [src.singular_name]\s)" - if (can_build) - t1 += text("[title] ") - else - t1 += text("[]", title) - continue - if (R.max_res_amount>1 && max_multiplier>1) - max_multiplier = min(max_multiplier, round(R.max_res_amount/R.res_amount)) - t1 += " |" - var/list/multipliers = list(5,10,25) - for (var/n in multipliers) - if (max_multiplier>=n) - t1 += " [n*R.res_amount]x" - if (!(max_multiplier in multipliers)) - t1 += " [max_multiplier*R.res_amount]x" +/obj/item/stack/tgui_static_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() - t1 += "
" - user << browse(t1, "window=stack") - onclose(user, "stack") - return + data["recipes"] = recursively_build_recipes(recipes) + + return data + +/obj/item/stack/proc/recursively_build_recipes(list/recipe_to_iterate) + var/list/L = list() + for(var/recipe in recipe_to_iterate) + if(istype(recipe, /datum/stack_recipe_list)) + var/datum/stack_recipe_list/R = recipe + L["[R.title]"] = recursively_build_recipes(R.recipes) + if(istype(recipe, /datum/stack_recipe)) + var/datum/stack_recipe/R = recipe + L["[R.title]"] = build_recipe(R) + + return L + +/obj/item/stack/proc/build_recipe(datum/stack_recipe/R) + return list( + "res_amount" = R.res_amount, + "max_res_amount" = R.max_res_amount, + "req_amount" = R.req_amount, + "ref" = "\ref[R]", + ) + +/obj/item/stack/tgui_state(mob/user) + return GLOB.tgui_hands_state + +/obj/item/stack/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("make") + if(get_amount() < 1) + qdel(src) + return + + var/datum/stack_recipe/R = locate(params["ref"]) + if(!is_valid_recipe(R, recipes)) //href exploit protection + return FALSE + var/multiplier = text2num(params["multiplier"]) + if(!multiplier || (multiplier <= 0)) //href exploit protection + return + produce_recipe(R, multiplier, usr) + return TRUE + +/obj/item/stack/proc/is_valid_recipe(datum/stack_recipe/R, list/recipe_list) + for(var/S in recipe_list) + if(S == R) + return TRUE + if(istype(S, /datum/stack_recipe_list)) + var/datum/stack_recipe_list/L = S + if(is_valid_recipe(R, L.recipes)) + return TRUE + + return FALSE /obj/item/stack/proc/produce_recipe(datum/stack_recipe/recipe, var/quantity, mob/user) var/required = quantity*recipe.req_amount @@ -177,35 +195,6 @@ else O.color = color -/obj/item/stack/Topic(href, href_list) - ..() - if ((usr.restrained() || usr.stat || usr.get_active_hand() != src)) - return - - if (href_list["sublist"] && !href_list["make"]) - list_recipes(usr, text2num(href_list["sublist"])) - - if (href_list["make"]) - if (src.get_amount() < 1) qdel(src) //Never should happen - - var/list/recipes_list = recipes - if (href_list["sublist"]) - var/datum/stack_recipe_list/srl = recipes_list[text2num(href_list["sublist"])] - recipes_list = srl.recipes - - var/datum/stack_recipe/R = recipes_list[text2num(href_list["make"])] - var/multiplier = text2num(href_list["multiplier"]) - if (!multiplier || (multiplier <= 0)) //href exploit protection - return - - src.produce_recipe(R, multiplier, usr) - - if (src && usr.machine==src) //do not reopen closed window - spawn( 0 ) - src.interact(usr) - return - return - //Return 1 if an immediate subsequent call to use() would succeed. //Ensures that code dealing with stacks uses the same logic /obj/item/stack/proc/can_use(var/used) diff --git a/code/game/objects/items/stacks/tickets.dm b/code/game/objects/items/stacks/tickets.dm new file mode 100644 index 0000000000..1d9cbf047e --- /dev/null +++ b/code/game/objects/items/stacks/tickets.dm @@ -0,0 +1,32 @@ +/obj/item/stack/arcadeticket + name = "arcade tickets" + desc = "Wow! With enough of these, you could buy a bike! ...Pssh, yeah right." + singular_name = "arcade ticket" + icon_state = "arcade-ticket" + item_state = "tickets" + w_class = ITEMSIZE_TINY + max_amount = 30 + +/obj/item/stack/arcadeticket/New(loc, amount = null) + . = ..() + update_icon() + +/obj/item/stack/arcadeticket/update_icon() + var/amount = get_amount() + switch(amount) + if(12 to INFINITY) + icon_state = "arcade-ticket_4" + if(6 to 12) + icon_state = "arcade-ticket_3" + if(2 to 6) + icon_state = "arcade-ticket_2" + else + icon_state = "arcade-ticket" + +/obj/item/stack/arcadeticket/proc/pay_tickets() + amount -= 2 + if(amount == 0) + qdel(src) + +/obj/item/stack/arcadeticket/thirty + amount = 30 \ No newline at end of file diff --git a/code/game/objects/items/trash.dm b/code/game/objects/items/trash.dm index 73d3dd45e7..36c8c6e7ec 100644 --- a/code/game/objects/items/trash.dm +++ b/code/game/objects/items/trash.dm @@ -126,11 +126,13 @@ name = "empty cup" icon_state = "coffee_vended" drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' /obj/item/trash/ramen name = "cup ramen" icon_state = "ramen" drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' /obj/item/trash/tray name = "tray" @@ -141,6 +143,8 @@ name = "candle" icon = 'icons/obj/candle.dmi' icon_state = "candle4" + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' /obj/item/trash/liquidfood name = "\improper \"LiquidFood\" ration packet" @@ -162,18 +166,26 @@ /obj/item/trash/brownies name = "brownie tray" icon_state = "brownies" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' /obj/item/trash/snacktray name = "snacktray" icon_state = "snacktray" + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' /obj/item/trash/dipbowl name = "dip bowl" icon_state = "dipbowl" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' /obj/item/trash/chipbasket name = "empty basket" icon_state = "chipbasket_empty" + drop_sound = 'sound/items/drop/food.ogg' + pickup_sound = 'sound/items/pickup/food.ogg' /obj/item/trash/spitgum name = "old gum" @@ -181,12 +193,15 @@ icon = 'icons/obj/clothing/masks.dmi' icon_state = "spit-gum" drop_sound = 'sound/items/drop/flesh.ogg' + pickup_sound = 'sound/items/pickup/flesh.ogg' /obj/item/trash/lollibutt name = "lollipop stick" desc = "A lollipop stick devoid of pop." icon = 'icons/obj/clothing/masks.dmi' icon_state = "pop-stick" + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' /obj/item/trash/spitwad name = "spit wad" @@ -194,6 +209,7 @@ icon = 'icons/obj/clothing/masks.dmi' icon_state = "spit-chew" drop_sound = 'sound/items/drop/flesh.ogg' + pickup_sound = 'sound/items/pickup/flesh.ogg' slot_flags = SLOT_EARS | SLOT_MASK /obj/item/trash/attack(mob/M as mob, mob/living/user as mob) diff --git a/code/game/objects/items/weapons/augment_items.dm b/code/game/objects/items/weapons/augment_items.dm new file mode 100644 index 0000000000..c0df9c9688 --- /dev/null +++ b/code/game/objects/items/weapons/augment_items.dm @@ -0,0 +1,37 @@ +// **For augment items that aren't subtypes of other things.** + +/obj/item/weapon/melee/augment + name = "integrated item" + desc = "A surprisingly non-descript item, integrated into its user. You probably shouldn't be seeing this." + icon = 'icons/obj/surgery.dmi' + icon_state = "augment_box" + + +/obj/item/weapon/melee/augment/blade + name = "handblade" + desc = "A sleek-looking telescopic blade that fits inside the hand. Favored by infiltration specialists and assassins." + icon_state = "augment_handblade" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_melee.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_melee.dmi', + ) + w_class = ITEMSIZE_SMALL + force = 15 + armor_penetration = 25 + sharp = 1 + attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut") + defend_chance = 10 + projectile_parry_chance = 5 + hitsound = 'sound/weapons/bladeslice.ogg' + +/obj/item/weapon/melee/augment/blade/arm + name = "armblade" + desc = "A sleek-looking cybernetic blade that cleaves through people like butter. Favored by psychopaths and assassins." + icon_state = "augment_armblade" + w_class = ITEMSIZE_HUGE + force = 30 + armor_penetration = 15 + edge = 1 + pry = 1 + defend_chance = 40 + projectile_parry_chance = 20 \ No newline at end of file diff --git a/code/game/objects/items/weapons/autopsy.dm b/code/game/objects/items/weapons/autopsy.dm index 94a2636798..352a3955a3 100644 --- a/code/game/objects/items/weapons/autopsy.dm +++ b/code/game/objects/items/weapons/autopsy.dm @@ -14,6 +14,8 @@ var/list/datum/autopsy_data_scanner/chemtraces = list() var/target_name = null var/timeofdeath = null + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' /datum/autopsy_data_scanner var/weapon = null // this is the DEFINITE weapon type that was used diff --git a/code/game/objects/items/weapons/circuitboards/circuitboard.dm b/code/game/objects/items/weapons/circuitboards/circuitboard.dm index f049197d88..8b913eaf45 100644 --- a/code/game/objects/items/weapons/circuitboards/circuitboard.dm +++ b/code/game/objects/items/weapons/circuitboards/circuitboard.dm @@ -20,6 +20,8 @@ var/board_type = new /datum/frame/frame_types/computer var/list/req_components = null var/contain_parts = 1 + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' //Called when the circuitboard is used to contruct a new machine. /obj/item/weapon/circuitboard/proc/construct(var/obj/machinery/M) diff --git a/code/game/objects/items/weapons/flamethrower.dm b/code/game/objects/items/weapons/flamethrower.dm index 6cd2a65774..b2e783ba69 100644 --- a/code/game/objects/items/weapons/flamethrower.dm +++ b/code/game/objects/items/weapons/flamethrower.dm @@ -110,10 +110,6 @@ update_icon() return - if(istype(W, /obj/item/device/analyzer)) - var/obj/item/device/analyzer/A = W - A.analyze_gases(src, user) - return ..() return diff --git a/code/game/objects/items/weapons/id cards/syndicate_ids.dm b/code/game/objects/items/weapons/id cards/syndicate_ids.dm index 68cb0298a4..afb85ecb7a 100644 --- a/code/game/objects/items/weapons/id cards/syndicate_ids.dm +++ b/code/game/objects/items/weapons/id cards/syndicate_ids.dm @@ -6,8 +6,11 @@ var/electronic_warfare = 1 var/mob/registered_user = null + var/datum/tgui_module/agentcard/agentcard_module + /obj/item/weapon/card/id/syndicate/Initialize() . = ..() + agentcard_module = new(src) access = syndicate_access.Copy() /obj/item/weapon/card/id/syndicate/station_access/Initialize() @@ -15,6 +18,7 @@ access |= get_all_station_access() /obj/item/weapon/card/id/syndicate/Destroy() + QDEL_NULL(agentcard_module) unset_registered_user(registered_user) return ..() @@ -36,33 +40,12 @@ if(registered_user == user) switch(alert("Would you like edit the ID, or show it?","Show or Edit?", "Edit","Show")) if("Edit") - ui_interact(user) + agentcard_module.tgui_interact(user) if("Show") ..() else ..() -/obj/item/weapon/card/id/syndicate/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - var/entries[0] - entries[++entries.len] = list("name" = "Age", "value" = age) - entries[++entries.len] = list("name" = "Appearance", "value" = "Set") - entries[++entries.len] = list("name" = "Assignment", "value" = assignment) - entries[++entries.len] = list("name" = "Blood Type", "value" = blood_type) - entries[++entries.len] = list("name" = "DNA Hash", "value" = dna_hash) - entries[++entries.len] = list("name" = "Fingerprint Hash", "value" = fingerprint_hash) - entries[++entries.len] = list("name" = "Name", "value" = registered_name) - entries[++entries.len] = list("name" = "Photo", "value" = "Update") - entries[++entries.len] = list("name" = "Sex", "value" = sex) - entries[++entries.len] = list("name" = "Factory Reset", "value" = "Use With Care") - data["electronic_warfare"] = electronic_warfare - data["entries"] = entries - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "agent_id_card.tmpl", "Fake ID", 600, 400) - ui.set_initial_data(data) - ui.open() /obj/item/weapon/card/id/syndicate/proc/register_user(var/mob/user) if(!istype(user) || user == registered_user) @@ -79,114 +62,6 @@ registered_user.unregister(OBSERVER_EVENT_DESTROY, src) registered_user = null -/obj/item/weapon/card/id/syndicate/CanUseTopic(mob/user) - if(user != registered_user) - return STATUS_CLOSE - return ..() - -/obj/item/weapon/card/id/syndicate/Topic(href, href_list, var/datum/topic_state/state) - if(..()) - return 1 - - var/user = usr - if(href_list["electronic_warfare"]) - electronic_warfare = text2num(href_list["electronic_warfare"]) - to_chat(user, "Electronic warfare [electronic_warfare ? "enabled" : "disabled"].") - else if(href_list["set"]) - switch(href_list["set"]) - if("Age") - var/new_age = input(user,"What age would you like to put on this card?","Agent Card Age", age) as null|num - if(!isnull(new_age) && CanUseTopic(user, state)) - if(new_age < 0) - age = initial(age) - else - age = new_age - to_chat(user, "Age has been set to '[age]'.") - . = 1 - if("Appearance") - var/datum/card_state/choice = input(user, "Select the appearance for this card.", "Agent Card Appearance") as null|anything in id_card_states() - if(choice && CanUseTopic(user, state)) - src.icon_state = choice.icon_state - src.item_state = choice.item_state - to_chat(usr, "Appearance changed to [choice].") - . = 1 - if("Assignment") - var/new_job = sanitize(input(user,"What assignment would you like to put on this card?\nChanging assignment will not grant or remove any access levels.","Agent Card Assignment", assignment) as null|text) - if(!isnull(new_job) && CanUseTopic(user, state)) - src.assignment = new_job - to_chat(user, "Occupation changed to '[new_job]'.") - update_name() - . = 1 - if("Blood Type") - var/default = blood_type - if(default == initial(blood_type) && ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.dna) - default = H.dna.b_type - var/new_blood_type = sanitize(input(user,"What blood type would you like to be written on this card?","Agent Card Blood Type",default) as null|text) - if(!isnull(new_blood_type) && CanUseTopic(user, state)) - src.blood_type = new_blood_type - to_chat(user, "Blood type changed to '[new_blood_type]'.") - . = 1 - if("DNA Hash") - var/default = dna_hash - if(default == initial(dna_hash) && ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.dna) - default = H.dna.unique_enzymes - var/new_dna_hash = sanitize(input(user,"What DNA hash would you like to be written on this card?","Agent Card DNA Hash",default) as null|text) - if(!isnull(new_dna_hash) && CanUseTopic(user, state)) - src.dna_hash = new_dna_hash - to_chat(user, "DNA hash changed to '[new_dna_hash]'.") - . = 1 - if("Fingerprint Hash") - var/default = fingerprint_hash - if(default == initial(fingerprint_hash) && ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.dna) - default = md5(H.dna.uni_identity) - var/new_fingerprint_hash = sanitize(input(user,"What fingerprint hash would you like to be written on this card?","Agent Card Fingerprint Hash",default) as null|text) - if(!isnull(new_fingerprint_hash) && CanUseTopic(user, state)) - src.fingerprint_hash = new_fingerprint_hash - to_chat(user, "Fingerprint hash changed to '[new_fingerprint_hash]'.") - . = 1 - if("Name") - var/new_name = sanitizeName(input(user,"What name would you like to put on this card?","Agent Card Name", registered_name) as null|text) - if(!isnull(new_name) && CanUseTopic(user, state)) - src.registered_name = new_name - update_name() - to_chat(user, "Name changed to '[new_name]'.") - . = 1 - if("Photo") - set_id_photo(user) - to_chat(user, "Photo changed.") - . = 1 - if("Sex") - var/new_sex = sanitize(input(user,"What sex would you like to put on this card?","Agent Card Sex", sex) as null|text) - if(!isnull(new_sex) && CanUseTopic(user, state)) - src.sex = new_sex - to_chat(user, "Sex changed to '[new_sex]'.") - . = 1 - if("Factory Reset") - if(alert("This will factory reset the card, including access and owner. Continue?", "Factory Reset", "No", "Yes") == "Yes" && CanUseTopic(user, state)) - age = initial(age) - access = syndicate_access.Copy() - assignment = initial(assignment) - blood_type = initial(blood_type) - dna_hash = initial(dna_hash) - electronic_warfare = initial(electronic_warfare) - fingerprint_hash = initial(fingerprint_hash) - icon_state = initial(icon_state) - name = initial(name) - registered_name = initial(registered_name) - unset_registered_user() - sex = initial(sex) - to_chat(user, "All information has been deleted from \the [src].") - . = 1 - - // Always update the UI, or buttons will spin indefinitely - SSnanoui.update_uis(src) - /var/global/list/id_card_states /proc/id_card_states() if(!id_card_states) diff --git a/code/game/objects/items/weapons/implants/implantaugment.dm b/code/game/objects/items/weapons/implants/implantaugment.dm index be6c8721ee..020976447d 100644 --- a/code/game/objects/items/weapons/implants/implantaugment.dm +++ b/code/game/objects/items/weapons/implants/implantaugment.dm @@ -169,6 +169,10 @@ organ_to_implant = /obj/item/organ/internal/augment/armmounted/hand/sword organ_display_name = "weapon augment" +/obj/item/weapon/implant/organ/limbaugment/wrist/blade + organ_to_implant = /obj/item/organ/internal/augment/armmounted/hand/blade + organ_display_name = "weapon augment" + // Fore-arm /obj/item/weapon/implant/organ/limbaugment/laser organ_to_implant = /obj/item/organ/internal/augment/armmounted @@ -185,6 +189,10 @@ /obj/item/weapon/implant/organ/limbaugment/upperarm/surge organ_to_implant = /obj/item/organ/internal/augment/armmounted/shoulder/surge +/obj/item/weapon/implant/organ/limbaugment/upperarm/blade + organ_to_implant = /obj/item/organ/internal/augment/armmounted/shoulder/blade + organ_display_name = "weapon augment" + /* * Others */ diff --git a/code/game/objects/items/weapons/implants/implantcase.dm b/code/game/objects/items/weapons/implants/implantcase.dm index 3bcb295e2a..c404089028 100644 --- a/code/game/objects/items/weapons/implants/implantcase.dm +++ b/code/game/objects/items/weapons/implants/implantcase.dm @@ -279,3 +279,23 @@ src.imp = new /obj/item/weapon/implant/organ/pelvic( src ) ..() return + +/obj/item/weapon/implantcase/armblade + name = "glass case - 'Armblade'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/armblade/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/upperarm/blade( src ) + ..() + return + +/obj/item/weapon/implantcase/handblade + name = "glass case - 'Handblade'" + desc = "A case containing a nanite fabricator implant." + icon_state = "implantcase-b" + +/obj/item/weapon/implantcase/handblade/New() + src.imp = new /obj/item/weapon/implant/organ/limbaugment/wrist/blade( src ) + ..() + return diff --git a/code/game/objects/items/weapons/storage/storage.dm b/code/game/objects/items/weapons/storage/storage.dm index bfcbd695e5..4759b06667 100644 --- a/code/game/objects/items/weapons/storage/storage.dm +++ b/code/game/objects/items/weapons/storage/storage.dm @@ -362,6 +362,8 @@ usr.client.screen -= W W.dropped(usr) add_fingerprint(usr) + if (use_sound) + playsound(src, src.use_sound, 50, 0, -5) //Something broke "add item to container" sounds, this is a hacky fix. if(!prevent_warning) for(var/mob/M in viewers(usr, null)) diff --git a/code/game/objects/items/weapons/storage/uplink_kits.dm b/code/game/objects/items/weapons/storage/uplink_kits.dm index abae12ed8a..324fb93dd8 100644 --- a/code/game/objects/items/weapons/storage/uplink_kits.dm +++ b/code/game/objects/items/weapons/storage/uplink_kits.dm @@ -128,6 +128,12 @@ /obj/item/weapon/storage/box/syndie_kit/imp_aug/sprinter case_type = /obj/item/weapon/implantcase/sprinter + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/armblade + case_type = /obj/item/weapon/implantcase/armblade + +/obj/item/weapon/storage/box/syndie_kit/imp_aug/handblade + case_type = /obj/item/weapon/implantcase/handblade /obj/item/weapon/storage/box/syndie_kit/space name = "boxed space suit and helmet" diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm index 83bb322e0e..274ea23ab8 100644 --- a/code/game/objects/items/weapons/storage/wallets.dm +++ b/code/game/objects/items/weapons/storage/wallets.dm @@ -42,9 +42,9 @@ slot_flags = SLOT_ID var/obj/item/weapon/card/id/front_id = null - - drop_sound = 'sound/items/drop/cloth.ogg' - pickup_sound = 'sound/items/pickup/cloth.ogg' + + drop_sound = 'sound/items/drop/leather.ogg' + pickup_sound = 'sound/items/pickup/leather.ogg' /obj/item/weapon/storage/wallet/remove_from_storage(obj/item/W as obj, atom/new_location) . = ..(W, new_location) diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm index 358e650b2d..5f587fccf2 100644 --- a/code/game/objects/items/weapons/tanks/tanks.dm +++ b/code/game/objects/items/weapons/tanks/tanks.dm @@ -120,9 +120,6 @@ var/list/global/tank_gauge_cache = list() if (istype(src.loc, /obj/item/assembly)) icon = src.loc - if ((istype(W, /obj/item/device/analyzer)) && get_dist(user, src) <= 1) - var/obj/item/device/analyzer/A = W - A.analyze_gases(src, user) else if (istype(W,/obj/item/latexballon)) var/obj/item/latexballon/LB = W LB.blow(src) diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 9bca1c2933..688236eda8 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -42,7 +42,7 @@ old_turf.unregister_dangerous_object(src) new_turf.register_dangerous_object(src) -/obj/Topic(href, href_list, var/datum/topic_state/state = default_state) +/obj/Topic(href, href_list, var/datum/tgui_state/state = GLOB.tgui_default_state) if(usr && ..()) return 1 @@ -55,7 +55,7 @@ CouldNotUseTopic(usr) return 1 -/obj/CanUseTopic(var/mob/user, var/datum/topic_state/state = default_state) +/obj/CanUseTopic(var/mob/user, var/datum/tgui_state/state = GLOB.tgui_default_state) if(user.CanUseObjTopic(src)) return ..() to_chat(user, "[bicon(src)]Access Denied!") @@ -69,7 +69,7 @@ return 1 /obj/proc/CouldUseTopic(var/mob/user) - var/atom/host = nano_host() + var/atom/host = tgui_host() host.add_hiddenprint(user) /obj/proc/CouldNotUseTopic(var/mob/user) @@ -135,7 +135,6 @@ in_use = 0 /obj/attack_ghost(mob/user) - ui_interact(user) tgui_interact(user) ..() diff --git a/code/game/objects/random/guns_and_ammo.dm b/code/game/objects/random/guns_and_ammo.dm index 43a166a647..736565b14d 100644 --- a/code/game/objects/random/guns_and_ammo.dm +++ b/code/game/objects/random/guns_and_ammo.dm @@ -153,6 +153,65 @@ prob(2);/obj/item/ammo_magazine/m9mmt, prob(6);/obj/item/ammo_magazine/m9mmt/rubber) +/obj/random/grenade + name = "Random Grenade" + desc = "This is random thrown grenades (no C4/etc.)." + icon = 'icons/obj/grenade.dmi' + icon_state = "clusterbang_segment" + +/obj/random/grenade/item_to_spawn() + return pick( prob(15);/obj/item/weapon/grenade/concussion, + prob(5);/obj/item/weapon/grenade/empgrenade, + prob(15);/obj/item/weapon/grenade/empgrenade/low_yield, + prob(5);/obj/item/weapon/grenade/chem_grenade/metalfoam, + prob(2);/obj/item/weapon/grenade/chem_grenade/incendiary, + prob(10);/obj/item/weapon/grenade/chem_grenade/antiweed, + prob(10);/obj/item/weapon/grenade/chem_grenade/cleaner, + prob(10);/obj/item/weapon/grenade/chem_grenade/teargas, + prob(5);/obj/item/weapon/grenade/explosive, + prob(10);/obj/item/weapon/grenade/explosive/mini, + prob(2);/obj/item/weapon/grenade/explosive/frag, + prob(15);/obj/item/weapon/grenade/flashbang, + prob(1);/obj/item/weapon/grenade/flashbang/clusterbang, //I can't not do this. + prob(15);/obj/item/weapon/grenade/shooter/rubber, + prob(10);/obj/item/weapon/grenade/shooter/energy/flash, + prob(15);/obj/item/weapon/grenade/smokebomb + ) + +/obj/random/grenade/less_lethal + name = "Random Security Grenade" + desc = "This is a random thrown grenade that shouldn't kill anyone." + icon = 'icons/obj/grenade.dmi' + icon_state = "clusterbang_segment" + +/obj/random/grenade/less_lethal/item_to_spawn() + return pick( prob(20);/obj/item/weapon/grenade/concussion, + prob(15);/obj/item/weapon/grenade/empgrenade/low_yield, + prob(15);/obj/item/weapon/grenade/chem_grenade/metalfoam, + prob(20);/obj/item/weapon/grenade/chem_grenade/teargas, + prob(20);/obj/item/weapon/grenade/flashbang, + prob(1);/obj/item/weapon/grenade/flashbang/clusterbang, //I *still* can't not do this. + prob(15);/obj/item/weapon/grenade/shooter/rubber, + prob(10);/obj/item/weapon/grenade/shooter/energy/flash + ) + +/obj/random/grenade/box + name = "Random Grenade Box" + desc = "This is a random box of grenades. Not to be mistaken for a box of random grenades. Or a grenade of random boxes - but that would just be silly." + icon = 'icons/obj/grenade.dmi' + icon_state = "clusterbang_segment" + +/obj/random/grenade/box/item_to_spawn() + return pick( prob(20);/obj/item/weapon/storage/box/flashbangs, + prob(10);/obj/item/weapon/storage/box/emps, + prob(20);/obj/item/weapon/storage/box/empslite, + prob(15);/obj/item/weapon/storage/box/smokes, + prob(5);/obj/item/weapon/storage/box/anti_photons, + prob(5);/obj/item/weapon/storage/box/frags, + prob(10);/obj/item/weapon/storage/box/metalfoam, + prob(15);/obj/item/weapon/storage/box/teargas + ) + /obj/random/projectile/random name = "Random Projectile Weapon" desc = "This is a random weapon." diff --git a/code/game/objects/random/mapping.dm b/code/game/objects/random/mapping.dm index 7319ca42e5..16be9dff94 100644 --- a/code/game/objects/random/mapping.dm +++ b/code/game/objects/random/mapping.dm @@ -585,7 +585,7 @@ /obj/item/clothing/mask/breath, /obj/structure/closet/crate/aether //AETHER AIRSUPPLY ), - prob(10);list( + prob(5);list( /obj/random/multiple/voidsuit/vintage, /obj/random/multiple/voidsuit/vintage, /obj/random/tank, @@ -625,13 +625,20 @@ /obj/random/powercell, /obj/structure/closet/crate/einstein //EINSTEIN BATTERYPACK ), - prob(10);list( + prob(5);list( /obj/item/weapon/circuitboard/smes, /obj/random/smes_coil, /obj/random/smes_coil, /obj/structure/closet/crate/focalpoint //FOCAL SMES ), - prob(15);list( + prob(10);list( + /obj/item/weapon/module/power_control, + /obj/item/stack/cable_coil, + /obj/item/frame/apc, + /obj/item/weapon/cell/apc, + /obj/structure/closet/crate/focalpoint //FOCAL APC + ), + prob(5);list( /obj/random/drinkbottle, /obj/random/drinkbottle, /obj/random/drinkbottle, @@ -640,7 +647,7 @@ /obj/random/cigarettes, /obj/structure/closet/crate/gilthari //GILTHARI LUXURY ), - prob(15);list( + prob(10);list( /obj/random/tech_supply/nofail, /obj/random/tech_supply/component/nofail, /obj/random/tech_supply/component/nofail, @@ -655,7 +662,7 @@ /obj/random/multiple/ore_pile, /obj/structure/closet/crate/grayson //GRAYSON ORES ), - prob(15);list( + prob(10);list( /obj/random/material/refined, /obj/random/material/refined, /obj/random/material/refined, @@ -671,24 +678,48 @@ /obj/item/weapon/cell/device/weapon, /obj/structure/closet/crate/secure/heph //HEPHAESTUS ENERGY ), - prob(2);list( - /obj/random/projectile/random, - /obj/random/projectile/random, - /obj/structure/closet/crate/secure/heph //HEPHAESTUS PROJECTILE + prob(1);list( + /obj/random/grenade/box, + /obj/random/grenade/box, + /obj/structure/closet/crate/secure/heph //HEPHAESTUS GRENADES ), prob(2);list( + /obj/random/projectile/random, + /obj/random/projectile/random, + /obj/structure/closet/crate/secure/lawson //LAWSON PROJECTILE + ), + prob(3);list( + /obj/random/grenade/less_lethal, + /obj/random/grenade/less_lethal, + /obj/random/grenade/less_lethal, + /obj/random/grenade/less_lethal, + /obj/structure/closet/crate/secure/nanotrasen //NTSEC CROWD GRENADES + ), + prob(5);list( /obj/random/multiple/voidsuit/security, /obj/random/tank, /obj/item/clothing/mask/breath, /obj/structure/closet/crate/secure/nanotrasen //NTSEC SUIT ), - prob(2);list( + prob(5);list( /obj/random/multiple/voidsuit/medical, /obj/random/tank, /obj/item/clothing/mask/breath, /obj/structure/closet/crate/secure/veymed //VM SUIT ), prob(5);list( + /obj/random/multiple/voidsuit/mining, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/grayson //GRAYSON SUIT + ), + prob(5);list( + /obj/random/multiple/voidsuit/engineering, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/xion //XION SUIT + ), + prob(10);list( /obj/random/firstaid, /obj/random/medical, /obj/random/medical, @@ -697,11 +728,13 @@ /obj/random/medical/lite, /obj/structure/closet/crate/veymed //VM GRABBAG ), - prob(5);list( + prob(10);list( /obj/random/firstaid, /obj/random/firstaid, /obj/random/firstaid, /obj/random/firstaid, + /obj/random/unidentified_medicine/fresh_medicine, + /obj/random/unidentified_medicine/fresh_medicine, /obj/structure/closet/crate/veymed //VM FAKS ), prob(10);list( @@ -726,19 +759,337 @@ /obj/random/medical/pillbottle, /obj/random/medical/pillbottle, /obj/random/medical/pillbottle, - /obj/random/medical/pillbottle, + /obj/random/unidentified_medicine/fresh_medicine, + /obj/random/unidentified_medicine/fresh_medicine, /obj/structure/closet/crate/zenghu //ZENGHU PILLS ), + prob(10);list( + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/weapon/clipboard, + /obj/item/weapon/clipboard, + /obj/item/weapon/pen/red, + /obj/item/weapon/pen/blue, + /obj/item/weapon/pen/blue, + /obj/item/device/camera_film, + /obj/item/weapon/folder/blue, + /obj/item/weapon/folder/red, + /obj/item/weapon/folder/yellow, + /obj/item/weapon/hand_labeler, + /obj/item/weapon/tape_roll, + /obj/item/weapon/paper_bin, + /obj/item/sticky_pad/random, + /obj/structure/closet/crate/ummarcar //UMMARCAR OFFICE TRASH + ), + prob(5);list( + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/structure/closet/crate/unathi //UNAJERKY + ), + prob(10);list( + /obj/item/weapon/reagent_containers/glass/bucket, + /obj/item/weapon/mop, + /obj/item/clothing/under/rank/janitor, + /obj/item/weapon/cartridge/janitor, + /obj/item/clothing/gloves/black, + /obj/item/clothing/head/soft/purple, + /obj/item/weapon/storage/belt/janitor, + /obj/item/clothing/shoes/galoshes, + /obj/item/weapon/storage/bag/trash, + /obj/item/device/lightreplacer, + /obj/item/weapon/reagent_containers/spray/cleaner, + /obj/item/weapon/reagent_containers/glass/rag, + /obj/item/weapon/grenade/chem_grenade/cleaner, + /obj/item/weapon/grenade/chem_grenade/cleaner, + /obj/item/weapon/grenade/chem_grenade/cleaner, + /obj/structure/closet/crate/galaksi //GALAKSI JANITOR SUPPLIES + ), + prob(5);list( + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/structure/closet/crate/allico //GUMMIES + ), prob(2);list( + /obj/item/weapon/tank/phoron/pressurized, + /obj/item/weapon/tank/phoron/pressurized, + /obj/structure/closet/crate/secure/phoron //HQ FUEL TANKS + ), + prob(1);list( /obj/random/contraband/nofail, /obj/random/contraband/nofail, - /obj/random/contraband/nofail, + /obj/random/unidentified_medicine/combat_medicine, + /obj/random/unidentified_medicine/combat_medicine, /obj/random/projectile/random, /obj/random/projectile/random, /obj/random/mre, /obj/random/mre, + /obj/structure/closet/crate/secure/saare //SAARE GRAB BAG + ), + prob(2);list( + /obj/random/grenade, + /obj/random/grenade, + /obj/random/grenade, + /obj/random/grenade, + /obj/random/grenade, + /obj/random/grenade, + /obj/structure/closet/crate/secure/saare //SAARE GRENADES + ), + prob(1);list( + /obj/random/cash/big, + /obj/random/cash/big, + /obj/random/cash/big, + /obj/random/cash/huge, + /obj/random/cash/huge, + /obj/random/cash/huge, + /obj/structure/closet/crate/secure/saare //SAARE CASH CRATE + ) + ) + +/obj/random/multiple/corp_crate/no_weapons + name = "random corporate crate (no weapons)" + desc = "A random corporate crate with thematic contents. No weapons." + icon = 'icons/obj/storage.dmi' + icon_state = "crate" + +/obj/random/multiple/corp_crate/no_weapons/item_to_spawn() + return pick( + prob(10);list( + /obj/random/tank, + /obj/random/tank, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/item/clothing/mask/breath, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/aether //AETHER AIRSUPPLY + ), + prob(5);list( + /obj/random/multiple/voidsuit/vintage, + /obj/random/multiple/voidsuit/vintage, + /obj/random/tank, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/aether //AETHER OLDSUITS + ), + prob(10);list( /obj/random/mre, - /obj/structure/closet/crate/secure/saare //SAARE GUNS + /obj/random/mre, + /obj/random/mre, + /obj/random/mre, + /obj/random/mre, + /obj/structure/closet/crate/centauri //CENTAURI MRES + ), + prob(10);list( + /obj/random/drinksoft, + /obj/random/drinksoft, + /obj/random/drinksoft, + /obj/random/drinksoft, + /obj/random/drinksoft, + /obj/structure/closet/crate/freezer/centauri //CENTAURI SODA + ), + prob(10);list( + /obj/random/snack, + /obj/random/snack, + /obj/random/snack, + /obj/random/snack, + /obj/random/snack, + /obj/structure/closet/crate/freezer/centauri //CENTAURI SNACKS + ), + prob(10);list( + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/structure/closet/crate/einstein //EINSTEIN BATTERYPACK + ), + prob(5);list( + /obj/item/weapon/circuitboard/smes, + /obj/random/smes_coil, + /obj/random/smes_coil, + /obj/structure/closet/crate/focalpoint //FOCAL SMES + ), + prob(10);list( + /obj/item/weapon/module/power_control, + /obj/item/stack/cable_coil, + /obj/item/frame/apc, + /obj/item/weapon/cell/apc, + /obj/structure/closet/crate/focalpoint //FOCAL APC + ), + prob(5);list( + /obj/random/drinkbottle, + /obj/random/drinkbottle, + /obj/random/drinkbottle, + /obj/random/cigarettes, + /obj/random/cigarettes, + /obj/random/cigarettes, + /obj/structure/closet/crate/gilthari //GILTHARI LUXURY + ), + prob(10);list( + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/structure/closet/crate/grayson //GRAYSON TECH + ), + prob(15);list( + /obj/random/multiple/ore_pile, + /obj/random/multiple/ore_pile, + /obj/random/multiple/ore_pile, + /obj/random/multiple/ore_pile, + /obj/structure/closet/crate/grayson //GRAYSON ORES + ), + prob(10);list( + /obj/random/material/refined, + /obj/random/material/refined, + /obj/random/material/refined, + /obj/random/material/refined, + /obj/structure/closet/crate/grayson //GRAYSON MATS + ), + prob(5);list( + /obj/random/multiple/voidsuit/security, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/secure/nanotrasen //NTSEC SUIT + ), + prob(5);list( + /obj/random/multiple/voidsuit/medical, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/secure/veymed //VM SUIT + ), + prob(5);list( + /obj/random/multiple/voidsuit/mining, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/grayson //GRAYSON SUIT + ), + prob(5);list( + /obj/random/multiple/voidsuit/engineering, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/xion //XION SUIT + ), + prob(10);list( + /obj/random/firstaid, + /obj/random/medical, + /obj/random/medical, + /obj/random/medical, + /obj/random/medical/lite, + /obj/random/medical/lite, + /obj/structure/closet/crate/veymed //VM GRABBAG + ), + prob(10);list( + /obj/random/firstaid, + /obj/random/firstaid, + /obj/random/firstaid, + /obj/random/firstaid, + /obj/random/unidentified_medicine/fresh_medicine, + /obj/random/unidentified_medicine/fresh_medicine, + /obj/structure/closet/crate/veymed //VM FAKS + ), + prob(10);list( + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/structure/closet/crate/xion //XION SUPPLY + ), + prob(10);list( + /obj/random/firstaid, + /obj/random/medical, + /obj/random/medical/pillbottle, + /obj/random/medical/pillbottle, + /obj/random/medical/lite, + /obj/random/medical/lite, + /obj/structure/closet/crate/zenghu //ZENGHU GRABBAG + ), + prob(10);list( + /obj/random/medical/pillbottle, + /obj/random/medical/pillbottle, + /obj/random/medical/pillbottle, + /obj/random/medical/pillbottle, + /obj/random/unidentified_medicine/fresh_medicine, + /obj/random/unidentified_medicine/fresh_medicine, + /obj/structure/closet/crate/zenghu //ZENGHU PILLS + ), + prob(10);list( + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/device/toner, + /obj/item/weapon/clipboard, + /obj/item/weapon/clipboard, + /obj/item/weapon/pen/red, + /obj/item/weapon/pen/blue, + /obj/item/weapon/pen/blue, + /obj/item/device/camera_film, + /obj/item/weapon/folder/blue, + /obj/item/weapon/folder/red, + /obj/item/weapon/folder/yellow, + /obj/item/weapon/hand_labeler, + /obj/item/weapon/tape_roll, + /obj/item/weapon/paper_bin, + /obj/item/sticky_pad/random, + /obj/structure/closet/crate/ummarcar //UMMARCAR OFFICE TRASH + ), + prob(5);list( + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/item/weapon/reagent_containers/food/snacks/unajerky, + /obj/structure/closet/crate/unathi //UNAJERKY + ), + prob(10);list( + /obj/item/weapon/reagent_containers/glass/bucket, + /obj/item/weapon/mop, + /obj/item/clothing/under/rank/janitor, + /obj/item/weapon/cartridge/janitor, + /obj/item/clothing/gloves/black, + /obj/item/clothing/head/soft/purple, + /obj/item/weapon/storage/belt/janitor, + /obj/item/clothing/shoes/galoshes, + /obj/item/weapon/storage/bag/trash, + /obj/item/device/lightreplacer, + /obj/item/weapon/reagent_containers/spray/cleaner, + /obj/item/weapon/reagent_containers/glass/rag, + /obj/item/weapon/grenade/chem_grenade/cleaner, + /obj/item/weapon/grenade/chem_grenade/cleaner, + /obj/item/weapon/grenade/chem_grenade/cleaner, + /obj/structure/closet/crate/galaksi //GALAKSI JANITOR SUPPLIES + ), + prob(5);list( + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/item/weapon/reagent_containers/food/snacks/candy/gummy, + /obj/structure/closet/crate/allico //GUMMIES + ), + prob(2);list( + /obj/item/weapon/tank/phoron/pressurized, + /obj/item/weapon/tank/phoron/pressurized, + /obj/structure/closet/crate/secure/phoron //HQ FUEL TANKS ), prob(1);list( /obj/random/cash/big, @@ -849,6 +1200,100 @@ /obj/structure/closet/crate/large/secure/xion //XION TECH COMPS ) ) + +/obj/random/multiple/large_corp_crate/no_weapons + name = "random large corporate crate (no weapons)" + desc = "A random large corporate crate with thematic contents. No weapons." + icon = 'icons/obj/storage.dmi' + icon_state = "largermetal" + +/obj/random/multiple/large_corp_crate/no_weapons/item_to_spawn() + return pick( + prob(30);list( + /obj/random/multiple/voidsuit/vintage, + /obj/random/multiple/voidsuit/vintage, + /obj/random/tank, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/item/clothing/mask/breath, + /obj/random/multiple/voidsuit/vintage, + /obj/random/multiple/voidsuit/vintage, + /obj/random/tank, + /obj/random/tank, + /obj/item/clothing/mask/breath, + /obj/item/clothing/mask/breath, + /obj/structure/closet/crate/large/aether //AETHER SUITSBOX + ), + prob(30);list( + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/random/powercell, + /obj/structure/closet/crate/large/einstein //EIN BATTERY MEGAPACK + ), + prob(20);list( + /obj/item/weapon/circuitboard/smes, + /obj/item/weapon/circuitboard/smes, + /obj/random/smes_coil, + /obj/random/smes_coil, + /obj/random/smes_coil, + /obj/random/smes_coil, + /obj/random/smes_coil, + /obj/random/smes_coil, + /obj/structure/closet/crate/large/einstein //EIN SMESBOX + ), + prob(20);list( + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/random/tech_supply/nofail, + /obj/structure/closet/crate/large/xion //XION TECH SUPPLY + ), + prob(20);list( + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/random/tech_supply/component/nofail, + /obj/structure/closet/crate/large/secure/xion //XION TECH COMPS + ) + ) + +//recursion crate! +/obj/random/multiple/random_size_crate + name = "random size corporate crate" + desc = "A random size corporate crate with thematic contents: prefers small crates." + icon = 'icons/obj/storage.dmi' + icon_state = "largermetal" + +/obj/random/multiple/random_size_crate/item_to_spawn() + return pick( + prob(85);list( + /obj/random/multiple/corp_crate + ), + prob(15);list( + /obj/random/multiple/large_corp_crate + ) + ) /* * Turf swappers. */ diff --git a/code/game/objects/random/mapping_vr.dm b/code/game/objects/random/mapping_vr.dm new file mode 100644 index 0000000000..43431a8b16 --- /dev/null +++ b/code/game/objects/random/mapping_vr.dm @@ -0,0 +1,10 @@ +/obj/random/empty_or_lootable_crate + name = "random crate" + desc = "Spawns a random crate which may or may not have contents. Sometimes spawns nothing." + icon = 'icons/obj/storage.dmi' + icon_state = "moneybag" + spawn_nothing_percentage = 20 + +/obj/random/empty_or_lootable_crate/item_to_spawn() + return pick(/obj/random/crate, + /obj/random/multiple/corp_crate) \ No newline at end of file diff --git a/code/game/objects/random/spacesuits.dm b/code/game/objects/random/spacesuits.dm index 84cf74fb48..da380f2842 100644 --- a/code/game/objects/random/spacesuits.dm +++ b/code/game/objects/random/spacesuits.dm @@ -29,6 +29,10 @@ /obj/item/clothing/suit/space/void/engineering/alt, /obj/item/clothing/head/helmet/space/void/engineering/alt ), + prob(5);list( + /obj/item/clothing/suit/space/void/engineering/hazmat, + /obj/item/clothing/head/helmet/space/void/engineering/hazmat + ), prob(5);list( /obj/item/clothing/suit/space/void/engineering/construction, /obj/item/clothing/head/helmet/space/void/engineering/construction @@ -109,7 +113,35 @@ ) ) +/obj/random/multiple/voidsuit/engineering + name = "Random Engineering Voidsuit" + desc = "This is a random engineering voidsuit." + icon = 'icons/obj/clothing/spacesuits.dmi' + icon_state = "rig-engineering" +/obj/random/multiple/voidsuit/engineering/item_to_spawn() + return pick( + prob(35);list( + /obj/item/clothing/suit/space/void/engineering, + /obj/item/clothing/head/helmet/space/void/engineering + ), + prob(5);list( + /obj/item/clothing/suit/space/void/engineering/alt, + /obj/item/clothing/head/helmet/space/void/engineering/alt + ), + prob(15);list( + /obj/item/clothing/suit/space/void/engineering/hazmat, + /obj/item/clothing/head/helmet/space/void/engineering/hazmat + ), + prob(15);list( + /obj/item/clothing/suit/space/void/engineering/construction, + /obj/item/clothing/head/helmet/space/void/engineering/construction + ), + prob(5);list( + /obj/item/clothing/suit/space/void/engineering/salvage, + /obj/item/clothing/head/helmet/space/void/engineering/salvage + ) + ) /obj/random/multiple/voidsuit/security name = "Random Security Voidsuit" diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index fc136cfda8..29498bb2df 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -16,8 +16,8 @@ LINEN BINS throw_speed = 1 throw_range = 2 w_class = ITEMSIZE_SMALL - drop_sound = 'sound/items/drop/cloth.ogg' - pickup_sound = 'sound/items/pickup/cloth.ogg' + drop_sound = 'sound/items/drop/clothing.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' /obj/item/weapon/bedsheet/attack_self(mob/user as mob) user.drop_item() diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index de07aa8de8..a6dc8e42ac 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -20,8 +20,90 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) var/has_items = FALSE var/dismantled = TRUE var/signs = 0 //maximum capacity hardcoded below + var/list/tgui_icons = list() + var/static/list/equippable_item_whitelist +/obj/structure/janitorialcart/proc/equip_janicart_item(mob/user, obj/item/I) + if(!equippable_item_whitelist) + equippable_item_whitelist = typecacheof(list( + /obj/item/weapon/storage/bag/trash, + /obj/item/weapon/mop, + /obj/item/weapon/reagent_containers/spray, + /obj/item/device/lightreplacer, + /obj/item/clothing/suit/caution, + )) + + if(!is_type_in_typecache(I, equippable_item_whitelist)) + to_chat(user, "There's no room in [src] for [I].") + return FALSE + + if(!user.unEquip(I, 0, src)) + to_chat(user, "[I] is stuck to your hand.") + return FALSE + + if(istype(I, /obj/item/weapon/storage/bag/trash)) + if(mybag) + to_chat(user, "[src] already has \an [I].") + return FALSE + mybag = I + setTguiIcon("mybag", mybag) + + else if(istype(I, /obj/item/weapon/mop)) + if(mymop) + to_chat(user, "[src] already has \an [I].") + return FALSE + mymop = I + setTguiIcon("mymop", mymop) + + else if(istype(I, /obj/item/weapon/reagent_containers/spray)) + if(myspray) + to_chat(user, "[src] already has \an [I].") + return FALSE + myspray = I + setTguiIcon("myspray", myspray) + + else if(istype(I, /obj/item/device/lightreplacer)) + if(myreplacer) + to_chat(user, "[src] already has \an [I].") + return FALSE + myreplacer = I + setTguiIcon("myreplacer", myreplacer) + + else if(istype(I, /obj/item/clothing/suit/caution)) + if(signs < 4) + signs++ + setTguiIcon("signs", I) + else + to_chat(user, "[src] can't hold any more signs.") + return FALSE + else + // This may look like duplicate code, but it's important that we don't call unEquip *and* warn the user if + // something horrible goes wrong. (this else is never supposed to happen) + to_chat(user, "There's no room in [src] for [I].") + return FALSE + + update_icon() + to_chat(user, "You put [I] into [src].") + return TRUE + +/obj/structure/janitorialcart/proc/setTguiIcon(key, atom/A) + if(!istype(A) || !key) + return + + var/icon/F = getFlatIcon(A, defdir = SOUTH, no_anim = TRUE) + tgui_icons["[key]"] = "'data:image/png;base64,[icon2base64(F)]'" + SStgui.update_uis(src) + +/obj/structure/janitorialcart/proc/nullTguiIcon(key) + if(!key) + return + tgui_icons.Remove(key) + SStgui.update_uis(src) + +/obj/structure/janitorialcart/proc/clearTguiIcons() + tgui_icons.Cut() + SStgui.update_uis(src) /obj/structure/janitorialcart/Destroy() QDEL_NULL(mybag) @@ -29,20 +111,22 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) QDEL_NULL(myspray) QDEL_NULL(myreplacer) QDEL_NULL(mybucket) + clearTguiIcons() return ..() /obj/structure/janitorialcart/examine(mob/user) - if(..(user, 1)) - if (mybucket) - var/contains = mybucket.reagents.total_volume - to_chat(user, "\icon[src] The bucket contains [contains] unit\s of liquid!") - else - to_chat(user, "\icon[src] There is no bucket mounted on it!") + . = ..(user) + if(istype(mybucket)) + var/contains = mybucket.reagents.total_volume + . += "[bicon(src)] The bucket contains [contains] unit\s of liquid!" + else + . += "[bicon(src)] There is no bucket mounted on it!" /obj/structure/janitorialcart/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob) if (istype(O, /obj/structure/mopbucket) && !mybucket) O.forceMove(src) mybucket = O + setTguiIcon("mybucket", mybucket) to_chat(user, "You mount the [O] on the janicart.") update_icon() else @@ -70,38 +154,19 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) return 1 else if(istype(I, /obj/item/weapon/reagent_containers/spray) && !myspray) - user.unEquip(I, 0, src) - myspray = I - update_icon() - updateUsrDialog() - to_chat(user, "You put [I] into [src].") + equip_janicart_item(user, I) return 1 else if(istype(I, /obj/item/device/lightreplacer) && !myreplacer) - user.unEquip(I, 0, src) - myreplacer = I - update_icon() - updateUsrDialog() - to_chat(user, "You put [I] into [src].") + equip_janicart_item(user, I) return 1 else if(istype(I, /obj/item/weapon/storage/bag/trash) && !mybag) - user.unEquip(I, 0, src) - mybag = I - update_icon() - updateUsrDialog() - to_chat(user, "You put [I] into [src].") + equip_janicart_item(user, I) return 1 else if(istype(I, /obj/item/clothing/suit/caution)) - if(signs < 4) - user.unEquip(I, 0, src) - signs++ - update_icon() - updateUsrDialog() - to_chat(user, "You put [I] into [src].") - else - to_chat(user, "[src] can't hold any more signs.") + equip_janicart_item(user, I) return 1 else if(mybag) @@ -124,16 +189,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) if(user.incapacitated() || !Adjacent(user)) return var/obj/I = usr.get_active_hand() if(istype(I, /obj/item/weapon/mop)) - if(!mymop) - usr.drop_from_inventory(I,src) - mymop = I - update_icon() - updateUsrDialog() - to_chat(usr, "You put [I] into [src].") - update_icon() - else - to_chat(usr, "The cart already has a mop attached") - return + equip_janicart_item(user, I) else if(istype(I, /obj/item/weapon/reagent_containers) && mybucket) var/obj/item/weapon/reagent_containers/C = I C.afterattack(mybucket, usr, 1) @@ -141,75 +197,95 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) /obj/structure/janitorialcart/attack_hand(mob/user) - ui_interact(user) + tgui_interact(user) return -/obj/structure/janitorialcart/ui_interact(var/mob/user, var/ui_key = "main", var/datum/nanoui/ui = null, var/force_open = TRUE) - var/data[0] - data["name"] = capitalize(name) - data["bag"] = mybag ? capitalize(mybag.name) : null - data["bucket"] = mybucket ? capitalize(mybucket.name) : null - data["mop"] = mymop ? capitalize(mymop.name) : null - data["spray"] = myspray ? capitalize(myspray.name) : null - data["replacer"] = myreplacer ? capitalize(myreplacer.name) : null - data["signs"] = signs ? "[signs] sign\s" : null - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) +/obj/structure/janitorialcart/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, ui_key, "janitorcart.tmpl", "Janitorial cart", 240, 160) - ui.set_initial_data(data) + ui = new(user, src, "JanitorCart", name) // 240, 160 + ui.set_autoupdate(FALSE) ui.open() +/obj/structure/janitorialcart/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() -/obj/structure/janitorialcart/Topic(href, href_list) - if(!in_range(src, usr)) - return - if(!isliving(usr)) - return - var/mob/living/user = usr + data["mybag"] = mybag ? capitalize(mybag.name) : null + data["mybucket"] = mybucket ? capitalize(mybucket.name) : null + data["mymop"] = mymop ? capitalize(mymop.name) : null + data["myspray"] = myspray ? capitalize(myspray.name) : null + data["myreplacer"] = myreplacer ? capitalize(myreplacer.name) : null + data["signs"] = signs ? "[signs] sign\s" : null - if(href_list["take"]) - switch(href_list["take"]) - if("garbage") - if(mybag) - user.put_in_hands(mybag) - to_chat(user, "You take [mybag] from [src].") - mybag = null - if("mop") - if(mymop) - user.put_in_hands(mymop) - to_chat(user, "You take [mymop] from [src].") - mymop = null - if("spray") - if(myspray) - user.put_in_hands(myspray) - to_chat(user, "You take [myspray] from [src].") - myspray = null - if("replacer") - if(myreplacer) - user.put_in_hands(myreplacer) - to_chat(user, "You take [myreplacer] from [src].") - myreplacer = null - if("sign") - if(signs) - var/obj/item/clothing/suit/caution/Sign = locate() in src - if(Sign) - user.put_in_hands(Sign) - to_chat(user, "You take \a [Sign] from [src].") - signs-- - else - warning("[src] signs ([signs]) didn't match contents") - signs = 0 - if("bucket") - if(mybucket) - mybucket.forceMove(get_turf(user)) - to_chat(user, "You unmount [mybucket] from [src].") - mybucket = null + data["icons"] = tgui_icons + return data + +/obj/structure/janitorialcart/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + var/obj/item/I = usr.get_active_hand() + + switch(action) + if("bag") + if(mybag) + usr.put_in_hands(mybag) + to_chat(usr, "You take [mybag] from [src].") + mybag = null + nullTguiIcon("mybag") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("mop") + if(mymop) + usr.put_in_hands(mymop) + to_chat(usr, "You take [mymop] from [src].") + mymop = null + nullTguiIcon("mymop") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("spray") + if(myspray) + usr.put_in_hands(myspray) + to_chat(usr, "You take [myspray] from [src].") + myspray = null + nullTguiIcon("myspray") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("replacer") + if(myreplacer) + usr.put_in_hands(myreplacer) + to_chat(usr, "You take [myreplacer] from [src].") + myreplacer = null + nullTguiIcon("myreplacer") + else if(is_type_in_typecache(I, equippable_item_whitelist)) + equip_janicart_item(usr, I) + if("sign") + if(istype(I, /obj/item/clothing/suit/caution) && signs < 4) + equip_janicart_item(usr, I) + else if(signs) + var/obj/item/clothing/suit/caution/sign = locate() in src + if(sign) + usr.put_in_hands(sign) + to_chat(usr, "You take \a [sign] from [src].") + signs-- + if(!signs) + nullTguiIcon("signs") + else + to_chat(usr, "[src] doesn't have any signs left.") + if("bucket") + if(mybucket) + mybucket.forceMove(get_turf(usr)) + to_chat(usr, "You unmount [mybucket] from [src].") + mybucket = null + nullTguiIcon("mybucket") + else + to_chat(usr, "((Drag and drop a mop bucket onto [src] to equip it.))") + return FALSE + else + return FALSE update_icon() - updateUsrDialog() - - + return TRUE /obj/structure/janitorialcart/update_icon() overlays.Cut() @@ -229,11 +305,6 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) if(signs) overlays += "cart_sign[signs]" - - - - - //This is called if the cart is caught in an explosion, or destroyed by weapon fire /obj/structure/janitorialcart/proc/spill(var/chance = 100) var/turf/dropspot = get_turf(src) @@ -275,6 +346,7 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) mybag = null update_icon() + clearTguiIcons() diff --git a/code/game/objects/structures/safe.dm b/code/game/objects/structures/safe.dm index e68fa43640..a9815a4070 100644 --- a/code/game/objects/structures/safe.dm +++ b/code/game/objects/structures/safe.dm @@ -172,7 +172,7 @@ obj/structure/safe/ex_act(severity) icon_state = "floorsafe" density = 0 level = 1 //underfloor - plane = TURF_PLANE + plane = PLATING_PLANE layer = ABOVE_UTILITY /obj/structure/safe/floor/Initialize() diff --git a/code/game/objects/structures/under_wardrobe.dm b/code/game/objects/structures/under_wardrobe.dm index cb5db5411d..c919663eaa 100644 --- a/code/game/objects/structures/under_wardrobe.dm +++ b/code/game/objects/structures/under_wardrobe.dm @@ -67,7 +67,7 @@ if(!UWC) return var/datum/category_item/underwear/selected_underwear = input(H, "Choose underwear:", "Choose underwear", H.all_underwear[UWC.name]) as null|anything in UWC.items - if(selected_underwear && CanUseTopic(H, default_state)) + if(selected_underwear && CanUseTopic(H, GLOB.tgui_default_state)) H.all_underwear[UWC.name] = selected_underwear H.hide_underwear[UWC.name] = FALSE . = TRUE diff --git a/code/game/objects/weapons.dm b/code/game/objects/weapons.dm index 19fe4eb03c..cd0190c71e 100644 --- a/code/game/objects/weapons.dm +++ b/code/game/objects/weapons.dm @@ -36,6 +36,8 @@ continue if(!SM.Adjacent(user) || !SM.Adjacent(target)) // Cleaving only hits mobs near the target mob and user. continue + if(!attack_can_reach(user, SM, 1)) + continue if(resolve_attackby(SM, user, attack_modifier = 0.5)) // Hit them with the weapon. This won't cause recursive cleaving due to the cleaving variable being set to true. hit_mobs++ diff --git a/code/game/sound.dm b/code/game/sound.dm index eabbca5c84..415a26c78c 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -266,6 +266,15 @@ soundin = pick('sound/machines/sm/accent/normal/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') if("smdelam") soundin = pick('sound/machines/sm/accent/delam/1.ogg', 'sound/machines/sm/accent/normal/2.ogg', 'sound/machines/sm/accent/normal/3.ogg', 'sound/machines/sm/accent/normal/4.ogg', 'sound/machines/sm/accent/normal/5.ogg', 'sound/machines/sm/accent/normal/6.ogg', 'sound/machines/sm/accent/normal/7.ogg', 'sound/machines/sm/accent/normal/8.ogg', 'sound/machines/sm/accent/normal/9.ogg', 'sound/machines/sm/accent/normal/10.ogg', 'sound/machines/sm/accent/normal/11.ogg', 'sound/machines/sm/accent/normal/12.ogg', 'sound/machines/sm/accent/normal/13.ogg', 'sound/machines/sm/accent/normal/14.ogg', 'sound/machines/sm/accent/normal/15.ogg', 'sound/machines/sm/accent/normal/16.ogg', 'sound/machines/sm/accent/normal/17.ogg', 'sound/machines/sm/accent/normal/18.ogg', 'sound/machines/sm/accent/normal/19.ogg', 'sound/machines/sm/accent/normal/20.ogg', 'sound/machines/sm/accent/normal/21.ogg', 'sound/machines/sm/accent/normal/22.ogg', 'sound/machines/sm/accent/normal/23.ogg', 'sound/machines/sm/accent/normal/24.ogg', 'sound/machines/sm/accent/normal/25.ogg', 'sound/machines/sm/accent/normal/26.ogg', 'sound/machines/sm/accent/normal/27.ogg', 'sound/machines/sm/accent/normal/28.ogg', 'sound/machines/sm/accent/normal/29.ogg', 'sound/machines/sm/accent/normal/30.ogg', 'sound/machines/sm/accent/normal/31.ogg', 'sound/machines/sm/accent/normal/32.ogg', 'sound/machines/sm/accent/normal/33.ogg', 'sound/machines/sm/supermatter1.ogg', 'sound/machines/sm/supermatter2.ogg', 'sound/machines/sm/supermatter3.ogg') + if ("generic_drop") + soundin = pick( + 'sound/items/drop/generic1.ogg', + 'sound/items/drop/generic2.ogg' ) + if ("generic_pickup") + soundin = pick( + 'sound/items/pickup/generic1.ogg', + 'sound/items/pickup/generic2.ogg', + 'sound/items/pickup/generic3.ogg') return soundin //Are these even used? diff --git a/code/game/turfs/simulated/floor_types_vr.dm b/code/game/turfs/simulated/floor_types_vr.dm index c4315b29b2..78e417fdc1 100644 --- a/code/game/turfs/simulated/floor_types_vr.dm +++ b/code/game/turfs/simulated/floor_types_vr.dm @@ -41,4 +41,16 @@ set_light(3,3,"#26c5a9") spawn(5 SECONDS) icon_state = "floor" - set_light(0,0,"#ffffff") \ No newline at end of file + set_light(0,0,"#ffffff") + +/turf/simulated/floor/gorefloor + name = "infected tile" + desc = "Slick, sickly-squirming meat has grown in and out of cracks once empty. It pulsates intermittently, and with every beat, blood seeps out of pores." + icon_state = "bloodfloor_1" + icon = 'icons/goonstation/turf/meatland.dmi' + +/turf/simulated/floor/gorefloor2 + name = "putrid mass" + desc = "It is entirely made of sick, gurgling flesh. It is releasing a sickly odour." + icon_state = "bloodfloor_2" + icon = 'icons/goonstation/turf/meatland.dmi' \ No newline at end of file diff --git a/code/game/turfs/simulated/wall_types_vr.dm b/code/game/turfs/simulated/wall_types_vr.dm index 21bea538dc..d2461282e2 100644 --- a/code/game/turfs/simulated/wall_types_vr.dm +++ b/code/game/turfs/simulated/wall_types_vr.dm @@ -61,6 +61,24 @@ var/list/flesh_overlay_cache = list() var/turf/simulated/flesh/F = get_step(src, direction) F.update_icon() +/turf/simulated/gore + name = "wall of viscera" + desc = "Its veins pulse in a sickeningly rapid fashion, while certain spots of the wall rise and fall gently, much like slow, deliberate breathing." + icon = 'icons/goonstation/turf/meatland.dmi' + icon_state = "bloodwall_2" + opacity = 1 + density = 1 + blocks_air = 1 + +/turf/simulated/goreeyes + name = "wall of viscera" + desc = "Strangely observant eyes dot the wall. Getting too close has the eyes fixate on you, while their pupils shake violently. Each socket is connected by a series of winding, writhing veins." + icon = 'icons/goonstation/turf/meatland.dmi' + icon_state = "bloodwall_4" + opacity = 1 + density = 1 + blocks_air = 1 + /turf/simulated/shuttle/wall/flock icon = 'icons/goonstation/featherzone.dmi' icon_state = "flockwall0" @@ -94,4 +112,4 @@ var/list/flesh_overlay_cache = list() icon_state = "hull-plastitanium" icon = 'icons/turf/wall_masks_vr.dmi' /turf/simulated/wall/plastihull/Initialize(mapload) - . = ..(mapload, MAT_PLASTITANIUMHULL, null,MAT_PLASTITANIUMHULL) \ No newline at end of file + . = ..(mapload, MAT_PLASTITANIUMHULL, null,MAT_PLASTITANIUMHULL) diff --git a/code/modules/admin/admin_verb_lists.dm b/code/modules/admin/admin_verb_lists.dm index b576bec44a..0eaedc9fbe 100644 --- a/code/modules/admin/admin_verb_lists.dm +++ b/code/modules/admin/admin_verb_lists.dm @@ -181,7 +181,6 @@ var/list/admin_verbs_server = list( /datum/admins/proc/toggle_space_ninja, /client/proc/toggle_random_events, /client/proc/check_customitem_activity, - /client/proc/nanomapgen_DumpImage, /client/proc/modify_server_news, /client/proc/recipe_dump, /client/proc/panicbunker, diff --git a/code/modules/asset_cache/asset_list_items.dm b/code/modules/asset_cache/asset_list_items.dm index 384dcbcd9b..815d250dcc 100644 --- a/code/modules/asset_cache/asset_list_items.dm +++ b/code/modules/asset_cache/asset_list_items.dm @@ -6,36 +6,36 @@ "tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css', ) -// /datum/asset/simple/headers -// assets = list( -// "alarm_green.gif" = 'icons/program_icons/alarm_green.gif', -// "alarm_red.gif" = 'icons/program_icons/alarm_red.gif', -// "batt_5.gif" = 'icons/program_icons/batt_5.gif', -// "batt_20.gif" = 'icons/program_icons/batt_20.gif', -// "batt_40.gif" = 'icons/program_icons/batt_40.gif', -// "batt_60.gif" = 'icons/program_icons/batt_60.gif', -// "batt_80.gif" = 'icons/program_icons/batt_80.gif', -// "batt_100.gif" = 'icons/program_icons/batt_100.gif', -// "charging.gif" = 'icons/program_icons/charging.gif', -// "downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif', -// "downloader_running.gif" = 'icons/program_icons/downloader_running.gif', -// "ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif', -// "ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif', -// "power_norm.gif" = 'icons/program_icons/power_norm.gif', -// "power_warn.gif" = 'icons/program_icons/power_warn.gif', -// "sig_high.gif" = 'icons/program_icons/sig_high.gif', -// "sig_low.gif" = 'icons/program_icons/sig_low.gif', -// "sig_lan.gif" = 'icons/program_icons/sig_lan.gif', -// "sig_none.gif" = 'icons/program_icons/sig_none.gif', -// "smmon_0.gif" = 'icons/program_icons/smmon_0.gif', -// "smmon_1.gif" = 'icons/program_icons/smmon_1.gif', -// "smmon_2.gif" = 'icons/program_icons/smmon_2.gif', -// "smmon_3.gif" = 'icons/program_icons/smmon_3.gif', -// "smmon_4.gif" = 'icons/program_icons/smmon_4.gif', -// "smmon_5.gif" = 'icons/program_icons/smmon_5.gif', -// "smmon_6.gif" = 'icons/program_icons/smmon_6.gif', -// "borg_mon.gif" = 'icons/program_icons/borg_mon.gif' -// ) +/datum/asset/simple/headers + assets = list( + "alarm_green.gif" = 'icons/program_icons/alarm_green.gif', + "alarm_red.gif" = 'icons/program_icons/alarm_red.gif', + "batt_5.gif" = 'icons/program_icons/batt_5.gif', + "batt_20.gif" = 'icons/program_icons/batt_20.gif', + "batt_40.gif" = 'icons/program_icons/batt_40.gif', + "batt_60.gif" = 'icons/program_icons/batt_60.gif', + "batt_80.gif" = 'icons/program_icons/batt_80.gif', + "batt_100.gif" = 'icons/program_icons/batt_100.gif', + "charging.gif" = 'icons/program_icons/charging.gif', + "downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif', + "downloader_running.gif" = 'icons/program_icons/downloader_running.gif', + "ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif', + "ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif', + "power_norm.gif" = 'icons/program_icons/power_norm.gif', + "power_warn.gif" = 'icons/program_icons/power_warn.gif', + "sig_high.gif" = 'icons/program_icons/sig_high.gif', + "sig_low.gif" = 'icons/program_icons/sig_low.gif', + "sig_lan.gif" = 'icons/program_icons/sig_lan.gif', + "sig_none.gif" = 'icons/program_icons/sig_none.gif', + "smmon_0.gif" = 'icons/program_icons/smmon_0.gif', + "smmon_1.gif" = 'icons/program_icons/smmon_1.gif', + "smmon_2.gif" = 'icons/program_icons/smmon_2.gif', + "smmon_3.gif" = 'icons/program_icons/smmon_3.gif', + "smmon_4.gif" = 'icons/program_icons/smmon_4.gif', + "smmon_5.gif" = 'icons/program_icons/smmon_5.gif', + "smmon_6.gif" = 'icons/program_icons/smmon_6.gif', + // "borg_mon.gif" = 'icons/program_icons/borg_mon.gif' + ) // /datum/asset/simple/radar_assets // assets = list( @@ -206,15 +206,15 @@ // "none_button.png" = 'html/none_button.png', // ) -// /datum/asset/simple/arcade -// assets = list( -// "boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif', -// "boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif', -// "boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif', -// "boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif', -// "boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif', -// "boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif', -// ) +/datum/asset/simple/arcade + assets = list( + "boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif', + "boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif', + "boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif', + "boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif', + "boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif', + "boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif', + ) // /datum/asset/spritesheet/simple/achievements // name ="achievements" @@ -433,45 +433,6 @@ // Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal") // ..() -/datum/asset/nanoui - var/list/common = list() - - var/list/common_dirs = list( - "nano/css/", - "nano/images/", - "nano/images/modular_computers/", - "nano/js/" - ) - var/list/template_dirs = list( - "nano/templates/" - ) - -/datum/asset/nanoui/register() - // Crawl the directories to find files. - for(var/path in common_dirs) - var/list/filenames = flist(path) - for(var/filename in filenames) - if(copytext(filename, length(filename)) != "/") // Ignore directories. - if(fexists(path + filename)) - common[filename] = fcopy_rsc(path + filename) - register_asset(filename, common[filename]) - // Combine all templates into a single bundle. - var/list/template_data = list() - for(var/path in template_dirs) - var/list/filenames = flist(path) - for(var/filename in filenames) - if(copytext(filename, length(filename) - 4) == ".tmpl") // Ignore directories. - template_data[filename] = file2text(path + filename) - var/template_bundle = "function nanouiTemplateBundle(){return [json_encode(template_data)];}" - var/fname = "data/nano_templates_bundle.js" - fdel(fname) - text2file(template_bundle, fname) - register_asset("nano_templates_bundle.js", fcopy_rsc(fname)) - fdel(fname) - -/datum/asset/nanoui/send(client) - send_asset_list(client, common) - //Pill sprites for UIs /datum/asset/chem_master var/assets = list() @@ -522,4 +483,31 @@ /datum/asset/spritesheet/sheetmaterials/register() InsertAll("", 'icons/obj/stacks.dmi') - ..() \ No newline at end of file + ..() + +// Nanomaps +/datum/asset/simple/nanomaps + // It REALLY doesnt matter too much if these arent up to date + // They are relatively big + assets = list( + // VOREStation Edit: We don't need Southern Cross + // "southern_cross_nanomap_z1.png" = 'icons/_nanomaps/southern_cross_nanomap_z1.png', + // "southern_cross_nanomap_z10.png" = 'icons/_nanomaps/southern_cross_nanomap_z10.png', + // "southern_cross_nanomap_z2.png" = 'icons/_nanomaps/southern_cross_nanomap_z2.png', + // "southern_cross_nanomap_z3.png" = 'icons/_nanomaps/southern_cross_nanomap_z3.png', + // "southern_cross_nanomap_z5.png" = 'icons/_nanomaps/southern_cross_nanomap_z5.png', + // "southern_cross_nanomap_z6.png" = 'icons/_nanomaps/southern_cross_nanomap_z6.png', + "tether_nanomap_z1.png" = 'icons/_nanomaps/tether_nanomap_z1.png', + "tether_nanomap_z2.png" = 'icons/_nanomaps/tether_nanomap_z2.png', + "tether_nanomap_z3.png" = 'icons/_nanomaps/tether_nanomap_z3.png', + "tether_nanomap_z4.png" = 'icons/_nanomaps/tether_nanomap_z4.png', + "tether_nanomap_z5.png" = 'icons/_nanomaps/tether_nanomap_z5.png', + "tether_nanomap_z6.png" = 'icons/_nanomaps/tether_nanomap_z6.png', + "tether_nanomap_z7.png" = 'icons/_nanomaps/tether_nanomap_z7.png', + "tether_nanomap_z8.png" = 'icons/_nanomaps/tether_nanomap_z8.png', + "tether_nanomap_z9.png" = 'icons/_nanomaps/tether_nanomap_z9.png', + "tether_nanomap_z10.png" = 'icons/_nanomaps/tether_nanomap_z10.png', + "tether_nanomap_z13.png" = 'icons/_nanomaps/tether_nanomap_z13.png', + "tether_nanomap_z14.png" = 'icons/_nanomaps/tether_nanomap_z14.png', + // VOREStation Edit End + ) \ No newline at end of file diff --git a/code/modules/awaymissions/loot_vr.dm b/code/modules/awaymissions/loot_vr.dm index d48c65bd61..2fecbbded8 100644 --- a/code/modules/awaymissions/loot_vr.dm +++ b/code/modules/awaymissions/loot_vr.dm @@ -391,4 +391,9 @@ /obj/structure/symbol/sa desc = "It looks like a right triangle with a dot to the side. It reminds you of a wooden strut between a wall and ceiling." - icon_state = "sa" \ No newline at end of file + icon_state = "sa" + +/obj/structure/symbol/maint + name = "maintenance panel" + desc = "This sign suggests that the wall it's attached to can be opened somehow." + icon_state = "maintenance_panel" \ No newline at end of file diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes.dm b/code/modules/client/preference_setup/loadout/loadout_eyes.dm index 989ed954d3..cc171a08b1 100644 --- a/code/modules/client/preference_setup/loadout/loadout_eyes.dm +++ b/code/modules/client/preference_setup/loadout/loadout_eyes.dm @@ -5,6 +5,16 @@ slot = slot_glasses sort_category = "Glasses and Eyewear" +/datum/gear/eyes/eyepatchwhite + display_name = "eyepatch (recolorable)" + path = /obj/item/clothing/glasses/eyepatchwhite + slot = slot_glasses + sort_category = "Glasses and Eyewear" + +/datum/gear/eyes/eyepatchwhite/New() + ..() + gear_tweaks += gear_tweak_free_color_choice + /datum/gear/eyes/glasses display_name = "Glasses, prescription" path = /obj/item/clothing/glasses/regular diff --git a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm index a01f2ff64a..c0211f71e6 100644 --- a/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm +++ b/code/modules/client/preference_setup/loadout/loadout_fluffitems_vr.dm @@ -911,6 +911,13 @@ ckeywhitelist = list("theskringdinger") character_name = list("Monty Kopic") +/datum/gear/fluff/shadow_laptop + path = /obj/item/modular_computer/laptop/preset/custom_loadout/advanced/shadowlarkens + display_name = "Shadow's Laptop" + ckeywhitelist = list("tigercat2000") + character_name = list("Shadow Larkens") + cost = 5 + /datum/gear/fluff/konor_medal path = /obj/item/clothing/accessory/medal/silver/unity display_name = "Konor's Unity Medal" diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 102280055a..c8e9f3414c 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -2,7 +2,7 @@ name = "clothing" siemens_coefficient = 0.9 drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/cloth.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' var/list/species_restricted = null //Only these species can wear this kit. var/gunshot_residue //Used by forensics. @@ -435,6 +435,8 @@ fingerprint_chance = 100 punch_force = 2 body_parts_covered = 0 + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' /////////////////////////////////////////////////////////////////////// //Head @@ -573,6 +575,9 @@ var/list/say_messages var/list/say_verbs + drop_sound = "generic_drop" + pickup_sound = "generic_pickup" + /obj/item/clothing/mask/update_clothing_icon() if (ismob(src.loc)) var/mob/M = src.loc @@ -733,6 +738,7 @@ siemens_coefficient = 0.9 w_class = ITEMSIZE_NORMAL preserve_item = 1 + equip_sound = 'sound/items/jumpsuit_equip.ogg' sprite_sheets = list( diff --git a/code/modules/clothing/ears/ears.dm b/code/modules/clothing/ears/ears.dm index 8fdd6deb94..07e74d7321 100644 --- a/code/modules/clothing/ears/ears.dm +++ b/code/modules/clothing/ears/ears.dm @@ -53,8 +53,8 @@ desc = "A delicate golden chain worn by female skrell to decorate their head tails." icon_state = "skrell_chain" item_state_slots = list(slot_r_hand_str = "egg5", slot_l_hand_str = "egg5") - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' /obj/item/clothing/ears/skrell/chain/silver name = "Silver headtail chains" @@ -85,8 +85,8 @@ desc = "Golden metallic bands worn by male skrell to adorn their head tails." icon_state = "skrell_band" item_state_slots = list(slot_r_hand_str = "egg5", slot_l_hand_str = "egg5") - drop_sound = 'sound/items/drop/accessory.ogg' - pickup_sound = 'sound/items/pickup/accessory.ogg' + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' /obj/item/clothing/ears/skrell/band/silver name = "Silver headtail bands" diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index ff9a69fea8..e65590f86c 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -199,6 +199,30 @@ BLIND // can't see anything icon_state = initial(icon_state) update_clothing_icon() +/obj/item/clothing/glasses/eyepatchwhite + name = "eyepatch" + desc = "A simple eyepatch made of a strip of cloth tied around the head." + icon_state = "eyepatch_white" + item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") + body_parts_covered = 0 + var/eye = null + drop_sound = 'sound/items/drop/gloves.ogg' + pickup_sound = 'sound/items/pickup/gloves.ogg' + +/obj/item/clothing/glasses/eyepatchwhite/verb/switcheye() + set name = "Switch Eyepatch" + set category = "Object" + set src in usr + if(!istype(usr, /mob/living)) return + if(usr.stat) return + + eye = !eye + if(eye) + icon_state = "[icon_state]_1" + else + icon_state = initial(icon_state) + update_clothing_icon() + /obj/item/clothing/glasses/monocle name = "monocle" desc = "Such a dapper eyepiece!" diff --git a/code/modules/clothing/glasses/hud_vr.dm b/code/modules/clothing/glasses/hud_vr.dm index 613c09e91e..718b9e6908 100644 --- a/code/modules/clothing/glasses/hud_vr.dm +++ b/code/modules/clothing/glasses/hud_vr.dm @@ -8,8 +8,6 @@ var/obj/item/clothing/glasses/hud/omni/hud = null var/mode = "civ" icon_state = "glasses" - var/datum/nano_module/arscreen - var/arscreen_path var/datum/tgui_module/tgarscreen var/tgarscreen_path var/flash_prot = 0 //0 for none, 1 for flash weapon protection, 2 for welder protection @@ -18,34 +16,24 @@ /obj/item/clothing/glasses/omnihud/New() ..() - if(arscreen_path) - arscreen = new arscreen_path(src) if(tgarscreen_path) tgarscreen = new tgarscreen_path(src) /obj/item/clothing/glasses/omnihud/Destroy() - QDEL_NULL(arscreen) QDEL_NULL(tgarscreen) . = ..() /obj/item/clothing/glasses/omnihud/dropped() - if(arscreen) - SSnanoui.close_uis(src) if(tgarscreen) SStgui.close_uis(src) ..() /obj/item/clothing/glasses/omnihud/emp_act(var/severity) - if(arscreen) - SSnanoui.close_uis(src) if(tgarscreen) SStgui.close_uis(src) - var/disconnect_ar = arscreen var/disconnect_tgar = tgarscreen - arscreen = null tgarscreen = null spawn(20 SECONDS) - arscreen = disconnect_ar tgarscreen = disconnect_tgar //extra fun for non-sci variants; a small chance flip the state to the dumb 3d glasses when EMP'd diff --git a/code/modules/clothing/head/flowercrowns.dm b/code/modules/clothing/head/flowercrowns.dm index 15bfe16162..81193f337d 100644 --- a/code/modules/clothing/head/flowercrowns.dm +++ b/code/modules/clothing/head/flowercrowns.dm @@ -37,6 +37,7 @@ icon_state = "sunflower_crown" body_parts_covered = 0 drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' /obj/item/clothing/head/lavender_crown name = "lavender crown" @@ -44,6 +45,7 @@ icon_state = "lavender_crown" body_parts_covered = 0 drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' /obj/item/clothing/head/poppy_crown name = "poppy crown" @@ -51,6 +53,7 @@ icon_state = "poppy_crown" body_parts_covered = 0 drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' /obj/item/clothing/head/rose_crown name = "rose crown" @@ -58,3 +61,4 @@ icon_state = "poppy_crown" body_parts_covered = 0 drop_sound = 'sound/items/drop/herb.ogg' + pickup_sound = 'sound/items/pickup/herb.ogg' diff --git a/code/modules/clothing/head/misc.dm b/code/modules/clothing/head/misc.dm index 818c91eda5..dff571c50f 100644 --- a/code/modules/clothing/head/misc.dm +++ b/code/modules/clothing/head/misc.dm @@ -17,8 +17,8 @@ desc = "A nice hair pin." slot_flags = SLOT_HEAD | SLOT_EARS body_parts_covered = 0 - drop_sound = 'sound/items/drop/ring.ogg' - pickup_sound = 'sound/items/pickup/ring.ogg' + drop_sound = 'sound/items/drop/accessory.ogg' + pickup_sound = 'sound/items/pickup/accessory.ogg' /obj/item/clothing/head/pin/pink icon_state = "pinkpin" @@ -424,7 +424,8 @@ desc = "A jeweled headpiece originating in India." icon_state = "maangtikka" body_parts_covered = 0 - drop_sound = 'sound/items/drop/accessory.ogg' + drop_sound = 'sound/items/drop/ring.ogg' + pickup_sound = 'sound/items/pickup/ring.ogg' /obj/item/clothing/head/jingasa name = "jingasa" diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index b64f437207..cf7a0bc939 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -10,6 +10,8 @@ permeability_coefficient = 0.50 var/hanging = 0 action_button_name = "Adjust Breath Mask" + pickup_sound = 'sound/items/pickup/component.ogg' + drop_sound = 'sound/items/drop/component.ogg' /obj/item/clothing/mask/breath/proc/adjust_mask(mob/user) diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 116a54e494..25b9e00f68 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -13,6 +13,7 @@ var/gas_filter_strength = 1 //For gas mask filters var/list/filtered_gases = list("phoron", "nitrous_oxide") armor = list(melee = 0, bullet = 0, laser = 0, energy = 0, bomb = 0, bio = 75, rad = 0) + pickup_sound = 'sound/items/pickup/rubber.ogg' /obj/item/clothing/mask/gas/filter_air(datum/gas_mixture/air) var/datum/gas_mixture/gas_filtered = new diff --git a/code/modules/clothing/shoes/miscellaneous.dm b/code/modules/clothing/shoes/miscellaneous.dm index 25b8558da9..996b2a9761 100644 --- a/code/modules/clothing/shoes/miscellaneous.dm +++ b/code/modules/clothing/shoes/miscellaneous.dm @@ -108,7 +108,7 @@ obj/item/clothing/shoes/sandal/clogs species_restricted = null w_class = ITEMSIZE_SMALL drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/cloth.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' /obj/item/clothing/shoes/slippers/worn name = "worn bunny slippers" @@ -172,7 +172,7 @@ obj/item/clothing/shoes/sandal/clogs w_class = ITEMSIZE_SMALL species_restricted = null drop_sound = 'sound/items/drop/clothing.ogg' - pickup_sound = 'sound/items/pickup/cloth.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' /obj/item/clothing/shoes/boots/ranger var/bootcolor = "white" diff --git a/code/modules/clothing/spacesuits/rig/modules/computer.dm b/code/modules/clothing/spacesuits/rig/modules/computer.dm index cf055ae9c0..fbf08479f4 100644 --- a/code/modules/clothing/spacesuits/rig/modules/computer.dm +++ b/code/modules/clothing/spacesuits/rig/modules/computer.dm @@ -23,7 +23,7 @@ to_chat(usr, "Your module is not installed in a hardsuit.") return - module.holder.ui_interact(usr, nano_state = contained_state) + module.holder.tgui_interact(usr, custom_state = GLOB.tgui_contained_state) /obj/item/rig_module/ai_container diff --git a/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm b/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm index e6848a66d0..4f7dfef043 100644 --- a/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm +++ b/code/modules/clothing/spacesuits/rig/modules/specific/ai_container.dm @@ -134,7 +134,7 @@ if(!target) if(ai_card) if(istype(ai_card,/obj/item/device/aicard)) - ai_card.tgui_interact(H, custom_state = deep_inventory_state) + ai_card.tgui_interact(H, custom_state = GLOB.tgui_deep_inventory_state) else eject_ai(H) update_verb_holder() diff --git a/code/modules/clothing/under/accessories/badges.dm b/code/modules/clothing/under/accessories/badges.dm index e6a0a787a9..085279c424 100644 --- a/code/modules/clothing/under/accessories/badges.dm +++ b/code/modules/clothing/under/accessories/badges.dm @@ -13,9 +13,6 @@ var/stored_name var/badge_string = "Corporate Security" - - drop_sound = 'sound/items/drop/ring.ogg' - pickup_sound = 'sound/items/pickup/ring.ogg' /obj/item/clothing/accessory/badge/old name = "faded badge" diff --git a/code/modules/economy/Accounts_DB.dm b/code/modules/economy/Accounts_DB.dm index 9e116ecf60..68f71184e9 100644 --- a/code/modules/economy/Accounts_DB.dm +++ b/code/modules/economy/Accounts_DB.dm @@ -15,7 +15,7 @@ var/const/fund_cap = 1000000 /obj/machinery/account_database/proc/get_access_level() - if (!held_card) + if(!held_card) return 0 if(access_cent_captain in held_card.access) return 2 @@ -53,19 +53,24 @@ O.loc = src held_card = O - SSnanoui.update_uis(src) + SStgui.update_uis(src) attack_hand(user) /obj/machinery/account_database/attack_hand(mob/user as mob) if(stat & (NOPOWER|BROKEN)) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/account_database/ui_interact(mob/user, ui_key="main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) +/obj/machinery/account_database/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "AccountsTerminal", name) + ui.open() + + +/obj/machinery/account_database/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() - var/data[0] - data["src"] = "\ref[src]" data["id_inserted"] = !!held_card data["id_card"] = held_card ? text("[held_card.registered_name], [held_card.assignment]") : "-----" data["access_level"] = get_access_level() @@ -73,10 +78,14 @@ data["creating_new_account"] = creating_new_account data["detailed_account_view"] = !!detailed_account_view data["station_account_number"] = station_account.account_number - data["transactions"] = null - data["accounts"] = null - if (detailed_account_view) + data["account_number"] = null + data["owner_name"] = null + data["money"] = null + data["suspended"] = null + data["transactions"] = list() + + if(detailed_account_view) data["account_number"] = detailed_account_view.account_number data["owner_name"] = detailed_account_view.owner_name data["money"] = detailed_account_view.money @@ -92,11 +101,10 @@ "amount" = T.amount, \ "source_terminal" = T.source_terminal))) - if (trx.len > 0) - data["transactions"] = trx + data["transactions"] = trx - var/list/accounts[0] - for(var/i=1, i<=all_money_accounts.len, i++) + var/list/accounts = list() + for(var/i in 1 to LAZYLEN(all_money_accounts)) var/datum/money_account/D = all_money_accounts[i] if(D.offmap) continue @@ -106,174 +114,168 @@ "suspended"=D.suspended ? "SUSPENDED" : "",\ "account_index"=i))) - if (accounts.len > 0) - data["accounts"] = accounts + data["accounts"] = accounts - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "accounts_terminal.tmpl", src.name, 400, 640) - ui.set_initial_data(data) - ui.open() + return data -/obj/machinery/account_database/Topic(href, href_list) +/obj/machinery/account_database/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 + return TRUE - var/datum/nanoui/ui = SSnanoui.get_open_ui(usr, src, "main") + switch(action) + if("create_account") + creating_new_account = 1 - if(href_list["choice"]) - switch(href_list["choice"]) - if("create_account") - creating_new_account = 1 + if("add_funds") + var/amount = input("Enter the amount you wish to add", "Silently add funds") as num + if(detailed_account_view) + detailed_account_view.money = min(detailed_account_view.money + amount, fund_cap) - if("add_funds") - var/amount = input("Enter the amount you wish to add", "Silently add funds") as num - if(detailed_account_view) - detailed_account_view.money = min(detailed_account_view.money + amount, fund_cap) + if("remove_funds") + var/amount = input("Enter the amount you wish to remove", "Silently remove funds") as num + if(detailed_account_view) + detailed_account_view.money = max(detailed_account_view.money - amount, -fund_cap) - if("remove_funds") - var/amount = input("Enter the amount you wish to remove", "Silently remove funds") as num - if(detailed_account_view) - detailed_account_view.money = max(detailed_account_view.money - amount, -fund_cap) + if("toggle_suspension") + if(detailed_account_view) + detailed_account_view.suspended = !detailed_account_view.suspended + callHook("change_account_status", list(detailed_account_view)) - if("toggle_suspension") - if(detailed_account_view) - detailed_account_view.suspended = !detailed_account_view.suspended - callHook("change_account_status", list(detailed_account_view)) + if("finalise_create_account") + var/account_name = params["holder_name"] + var/starting_funds = max(text2num(params["starting_funds"]), 0) - if("finalise_create_account") - var/account_name = href_list["holder_name"] - var/starting_funds = max(text2num(href_list["starting_funds"]), 0) + starting_funds = CLAMP(starting_funds, 0, station_account.money) // Not authorized to put the station in debt. + starting_funds = min(starting_funds, fund_cap) // Not authorized to give more than the fund cap. - starting_funds = CLAMP(starting_funds, 0, station_account.money) // Not authorized to put the station in debt. - starting_funds = min(starting_funds, fund_cap) // Not authorized to give more than the fund cap. + create_account(account_name, starting_funds, src) + if(starting_funds > 0) + //subtract the money + station_account.money -= starting_funds - create_account(account_name, starting_funds, src) - if(starting_funds > 0) - //subtract the money - station_account.money -= starting_funds - - //create a transaction log entry - var/trx = create_transation(account_name, "New account activation", "([starting_funds])") - station_account.transaction_log.Add(trx) - - creating_new_account = 0 - ui.close() + //create a transaction log entry + var/trx = create_transation(account_name, "New account activation", "([starting_funds])") + station_account.transaction_log.Add(trx) creating_new_account = 0 - if("insert_card") - if(held_card) - held_card.loc = src.loc - if(ishuman(usr) && !usr.get_active_hand()) - usr.put_in_hands(held_card) - held_card = null + creating_new_account = 0 + if("insert_card") + if(held_card) + held_card.loc = src.loc - else - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/weapon/card/id)) - var/obj/item/weapon/card/id/C = I - usr.drop_item() - C.loc = src - held_card = C + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(held_card) + held_card = null - if("view_account_detail") - var/index = text2num(href_list["account_index"]) - if(index && index <= all_money_accounts.len) - detailed_account_view = all_money_accounts[index] + else + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/C = I + usr.drop_item() + C.loc = src + held_card = C - if("view_accounts_list") - detailed_account_view = null - creating_new_account = 0 + if("view_account_detail") + var/index = text2num(params["account_index"]) + if(index && index <= all_money_accounts.len) + detailed_account_view = all_money_accounts[index] - if("revoke_payroll") - var/funds = detailed_account_view.money - var/account_trx = create_transation(station_account.owner_name, "Revoke payroll", "([funds])") - var/station_trx = create_transation(detailed_account_view.owner_name, "Revoke payroll", funds) + if("view_accounts_list") + detailed_account_view = null + creating_new_account = 0 - station_account.money += funds - detailed_account_view.money = 0 + if("revoke_payroll") + var/funds = detailed_account_view.money + var/account_trx = create_transation(station_account.owner_name, "Revoke payroll", "([funds])") + var/station_trx = create_transation(detailed_account_view.owner_name, "Revoke payroll", funds) - detailed_account_view.transaction_log.Add(account_trx) - station_account.transaction_log.Add(station_trx) + station_account.money += funds + detailed_account_view.money = 0 - callHook("revoke_payroll", list(detailed_account_view)) + detailed_account_view.transaction_log.Add(account_trx) + station_account.transaction_log.Add(station_trx) - if("print") - var/text - var/obj/item/weapon/paper/P = new(loc) - if (detailed_account_view) - P.name = "account #[detailed_account_view.account_number] details" - var/title = "Account #[detailed_account_view.account_number] Details" - text = {" - [accounting_letterhead(title)] - Holder: [detailed_account_view.owner_name]
- Balance: $[detailed_account_view.money]
- Status: [detailed_account_view.suspended ? "Suspended" : "Active"]
- Transactions: ([detailed_account_view.transaction_log.len])
- - - - - - - - - - - - "} + callHook("revoke_payroll", list(detailed_account_view)) - for (var/datum/transaction/T in detailed_account_view.transaction_log) - text += {" - - - - - - - - "} + if("print") + print() - text += {" - -
TimestampTargetReasonValueTerminal
[T.date] [T.time][T.target_name][T.purpose][T.amount][T.source_terminal]
- "} + return TRUE - else - P.name = "financial account list" - text = {" - [accounting_letterhead("Financial Account List")] +/obj/machinery/account_database/proc/print() + var/text + var/obj/item/weapon/paper/P = new(loc) + if(detailed_account_view) + P.name = "account #[detailed_account_view.account_number] details" + var/title = "Account #[detailed_account_view.account_number] Details" + text = {" + [accounting_letterhead(title)] + Holder: [detailed_account_view.owner_name]
+ Balance: $[detailed_account_view.money]
+ Status: [detailed_account_view.suspended ? "Suspended" : "Active"]
+ Transactions: ([detailed_account_view.transaction_log.len])
+ + + + + + + + + + + + "} -
TimestampTargetReasonValueTerminal
- - - - - - - - - - "} + for (var/datum/transaction/T in detailed_account_view.transaction_log) + text += {" + + + + + + + + "} - for(var/i=1, i<=all_money_accounts.len, i++) - var/datum/money_account/D = all_money_accounts[i] - text += {" - - - - - - - "} + text += {" + +
Account NumberHolderBalanceStatus
[T.date] [T.time][T.target_name][T.purpose][T.amount][T.source_terminal]
#[D.account_number][D.owner_name]$[D.money][D.suspended ? "Suspended" : "Active"]
+ "} - text += {" - - - "} + else + P.name = "financial account list" + text = {" + [accounting_letterhead("Financial Account List")] - P.info = text - state("The terminal prints out a report.") + + + + + + + + + + + "} - return 1 + for(var/i=1, i<=all_money_accounts.len, i++) + var/datum/money_account/D = all_money_accounts[i] + text += {" + + + + + + + "} + + text += {" + +
Account NumberHolderBalanceStatus
#[D.account_number][D.owner_name]$[D.money][D.suspended ? "Suspended" : "Active"]
+ "} + + P.info = text + state("The terminal prints out a report.") \ No newline at end of file diff --git a/code/modules/events/money_spam.dm b/code/modules/events/money_spam.dm index 0b877cef3d..ad8a1bbc3f 100644 --- a/code/modules/events/money_spam.dm +++ b/code/modules/events/money_spam.dm @@ -36,7 +36,11 @@ var/obj/item/device/pda/P var/list/viables = list() for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) - if (!check_pda.owner||check_pda.toff||check_pda == src||check_pda.hidden) + if (!check_pda.owner || check_pda == src || check_pda.hidden) + continue + + var/datum/data/pda/app/messenger/M = check_pda.find_program(/datum/data/pda/app/messenger) + if(!M || M.toff) continue viables.Add(check_pda) @@ -112,17 +116,5 @@ //Commented out because we don't send messages like this anymore. Instead it will just popup in their chat window. //P.tnote += "← From [sender] (Unknown / spam?):
[message]
" - if (!P.message_silent) - playsound(P, 'sound/machines/twobeep.ogg', 50, 1) - for (var/mob/O in hearers(3, P.loc)) - if(!P.message_silent) O.show_message(text("[bicon(P)] *[P.ttone]*")) - //Search for holder of the PDA. - var/mob/living/L = null - if(P.loc && isliving(P.loc)) - L = P.loc - //Maybe they are a pAI! - else - L = get(P, /mob/living/silicon) - - if(L) - to_chat(L, "[bicon(P)] Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)") + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + PM.notify("Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)", 0) diff --git a/code/modules/events/supply_demand_vr.dm b/code/modules/events/supply_demand_vr.dm index 6c59145fe5..6447630c9e 100644 --- a/code/modules/events/supply_demand_vr.dm +++ b/code/modules/events/supply_demand_vr.dm @@ -84,17 +84,9 @@ var/datum/supply_demand_order/random = pick(required_items) command_announcement.Announce("What happened? Accounting is here right now and they're already asking where that [random.name] is. Damn, I gotta go", my_department) var/message = "The delivery deadline was reached with the following needs outstanding:
" - for (var/datum/supply_demand_order/req in required_items) + for(var/datum/supply_demand_order/req in required_items) message += req.describe() + "
" - for (var/obj/machinery/computer/communications/C in machines) - if(C.operable()) - var/obj/item/weapon/paper/P = new /obj/item/weapon/paper( C.loc ) - P.name = "'[my_department] Mission Summary'" - P.info = message - P.update_space(P.info) - P.update_icon() - C.messagetitle.Add("[my_department] Mission Summary") - C.messagetext.Add(P.info) + post_comm_message("'[my_department] Mission Summary'", message) /** * Event Handler for responding to the supply shuttle arriving at centcom. */ diff --git a/code/modules/food/food/drinks.dm b/code/modules/food/food/drinks.dm index 044bd74491..2723df7fe2 100644 --- a/code/modules/food/food/drinks.dm +++ b/code/modules/food/food/drinks.dm @@ -5,8 +5,8 @@ name = "drink" desc = "yummy" icon = 'icons/obj/drinks.dmi' - drop_sound = 'sound/items/drop/bottle.ogg' - pickup_sound = 'sound/items/pickup/bottle.ogg' + drop_sound = 'sound/items/drop/drinkglass.ogg' + pickup_sound = 'sound/items/pickup/drinkglass.ogg' icon_state = null flags = OPENCONTAINER amount_per_transfer_from_this = 5 @@ -234,6 +234,7 @@ trash = /obj/item/trash/coffee center_of_mass = list("x"=15, "y"=13) drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' /obj/item/weapon/reagent_containers/food/drinks/h_chocolate/Initialize() ..() @@ -248,6 +249,7 @@ trash = /obj/item/trash/coffee center_of_mass = list("x"=16, "y"=14) drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' /obj/item/weapon/reagent_containers/food/drinks/greentea/Initialize() . = ..() @@ -262,6 +264,7 @@ trash = /obj/item/trash/coffee center_of_mass = list("x"=16, "y"=14) drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' /obj/item/weapon/reagent_containers/food/drinks/chaitea/Initialize() . = ..() @@ -276,6 +279,7 @@ trash = /obj/item/trash/coffee center_of_mass = list("x"=16, "y"=14) drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' /obj/item/weapon/reagent_containers/food/drinks/decaf/Initialize() . = ..() @@ -289,6 +293,7 @@ trash = /obj/item/trash/ramen center_of_mass = list("x"=16, "y"=11) drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' /obj/item/weapon/reagent_containers/food/drinks/dry_ramen/Initialize() ..() @@ -302,6 +307,7 @@ volume = 10 center_of_mass = list("x"=16, "y"=12) drop_sound = 'sound/items/drop/papercup.ogg' + pickup_sound = 'sound/items/pickup/papercup.ogg' /obj/item/weapon/reagent_containers/food/drinks/sillycup/Initialize() . = ..() diff --git a/code/modules/food/food/drinks/bottle.dm b/code/modules/food/food/drinks/bottle.dm index b5d72d733f..a5fd76417b 100644 --- a/code/modules/food/food/drinks/bottle.dm +++ b/code/modules/food/food/drinks/bottle.dm @@ -19,6 +19,7 @@ if(isGlass) unacidable = 1 drop_sound = 'sound/items/drop/bottle.ogg' + pickup_sound = 'sound/items/pickup/bottle.ogg' /obj/item/weapon/reagent_containers/food/drinks/bottle/Destroy() if(rag) diff --git a/code/modules/food/food/drinks/drinkingglass.dm b/code/modules/food/food/drinks/drinkingglass.dm index 260c4a7ae6..7324dc8ad8 100644 --- a/code/modules/food/food/drinks/drinkingglass.dm +++ b/code/modules/food/food/drinks/drinkingglass.dm @@ -8,8 +8,6 @@ volume = 30 unacidable = 1 //glass center_of_mass = list("x"=16, "y"=10) - drop_sound = 'sound/items/drop/drinkglass.ogg' - pickup_sound = 'sound/items/pickup/drinkglass.ogg' matter = list("glass" = 500) on_reagent_change() diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm index 65492f0109..ad30c244f2 100644 --- a/code/modules/food/food/snacks.dm +++ b/code/modules/food/food/snacks.dm @@ -318,6 +318,7 @@ nutriment_amt = 1 nutriment_desc = list("candy" = 1) + /obj/item/weapon/reagent_containers/food/snacks/candy/Initialize() . = ..() reagents.add_reagent("sugar", 3) @@ -4161,6 +4162,8 @@ trash = /obj/item/trash/unajerky filling_color = "#631212" center_of_mass = list("x"=15, "y"=9) + drop_sound = 'sound/items/drop/soda.ogg' + pickup_sound = 'sound/items/pickup/soda.ogg' /obj/item/weapon/reagent_containers/food/snacks/unajerky/Initialize() . =..() diff --git a/code/modules/food/kitchen/cooking_machines/_appliance.dm b/code/modules/food/kitchen/cooking_machines/_appliance.dm index a0be64fff9..b2f51eacd7 100644 --- a/code/modules/food/kitchen/cooking_machines/_appliance.dm +++ b/code/modules/food/kitchen/cooking_machines/_appliance.dm @@ -16,7 +16,7 @@ use_power = USE_POWER_IDLE idle_power_usage = 5 // Power used when turned on, but not processing anything active_power_usage = 1000 // Power used when turned on and actively cooking something - + var/cooking_power = 0 // Effectiveness/speed at cooking var/cooking_coeff = 0 // Optimal power * proximity to optimal temp; used to calc. cooking power. var/heating_power = 1000 // Effectiveness at heating up; not used for mixers, should be equal to active_power_usage @@ -44,9 +44,9 @@ /obj/machinery/appliance/Initialize() . = ..() - + default_apply_parts() - + if(output_options.len) verbs += /obj/machinery/appliance/proc/choose_output @@ -206,7 +206,7 @@ //Handles all validity checking and error messages for inserting things /obj/machinery/appliance/proc/can_insert(var/obj/item/I, var/mob/user) - if (istype(I, /obj/item/weapon/gripper)) + if(istype(I.loc, /mob/living/silicon)) return 0 else if (istype(I.loc, /obj/item/rig_module)) return 0 @@ -266,26 +266,52 @@ to_chat(user, "\The [src] is not working.") return FALSE - var/result = can_insert(I, user) - if(!result) - if(!(default_deconstruction_screwdriver(user, I))) - default_part_replacement(user, I) - return FALSE + var/obj/item/ToCook = I - if(result == 2) - var/obj/item/weapon/grab/G = I - if (G && istype(G) && G.affecting) - cook_mob(G.affecting, user) - return FALSE + if(istype(I, /obj/item/weapon/gripper)) + var/obj/item/weapon/gripper/GR = I + var/obj/item/Wrap = GR.wrapped + if(Wrap) + Wrap.loc = get_turf(src) + var/result = can_insert(Wrap, user) + if(!result) + Wrap.forceMove(GR) + if(!(default_deconstruction_screwdriver(user, I))) + default_part_replacement(user, I) + return + + if(QDELETED(GR.wrapped)) + GR.wrapped = null + + if(GR?.wrapped.loc != src) + GR.drop_item_nm() + + ToCook = Wrap + else + attack_hand(user) + return + + else + var/result = can_insert(I, user) + if(!result) + if(!(default_deconstruction_screwdriver(user, I))) + default_part_replacement(user, I) + return + + if(result == 2) + var/obj/item/weapon/grab/G = I + if (G && istype(G) && G.affecting) + cook_mob(G.affecting, user) + return //From here we can start cooking food - . = add_content(I, user) + add_content(ToCook, user) update_icon() //Override for container mechanics /obj/machinery/appliance/proc/add_content(var/obj/item/I, var/mob/user) - if(!user.unEquip(I)) - return FALSE + if(!user.unEquip(I) && !isturf(I.loc)) + return var/datum/cooking_item/CI = has_space(I) if (istype(I, /obj/item/weapon/reagent_containers/cooking_container) && CI == 1) @@ -458,7 +484,7 @@ //Final step. Cook function just cooks batter for now. for (var/obj/item/weapon/reagent_containers/food/snacks/S in CI.container) S.cook() - + //Combination cooking involves combining the names and reagents of ingredients into a predefined output object //The ingredients represent flavours or fillings. EG: donut pizza, cheese bread @@ -566,7 +592,7 @@ smoke.attach(src) smoke.set_up(10, 0, get_turf(src), 300) smoke.start() - + // Set off fire alarms! var/obj/machinery/firealarm/FA = locate() in get_area(src) if(FA) diff --git a/code/modules/food/kitchen/cooking_machines/container.dm b/code/modules/food/kitchen/cooking_machines/container.dm index d650a2266e..408835a4da 100644 --- a/code/modules/food/kitchen/cooking_machines/container.dm +++ b/code/modules/food/kitchen/cooking_machines/container.dm @@ -32,13 +32,26 @@ /obj/item/weapon/reagent_containers/cooking_container/attackby(var/obj/item/I as obj, var/mob/user as mob) + if(istype(I, /obj/item/weapon/gripper)) + var/obj/item/weapon/gripper/GR = I + if(GR.wrapped) + GR.wrapped.forceMove(get_turf(src)) + attackby(GR.wrapped, user) + if(QDELETED(GR.wrapped)) + GR.wrapped = null + + if(GR?.wrapped.loc != src) + GR.wrapped = null + + return + for (var/possible_type in insertable) if (istype(I, possible_type)) if (!can_fit(I)) to_chat(user, "There's no more space in the [src] for that!") return 0 - if(!user.unEquip(I)) + if(!user.unEquip(I) && !isturf(I.loc)) return I.forceMove(src) to_chat(user, "You put the [I] into the [src].") @@ -152,7 +165,7 @@ /obj/item/weapon/reagent_containers/cooking_container/oven/Initialize() . = ..() - + // We add to the insertable list specifically for the oven trays, to allow specialty cakes. insertable += list( /obj/item/clothing/head/cakehat, // This is because we want to allow birthday cakes to be makeable. @@ -164,7 +177,7 @@ shortname = "basket" desc = "Put ingredients in this; designed for use with a deep fryer. Warranty void if used incorrectly. Alt click to remove contents." icon_state = "basket" - + /obj/item/weapon/reagent_containers/cooking_container/grill name = "grill rack" shortname = "rack" diff --git a/code/modules/food/recipes_oven.dm b/code/modules/food/recipes_oven.dm index 7a477cf943..6524763a96 100644 --- a/code/modules/food/recipes_oven.dm +++ b/code/modules/food/recipes_oven.dm @@ -81,7 +81,7 @@ /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough ) result = /obj/item/weapon/reagent_containers/food/snacks/flatbread - + /datum/recipe/tortilla appliance = OVEN reagents = list("flour" = 5) @@ -408,7 +408,7 @@ /obj/item/weapon/reagent_containers/food/snacks/cheesewedge ) result = /obj/item/weapon/reagent_containers/food/snacks/sliceable/cheesecake - + /datum/recipe/cake/peanut fruit = list("peanut" = 3) reagents = list("milk" = 5, "flour" = 10, "sugar" = 5, "egg" = 6, "peanutbutter" = 5) @@ -458,7 +458,7 @@ /datum/recipe/pancakes appliance = OVEN - fruit = list("blueberries" = 2) + fruit = list("berries" = 2) items = list( /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough, /obj/item/weapon/reagent_containers/food/snacks/sliceable/flatdough diff --git a/code/modules/gamemaster/event2/events/everyone/pda_spam.dm b/code/modules/gamemaster/event2/events/everyone/pda_spam.dm index b56687283c..c6a1532d0e 100644 --- a/code/modules/gamemaster/event2/events/everyone/pda_spam.dm +++ b/code/modules/gamemaster/event2/events/everyone/pda_spam.dm @@ -35,7 +35,11 @@ var/list/viables = list() for(var/obj/item/device/pda/check_pda in sortAtom(PDAs)) - if(!check_pda.owner || check_pda.toff || check_pda.hidden || check_pda.spam_proof) + if (!check_pda.owner || check_pda == src || check_pda.hidden) + continue + + var/datum/data/pda/app/messenger/M = check_pda.find_program(/datum/data/pda/app/messenger) + if(!M || M.toff) continue viables += check_pda @@ -93,7 +97,7 @@ message = pick("Luxury watches for Blowout sale prices!",\ "Watches, Jewelry & Accessories, Bags & Wallets !",\ "Deposit 100$ and get 300$ totally free!",\ - " 100K NT.|WOWGOLD õnly $89 ",\ + " 100K NT.|WOWGOLD �nly $89 ",\ "We have been filed with a complaint from one of your customers in respect of their business relations with you.",\ "We kindly ask you to open the COMPLAINT REPORT (attached) to reply on this complaint..") if(4) @@ -127,7 +131,8 @@ /datum/event2/event/pda_spam/proc/send_spam(obj/item/device/pda/P, sender, message) last_spam_time = world.time - P.spam_message(sender, message) + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + PM.notify("Message from [sender] (Unknown / spam?), \"[message]\" (Unable to Reply)", 0) if(spam_debug) log_debug("PDA Spam event sent spam to \the [P].") diff --git a/code/modules/gamemaster/event2/events/security/prison_break.dm b/code/modules/gamemaster/event2/events/security/prison_break.dm index ec07302f13..9cf3e9aac6 100644 --- a/code/modules/gamemaster/event2/events/security/prison_break.dm +++ b/code/modules/gamemaster/event2/events/security/prison_break.dm @@ -218,7 +218,7 @@ /datum/event2/event/prison_break/start() for(var/area/A in areas_to_break) spawn(0) // So we don't block the ticker. - A.prison_break(open_blast_doors = !ignore_blast_doors) + A.prison_break(TRUE, TRUE, !ignore_blast_doors) // Naming `open_blast_doors` causes mysterious runtimes. // There's between 40 seconds and one minute before the whole station knows. // If there's a baddie engineer, they can choose to keep their early announcement to themselves and get a minute to exploit it. diff --git a/code/modules/hydroponics/seed_datums.dm b/code/modules/hydroponics/seed_datums.dm deleted file mode 100644 index cbdeaca55c..0000000000 --- a/code/modules/hydroponics/seed_datums.dm +++ /dev/null @@ -1,1544 +0,0 @@ -// Chili plants/variants. -/datum/seed/chili - name = "chili" - seed_name = "chili" - display_name = "chili plants" - kitchen_tag = "chili" - chems = list("capsaicin" = list(3,5), "nutriment" = list(1,25)) - mutants = list("icechili") - -/datum/seed/chili/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,20) - set_trait(TRAIT_PRODUCT_ICON,"chili") - set_trait(TRAIT_PRODUCT_COLOUR,"#ED3300") - set_trait(TRAIT_PLANT_ICON,"bush2") - set_trait(TRAIT_IDEAL_HEAT, 298) - set_trait(TRAIT_IDEAL_LIGHT, 7) - -/datum/seed/chili/ice - name = "icechili" - seed_name = "ice pepper" - display_name = "ice-pepper plants" - kitchen_tag = "icechili" - mutants = null - chems = list("frostoil" = list(3,5), "nutriment" = list(1,50)) - -/datum/seed/chili/ice/New() - ..() - set_trait(TRAIT_MATURATION,4) - set_trait(TRAIT_PRODUCTION,4) - set_trait(TRAIT_PRODUCT_COLOUR,"#00EDC6") - -// Berry plants/variants. -/datum/seed/berry - name = "berries" - seed_name = "berry" - display_name = "berry bush" - kitchen_tag = "berries" - mutants = list("glowberries","poisonberries") - chems = list("nutriment" = list(1,10), "berryjuice" = list(10,10)) - -/datum/seed/berry/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_JUICY,1) - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"berry") - set_trait(TRAIT_PRODUCT_COLOUR,"#FA1616") - set_trait(TRAIT_PLANT_ICON,"bush") - set_trait(TRAIT_WATER_CONSUMPTION, 6) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/berry/glow - name = "glowberries" - seed_name = "glowberry" - display_name = "glowberry bush" - mutants = null - chems = list("nutriment" = list(1,10), "uranium" = list(3,5)) - -/datum/seed/berry/glow/New() - ..() - set_trait(TRAIT_SPREAD,1) - set_trait(TRAIT_BIOLUM,1) - set_trait(TRAIT_BIOLUM_COLOUR,"#006622") - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_COLOUR,"#c9fa16") - set_trait(TRAIT_WATER_CONSUMPTION, 3) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) - -/datum/seed/berry/poison - name = "poisonberries" - seed_name = "poison berry" - kitchen_tag = "poisonberries" - display_name = "poison berry bush" - mutants = list("deathberries") - chems = list("nutriment" = list(1), "toxin" = list(3,5), "poisonberryjuice" = list(10,5)) - -/datum/seed/berry/poison/New() - ..() - set_trait(TRAIT_PRODUCT_COLOUR,"#6DC961") - set_trait(TRAIT_WATER_CONSUMPTION, 3) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) - -/datum/seed/berry/poison/death - name = "deathberries" - seed_name = "death berry" - display_name = "death berry bush" - mutants = null - chems = list("nutriment" = list(1), "toxin" = list(3,3), "lexorin" = list(1,5)) - -/datum/seed/berry/poison/death/New() - ..() - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,50) - set_trait(TRAIT_PRODUCT_COLOUR,"#7A5454") - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.35) - -// Nettles/variants. -/datum/seed/nettle - name = "nettle" - seed_name = "nettle" - display_name = "nettles" - mutants = list("deathnettle") - chems = list("nutriment" = list(1,50), "sacid" = list(0,1)) - kitchen_tag = "nettle" - -/datum/seed/nettle/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_STINGS,1) - set_trait(TRAIT_PLANT_ICON,"bush5") - set_trait(TRAIT_PRODUCT_ICON,"nettles") - set_trait(TRAIT_PRODUCT_COLOUR,"#728A54") - -/datum/seed/nettle/death - name = "deathnettle" - seed_name = "death nettle" - display_name = "death nettles" - kitchen_tag = "deathnettle" - mutants = null - chems = list("nutriment" = list(1,50), "pacid" = list(0,1)) - -/datum/seed/nettle/death/New() - ..() - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_PRODUCT_COLOUR,"#8C5030") - set_trait(TRAIT_PLANT_COLOUR,"#634941") - -//Tomatoes/variants. -/datum/seed/tomato - name = "tomato" - seed_name = "tomato" - display_name = "tomato plant" - mutants = list("bluetomato","bloodtomato") - chems = list("nutriment" = list(1,10), "tomatojuice" = list(10,10)) - kitchen_tag = "tomato" - -/datum/seed/tomato/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_JUICY,1) - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"tomato") - set_trait(TRAIT_PRODUCT_COLOUR,"#D10000") - set_trait(TRAIT_PLANT_ICON,"bush3") - set_trait(TRAIT_IDEAL_LIGHT, 6) - set_trait(TRAIT_WATER_CONSUMPTION, 6) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) - -/datum/seed/tomato/blood - name = "bloodtomato" - seed_name = "blood tomato" - display_name = "blood tomato plant" - mutants = list("killertomato") - chems = list("nutriment" = list(1,10), "blood" = list(1,5)) - splat_type = /obj/effect/decal/cleanable/blood/splatter - -/datum/seed/tomato/blood/New() - ..() - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_PRODUCT_COLOUR,"#FF0000") - -/datum/seed/tomato/killer - name = "killertomato" - seed_name = "killer tomato" - display_name = "killer tomato plant" - mutants = null - can_self_harvest = 1 - has_mob_product = /mob/living/simple_mob/tomato - -/datum/seed/tomato/killer/New() - ..() - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_PRODUCT_COLOUR,"#A86747") - -/datum/seed/tomato/blue - name = "bluetomato" - seed_name = "blue tomato" - display_name = "blue tomato plant" - mutants = list("bluespacetomato") - chems = list("nutriment" = list(1,20), "lube" = list(1,5)) - -/datum/seed/tomato/blue/New() - ..() - set_trait(TRAIT_PRODUCT_COLOUR,"#4D86E8") - set_trait(TRAIT_PLANT_COLOUR,"#070AAD") - -/datum/seed/tomato/blue/teleport - name = "bluespacetomato" - seed_name = "bluespace tomato" - display_name = "bluespace tomato plant" - mutants = null - chems = list("nutriment" = list(1,20), "singulo" = list(10,5)) - -/datum/seed/tomato/blue/teleport/New() - ..() - set_trait(TRAIT_TELEPORTING,1) - set_trait(TRAIT_PRODUCT_COLOUR,"#00E5FF") - set_trait(TRAIT_BIOLUM,1) - set_trait(TRAIT_BIOLUM_COLOUR,"#4DA4A8") - -//Eggplants/varieties. -/datum/seed/eggplant - name = "eggplant" - seed_name = "eggplant" - display_name = "eggplants" - kitchen_tag = "eggplant" - mutants = list("egg-plant") - chems = list("nutriment" = list(1,10)) - -/datum/seed/eggplant/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,20) - set_trait(TRAIT_PRODUCT_ICON,"eggplant") - set_trait(TRAIT_PRODUCT_COLOUR,"#892694") - set_trait(TRAIT_PLANT_ICON,"bush4") - set_trait(TRAIT_IDEAL_HEAT, 298) - set_trait(TRAIT_IDEAL_LIGHT, 7) - -// Return of Eggy. Just makes purple eggs. If the reagents are separated from the egg production by xenobotany or RNG, it's still an Egg plant. -/datum/seed/eggplant/egg - name = "egg-plant" - seed_name = "egg-plant" - display_name = "egg-plants" - kitchen_tag = "egg-plant" - mutants = null - chems = list("nutriment" = list(1,5), "egg" = list(3,12)) - has_item_product = /obj/item/weapon/reagent_containers/food/snacks/egg/purple - -//Apples/varieties. -/datum/seed/apple - name = "apple" - seed_name = "apple" - display_name = "apple tree" - kitchen_tag = "apple" - mutants = list("poisonapple","goldapple") - chems = list("nutriment" = list(1,10),"applejuice" = list(10,20)) - -/datum/seed/apple/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,5) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"apple") - set_trait(TRAIT_PRODUCT_COLOUR,"#FF540A") - set_trait(TRAIT_PLANT_ICON,"tree2") - set_trait(TRAIT_FLESH_COLOUR,"#E8E39B") - set_trait(TRAIT_IDEAL_LIGHT, 4) - -/datum/seed/apple/poison - name = "poisonapple" - mutants = null - chems = list("cyanide" = list(1,5)) - -/datum/seed/apple/gold - name = "goldapple" - seed_name = "golden apple" - display_name = "gold apple tree" - kitchen_tag = "goldapple" - mutants = null - chems = list("nutriment" = list(1,10), "gold" = list(1,5)) - -/datum/seed/apple/gold/New() - ..() - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_PRODUCTION,10) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_PRODUCT_COLOUR,"#FFDD00") - set_trait(TRAIT_PLANT_COLOUR,"#D6B44D") - -/datum/seed/apple/sif - name = "sifbulb" - seed_name = "sivian tree" - display_name = "sivian tree" - kitchen_tag = "apple" - chems = list("nutriment" = list(1,5),"sifsap" = list(10,20)) - -/datum/seed/apple/sif/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,3) - set_trait(TRAIT_PRODUCTION,10) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,12) - set_trait(TRAIT_PRODUCT_ICON,"alien3") - set_trait(TRAIT_PRODUCT_COLOUR,"#0720c3") - set_trait(TRAIT_PLANT_ICON,"tree5") - set_trait(TRAIT_FLESH_COLOUR,"#05157d") - set_trait(TRAIT_IDEAL_LIGHT, 1) - -//Ambrosia/varieties. -/datum/seed/ambrosia - name = "ambrosia" - seed_name = "ambrosia vulgaris" - display_name = "ambrosia vulgaris" - kitchen_tag = "ambrosia" - mutants = list("ambrosiadeus") - chems = list("nutriment" = list(1), "space_drugs" = list(1,8), "kelotane" = list(1,8,1), "bicaridine" = list(1,10,1), "toxin" = list(1,10)) - -/datum/seed/ambrosia/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,6) - set_trait(TRAIT_POTENCY,5) - set_trait(TRAIT_PRODUCT_ICON,"ambrosia") - set_trait(TRAIT_PRODUCT_COLOUR,"#9FAD55") - set_trait(TRAIT_PLANT_ICON,"ambrosia") - set_trait(TRAIT_IDEAL_LIGHT, 6) - -/datum/seed/ambrosia/deus - name = "ambrosiadeus" - seed_name = "ambrosia deus" - display_name = "ambrosia deus" - kitchen_tag = "ambrosiadeus" - mutants = list("ambrosiainfernus") - chems = list("nutriment" = list(1), "bicaridine" = list(1,8), "synaptizine" = list(1,8,1), "hyperzine" = list(1,10,1), "space_drugs" = list(1,10)) - -/datum/seed/ambrosia/deus/New() - ..() - set_trait(TRAIT_PRODUCT_COLOUR,"#A3F0AD") - set_trait(TRAIT_PLANT_COLOUR,"#2A9C61") - -/datum/seed/ambrosia/infernus - name = "ambrosiainfernus" - seed_name = "ambrosia infernus" - display_name = "ambrosia infernus" - kitchen_tag = "ambrosiainfernus" - mutants = null - chems = list("nutriment" = list(1,3), "oxycodone" = list(1,8), "impedrezene" = list(1,10), "mindbreaker" = list(1,10)) - -/datum/seed/ambrosia/infernus/New() - ..() - set_trait(TRAIT_PRODUCT_COLOUR,"#dc143c") - set_trait(TRAIT_PLANT_COLOUR,"#b22222") - -//Mushrooms/varieties. -/datum/seed/mushroom - name = "mushrooms" - seed_name = "chanterelle" - seed_noun = "spores" - display_name = "chanterelle mushrooms" - mutants = list("reishi","amanita","plumphelmet") - chems = list("nutriment" = list(1,25)) - splat_type = /obj/effect/plant - kitchen_tag = "mushroom" - -/datum/seed/mushroom/New() - ..() - set_trait(TRAIT_MATURATION,7) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,5) - set_trait(TRAIT_POTENCY,1) - set_trait(TRAIT_PRODUCT_ICON,"mushroom4") - set_trait(TRAIT_PRODUCT_COLOUR,"#DBDA72") - set_trait(TRAIT_PLANT_COLOUR,"#D9C94E") - set_trait(TRAIT_PLANT_ICON,"mushroom") - set_trait(TRAIT_WATER_CONSUMPTION, 6) - set_trait(TRAIT_IDEAL_HEAT, 288) - set_trait(TRAIT_LIGHT_TOLERANCE, 6) - -/datum/seed/mushroom/mold - name = "mold" - seed_name = "brown mold" - display_name = "brown mold" - mutants = null - -/datum/seed/mushroom/mold/New() - ..() - set_trait(TRAIT_SPREAD,1) - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_YIELD,-1) - set_trait(TRAIT_PRODUCT_ICON,"mushroom5") - set_trait(TRAIT_PRODUCT_COLOUR,"#7A5F20") - set_trait(TRAIT_PLANT_COLOUR,"#7A5F20") - set_trait(TRAIT_PLANT_ICON,"mushroom9") - -/datum/seed/mushroom/plump - name = "plumphelmet" - seed_name = "plump helmet" - display_name = "plump helmet mushrooms" - mutants = list("walkingmushroom","towercap") - chems = list("nutriment" = list(2,10)) - kitchen_tag = "plumphelmet" - -/datum/seed/mushroom/plump/New() - ..() - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,0) - set_trait(TRAIT_PRODUCT_ICON,"mushroom10") - set_trait(TRAIT_PRODUCT_COLOUR,"#B57BB0") - set_trait(TRAIT_PLANT_COLOUR,"#9E4F9D") - set_trait(TRAIT_PLANT_ICON,"mushroom2") - -/datum/seed/mushroom/hallucinogenic - name = "reishi" - seed_name = "reishi" - display_name = "reishi" - mutants = list("libertycap","glowshroom") - chems = list("nutriment" = list(1,50), "psilocybin" = list(3,5)) - -/datum/seed/mushroom/hallucinogenic/New() - ..() - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,15) - set_trait(TRAIT_PRODUCT_ICON,"mushroom11") - set_trait(TRAIT_PRODUCT_COLOUR,"#FFB70F") - set_trait(TRAIT_PLANT_COLOUR,"#F58A18") - set_trait(TRAIT_PLANT_ICON,"mushroom6") - -/datum/seed/mushroom/hallucinogenic/strong - name = "libertycap" - seed_name = "liberty cap" - display_name = "liberty cap mushrooms" - mutants = null - chems = list("nutriment" = list(1), "stoxin" = list(3,3), "space_drugs" = list(1,25)) - -/datum/seed/mushroom/hallucinogenic/strong/New() - ..() - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_POTENCY,15) - set_trait(TRAIT_PRODUCT_ICON,"mushroom8") - set_trait(TRAIT_PRODUCT_COLOUR,"#F2E550") - set_trait(TRAIT_PLANT_COLOUR,"#D1CA82") - set_trait(TRAIT_PLANT_ICON,"mushroom3") - -/datum/seed/mushroom/poison - name = "amanita" - seed_name = "fly amanita" - display_name = "fly amanita mushrooms" - mutants = list("destroyingangel","plastic") - chems = list("nutriment" = list(1), "amatoxin" = list(3,3), "psilocybin" = list(1,25)) - -/datum/seed/mushroom/poison/New() - ..() - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"mushroom") - set_trait(TRAIT_PRODUCT_COLOUR,"#FF4545") - set_trait(TRAIT_PLANT_COLOUR,"#E0DDBA") - set_trait(TRAIT_PLANT_ICON,"mushroom4") - -/datum/seed/mushroom/poison/death - name = "destroyingangel" - seed_name = "destroying angel" - display_name = "destroying angel mushrooms" - mutants = null - chems = list("nutriment" = list(1,50), "amatoxin" = list(13,3), "psilocybin" = list(1,25)) - -/datum/seed/mushroom/poison/death/New() - ..() - set_trait(TRAIT_MATURATION,12) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,35) - set_trait(TRAIT_PRODUCT_ICON,"mushroom3") - set_trait(TRAIT_PRODUCT_COLOUR,"#EDE8EA") - set_trait(TRAIT_PLANT_COLOUR,"#E6D8DD") - set_trait(TRAIT_PLANT_ICON,"mushroom5") - -/datum/seed/mushroom/towercap - name = "towercap" - seed_name = "tower cap" - display_name = "tower caps" - chems = list("woodpulp" = list(10,1)) - mutants = null - has_item_product = /obj/item/stack/material/log - -/datum/seed/mushroom/towercap/New() - ..() - set_trait(TRAIT_MATURATION,15) - set_trait(TRAIT_PRODUCT_ICON,"mushroom7") - set_trait(TRAIT_PRODUCT_COLOUR,"#79A36D") - set_trait(TRAIT_PLANT_COLOUR,"#857F41") - set_trait(TRAIT_PLANT_ICON,"mushroom8") - -/datum/seed/mushroom/glowshroom - name = "glowshroom" - seed_name = "glowshroom" - display_name = "glowshrooms" - mutants = null - chems = list("radium" = list(1,20)) - -/datum/seed/mushroom/glowshroom/New() - ..() - set_trait(TRAIT_SPREAD,1) - set_trait(TRAIT_MATURATION,15) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,30) - set_trait(TRAIT_BIOLUM,1) - set_trait(TRAIT_BIOLUM_COLOUR,"#006622") - set_trait(TRAIT_PRODUCT_ICON,"mushroom2") - set_trait(TRAIT_PRODUCT_COLOUR,"#DDFAB6") - set_trait(TRAIT_PLANT_COLOUR,"#EFFF8A") - set_trait(TRAIT_PLANT_ICON,"mushroom7") - -/datum/seed/mushroom/plastic - name = "plastic" - seed_name = "plastellium" - display_name = "plastellium" - mutants = null - chems = list("plasticide" = list(1,10)) - -/datum/seed/mushroom/plastic/New() - ..() - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,6) - set_trait(TRAIT_POTENCY,20) - set_trait(TRAIT_PRODUCT_ICON,"mushroom6") - set_trait(TRAIT_PRODUCT_COLOUR,"#E6E6E6") - set_trait(TRAIT_PLANT_COLOUR,"#E6E6E6") - set_trait(TRAIT_PLANT_ICON,"mushroom10") - -/datum/seed/mushroom/spore - name = "sporeshroom" - seed_name = "corpellian" - display_name = "corpellian" - mutants = null - chems = list("serotrotium" = list(5,10), "mold" = list(1,10)) - -/datum/seed/mushroom/spore/New() - ..() - set_trait(TRAIT_MATURATION,15) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,20) - set_trait(TRAIT_PRODUCT_ICON,"mushroom5") - set_trait(TRAIT_PRODUCT_COLOUR,"#e29cd2") - set_trait(TRAIT_PLANT_COLOUR,"#f8e6f4") - set_trait(TRAIT_PLANT_ICON,"mushroom9") - set_trait(TRAIT_SPORING, TRUE) - -//Flowers/varieties -/datum/seed/flower - name = "harebells" - seed_name = "harebell" - display_name = "harebells" - kitchen_tag = "harebell" - chems = list("nutriment" = list(1,20)) - -/datum/seed/flower/New() - ..() - set_trait(TRAIT_MATURATION,7) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_PRODUCT_ICON,"flower5") - set_trait(TRAIT_PRODUCT_COLOUR,"#C492D6") - set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") - set_trait(TRAIT_PLANT_ICON,"flower") - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/flower/poppy - name = "poppies" - seed_name = "poppy" - display_name = "poppies" - kitchen_tag = "poppy" - chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10)) - -/datum/seed/flower/poppy/New() - ..() - set_trait(TRAIT_POTENCY,20) - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,6) - set_trait(TRAIT_PRODUCT_ICON,"flower3") - set_trait(TRAIT_PRODUCT_COLOUR,"#B33715") - set_trait(TRAIT_PLANT_ICON,"flower3") - set_trait(TRAIT_IDEAL_LIGHT, 6) - set_trait(TRAIT_WATER_CONSUMPTION, 0.5) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/flower/sunflower - name = "sunflowers" - seed_name = "sunflower" - display_name = "sunflowers" - kitchen_tag = "sunflower" - -/datum/seed/flower/sunflower/New() - ..() - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCT_ICON,"flower2") - set_trait(TRAIT_PRODUCT_COLOUR,"#FFF700") - set_trait(TRAIT_PLANT_ICON,"flower2") - set_trait(TRAIT_IDEAL_LIGHT, 7) - set_trait(TRAIT_WATER_CONSUMPTION, 6) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/flower/lavender - name = "lavender" - seed_name = "lavender" - display_name = "lavender" - kitchen_tag = "lavender" - chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10)) - -/datum/seed/flower/lavender/New() - ..() - set_trait(TRAIT_MATURATION,7) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,5) - set_trait(TRAIT_PRODUCT_ICON,"flower6") - set_trait(TRAIT_PRODUCT_COLOUR,"#B57EDC") - set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") - set_trait(TRAIT_PLANT_ICON,"flower4") - set_trait(TRAIT_IDEAL_LIGHT, 7) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.05) - set_trait(TRAIT_WATER_CONSUMPTION, 0.5) - -/datum/seed/flower/rose - name = "rose" - seed_name = "rose" - display_name = "rose" - kitchen_tag = "rose" - mutants = list("bloodrose") - chems = list("nutriment" = list(1,5), "stoxin" = list(0,2)) - -/datum/seed/flower/rose/New() - ..() - set_trait(TRAIT_MATURATION,7) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_PRODUCT_ICON,"flowers") - set_trait(TRAIT_PRODUCT_COLOUR,"#ce0e0e") - set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") - set_trait(TRAIT_PLANT_ICON,"bush5") - set_trait(TRAIT_IDEAL_LIGHT, 7) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.1) - set_trait(TRAIT_WATER_CONSUMPTION, 0.5) - set_trait(TRAIT_STINGS,1) - -/datum/seed/flower/rose/blood - name = "bloodrose" - display_name = "bleeding rose" - mutants = null - chems = list("nutriment" = list(1,5), "stoxin" = list(1,5), "blood" = list(0,2)) - -/datum/seed/flower/rose/blood/New() - ..() - set_trait(TRAIT_IDEAL_LIGHT, 1) - set_trait(TRAIT_PLANT_COLOUR,"#5e0303") - set_trait(TRAIT_CARNIVOROUS,1) - -//Grapes/varieties -/datum/seed/grapes - name = "grapes" - seed_name = "grape" - display_name = "grapevines" - kitchen_tag = "grapes" - mutants = list("greengrapes") - chems = list("nutriment" = list(1,10), "sugar" = list(1,5), "grapejuice" = list(10,10)) - -/datum/seed/grapes/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,3) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"grapes") - set_trait(TRAIT_PRODUCT_COLOUR,"#BB6AC4") - set_trait(TRAIT_PLANT_COLOUR,"#378F2E") - set_trait(TRAIT_PLANT_ICON,"vine") - set_trait(TRAIT_IDEAL_LIGHT, 6) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/grapes/green - name = "greengrapes" - seed_name = "green grape" - display_name = "green grapevines" - mutants = null - chems = list("nutriment" = list(1,10), "kelotane" = list(3,5), "grapejuice" = list(10,10)) - -/datum/seed/grapes/green/New() - ..() - set_trait(TRAIT_PRODUCT_COLOUR,"42ed2f") - -// Lettuce/varieties. -/datum/seed/lettuce - name = "lettuce" - seed_name = "lettuce" - display_name = "lettuce" - kitchen_tag = "cabbage" - chems = list("nutriment" = list(1,15)) - -/datum/seed/lettuce/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,4) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,6) - set_trait(TRAIT_POTENCY,8) - set_trait(TRAIT_PRODUCT_ICON,"lettuce") - set_trait(TRAIT_PRODUCT_COLOUR,"#A8D0A7") - set_trait(TRAIT_PLANT_COLOUR,"#6D9C6B") - set_trait(TRAIT_PLANT_ICON,"vine2") - set_trait(TRAIT_IDEAL_LIGHT, 6) - set_trait(TRAIT_WATER_CONSUMPTION, 8) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.13) - -/datum/seed/lettuce/ice - name = "siflettuce" - seed_name = "glacial lettuce" - display_name = "glacial lettuce" - kitchen_tag = "icelettuce" - chems = list("nutriment" = list(1,5), "paracetamol" = list(0,2)) - -/datum/seed/lettuce/ice/New() - ..() - set_trait(TRAIT_ALTER_TEMP, -5) - set_trait(TRAIT_PRODUCT_COLOUR,"#9ABCC9") - -//Wabback / varieties. -/datum/seed/wabback - name = "whitewabback" - seed_name = "white wabback" - seed_noun = "nodes" - display_name = "white wabback" - chems = list("nutriment" = list(1,10), "protein" = list(1,5), "enzyme" = list(0,3)) - kitchen_tag = "wabback" - mutants = list("blackwabback","wildwabback") - has_item_product = /obj/item/stack/material/cloth - -/datum/seed/wabback/New() - ..() - set_trait(TRAIT_IDEAL_LIGHT, 5) - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_PRODUCTION,3) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,5) - set_trait(TRAIT_PRODUCT_ICON,"carrot2") - set_trait(TRAIT_PRODUCT_COLOUR,"#E6EDFA") - set_trait(TRAIT_PLANT_ICON,"chute") - set_trait(TRAIT_PLANT_COLOUR, "#0650ce") - set_trait(TRAIT_WATER_CONSUMPTION, 10) - set_trait(TRAIT_ALTER_TEMP, -1) - set_trait(TRAIT_CARNIVOROUS,1) - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_SPREAD,1) - -/datum/seed/wabback/vine - name = "blackwabback" - seed_name = "black wabback" - display_name = "black wabback" - mutants = null - chems = list("nutriment" = list(1,3), "protein" = list(1,10), "serotrotium_v" = list(0,1)) - -/datum/seed/wabback/vine/New() - ..() - set_trait(TRAIT_PRODUCT_COLOUR,"#2E2F32") - set_trait(TRAIT_CARNIVOROUS,2) - -/datum/seed/wabback/wild - name = "wildwabback" - seed_name = "wild wabback" - display_name = "wild wabback" - mutants = list("whitewabback") - has_item_product = null - chems = list("nutriment" = list(1,15), "protein" = list(0,2), "enzyme" = list(0,1)) - -/datum/seed/wabback/wild/New() - ..() - set_trait(TRAIT_IDEAL_LIGHT, 3) - set_trait(TRAIT_WATER_CONSUMPTION, 7) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.1) - set_trait(TRAIT_YIELD,5) - -//Everything else -/datum/seed/peanuts - name = "peanut" - seed_name = "peanut" - display_name = "peanut vines" - kitchen_tag = "peanut" - chems = list("nutriment" = list(1,10), "peanutoil" = list(3,10)) - -/datum/seed/peanuts/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,6) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"nuts") - set_trait(TRAIT_PRODUCT_COLOUR,"#C4AE7A") - set_trait(TRAIT_PLANT_ICON,"bush2") - set_trait(TRAIT_IDEAL_LIGHT, 6) - -/datum/seed/vanilla - name = "vanilla" - seed_name = "vanilla" - display_name = "vanilla" - kitchen_tag = "vanilla" - chems = list("nutriment" = list(1,10), "vanilla" = list(2,8), "sugar" = list(1, 4)) - -/datum/seed/vanilla/New() - ..() - set_trait(TRAIT_MATURATION,7) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_PRODUCT_ICON,"chili") - set_trait(TRAIT_PRODUCT_COLOUR,"#B57EDC") - set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") - set_trait(TRAIT_PLANT_ICON,"bush5") - set_trait(TRAIT_IDEAL_LIGHT, 8) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.3) - set_trait(TRAIT_WATER_CONSUMPTION, 0.5) - -/datum/seed/cabbage - name = "cabbage" - seed_name = "cabbage" - display_name = "cabbages" - kitchen_tag = "cabbage" - chems = list("nutriment" = list(1,10)) - -/datum/seed/cabbage/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,3) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"cabbage") - set_trait(TRAIT_PRODUCT_COLOUR,"#84BD82") - set_trait(TRAIT_PLANT_COLOUR,"#6D9C6B") - set_trait(TRAIT_PLANT_ICON,"vine2") - set_trait(TRAIT_IDEAL_LIGHT, 6) - set_trait(TRAIT_WATER_CONSUMPTION, 6) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/banana - name = "banana" - seed_name = "banana" - display_name = "banana tree" - kitchen_tag = "banana" - chems = list("banana" = list(10,10)) - trash_type = /obj/item/weapon/bananapeel - -/datum/seed/banana/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_PRODUCT_ICON,"bananas") - set_trait(TRAIT_PRODUCT_COLOUR,"#FFEC1F") - set_trait(TRAIT_PLANT_COLOUR,"#69AD50") - set_trait(TRAIT_PLANT_ICON,"tree4") - set_trait(TRAIT_IDEAL_HEAT, 298) - set_trait(TRAIT_IDEAL_LIGHT, 7) - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/corn - name = "corn" - seed_name = "corn" - display_name = "ears of corn" - kitchen_tag = "corn" - chems = list("nutriment" = list(1,10), "cornoil" = list(3,15)) - trash_type = /obj/item/weapon/corncob - -/datum/seed/corn/New() - ..() - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,20) - set_trait(TRAIT_PRODUCT_ICON,"corn") - set_trait(TRAIT_PRODUCT_COLOUR,"#FFF23B") - set_trait(TRAIT_PLANT_COLOUR,"#87C969") - set_trait(TRAIT_PLANT_ICON,"corn") - set_trait(TRAIT_IDEAL_HEAT, 298) - set_trait(TRAIT_IDEAL_LIGHT, 6) - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/potato - name = "potato" - seed_name = "potato" - display_name = "potatoes" - kitchen_tag = "potato" - chems = list("nutriment" = list(1,10), "potatojuice" = list(10,10)) - -/datum/seed/potato/New() - ..() - set_trait(TRAIT_PRODUCES_POWER,1) - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"potato") - set_trait(TRAIT_PRODUCT_COLOUR,"#D4CAB4") - set_trait(TRAIT_PLANT_ICON,"bush2") - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/onion - name = "onion" - seed_name = "onion" - display_name = "onions" - kitchen_tag = "onion" - chems = list("nutriment" = list(1,10)) - -/datum/seed/onion/New() - ..() - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"onion") - set_trait(TRAIT_PRODUCT_COLOUR,"#E0C367") - set_trait(TRAIT_PLANT_ICON,"carrot") - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/soybean - name = "soybean" - seed_name = "soybean" - display_name = "soybeans" - kitchen_tag = "soybeans" - chems = list("nutriment" = list(1,20), "soymilk" = list(10,20)) - -/datum/seed/soybean/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,4) - set_trait(TRAIT_PRODUCTION,4) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,5) - set_trait(TRAIT_PRODUCT_ICON,"bean") - set_trait(TRAIT_PRODUCT_COLOUR,"#EBE7C0") - set_trait(TRAIT_PLANT_ICON,"stalk") - -/datum/seed/wheat - name = "wheat" - seed_name = "wheat" - display_name = "wheat stalks" - kitchen_tag = "wheat" - chems = list("nutriment" = list(1,25), "flour" = list(10,30)) - -/datum/seed/wheat/New() - ..() - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,5) - set_trait(TRAIT_PRODUCT_ICON,"wheat") - set_trait(TRAIT_PRODUCT_COLOUR,"#DBD37D") - set_trait(TRAIT_PLANT_COLOUR,"#BFAF82") - set_trait(TRAIT_PLANT_ICON,"stalk2") - set_trait(TRAIT_IDEAL_LIGHT, 6) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/rice - name = "rice" - seed_name = "rice" - display_name = "rice stalks" - kitchen_tag = "rice" - chems = list("nutriment" = list(1,25), "rice" = list(10,15)) - -/datum/seed/rice/New() - ..() - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,5) - set_trait(TRAIT_PRODUCT_ICON,"rice") - set_trait(TRAIT_PRODUCT_COLOUR,"#D5E6D1") - set_trait(TRAIT_PLANT_COLOUR,"#8ED17D") - set_trait(TRAIT_PLANT_ICON,"stalk2") - set_trait(TRAIT_WATER_CONSUMPTION, 6) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/carrots - name = "carrot" - seed_name = "carrot" - display_name = "carrots" - kitchen_tag = "carrot" - chems = list("nutriment" = list(1,20), "imidazoline" = list(3,5), "carrotjuice" = list(10,20)) - -/datum/seed/carrots/New() - ..() - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,5) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"carrot") - set_trait(TRAIT_PRODUCT_COLOUR,"#FFDB4A") - set_trait(TRAIT_PLANT_ICON,"carrot") - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/weeds - name = "weeds" - seed_name = "weed" - display_name = "weeds" - -/datum/seed/weeds/New() - ..() - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,-1) - set_trait(TRAIT_POTENCY,-1) - set_trait(TRAIT_IMMUTABLE,-1) - set_trait(TRAIT_PRODUCT_ICON,"flower4") - set_trait(TRAIT_PRODUCT_COLOUR,"#FCEB2B") - set_trait(TRAIT_PLANT_COLOUR,"#59945A") - set_trait(TRAIT_PLANT_ICON,"bush6") - -/datum/seed/whitebeets - name = "whitebeet" - seed_name = "white-beet" - display_name = "white-beets" - kitchen_tag = "whitebeet" - chems = list("nutriment" = list(0,20), "sugar" = list(1,5)) - -/datum/seed/whitebeets/New() - ..() - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,6) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"carrot2") - set_trait(TRAIT_PRODUCT_COLOUR,"#EEF5B0") - set_trait(TRAIT_PLANT_COLOUR,"#4D8F53") - set_trait(TRAIT_PLANT_ICON,"carrot2") - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/sugarcane - name = "sugarcane" - seed_name = "sugarcane" - display_name = "sugarcanes" - kitchen_tag = "sugarcanes" - chems = list("sugar" = list(4,5)) - -/datum/seed/sugarcane/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,3) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"stalk") - set_trait(TRAIT_PRODUCT_COLOUR,"#B4D6BD") - set_trait(TRAIT_PLANT_COLOUR,"#6BBD68") - set_trait(TRAIT_PLANT_ICON,"stalk3") - set_trait(TRAIT_IDEAL_HEAT, 298) - -/datum/seed/rhubarb - name = "rhubarb" - seed_name = "rhubarb" - display_name = "rhubarb" - kitchen_tag = "rhubarb" - chems = list("nutriment" = list(1,15)) - -/datum/seed/rhubarb/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,3) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,5) - set_trait(TRAIT_POTENCY,6) - set_trait(TRAIT_PRODUCT_ICON,"stalk") - set_trait(TRAIT_PRODUCT_COLOUR,"#FD5656") - set_trait(TRAIT_PLANT_ICON,"stalk3") - -/datum/seed/celery - name = "celery" - seed_name = "celery" - display_name = "celery" - kitchen_tag = "celery" - chems = list("nutriment" = list(5,20)) - -/datum/seed/celery/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,4) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,8) - set_trait(TRAIT_PRODUCT_ICON,"stalk") - set_trait(TRAIT_PRODUCT_COLOUR,"#56FD56") - set_trait(TRAIT_PLANT_ICON,"stalk3") - -/datum/seed/spineapple - name = "spineapple" - seed_name = "spineapple" - display_name = "spineapple" - kitchen_tag = "pineapple" - chems = list("nutriment" = list(1,5), "enzyme" = list(1,5), "pineapplejuice" = list(1, 20)) - -/datum/seed/spineapple/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,10) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,1) - set_trait(TRAIT_POTENCY,13) - set_trait(TRAIT_PRODUCT_ICON,"pineapple") - set_trait(TRAIT_PRODUCT_COLOUR,"#FFF23B") - set_trait(TRAIT_PLANT_COLOUR,"#87C969") - set_trait(TRAIT_PLANT_ICON,"corn") - set_trait(TRAIT_IDEAL_HEAT, 298) - set_trait(TRAIT_IDEAL_LIGHT, 4) - set_trait(TRAIT_WATER_CONSUMPTION, 8) - set_trait(TRAIT_STINGS,1) - -/datum/seed/durian - name = "durian" - seed_name = "durian" - seed_noun = "pits" - display_name = "durian" - kitchen_tag = "durian" - chems = list("nutriment" = list(1,5), "durianpaste" = list(1, 20)) - -/datum/seed/durian/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"spinefruit") - set_trait(TRAIT_PRODUCT_COLOUR,"#757631") - set_trait(TRAIT_PLANT_COLOUR,"#87C969") - set_trait(TRAIT_PLANT_ICON,"tree") - set_trait(TRAIT_IDEAL_LIGHT, 8) - set_trait(TRAIT_WATER_CONSUMPTION, 8) - -/datum/seed/watermelon - name = "watermelon" - seed_name = "watermelon" - display_name = "watermelon vine" - kitchen_tag = "watermelon" - chems = list("nutriment" = list(1,6), "watermelonjuice" = list(10,6)) - -/datum/seed/watermelon/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_JUICY,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,1) - set_trait(TRAIT_PRODUCT_ICON,"vine") - set_trait(TRAIT_PRODUCT_COLOUR,"#3D8C3A") - set_trait(TRAIT_PLANT_COLOUR,"#257522") - set_trait(TRAIT_PLANT_ICON,"vine2") - set_trait(TRAIT_FLESH_COLOUR,"#F22C2C") - set_trait(TRAIT_IDEAL_HEAT, 298) - set_trait(TRAIT_IDEAL_LIGHT, 6) - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/pumpkin - name = "pumpkin" - seed_name = "pumpkin" - display_name = "pumpkin vine" - kitchen_tag = "pumpkin" - chems = list("nutriment" = list(1,6)) - -/datum/seed/pumpkin/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"vine2") - set_trait(TRAIT_PRODUCT_COLOUR,"#DBAC02") - set_trait(TRAIT_PLANT_COLOUR,"#21661E") - set_trait(TRAIT_PLANT_ICON,"vine2") - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/citrus - name = "lime" - seed_name = "lime" - display_name = "lime trees" - kitchen_tag = "lime" - chems = list("nutriment" = list(1,20), "limejuice" = list(10,20)) - -/datum/seed/citrus/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_JUICY,1) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,15) - set_trait(TRAIT_PRODUCT_ICON,"treefruit") - set_trait(TRAIT_PRODUCT_COLOUR,"#3AF026") - set_trait(TRAIT_PLANT_ICON,"tree") - set_trait(TRAIT_FLESH_COLOUR,"#3AF026") - -/datum/seed/citrus/lemon - name = "lemon" - seed_name = "lemon" - display_name = "lemon trees" - kitchen_tag = "lemon" - chems = list("nutriment" = list(1,20), "lemonjuice" = list(10,20)) - -/datum/seed/citrus/lemon/New() - ..() - set_trait(TRAIT_PRODUCES_POWER,1) - set_trait(TRAIT_PRODUCT_ICON,"lemon") - set_trait(TRAIT_PRODUCT_COLOUR,"#F0E226") - set_trait(TRAIT_FLESH_COLOUR,"#F0E226") - set_trait(TRAIT_IDEAL_LIGHT, 6) - -/datum/seed/citrus/orange - name = "orange" - seed_name = "orange" - display_name = "orange trees" - kitchen_tag = "orange" - chems = list("nutriment" = list(1,20), "orangejuice" = list(10,20)) - -/datum/seed/citrus/orange/New() - ..() - set_trait(TRAIT_PRODUCT_COLOUR,"#FFC20A") - set_trait(TRAIT_FLESH_COLOUR,"#FFC20A") - -/datum/seed/grass - name = "grass" - seed_name = "grass" - display_name = "grass" - kitchen_tag = "grass" - chems = list("nutriment" = list(1,20)) - -/datum/seed/grass/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,2) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,5) - set_trait(TRAIT_PRODUCT_ICON,"grass") - set_trait(TRAIT_PRODUCT_COLOUR,"#09FF00") - set_trait(TRAIT_PLANT_COLOUR,"#07D900") - set_trait(TRAIT_PLANT_ICON,"grass") - set_trait(TRAIT_WATER_CONSUMPTION, 0.5) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/cocoa - name = "cocoa" - seed_name = "cacao" - display_name = "cacao tree" - kitchen_tag = "cocoa" - chems = list("nutriment" = list(1,10), "coco" = list(4,5)) - -/datum/seed/cocoa/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"treefruit") - set_trait(TRAIT_PRODUCT_COLOUR,"#CCA935") - set_trait(TRAIT_PLANT_ICON,"tree2") - set_trait(TRAIT_IDEAL_HEAT, 298) - set_trait(TRAIT_WATER_CONSUMPTION, 6) - -/datum/seed/cherries - name = "cherry" - seed_name = "cherry" - seed_noun = "pits" - display_name = "cherry tree" - kitchen_tag = "cherries" - chems = list("nutriment" = list(1,15), "sugar" = list(1,15), "cherryjelly" = list(10,15)) - -/datum/seed/cherries/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_JUICY,1) - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"cherry") - set_trait(TRAIT_PRODUCT_COLOUR,"#A80000") - set_trait(TRAIT_PLANT_ICON,"tree2") - set_trait(TRAIT_PLANT_COLOUR,"#2F7D2D") - -/datum/seed/kudzu - name = "kudzu" - seed_name = "kudzu" - display_name = "kudzu vines" - kitchen_tag = "kudzu" - chems = list("nutriment" = list(1,50), "anti_toxin" = list(1,25)) - -/datum/seed/kudzu/New() - ..() - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_SPREAD,2) - set_trait(TRAIT_PRODUCT_ICON,"treefruit") - set_trait(TRAIT_PRODUCT_COLOUR,"#96D278") - set_trait(TRAIT_PLANT_COLOUR,"#6F7A63") - set_trait(TRAIT_PLANT_ICON,"vine2") - set_trait(TRAIT_WATER_CONSUMPTION, 0.5) - -/datum/seed/diona - name = "diona" - seed_name = "diona" - seed_noun = "nodes" - display_name = "replicant pods" - can_self_harvest = 1 - apply_color_to_mob = FALSE - has_mob_product = /mob/living/carbon/alien/diona - -/datum/seed/diona/New() - ..() - set_trait(TRAIT_IMMUTABLE,1) - set_trait(TRAIT_ENDURANCE,8) - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,10) - set_trait(TRAIT_YIELD,1) - set_trait(TRAIT_POTENCY,30) - set_trait(TRAIT_PRODUCT_ICON,"diona") - set_trait(TRAIT_PRODUCT_COLOUR,"#799957") - set_trait(TRAIT_PLANT_COLOUR,"#66804B") - set_trait(TRAIT_PLANT_ICON,"alien4") - -/datum/seed/shand - name = "shand" - seed_name = "Selem's hand" - display_name = "Selem's hand leaves" - kitchen_tag = "shand" - chems = list("bicaridine" = list(0,10)) - -/datum/seed/shand/New() - ..() - set_trait(TRAIT_MATURATION,3) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"alien3") - set_trait(TRAIT_PRODUCT_COLOUR,"#378C61") - set_trait(TRAIT_PLANT_COLOUR,"#378C61") - set_trait(TRAIT_PLANT_ICON,"tree5") - set_trait(TRAIT_IDEAL_HEAT, 283) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/mtear - name = "mtear" - seed_name = "Malani's tear" - display_name = "Malani's tear leaves" - kitchen_tag = "mtear" - chems = list("honey" = list(1,10), "kelotane" = list(3,5)) - -/datum/seed/mtear/New() - ..() - set_trait(TRAIT_MATURATION,3) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_PRODUCT_ICON,"alien4") - set_trait(TRAIT_PRODUCT_COLOUR,"#4CC5C7") - set_trait(TRAIT_PLANT_COLOUR,"#4CC789") - set_trait(TRAIT_PLANT_ICON,"bush7") - set_trait(TRAIT_IDEAL_HEAT, 283) - set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) - -/datum/seed/telriis - name = "telriis" - seed_name = "telriis" - display_name = "telriis grass" - kitchen_tag = "telriis" - chems = list("pwine" = list(1,5), "nutriment" = list(1,6)) - -/datum/seed/telriis/New() - ..() - set_trait(TRAIT_PLANT_ICON,"ambrosia") - set_trait(TRAIT_PRODUCT_ICON,"ambrosia") - set_trait(TRAIT_ENDURANCE,50) - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,5) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,5) - -/datum/seed/thaadra - name = "thaadra" - seed_name = "thaa'dra" - display_name = "thaa'dra lichen" - kitchen_tag = "thaadra" - chems = list("frostoil" = list(1,5),"nutriment" = list(1,5)) - -/datum/seed/thaadra/New() - ..() - set_trait(TRAIT_PLANT_ICON,"grass") - set_trait(TRAIT_PLANT_COLOUR,"#ABC7D2") - set_trait(TRAIT_ENDURANCE,10) - set_trait(TRAIT_MATURATION,5) - set_trait(TRAIT_PRODUCTION,9) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,5) - -/datum/seed/jurlmah - name = "jurlmah" - seed_name = "jurl'mah" - display_name = "jurl'mah reeds" - kitchen_tag = "jurlmah" - chems = list("serotrotium" = list(1,5),"nutriment" = list(1,5)) - -/datum/seed/jurlmah/New() - ..() - set_trait(TRAIT_PLANT_ICON,"mushroom9") - set_trait(TRAIT_ENDURANCE,12) - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_PRODUCTION,9) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,10) - -/datum/seed/amauri - name = "amauri" - seed_name = "amauri" - display_name = "amauri plant" - kitchen_tag = "amauri" - chems = list("zombiepowder" = list(1,10),"condensedcapsaicin" = list(1,5),"nutriment" = list(1,5)) - -/datum/seed/amauri/New() - ..() - set_trait(TRAIT_PLANT_ICON,"bush4") - set_trait(TRAIT_ENDURANCE,10) - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_PRODUCTION,9) - set_trait(TRAIT_YIELD,4) - set_trait(TRAIT_POTENCY,10) - -/datum/seed/gelthi - name = "gelthi" - seed_name = "gelthi" - display_name = "gelthi plant" - kitchen_tag = "gelthi" - chems = list("stoxin" = list(1,5),"capsaicin" = list(1,5),"nutriment" = list(1,5)) - -/datum/seed/gelthi/New() - ..() - set_trait(TRAIT_PLANT_ICON,"mushroom3") - set_trait(TRAIT_ENDURANCE,15) - set_trait(TRAIT_MATURATION,6) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_POTENCY,1) - -/datum/seed/vale - name = "vale" - seed_name = "vale" - display_name = "vale bush" - kitchen_tag = "vale" - chems = list("paracetamol" = list(1,5),"dexalin" = list(1,2),"nutriment"= list(1,5)) - -/datum/seed/vale/New() - ..() - set_trait(TRAIT_PLANT_ICON,"flower4") - set_trait(TRAIT_ENDURANCE,15) - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_PRODUCTION,10) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,3) - -/datum/seed/surik - name = "surik" - seed_name = "surik" - display_name = "surik vine" - kitchen_tag = "surik" - chems = list("impedrezene" = list(1,3),"synaptizine" = list(1,2),"nutriment" = list(1,5)) - -/datum/seed/surik/New() - ..() - set_trait(TRAIT_PLANT_ICON,"bush6") - set_trait(TRAIT_ENDURANCE,18) - set_trait(TRAIT_MATURATION,7) - set_trait(TRAIT_PRODUCTION,7) - set_trait(TRAIT_YIELD,3) - set_trait(TRAIT_POTENCY,3) - -// Alien weeds. -/datum/seed/xenomorph - name = "xenomorph" - seed_name = "alien weed" - display_name = "alien weeds" - force_layer = 3 - chems = list("phoron" = list(1,3)) - -/datum/seed/xenomorph/New() - ..() - set_trait(TRAIT_PLANT_ICON,"vine2") - set_trait(TRAIT_IMMUTABLE,1) - set_trait(TRAIT_PRODUCT_COLOUR,"#3D1934") - set_trait(TRAIT_FLESH_COLOUR,"#3D1934") - set_trait(TRAIT_PLANT_COLOUR,"#3D1934") - set_trait(TRAIT_PRODUCTION,1) - set_trait(TRAIT_YIELD,-1) - set_trait(TRAIT_SPREAD,2) - set_trait(TRAIT_POTENCY,50) - -// Gnomes -/datum/seed/gnomes - name = "gnomes" - seed_name = "gnomes" - display_name = "gnomes" - force_layer = 3 - chems = list("magicdust" = list(5,20)) - -/datum/seed/gnomes/New() - ..() - set_trait(TRAIT_HARVEST_REPEAT,1) - set_trait(TRAIT_PLANT_ICON,"gnomes") - set_trait(TRAIT_PRODUCT_ICON,"gnomes") - set_trait(TRAIT_PRODUCT_COLOUR,"") - set_trait(TRAIT_FLESH_COLOUR,"") - set_trait(TRAIT_PLANT_COLOUR,"") - set_trait(TRAIT_BIOLUM_COLOUR,"#fff200") - set_trait(TRAIT_MATURATION,8) - set_trait(TRAIT_PRODUCTION,6) - set_trait(TRAIT_BIOLUM,1) - set_trait(TRAIT_YIELD,2) - set_trait(TRAIT_SPREAD,1) - set_trait(TRAIT_POTENCY,10) - set_trait(TRAIT_REQUIRES_NUTRIENTS,0) - set_trait(TRAIT_REQUIRES_WATER,0) diff --git a/code/modules/hydroponics/seed_packets.dm b/code/modules/hydroponics/seed_packets.dm index 45d74e26fd..3db8840058 100644 --- a/code/modules/hydroponics/seed_packets.dm +++ b/code/modules/hydroponics/seed_packets.dm @@ -337,3 +337,6 @@ GLOBAL_LIST_BOILERPLATE(all_seed_packs, /obj/item/seeds) /obj/item/seeds/sifbulb seed_type = "sifbulb" + +/obj/item/seeds/wurmwoad + seed_type = "wurmwoad" diff --git a/code/modules/hydroponics/seed_storage.dm b/code/modules/hydroponics/seed_storage.dm index 39b9dc3ebc..f75d17f63e 100644 --- a/code/modules/hydroponics/seed_storage.dm +++ b/code/modules/hydroponics/seed_storage.dm @@ -138,7 +138,8 @@ /obj/item/seeds/wabback = 2, /obj/item/seeds/watermelonseed = 3, /obj/item/seeds/wheatseed = 3, - /obj/item/seeds/whitebeetseed = 3 + /obj/item/seeds/whitebeetseed = 3, + /obj/item/seeds/wurmwoad = 3 ) /obj/machinery/seed_storage/xenobotany @@ -195,7 +196,8 @@ /obj/item/seeds/wabback = 2, /obj/item/seeds/watermelonseed = 3, /obj/item/seeds/wheatseed = 3, - /obj/item/seeds/whitebeetseed = 3 + /obj/item/seeds/whitebeetseed = 3, + /obj/item/seeds/wurmwoad = 3 ) /obj/machinery/seed_storage/attack_hand(mob/user as mob) diff --git a/code/modules/hydroponics/seedtypes/amauri.dm b/code/modules/hydroponics/seedtypes/amauri.dm new file mode 100644 index 0000000000..0ad44e7862 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/amauri.dm @@ -0,0 +1,15 @@ +/datum/seed/amauri + name = "amauri" + seed_name = "amauri" + display_name = "amauri plant" + kitchen_tag = "amauri" + chems = list("zombiepowder" = list(1,10),"condensedcapsaicin" = list(1,5),"nutriment" = list(1,5)) + +/datum/seed/amauri/New() + ..() + set_trait(TRAIT_PLANT_ICON,"bush4") + set_trait(TRAIT_ENDURANCE,10) + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,9) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/ambrosia.dm b/code/modules/hydroponics/seedtypes/ambrosia.dm new file mode 100644 index 0000000000..535d5129a3 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/ambrosia.dm @@ -0,0 +1,46 @@ +//Ambrosia/varieties. +/datum/seed/ambrosia + name = "ambrosia" + seed_name = "ambrosia vulgaris" + display_name = "ambrosia vulgaris" + kitchen_tag = "ambrosia" + mutants = list("ambrosiadeus") + chems = list("nutriment" = list(1), "space_drugs" = list(1,8), "kelotane" = list(1,8,1), "bicaridine" = list(1,10,1), "toxin" = list(1,10)) + +/datum/seed/ambrosia/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,6) + set_trait(TRAIT_POTENCY,5) + set_trait(TRAIT_PRODUCT_ICON,"ambrosia") + set_trait(TRAIT_PRODUCT_COLOUR,"#9FAD55") + set_trait(TRAIT_PLANT_ICON,"ambrosia") + set_trait(TRAIT_IDEAL_LIGHT, 6) + +/datum/seed/ambrosia/deus + name = "ambrosiadeus" + seed_name = "ambrosia deus" + display_name = "ambrosia deus" + kitchen_tag = "ambrosiadeus" + mutants = list("ambrosiainfernus") + chems = list("nutriment" = list(1), "bicaridine" = list(1,8), "synaptizine" = list(1,8,1), "hyperzine" = list(1,10,1), "space_drugs" = list(1,10)) + +/datum/seed/ambrosia/deus/New() + ..() + set_trait(TRAIT_PRODUCT_COLOUR,"#A3F0AD") + set_trait(TRAIT_PLANT_COLOUR,"#2A9C61") + +/datum/seed/ambrosia/infernus + name = "ambrosiainfernus" + seed_name = "ambrosia infernus" + display_name = "ambrosia infernus" + kitchen_tag = "ambrosiainfernus" + mutants = null + chems = list("nutriment" = list(1,3), "oxycodone" = list(1,8), "impedrezene" = list(1,10), "mindbreaker" = list(1,10)) + +/datum/seed/ambrosia/infernus/New() + ..() + set_trait(TRAIT_PRODUCT_COLOUR,"#dc143c") + set_trait(TRAIT_PLANT_COLOUR,"#b22222") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/apples.dm b/code/modules/hydroponics/seedtypes/apples.dm new file mode 100644 index 0000000000..7044a0c6dc --- /dev/null +++ b/code/modules/hydroponics/seedtypes/apples.dm @@ -0,0 +1,62 @@ +//Apples/varieties. +/datum/seed/apple + name = "apple" + seed_name = "apple" + display_name = "apple tree" + kitchen_tag = "apple" + mutants = list("poisonapple","goldapple") + chems = list("nutriment" = list(1,10),"applejuice" = list(10,20)) + +/datum/seed/apple/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,5) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"apple") + set_trait(TRAIT_PRODUCT_COLOUR,"#FF540A") + set_trait(TRAIT_PLANT_ICON,"tree2") + set_trait(TRAIT_FLESH_COLOUR,"#E8E39B") + set_trait(TRAIT_IDEAL_LIGHT, 4) + +/datum/seed/apple/poison + name = "poisonapple" + mutants = null + chems = list("cyanide" = list(1,5)) + +/datum/seed/apple/gold + name = "goldapple" + seed_name = "golden apple" + display_name = "gold apple tree" + kitchen_tag = "goldapple" + mutants = null + chems = list("nutriment" = list(1,10), "gold" = list(1,5)) + +/datum/seed/apple/gold/New() + ..() + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,10) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_PRODUCT_COLOUR,"#FFDD00") + set_trait(TRAIT_PLANT_COLOUR,"#D6B44D") + +/datum/seed/apple/sif + name = "sifbulb" + seed_name = "sivian tree" + display_name = "sivian pod" + kitchen_tag = "apple" + chems = list("nutriment" = list(1,5),"sifsap" = list(10,20)) + +/datum/seed/apple/sif/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,3) + set_trait(TRAIT_PRODUCTION,10) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,12) + set_trait(TRAIT_PRODUCT_ICON,"alien3") + set_trait(TRAIT_PRODUCT_COLOUR,"#0720c3") + set_trait(TRAIT_PLANT_ICON,"tree5") + set_trait(TRAIT_FLESH_COLOUR,"#05157d") + set_trait(TRAIT_IDEAL_LIGHT, 1) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/banana.dm b/code/modules/hydroponics/seedtypes/banana.dm new file mode 100644 index 0000000000..9259318d77 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/banana.dm @@ -0,0 +1,21 @@ +/datum/seed/banana + name = "banana" + seed_name = "banana" + display_name = "banana tree" + kitchen_tag = "banana" + chems = list("banana" = list(10,10)) + trash_type = /obj/item/weapon/bananapeel + +/datum/seed/banana/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_PRODUCT_ICON,"bananas") + set_trait(TRAIT_PRODUCT_COLOUR,"#FFEC1F") + set_trait(TRAIT_PLANT_COLOUR,"#69AD50") + set_trait(TRAIT_PLANT_ICON,"tree4") + set_trait(TRAIT_IDEAL_HEAT, 298) + set_trait(TRAIT_IDEAL_LIGHT, 7) + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/berries.dm b/code/modules/hydroponics/seedtypes/berries.dm new file mode 100644 index 0000000000..2ad36353fe --- /dev/null +++ b/code/modules/hydroponics/seedtypes/berries.dm @@ -0,0 +1,70 @@ +// Berry plants/variants. +/datum/seed/berry + name = "berries" + seed_name = "berry" + display_name = "berry bush" + kitchen_tag = "berries" + mutants = list("glowberries","poisonberries") + chems = list("nutriment" = list(1,10), "berryjuice" = list(10,10)) + +/datum/seed/berry/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_JUICY,1) + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"berry") + set_trait(TRAIT_PRODUCT_COLOUR,"#FA1616") + set_trait(TRAIT_PLANT_ICON,"bush") + set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + +/datum/seed/berry/glow + name = "glowberries" + seed_name = "glowberry" + display_name = "glowberry bush" + mutants = null + chems = list("nutriment" = list(1,10), "uranium" = list(3,5)) + +/datum/seed/berry/glow/New() + ..() + set_trait(TRAIT_SPREAD,1) + set_trait(TRAIT_BIOLUM,1) + set_trait(TRAIT_BIOLUM_COLOUR,"#006622") + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_COLOUR,"#c9fa16") + set_trait(TRAIT_WATER_CONSUMPTION, 3) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) + +/datum/seed/berry/poison + name = "poisonberries" + seed_name = "poison berry" + kitchen_tag = "poisonberries" + display_name = "poison berry bush" + mutants = list("deathberries") + chems = list("nutriment" = list(1), "toxin" = list(3,5), "poisonberryjuice" = list(10,5)) + +/datum/seed/berry/poison/New() + ..() + set_trait(TRAIT_PRODUCT_COLOUR,"#6DC961") + set_trait(TRAIT_WATER_CONSUMPTION, 3) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) + +/datum/seed/berry/poison/death + name = "deathberries" + seed_name = "death berry" + display_name = "death berry bush" + mutants = null + chems = list("nutriment" = list(1), "toxin" = list(3,3), "lexorin" = list(1,5)) + +/datum/seed/berry/poison/death/New() + ..() + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,50) + set_trait(TRAIT_PRODUCT_COLOUR,"#7A5454") + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.35) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/cabbage.dm b/code/modules/hydroponics/seedtypes/cabbage.dm new file mode 100644 index 0000000000..0ee7fb2693 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/cabbage.dm @@ -0,0 +1,21 @@ +/datum/seed/cabbage + name = "cabbage" + seed_name = "cabbage" + display_name = "cabbages" + kitchen_tag = "cabbage" + chems = list("nutriment" = list(1,10)) + +/datum/seed/cabbage/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,3) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"cabbage") + set_trait(TRAIT_PRODUCT_COLOUR,"#84BD82") + set_trait(TRAIT_PLANT_COLOUR,"#6D9C6B") + set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/carrots.dm b/code/modules/hydroponics/seedtypes/carrots.dm new file mode 100644 index 0000000000..2b8ef7577b --- /dev/null +++ b/code/modules/hydroponics/seedtypes/carrots.dm @@ -0,0 +1,17 @@ +/datum/seed/carrots + name = "carrot" + seed_name = "carrot" + display_name = "carrots" + kitchen_tag = "carrot" + chems = list("nutriment" = list(1,20), "imidazoline" = list(3,5), "carrotjuice" = list(10,20)) + +/datum/seed/carrots/New() + ..() + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,5) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"carrot") + set_trait(TRAIT_PRODUCT_COLOUR,"#FFDB4A") + set_trait(TRAIT_PLANT_ICON,"carrot") + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/celery.dm b/code/modules/hydroponics/seedtypes/celery.dm new file mode 100644 index 0000000000..c404ed670f --- /dev/null +++ b/code/modules/hydroponics/seedtypes/celery.dm @@ -0,0 +1,17 @@ +/datum/seed/celery + name = "celery" + seed_name = "celery" + display_name = "celery" + kitchen_tag = "celery" + chems = list("nutriment" = list(5,20)) + +/datum/seed/celery/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,4) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,8) + set_trait(TRAIT_PRODUCT_ICON,"stalk") + set_trait(TRAIT_PRODUCT_COLOUR,"#56FD56") + set_trait(TRAIT_PLANT_ICON,"stalk3") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/cherries.dm b/code/modules/hydroponics/seedtypes/cherries.dm new file mode 100644 index 0000000000..ece8d793e7 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/cherries.dm @@ -0,0 +1,20 @@ +/datum/seed/cherries + name = "cherry" + seed_name = "cherry" + seed_noun = "pits" + display_name = "cherry tree" + kitchen_tag = "cherries" + chems = list("nutriment" = list(1,15), "sugar" = list(1,15), "cherryjelly" = list(10,15)) + +/datum/seed/cherries/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_JUICY,1) + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"cherry") + set_trait(TRAIT_PRODUCT_COLOUR,"#A80000") + set_trait(TRAIT_PLANT_ICON,"tree2") + set_trait(TRAIT_PLANT_COLOUR,"#2F7D2D") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/chili.dm b/code/modules/hydroponics/seedtypes/chili.dm new file mode 100644 index 0000000000..bee50a48bc --- /dev/null +++ b/code/modules/hydroponics/seedtypes/chili.dm @@ -0,0 +1,35 @@ +// Chili plants/variants. +/datum/seed/chili + name = "chili" + seed_name = "chili" + display_name = "chili plants" + kitchen_tag = "chili" + chems = list("capsaicin" = list(3,5), "nutriment" = list(1,25)) + mutants = list("icechili") + +/datum/seed/chili/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,20) + set_trait(TRAIT_PRODUCT_ICON,"chili") + set_trait(TRAIT_PRODUCT_COLOUR,"#ED3300") + set_trait(TRAIT_PLANT_ICON,"bush2") + set_trait(TRAIT_IDEAL_HEAT, 298) + set_trait(TRAIT_IDEAL_LIGHT, 7) + +/datum/seed/chili/ice + name = "icechili" + seed_name = "ice pepper" + display_name = "ice-pepper plants" + kitchen_tag = "icechili" + mutants = null + chems = list("frostoil" = list(3,5), "nutriment" = list(1,50)) + +/datum/seed/chili/ice/New() + ..() + set_trait(TRAIT_MATURATION,4) + set_trait(TRAIT_PRODUCTION,4) + set_trait(TRAIT_PRODUCT_COLOUR,"#00EDC6") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/citrus.dm b/code/modules/hydroponics/seedtypes/citrus.dm new file mode 100644 index 0000000000..ebc154aa40 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/citrus.dm @@ -0,0 +1,46 @@ +/datum/seed/citrus + name = "lime" + seed_name = "lime" + display_name = "lime trees" + kitchen_tag = "lime" + chems = list("nutriment" = list(1,20), "limejuice" = list(10,20)) + +/datum/seed/citrus/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_JUICY,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,15) + set_trait(TRAIT_PRODUCT_ICON,"treefruit") + set_trait(TRAIT_PRODUCT_COLOUR,"#3AF026") + set_trait(TRAIT_PLANT_ICON,"tree") + set_trait(TRAIT_FLESH_COLOUR,"#3AF026") + +/datum/seed/citrus/lemon + name = "lemon" + seed_name = "lemon" + display_name = "lemon trees" + kitchen_tag = "lemon" + chems = list("nutriment" = list(1,20), "lemonjuice" = list(10,20)) + +/datum/seed/citrus/lemon/New() + ..() + set_trait(TRAIT_PRODUCES_POWER,1) + set_trait(TRAIT_PRODUCT_ICON,"lemon") + set_trait(TRAIT_PRODUCT_COLOUR,"#F0E226") + set_trait(TRAIT_FLESH_COLOUR,"#F0E226") + set_trait(TRAIT_IDEAL_LIGHT, 6) + +/datum/seed/citrus/orange + name = "orange" + seed_name = "orange" + display_name = "orange trees" + kitchen_tag = "orange" + chems = list("nutriment" = list(1,20), "orangejuice" = list(10,20)) + +/datum/seed/citrus/orange/New() + ..() + set_trait(TRAIT_PRODUCT_COLOUR,"#FFC20A") + set_trait(TRAIT_FLESH_COLOUR,"#FFC20A") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/cocoa.dm b/code/modules/hydroponics/seedtypes/cocoa.dm new file mode 100644 index 0000000000..7f7aa31b39 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/cocoa.dm @@ -0,0 +1,19 @@ +/datum/seed/cocoa + name = "cocoa" + seed_name = "cacao" + display_name = "cacao tree" + kitchen_tag = "cocoa" + chems = list("nutriment" = list(1,10), "coco" = list(4,5)) + +/datum/seed/cocoa/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"treefruit") + set_trait(TRAIT_PRODUCT_COLOUR,"#CCA935") + set_trait(TRAIT_PLANT_ICON,"tree2") + set_trait(TRAIT_IDEAL_HEAT, 298) + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/corn.dm b/code/modules/hydroponics/seedtypes/corn.dm new file mode 100644 index 0000000000..2a4b2824f8 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/corn.dm @@ -0,0 +1,21 @@ +/datum/seed/corn + name = "corn" + seed_name = "corn" + display_name = "ears of corn" + kitchen_tag = "corn" + chems = list("nutriment" = list(1,10), "cornoil" = list(3,15)) + trash_type = /obj/item/weapon/corncob + +/datum/seed/corn/New() + ..() + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,20) + set_trait(TRAIT_PRODUCT_ICON,"corn") + set_trait(TRAIT_PRODUCT_COLOUR,"#FFF23B") + set_trait(TRAIT_PLANT_COLOUR,"#87C969") + set_trait(TRAIT_PLANT_ICON,"corn") + set_trait(TRAIT_IDEAL_HEAT, 298) + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/diona.dm b/code/modules/hydroponics/seedtypes/diona.dm new file mode 100644 index 0000000000..be3b80b6bf --- /dev/null +++ b/code/modules/hydroponics/seedtypes/diona.dm @@ -0,0 +1,21 @@ +/datum/seed/diona + name = "diona" + seed_name = "diona" + seed_noun = "nodes" + display_name = "replicant pods" + can_self_harvest = 1 + apply_color_to_mob = FALSE + has_mob_product = /mob/living/carbon/alien/diona + +/datum/seed/diona/New() + ..() + set_trait(TRAIT_IMMUTABLE,1) + set_trait(TRAIT_ENDURANCE,8) + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,10) + set_trait(TRAIT_YIELD,1) + set_trait(TRAIT_POTENCY,30) + set_trait(TRAIT_PRODUCT_ICON,"diona") + set_trait(TRAIT_PRODUCT_COLOUR,"#799957") + set_trait(TRAIT_PLANT_COLOUR,"#66804B") + set_trait(TRAIT_PLANT_ICON,"alien4") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/durian.dm b/code/modules/hydroponics/seedtypes/durian.dm new file mode 100644 index 0000000000..8963f4c9ec --- /dev/null +++ b/code/modules/hydroponics/seedtypes/durian.dm @@ -0,0 +1,21 @@ +/datum/seed/durian + name = "durian" + seed_name = "durian" + seed_noun = "pits" + display_name = "durian" + kitchen_tag = "durian" + chems = list("nutriment" = list(1,5), "durianpaste" = list(1, 20)) + +/datum/seed/durian/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"spinefruit") + set_trait(TRAIT_PRODUCT_COLOUR,"#757631") + set_trait(TRAIT_PLANT_COLOUR,"#87C969") + set_trait(TRAIT_PLANT_ICON,"tree") + set_trait(TRAIT_IDEAL_LIGHT, 8) + set_trait(TRAIT_WATER_CONSUMPTION, 8) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/eggplant.dm b/code/modules/hydroponics/seedtypes/eggplant.dm new file mode 100644 index 0000000000..c856f0d382 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/eggplant.dm @@ -0,0 +1,31 @@ +//Eggplants/varieties. +/datum/seed/eggplant + name = "eggplant" + seed_name = "eggplant" + display_name = "eggplants" + kitchen_tag = "eggplant" + mutants = list("egg-plant") + chems = list("nutriment" = list(1,10)) + +/datum/seed/eggplant/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,20) + set_trait(TRAIT_PRODUCT_ICON,"eggplant") + set_trait(TRAIT_PRODUCT_COLOUR,"#892694") + set_trait(TRAIT_PLANT_ICON,"bush4") + set_trait(TRAIT_IDEAL_HEAT, 298) + set_trait(TRAIT_IDEAL_LIGHT, 7) + +// Return of Eggy. Just makes purple eggs. If the reagents are separated from the egg production by xenobotany or RNG, it's still an Egg plant. +/datum/seed/eggplant/egg + name = "egg-plant" + seed_name = "egg-plant" + display_name = "egg-plants" + kitchen_tag = "egg-plant" + mutants = null + chems = list("nutriment" = list(1,5), "egg" = list(3,12)) + has_item_product = /obj/item/weapon/reagent_containers/food/snacks/egg/purple \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/flowers.dm b/code/modules/hydroponics/seedtypes/flowers.dm new file mode 100644 index 0000000000..2c2ffe13ec --- /dev/null +++ b/code/modules/hydroponics/seedtypes/flowers.dm @@ -0,0 +1,108 @@ +//Flowers/varieties +/datum/seed/flower + name = "harebells" + seed_name = "harebell" + display_name = "harebells" + kitchen_tag = "harebell" + chems = list("nutriment" = list(1,20)) + +/datum/seed/flower/New() + ..() + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_PRODUCT_ICON,"flower5") + set_trait(TRAIT_PRODUCT_COLOUR,"#C492D6") + set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") + set_trait(TRAIT_PLANT_ICON,"flower") + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + +/datum/seed/flower/poppy + name = "poppies" + seed_name = "poppy" + display_name = "poppies" + kitchen_tag = "poppy" + chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10)) + +/datum/seed/flower/poppy/New() + ..() + set_trait(TRAIT_POTENCY,20) + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,6) + set_trait(TRAIT_PRODUCT_ICON,"flower3") + set_trait(TRAIT_PRODUCT_COLOUR,"#B33715") + set_trait(TRAIT_PLANT_ICON,"flower3") + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + +/datum/seed/flower/sunflower + name = "sunflowers" + seed_name = "sunflower" + display_name = "sunflowers" + kitchen_tag = "sunflower" + +/datum/seed/flower/sunflower/New() + ..() + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCT_ICON,"flower2") + set_trait(TRAIT_PRODUCT_COLOUR,"#FFF700") + set_trait(TRAIT_PLANT_ICON,"flower2") + set_trait(TRAIT_IDEAL_LIGHT, 7) + set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + +/datum/seed/flower/lavender + name = "lavender" + seed_name = "lavender" + display_name = "lavender" + kitchen_tag = "lavender" + chems = list("nutriment" = list(1,20), "bicaridine" = list(1,10)) + +/datum/seed/flower/lavender/New() + ..() + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,5) + set_trait(TRAIT_PRODUCT_ICON,"flower6") + set_trait(TRAIT_PRODUCT_COLOUR,"#B57EDC") + set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") + set_trait(TRAIT_PLANT_ICON,"flower4") + set_trait(TRAIT_IDEAL_LIGHT, 7) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.05) + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + +/datum/seed/flower/rose + name = "rose" + seed_name = "rose" + display_name = "rose" + kitchen_tag = "rose" + mutants = list("bloodrose") + chems = list("nutriment" = list(1,5), "stoxin" = list(0,2)) + +/datum/seed/flower/rose/New() + ..() + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_PRODUCT_ICON,"flowers") + set_trait(TRAIT_PRODUCT_COLOUR,"#ce0e0e") + set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") + set_trait(TRAIT_PLANT_ICON,"bush5") + set_trait(TRAIT_IDEAL_LIGHT, 7) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.1) + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + set_trait(TRAIT_STINGS,1) + +/datum/seed/flower/rose/blood + name = "bloodrose" + display_name = "bleeding rose" + mutants = null + chems = list("nutriment" = list(1,5), "stoxin" = list(1,5), "blood" = list(0,2)) + +/datum/seed/flower/rose/blood/New() + ..() + set_trait(TRAIT_IDEAL_LIGHT, 1) + set_trait(TRAIT_PLANT_COLOUR,"#5e0303") + set_trait(TRAIT_CARNIVOROUS,1) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/gelthi.dm b/code/modules/hydroponics/seedtypes/gelthi.dm new file mode 100644 index 0000000000..1fa365c875 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/gelthi.dm @@ -0,0 +1,15 @@ +/datum/seed/gelthi + name = "gelthi" + seed_name = "gelthi" + display_name = "gelthi plant" + kitchen_tag = "gelthi" + chems = list("stoxin" = list(1,5),"capsaicin" = list(1,5),"nutriment" = list(1,5)) + +/datum/seed/gelthi/New() + ..() + set_trait(TRAIT_PLANT_ICON,"mushroom3") + set_trait(TRAIT_ENDURANCE,15) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,1) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/gnomes.dm b/code/modules/hydroponics/seedtypes/gnomes.dm new file mode 100644 index 0000000000..2ee0901926 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/gnomes.dm @@ -0,0 +1,25 @@ +// Gnomes +/datum/seed/gnomes + name = "gnomes" + seed_name = "gnomes" + display_name = "gnomes" + force_layer = 3 + chems = list("magicdust" = list(5,20)) + +/datum/seed/gnomes/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_PLANT_ICON,"gnomes") + set_trait(TRAIT_PRODUCT_ICON,"gnomes") + set_trait(TRAIT_PRODUCT_COLOUR,"") + set_trait(TRAIT_FLESH_COLOUR,"") + set_trait(TRAIT_PLANT_COLOUR,"") + set_trait(TRAIT_BIOLUM_COLOUR,"#fff200") + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_BIOLUM,1) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_SPREAD,1) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_REQUIRES_NUTRIENTS,0) + set_trait(TRAIT_REQUIRES_WATER,0) diff --git a/code/modules/hydroponics/seedtypes/grapes.dm b/code/modules/hydroponics/seedtypes/grapes.dm new file mode 100644 index 0000000000..e61978e5f0 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/grapes.dm @@ -0,0 +1,33 @@ +//Grapes/varieties +/datum/seed/grapes + name = "grapes" + seed_name = "grape" + display_name = "grapevines" + kitchen_tag = "grapes" + mutants = list("greengrapes") + chems = list("nutriment" = list(1,10), "sugar" = list(1,5), "grapejuice" = list(10,10)) + +/datum/seed/grapes/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,3) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"grapes") + set_trait(TRAIT_PRODUCT_COLOUR,"#BB6AC4") + set_trait(TRAIT_PLANT_COLOUR,"#378F2E") + set_trait(TRAIT_PLANT_ICON,"vine") + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) + +/datum/seed/grapes/green + name = "greengrapes" + seed_name = "green grape" + display_name = "green grapevines" + mutants = null + chems = list("nutriment" = list(1,10), "kelotane" = list(3,5), "grapejuice" = list(10,10)) + +/datum/seed/grapes/green/New() + ..() + set_trait(TRAIT_PRODUCT_COLOUR,"42ed2f") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/grass.dm b/code/modules/hydroponics/seedtypes/grass.dm new file mode 100644 index 0000000000..0a94f9707f --- /dev/null +++ b/code/modules/hydroponics/seedtypes/grass.dm @@ -0,0 +1,19 @@ +/datum/seed/grass + name = "grass" + seed_name = "grass" + display_name = "grass" + kitchen_tag = "grass" + chems = list("nutriment" = list(1,20)) + +/datum/seed/grass/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,2) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,5) + set_trait(TRAIT_PRODUCT_ICON,"grass") + set_trait(TRAIT_PRODUCT_COLOUR,"#09FF00") + set_trait(TRAIT_PLANT_COLOUR,"#07D900") + set_trait(TRAIT_PLANT_ICON,"grass") + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/jurlmah.dm b/code/modules/hydroponics/seedtypes/jurlmah.dm new file mode 100644 index 0000000000..61e810b9da --- /dev/null +++ b/code/modules/hydroponics/seedtypes/jurlmah.dm @@ -0,0 +1,15 @@ +/datum/seed/jurlmah + name = "jurlmah" + seed_name = "jurl'mah" + display_name = "jurl'mah reeds" + kitchen_tag = "jurlmah" + chems = list("serotrotium" = list(1,5),"nutriment" = list(1,5)) + +/datum/seed/jurlmah/New() + ..() + set_trait(TRAIT_PLANT_ICON,"mushroom9") + set_trait(TRAIT_ENDURANCE,12) + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,9) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,10) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/kudzu.dm b/code/modules/hydroponics/seedtypes/kudzu.dm new file mode 100644 index 0000000000..336c205b25 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/kudzu.dm @@ -0,0 +1,19 @@ +/datum/seed/kudzu + name = "kudzu" + seed_name = "kudzu" + display_name = "kudzu vines" + kitchen_tag = "kudzu" + chems = list("nutriment" = list(1,50), "anti_toxin" = list(1,25)) + +/datum/seed/kudzu/New() + ..() + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_SPREAD,2) + set_trait(TRAIT_PRODUCT_ICON,"treefruit") + set_trait(TRAIT_PRODUCT_COLOUR,"#96D278") + set_trait(TRAIT_PLANT_COLOUR,"#6F7A63") + set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/lettuce.dm b/code/modules/hydroponics/seedtypes/lettuce.dm new file mode 100644 index 0000000000..ae2830f88a --- /dev/null +++ b/code/modules/hydroponics/seedtypes/lettuce.dm @@ -0,0 +1,34 @@ +// Lettuce/varieties. +/datum/seed/lettuce + name = "lettuce" + seed_name = "lettuce" + display_name = "lettuce" + kitchen_tag = "cabbage" + chems = list("nutriment" = list(1,15)) + +/datum/seed/lettuce/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,4) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,6) + set_trait(TRAIT_POTENCY,8) + set_trait(TRAIT_PRODUCT_ICON,"lettuce") + set_trait(TRAIT_PRODUCT_COLOUR,"#A8D0A7") + set_trait(TRAIT_PLANT_COLOUR,"#6D9C6B") + set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_WATER_CONSUMPTION, 8) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.13) + +/datum/seed/lettuce/ice + name = "siflettuce" + seed_name = "glacial lettuce" + display_name = "glacial lettuce" + kitchen_tag = "icelettuce" + chems = list("nutriment" = list(1,5), "paracetamol" = list(0,2)) + +/datum/seed/lettuce/ice/New() + ..() + set_trait(TRAIT_ALTER_TEMP, -5) + set_trait(TRAIT_PRODUCT_COLOUR,"#9ABCC9") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/malanitear.dm b/code/modules/hydroponics/seedtypes/malanitear.dm new file mode 100644 index 0000000000..15b62d23d4 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/malanitear.dm @@ -0,0 +1,19 @@ +/datum/seed/mtear + name = "mtear" + seed_name = "Malani's tear" + display_name = "Malani's tear leaves" + kitchen_tag = "mtear" + chems = list("honey" = list(1,10), "kelotane" = list(3,5)) + +/datum/seed/mtear/New() + ..() + set_trait(TRAIT_MATURATION,3) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"alien4") + set_trait(TRAIT_PRODUCT_COLOUR,"#4CC5C7") + set_trait(TRAIT_PLANT_COLOUR,"#4CC789") + set_trait(TRAIT_PLANT_ICON,"bush7") + set_trait(TRAIT_IDEAL_HEAT, 283) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/mushrooms.dm b/code/modules/hydroponics/seedtypes/mushrooms.dm new file mode 100644 index 0000000000..ceb6b0a33b --- /dev/null +++ b/code/modules/hydroponics/seedtypes/mushrooms.dm @@ -0,0 +1,200 @@ +//Mushrooms/varieties. +/datum/seed/mushroom + name = "mushrooms" + seed_name = "chanterelle" + seed_noun = "spores" + display_name = "chanterelle mushrooms" + mutants = list("reishi","amanita","plumphelmet") + chems = list("nutriment" = list(1,25)) + splat_type = /obj/effect/plant + kitchen_tag = "mushroom" + +/datum/seed/mushroom/New() + ..() + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,5) + set_trait(TRAIT_POTENCY,1) + set_trait(TRAIT_PRODUCT_ICON,"mushroom4") + set_trait(TRAIT_PRODUCT_COLOUR,"#DBDA72") + set_trait(TRAIT_PLANT_COLOUR,"#D9C94E") + set_trait(TRAIT_PLANT_ICON,"mushroom") + set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_trait(TRAIT_IDEAL_HEAT, 288) + set_trait(TRAIT_LIGHT_TOLERANCE, 6) + +/datum/seed/mushroom/mold + name = "mold" + seed_name = "brown mold" + display_name = "brown mold" + mutants = null + +/datum/seed/mushroom/mold/New() + ..() + set_trait(TRAIT_SPREAD,1) + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_YIELD,-1) + set_trait(TRAIT_PRODUCT_ICON,"mushroom5") + set_trait(TRAIT_PRODUCT_COLOUR,"#7A5F20") + set_trait(TRAIT_PLANT_COLOUR,"#7A5F20") + set_trait(TRAIT_PLANT_ICON,"mushroom9") + +/datum/seed/mushroom/plump + name = "plumphelmet" + seed_name = "plump helmet" + display_name = "plump helmet mushrooms" + mutants = list("walkingmushroom","towercap") + chems = list("nutriment" = list(2,10)) + kitchen_tag = "plumphelmet" + +/datum/seed/mushroom/plump/New() + ..() + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,0) + set_trait(TRAIT_PRODUCT_ICON,"mushroom10") + set_trait(TRAIT_PRODUCT_COLOUR,"#B57BB0") + set_trait(TRAIT_PLANT_COLOUR,"#9E4F9D") + set_trait(TRAIT_PLANT_ICON,"mushroom2") + +/datum/seed/mushroom/hallucinogenic + name = "reishi" + seed_name = "reishi" + display_name = "reishi" + mutants = list("libertycap","glowshroom") + chems = list("nutriment" = list(1,50), "psilocybin" = list(3,5)) + +/datum/seed/mushroom/hallucinogenic/New() + ..() + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,15) + set_trait(TRAIT_PRODUCT_ICON,"mushroom11") + set_trait(TRAIT_PRODUCT_COLOUR,"#FFB70F") + set_trait(TRAIT_PLANT_COLOUR,"#F58A18") + set_trait(TRAIT_PLANT_ICON,"mushroom6") + +/datum/seed/mushroom/hallucinogenic/strong + name = "libertycap" + seed_name = "liberty cap" + display_name = "liberty cap mushrooms" + mutants = null + chems = list("nutriment" = list(1), "stoxin" = list(3,3), "space_drugs" = list(1,25)) + +/datum/seed/mushroom/hallucinogenic/strong/New() + ..() + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_POTENCY,15) + set_trait(TRAIT_PRODUCT_ICON,"mushroom8") + set_trait(TRAIT_PRODUCT_COLOUR,"#F2E550") + set_trait(TRAIT_PLANT_COLOUR,"#D1CA82") + set_trait(TRAIT_PLANT_ICON,"mushroom3") + +/datum/seed/mushroom/poison + name = "amanita" + seed_name = "fly amanita" + display_name = "fly amanita mushrooms" + mutants = list("destroyingangel","plastic") + chems = list("nutriment" = list(1), "amatoxin" = list(3,3), "psilocybin" = list(1,25)) + +/datum/seed/mushroom/poison/New() + ..() + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"mushroom") + set_trait(TRAIT_PRODUCT_COLOUR,"#FF4545") + set_trait(TRAIT_PLANT_COLOUR,"#E0DDBA") + set_trait(TRAIT_PLANT_ICON,"mushroom4") + +/datum/seed/mushroom/poison/death + name = "destroyingangel" + seed_name = "destroying angel" + display_name = "destroying angel mushrooms" + mutants = null + chems = list("nutriment" = list(1,50), "amatoxin" = list(13,3), "psilocybin" = list(1,25)) + +/datum/seed/mushroom/poison/death/New() + ..() + set_trait(TRAIT_MATURATION,12) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,35) + set_trait(TRAIT_PRODUCT_ICON,"mushroom3") + set_trait(TRAIT_PRODUCT_COLOUR,"#EDE8EA") + set_trait(TRAIT_PLANT_COLOUR,"#E6D8DD") + set_trait(TRAIT_PLANT_ICON,"mushroom5") + +/datum/seed/mushroom/towercap + name = "towercap" + seed_name = "tower cap" + display_name = "tower caps" + chems = list("woodpulp" = list(10,1)) + mutants = null + has_item_product = /obj/item/stack/material/log + +/datum/seed/mushroom/towercap/New() + ..() + set_trait(TRAIT_MATURATION,15) + set_trait(TRAIT_PRODUCT_ICON,"mushroom7") + set_trait(TRAIT_PRODUCT_COLOUR,"#79A36D") + set_trait(TRAIT_PLANT_COLOUR,"#857F41") + set_trait(TRAIT_PLANT_ICON,"mushroom8") + +/datum/seed/mushroom/glowshroom + name = "glowshroom" + seed_name = "glowshroom" + display_name = "glowshrooms" + mutants = null + chems = list("radium" = list(1,20)) + +/datum/seed/mushroom/glowshroom/New() + ..() + set_trait(TRAIT_SPREAD,1) + set_trait(TRAIT_MATURATION,15) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,30) + set_trait(TRAIT_BIOLUM,1) + set_trait(TRAIT_BIOLUM_COLOUR,"#006622") + set_trait(TRAIT_PRODUCT_ICON,"mushroom2") + set_trait(TRAIT_PRODUCT_COLOUR,"#DDFAB6") + set_trait(TRAIT_PLANT_COLOUR,"#EFFF8A") + set_trait(TRAIT_PLANT_ICON,"mushroom7") + +/datum/seed/mushroom/plastic + name = "plastic" + seed_name = "plastellium" + display_name = "plastellium" + mutants = null + chems = list("plasticide" = list(1,10)) + +/datum/seed/mushroom/plastic/New() + ..() + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,6) + set_trait(TRAIT_POTENCY,20) + set_trait(TRAIT_PRODUCT_ICON,"mushroom6") + set_trait(TRAIT_PRODUCT_COLOUR,"#E6E6E6") + set_trait(TRAIT_PLANT_COLOUR,"#E6E6E6") + set_trait(TRAIT_PLANT_ICON,"mushroom10") + +/datum/seed/mushroom/spore + name = "sporeshroom" + seed_name = "corpellian" + display_name = "corpellian" + mutants = null + chems = list("serotrotium" = list(5,10), "mold" = list(1,10)) + +/datum/seed/mushroom/spore/New() + ..() + set_trait(TRAIT_MATURATION,15) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,20) + set_trait(TRAIT_PRODUCT_ICON,"mushroom5") + set_trait(TRAIT_PRODUCT_COLOUR,"#e29cd2") + set_trait(TRAIT_PLANT_COLOUR,"#f8e6f4") + set_trait(TRAIT_PLANT_ICON,"mushroom9") + set_trait(TRAIT_SPORING, TRUE) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/nettles.dm b/code/modules/hydroponics/seedtypes/nettles.dm new file mode 100644 index 0000000000..5a1073c6fc --- /dev/null +++ b/code/modules/hydroponics/seedtypes/nettles.dm @@ -0,0 +1,35 @@ +// Nettles/variants. +/datum/seed/nettle + name = "nettle" + seed_name = "nettle" + display_name = "nettles" + mutants = list("deathnettle") + chems = list("nutriment" = list(1,50), "sacid" = list(0,1)) + kitchen_tag = "nettle" + +/datum/seed/nettle/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_STINGS,1) + set_trait(TRAIT_PLANT_ICON,"bush5") + set_trait(TRAIT_PRODUCT_ICON,"nettles") + set_trait(TRAIT_PRODUCT_COLOUR,"#728A54") + +/datum/seed/nettle/death + name = "deathnettle" + seed_name = "death nettle" + display_name = "death nettles" + kitchen_tag = "deathnettle" + mutants = null + chems = list("nutriment" = list(1,50), "pacid" = list(0,1)) + +/datum/seed/nettle/death/New() + ..() + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_PRODUCT_COLOUR,"#8C5030") + set_trait(TRAIT_PLANT_COLOUR,"#634941") diff --git a/code/modules/hydroponics/seedtypes/onion.dm b/code/modules/hydroponics/seedtypes/onion.dm new file mode 100644 index 0000000000..2123ad2b38 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/onion.dm @@ -0,0 +1,17 @@ +/datum/seed/onion + name = "onion" + seed_name = "onion" + display_name = "onions" + kitchen_tag = "onion" + chems = list("nutriment" = list(1,10)) + +/datum/seed/onion/New() + ..() + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"onion") + set_trait(TRAIT_PRODUCT_COLOUR,"#E0C367") + set_trait(TRAIT_PLANT_ICON,"carrot") + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/peanuts.dm b/code/modules/hydroponics/seedtypes/peanuts.dm new file mode 100644 index 0000000000..cc710d25ca --- /dev/null +++ b/code/modules/hydroponics/seedtypes/peanuts.dm @@ -0,0 +1,19 @@ +//Everything else +/datum/seed/peanuts + name = "peanut" + seed_name = "peanut" + display_name = "peanut vines" + kitchen_tag = "peanut" + chems = list("nutriment" = list(1,10), "peanutoil" = list(3,10)) + +/datum/seed/peanuts/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,6) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"nuts") + set_trait(TRAIT_PRODUCT_COLOUR,"#C4AE7A") + set_trait(TRAIT_PLANT_ICON,"bush2") + set_trait(TRAIT_IDEAL_LIGHT, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/potato.dm b/code/modules/hydroponics/seedtypes/potato.dm new file mode 100644 index 0000000000..8aad55afc6 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/potato.dm @@ -0,0 +1,18 @@ +/datum/seed/potato + name = "potato" + seed_name = "potato" + display_name = "potatoes" + kitchen_tag = "potato" + chems = list("nutriment" = list(1,10), "potatojuice" = list(10,10)) + +/datum/seed/potato/New() + ..() + set_trait(TRAIT_PRODUCES_POWER,1) + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"potato") + set_trait(TRAIT_PRODUCT_COLOUR,"#D4CAB4") + set_trait(TRAIT_PLANT_ICON,"bush2") + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/pumpkin.dm b/code/modules/hydroponics/seedtypes/pumpkin.dm new file mode 100644 index 0000000000..916d44e58b --- /dev/null +++ b/code/modules/hydroponics/seedtypes/pumpkin.dm @@ -0,0 +1,19 @@ +/datum/seed/pumpkin + name = "pumpkin" + seed_name = "pumpkin" + display_name = "pumpkin vine" + kitchen_tag = "pumpkin" + chems = list("nutriment" = list(1,6)) + +/datum/seed/pumpkin/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"vine2") + set_trait(TRAIT_PRODUCT_COLOUR,"#DBAC02") + set_trait(TRAIT_PLANT_COLOUR,"#21661E") + set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/rhubarb.dm b/code/modules/hydroponics/seedtypes/rhubarb.dm new file mode 100644 index 0000000000..f3ee13ce41 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/rhubarb.dm @@ -0,0 +1,17 @@ +/datum/seed/rhubarb + name = "rhubarb" + seed_name = "rhubarb" + display_name = "rhubarb" + kitchen_tag = "rhubarb" + chems = list("nutriment" = list(1,15)) + +/datum/seed/rhubarb/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,3) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,5) + set_trait(TRAIT_POTENCY,6) + set_trait(TRAIT_PRODUCT_ICON,"stalk") + set_trait(TRAIT_PRODUCT_COLOUR,"#FD5656") + set_trait(TRAIT_PLANT_ICON,"stalk3") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/rice.dm b/code/modules/hydroponics/seedtypes/rice.dm new file mode 100644 index 0000000000..413c43b9fc --- /dev/null +++ b/code/modules/hydroponics/seedtypes/rice.dm @@ -0,0 +1,19 @@ +/datum/seed/rice + name = "rice" + seed_name = "rice" + display_name = "rice stalks" + kitchen_tag = "rice" + chems = list("nutriment" = list(1,25), "rice" = list(10,15)) + +/datum/seed/rice/New() + ..() + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,5) + set_trait(TRAIT_PRODUCT_ICON,"rice") + set_trait(TRAIT_PRODUCT_COLOUR,"#D5E6D1") + set_trait(TRAIT_PLANT_COLOUR,"#8ED17D") + set_trait(TRAIT_PLANT_ICON,"stalk2") + set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/selemhand.dm b/code/modules/hydroponics/seedtypes/selemhand.dm new file mode 100644 index 0000000000..5b49728c61 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/selemhand.dm @@ -0,0 +1,19 @@ +/datum/seed/shand + name = "shand" + seed_name = "Selem's hand" + display_name = "Selem's hand leaves" + kitchen_tag = "shand" + chems = list("bicaridine" = list(0,10)) + +/datum/seed/shand/New() + ..() + set_trait(TRAIT_MATURATION,3) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"alien3") + set_trait(TRAIT_PRODUCT_COLOUR,"#378C61") + set_trait(TRAIT_PLANT_COLOUR,"#378C61") + set_trait(TRAIT_PLANT_ICON,"tree5") + set_trait(TRAIT_IDEAL_HEAT, 283) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/soybean.dm b/code/modules/hydroponics/seedtypes/soybean.dm new file mode 100644 index 0000000000..22329be263 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/soybean.dm @@ -0,0 +1,17 @@ +/datum/seed/soybean + name = "soybean" + seed_name = "soybean" + display_name = "soybeans" + kitchen_tag = "soybeans" + chems = list("nutriment" = list(1,20), "soymilk" = list(10,20)) + +/datum/seed/soybean/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,4) + set_trait(TRAIT_PRODUCTION,4) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,5) + set_trait(TRAIT_PRODUCT_ICON,"bean") + set_trait(TRAIT_PRODUCT_COLOUR,"#EBE7C0") + set_trait(TRAIT_PLANT_ICON,"stalk") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/spineapple.dm b/code/modules/hydroponics/seedtypes/spineapple.dm new file mode 100644 index 0000000000..b8ff58f597 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/spineapple.dm @@ -0,0 +1,22 @@ +/datum/seed/spineapple + name = "spineapple" + seed_name = "spineapple" + display_name = "spineapple" + kitchen_tag = "pineapple" + chems = list("nutriment" = list(1,5), "enzyme" = list(1,5), "pineapplejuice" = list(1, 20)) + +/datum/seed/spineapple/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,10) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,1) + set_trait(TRAIT_POTENCY,13) + set_trait(TRAIT_PRODUCT_ICON,"pineapple") + set_trait(TRAIT_PRODUCT_COLOUR,"#FFF23B") + set_trait(TRAIT_PLANT_COLOUR,"#87C969") + set_trait(TRAIT_PLANT_ICON,"corn") + set_trait(TRAIT_IDEAL_HEAT, 298) + set_trait(TRAIT_IDEAL_LIGHT, 4) + set_trait(TRAIT_WATER_CONSUMPTION, 8) + set_trait(TRAIT_STINGS,1) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/sugarcane.dm b/code/modules/hydroponics/seedtypes/sugarcane.dm new file mode 100644 index 0000000000..c670500a1d --- /dev/null +++ b/code/modules/hydroponics/seedtypes/sugarcane.dm @@ -0,0 +1,19 @@ +/datum/seed/sugarcane + name = "sugarcane" + seed_name = "sugarcane" + display_name = "sugarcanes" + kitchen_tag = "sugarcanes" + chems = list("sugar" = list(4,5)) + +/datum/seed/sugarcane/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,3) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"stalk") + set_trait(TRAIT_PRODUCT_COLOUR,"#B4D6BD") + set_trait(TRAIT_PLANT_COLOUR,"#6BBD68") + set_trait(TRAIT_PLANT_ICON,"stalk3") + set_trait(TRAIT_IDEAL_HEAT, 298) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/surik.dm b/code/modules/hydroponics/seedtypes/surik.dm new file mode 100644 index 0000000000..8ea521995c --- /dev/null +++ b/code/modules/hydroponics/seedtypes/surik.dm @@ -0,0 +1,15 @@ +/datum/seed/surik + name = "surik" + seed_name = "surik" + display_name = "surik vine" + kitchen_tag = "surik" + chems = list("impedrezene" = list(1,3),"synaptizine" = list(1,2),"nutriment" = list(1,5)) + +/datum/seed/surik/New() + ..() + set_trait(TRAIT_PLANT_ICON,"bush6") + set_trait(TRAIT_ENDURANCE,18) + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,7) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,3) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/telriis.dm b/code/modules/hydroponics/seedtypes/telriis.dm new file mode 100644 index 0000000000..47c577b787 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/telriis.dm @@ -0,0 +1,16 @@ +/datum/seed/telriis + name = "telriis" + seed_name = "telriis" + display_name = "telriis grass" + kitchen_tag = "telriis" + chems = list("pwine" = list(1,5), "nutriment" = list(1,6)) + +/datum/seed/telriis/New() + ..() + set_trait(TRAIT_PLANT_ICON,"ambrosia") + set_trait(TRAIT_PRODUCT_ICON,"ambrosia") + set_trait(TRAIT_ENDURANCE,50) + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,5) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/thaadra.dm b/code/modules/hydroponics/seedtypes/thaadra.dm new file mode 100644 index 0000000000..209b495c82 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/thaadra.dm @@ -0,0 +1,16 @@ +/datum/seed/thaadra + name = "thaadra" + seed_name = "thaa'dra" + display_name = "thaa'dra lichen" + kitchen_tag = "thaadra" + chems = list("frostoil" = list(1,5),"nutriment" = list(1,5)) + +/datum/seed/thaadra/New() + ..() + set_trait(TRAIT_PLANT_ICON,"grass") + set_trait(TRAIT_PLANT_COLOUR,"#ABC7D2") + set_trait(TRAIT_ENDURANCE,10) + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,9) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,5) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/tomatoes.dm b/code/modules/hydroponics/seedtypes/tomatoes.dm new file mode 100644 index 0000000000..3454d69952 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/tomatoes.dm @@ -0,0 +1,75 @@ +//Tomatoes/variants. +/datum/seed/tomato + name = "tomato" + seed_name = "tomato" + display_name = "tomato plant" + mutants = list("bluetomato","bloodtomato") + chems = list("nutriment" = list(1,10), "tomatojuice" = list(10,10)) + kitchen_tag = "tomato" + +/datum/seed/tomato/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_JUICY,1) + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"tomato") + set_trait(TRAIT_PRODUCT_COLOUR,"#D10000") + set_trait(TRAIT_PLANT_ICON,"bush3") + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_WATER_CONSUMPTION, 6) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) + +/datum/seed/tomato/blood + name = "bloodtomato" + seed_name = "blood tomato" + display_name = "blood tomato plant" + mutants = list("killertomato") + chems = list("nutriment" = list(1,10), "blood" = list(1,5)) + splat_type = /obj/effect/decal/cleanable/blood/splatter + +/datum/seed/tomato/blood/New() + ..() + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_PRODUCT_COLOUR,"#FF0000") + +/datum/seed/tomato/killer + name = "killertomato" + seed_name = "killer tomato" + display_name = "killer tomato plant" + mutants = null + can_self_harvest = 1 + has_mob_product = /mob/living/simple_mob/tomato + +/datum/seed/tomato/killer/New() + ..() + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_PRODUCT_COLOUR,"#A86747") + +/datum/seed/tomato/blue + name = "bluetomato" + seed_name = "blue tomato" + display_name = "blue tomato plant" + mutants = list("bluespacetomato") + chems = list("nutriment" = list(1,20), "lube" = list(1,5)) + +/datum/seed/tomato/blue/New() + ..() + set_trait(TRAIT_PRODUCT_COLOUR,"#4D86E8") + set_trait(TRAIT_PLANT_COLOUR,"#070AAD") + +/datum/seed/tomato/blue/teleport + name = "bluespacetomato" + seed_name = "bluespace tomato" + display_name = "bluespace tomato plant" + mutants = null + chems = list("nutriment" = list(1,20), "singulo" = list(10,5)) + +/datum/seed/tomato/blue/teleport/New() + ..() + set_trait(TRAIT_TELEPORTING,1) + set_trait(TRAIT_PRODUCT_COLOUR,"#00E5FF") + set_trait(TRAIT_BIOLUM,1) + set_trait(TRAIT_BIOLUM_COLOUR,"#4DA4A8") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/vale.dm b/code/modules/hydroponics/seedtypes/vale.dm new file mode 100644 index 0000000000..166fafcf97 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/vale.dm @@ -0,0 +1,15 @@ +/datum/seed/vale + name = "vale" + seed_name = "vale" + display_name = "vale bush" + kitchen_tag = "vale" + chems = list("paracetamol" = list(1,5),"dexalin" = list(1,2),"nutriment"= list(1,5)) + +/datum/seed/vale/New() + ..() + set_trait(TRAIT_PLANT_ICON,"flower4") + set_trait(TRAIT_ENDURANCE,15) + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,10) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,3) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/vanilla.dm b/code/modules/hydroponics/seedtypes/vanilla.dm new file mode 100644 index 0000000000..a2bc5c8dcf --- /dev/null +++ b/code/modules/hydroponics/seedtypes/vanilla.dm @@ -0,0 +1,19 @@ +/datum/seed/vanilla + name = "vanilla" + seed_name = "vanilla" + display_name = "vanilla" + kitchen_tag = "vanilla" + chems = list("nutriment" = list(1,10), "vanilla" = list(2,8), "sugar" = list(1, 4)) + +/datum/seed/vanilla/New() + ..() + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,5) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_PRODUCT_ICON,"chili") + set_trait(TRAIT_PRODUCT_COLOUR,"#B57EDC") + set_trait(TRAIT_PLANT_COLOUR,"#6B8C5E") + set_trait(TRAIT_PLANT_ICON,"bush5") + set_trait(TRAIT_IDEAL_LIGHT, 8) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.3) + set_trait(TRAIT_WATER_CONSUMPTION, 0.5) diff --git a/code/modules/hydroponics/seedtypes/wabback.dm b/code/modules/hydroponics/seedtypes/wabback.dm new file mode 100644 index 0000000000..8c1e9411f0 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/wabback.dm @@ -0,0 +1,54 @@ +//Wabback / varieties. +/datum/seed/wabback + name = "whitewabback" + seed_name = "white wabback" + seed_noun = "nodes" + display_name = "white wabback" + chems = list("nutriment" = list(1,10), "protein" = list(1,5), "enzyme" = list(0,3)) + kitchen_tag = "wabback" + mutants = list("blackwabback","wildwabback") + has_item_product = /obj/item/stack/material/cloth + +/datum/seed/wabback/New() + ..() + set_trait(TRAIT_IDEAL_LIGHT, 5) + set_trait(TRAIT_MATURATION,8) + set_trait(TRAIT_PRODUCTION,3) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,5) + set_trait(TRAIT_PRODUCT_ICON,"carrot2") + set_trait(TRAIT_PRODUCT_COLOUR,"#E6EDFA") + set_trait(TRAIT_PLANT_ICON,"chute") + set_trait(TRAIT_PLANT_COLOUR, "#0650ce") + set_trait(TRAIT_WATER_CONSUMPTION, 10) + set_trait(TRAIT_ALTER_TEMP, -1) + set_trait(TRAIT_CARNIVOROUS,1) + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_SPREAD,1) + +/datum/seed/wabback/vine + name = "blackwabback" + seed_name = "black wabback" + display_name = "black wabback" + mutants = null + chems = list("nutriment" = list(1,3), "protein" = list(1,10), "serotrotium_v" = list(0,1)) + +/datum/seed/wabback/vine/New() + ..() + set_trait(TRAIT_PRODUCT_COLOUR,"#2E2F32") + set_trait(TRAIT_CARNIVOROUS,2) + +/datum/seed/wabback/wild + name = "wildwabback" + seed_name = "wild wabback" + display_name = "wild wabback" + mutants = list("whitewabback") + has_item_product = null + chems = list("nutriment" = list(1,15), "protein" = list(0,2), "enzyme" = list(0,1)) + +/datum/seed/wabback/wild/New() + ..() + set_trait(TRAIT_IDEAL_LIGHT, 3) + set_trait(TRAIT_WATER_CONSUMPTION, 7) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.1) + set_trait(TRAIT_YIELD,5) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/watermelon.dm b/code/modules/hydroponics/seedtypes/watermelon.dm new file mode 100644 index 0000000000..79ae157295 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/watermelon.dm @@ -0,0 +1,23 @@ +/datum/seed/watermelon + name = "watermelon" + seed_name = "watermelon" + display_name = "watermelon vine" + kitchen_tag = "watermelon" + chems = list("nutriment" = list(1,6), "watermelonjuice" = list(10,6)) + +/datum/seed/watermelon/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_JUICY,1) + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,3) + set_trait(TRAIT_POTENCY,1) + set_trait(TRAIT_PRODUCT_ICON,"vine") + set_trait(TRAIT_PRODUCT_COLOUR,"#3D8C3A") + set_trait(TRAIT_PLANT_COLOUR,"#257522") + set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_FLESH_COLOUR,"#F22C2C") + set_trait(TRAIT_IDEAL_HEAT, 298) + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/weeds.dm b/code/modules/hydroponics/seedtypes/weeds.dm new file mode 100644 index 0000000000..9a867174b8 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/weeds.dm @@ -0,0 +1,16 @@ +/datum/seed/weeds + name = "weeds" + seed_name = "weed" + display_name = "weeds" + +/datum/seed/weeds/New() + ..() + set_trait(TRAIT_MATURATION,5) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,-1) + set_trait(TRAIT_POTENCY,-1) + set_trait(TRAIT_IMMUTABLE,-1) + set_trait(TRAIT_PRODUCT_ICON,"flower4") + set_trait(TRAIT_PRODUCT_COLOUR,"#FCEB2B") + set_trait(TRAIT_PLANT_COLOUR,"#59945A") + set_trait(TRAIT_PLANT_ICON,"bush6") \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/wheat.dm b/code/modules/hydroponics/seedtypes/wheat.dm new file mode 100644 index 0000000000..a657490c15 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/wheat.dm @@ -0,0 +1,19 @@ +/datum/seed/wheat + name = "wheat" + seed_name = "wheat" + display_name = "wheat stalks" + kitchen_tag = "wheat" + chems = list("nutriment" = list(1,25), "flour" = list(10,30)) + +/datum/seed/wheat/New() + ..() + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,4) + set_trait(TRAIT_POTENCY,5) + set_trait(TRAIT_PRODUCT_ICON,"wheat") + set_trait(TRAIT_PRODUCT_COLOUR,"#DBD37D") + set_trait(TRAIT_PLANT_COLOUR,"#BFAF82") + set_trait(TRAIT_PLANT_ICON,"stalk2") + set_trait(TRAIT_IDEAL_LIGHT, 6) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.15) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/whitebeets.dm b/code/modules/hydroponics/seedtypes/whitebeets.dm new file mode 100644 index 0000000000..3534fcc7ff --- /dev/null +++ b/code/modules/hydroponics/seedtypes/whitebeets.dm @@ -0,0 +1,18 @@ +/datum/seed/whitebeets + name = "whitebeet" + seed_name = "white-beet" + display_name = "white-beets" + kitchen_tag = "whitebeet" + chems = list("nutriment" = list(0,20), "sugar" = list(1,5)) + +/datum/seed/whitebeets/New() + ..() + set_trait(TRAIT_MATURATION,6) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,6) + set_trait(TRAIT_POTENCY,10) + set_trait(TRAIT_PRODUCT_ICON,"carrot2") + set_trait(TRAIT_PRODUCT_COLOUR,"#EEF5B0") + set_trait(TRAIT_PLANT_COLOUR,"#4D8F53") + set_trait(TRAIT_PLANT_ICON,"carrot2") + set_trait(TRAIT_WATER_CONSUMPTION, 6) \ No newline at end of file diff --git a/code/modules/hydroponics/seedtypes/wurmwoad.dm b/code/modules/hydroponics/seedtypes/wurmwoad.dm new file mode 100644 index 0000000000..bb4df620a2 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/wurmwoad.dm @@ -0,0 +1,23 @@ +// Wurmwoad, the Space Spice maker. Totally is actually, 100% literal worms. + +/datum/seed/wurmwoad + name = "wurmwoad" + seed_name = "wurmwoad" + display_name = "wurmwoad growth" + chems = list("nutriment" = list(1,10), "spacespice" = list(5,15)) + kitchen_tag = "wurmwoad" + +/datum/seed/wurmwoad/New() + ..() + set_trait(TRAIT_HARVEST_REPEAT,1) + set_trait(TRAIT_MATURATION,7) + set_trait(TRAIT_PRODUCTION,6) + set_trait(TRAIT_YIELD,2) + set_trait(TRAIT_POTENCY,8) + set_trait(TRAIT_PRODUCT_ICON,"eyepod") + set_trait(TRAIT_PRODUCT_COLOUR,"#e08702") + set_trait(TRAIT_PLANT_COLOUR,"#f1d1d2") + set_trait(TRAIT_PLANT_ICON,"worm") + set_trait(TRAIT_IDEAL_LIGHT, 1) + set_trait(TRAIT_WATER_CONSUMPTION, 8) + set_trait(TRAIT_NUTRIENT_CONSUMPTION, 0.25) diff --git a/code/modules/hydroponics/seedtypes/xeno.dm b/code/modules/hydroponics/seedtypes/xeno.dm new file mode 100644 index 0000000000..2b1acd76b0 --- /dev/null +++ b/code/modules/hydroponics/seedtypes/xeno.dm @@ -0,0 +1,19 @@ +// Alien weeds. +/datum/seed/xenomorph + name = "xenomorph" + seed_name = "alien weed" + display_name = "alien weeds" + force_layer = 3 + chems = list("phoron" = list(1,3)) + +/datum/seed/xenomorph/New() + ..() + set_trait(TRAIT_PLANT_ICON,"vine2") + set_trait(TRAIT_IMMUTABLE,1) + set_trait(TRAIT_PRODUCT_COLOUR,"#3D1934") + set_trait(TRAIT_FLESH_COLOUR,"#3D1934") + set_trait(TRAIT_PLANT_COLOUR,"#3D1934") + set_trait(TRAIT_PRODUCTION,1) + set_trait(TRAIT_YIELD,-1) + set_trait(TRAIT_SPREAD,2) + set_trait(TRAIT_POTENCY,50) \ No newline at end of file diff --git a/code/modules/hydroponics/trays/tray_tools.dm b/code/modules/hydroponics/trays/tray_tools.dm index d05922b2f9..cedcc3f8b9 100644 --- a/code/modules/hydroponics/trays/tray_tools.dm +++ b/code/modules/hydroponics/trays/tray_tools.dm @@ -19,6 +19,10 @@ var/datum/seed/last_seed var/list/last_reagents +/obj/item/device/analyzer/plant_analyzer/Destroy() + . = ..() + QDEL_NULL(last_seed) + /obj/item/device/analyzer/plant_analyzer/attack_self(mob/user) tgui_interact(user) @@ -34,7 +38,7 @@ /obj/item/device/analyzer/plant_analyzer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) var/list/data = ..() - var/datum/seed/grown_seed = locate(last_seed) + var/datum/seed/grown_seed = last_seed if(!istype(grown_seed)) return list("no_seed" = TRUE) @@ -95,7 +99,9 @@ to_chat(user, "[src] can tell you nothing about \the [target].") return - last_seed = REF(grown_seed) + last_seed = grown_seed.diverge() + if(!istype(last_seed)) + last_seed = grown_seed // TRAIT_IMMUTABLE makes diverge() return null user.visible_message("[user] runs the scanner over \the [target].") @@ -119,7 +125,7 @@ print_report(usr) /obj/item/device/analyzer/plant_analyzer/proc/print_report(var/mob/living/user) - var/datum/seed/grown_seed = locate(last_seed) + var/datum/seed/grown_seed = last_seed if(!istype(grown_seed)) to_chat(user, "There is no scan data to print.") return diff --git a/code/modules/integrated_electronics/core/assemblies/device.dm b/code/modules/integrated_electronics/core/assemblies/device.dm index 3170393fc3..8500f4dafc 100644 --- a/code/modules/integrated_electronics/core/assemblies/device.dm +++ b/code/modules/integrated_electronics/core/assemblies/device.dm @@ -78,7 +78,7 @@ output.assembly = src /obj/item/device/electronic_assembly/device/check_interactivity(mob/user) - if(!CanInteract(user, state = deep_inventory_state)) + if(!CanInteract(user, state = GLOB.tgui_deep_inventory_state)) return 0 return 1 diff --git a/code/modules/integrated_electronics/core/pins.dm b/code/modules/integrated_electronics/core/pins.dm index ddc0b06f8a..c194b4a8a2 100644 --- a/code/modules/integrated_electronics/core/pins.dm +++ b/code/modules/integrated_electronics/core/pins.dm @@ -40,8 +40,8 @@ D [1]/ || holder = null . = ..() -/datum/integrated_io/nano_host() - return holder.nano_host() +/datum/integrated_io/tgui_host() + return holder.tgui_host() /datum/integrated_io/proc/data_as_type(var/as_type) diff --git a/code/modules/integrated_electronics/core/special_pins/list_pin.dm b/code/modules/integrated_electronics/core/special_pins/list_pin.dm index 77d83f82b5..4eeb756cd0 100644 --- a/code/modules/integrated_electronics/core/special_pins/list_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/list_pin.dm @@ -117,7 +117,7 @@ /datum/integrated_io/list/display_pin_type() return IC_FORMAT_LIST -/datum/integrated_io/list/Topic(href, href_list, state = interactive_state) +/datum/integrated_io/list/Topic(href, href_list, state = GLOB.tgui_always_state) if(!holder.check_interactivity(usr)) return if(..()) diff --git a/code/modules/integrated_electronics/core/tools.dm b/code/modules/integrated_electronics/core/tools.dm index 79533bfe25..5bf48370e2 100644 --- a/code/modules/integrated_electronics/core/tools.dm +++ b/code/modules/integrated_electronics/core/tools.dm @@ -115,7 +115,7 @@ /obj/item/device/integrated_electronics/debugger/attack_self(mob/user) var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null") - if(!CanInteract(user, physical_state)) + if(!CanInteract(user, GLOB.tgui_physical_state)) return var/new_data = null @@ -124,13 +124,13 @@ accepting_refs = 0 new_data = input("Now type in a string.","[src] string writing") as null|text new_data = sanitizeSafe(new_data, MAX_MESSAGE_LEN, 0, 0) - if(istext(new_data) && CanInteract(user, physical_state)) + if(istext(new_data) && CanInteract(user, GLOB.tgui_physical_state)) data_to_write = new_data to_chat(user, "You set \the [src]'s memory to \"[new_data]\".") if("number") accepting_refs = 0 new_data = input("Now type in a number.","[src] number writing") as null|num - if(isnum(new_data) && CanInteract(user, physical_state)) + if(isnum(new_data) && CanInteract(user, GLOB.tgui_physical_state)) data_to_write = new_data to_chat(user, "You set \the [src]'s memory to [new_data].") if("ref") diff --git a/code/modules/integrated_electronics/subtypes/input.dm b/code/modules/integrated_electronics/subtypes/input.dm index a084633642..9c140cedae 100644 --- a/code/modules/integrated_electronics/subtypes/input.dm +++ b/code/modules/integrated_electronics/subtypes/input.dm @@ -54,7 +54,7 @@ /obj/item/integrated_circuit/input/numberpad/ask_for_input(mob/user) var/new_input = input(user, "Enter a number, please.","Number pad", get_pin_data(IC_OUTPUT, 1)) as null|num - if(isnum(new_input) && CanInteract(user, physical_state)) + if(isnum(new_input) && CanInteract(user, GLOB.tgui_physical_state)) set_pin_data(IC_OUTPUT, 1, new_input) push_data() activate_pin(1) @@ -73,7 +73,7 @@ /obj/item/integrated_circuit/input/textpad/ask_for_input(mob/user) var/new_input = input(user, "Enter some words, please.","Number pad", get_pin_data(IC_OUTPUT, 1)) as null|text - if(istext(new_input) && CanInteract(user, physical_state)) + if(istext(new_input) && CanInteract(user, GLOB.tgui_physical_state)) set_pin_data(IC_OUTPUT, 1, new_input) push_data() activate_pin(1) @@ -92,7 +92,7 @@ /obj/item/integrated_circuit/input/colorpad/ask_for_input(mob/user) var/new_color = input(user, "Enter a color, please.", "Color pad", get_pin_data(IC_OUTPUT, 1)) as color|null - if(new_color && CanInteract(user, physical_state)) + if(new_color && CanInteract(user, GLOB.tgui_physical_state)) set_pin_data(IC_OUTPUT, 1, new_color) push_data() activate_pin(1) diff --git a/code/modules/integrated_electronics/subtypes/memory.dm b/code/modules/integrated_electronics/subtypes/memory.dm index 630136e7d0..90cb23de35 100644 --- a/code/modules/integrated_electronics/subtypes/memory.dm +++ b/code/modules/integrated_electronics/subtypes/memory.dm @@ -89,7 +89,7 @@ /obj/item/integrated_circuit/memory/constant/attack_self(mob/user) var/datum/integrated_io/O = outputs[1] var/type_to_use = input("Please choose a type to use.","[src] type setting") as null|anything in list("string","number","ref", "null") - if(!CanInteract(user, physical_state)) + if(!CanInteract(user, GLOB.tgui_physical_state)) return var/new_data = null @@ -97,13 +97,13 @@ if("string") accepting_refs = 0 new_data = input("Now type in a string.","[src] string writing") as null|text - if(istext(new_data) && CanInteract(user, physical_state)) + if(istext(new_data) && CanInteract(user, GLOB.tgui_physical_state)) O.data = new_data to_chat(user, "You set \the [src]'s memory to [O.display_data(O.data)].") if("number") accepting_refs = 0 new_data = input("Now type in a number.","[src] number writing") as null|num - if(isnum(new_data) && CanInteract(user, physical_state)) + if(isnum(new_data) && CanInteract(user, GLOB.tgui_physical_state)) O.data = new_data to_chat(user, "You set \the [src]'s memory to [O.display_data(O.data)].") if("ref") diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm index 153e023ad0..68bb201552 100644 --- a/code/modules/materials/material_sheets.dm +++ b/code/modules/materials/material_sheets.dm @@ -403,8 +403,8 @@ no_variants = FALSE pass_color = TRUE strict_color_stacking = TRUE - drop_sound = 'sound/items/drop/cloth.ogg' - pickup_sound = 'sound/items/pickup/cloth.ogg' + drop_sound = 'sound/items/drop/clothing.ogg' + pickup_sound = 'sound/items/pickup/clothing.ogg' /obj/item/stack/material/cloth/diyaab color = "#c6ccf0" diff --git a/code/modules/mob/living/bot/cleanbot.dm b/code/modules/mob/living/bot/cleanbot.dm index de4c2a2d78..cdcba151bc 100644 --- a/code/modules/mob/living/bot/cleanbot.dm +++ b/code/modules/mob/living/bot/cleanbot.dm @@ -220,6 +220,7 @@ target_types += /obj/effect/decal/cleanable/liquid_fuel target_types += /obj/effect/decal/cleanable/mucus target_types += /obj/effect/decal/cleanable/dirt + target_types += /obj/effect/decal/cleanable/filth if(blood) target_types += /obj/effect/decal/cleanable/blood diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 41a43324ff..889f5281cb 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -210,7 +210,7 @@ return spread_fire(AM) - + ..() // call parent because we moved behavior to parent // Get rank from ID, ID inside PDA, PDA, ID in wallet, etc. @@ -220,7 +220,7 @@ if (pda.id) return pda.id.rank ? pda.id.rank : if_no_job else - return pda.ownrank + return pda.ownrank ? pda.ownrank : if_no_job else var/obj/item/weapon/card/id/id = get_idcard() if(id) @@ -236,7 +236,7 @@ if (pda.id) return pda.id.assignment else - return pda.ownjob + return pda.ownjob ? pda.ownjob : if_no_job else var/obj/item/weapon/card/id/id = get_idcard() if(id) @@ -252,7 +252,7 @@ if (pda.id) return pda.id.registered_name else - return pda.owner + return pda.owner ? pda.owner : if_no_id else var/obj/item/weapon/card/id/id = get_idcard() if(id) @@ -285,7 +285,7 @@ . = if_no_id if(istype(wear_id,/obj/item/device/pda)) var/obj/item/device/pda/P = wear_id - return P.owner + return P.owner ? P.owner : if_no_id if(wear_id) var/obj/item/weapon/card/id/I = wear_id.GetID() if(I) diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 39ad53458b..fbf1b76b90 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1290,13 +1290,19 @@ clear_fullscreen("blind") clear_alert("blind") - if(disabilities & NEARSIGHTED) //this looks meh but saves a lot of memory by not requiring to add var/prescription - if(glasses) //to every /obj/item + var/apply_nearsighted_overlay = FALSE + if(disabilities & NEARSIGHTED) + apply_nearsighted_overlay = TRUE + + if(glasses) var/obj/item/clothing/glasses/G = glasses - if(!G.prescription) - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) - else if (!nif || !nif.flag_check(NIF_V_CORRECTIVE,NIF_FLAGS_VISION)) //VOREStation Edit - NIF - set_fullscreen(disabilities & NEARSIGHTED, "impaired", /obj/screen/fullscreen/impaired, 1) + if(G.prescription) + apply_nearsighted_overlay = FALSE + + if(nif && nif.flag_check(NIF_V_CORRECTIVE, NIF_FLAGS_VISION)) // VOREStation Edit - NIF + apply_nearsighted_overlay = FALSE + + set_fullscreen(apply_nearsighted_overlay, "nearsighted", /obj/screen/fullscreen/impaired, 1) set_fullscreen(eye_blurry, "blurry", /obj/screen/fullscreen/blurry) set_fullscreen(druggy, "high", /obj/screen/fullscreen/high) diff --git a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm index 632d45ea4d..c1c9ad064b 100644 --- a/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm +++ b/code/modules/mob/living/carbon/human/species/shadekin/shadekin_abilities.dm @@ -95,7 +95,7 @@ var/list/potentials = living_mobs(0) if(potentials.len) var/mob/living/target = pick(potentials) - if(istype(target) && vore_selected) + if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) target.forceMove(vore_selected) to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm index 282f8b6c3d..98f0a3190d 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm @@ -257,7 +257,7 @@ var/list/potentials = living_mobs(0) if(potentials.len) var/mob/living/target = pick(potentials) - if(istype(target) && vore_selected) + if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) if(target.buckled) target.buckled.unbuckle_mob(target, force = TRUE) target.forceMove(vore_selected) diff --git a/code/modules/mob/living/carbon/human/stripping.dm b/code/modules/mob/living/carbon/human/stripping.dm index 61bb766140..9f926272be 100644 --- a/code/modules/mob/living/carbon/human/stripping.dm +++ b/code/modules/mob/living/carbon/human/stripping.dm @@ -58,14 +58,22 @@ var/stripping var/obj/item/held = user.get_active_hand() if(!istype(held) || is_robot_module(held)) + stripping = TRUE + else + var/obj/item/weapon/holder/holder = held + if(istype(holder) && src == holder.held_mob) + stripping = TRUE + else + var/obj/item/weapon/grab/grab = held + if(istype(grab) && grab.affecting == src) + stripping = TRUE + + if(stripping) if(!istype(target_slot)) // They aren't holding anything valid and there's nothing to remove, why are we even here? return if(!target_slot.canremove) to_chat(user, "You cannot remove \the [src]'s [target_slot.name].") return - stripping = 1 - - if(stripping) visible_message("\The [user] is trying to remove \the [src]'s [target_slot.name]!") else if(slot_to_strip == slot_wear_mask && istype(held, /obj/item/weapon/grenade)) @@ -76,8 +84,14 @@ if(!do_after(user,HUMAN_STRIP_DELAY,src)) return - if(!stripping && user.get_active_hand() != held) - return + if(!stripping) + if(user.get_active_hand() != held) + return + var/obj/item/weapon/holder/mobheld = held + if(istype(mobheld)&&mobheld.held_mob==src) + to_chat(user, "You can't put someone on themselves! Stop trying to break reality!") + return + if(stripping) add_attack_logs(user,src,"Removed equipment from slot [target_slot]") diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 063b4fe188..a88a434d96 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -10,9 +10,6 @@ if(!loc) return - if(machine && !CanMouseDrop(machine, src)) - machine = null - var/datum/gas_mixture/environment = loc.return_air() //handle_modifiers() // Do this early since it might affect other things later. //VOREStation Edit diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 60966f6da2..0c20f6bf31 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -20,7 +20,6 @@ var/list/ai_verbs_default = list( /mob/living/silicon/ai/proc/ai_call_shuttle, /mob/living/silicon/ai/proc/ai_camera_track, /mob/living/silicon/ai/proc/ai_camera_list, - /mob/living/silicon/ai/proc/ai_roster, /mob/living/silicon/ai/proc/ai_checklaws, /mob/living/silicon/ai/proc/toggle_camera_light, /mob/living/silicon/ai/proc/take_image, @@ -48,7 +47,7 @@ var/list/ai_verbs_default = list( anchored = 1 // -- TLE density = 1 status_flags = CANSTUN|CANPARALYSE|CANPUSH - shouldnt_see = list(/obj/effect/rune) + shouldnt_see = list(/mob/observer/eye, /obj/effect/rune) var/list/network = list(NETWORK_DEFAULT) var/obj/machinery/camera/camera = null var/aiRestorePowerRoutine = 0 @@ -355,12 +354,6 @@ var/list/ai_verbs_default = list( if(new_sprite) selected_sprite = new_sprite updateicon() -// this verb lets the ai see the stations manifest -/mob/living/silicon/ai/proc/ai_roster() - set category = "AI Commands" - set name = "Show Crew Manifest" - show_station_manifest() - /mob/living/silicon/ai/var/message_cooldown = 0 /mob/living/silicon/ai/proc/ai_announcement() set category = "AI Commands" @@ -399,9 +392,7 @@ var/list/ai_verbs_default = list( // hack to display shuttle timer if(emergency_shuttle.online()) - var/obj/machinery/computer/communications/C = locate() in machines - if(C) - C.post_status("shuttle") + post_status(src, "shuttle", user = src) /mob/living/silicon/ai/proc/ai_recall_shuttle() set category = "AI Commands" diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm index 3a995ba19d..cf135f9d55 100644 --- a/code/modules/mob/living/silicon/pai/pai.dm +++ b/code/modules/mob/living/silicon/pai/pai.dm @@ -122,7 +122,10 @@ pda.ownjob = "Personal Assistant" pda.owner = text("[]", src) pda.name = pda.owner + " (" + pda.ownjob + ")" - pda.toff = 1 + + var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger) + if(M) + M.toff = TRUE ..() /mob/living/silicon/pai/Login() @@ -210,7 +213,7 @@ medicalActive1 = null medicalActive2 = null medical_cannotfind = 0 - SSnanoui.update_uis(src) + SStgui.update_uis(src) to_chat(usr, "You reset your record-viewing software.") /mob/living/silicon/pai/cancel_camera() diff --git a/code/modules/mob/living/silicon/pai/software.dm b/code/modules/mob/living/silicon/pai/software.dm index bbb038dead..cad5229097 100644 --- a/code/modules/mob/living/silicon/pai/software.dm +++ b/code/modules/mob/living/silicon/pai/software.dm @@ -41,27 +41,24 @@ var/global/list/default_pai_software = list() set category = "pAI Commands" set name = "Software Interface" - ui_interact(src) + tgui_interact(src) -/mob/living/silicon/pai/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1) - if(user != src) - if(ui) ui.set_status(STATUS_CLOSE, 0) - return +/mob/living/silicon/pai/tgui_state(mob/user) + return GLOB.tgui_self_state - if(ui_key != "main") - var/datum/pai_software/S = software[ui_key] - if(S && !S.toggle) - S.on_ui_interact(src, ui, force_open) - else - if(ui) ui.set_status(STATUS_CLOSE, 0) - return - - var/data[0] +/mob/living/silicon/pai/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIInterface", "pAI Software Interface") + ui.open() +/mob/living/silicon/pai/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + // Software we have bought - var/bought_software[0] + var/list/bought_software = list() // Software we have not bought - var/not_bought_software[0] + var/list/not_bought_software = list() for(var/key in pai_software_by_key) var/datum/pai_software/S = pai_software_by_key[key] @@ -70,62 +67,52 @@ var/global/list/default_pai_software = list() software_data["id"] = S.id if(key in software) software_data["on"] = S.is_active(src) - bought_software[++bought_software.len] = software_data + bought_software.Add(list(software_data)) else software_data["ram"] = S.ram_cost - not_bought_software[++not_bought_software.len] = software_data + not_bought_software.Add(list(software_data)) data["bought"] = bought_software data["not_bought"] = not_bought_software data["available_ram"] = ram // Emotions - var/emotions[0] + var/list/emotions = list() for(var/name in pai_emotions) - var/emote[0] + var/list/emote = list() emote["name"] = name emote["id"] = pai_emotions[name] - emotions[++emotions.len] = emote + emotions.Add(list(emote)) data["emotions"] = emotions data["current_emotion"] = card.current_emotion - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "pai_interface.tmpl", "pAI Software Interface", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data -/mob/living/silicon/pai/Topic(href, href_list) - . = ..() - if(.) return +/mob/living/silicon/pai/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE - if(href_list["software"]) - var/soft = href_list["software"] - var/datum/pai_software/S = software[soft] - if(S.toggle) - S.toggle(src) - else - ui_interact(src, ui_key = soft) - return 1 + switch(action) + if("software") + var/soft = params["software"] + var/datum/pai_software/S = software[soft] + if(S.toggle) + S.toggle(src) + else + S.tgui_interact(src, parent_ui = ui) + return TRUE - else if(href_list["stopic"]) - var/soft = href_list["stopic"] - var/datum/pai_software/S = software[soft] - if(S) - return S.Topic(href, href_list) + if("purchase") + var/soft = params["purchase"] + var/datum/pai_software/S = pai_software_by_key[soft] + if(S && (ram >= S.ram_cost)) + ram -= S.ram_cost + software[S.id] = S + return TRUE - else if(href_list["purchase"]) - var/soft = href_list["purchase"] - var/datum/pai_software/S = pai_software_by_key[soft] - if(S && (ram >= S.ram_cost)) - ram -= S.ram_cost - software[S.id] = S - return 1 - - else if(href_list["image"]) - var/img = text2num(href_list["image"]) - if(1 <= img && img <= (pai_emotions.len)) - card.setEmotion(img) - return 1 + if("image") + var/img = text2num(params["image"]) + if(1 <= img && img <= (pai_emotions.len)) + card.setEmotion(img) + return TRUE diff --git a/code/modules/mob/living/silicon/pai/software_modules.dm b/code/modules/mob/living/silicon/pai/software_modules.dm index 8f7e8462de..e518e74a4a 100644 --- a/code/modules/mob/living/silicon/pai/software_modules.dm +++ b/code/modules/mob/living/silicon/pai/software_modules.dm @@ -12,14 +12,19 @@ // Whether pAIs should automatically receive this module at no cost var/default = 0 - proc/on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - return +/datum/pai_software/proc/toggle(mob/living/silicon/pai/user) + return - proc/toggle(mob/living/silicon/pai/user) - return +/datum/pai_software/proc/is_active(mob/living/silicon/pai/user) + return 0 - proc/is_active(mob/living/silicon/pai/user) - return 0 +/datum/pai_software/tgui_state(mob/user) + return GLOB.tgui_always_state + +/datum/pai_software/tgui_status(mob/user) + if(!istype(user, /mob/living/silicon/pai)) + return STATUS_CLOSE + return ..() /datum/pai_software/directives name = "Directives" @@ -28,55 +33,58 @@ toggle = 0 default = 1 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/pai_software/directives/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIDirectives", name, parent_ui) + ui.open() - data["master"] = user.master - data["dna"] = user.master_dna - data["prime"] = user.pai_law0 - data["supplemental"] = user.pai_laws +/datum/pai_software/directives/tgui_data(mob/living/silicon/pai/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = list() - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_directives.tmpl", "pAI Directives", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + data["master"] = user.master + data["dna"] = user.master_dna + data["prime"] = user.pai_law0 + data["supplemental"] = user.pai_laws - Topic(href, href_list) - var/mob/living/silicon/pai/P = usr - if(!istype(P)) return + return data - if(href_list["getdna"]) - var/mob/living/M = P.loc - var/count = 0 +/datum/pai_software/directives/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + var/mob/living/silicon/pai/P = usr + if(!istype(P)) + return TRUE + if(..()) + return TRUE - // Find the carrier - while(!istype(M, /mob/living)) - if(!M || !M.loc || count > 6) - //For a runtime where M ends up in nullspace (similar to bluespace but less colourful) - to_chat(src, "You are not being carried by anyone!") - return 0 - M = M.loc - count++ + if(action == "getdna") + var/mob/living/M = P.loc - // Check the carrier - var/datum/gender/TM = gender_datums[M.get_visible_gender()] - var/answer = input(M, "[P] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "[P] Check DNA", "No") in list("Yes", "No") - if(answer == "Yes") - var/turf/T = get_turf_or_move(P.loc) - for (var/mob/v in viewers(T)) - v.show_message("[M] presses [TM.his] thumb against [P].", 3, "[P] makes a sharp clicking sound as it extracts DNA material from [M].", 2) - var/datum/dna/dna = M.dna - to_chat(P, "

[M]'s UE string : [dna.unique_enzymes]

") - if(dna.unique_enzymes == P.master_dna) - to_chat(P, "DNA is a match to stored Master DNA.") - else - to_chat(P, "DNA does not match stored Master DNA.") + var/count = 0 + // Find the carrier + while(!istype(M, /mob/living)) + if(!M || !M.loc || count > 6) + //For a runtime where M ends up in nullspace (similar to bluespace but less colourful) + to_chat(src, "You are not being carried by anyone!") + return 0 + M = M.loc + count++ + + // Check the carrier + var/datum/gender/TM = gender_datums[M.get_visible_gender()] + var/answer = input(M, "[P] is requesting a DNA sample from you. Will you allow it to confirm your identity?", "[P] Check DNA", "No") in list("Yes", "No") + if(answer == "Yes") + var/turf/T = get_turf(P.loc) + for (var/mob/v in viewers(T)) + v.show_message("[M] presses [TM.his] thumb against [P].", 3, "[P] makes a sharp clicking sound as it extracts DNA material from [M].", 2) + var/datum/dna/dna = M.dna + to_chat(P, "

[M]'s UE string : [dna.unique_enzymes]

") + if(dna.unique_enzymes == P.master_dna) + to_chat(P, "DNA is a match to stored Master DNA.") else - to_chat(P, "[M] does not seem like [TM.he] is going to provide a DNA sample willingly.") - return 1 + to_chat(P, "DNA does not match stored Master DNA.") + else + to_chat(P, "[M] does not seem like [TM.he] is going to provide a DNA sample willingly.") + return TRUE /datum/pai_software/radio_config name = "Radio Configuration" @@ -85,35 +93,8 @@ toggle = 0 default = 1 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui = null, force_open = 1) - var/data[0] - - data["listening"] = user.radio.broadcasting - data["frequency"] = format_frequency(user.radio.frequency) - - var/channels[0] - for(var/ch_name in user.radio.channels) - var/ch_stat = user.radio.channels[ch_name] - var/ch_dat[0] - ch_dat["name"] = ch_name - // FREQ_LISTENING is const in /obj/item/device/radio - ch_dat["listening"] = !!(ch_stat & user.radio.FREQ_LISTENING) - channels[++channels.len] = ch_dat - - data["channels"] = channels - - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - ui = new(user, user, id, "pai_radio.tmpl", "Radio Configuration", 300, 150) - ui.set_initial_data(data) - ui.open() - - Topic(href, href_list) - var/mob/living/silicon/pai/P = usr - if(!istype(P)) return - - P.radio.Topic(href, href_list) - return 1 +/datum/pai_software/radio_config/tgui_interact(mob/living/silicon/pai/user, datum/tgui/ui, datum/tgui/parent_ui) + return user.radio.tgui_interact(user, parent_ui = parent_ui) /datum/pai_software/crew_manifest name = "Crew Manifest" @@ -121,20 +102,18 @@ id = "manifest" toggle = 0 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) +/datum/pai_software/crew_manifest/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "CrewManifest", name, parent_ui) + ui.open() + +/datum/pai_software/crew_manifest/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + if(data_core) data_core.get_manifest_list() - - var/data[0] - // This is dumb, but NanoUI breaks if it has no data to send - data["manifest"] = PDA_Manifest - - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "crew_manifest.tmpl", "Crew Manifest", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + data["manifest"] = PDA_Manifest + return data /datum/pai_software/messenger name = "Digital Messenger" @@ -142,75 +121,8 @@ id = "messenger" toggle = 0 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - var/data[0] - - data["receiver_off"] = user.pda.toff - data["ringer_off"] = user.pda.message_silent - data["current_ref"] = null - data["current_name"] = user.current_pda_messaging - - var/pdas[0] - if(!user.pda.toff) - for(var/obj/item/device/pda/P in sortAtom(PDAs)) - if(!P.owner || P.toff || P == user.pda || P.hidden) continue - var/pda[0] - pda["name"] = "[P]" - pda["owner"] = "[P.owner]" - pda["ref"] = "\ref[P]" - if(P.owner == user.current_pda_messaging) - data["current_ref"] = "\ref[P]" - pdas[++pdas.len] = pda - - data["pdas"] = pdas - - var/messages[0] - if(user.current_pda_messaging) - for(var/index in user.pda.tnote) - if(index["owner"] != user.current_pda_messaging) - continue - var/msg[0] - var/sent = index["sent"] - msg["sent"] = sent ? 1 : 0 - msg["target"] = index["owner"] - msg["message"] = index["message"] - messages[++messages.len] = msg - - data["messages"] = messages - - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_messenger.tmpl", "Digital Messenger", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - - Topic(href, href_list) - var/mob/living/silicon/pai/P = usr - if(!istype(P)) return - - if(!isnull(P.pda)) - if(href_list["toggler"]) - P.pda.toff = href_list["toggler"] != "1" - return 1 - else if(href_list["ringer"]) - P.pda.message_silent = href_list["ringer"] != "1" - return 1 - else if(href_list["select"]) - var/s = href_list["select"] - if(s == "*NONE*") - P.current_pda_messaging = null - else - P.current_pda_messaging = s - return 1 - else if(href_list["target"]) - if(P.silence_time) - return alert("Communications circuits remain uninitialized.") - - var/target = locate(href_list["target"]) - P.pda.create_message(P, target, 1) - return 1 +/datum/pai_software/messenger/tgui_interact(mob/living/silicon/pai/user, datum/tgui/ui, datum/tgui/parent_ui) + return user.pda.tgui_interact(user, parent_ui = parent_ui) /datum/pai_software/med_records name = "Medical Records" @@ -218,53 +130,55 @@ id = "med_records" toggle = 0 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/pai_software/med_records/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIMedrecords", name, parent_ui) + ui.open() - var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) - var/record[0] - record["name"] = general.fields["name"] - record["ref"] = "\ref[general]" - records[++records.len] = record +/datum/pai_software/med_records/tgui_data(mob/living/silicon/pai/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/list/records = list() + for(var/datum/data/record/general in sortRecord(data_core.general)) + var/list/record = list() + record["name"] = general.fields["name"] + record["ref"] = "\ref[general]" + records.Add(list(record)) - data["records"] = records + data["records"] = records - var/datum/data/record/G = user.medicalActive1 - var/datum/data/record/M = user.medicalActive2 - data["general"] = G ? G.fields : null - data["medical"] = M ? M.fields : null - data["could_not_find"] = user.medical_cannotfind + var/datum/data/record/G = user.medicalActive1 + var/datum/data/record/M = user.medicalActive2 + data["general"] = G ? G.fields : null + data["medical"] = M ? M.fields : null + data["could_not_find"] = user.medical_cannotfind - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_medrecords.tmpl", "Medical Records", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data - Topic(href, href_list) - var/mob/living/silicon/pai/P = usr - if(!istype(P)) return +/datum/pai_software/med_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + var/mob/living/silicon/pai/P = usr + if(!istype(P)) + return - if(href_list["select"]) - var/datum/data/record/record = locate(href_list["select"]) - if(record) - var/datum/data/record/R = record - var/datum/data/record/M = null - if (!( data_core.general.Find(R) )) - P.medical_cannotfind = 1 - else - P.medical_cannotfind = 0 - for(var/datum/data/record/E in data_core.medical) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - M = E - P.medicalActive1 = R - P.medicalActive2 = M - else + if(action == "select") + var/datum/data/record/record = locate(params["select"]) + if(record) + var/datum/data/record/R = record + var/datum/data/record/M = null + if (!( data_core.general.Find(R) )) P.medical_cannotfind = 1 - return 1 + else + P.medical_cannotfind = 0 + for(var/datum/data/record/E in data_core.medical) + if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + M = E + P.medicalActive1 = R + P.medicalActive2 = M + else + P.medical_cannotfind = 1 + return 1 /datum/pai_software/sec_records name = "Security Records" @@ -272,57 +186,59 @@ id = "sec_records" toggle = 0 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/pai_software/sec_records/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAISecrecords", name, parent_ui) + ui.open() - var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) - var/record[0] - record["name"] = general.fields["name"] - record["ref"] = "\ref[general]" - records[++records.len] = record +/datum/pai_software/sec_records/tgui_data(mob/living/silicon/pai/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/list/records = list() + for(var/datum/data/record/general in sortRecord(data_core.general)) + var/list/record = list() + record["name"] = general.fields["name"] + record["ref"] = "\ref[general]" + records.Add(list(record)) - data["records"] = records + data["records"] = records - var/datum/data/record/G = user.securityActive1 - var/datum/data/record/S = user.securityActive2 - data["general"] = G ? G.fields : null - data["security"] = S ? S.fields : null - data["could_not_find"] = user.security_cannotfind + var/datum/data/record/G = user.securityActive1 + var/datum/data/record/S = user.securityActive2 + data["general"] = G ? G.fields : null + data["security"] = S ? S.fields : null + data["could_not_find"] = user.security_cannotfind - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_secrecords.tmpl", "Security Records", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data - Topic(href, href_list) - var/mob/living/silicon/pai/P = usr - if(!istype(P)) return +/datum/pai_software/sec_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + var/mob/living/silicon/pai/P = usr + if(!istype(P)) + return - if(href_list["select"]) - var/datum/data/record/record = locate(href_list["select"]) - if(record) - var/datum/data/record/R = record - var/datum/data/record/S = null - if (!( data_core.general.Find(R) )) - P.securityActive1 = null - P.securityActive2 = null - P.security_cannotfind = 1 - else - P.security_cannotfind = 0 - for(var/datum/data/record/E in data_core.security) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - S = E - P.securityActive1 = R - P.securityActive2 = S - else + if(action == "select") + var/datum/data/record/record = locate(params["select"]) + if(record) + var/datum/data/record/R = record + var/datum/data/record/S = null + if (!( data_core.general.Find(R) )) P.securityActive1 = null P.securityActive2 = null P.security_cannotfind = 1 - return 1 + else + P.security_cannotfind = 0 + for(var/datum/data/record/E in data_core.security) + if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + S = E + P.securityActive1 = R + P.securityActive2 = S + else + P.securityActive1 = null + P.securityActive2 = null + P.security_cannotfind = 1 + return TRUE /datum/pai_software/door_jack name = "Door Jack" @@ -330,47 +246,49 @@ id = "door_jack" toggle = 0 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/pai_software/door_jack/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIDoorjack", "Door Jack", parent_ui) + ui.open() - data["cable"] = user.cable != null - data["machine"] = user.cable && (user.cable.machine != null) - data["inprogress"] = user.hackdoor != null - data["progress_a"] = round(user.hackprogress / 10) - data["progress_b"] = user.hackprogress % 10 - data["aborted"] = user.hack_aborted +/datum/pai_software/door_jack/tgui_data(mob/living/silicon/pai/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_doorjack.tmpl", "Door Jack", 300, 150) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + data["cable"] = user.cable != null + data["machine"] = user.cable && (user.cable.machine != null) + data["inprogress"] = user.hackdoor != null + data["progress_a"] = round(user.hackprogress / 10) + data["progress_b"] = user.hackprogress % 10 + data["aborted"] = user.hack_aborted - Topic(href, href_list) - var/mob/living/silicon/pai/P = usr - if(!istype(P)) return + return data - if(href_list["jack"]) +/datum/pai_software/door_jack/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + var/mob/living/silicon/pai/P = usr + if(!istype(P) || ..()) + return TRUE + + switch(action) + if("jack") if(P.cable && P.cable.machine) P.hackdoor = P.cable.machine P.hackloop() return 1 - else if(href_list["cancel"]) + if("cancel") P.hackdoor = null return 1 - else if(href_list["cable"]) - var/turf/T = get_turf_or_move(P.loc) + if("cable") + var/turf/T = get_turf(P) P.hack_aborted = 0 P.cable = new /obj/item/weapon/pai_cable(T) for(var/mob/M in viewers(T)) M.show_message("A port on [P] opens to reveal [P.cable], which promptly falls to the floor.", 3, - "You hear the soft click of something light and hard falling to the ground.", 2) + "You hear the soft click of something light and hard falling to the ground.", 2) return 1 /mob/living/silicon/pai/proc/hackloop() - var/turf/T = get_turf_or_move(src.loc) + var/turf/T = get_turf(src) for(var/mob/living/silicon/ai/AI in player_list) if(T.loc) to_chat(AI, "Network Alert: Brute-force encryption crack in progress in [T.loc].") @@ -404,134 +322,155 @@ id = "atmos_sense" toggle = 0 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/pai_software/atmosphere_sensor/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIAtmos", name, parent_ui) + ui.open() - var/turf/T = get_turf_or_move(user.loc) - if(!T) - data["reading"] = 0 - data["pressure"] = 0 - data["temperature"] = 0 - data["temperatureC"] = 0 - data["gas"] = list() - else - var/datum/gas_mixture/env = T.return_air() - data["reading"] = 1 - var/pres = env.return_pressure() * 10 - data["pressure"] = "[round(pres/10)].[pres%10]" - data["temperature"] = round(env.temperature) - data["temperatureC"] = round(env.temperature-T0C) +/datum/pai_software/atmosphere_sensor/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() - var/t_moles = env.total_moles - var/gases[0] - for(var/g in env.gas) - var/gas[0] - gas["name"] = gas_data.name[g] - gas["percent"] = round((env.gas[g] / t_moles) * 100) - gases[++gases.len] = gas - data["gas"] = gases + var/list/results = list() + var/turf/T = get_turf(user) + if(!isnull(T)) + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles + if (total_moles) + var/o2_level = environment.gas["oxygen"]/total_moles + var/n2_level = environment.gas["nitrogen"]/total_moles + var/co2_level = environment.gas["carbon_dioxide"]/total_moles + var/phoron_level = environment.gas["phoron"]/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_atmosphere.tmpl", "Atmosphere Sensor", 350, 300) - ui.set_initial_data(data) - ui.open() + // entry is what the element is describing + // Type identifies which unit or other special characters to use + // Val is the information reported + // Bad_high/_low are the values outside of which the entry reports as dangerous + // Poor_high/_low are the values outside of which the entry reports as unideal + // Values were extracted from the template itself + results = list( + list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), + list("entry" = "Temperature", "units" = "°C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), + list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), + list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) + ) + + if(isnull(results)) + results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) + + data["aircontents"] = results + + return data /datum/pai_software/sec_hud name = "Security HUD" ram_cost = 20 id = "sec_hud" - toggle(mob/living/silicon/pai/user) - user.secHUD = !user.secHUD - user.plane_holder.set_vis(VIS_CH_ID, user.secHUD) - user.plane_holder.set_vis(VIS_CH_WANTED, user.secHUD) - user.plane_holder.set_vis(VIS_CH_IMPTRACK, user.secHUD) - user.plane_holder.set_vis(VIS_CH_IMPLOYAL, user.secHUD) - user.plane_holder.set_vis(VIS_CH_IMPCHEM, user.secHUD) +/datum/pai_software/sec_hud/toggle(mob/living/silicon/pai/user) + user.secHUD = !user.secHUD + user.plane_holder.set_vis(VIS_CH_ID, user.secHUD) + user.plane_holder.set_vis(VIS_CH_WANTED, user.secHUD) + user.plane_holder.set_vis(VIS_CH_IMPTRACK, user.secHUD) + user.plane_holder.set_vis(VIS_CH_IMPLOYAL, user.secHUD) + user.plane_holder.set_vis(VIS_CH_IMPCHEM, user.secHUD) - is_active(mob/living/silicon/pai/user) - return user.secHUD +/datum/pai_software/sec_hud/is_active(mob/living/silicon/pai/user) + return user.secHUD /datum/pai_software/med_hud name = "Medical HUD" ram_cost = 20 id = "med_hud" - toggle(mob/living/silicon/pai/user) - user.medHUD = !user.medHUD - user.plane_holder.set_vis(VIS_CH_STATUS, user.medHUD) - user.plane_holder.set_vis(VIS_CH_HEALTH, user.medHUD) +/datum/pai_software/med_hud/toggle(mob/living/silicon/pai/user) + user.medHUD = !user.medHUD + user.plane_holder.set_vis(VIS_CH_STATUS, user.medHUD) + user.plane_holder.set_vis(VIS_CH_HEALTH, user.medHUD) - is_active(mob/living/silicon/pai/user) - return user.medHUD +/datum/pai_software/med_hud/is_active(mob/living/silicon/pai/user) + return user.medHUD /datum/pai_software/translator name = "Universal Translator" ram_cost = 35 id = "translator" - toggle(mob/living/silicon/pai/user) - // Sol Common, Tradeband, Terminus and Gutter are added with New() and are therefore the current default, always active languages - user.translator_on = !user.translator_on - if(user.translator_on) - user.add_language(LANGUAGE_UNATHI) - user.add_language(LANGUAGE_SIIK) - user.add_language(LANGUAGE_AKHANI) - user.add_language(LANGUAGE_SKRELLIAN) - user.add_language(LANGUAGE_ZADDAT) - user.add_language(LANGUAGE_SCHECHI) - else - user.remove_language(LANGUAGE_UNATHI) - user.remove_language(LANGUAGE_SIIK) - user.remove_language(LANGUAGE_AKHANI) - user.remove_language(LANGUAGE_SKRELLIAN) - user.remove_language(LANGUAGE_ZADDAT) - user.remove_language(LANGUAGE_SCHECHI) +/datum/pai_software/translator/toggle(mob/living/silicon/pai/user) + // Sol Common, Tradeband, Terminus and Gutter are added with New() and are therefore the current default, always active languages + user.translator_on = !user.translator_on + if(user.translator_on) + user.add_language(LANGUAGE_UNATHI) + user.add_language(LANGUAGE_SIIK) + user.add_language(LANGUAGE_AKHANI) + user.add_language(LANGUAGE_SKRELLIAN) + user.add_language(LANGUAGE_ZADDAT) + user.add_language(LANGUAGE_SCHECHI) + else + user.remove_language(LANGUAGE_UNATHI) + user.remove_language(LANGUAGE_SIIK) + user.remove_language(LANGUAGE_AKHANI) + user.remove_language(LANGUAGE_SKRELLIAN) + user.remove_language(LANGUAGE_ZADDAT) + user.remove_language(LANGUAGE_SCHECHI) - is_active(mob/living/silicon/pai/user) - return user.translator_on +/datum/pai_software/translator/is_active(mob/living/silicon/pai/user) + return user.translator_on /datum/pai_software/signaller - name = "Remote Signaller" + name = "Remote Signaler" ram_cost = 5 id = "signaller" toggle = 0 - on_ui_interact(mob/living/silicon/pai/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/pai_software/signaller/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Signaler", "Signaler", parent_ui) + ui.open() - data["frequency"] = format_frequency(user.sradio.frequency) - data["code"] = user.sradio.code +/datum/pai_software/signaller/tgui_data(mob/living/silicon/pai/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/obj/item/radio/integrated/signal/R = user.sradio - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_signaller.tmpl", "Signaller", 320, 150) - ui.set_initial_data(data) - ui.open() + data["frequency"] = R.frequency + data["minFrequency"] = RADIO_LOW_FREQ + data["maxFrequency"] = RADIO_HIGH_FREQ + data["code"] = R.code - Topic(href, href_list) - var/mob/living/silicon/pai/P = usr - if(!istype(P)) return + return data - if(href_list["send"]) - P.sradio.send_signal("ACTIVATE") - for(var/mob/O in hearers(1, P.loc)) - O.show_message("[bicon(P)] *beep* *beep*", 3, "*beep* *beep*", 2) - return 1 +/datum/pai_software/signaller/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE - else if(href_list["freq"]) - var/new_frequency = (P.sradio.frequency + text2num(href_list["freq"])) - if(new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ) - new_frequency = sanitize_frequency(new_frequency) - P.sradio.set_frequency(new_frequency) - return 1 + var/mob/living/silicon/pai/user = usr + if(istype(user)) + var/obj/item/radio/integrated/signal/R = user.sradio - else if(href_list["code"]) - P.sradio.code += text2num(href_list["code"]) - P.sradio.code = round(P.sradio.code) - P.sradio.code = min(100, P.sradio.code) - P.sradio.code = max(1, P.sradio.code) - return 1 + switch(action) + if("signal") + spawn(0) + R.send_signal("ACTIVATE") + for(var/mob/O in hearers(1, R.loc)) + O.show_message("[bicon(R)] *beep* *beep*", 3, "*beep* *beep*", 2) + if("freq") + var/frequency = unformat_frequency(params["freq"]) + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + R.set_frequency(frequency) + . = TRUE + if("code") + R.code = clamp(round(text2num(params["code"])), 1, 100) + . = TRUE + if("reset") + if(params["reset"] == "freq") + R.set_frequency(initial(R.frequency)) + else + R.code = initial(R.code) + . = TRUE diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index ad1e767aca..7d67ca3cc3 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -349,12 +349,6 @@ updatename() updateicon() -// this verb lets cyborgs see the stations manifest -/mob/living/silicon/robot/verb/cmd_station_manifest() - set category = "Robot Commands" - set name = "Show Crew Manifest" - show_station_manifest() - /mob/living/silicon/robot/proc/self_diagnosis() if(!is_component_functioning("diagnosis unit")) return null diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm index e6dface45a..ef5336e5b9 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm @@ -149,7 +149,8 @@ pto_type = PTO_SCIENCE vr_sprites = list( "Acheron" = "mechoid-Science", - "ZOOM-BA" = "zoomba-research" + "ZOOM-BA" = "zoomba-research", + "XI-GUS" = "spiderscience" ) /obj/item/weapon/robot_module/robot/security/combat diff --git a/code/modules/mob/living/silicon/robot/robot_vr.dm b/code/modules/mob/living/silicon/robot/robot_vr.dm index 840026946f..21011e84c7 100644 --- a/code/modules/mob/living/silicon/robot/robot_vr.dm +++ b/code/modules/mob/living/silicon/robot/robot_vr.dm @@ -53,7 +53,8 @@ "zoomba-service", "zoomba-combat", "zoomba-combat-roll", - "zoomba-combat-shield" + "zoomba-combat-shield", + "spiderscience" ) //List of all used sprites that are in robots_vr.dmi diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index b6cf748fb8..b9c2c60c47 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -153,18 +153,6 @@ show_malf_ai() ..() -// this function displays the stations manifest in a separate window -/mob/living/silicon/proc/show_station_manifest() - var/dat = "
" - if(!data_core) - to_chat(src, "There is no data to form a manifest with. Contact your Nanotrasen administrator.") - return - dat += data_core.get_manifest(1) //The 1 makes it monochrome. - - var/datum/browser/popup = new(src, "Crew Manifest", "Crew Manifest", 370, 420, src) - popup.set_content(dat) - popup.open() - //can't inject synths /mob/living/silicon/can_inject(var/mob/user, var/error_msg) if(error_msg) diff --git a/code/modules/mob/living/silicon/subystems.dm b/code/modules/mob/living/silicon/subystems.dm index be6a68591d..890f1a8b37 100644 --- a/code/modules/mob/living/silicon/subystems.dm +++ b/code/modules/mob/living/silicon/subystems.dm @@ -2,6 +2,7 @@ var/register_alarms = 1 var/datum/tgui_module/alarm_monitor/all/robot/alarm_monitor var/datum/tgui_module/atmos_control/robot/atmos_control + var/datum/tgui_module/crew_manifest/robot/crew_manifest var/datum/tgui_module/crew_monitor/robot/crew_monitor var/datum/tgui_module/law_manager/robot/law_manager var/datum/tgui_module/power_monitor/robot/power_monitor @@ -10,6 +11,7 @@ /mob/living/silicon var/list/silicon_subsystems = list( /mob/living/silicon/proc/subsystem_alarm_monitor, + /mob/living/silicon/proc/subsystem_crew_manifest, /mob/living/silicon/proc/subsystem_law_manager ) @@ -17,6 +19,7 @@ silicon_subsystems = list( /mob/living/silicon/proc/subsystem_alarm_monitor, /mob/living/silicon/proc/subsystem_atmos_control, + /mob/living/silicon/proc/subsystem_crew_manifest, /mob/living/silicon/proc/subsystem_crew_monitor, /mob/living/silicon/proc/subsystem_law_manager, /mob/living/silicon/proc/subsystem_power_monitor, @@ -30,6 +33,7 @@ /mob/living/silicon/proc/init_subsystems() alarm_monitor = new(src) atmos_control = new(src) + crew_manifest = new(src) crew_monitor = new(src) law_manager = new(src) power_monitor = new(src) @@ -60,6 +64,15 @@ atmos_control.tgui_interact(usr) +/******************** +* Crew Manifest * +********************/ +/mob/living/silicon/proc/subsystem_crew_manifest() + set category = "Subystems" + set name = "Crew Manifest" + + crew_manifest.tgui_interact(usr) + /******************** * Crew Monitor * ********************/ diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm index 3a27d98d01..f3bf4543db 100644 --- a/code/modules/mob/living/simple_mob/simple_mob.dm +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -271,18 +271,22 @@ // Harvest an animal's delicious byproducts -/mob/living/simple_mob/proc/harvest(var/mob/user) +/mob/living/simple_mob/proc/harvest(var/mob/user, var/invisible) var/actual_meat_amount = max(1,(meat_amount/2)) + var/attacker_name = user.name + if(invisible) + attacker_name = "someone" + if(meat_type && actual_meat_amount>0 && (stat == DEAD)) for(var/i=0;i[user] chops up \the [src]!") + user.visible_message("[attacker_name] chops up \the [src]!") new/obj/effect/decal/cleanable/blood/splatter(get_turf(src)) qdel(src) else - user.visible_message("[user] butchers \the [src] messily!") + user.visible_message("[attacker_name] butchers \the [src] messily!") gib() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm index d33e93e042..76d7bb9353 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm @@ -25,7 +25,7 @@ GLOBAL_VAR_INIT(chicken_count, 0) // How mant chickens DO we have? say_list_type = /datum/say_list/chicken meat_amount = 2 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken var/eggsleft = 0 var/body_color @@ -125,7 +125,7 @@ GLOBAL_VAR_INIT(chicken_count, 0) // How mant chickens DO we have? say_list_type = /datum/say_list/chick meat_amount = 1 - meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat + meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat/chicken var/amount_grown = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm index 7c371e078d..d3cda1f22b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/space/alien.dm @@ -18,6 +18,7 @@ maxHealth = 100 health = 100 + see_in_dark = 7 harm_intent_damage = 5 melee_damage_lower = 25 @@ -122,4 +123,4 @@ /mob/living/simple_mob/animal/space/alien/death() ..() visible_message("[src] lets out a waning guttural screech, green blood bubbling from its maw...") - playsound(src, 'sound/voice/hiss6.ogg', 100, 1) \ No newline at end of file + playsound(src, 'sound/voice/hiss6.ogg', 100, 1) diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm index 3b39c4121e..4545057bc6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm +++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/viscerator.dm @@ -89,7 +89,7 @@ return L.assess_perp(src, FALSE, FALSE, TRUE, FALSE) <= 3 // Variant that has high armor pen. Slightly slower attack speed and movement. Meant to be dispersed in groups with other ones -/mob/living/simple_mob/mechanical/viscerator/piercing. +/mob/living/simple_mob/mechanical/viscerator/piercing attack_armor_pen = 20 base_attack_cooldown = 10 // One attack a second or so. movement_cooldown = 0.5 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm index c95dbded2c..87200c0caf 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/demon/demon_abilities.dm @@ -56,7 +56,7 @@ var/list/potentials = living_mobs(0) if(potentials.len) var/mob/living/target = pick(potentials) - if(istype(target) && vore_selected) + if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) target.forceMove(vore_selected) to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") @@ -203,7 +203,7 @@ var/list/potentials = living_mobs(0) if(potentials.len) var/mob/living/target = pick(potentials) - if(istype(target) && vore_selected) + if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) target.forceMove(vore_selected) to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm index f5004db3c1..7b2da46008 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/morph/morph.dm @@ -89,6 +89,10 @@ ..() /mob/living/simple_mob/vore/hostile/morph/proc/assume(atom/movable/target) + var/mob/living/carbon/human/humantarget = target + if(istype(humantarget) && humantarget.resleeve_lock && ckey != humantarget.resleeve_lock) + to_chat(src, "[target] cannot be impersonated!") + return if(morphed) to_chat(src, "You must restore to your original form first!") return diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm index 491d0011d2..f1fc9914c2 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/shadekin/ability_procs.dm @@ -44,7 +44,7 @@ var/list/potentials = living_mobs(0) if(potentials.len) var/mob/living/target = pick(potentials) - if(istype(target) && vore_selected) + if(istype(target) && target.devourable && target.can_be_drop_prey && vore_selected) target.forceMove(vore_selected) to_chat(target,"\The [src] phases in around you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index ffaeef3c09..d965956dfe 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -1,5 +1,4 @@ /mob/Logout() - SSnanoui.user_logout(src) // this is used to clean up (remove) this user's Nano UIs SStgui.on_logout(src) // Cleanup any TGUIs the user has open player_list -= src disconnect_time = world.realtime //VOREStation Addition: logging when we disappear. diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index bb61f345b6..2722a12f01 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -84,7 +84,7 @@ else exclude_mobs = list(src) src.show_message(self_message, 1, blind_message, 2) - . = ..() + . = ..(message, blind_message, exclude_mobs) // Returns an amount of power drawn from the object (-1 if it's not viable). // If drain_check is set it will not actually drain power, just return a value. diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 7be3255cbd..c5066adb7c 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -202,7 +202,7 @@ var/mob/teleop = null var/turf/listed_turf = null //the current turf being examined in the stat panel - var/list/shouldnt_see = list() //list of objects that this mob shouldn't see in the stat panel. this silliness is needed because of AI alt+click and cult blood runes + var/list/shouldnt_see = list(/mob/observer/eye) //list of objects that this mob shouldn't see in the stat panel. this silliness is needed because of AI alt+click and cult blood runes var/list/active_genes=list() var/mob_size = MOB_MEDIUM diff --git a/code/modules/mob/new_player/sprite_accessories_vr.dm b/code/modules/mob/new_player/sprite_accessories_vr.dm index e624351360..ad89abfd3b 100644 --- a/code/modules/mob/new_player/sprite_accessories_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_vr.dm @@ -240,13 +240,6 @@ icon_state = "hair_fingerwave" species_allowed = list(SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_TAJ, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_PROTEAN) - teshari_fluffymohawk - name = "Teshari Fluffy Mohawk" - icon = 'icons/mob/human_face_vr.dmi' - icon_add = 'icons/mob/human_face_vr_add.dmi' - icon_state = "teshari_fluffymohawk" - species_allowed = list(SPECIES_TESHARI) - //Teshari things teshari icon_add = 'icons/mob/human_face_vr_add.dmi' @@ -290,6 +283,22 @@ teshari_mushroom icon_add = 'icons/mob/human_face_vr_add.dmi' + teshari_twies + icon_add = 'icons/mob/human_face_vr_add.dmi' + + teshari_backstrafe + icon_add = 'icons/mob/human_face_vr_add.dmi' + + teshari_longway + icon_add = 'icons/mob/human_face_vr_add.dmi' + + teshari_tree + icon_add = 'icons/mob/human_face_vr_add.dmi' + + teshari_fluffymohawk + icon = 'icons/mob/human_face_vr.dmi' + icon_add = 'icons/mob/human_face_vr_add.dmi' + //Skrell 'hairstyles' - these were requested for a chimera and screw it, if one wants to eat seafood, go nuts skr_tentacle_veryshort name = "Skrell Very Short Tentacles" @@ -1166,3 +1175,8 @@ icon_state = "dnose" color_blend_mode = ICON_MULTIPLY body_parts = list(BP_HEAD) + + bee_stripes + name = "bee stripes" + icon_state = "beestripes" + body_parts = list(BP_TORSO,BP_GROIN) \ No newline at end of file diff --git a/code/modules/modular_computers/NTNet/NTNet.dm b/code/modules/modular_computers/NTNet/NTNet.dm index 8370683522..6479f92a21 100644 --- a/code/modules/modular_computers/NTNet/NTNet.dm +++ b/code/modules/modular_computers/NTNet/NTNet.dm @@ -181,5 +181,8 @@ var/global/datum/ntnet/ntnet_global = new() return 1 return 0 - +/datum/ntnet/proc/get_chat_channel_by_id(id) + for(var/datum/ntnet_conversation/chan in chat_channels) + if(chan.id == id) + return chan diff --git a/code/modules/modular_computers/computers/modular_computer/core.dm b/code/modules/modular_computers/computers/modular_computer/core.dm index 97b4cfe851..d4b311e8e2 100644 --- a/code/modules/modular_computers/computers/modular_computer/core.dm +++ b/code/modules/modular_computers/computers/modular_computer/core.dm @@ -42,6 +42,8 @@ return 1 /obj/item/modular_computer/Initialize() + if(!overlay_icon) + overlay_icon = icon START_PROCESSING(SSobj, src) install_default_hardware() if(hard_drive) @@ -72,20 +74,20 @@ overlays.Cut() if(bsod) - overlays.Add("bsod") + overlays += image(icon = overlay_icon, icon_state = "bsod") return if(!enabled) if(icon_state_screensaver) - overlays.Add(icon_state_screensaver) + overlays += image(icon = overlay_icon, icon_state = icon_state_screensaver) set_light(0) return set_light(light_strength) if(active_program) - overlays.Add(active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu) + overlays += image(icon = overlay_icon, icon_state = active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu) if(active_program.program_key_state) - overlays.Add(active_program.program_key_state) + overlays += image(icon = overlay_icon, icon_state = active_program.program_key_state) else - overlays.Add(icon_state_menu) + overlays += image(icon = overlay_icon, icon_state = icon_state_menu) /obj/item/modular_computer/proc/turn_on(var/mob/user) if(bsod) @@ -119,7 +121,7 @@ active_program = null var/mob/user = usr if(user && istype(user)) - ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + tgui_interact(user) // Re-open the UI on this computer. It should show the main screen now. update_icon() // Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on) @@ -154,7 +156,7 @@ run_program(autorun.stored_data) if(user) - ui_interact(user) + tgui_interact(user) /obj/item/modular_computer/proc/minimize_program(mob/user) if(!active_program || !processor_unit) @@ -162,12 +164,11 @@ idle_threads.Add(active_program) active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs - SSnanoui.close_uis(active_program.NM ? active_program.NM : active_program) SStgui.close_uis(active_program.TM ? active_program.TM : active_program) active_program = null update_icon() if(istype(user)) - ui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + tgui_interact(user) // Re-open the UI on this computer. It should show the main screen now. /obj/item/modular_computer/proc/run_program(prog) @@ -207,12 +208,12 @@ return 1 /obj/item/modular_computer/proc/update_uis() - if(active_program) //Should we update program ui or computer ui? - SSnanoui.update_uis(active_program) - if(active_program.NM) - SSnanoui.update_uis(active_program.NM) + if(active_program) + SStgui.update_uis(active_program) + if(active_program.TM) + SStgui.update_uis(active_program.TM) else - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/item/modular_computer/proc/check_update_ui_need() var/ui_update_needed = 0 diff --git a/code/modules/modular_computers/computers/modular_computer/interaction.dm b/code/modules/modular_computers/computers/modular_computer/interaction.dm index c9ee11a77e..24c02c1add 100644 --- a/code/modules/modular_computers/computers/modular_computer/interaction.dm +++ b/code/modules/modular_computers/computers/modular_computer/interaction.dm @@ -99,7 +99,7 @@ /obj/item/modular_computer/attack_ghost(var/mob/observer/ghost/user) if(enabled) - ui_interact(user) + tgui_interact(user) else if(check_rights(R_ADMIN|R_EVENT, 0, user)) var/response = alert(user, "This computer is turned off. Would you like to turn it on?", "Admin Override", "Yes", "No") if(response == "Yes") @@ -116,7 +116,7 @@ // On-click handling. Turns on the computer if it's off and opens the GUI. /obj/item/modular_computer/attack_self(var/mob/user) if(enabled && screen_on) - ui_interact(user) + tgui_interact(user) else if(!enabled && screen_on) turn_on(user) diff --git a/code/modules/modular_computers/computers/modular_computer/ui.dm b/code/modules/modular_computers/computers/modular_computer/ui.dm index 81081c0cb1..5166d0d777 100644 --- a/code/modules/modular_computers/computers/modular_computer/ui.dm +++ b/code/modules/modular_computers/computers/modular_computer/ui.dm @@ -1,5 +1,10 @@ -// Operates NanoUI -/obj/item/modular_computer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +// Operates TGUI +/obj/item/modular_computer/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/headers) + ) + +/obj/item/modular_computer/tgui_interact(mob/user, datum/tgui/ui) if(!screen_on || !enabled) if(ui) ui.close() @@ -13,7 +18,7 @@ if(active_program) if(ui) // This is the main laptop screen. Since we are switching to program's UI close it for now. ui.close() - active_program.ui_interact(user) + active_program.tgui_interact(user) return // We are still here, that means there is no program loaded. Load the BIOS/ROM/OS/whatever you want to call it. @@ -22,80 +27,100 @@ visible_message("\The [src] beeps three times, it's screen displaying \"DISK ERROR\" warning.") return // No HDD, No HDD files list or no stored files. Something is very broken. - var/datum/computer_file/data/autorun = hard_drive.find_file_by_name("autorun") - - var/list/data = get_header_data() - - var/list/programs = list() - for(var/datum/computer_file/program/P in hard_drive.stored_files) - var/list/program = list() - program["name"] = P.filename - program["desc"] = P.filedesc - program["icon"] = P.program_menu_icon - program["autorun"] = (istype(autorun) && (autorun.stored_data == P.filename)) ? 1 : 0 - if(P in idle_threads) - program["running"] = 1 - programs.Add(list(program)) - - data["programs"] = programs - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "laptop_mainscreen.tmpl", "NTOS Main Menu", 400, 500) - ui.auto_update_layout = 1 - ui.set_initial_data(data) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "NtosMain") + ui.set_autoupdate(TRUE) ui.open() - ui.set_auto_update(1) + +/obj/item/modular_computer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = get_header_data() + data["device_theme"] = device_theme + + data["login"] = list() + var/obj/item/weapon/computer_hardware/card_slot/cardholder = card_slot + if(cardholder) + var/obj/item/weapon/card/id/stored_card = cardholder.stored_card + if(stored_card) + var/stored_name = stored_card.registered_name + var/stored_title = stored_card.assignment + if(!stored_name) + stored_name = "Unknown" + if(!stored_title) + stored_title = "Unknown" + data["login"] = list( + IDName = stored_name, + IDJob = stored_title, + ) + + data["removable_media"] = list() + + var/datum/computer_file/data/autorun = hard_drive.find_file_by_name("autorun") + data["programs"] = list() + for(var/datum/computer_file/program/P in hard_drive.stored_files) + var/running = FALSE + if(P in idle_threads) + running = TRUE + + data["programs"] += list(list( + "name" = P.filename, + "desc" = P.filedesc, + "icon" = P.program_menu_icon, + "running" = running, + "autorun" = (istype(autorun) && (autorun.stored_data == P.filename)) ? 1 : 0 + )) + + data["has_light"] = FALSE // has_light + data["light_on"] = FALSE // light_on + data["comp_light_color"] = null // comp_light_color + + return data // Handles user's GUI input -/obj/item/modular_computer/Topic(href, href_list) +/obj/item/modular_computer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 - if( href_list["PC_exit"] ) - kill_program() - return 1 - if( href_list["PC_enable_component"] ) - var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(href_list["PC_enable_component"]) - if(H && istype(H) && !H.enabled) - H.enabled = 1 - . = 1 - if( href_list["PC_disable_component"] ) - var/obj/item/weapon/computer_hardware/H = find_hardware_by_name(href_list["PC_disable_component"]) - if(H && istype(H) && H.enabled) - H.enabled = 0 - . = 1 - if( href_list["PC_shutdown"] ) - shutdown_computer() - return 1 - if( href_list["PC_minimize"] ) - var/mob/user = usr - minimize_program(user) + return TRUE + + switch(action) + if("PC_exit") + kill_program() + return TRUE + if("PC_shutdown") + shutdown_computer() + return TRUE + if("PC_minimize") + var/mob/user = usr + minimize_program(user) + if("PC_killprogram") + var/prog = params["name"] + var/datum/computer_file/program/P = null + var/mob/user = usr + if(hard_drive) + P = hard_drive.find_file_by_name(prog) - if( href_list["PC_killprogram"] ) - var/prog = href_list["PC_killprogram"] - var/datum/computer_file/program/P = null - var/mob/user = usr - if(hard_drive) - P = hard_drive.find_file_by_name(prog) + if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED) + return - if(!istype(P) || P.program_state == PROGRAM_STATE_KILLED) + P.kill_program(1) + to_chat(user, "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.") + return TRUE + if("PC_runprogram") + return run_program(params["name"]) + if("PC_setautorun") + if(!hard_drive) + return + set_autorun(params["name"]) + return TRUE + if("PC_Eject_Disk") + var/param = params["name"] + switch(param) + if("ID") + proc_eject_id(usr) + return TRUE + else return - P.kill_program(1) - update_uis() - to_chat(user, "Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.") - - if( href_list["PC_runprogram"] ) - return run_program(href_list["PC_runprogram"]) - - if( href_list["PC_setautorun"] ) - if(!hard_drive) - return - set_autorun(href_list["PC_setautorun"]) - - if(.) - update_uis() - -// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_" +// Function used by TGUI's to obtain data for header. All relevant entries begin with "PC_" /obj/item/modular_computer/proc/get_header_data() var/list/data = list() @@ -152,4 +177,4 @@ data["PC_stationtime"] = stationtime2text() data["PC_hasheader"] = 1 data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen - return data \ No newline at end of file + return data diff --git a/code/modules/modular_computers/computers/modular_computer/variables.dm b/code/modules/modular_computers/computers/modular_computer/variables.dm index 8a1641a125..373d2ad3e1 100644 --- a/code/modules/modular_computers/computers/modular_computer/variables.dm +++ b/code/modules/modular_computers/computers/modular_computer/variables.dm @@ -6,6 +6,7 @@ var/enabled = 0 // Whether the computer is turned on. var/screen_on = 1 // Whether the computer is active/opened/it's screen is on. + var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices. var/datum/computer_file/program/active_program = null // A currently active program running on the computer. var/hardware_flag = 0 // A flag that describes this device type var/last_power_usage = 0 // Last tick power usage of this computer @@ -23,6 +24,7 @@ // If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example. icon = null // This thing isn't meant to be used on it's own. Subtypes should supply their own icon. + var/overlay_icon = null // Icon file used for overlays icon_state = null center_of_mass = null // No pixelshifting by placing on tables, etc. randpixel = 0 // And no random pixelshifting on-creation either. diff --git a/code/modules/modular_computers/file_system/news_article.dm b/code/modules/modular_computers/file_system/news_article.dm index 833ba382e9..dffb978383 100644 --- a/code/modules/modular_computers/file_system/news_article.dm +++ b/code/modules/modular_computers/file_system/news_article.dm @@ -17,7 +17,7 @@ // NEWS DEFINITIONS BELOW THIS LINE -/* KEPT HERE AS AN EXAMPLE +/* /datum/computer_file/data/news_article/space/vol_one filename = "SPACE Magazine vol. 1" server_file_path = 'news_articles/space_magazine_1.html' diff --git a/code/modules/modular_computers/file_system/program.dm b/code/modules/modular_computers/file_system/program.dm index 08589fb13c..d5e55940d0 100644 --- a/code/modules/modular_computers/file_system/program.dm +++ b/code/modules/modular_computers/file_system/program.dm @@ -5,9 +5,6 @@ var/required_access = null // List of required accesses to run/download the program. var/requires_access_to_run = 1 // Whether the program checks for required_access when run. var/requires_access_to_download = 1 // Whether the program checks for required_access when downloading. - // NanoModule - var/datum/nano_module/NM = null // If the program uses NanoModule, put it here and it will be automagically opened. Otherwise implement ui_interact. - var/nanomodule_path = null // Path to nanomodule, make sure to set this if implementing new program. // TGUIModule var/datum/tgui_module/TM = null // If the program uses TGUIModule, put it here and it will be automagically opened. Otherwise implement tgui_interact. var/tguimodule_path = null // Path to tguimodule, make sure to set this if implementing new program. @@ -29,6 +26,8 @@ var/computer_emagged = 0 // Set to 1 if computer that's running us was emagged. Computer updates this every Process() tick var/ui_header = null // Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /nano/images/status_icons. Be careful not to use too large images! var/ntnet_speed = 0 // GQ/s - current network connectivity transfer rate + /// Name of the tgui interface + var/tgui_id /datum/computer_file/program/New(var/obj/item/modular_computer/comp = null) ..() @@ -39,16 +38,12 @@ computer = null . = ..() -/datum/computer_file/program/nano_host() - return computer.nano_host() - /datum/computer_file/program/tgui_host() return computer.tgui_host() /datum/computer_file/program/clone() var/datum/computer_file/program/temp = ..() temp.required_access = required_access - temp.nanomodule_path = nanomodule_path temp.filedesc = filedesc temp.program_icon_state = program_icon_state temp.requires_ntnet = requires_ntnet @@ -134,13 +129,9 @@ /datum/computer_file/program/proc/run_program(var/mob/living/user) if(can_run(user, 1) || !requires_access_to_run) computer.active_program = src - if(nanomodule_path) - NM = new nanomodule_path(src, new /datum/topic_manager/program(src), src) - NM.using_access = user.GetAccess() if(tguimodule_path) TM = new tguimodule_path(src) TM.using_access = user.GetAccess() - TM.tgui_interact(user) if(requires_ntnet && network_destination) generate_network_log("Connection opened to [network_destination].") program_state = PROGRAM_STATE_ACTIVE @@ -152,26 +143,28 @@ program_state = PROGRAM_STATE_KILLED if(network_destination) generate_network_log("Connection to [network_destination] closed.") - QDEL_NULL(NM) if(TM) SStgui.close_uis(TM) - qdel(TM) - TM = null + QDEL_NULL(TM) return 1 -// This is called every tick when the program is enabled. Ensure you do parent call if you override it. If parent returns 1 continue with UI initialisation. -// It returns 0 if it can't run or if NanoModule was used instead. I suggest using NanoModules where applicable. -/datum/computer_file/program/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - if(program_state != PROGRAM_STATE_ACTIVE) // Our program was closed. Close the ui if it exists. +/datum/computer_file/program/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/headers) + ) + +/datum/computer_file/program/tgui_interact(mob/user, datum/tgui/ui) + if(program_state != PROGRAM_STATE_ACTIVE) if(ui) ui.close() - return computer.ui_interact(user) - if(istype(NM)) - NM.ui_interact(user, ui_key, null, force_open) - return 0 + return computer.tgui_interact(user) if(istype(TM)) TM.tgui_interact(user) return 0 + ui = SStgui.try_update_ui(user, src, ui) + if(!ui && tgui_id) + ui = new(user, src, tgui_id, filedesc) + ui.open() return 1 // CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC: @@ -185,49 +178,56 @@ if(computer) return computer.Topic(href, href_list) +// CONVENTIONS, READ THIS WHEN CREATING NEW PROGRAM AND OVERRIDING THIS PROC: +// Topic calls are automagically forwarded from NanoModule this program contains. +// Calls beginning with "PRG_" are reserved for programs handling. +// Calls beginning with "PC_" are reserved for computer handling (by whatever runs the program) +// ALWAYS INCLUDE PARENT CALL ..() OR DIE IN FIRE. +/datum/computer_file/program/tgui_act(action,list/params, datum/tgui/ui) + if(..()) + return 1 + if(computer) + switch(action) + if("PC_exit") + computer.kill_program() + ui.close() + return 1 + if("PC_shutdown") + computer.shutdown_computer() + ui.close() + return 1 + if("PC_minimize") + var/mob/user = usr + if(!computer.active_program) + return + + computer.idle_threads.Add(computer.active_program) + program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs + + computer.active_program = null + computer.update_icon() + ui.close() + + if(user && istype(user)) + computer.tgui_interact(user) // Re-open the UI on this computer. It should show the main screen now. + + + // Relays the call to nano module, if we have one /datum/computer_file/program/proc/check_eye(var/mob/user) - if(NM) - return NM.check_eye(user) if(TM) return TM.check_eye(user) else return -1 -/obj/item/modular_computer/initial_data() - return get_header_data() - -/obj/item/modular_computer/update_layout() - return TRUE - -/datum/nano_module/program - //available_to_ai = FALSE - var/datum/computer_file/program/program = null // Program-Based computer program that runs this nano module. Defaults to null. - -/datum/nano_module/program/New(var/host, var/topic_manager, var/program) - ..() - src.program = program - -/datum/topic_manager/program - var/datum/program - -/datum/topic_manager/program/New(var/datum/program) - ..() - src.program = program - -// Calls forwarded to PROGRAM itself should begin with "PRG_" -// Calls forwarded to COMPUTER running the program should begin with "PC_" -/datum/topic_manager/program/Topic(href, href_list) - return program && program.Topic(href, href_list) - /datum/computer_file/program/apply_visual(mob/M) - if(NM) - return NM.apply_visual(M) + if(TM) + return TM.apply_visual(M) /datum/computer_file/program/remove_visual(mob/M) - if(NM) - return NM.remove_visual(M) + if(TM) + return TM.remove_visual(M) /datum/computer_file/program/proc/relaymove(var/mob/M, direction) - if(NM) - return NM.relaymove(M, direction) \ No newline at end of file + if(TM) + return TM.relaymove(M, direction) \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/antagonist/access_decrypter.dm b/code/modules/modular_computers/file_system/programs/antagonist/access_decrypter.dm index 04bf931593..11547e3426 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/access_decrypter.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/access_decrypter.dm @@ -6,15 +6,17 @@ program_menu_icon = "unlocked" extended_desc = "This highly advanced script can very slowly decrypt operational codes used in almost any network. These codes can be downloaded to an ID card to expand the available access. The system administrator will probably notice this." size = 34 - requires_ntnet = 1 - available_on_ntnet = 0 - available_on_syndinet = 1 - nanomodule_path = /datum/nano_module/program/access_decrypter/ + requires_ntnet = TRUE + available_on_ntnet = FALSE + available_on_syndinet = TRUE + tgui_id = "NtosAccessDecrypter" + var/message = "" var/running = FALSE var/progress = 0 var/target_progress = 300 var/datum/access/target_access = null + var/list/restricted_access_codes = list(access_change_ids, access_network) // access codes that are not hackable due to balance reasons /datum/computer_file/program/access_decrypter/kill_program(var/forced) reset() @@ -48,82 +50,63 @@ message = "Successfully decrypted and saved operational key codes. Downloaded access codes for: [target_access.desc]" target_access = null -/datum/computer_file/program/access_decrypter/Topic(href, href_list) +/datum/computer_file/program/access_decrypter/tgui_act(action, list/params, datum/tgui/ui) if(..()) - return 1 - if(href_list["PRG_reset"]) - reset() - return 1 - if(href_list["PRG_execute"]) - if(running) - return 1 - if(text2num(href_list["allowed"])) - return 1 - var/obj/item/weapon/computer_hardware/processor_unit/CPU = computer.processor_unit - var/obj/item/weapon/computer_hardware/card_slot/RFID = computer.card_slot - if(!istype(CPU) || !CPU.check_functionality() || !istype(RFID) || !RFID.check_functionality()) - message = "A fatal hardware error has been detected." - return - if(!istype(RFID.stored_card)) - message = "RFID card is not present in the device. Operation aborted." - return - running = TRUE - target_access = get_access_by_id(href_list["PRG_execute"]) - if(ntnet_global.intrusion_detection_enabled) - ntnet_global.add_log("IDS WARNING - Unauthorised access attempt to primary keycode database from device: [computer.network_card.get_network_tag()]") - ntnet_global.intrusion_detection_alarm = 1 - return 1 + return TRUE + switch(action) + if("PRG_reset") + reset() + return TRUE + if("PRG_execute") + if(running) + return TRUE + if(text2num(params["allowed"])) + return TRUE + var/obj/item/weapon/computer_hardware/processor_unit/CPU = computer.processor_unit + var/obj/item/weapon/computer_hardware/card_slot/RFID = computer.card_slot + if(!istype(CPU) || !CPU.check_functionality() || !istype(RFID) || !RFID.check_functionality()) + message = "A fatal hardware error has been detected." + return + if(!istype(RFID.stored_card)) + message = "RFID card is not present in the device. Operation aborted." + return + running = TRUE + target_access = get_access_by_id("[params["access_target"]]") + if(ntnet_global.intrusion_detection_enabled) + ntnet_global.add_log("IDS WARNING - Unauthorised access attempt to primary keycode database from device: [computer.network_card.get_network_tag()]") + ntnet_global.intrusion_detection_alarm = TRUE + return TRUE -/datum/nano_module/program/access_decrypter - name = "NTNet Access Decrypter" - var/list/restricted_access_codes = list(access_change_ids, access_network) // access codes that are not hackable due to balance reasons - -/datum/nano_module/program/access_decrypter/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/computer_file/program/access_decrypter/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) if(!ntnet_global) return - var/datum/computer_file/program/access_decrypter/PRG = program - var/list/data = list() - if(!istype(PRG)) - return - data = PRG.get_header_data() + var/list/data = get_header_data() - if(PRG.message) - data["message"] = PRG.message - else if(PRG.running) + var/list/regions = list() + data["message"] = null + data["running"] = running + if(message) + data["message"] = message + else if(running) data["running"] = 1 - data["rate"] = PRG.computer.processor_unit.max_idle_programs - - // Stolen from DOS traffic generator, generates strings of 1s and 0s - var/percentage = (PRG.progress / PRG.target_progress) * 100 - var/list/strings[0] - for(var/j, j<10, j++) - var/string = "" - for(var/i, i<20, i++) - string = "[string][prob(percentage)]" - strings.Add(string) - data["dos_strings"] = strings - else if(program.computer.card_slot && program.computer.card_slot.stored_card) - var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card - var/list/regions = list() + data["rate"] = computer.processor_unit.max_idle_programs + data["factor"] = (progress / target_progress) + else if(computer?.card_slot?.stored_card) + var/obj/item/weapon/card/id/id_card = computer.card_slot.stored_card for(var/i = 1; i <= 7; i++) var/list/accesses = list() for(var/access in get_region_accesses(i)) - if (get_access_desc(access)) + if(get_access_desc(access) && !(access in restricted_access_codes)) accesses.Add(list(list( - "desc" = replacetext(get_access_desc(access), " ", " "), + "desc" = replacetext(get_access_desc(access), " ", " "), "ref" = access, - "allowed" = (access in id_card.access) ? 1 : 0, - "blocked" = (access in restricted_access_codes) ? 1 : 0))) + "allowed" = (access in id_card.access) ? 1 : 0 + ))) regions.Add(list(list( "name" = get_region_accesses_name(i), - "accesses" = accesses))) - data["regions"] = regions + "accesses" = accesses + ))) + data["regions"] = regions - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "access_decrypter.tmpl", "NTNet Access Decrypter", 550, 400, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) \ No newline at end of file + return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm index 0e0a1381bd..e1fa0cfa8a 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/dos.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/dos.dm @@ -6,10 +6,11 @@ program_menu_icon = "arrow-4-diag" extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect" size = 20 - requires_ntnet = 1 - available_on_ntnet = 0 - available_on_syndinet = 1 - nanomodule_path = /datum/nano_module/program/computer_dos/ + requires_ntnet = TRUE + available_on_ntnet = FALSE + available_on_syndinet = TRUE + tgui_id = "NtosNetDos" + var/obj/machinery/ntnet_relay/target = null var/dos_speed = 0 var/error = "" @@ -39,70 +40,51 @@ ..(forced) -/datum/nano_module/program/computer_dos - name = "DoS Traffic Generator" - -/datum/nano_module/program/computer_dos/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/computer_file/program/ntnet_dos/tgui_data(mob/user) if(!ntnet_global) return - var/datum/computer_file/program/ntnet_dos/PRG = program - var/list/data = list() - if(!istype(PRG)) - return - data = PRG.get_header_data() - if(PRG.error) - data["error"] = PRG.error - else if(PRG.target && PRG.executed) - data["target"] = 1 - data["speed"] = PRG.dos_speed + var/list/data = get_header_data() - // This is mostly visual, generate some strings of 1s and 0s - // Probability of 1 is equal of completion percentage of DoS attack on this relay. - // Combined with UI updates this adds quite nice effect to the UI - var/percentage = PRG.target.dos_overload * 100 / PRG.target.dos_capacity - var/list/strings[0] - for(var/j, j<10, j++) - var/string = "" - for(var/i, i<20, i++) - string = "[string][prob(percentage)]" - strings.Add(string) - data["dos_strings"] = strings + data["error"] = error + if(target && executed) + data["target"] = TRUE + data["speed"] = dos_speed + + data["overload"] = target.dos_overload + data["capacity"] = target.dos_capacity else - var/list/relays[0] + data["target"] = FALSE + data["relays"] = list() for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) - relays.Add(R.uid) - data["relays"] = relays - data["focus"] = PRG.target ? PRG.target.uid : null + data["relays"] += list(list("id" = R.uid)) + data["focus"] = target ? target.uid : null - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "ntnet_dos.tmpl", "DoS Traffic Generator", 400, 250, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data -/datum/computer_file/program/ntnet_dos/Topic(href, href_list) +/datum/computer_file/program/ntnet_dos/tgui_act(action, params) if(..()) - return 1 - if(href_list["PRG_target_relay"]) - for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) - if("[R.uid]" == href_list["PRG_target_relay"]) - target = R - return 1 - if(href_list["PRG_reset"]) - if(target) - target.dos_sources.Remove(src) - target = null - executed = 0 - error = "" - return 1 - if(href_list["PRG_execute"]) - if(target) - executed = 1 - target.dos_sources.Add(src) - if(ntnet_global.intrusion_detection_enabled) - ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [computer.network_card.get_network_tag()]") - ntnet_global.intrusion_detection_alarm = 1 - return 1 + return TRUE + switch(action) + if("PRG_target_relay") + for(var/obj/machinery/ntnet_relay/R in ntnet_global.relays) + if(R.uid == text2num(params["targid"])) + target = R + break + return TRUE + if("PRG_reset") + if(target) + target.dos_sources.Remove(src) + target = null + executed = FALSE + error = "" + return TRUE + if("PRG_execute") + if(target) + executed = TRUE + target.dos_sources.Add(src) + if(ntnet_global.intrusion_detection_enabled) + var/obj/item/weapon/computer_hardware/network_card/network_card = computer.network_card + ntnet_global.add_log("IDS WARNING - Excess traffic flood targeting relay [target.uid] detected from device: [network_card.get_network_tag()]") + ntnet_global.intrusion_detection_alarm = TRUE + return TRUE diff --git a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm index 17199c3b18..5d0e291336 100644 --- a/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm +++ b/code/modules/modular_computers/file_system/programs/antagonist/revelation.dm @@ -6,10 +6,10 @@ program_menu_icon = "home" extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution." size = 13 - requires_ntnet = 0 - available_on_ntnet = 0 - available_on_syndinet = 1 - nanomodule_path = /datum/nano_module/program/revelation/ + requires_ntnet = FALSE + available_on_ntnet = FALSE + available_on_syndinet = TRUE + tgui_id = "NtosRevelation" var/armed = 0 /datum/computer_file/program/revelation/run_program(var/mob/living/user) @@ -37,48 +37,31 @@ if(computer.tesla_link && prob(50)) qdel(computer.tesla_link) -/datum/computer_file/program/revelation/Topic(href, href_list) +/datum/computer_file/program/revelation/tgui_act(action, params) if(..()) - return 1 - else if(href_list["PRG_arm"]) - armed = !armed - else if(href_list["PRG_activate"]) - activate() - else if(href_list["PRG_obfuscate"]) - var/mob/living/user = usr - var/newname = sanitize(input(user, "Enter new program name: ")) - if(!newname) - return - filedesc = newname - for(var/datum/computer_file/program/P in ntnet_global.available_station_software) - if(filedesc == P.filedesc) - program_menu_icon = P.program_menu_icon - break - return 1 + return + switch(action) + if("PRG_arm") + armed = !armed + return TRUE + if("PRG_activate") + activate() + return TRUE + if("PRG_obfuscate") + var/newname = params["new_name"] + if(!newname) + return + filedesc = newname + return TRUE /datum/computer_file/program/revelation/clone() var/datum/computer_file/program/revelation/temp = ..() temp.armed = armed return temp -/datum/nano_module/program/revelation - name = "Revelation Virus" +/datum/computer_file/program/revelation/tgui_data(mob/user) + var/list/data = get_header_data() -/datum/nano_module/program/revelation/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = list() - var/datum/computer_file/program/revelation/PRG = program - if(!istype(PRG)) - return - - data = PRG.get_header_data() - - data["armed"] = PRG.armed - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "revelation.tmpl", "Revelation Virus", 400, 250, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + data["armed"] = armed + return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/command/card.dm b/code/modules/modular_computers/file_system/programs/command/card.dm index aa122c0abf..47fe4ea2b3 100644 --- a/code/modules/modular_computers/file_system/programs/command/card.dm +++ b/code/modules/modular_computers/file_system/programs/command/card.dm @@ -1,7 +1,7 @@ /datum/computer_file/program/card_mod filename = "cardmod" filedesc = "ID card modification program" - nanomodule_path = /datum/nano_module/program/card_mod + tguimodule_path = /datum/tgui_module/cardmod program_icon_state = "id" program_key_state = "id_key" program_menu_icon = "key" @@ -9,220 +9,3 @@ required_access = access_change_ids requires_ntnet = 0 size = 8 - -/datum/nano_module/program/card_mod - name = "ID card modification program" - var/mod_mode = 1 - var/is_centcom = 0 - var/show_assignments = 0 - -/datum/nano_module/program/card_mod/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() - - data["src"] = "\ref[src]" - data["station_name"] = station_name() - data["manifest"] = data_core ? data_core.get_manifest(0) : null - data["assignments"] = show_assignments - if(program && program.computer) - data["have_id_slot"] = !!program.computer.card_slot - data["have_printer"] = !!program.computer.nano_printer - data["authenticated"] = program.can_run(user) - if(!program.computer.card_slot) - mod_mode = 0 //We can't modify IDs when there is no card reader - else - data["have_id_slot"] = 0 - data["have_printer"] = 0 - data["authenticated"] = 0 - data["mmode"] = mod_mode - data["centcom_access"] = is_centcom - - if(program && program.computer && program.computer.card_slot) - var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card - data["has_id"] = !!id_card - data["id_account_number"] = id_card ? id_card.associated_account_number : null - data["id_rank"] = id_card && id_card.assignment ? id_card.assignment : "Unassigned" - data["id_owner"] = id_card && id_card.registered_name ? id_card.registered_name : "-----" - data["id_name"] = id_card ? id_card.name : "-----" - - var/list/departments = list() - for(var/D in SSjob.get_all_department_datums()) - var/datum/department/dept = D - if(!dept.assignable) // No AI ID cards for you. - continue - if(dept.centcom_only && !is_centcom) - continue - departments[++departments.len] = list("department_name" = dept.name, "jobs" = format_jobs(SSjob.get_job_titles_in_department(dept.name)) ) - - data["departments"] = departments - - data["all_centcom_access"] = is_centcom ? get_accesses(1) : null - data["regions"] = get_accesses() - - if(program.computer.card_slot && program.computer.card_slot.stored_card) - var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card - if(is_centcom) - var/list/all_centcom_access = list() - for(var/access in get_all_centcom_access()) - all_centcom_access.Add(list(list( - "desc" = replacetext(get_centcom_access_desc(access), " ", " "), - "ref" = access, - "allowed" = (access in id_card.access) ? 1 : 0))) - data["all_centcom_access"] = all_centcom_access - else - var/list/regions = list() - for(var/i = 1; i <= 7; i++) - var/list/accesses = list() - for(var/access in get_region_accesses(i)) - if (get_access_desc(access)) - accesses.Add(list(list( - "desc" = replacetext(get_access_desc(access), " ", " "), - "ref" = access, - "allowed" = (access in id_card.access) ? 1 : 0))) - - regions.Add(list(list( - "name" = get_region_accesses_name(i), - "accesses" = accesses))) - data["regions"] = regions - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "mod_identification_computer.tmpl", name, 600, 700, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - -/datum/nano_module/program/card_mod/proc/format_jobs(list/jobs) - var/obj/item/weapon/card/id/id_card = program.computer.card_slot ? program.computer.card_slot.stored_card : null - var/list/formatted = list() - for(var/job in jobs) - formatted.Add(list(list( - "display_name" = replacetext(job, " ", " "), - "target_rank" = id_card && id_card.assignment ? id_card.assignment : "Unassigned", - "job" = job))) - - return formatted - -/datum/nano_module/program/card_mod/proc/get_accesses(var/is_centcom = 0) - return null - - -/datum/computer_file/program/card_mod/Topic(href, href_list) - if(..()) - return 1 - - var/mob/user = usr - var/obj/item/weapon/card/id/user_id_card = user.GetIdCard() - var/obj/item/weapon/card/id/id_card - if (computer.card_slot) - id_card = computer.card_slot.stored_card - - var/datum/nano_module/program/card_mod/module = NM - switch(href_list["action"]) - if("switchm") - if(href_list["target"] == "mod") - module.mod_mode = 1 - else if (href_list["target"] == "manifest") - module.mod_mode = 0 - if("togglea") - if(module.show_assignments) - module.show_assignments = 0 - else - module.show_assignments = 1 - if("print") - if(computer && computer.nano_printer) //This option should never be called if there is no printer - if(module.mod_mode) - if(can_run(user, 1)) - var/contents = {"

Access Report

- Prepared By: [user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
- For: [id_card.registered_name ? id_card.registered_name : "Unregistered"]
-
- Assignment: [id_card.assignment]
- Account Number: #[id_card.associated_account_number]
- Blood Type: [id_card.blood_type]

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

Crew Manifest

-
- [data_core ? data_core.get_manifest(0) : ""] - "} - if(!computer.nano_printer.print_text(contents,text("crew manifest ([])", stationtime2text()))) - to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") - return - else - computer.visible_message("\The [computer] prints out paper.") - if("eject") - if(computer && computer.card_slot) - if(id_card) - data_core.manifest_modify(id_card.registered_name, id_card.assignment) - computer.proc_eject_id(user) - if("terminate") - if(computer && can_run(user, 1)) - id_card.assignment = "Dismissed" //VOREStation Edit: setting adjustment - id_card.access = list() - callHook("terminate_employee", list(id_card)) - if("edit") - if(computer && can_run(user, 1)) - if(href_list["name"]) - var/temp_name = sanitizeName(input("Enter name.", "Name", id_card.registered_name),allow_numbers=TRUE) - if(temp_name) - id_card.registered_name = temp_name - else - computer.visible_message("[computer] buzzes rudely.") - else if(href_list["account"]) - var/account_num = text2num(input("Enter account number.", "Account", id_card.associated_account_number)) - id_card.associated_account_number = account_num - if("assign") - if(computer && can_run(user, 1) && id_card) - var/t1 = href_list["assign_target"] - if(t1 == "Custom") - var/temp_t = sanitize(input("Enter a custom job assignment.","Assignment", id_card.assignment), 45) - //let custom jobs function as an impromptu alt title, mainly for sechuds - if(temp_t) - id_card.assignment = temp_t - else - var/list/access = list() - if(module.is_centcom) - access = get_centcom_access(t1) - else - var/datum/job/jobdatum - for(var/jobtype in typesof(/datum/job)) - var/datum/job/J = new jobtype - if(ckey(J.title) == ckey(t1)) - jobdatum = J - break - if(!jobdatum) - to_chat(usr, "No log exists for this job: [t1]") - return - - access = jobdatum.get_access() - - id_card.access = access - id_card.assignment = t1 - id_card.rank = t1 - - callHook("reassign_employee", list(id_card)) - if("access") - if(href_list["allowed"] && computer && can_run(user, 1)) - var/access_type = text2num(href_list["access_target"]) - var/access_allowed = text2num(href_list["allowed"]) - if(access_type in get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM)) - id_card.access -= access_type - if(!access_allowed) - id_card.access += access_type - if(id_card) - id_card.name = text("[id_card.registered_name]'s ID Card ([id_card.assignment])") - - SSnanoui.update_uis(NM) - return 1 diff --git a/code/modules/modular_computers/file_system/programs/command/comm.dm b/code/modules/modular_computers/file_system/programs/command/comm.dm index fc2efa8820..a2079ad494 100644 --- a/code/modules/modular_computers/file_system/programs/command/comm.dm +++ b/code/modules/modular_computers/file_system/programs/command/comm.dm @@ -9,7 +9,7 @@ program_icon_state = "comm" program_key_state = "med_key" program_menu_icon = "flag" - nanomodule_path = /datum/nano_module/program/comm + tguimodule_path = /datum/tgui_module/communications/ntos extended_desc = "Used to command and control. Can relay long-range communications. This program can not be run on tablet computers." required_access = access_heads requires_ntnet = 1 @@ -24,269 +24,6 @@ temp.message_core.messages = message_core.messages.Copy() return temp -/datum/nano_module/program/comm - name = "Command and Communications Program" - //available_to_ai = TRUE - var/current_status = STATE_DEFAULT - var/msg_line1 = "" - var/msg_line2 = "" - var/centcomm_message_cooldown = 0 - var/announcment_cooldown = 0 - var/datum/announcement/priority/crew_announcement = new - var/current_viewing_message_id = 0 - var/current_viewing_message = null - -/datum/nano_module/program/comm/New() - ..() - crew_announcement.newscast = 1 - -/datum/nano_module/program/comm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() - - if(program) - data["emagged"] = program.computer_emagged - data["net_comms"] = !!program.get_signal(NTNET_COMMUNICATION) //Double !! is needed to get 1 or 0 answer - data["net_syscont"] = !!program.get_signal(NTNET_SYSTEMCONTROL) - if(program.computer) - data["have_printer"] = !!program.computer.nano_printer - else - data["have_printer"] = 0 - else - data["emagged"] = 0 - data["net_comms"] = 1 - data["net_syscont"] = 1 - data["have_printer"] = 0 - - data["message_line1"] = msg_line1 - data["message_line2"] = msg_line2 - data["state"] = current_status - data["isAI"] = issilicon(usr) - data["authenticated"] = get_authentication_level(user) - data["current_security_level"] = security_level - data["current_security_level_title"] = num2seclevel(security_level) - - data["def_SEC_LEVEL_DELTA"] = SEC_LEVEL_DELTA - data["def_SEC_LEVEL_YELLOW"] = SEC_LEVEL_YELLOW - data["def_SEC_LEVEL_ORANGE"] = SEC_LEVEL_ORANGE - data["def_SEC_LEVEL_VIOLET"] = SEC_LEVEL_VIOLET - data["def_SEC_LEVEL_BLUE"] = SEC_LEVEL_BLUE - data["def_SEC_LEVEL_GREEN"] = SEC_LEVEL_GREEN - - var/datum/comm_message_listener/l = obtain_message_listener() - data["messages"] = l.messages - data["message_deletion_allowed"] = l != global_message_listener - data["message_current_id"] = current_viewing_message_id - if(current_viewing_message) - data["message_current"] = current_viewing_message - - if(emergency_shuttle.location()) - data["have_shuttle"] = 1 - if(emergency_shuttle.online()) - data["have_shuttle_called"] = 1 - else - data["have_shuttle_called"] = 0 - else - data["have_shuttle"] = 0 - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if(!ui) - ui = new(user, src, ui_key, "mod_communication.tmpl", name, 550, 420, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - -/datum/nano_module/program/comm/proc/get_authentication_level(var/mob/user) - if(program) - if(program.can_run(user, 0, access_captain)) - return 2 - else - return program.can_run(user) - return 1 - -/datum/nano_module/program/comm/proc/obtain_message_listener() - if(program) - var/datum/computer_file/program/comm/P = program - return P.message_core - return global_message_listener - -/datum/nano_module/program/comm/Topic(href, href_list) - if(..()) - return 1 - var/mob/user = usr - var/ntn_comm = program ? !!program.get_signal(NTNET_COMMUNICATION) : 1 - var/ntn_cont = program ? !!program.get_signal(NTNET_SYSTEMCONTROL) : 1 - var/datum/comm_message_listener/l = obtain_message_listener() - switch(href_list["action"]) - if("sw_menu") - . = 1 - current_status = text2num(href_list["target"]) - if("announce") - . = 1 - if(get_authentication_level(user) == 2 && !issilicon(usr) && ntn_comm) - if(user) - var/obj/item/weapon/card/id/id_card = user.GetIdCard() - crew_announcement.announcer = GetNameAndAssignmentFromId(id_card) - else - crew_announcement.announcer = "Unknown" - if(announcment_cooldown) - to_chat(usr, "Please allow at least one minute to pass between announcements") - return TRUE - var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|message - if(!input || !can_still_topic()) - return 1 - crew_announcement.Announce(input) - announcment_cooldown = 1 - spawn(600)//One minute cooldown - announcment_cooldown = 0 - if("message") - . = 1 - if(href_list["target"] == "emagged") - if(program) - if(get_authentication_level(user) == 2 && program.computer_emagged && !issilicon(usr) && ntn_comm) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "") as null|text) - if(!input || !can_still_topic()) - return 1 - Syndicate_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_say("[key_name(usr)] has made an illegal announcement: [input]") - centcomm_message_cooldown = 1 - spawn(300)//30 second cooldown - centcomm_message_cooldown = 0 - else if(href_list["target"] == "regular") - if(get_authentication_level(user) == 2 && !issilicon(usr) && ntn_comm) - if(centcomm_message_cooldown) - to_chat(usr, "Arrays recycling. Please stand by.") - SSnanoui.update_uis(src) - return - if(!is_relay_online())//Contact Centcom has a check, Syndie doesn't to allow for Traitor funs. - to_chat(usr, "No Emergency Bluespace Relay detected. Unable to transmit message.") - return 1 - var/input = sanitize(input("Please choose a message to transmit to Centcomm via quantum entanglement. \ - Please be aware that this process is very expensive, and abuse will lead to... termination. \ - Transmission does not guarantee a response. There is a 30 second delay before you may send another message, \ - be clear, full and concise.", "Central Command Quantum Messaging") as null|message) - if(!input || !can_still_topic()) - return 1 - CentCom_announce(input, usr) - to_chat(usr, "Message transmitted.") - log_say("[key_name(usr)] has made an IA Centcomm announcement: [input]") - centcomm_message_cooldown = 1 - spawn(300) //30 second cooldown - centcomm_message_cooldown = 0 - if("shuttle") - . = 1 - if(get_authentication_level(user) && ntn_cont) - if(href_list["target"] == "call") - var/confirm = alert("Are you sure you want to call the shuttle?", name, "No", "Yes") - if(confirm == "Yes" && can_still_topic()) - call_shuttle_proc(usr) - - if(href_list["target"] == "cancel" && !issilicon(usr)) - var/confirm = alert("Are you sure you want to cancel the shuttle?", name, "No", "Yes") - if(confirm == "Yes" && can_still_topic()) - cancel_call_proc(usr) - if("setstatus") - . = 1 - if(get_authentication_level(user) && ntn_cont) - switch(href_list["target"]) - if("line1") - var/linput = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", msg_line1) as text|null, 40), 40) - if(can_still_topic()) - msg_line1 = linput - if("line2") - var/linput = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", msg_line2) as text|null, 40), 40) - if(can_still_topic()) - msg_line2 = linput - if("message") - post_status("message", msg_line1, msg_line2) - if("alert") - post_status("alert", href_list["alert"]) - else - post_status(href_list["target"]) - if("setalert") - . = 1 - if(get_authentication_level(user) && !issilicon(usr) && ntn_cont && ntn_comm) - var/current_level = text2num(href_list["target"]) - var/confirm = alert("Are you sure you want to change alert level to [num2seclevel(current_level)]?", name, "No", "Yes") - if(confirm == "Yes" && can_still_topic()) - var/old_level = security_level - if(!current_level) current_level = SEC_LEVEL_GREEN - if(current_level < SEC_LEVEL_GREEN) current_level = SEC_LEVEL_GREEN - if(current_level > SEC_LEVEL_BLUE) current_level = SEC_LEVEL_BLUE //Cannot engage delta with this - set_security_level(current_level) - if(security_level != old_level) - log_game("[key_name(usr)] has changed the security level to [get_security_level()].") - message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") - switch(security_level) - if(SEC_LEVEL_GREEN) - feedback_inc("alert_comms_green",1) - if(SEC_LEVEL_YELLOW) - feedback_inc("alert_comms_yellow",1) - if(SEC_LEVEL_ORANGE) - feedback_inc("alert_comms_orange",1) - if(SEC_LEVEL_VIOLET) - feedback_inc("alert_comms_violet",1) - if(SEC_LEVEL_BLUE) - feedback_inc("alert_comms_blue",1) - else - to_chat(usr, "You press button, but red light flashes and nothing happens.")//This should never happen - - current_status = STATE_DEFAULT - if("viewmessage") - . = 1 - if(get_authentication_level(user) && ntn_comm) - current_viewing_message_id = text2num(href_list["target"]) - for(var/list/m in l.messages) - if(m["id"] == current_viewing_message_id) - current_viewing_message = m - current_status = STATE_VIEWMESSAGE - if("delmessage") - . = 1 - if(get_authentication_level(user) && ntn_comm && l != global_message_listener) - l.Remove(current_viewing_message) - current_status = STATE_MESSAGELIST - if("printmessage") - . = 1 - if(get_authentication_level(user) && ntn_comm) - if(program && program.computer && program.computer.nano_printer) - if(!program.computer.nano_printer.print_text(current_viewing_message["contents"],current_viewing_message["title"])) - to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") - else - program.computer.visible_message("\The [program.computer] prints out paper.") - - -/datum/nano_module/program/comm/proc/post_status(var/command, var/data1, var/data2) - - var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) - - if(!frequency) return - - - var/datum/signal/status_signal = new - status_signal.source = src - status_signal.transmission_method = TRANSMISSION_RADIO - status_signal.data["command"] = command - - switch(command) - if("message") - status_signal.data["msg1"] = data1 - status_signal.data["msg2"] = data2 - log_admin("STATUS: [key_name(usr)] set status screen message with [src]: [data1] [data2]") - if("alert") - status_signal.data["picture_state"] = data1 - - frequency.post_signal(src, status_signal) - -#undef STATE_DEFAULT -#undef STATE_MESSAGELIST -#undef STATE_VIEWMESSAGE -#undef STATE_STATUSDISPLAY -#undef STATE_ALERT_LEVEL - /* General message handling stuff */ @@ -304,19 +41,9 @@ proc/post_comm_message(var/message_title, var/message_text) message["title"] = message_title message["contents"] = message_text - for (var/datum/comm_message_listener/l in comm_message_listeners) + for(var/datum/comm_message_listener/l in comm_message_listeners) l.Add(message) - //Old console support - for (var/obj/machinery/computer/communications/comm in machines) - if (!(comm.stat & (BROKEN | NOPOWER)) && comm.prints_intercept) - var/obj/item/weapon/paper/intercept = new /obj/item/weapon/paper( comm.loc ) - intercept.name = message_title - intercept.info = message_text - - comm.messagetitle.Add(message_title) - comm.messagetext.Add(message_text) - /datum/comm_message_listener var/list/messages diff --git a/code/modules/modular_computers/file_system/programs/generic/configurator.dm b/code/modules/modular_computers/file_system/programs/generic/configurator.dm index c3ce1e358a..2d660c8b7e 100644 --- a/code/modules/modular_computers/file_system/programs/generic/configurator.dm +++ b/code/modules/modular_computers/file_system/programs/generic/configurator.dm @@ -14,51 +14,4 @@ size = 4 available_on_ntnet = 0 requires_ntnet = 0 - nanomodule_path = /datum/nano_module/program/computer_configurator/ - -/datum/nano_module/program/computer_configurator - name = "NTOS Computer Configuration Tool" - var/obj/item/modular_computer/movable = null - -/datum/nano_module/program/computer_configurator/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - if(program) - movable = program.computer - if(!istype(movable)) - movable = null - - // No computer connection, we can't get data from that. - if(!movable) - return 0 - - var/list/data = list() - - if(program) - data = program.get_header_data() - - var/list/hardware = movable.get_all_components() - - data["disk_size"] = movable.hard_drive.max_capacity - data["disk_used"] = movable.hard_drive.used_capacity - data["power_usage"] = movable.last_power_usage - data["battery_exists"] = movable.battery_module ? 1 : 0 - if(movable.battery_module) - data["battery_rating"] = movable.battery_module.battery.maxcharge - data["battery_percent"] = round(movable.battery_module.battery.percent()) - - var/list/all_entries[0] - for(var/obj/item/weapon/computer_hardware/H in hardware) - all_entries.Add(list(list( - "name" = H.name, - "desc" = H.desc, - "enabled" = H.enabled, - "critical" = H.critical, - "powerusage" = H.power_usage - ))) - - data["hardware"] = all_entries - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "laptop_configuration.tmpl", "NTOS Configuration Utility", 575, 700, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() \ No newline at end of file + tguimodule_path = /datum/tgui_module/computer_configurator diff --git a/code/modules/modular_computers/file_system/programs/generic/email_client.dm b/code/modules/modular_computers/file_system/programs/generic/email_client.dm index 3e2f979bb5..32d635fef4 100644 --- a/code/modules/modular_computers/file_system/programs/generic/email_client.dm +++ b/code/modules/modular_computers/file_system/programs/generic/email_client.dm @@ -11,15 +11,15 @@ var/stored_login = "" var/stored_password = "" - nanomodule_path = /datum/nano_module/email_client + tguimodule_path = /datum/tgui_module/email_client // Persistency. Unless you log out, or unless your password changes, this will pre-fill the login data when restarting the program /datum/computer_file/program/email_client/kill_program() - if(NM) - var/datum/nano_module/email_client/NME = NM - if(NME.current_account) - stored_login = NME.stored_login - stored_password = NME.stored_password + if(TM) + var/datum/tgui_module/email_client/TME = TM + if(TME.current_account) + stored_login = TME.stored_login + stored_password = TME.stored_password else stored_login = "" stored_password = "" @@ -27,473 +27,31 @@ /datum/computer_file/program/email_client/run_program() . = ..() - if(NM) - var/datum/nano_module/email_client/NME = NM - NME.stored_login = stored_login - NME.stored_password = stored_password - NME.log_in() - NME.error = "" - NME.check_for_new_messages(1) + if(TM) + var/datum/tgui_module/email_client/TME = TM + TME.stored_login = stored_login + TME.stored_password = stored_password + TME.log_in() + TME.error = "" + TME.check_for_new_messages(1) /datum/computer_file/program/email_client/proc/new_mail_notify() - computer.visible_message("\The [computer] beeps softly, indicating a new email has been received.", 1) + var/turf/T = get_turf(computer) // Because visible_message is being a butt + if(T) + T.visible_message("[computer] beeps softly, indicating a new email has been received.") + playsound(computer, 'sound/misc/server-ready.ogg', 100, 0) /datum/computer_file/program/email_client/process_tick() ..() - var/datum/nano_module/email_client/NME = NM - if(!istype(NME)) + var/datum/tgui_module/email_client/TME = TM + if(!istype(TME)) return - NME.relayed_process(ntnet_speed) + TME.relayed_process(ntnet_speed) - var/check_count = NME.check_for_new_messages() + var/check_count = TME.check_for_new_messages() if(check_count) if(check_count == 2) new_mail_notify() ui_header = "ntnrc_new.gif" else ui_header = "ntnrc_idle.gif" - -/datum/nano_module/email_client/ - name = "Email Client" - var/stored_login = "" - var/stored_password = "" - var/error = "" - - var/msg_title = "" - var/msg_body = "" - var/msg_recipient = "" - var/datum/computer_file/msg_attachment = null - var/folder = "Inbox" - var/addressbook = FALSE - var/new_message = FALSE - - var/last_message_count = 0 // How many messages were there during last check. - var/read_message_count = 0 // How many messages were there when user has last accessed the UI. - - var/datum/computer_file/downloading = null - var/download_progress = 0 - var/download_speed = 0 - - var/datum/computer_file/data/email_account/current_account = null - var/datum/computer_file/data/email_message/current_message = null - -/datum/nano_module/email_client/proc/log_in() - for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) - if(!account.can_login) - continue - if(account.login == stored_login) - if(account.password == stored_password) - if(account.suspended) - error = "This account has been suspended. Please contact the system administrator for assistance." - return 0 - current_account = account - return 1 - else - error = "Invalid Password" - return 0 - error = "Invalid Login" - return 0 - -// Returns 0 if no new messages were received, 1 if there is an unread message but notification has already been sent. -// and 2 if there is a new message that appeared in this tick (and therefore notification should be sent by the program). -/datum/nano_module/email_client/proc/check_for_new_messages(var/messages_read = FALSE) - if(!current_account) - return 0 - - var/list/allmails = current_account.all_emails() - - if(allmails.len > last_message_count) - . = 2 - else if(allmails.len > read_message_count) - . = 1 - else - . = 0 - - last_message_count = allmails.len - if(messages_read) - read_message_count = allmails.len - - -/datum/nano_module/email_client/proc/log_out() - current_account = null - downloading = null - download_progress = 0 - last_message_count = 0 - read_message_count = 0 - -/datum/nano_module/email_client/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() - - // Password has been changed by other client connected to this email account - if(current_account) - if(current_account.password != stored_password) - log_out() - error = "Invalid Password" - // Banned. - if(current_account.suspended) - log_out() - error = "This account has been suspended. Please contact the system administrator for assistance." - - if(error) - data["error"] = error - else if(downloading) - data["downloading"] = 1 - data["down_filename"] = "[downloading.filename].[downloading.filetype]" - data["down_progress"] = download_progress - data["down_size"] = downloading.size - data["down_speed"] = download_speed - - else if(istype(current_account)) - data["current_account"] = current_account.login - if(addressbook) - var/list/all_accounts = list() - for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) - if(!account.can_login) - continue - all_accounts.Add(list(list( - "login" = account.login - ))) - data["addressbook"] = 1 - data["accounts"] = all_accounts - else if(new_message) - data["new_message"] = 1 - data["msg_title"] = msg_title - data["msg_body"] = pencode2html(msg_body) - data["msg_recipient"] = msg_recipient - if(msg_attachment) - data["msg_hasattachment"] = 1 - data["msg_attachment_filename"] = "[msg_attachment.filename].[msg_attachment.filetype]" - data["msg_attachment_size"] = msg_attachment.size - else if (current_message) - data["cur_title"] = current_message.title - data["cur_body"] = pencode2html(current_message.stored_data) - data["cur_timestamp"] = current_message.timestamp - data["cur_source"] = current_message.source - data["cur_uid"] = current_message.uid - if(istype(current_message.attachment)) - data["cur_hasattachment"] = 1 - data["cur_attachment_filename"] = "[current_message.attachment.filename].[current_message.attachment.filetype]" - data["cur_attachment_size"] = current_message.attachment.size - else - data["label_inbox"] = "Inbox ([current_account.inbox.len])" - data["label_spam"] = "Spam ([current_account.spam.len])" - data["label_deleted"] = "Deleted ([current_account.deleted.len])" - var/list/message_source - if(folder == "Inbox") - message_source = current_account.inbox - else if(folder == "Spam") - message_source = current_account.spam - else if(folder == "Deleted") - message_source = current_account.deleted - - if(message_source) - data["folder"] = folder - var/list/all_messages = list() - for(var/datum/computer_file/data/email_message/message in message_source) - all_messages.Add(list(list( - "title" = message.title, - "body" = pencode2html(message.stored_data), - "source" = message.source, - "timestamp" = message.timestamp, - "uid" = message.uid - ))) - data["messages"] = all_messages - data["messagecount"] = all_messages.len - else - data["stored_login"] = stored_login - data["stored_password"] = stars(stored_password, 0) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "email_client.tmpl", "Email Client", 600, 450, state = state) - if(host.update_layout()) - ui.auto_update_layout = 1 - ui.set_auto_update(1) - ui.set_initial_data(data) - ui.open() - -/datum/nano_module/email_client/proc/find_message_by_fuid(var/fuid) - if(!istype(current_account)) - return - - // href_list works with strings, so this makes it a bit easier for us - if(istext(fuid)) - fuid = text2num(fuid) - - for(var/datum/computer_file/data/email_message/message in current_account.all_emails()) - if(message.uid == fuid) - return message - -/datum/nano_module/email_client/proc/clear_message() - new_message = FALSE - msg_title = "" - msg_body = "" - msg_recipient = "" - msg_attachment = null - current_message = null - -/datum/nano_module/email_client/proc/relayed_process(var/netspeed) - download_speed = netspeed - if(!downloading) - return - download_progress = min(download_progress + netspeed, downloading.size) - if(download_progress >= downloading.size) - var/obj/item/modular_computer/MC = nano_host() - if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) - error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" - downloading = null - download_progress = 0 - return 1 - - if(MC.hard_drive.store_file(downloading)) - error = "File successfully downloaded to local device." - else - error = "Error saving file: I/O Error: The hard drive may be full or nonfunctional." - downloading = null - download_progress = 0 - return 1 - - -/datum/nano_module/email_client/Topic(href, href_list) - if(..()) - return 1 - var/mob/living/user = usr - check_for_new_messages(1) // Any actual interaction (button pressing) is considered as acknowledging received message, for the purpose of notification icons. - if(href_list["login"]) - log_in() - return 1 - - if(href_list["logout"]) - log_out() - return 1 - - if(href_list["reset"]) - error = "" - return 1 - - if(href_list["new_message"]) - new_message = TRUE - return 1 - - if(href_list["cancel"]) - if(addressbook) - addressbook = FALSE - else - clear_message() - return 1 - - if(href_list["addressbook"]) - addressbook = TRUE - return 1 - - if(href_list["set_recipient"]) - msg_recipient = sanitize(href_list["set_recipient"]) - addressbook = FALSE - return 1 - - if(href_list["edit_title"]) - var/newtitle = sanitize(input(user,"Enter title for your message:", "Message title", msg_title), 100) - if(newtitle) - msg_title = newtitle - return 1 - - // This uses similar editing mechanism as the FileManager program, therefore it supports various paper tags and remembers formatting. - if(href_list["edit_body"]) - var/oldtext = html_decode(msg_body) - oldtext = replacetext(oldtext, "\[editorbr\]", "\n") - - var/newtext = sanitize(replacetext(input(usr, "Enter your message. You may use most tags from paper formatting", "Message Editor", oldtext) as message|null, "\n", "\[editorbr\]"), 20000) - if(newtext) - msg_body = newtext - return 1 - - if(href_list["edit_recipient"]) - var/newrecipient = sanitize(input(user,"Enter recipient's email address:", "Recipient", msg_recipient), 100) - if(newrecipient) - msg_recipient = newrecipient - return 1 - - if(href_list["edit_login"]) - var/newlogin = sanitize(input(user,"Enter login", "Login", stored_login), 100) - if(newlogin) - stored_login = newlogin - return 1 - - if(href_list["edit_password"]) - var/newpass = sanitize(input(user,"Enter password", "Password"), 100) - if(newpass) - stored_password = newpass - return 1 - - if(href_list["delete"]) - if(!istype(current_account)) - return 1 - var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["delete"]) - if(!istype(M)) - return 1 - if(folder == "Deleted") - current_account.deleted.Remove(M) - qdel(M) - else - current_account.deleted.Add(M) - current_account.inbox.Remove(M) - current_account.spam.Remove(M) - if(current_message == M) - current_message = null - return 1 - - if(href_list["send"]) - if(!current_account) - return 1 - if((msg_title == "") || (msg_body == "") || (msg_recipient == "")) - error = "Error sending mail: Title or message body is empty!" - return 1 - - var/datum/computer_file/data/email_message/message = new() - message.title = msg_title - message.stored_data = msg_body - message.source = current_account.login - message.attachment = msg_attachment - if(!current_account.send_mail(msg_recipient, message)) - error = "Error sending email: this address doesn't exist." - return 1 - else - error = "Email successfully sent." - clear_message() - return 1 - - if(href_list["set_folder"]) - folder = href_list["set_folder"] - return 1 - - if(href_list["reply"]) - var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["reply"]) - if(!istype(M)) - return 1 - - new_message = TRUE - msg_recipient = M.source - msg_title = "Re: [M.title]" - msg_body = "\[editorbr\]\[editorbr\]\[editorbr\]\[br\]==============================\[br\]\[editorbr\]" - msg_body += "Received by [current_account.login] at [M.timestamp]\[br\]\[editorbr\][M.stored_data]" - return 1 - - if(href_list["view"]) - var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["view"]) - if(istype(M)) - current_message = M - return 1 - - if(href_list["changepassword"]) - var/oldpassword = sanitize(input(user,"Please enter your old password:", "Password Change"), 100) - if(!oldpassword) - return 1 - var/newpassword1 = sanitize(input(user,"Please enter your new password:", "Password Change"), 100) - if(!newpassword1) - return 1 - var/newpassword2 = sanitize(input(user,"Please re-enter your new password:", "Password Change"), 100) - if(!newpassword2) - return 1 - - if(!istype(current_account)) - error = "Please log in before proceeding." - return 1 - - if(current_account.password != oldpassword) - error = "Incorrect original password" - return 1 - - if(newpassword1 != newpassword2) - error = "The entered passwords do not match." - return 1 - - current_account.password = newpassword1 - stored_password = newpassword1 - error = "Your password has been successfully changed!" - return 1 - - // The following entries are Modular Computer framework only, and therefore won't do anything in other cases (like AI View) - - if(href_list["save"]) - // Fully dependant on modular computers here. - var/obj/item/modular_computer/MC = nano_host() - - if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) - error = "Error exporting file. Are you using a functional and NTOS-compliant device?" - return 1 - - var/filename = sanitize(input(user,"Please specify file name:", "Message export"), 100) - if(!filename) - return 1 - - var/datum/computer_file/data/email_message/M = find_message_by_fuid(href_list["save"]) - var/datum/computer_file/data/mail = istype(M) ? M.export() : null - if(!istype(mail)) - return 1 - mail.filename = filename - if(!MC.hard_drive || !MC.hard_drive.store_file(mail)) - error = "Internal I/O error when writing file, the hard drive may be full." - else - error = "Email exported successfully" - return 1 - - if(href_list["addattachment"]) - var/obj/item/modular_computer/MC = nano_host() - msg_attachment = null - - if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) - error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" - return 1 - - var/list/filenames = list() - for(var/datum/computer_file/CF in MC.hard_drive.stored_files) - if(CF.unsendable) - continue - filenames.Add(CF.filename) - var/picked_file = input(user, "Please pick a file to send as attachment (max 32GQ)") as null|anything in filenames - - if(!picked_file) - return 1 - - if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) - error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" - return 1 - - for(var/datum/computer_file/CF in MC.hard_drive.stored_files) - if(CF.unsendable) - continue - if(CF.filename == picked_file) - msg_attachment = CF.clone() - break - if(!istype(msg_attachment)) - msg_attachment = null - error = "Unknown error when uploading attachment." - return 1 - - if(msg_attachment.size > 32) - error = "Error uploading attachment: File exceeds maximal permitted file size of 32GQ." - msg_attachment = null - else - error = "File [msg_attachment.filename].[msg_attachment.filetype] has been successfully uploaded." - return 1 - - if(href_list["downloadattachment"]) - if(!current_account || !current_message || !current_message.attachment) - return 1 - var/obj/item/modular_computer/MC = nano_host() - if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) - error = "Error downloading file. Are you using a functional and NTOSv2-compliant device?" - return 1 - - downloading = current_message.attachment.clone() - download_progress = 0 - return 1 - - if(href_list["canceldownload"]) - downloading = null - download_progress = 0 - return 1 - - if(href_list["remove_attachment"]) - msg_attachment = null - return 1 \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm index 42dfdd622d..13a137993e 100644 --- a/code/modules/modular_computers/file_system/programs/generic/file_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/file_browser.dm @@ -6,202 +6,190 @@ program_key_state = "generic_key" program_menu_icon = "folder-collapsed" size = 8 - requires_ntnet = 0 - available_on_ntnet = 0 - undeletable = 1 - nanomodule_path = /datum/nano_module/program/computer_filemanager/ + requires_ntnet = FALSE + available_on_ntnet = FALSE + undeletable = TRUE + tgui_id = "NtosFileManager" + var/open_file var/error -/datum/computer_file/program/filemanager/Topic(href, href_list) +/datum/computer_file/program/filemanager/tgui_act(action, list/params, datum/tgui/ui) if(..()) - return 1 + return TRUE - if(href_list["PRG_openfile"]) - . = 1 - open_file = href_list["PRG_openfile"] - if(href_list["PRG_newtextfile"]) - . = 1 - var/newname = sanitize(input(usr, "Enter file name or leave blank to cancel:", "File rename")) - if(!newname) - return 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - if(!HDD) - return 1 - var/datum/computer_file/data/F = new/datum/computer_file/data() - F.filename = newname - F.filetype = "TXT" - HDD.store_file(F) - if(href_list["PRG_deletefile"]) - . = 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - if(!HDD) - return 1 - var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_deletefile"]) - if(!file || file.undeletable) - return 1 - HDD.remove_file(file) - if(href_list["PRG_usbdeletefile"]) - . = 1 - var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.portable_drive - if(!RHDD) - return 1 - var/datum/computer_file/file = RHDD.find_file_by_name(href_list["PRG_usbdeletefile"]) - if(!file || file.undeletable) - return 1 - RHDD.remove_file(file) - if(href_list["PRG_closefile"]) - . = 1 - open_file = null - error = null - if(href_list["PRG_clone"]) - . = 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - if(!HDD) - return 1 - var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_clone"]) - if(!F || !istype(F)) - return 1 - var/datum/computer_file/C = F.clone(1) - HDD.store_file(C) - if(href_list["PRG_rename"]) - . = 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - if(!HDD) - return 1 - var/datum/computer_file/file = HDD.find_file_by_name(href_list["PRG_rename"]) - if(!file || !istype(file)) - return 1 - var/newname = sanitize(input(usr, "Enter new file name:", "File rename", file.filename)) - if(file && newname) + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + var/obj/item/weapon/computer_hardware/hard_drive/RHDD = computer.portable_drive + + switch(action) + if("PRG_openfile") + open_file = params["name"] + return TRUE + if("PRG_newtextfile") + if(!HDD) + return + var/newname = sanitize(input(usr, "Enter file name or leave blank to cancel:", "File rename")) + if(!newname) + return + var/datum/computer_file/data/F = new/datum/computer_file/data() + F.filename = newname + F.filetype = "TXT" + HDD.store_file(F) + return TRUE + if("PRG_closefile") + open_file = null + error = null + return TRUE + if("PRG_clone") + if(!HDD) + return + var/datum/computer_file/F = HDD.find_file_by_name(params["name"]) + if(!F || !istype(F)) + return + var/datum/computer_file/C = F.clone(1) + HDD.store_file(C) + return TRUE + if("PRG_edit") + if(!HDD) + return + if(!open_file) + return + var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) + if(!F || !istype(F)) + return + if(F.do_not_edit && (alert("WARNING: This file is not compatible with editor. Editing it may result in permanently corrupted formatting or damaged data consistency. Edit anyway?", "Incompatible File", "No", "Yes") == "No")) + return + + var/oldtext = html_decode(F.stored_data) + oldtext = replacetext(oldtext, "\[br\]", "\n") + + var/newtext = sanitize(replacetext(input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH) + if(!newtext) + return + + if(F) + var/datum/computer_file/data/backup = F.clone() + HDD.remove_file(F) + F.stored_data = newtext + F.calculate_size() + // We can't store the updated file, it's probably too large. Print an error and restore backed up version. + // This is mostly intended to prevent people from losing texts they spent lot of time working on due to running out of space. + // They will be able to copy-paste the text from error screen and store it in notepad or something. + if(!HDD.store_file(F)) + error = "I/O error: Unable to overwrite file. Hard drive is probably full. You may want to backup your changes before closing this window:

[html_decode(F.stored_data)]

" + HDD.store_file(backup) + return TRUE + if("PRG_printfile") + if(!HDD) + return + if(!open_file) + return + var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) + if(!F || !istype(F)) + return + if(!computer.nano_printer) + error = "Missing Hardware: Your computer does not have required hardware to complete this operation." + return + if(!computer.nano_printer.print_text(pencode2html(F.stored_data))) + error = "Hardware error: Printer was unable to print the file. It may be out of paper." + return + return TRUE + if("PRG_deletefile") + if(!HDD) + return + var/datum/computer_file/file = HDD.find_file_by_name(params["name"]) + if(!file || file.undeletable) + return + HDD.remove_file(file) + return TRUE + if("PRG_usbdeletefile") + if(!RHDD) + return + var/datum/computer_file/file = RHDD.find_file_by_name(params["name"]) + if(!file || file.undeletable) + return + RHDD.remove_file(file) + return TRUE + if("PRG_rename") + if(!HDD) + return + var/datum/computer_file/file = HDD.find_file_by_name(params["name"]) + if(!file) + return + var/newname = params["new_name"] + if(!newname) + return file.filename = newname - if(href_list["PRG_edit"]) - . = 1 - if(!open_file) - return 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - if(!HDD) - return 1 - var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) - if(!F || !istype(F)) - return 1 - if(F.do_not_edit && (alert("WARNING: This file is not compatible with editor. Editing it may result in permanently corrupted formatting or damaged data consistency. Edit anyway?", "Incompatible File", "No", "Yes") == "No")) - return 1 + return TRUE + if("PRG_copytousb") + if(!HDD || !RHDD) + return + var/datum/computer_file/F = HDD.find_file_by_name(params["name"]) + if(!F) + return + var/datum/computer_file/C = F.clone(FALSE) + RHDD.store_file(C) + return TRUE + if("PRG_copyfromusb") + if(!HDD || !RHDD) + return + var/datum/computer_file/F = RHDD.find_file_by_name(params["name"]) + if(!F || !istype(F)) + return + var/datum/computer_file/C = F.clone(FALSE) + HDD.store_file(C) + return TRUE - var/oldtext = html_decode(F.stored_data) - oldtext = replacetext(oldtext, "\[br\]", "\n") +/datum/computer_file/program/filemanager/tgui_data(mob/user) + var/list/data = get_header_data() - var/newtext = sanitize(replacetext(input(usr, "Editing file [open_file]. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH) - if(!newtext) - return + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive - if(F) - var/datum/computer_file/data/backup = F.clone() - HDD.remove_file(F) - F.stored_data = newtext - F.calculate_size() - // We can't store the updated file, it's probably too large. Print an error and restore backed up version. - // This is mostly intended to prevent people from losing texts they spent lot of time working on due to running out of space. - // They will be able to copy-paste the text from error screen and store it in notepad or something. - if(!HDD.store_file(F)) - error = "I/O error: Unable to overwrite file. Hard drive is probably full. You may want to backup your changes before closing this window:

[html_decode(F.stored_data)]

" - HDD.store_file(backup) - if(href_list["PRG_printfile"]) - . = 1 - if(!open_file) - return 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - if(!HDD) - return 1 - var/datum/computer_file/data/F = HDD.find_file_by_name(open_file) - if(!F || !istype(F)) - return 1 - if(!computer.nano_printer) - error = "Missing Hardware: Your computer does not have required hardware to complete this operation." - return 1 - if(!computer.nano_printer.print_text(pencode2html(F.stored_data))) - error = "Hardware error: Printer was unable to print the file. It may be out of paper." - return 1 - if(href_list["PRG_copytousb"]) - . = 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive - if(!HDD || !RHDD) - return 1 - var/datum/computer_file/F = HDD.find_file_by_name(href_list["PRG_copytousb"]) - if(!F || !istype(F)) - return 1 - var/datum/computer_file/C = F.clone(0) - RHDD.store_file(C) - if(href_list["PRG_copyfromusb"]) - . = 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive - if(!HDD || !RHDD) - return 1 - var/datum/computer_file/F = RHDD.find_file_by_name(href_list["PRG_copyfromusb"]) - if(!F || !istype(F)) - return 1 - var/datum/computer_file/C = F.clone(0) - HDD.store_file(C) - if(.) - SSnanoui.update_uis(NM) + data["error"] = null + if(error) + data["error"] = error + if(!computer || !HDD) + data["error"] = "I/O ERROR: Unable to access hard drive." + + data["filedata"] = null + data["filename"] = null + data["files"] = list() + data["usbconnected"] = FALSE + data["usbfiles"] = list() -/datum/nano_module/program/computer_filemanager - name = "NTOS File Manager" - -/datum/nano_module/program/computer_filemanager/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() - var/datum/computer_file/program/filemanager/PRG - PRG = program - - var/obj/item/weapon/computer_hardware/hard_drive/HDD - var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD - if(PRG.error) - data["error"] = PRG.error - if(PRG.open_file) + if(open_file) var/datum/computer_file/data/file - if(!PRG.computer || !PRG.computer.hard_drive) + if(!computer || !computer.hard_drive) data["error"] = "I/O ERROR: Unable to access hard drive." else - HDD = PRG.computer.hard_drive - file = HDD.find_file_by_name(PRG.open_file) + file = HDD.find_file_by_name(open_file) if(!istype(file)) data["error"] = "I/O ERROR: Unable to open file." else data["filedata"] = pencode2html(file.stored_data) data["filename"] = "[file.filename].[file.filetype]" else - if(!PRG.computer || !PRG.computer.hard_drive) - data["error"] = "I/O ERROR: Unable to access hard drive." - else - HDD = PRG.computer.hard_drive - RHDD = PRG.computer.portable_drive - var/list/files[0] - for(var/datum/computer_file/F in HDD.stored_files) - files.Add(list(list( + var/list/files = list() + for(var/datum/computer_file/F in HDD.stored_files) + files += list(list( + "name" = F.filename, + "type" = F.filetype, + "size" = F.size, + "undeletable" = F.undeletable + )) + data["files"] = files + if(RHDD) + data["usbconnected"] = TRUE + var/list/usbfiles = list() + for(var/datum/computer_file/F in RHDD.stored_files) + usbfiles += list(list( "name" = F.filename, "type" = F.filetype, "size" = F.size, "undeletable" = F.undeletable - ))) - data["files"] = files - if(RHDD) - data["usbconnected"] = 1 - var/list/usbfiles[0] - for(var/datum/computer_file/F in RHDD.stored_files) - usbfiles.Add(list(list( - "name" = F.filename, - "type" = F.filetype, - "size" = F.size, - "undeletable" = F.undeletable - ))) - data["usbfiles"] = usbfiles + )) + data["usbfiles"] = usbfiles - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "file_manager.tmpl", "NTOS File Manager", 575, 700, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() \ No newline at end of file + return data diff --git a/code/modules/modular_computers/file_system/programs/generic/game.dm b/code/modules/modular_computers/file_system/programs/generic/game.dm index b4c0688c29..4d21095f9d 100644 --- a/code/modules/modular_computers/file_system/programs/generic/game.dm +++ b/code/modules/modular_computers/file_system/programs/generic/game.dm @@ -1,152 +1,188 @@ // This file is used as a reference for Modular Computers Development guide on the wiki. It contains a lot of excess comments, as it is intended as explanation // for someone who may not be as experienced in coding. When making changes, please try to keep it this way. -// An actual program definition. /datum/computer_file/program/game - filename = "arcadec" // File name, as shown in the file browser program. - filedesc = "Unknown Game" // User-Friendly name. In this case, we will generate a random name in constructor. - program_icon_state = "game" // Icon state of this program's screen. - program_menu_icon = "script" - extended_desc = "Fun for the whole family! Probably not an AAA title, but at least you can download it on the corporate network.." // A nice description. - size = 5 // Size in GQ. Integers only. Smaller sizes should be used for utility/low use programs (like this one), while large sizes are for important programs. - requires_ntnet = 0 // This particular program does not require NTNet network conectivity... - available_on_ntnet = 1 // ... but we want it to be available for download. - nanomodule_path = /datum/nano_module/arcade_classic/ // Path of relevant nano module. The nano module is defined further in the file. - var/picked_enemy_name + filename = "dsarcade" // File name, as shown in the file browser program. + filedesc = "Donksoft Micro Arcade" // User-Friendly name. + program_icon_state = "arcade" // Icon state of this program's screen. + extended_desc = "This is a port of the classic game 'Outbomb Cuban Pete', redesigned to run on tablets; Now with thrilling graphics and chilling storytelling." // A nice description. + size = 6 // Size in GQ. Integers only. Smaller sizes should be used for utility/low use programs (like this one), while large sizes are for important programs. + requires_ntnet = FALSE // This particular program does not require NTNet network conectivity... + available_on_ntnet = TRUE // ... but we want it to be available for download. + tgui_id = "NtosArcade" // Path of relevant tgui template.js file. -// Blatantly stolen and shortened version from arcade machines. Generates a random enemy name -/datum/computer_file/program/game/proc/random_enemy_name() - var/name_part1 = pick("the Automatic ", "Farmer ", "Lord ", "Professor ", "the Cuban ", "the Evil ", "the Dread King ", "the Space ", "Lord ", "the Great ", "Duke ", "General ") - var/name_part2 = pick("Melonoid", "Murdertron", "Sorcerer", "Ruin", "Jeff", "Ectoplasm", "Crushulon", "Uhangoid", "Vhakoid", "Peteoid", "Slime", "Lizard Man", "Unicorn") - return "[name_part1] [name_part2]" + ///Returns TRUE if the game is being played. + var/game_active = TRUE + ///This disables buttom actions from having any impact if TRUE. Resets to FALSE when the player is allowed to make an action again. + var/pause_state = FALSE + var/boss_hp = 45 + var/boss_mp = 15 + var/player_hp = 30 + var/player_mp = 10 + var/ticket_count = 0 + ///Shows what text is shown on the app, usually showing the log of combat actions taken by the player. + var/heads_up = "Nanotrasen says, winners make us money." + var/boss_name = "Cuban Pete's Minion" + ///Determines which boss image to use on the UI. + var/boss_id = 1 -// When the program is first created, we generate a new enemy name and name ourselves accordingly. -/datum/computer_file/program/game/New() - ..() - picked_enemy_name = random_enemy_name() - filedesc = "Defeat [picked_enemy_name]" +// This is the primary game loop, which handles the logic of being defeated or winning. +/datum/computer_file/program/game/proc/game_check(mob/user) + sleep(5) + if(boss_hp <= 0) + heads_up = "You have crushed [boss_name]! Rejoice!" + playsound(computer.loc, 'sound/arcade/win.ogg', 50, TRUE, extrarange = -3, falloff = 10) + game_active = FALSE + program_icon_state = "arcade_off" + if(istype(computer)) + computer.update_icon() + ticket_count += 1 + // user?.mind?.adjust_experience(/datum/skill/gaming, 50) + sleep(10) + else if(player_hp <= 0 || player_mp <= 0) + heads_up = "You have been defeated... how will the station survive?" + playsound(computer.loc, 'sound/arcade/lose.ogg', 50, TRUE, extrarange = -3, falloff = 10) + game_active = FALSE + program_icon_state = "arcade_off" + if(istype(computer)) + computer.update_icon() + // user?.mind?.adjust_experience(/datum/skill/gaming, 10) + sleep(10) -// Important in order to ensure that copied versions will have the same enemy name. -/datum/computer_file/program/game/clone() - var/datum/computer_file/program/game/G = ..() - G.picked_enemy_name = picked_enemy_name - return G - -// When running the program, we also want to pass our enemy name to the nano module. -/datum/computer_file/program/game/run_program() - . = ..() - if(. && NM) - var/datum/nano_module/arcade_classic/NMC = NM - NMC.enemy_name = picked_enemy_name - - -// Nano module the program uses. -// This can be either /datum/nano_module/ or /datum/nano_module/program. The latter is intended for nano modules that are suposed to be exclusively used with modular computers, -// and should generally not be used, as such nano modules are hard to use on other places. -/datum/nano_module/arcade_classic/ - name = "Classic Arcade" - var/player_mana // Various variables specific to the nano module. In this case, the nano module is a simple arcade game, so the variables store health and other stats. - var/player_health - var/enemy_mana - var/enemy_health - var/enemy_name = "Greytide Horde" - var/gameover - var/information - -/datum/nano_module/arcade_classic/New() - ..() - new_game() - -// ui_interact handles transfer of data to NanoUI. Keep in mind that data you pass from here is actually sent to the client. In other words, don't send anything you don't want a client -// to see, and don't send unnecessarily large amounts of data (due to laginess). -/datum/nano_module/arcade_classic/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() - - data["player_health"] = player_health - data["player_mana"] = player_mana - data["enemy_health"] = enemy_health - data["enemy_mana"] = enemy_mana - data["enemy_name"] = enemy_name - data["gameover"] = gameover - data["information"] = information - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "arcade_classic.tmpl", "Defeat [enemy_name]", 500, 350, state = state) - if(host.update_layout()) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - -// Three helper procs i've created. These are unique to this particular nano module. If you are creating your own nano module, you'll most likely create similar procs too. -/datum/nano_module/arcade_classic/proc/enemy_play() - if((enemy_mana < 5) && prob(60)) - var/steal = rand(2, 3) - player_mana -= steal - enemy_mana += steal - information += "[enemy_name] steals [steal] of your power!" - else if((enemy_health < 15) && (enemy_mana > 3) && prob(80)) - var/healamt = min(rand(3, 5), enemy_mana) - enemy_mana -= healamt - enemy_health += healamt - information += "[enemy_name] heals for [healamt] health!" +// This handles the boss "AI". +/datum/computer_file/program/game/proc/enemy_check(mob/user) + var/boss_attackamt = 0 //Spam protection from boss attacks as well. + var/boss_mpamt = 0 + var/bossheal = 0 + if(pause_state == TRUE) + boss_attackamt = rand(3,6) + boss_mpamt = rand (2,4) + bossheal = rand (4,6) + if(game_active == FALSE) + return + if(boss_mp <= 5) + heads_up = "[boss_mpamt] magic power has been stolen from you!" + playsound(computer.loc, 'sound/arcade/steal.ogg', 50, TRUE, extrarange = -3, falloff = 10) + player_mp -= boss_mpamt + boss_mp += boss_mpamt + else if(boss_mp > 5 && boss_hp <12) + heads_up = "[boss_name] heals for [bossheal] health!" + playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3, falloff = 10) + boss_hp += bossheal + boss_mp -= boss_mpamt else - var/dam = rand(3,6) - player_health -= dam - information += "[enemy_name] attacks for [dam] damage!" + heads_up = "[boss_name] attacks you for [boss_attackamt] damage!" + playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3, falloff = 10) + player_hp -= boss_attackamt -/datum/nano_module/arcade_classic/proc/check_gameover() - if((player_health <= 0) || player_mana <= 0) - if(enemy_health <= 0) - information += "You have defeated [enemy_name], but you have died in the fight!" - else - information += "You have been defeated by [enemy_name]!" - gameover = 1 + pause_state = FALSE + game_check() + +/** + * UI assets define a list of asset datums to be sent with the UI. + * In this case, it's a bunch of cute enemy sprites. + */ +/datum/computer_file/program/game/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/arcade), + ) + +/** + * This provides all of the relevant data to the UI in a list(). + */ +/datum/computer_file/program/game/tgui_data(mob/user) + var/list/data = get_header_data() + data["Hitpoints"] = boss_hp + data["PlayerHitpoints"] = player_hp + data["PlayerMP"] = player_mp + data["TicketCount"] = ticket_count + data["GameActive"] = game_active + data["PauseState"] = pause_state + data["Status"] = heads_up + data["BossID"] = "boss[boss_id].gif" + return data + +/** + * This is tgui's replacement for Topic(). It handles any user input from the UI. + */ +/datum/computer_file/program/game/tgui_act(action, list/params) + if(..()) // Always call parent in tgui_act, it handles making sure the user is allowed to interact with the UI. return TRUE - else if(enemy_health <= 0) - gameover = 1 - information += "Congratulations! You have defeated [enemy_name]!" - return TRUE - return FALSE -/datum/nano_module/arcade_classic/proc/new_game() - player_mana = 10 - player_health = 30 - enemy_mana = 20 - enemy_health = 45 - gameover = FALSE - information = "A new game has started!" + var/obj/item/weapon/computer_hardware/nano_printer/printer + if(computer) + printer = computer.nano_printer - - -/datum/nano_module/arcade_classic/Topic(href, href_list) - if(..()) // Always begin your Topic() calls with a parent call! - return 1 - if(href_list["new_game"]) - new_game() - return 1 // Returning 1 (TRUE) in Topic automatically handles UI updates. - if(gameover) // If the game has already ended, we don't want the following three topic calls to be processed at all. - return 1 // Instead of adding checks into each of those three, we can easily add this one check here to reduce on code copy-paste. - if(href_list["attack"]) - var/damage = rand(2, 6) - information = "You attack for [damage] damage." - enemy_health -= damage - enemy_play() - check_gameover() - return 1 - if(href_list["heal"]) - var/healfor = rand(6, 8) - var/cost = rand(1, 3) - information = "You heal yourself for [healfor] damage, using [cost] energy in the process." - player_health += healfor - player_mana -= cost - enemy_play() - check_gameover() - return 1 - if(href_list["regain_mana"]) - var/regen = rand(4, 7) - information = "You rest of a while, regaining [regen] energy." - player_mana += regen - enemy_play() - check_gameover() - return 1 \ No newline at end of file + // var/gamerSkillLevel = usr.mind?.get_skill_level(/datum/skill/gaming) + // var/gamerSkill = usr.mind?.get_skill_modifier(/datum/skill/gaming, SKILL_RANDS_MODIFIER) + switch(action) + if("Attack") + var/attackamt = 0 //Spam prevention. + if(pause_state == FALSE) + attackamt = rand(2,6) // + rand(0, gamerSkill) + pause_state = TRUE + heads_up = "You attack for [attackamt] damage." + playsound(computer.loc, 'sound/arcade/hit.ogg', 50, TRUE, extrarange = -3, falloff = 10) + boss_hp -= attackamt + sleep(10) + game_check() + enemy_check() + return TRUE + if("Heal") + var/healamt = 0 //More Spam Prevention. + var/healcost = 0 + if(pause_state == FALSE) + healamt = rand(6,8) // + rand(0, gamerSkill) + var/maxPointCost = 3 + // if(gamerSkillLevel >= SKILL_LEVEL_JOURNEYMAN) + // maxPointCost = 2 + healcost = rand(1, maxPointCost) + pause_state = TRUE + heads_up = "You heal for [healamt] damage." + playsound(computer.loc, 'sound/arcade/heal.ogg', 50, TRUE, extrarange = -3, falloff = 10) + player_hp += healamt + player_mp -= healcost + sleep(10) + game_check() + enemy_check() + return TRUE + if("Recharge_Power") + var/rechargeamt = 0 //As above. + if(pause_state == FALSE) + rechargeamt = rand(4,7) // + rand(0, gamerSkill) + pause_state = TRUE + heads_up = "You regain [rechargeamt] magic power." + playsound(computer.loc, 'sound/arcade/mana.ogg', 50, TRUE, extrarange = -3, falloff = 10) + player_mp += rechargeamt + sleep(10) + game_check() + enemy_check() + return TRUE + if("Dispense_Tickets") + if(!printer) + to_chat(usr, "Hardware error: A printer is required to redeem tickets.") + return + if(printer.stored_paper <= 0) + to_chat(usr, "Hardware error: Printer is out of paper.") + return + else + computer.visible_message("\The [computer] prints out paper.") + if(ticket_count >= 1) + new /obj/item/stack/arcadeticket((get_turf(computer)), 1) + to_chat(usr, "[src] dispenses a ticket!") + ticket_count -= 1 + printer.stored_paper -= 1 + else + to_chat(usr, "You don't have any stored tickets!") + return TRUE + if("Start_Game") + game_active = TRUE + boss_hp = 45 + player_hp = 30 + player_mp = 10 + heads_up = "You stand before [boss_name]! Prepare for battle!" + program_icon_state = "arcade" + boss_id = rand(1,6) + pause_state = FALSE + if(istype(computer)) + computer.update_icon() diff --git a/code/modules/modular_computers/file_system/programs/generic/news_browser.dm b/code/modules/modular_computers/file_system/programs/generic/news_browser.dm index 1d4da6d4d5..e72e30fd38 100644 --- a/code/modules/modular_computers/file_system/programs/generic/news_browser.dm +++ b/code/modules/modular_computers/file_system/programs/generic/news_browser.dm @@ -6,16 +6,17 @@ program_key_state = "generic_key" program_menu_icon = "contact" size = 4 - requires_ntnet = 1 - available_on_ntnet = 1 + requires_ntnet = TRUE + available_on_ntnet = TRUE + + tgui_id = "NtosNewsBrowser" - nanomodule_path = /datum/nano_module/program/computer_newsbrowser/ var/datum/computer_file/data/news_article/loaded_article var/download_progress = 0 var/download_netspeed = 0 - var/downloading = 0 + var/downloading = FALSE var/message = "" - var/show_archived = 0 + var/show_archived = FALSE /datum/computer_file/program/newsbrowser/process_tick() if(!downloading) @@ -33,86 +34,31 @@ if(download_progress >= loaded_article.size) downloading = 0 requires_ntnet = 0 // Turn off NTNet requirement as we already loaded the file into local memory. - SSnanoui.update_uis(NM) + SStgui.update_uis(src) -/datum/computer_file/program/newsbrowser/kill_program() - ..() - requires_ntnet = 1 - loaded_article = null - download_progress = 0 - downloading = 0 - show_archived = 0 +/datum/computer_file/program/newsbrowser/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = get_header_data() -/datum/computer_file/program/newsbrowser/Topic(href, href_list) - if(..()) - return 1 - if(href_list["PRG_openarticle"]) - . = 1 - if(downloading || loaded_article) - return 1 - - for(var/datum/computer_file/data/news_article/N in ntnet_global.available_news) - if(N.uid == text2num(href_list["PRG_openarticle"])) - loaded_article = N.clone() - downloading = 1 - break - if(href_list["PRG_reset"]) - . = 1 - downloading = 0 - download_progress = 0 - requires_ntnet = 1 - loaded_article = null - if(href_list["PRG_clearmessage"]) - . = 1 - message = "" - if(href_list["PRG_savearticle"]) - . = 1 - if(downloading || !loaded_article) - return - - var/savename = sanitize(input(usr, "Enter file name or leave blank to cancel:", "Save article", loaded_article.filename)) - if(!savename) - return 1 - var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive - if(!HDD) - return 1 - var/datum/computer_file/data/news_article/N = loaded_article.clone() - N.filename = savename - HDD.store_file(N) - if(href_list["PRG_toggle_archived"]) - . = 1 - show_archived = !show_archived - if(.) - SSnanoui.update_uis(NM) - - -/datum/nano_module/program/computer_newsbrowser - name = "NTNet/ExoNet News Browser" - -/datum/nano_module/program/computer_newsbrowser/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - - var/datum/computer_file/program/newsbrowser/PRG - var/list/data = list() - if(program) - data = program.get_header_data() - PRG = program - else - return - - data["message"] = PRG.message - if(PRG.loaded_article && !PRG.downloading) // Viewing an article. - data["title"] = PRG.loaded_article.filename - data["cover"] = PRG.loaded_article.cover - data["article"] = PRG.loaded_article.stored_data - else if(PRG.downloading) // Downloading an article. - data["download_running"] = 1 - data["download_progress"] = PRG.download_progress - data["download_maxprogress"] = PRG.loaded_article.size - data["download_rate"] = PRG.download_netspeed + var/list/all_articles = list() + data["message"] = message + data["showing_archived"] = show_archived + data["download"] = null + data["article"] = null + if(loaded_article && !downloading) // Viewing an article. + data["article"] = list( + "title" = loaded_article.filename, + "cover" = loaded_article.cover, + "content" = loaded_article.stored_data, + ) + else if(downloading) // Downloading an article. + data["download"] = list( + "download_progress" = download_progress, + "download_maxprogress" = loaded_article.size, + "download_rate" = download_netspeed + ) else // Viewing list of articles - var/list/all_articles[0] for(var/datum/computer_file/data/news_article/F in ntnet_global.available_news) - if(!PRG.show_archived && F.archived) + if(!show_archived && F.archived) continue all_articles.Add(list(list( "name" = F.filename, @@ -120,12 +66,56 @@ "uid" = F.uid, "archived" = F.archived ))) - data["all_articles"] = all_articles - data["showing_archived"] = PRG.show_archived + data["all_articles"] = all_articles + + return data + +/datum/computer_file/program/newsbrowser/kill_program() + ..() + requires_ntnet = TRUE + loaded_article = null + download_progress = 0 + downloading = FALSE + show_archived = FALSE + +/datum/computer_file/program/newsbrowser/tgui_act(action, list/params, datum/tgui/ui) + if(..()) + return TRUE + switch(action) + if("PRG_openarticle") + . = TRUE + if(downloading || loaded_article) + return TRUE + + for(var/datum/computer_file/data/news_article/N in ntnet_global.available_news) + if(N.uid == text2num(params["uid"])) + loaded_article = N.clone() + downloading = 1 + break + if("PRG_reset") + . = TRUE + downloading = 0 + download_progress = 0 + requires_ntnet = 1 + loaded_article = null + if("PRG_clearmessage") + . = TRUE + message = "" + if("PRG_savearticle") + . = TRUE + if(downloading || !loaded_article) + return + + var/savename = sanitize(input(usr, "Enter file name or leave blank to cancel:", "Save article", loaded_article.filename)) + if(!savename) + return TRUE + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + if(!HDD) + return TRUE + var/datum/computer_file/data/news_article/N = loaded_article.clone() + N.filename = savename + HDD.store_file(N) + if("PRG_toggle_archived") + . = TRUE + show_archived = !show_archived - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "news_browser.tmpl", "NTNet/ExoNet News Browser", 575, 750, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() diff --git a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm index a0b2194168..14e2343e66 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntdownloader.dm @@ -5,29 +5,26 @@ program_key_state = "generic_key" program_menu_icon = "arrowthickstop-1-s" extended_desc = "This program allows downloads of software from official NT repositories" - unsendable = 1 - undeletable = 1 + unsendable = TRUE + undeletable = TRUE size = 4 - requires_ntnet = 1 + requires_ntnet = TRUE requires_ntnet_feature = NTNET_SOFTWAREDOWNLOAD - available_on_ntnet = 0 - nanomodule_path = /datum/nano_module/program/computer_ntnetdownload/ + available_on_ntnet = FALSE ui_header = "downloader_finished.gif" + tgui_id = "NtosNetDownloader" + var/datum/computer_file/program/downloaded_file = null var/hacked_download = 0 var/download_completion = 0 //GQ of downloaded data. var/download_netspeed = 0 var/downloaderror = "" + var/obj/item/modular_computer/my_computer = null var/list/downloads_queue[0] /datum/computer_file/program/ntnetdownload/kill_program() ..() - downloaded_file = null - download_completion = 0 - download_netspeed = 0 - downloaderror = "" - ui_header = "downloader_finished.gif" - + abort_file_download() /datum/computer_file/program/ntnetdownload/proc/begin_file_download(var/filename) if(downloaded_file) @@ -108,93 +105,85 @@ download_netspeed = NTNETSPEED_ETHERNET download_completion += download_netspeed -/datum/computer_file/program/ntnetdownload/Topic(href, href_list) +/datum/computer_file/program/ntnetdownload/tgui_act(action, params) if(..()) - return 1 - if(href_list["PRG_downloadfile"]) - if(!downloaded_file) - begin_file_download(href_list["PRG_downloadfile"]) - else if(check_file_download(href_list["PRG_downloadfile"]) && !downloads_queue.Find(href_list["PRG_downloadfile"]) && downloaded_file.filename != href_list["PRG_downloadfile"]) - downloads_queue += href_list["PRG_downloadfile"] - return 1 - if(href_list["PRG_removequeued"]) - downloads_queue.Remove(href_list["PRG_removequeued"]) - return 1 - if(href_list["PRG_reseterror"]) - if(downloaderror) - download_completion = 0 - download_netspeed = 0 - downloaded_file = null - downloaderror = "" - return 1 - return 0 - -/datum/nano_module/program/computer_ntnetdownload - name = "Network Downloader" - var/obj/item/modular_computer/my_computer = null - -/datum/nano_module/program/computer_ntnetdownload/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - if(program) - my_computer = program.computer + return TRUE + switch(action) + if("PRG_downloadfile") + if(!downloaded_file) + begin_file_download(params["filename"]) + else if(check_file_download(params["filename"]) && !downloads_queue.Find(params["filename"]) && downloaded_file.filename != params["filename"]) + downloads_queue += params["filename"] + return TRUE + if("PRG_removequeued") + downloads_queue.Remove(params["filename"]) + return TRUE + if("PRG_reseterror") + if(downloaderror) + download_completion = 0 + download_netspeed = 0 + downloaded_file = null + downloaderror = "" + return TRUE + return FALSE +/datum/computer_file/program/ntnetdownload/tgui_data(mob/user) + my_computer = computer if(!istype(my_computer)) return - var/list/data = list() - var/datum/computer_file/program/ntnetdownload/prog = program - // For now limited to execution by the downloader program - if(!prog || !istype(prog)) - return - if(program) - data = program.get_header_data() + var/list/data = get_header_data() - // This IF cuts on data transferred to client, so i guess it's worth it. - if(prog.downloaderror) // Download errored. Wait until user resets the program. - data["error"] = prog.downloaderror - if(prog.downloaded_file) // Download running. Wait please.. - data["downloadname"] = prog.downloaded_file.filename - data["downloaddesc"] = prog.downloaded_file.filedesc - data["downloadsize"] = prog.downloaded_file.size - data["downloadspeed"] = prog.download_netspeed - data["downloadcompletion"] = round(prog.download_completion, 0.1) + data["downloading"] = !!downloaded_file + data["error"] = downloaderror || FALSE + + if(downloaded_file) // Download running. Wait please.. + data["downloadname"] = downloaded_file.filename + data["downloaddesc"] = downloaded_file.filedesc + data["downloadsize"] = downloaded_file.size + data["downloadspeed"] = download_netspeed + data["downloadcompletion"] = round(download_completion, 0.1) data["disk_size"] = my_computer.hard_drive.max_capacity data["disk_used"] = my_computer.hard_drive.used_capacity var/list/all_entries[0] for(var/datum/computer_file/program/P in ntnet_global.available_station_software) // Only those programs our user can run will show in the list - if(!P.can_run(user) && P.requires_access_to_download) + if(!P.can_run(user) && P.requires_access_to_download || my_computer.hard_drive.find_file_by_name(P.filename)) continue all_entries.Add(list(list( - "filename" = P.filename, - "filedesc" = P.filedesc, - "fileinfo" = P.extended_desc, - "size" = P.size, - "icon" = P.program_menu_icon - ))) - data["hackedavailable"] = 0 - if(prog.computer_emagged) // If we are running on emagged computer we have access to some "bonus" software - var/list/hacked_programs[0] - for(var/datum/computer_file/program/P in ntnet_global.available_antag_software) - data["hackedavailable"] = 1 - hacked_programs.Add(list(list( "filename" = P.filename, "filedesc" = P.filedesc, "fileinfo" = P.extended_desc, + "compatibility" = check_compatibility(P), "size" = P.size, "icon" = P.program_menu_icon + ))) + data["hackedavailable"] = FALSE + if(computer_emagged) // If we are running on emagged computer we have access to some "bonus" software + var/list/hacked_programs[0] + for(var/datum/computer_file/program/P in ntnet_global.available_antag_software) + if(my_computer.hard_drive.find_file_by_name(P.filename)) + continue + data["hackedavailable"] = TRUE + hacked_programs.Add(list(list( + "filename" = P.filename, + "filedesc" = P.filedesc, + "fileinfo" = P.extended_desc, + "compatibility" = check_compatibility(P), + "size" = P.size, + "icon" = P.program_menu_icon ))) data["hacked_programs"] = hacked_programs data["downloadable_programs"] = all_entries + data["downloads_queue"] = downloads_queue - if(prog.downloads_queue.len > 0) - data["downloads_queue"] = prog.downloads_queue + return data - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "ntnet_downloader.tmpl", "NTNet Download Program", 575, 700, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) +/datum/computer_file/program/ntnetdownload/proc/check_compatibility(datum/computer_file/program/P) + var/hardflag = computer.hardware_flag + + if(P && P.is_supported_by_hardware(hardflag,0)) + return "Compatible" + return "Incompatible!" diff --git a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm index 6c67c02c0d..f2466213f3 100644 --- a/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm +++ b/code/modules/modular_computers/file_system/programs/generic/ntnrc_client.dm @@ -11,222 +11,220 @@ network_destination = "NTNRC server" ui_header = "ntnrc_idle.gif" available_on_ntnet = 1 - nanomodule_path = /datum/nano_module/program/computer_chatclient/ - var/last_message = null // Used to generate the toolbar icon + tgui_id = "NtosNetChat" + var/last_message // Used to generate the toolbar icon var/username - var/datum/ntnet_conversation/channel = null - var/operator_mode = 0 // Channel operator mode - var/netadmin_mode = 0 // Administrator mode (invisible to other users + bypasses passwords) + var/active_channel + var/list/channel_history = list() + var/operator_mode = FALSE // Channel operator mode + var/netadmin_mode = FALSE // Administrator mode (invisible to other users + bypasses passwords) /datum/computer_file/program/chatclient/New() username = "DefaultUser[rand(100, 999)]" -/datum/computer_file/program/chatclient/Topic(href, href_list) +/datum/computer_file/program/chatclient/tgui_act(action, params) if(..()) - return 1 + return - if(href_list["PRG_speak"]) - . = 1 - if(!channel) - return 1 - var/mob/living/user = usr - var/message = sanitize(input(user, "Enter message or leave blank to cancel: "), 512) - if(!message || !channel) - return - channel.add_message(message, username) + var/datum/ntnet_conversation/channel = ntnet_global.get_chat_channel_by_id(active_channel) + var/authed = FALSE + if(channel && ((channel.operator == src) || netadmin_mode)) + authed = TRUE + switch(action) + if("PRG_speak") + if(!channel || isnull(active_channel)) + return + var/message = reject_bad_text(params["message"]) + if(!message) + return + if(channel.password && !(src in channel.clients)) + if(channel.password == message) + channel.add_client(src) + return TRUE - if(href_list["PRG_joinchannel"]) - . = 1 - var/datum/ntnet_conversation/C - for(var/datum/ntnet_conversation/chan in ntnet_global.chat_channels) - if(chan.id == text2num(href_list["PRG_joinchannel"])) - C = chan - break + channel.add_message(message, username) + // var/mob/living/user = usr + // user.log_talk(message, LOG_CHAT, tag="as [username] to channel [channel.title]") + return TRUE + if("PRG_joinchannel") + var/new_target = text2num(params["id"]) + if(isnull(new_target) || new_target == active_channel) + return - if(!C) - return 1 + if(netadmin_mode) + active_channel = new_target // Bypasses normal leave/join and passwords. Technically makes the user invisible to others. + return TRUE - if(netadmin_mode) - channel = C // Bypasses normal leave/join and passwords. Technically makes the user invisible to others. - return 1 - - if(C.password) + active_channel = new_target + channel = ntnet_global.get_chat_channel_by_id(new_target) + if(!(src in channel.clients) && !channel.password) + channel.add_client(src) + return TRUE + if("PRG_leavechannel") + if(channel) + channel.remove_client(src) + active_channel = null + return TRUE + if("PRG_newchannel") + var/channel_title = reject_bad_text(params["new_channel_name"]) + if(!channel_title) + return + var/datum/ntnet_conversation/C = new /datum/ntnet_conversation() + C.add_client(src) + C.operator = src + C.title = channel_title + active_channel = C.id + return TRUE + if("PRG_toggleadmin") + if(netadmin_mode) + netadmin_mode = FALSE + if(channel) + channel.remove_client(src) // We shouldn't be in channel's user list, but just in case... + return TRUE var/mob/living/user = usr - var/password = sanitize(input(user,"Access Denied. Enter password:")) - if(C && (password == C.password)) - C.add_client(src) - channel = C - return 1 - C.add_client(src) - channel = C - if(href_list["PRG_leavechannel"]) - . = 1 - if(channel) - channel.remove_client(src) - channel = null - if(href_list["PRG_newchannel"]) - . = 1 - var/mob/living/user = usr - var/channel_title = sanitizeSafe(input(user,"Enter channel name or leave blank to cancel:"), 64) - if(!channel_title) - return - var/datum/ntnet_conversation/C = new/datum/ntnet_conversation() - C.add_client(src) - C.operator = src - channel = C - C.title = channel_title - if(href_list["PRG_toggleadmin"]) - . = 1 - if(netadmin_mode) - netadmin_mode = 0 - if(channel) - channel.remove_client(src) // We shouldn't be in channel's user list, but just in case... - channel = null - return 1 - var/mob/living/user = usr - if(can_run(usr, 1, access_network)) - if(channel) - var/response = alert(user, "Really engage admin-mode? You will be disconnected from your current channel!", "NTNRC Admin mode", "Yes", "No") - if(response == "Yes") - if(channel) - channel.remove_client(src) - channel = null - else - return - netadmin_mode = 1 - if(href_list["PRG_changename"]) - . = 1 - var/mob/living/user = usr - var/newname = sanitize(input(user,"Enter new nickname or leave blank to cancel:"), 20) - if(!newname) - return 1 - if(channel) - channel.add_status_message("[username] is now known as [newname].") - username = newname + if(can_run(user, TRUE, access_network)) + for(var/C in ntnet_global.chat_channels) + var/datum/ntnet_conversation/chan = C + chan.remove_client(src) + netadmin_mode = TRUE + return TRUE + if("PRG_changename") + var/newname = sanitize(params["new_name"]) + if(!newname) + return + for(var/C in ntnet_global.chat_channels) + var/datum/ntnet_conversation/chan = C + if(src in chan.clients) + chan.add_status_message("[username] is now known as [newname].") + username = newname + return TRUE + if("PRG_savelog") + if(!channel) + return + var/logname = stripped_input(params["log_name"]) + if(!logname) + return + var/datum/computer_file/data/logfile = new /datum/computer_file/data/logfile() + // Now we will generate HTML-compliant file that can actually be viewed/printed. + logfile.filename = logname + logfile.stored_data = "\[b\]Logfile dump from NTNRC channel [channel.title]\[/b\]\[BR\]" + for(var/logstring in channel.messages) + logfile.stored_data = "[logfile.stored_data][logstring]\[BR\]" + logfile.stored_data = "[logfile.stored_data]\[b\]Logfile dump completed.\[/b\]" + logfile.calculate_size() + if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(logfile)) + if(!computer) + // This program shouldn't even be runnable without computer. + CRASH("Var computer is null!") + if(!computer.hard_drive) + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") + else // In 99.9% cases this will mean our HDD is full + computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") + return TRUE + if("PRG_renamechannel") + if(!authed) + return + var/newname = reject_bad_text(params["new_name"]) + if(!newname || !channel) + return + channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.") + channel.title = newname + return TRUE + if("PRG_deletechannel") + if(authed) + qdel(channel) + active_channel = null + return TRUE + if("PRG_setpassword") + if(!authed) + return - if(href_list["PRG_savelog"]) - . = 1 - if(!channel) - return - var/mob/living/user = usr - var/logname = input(user,"Enter desired logfile name (.log) or leave blank to cancel:") - if(!logname || !channel) - return 1 - var/datum/computer_file/data/logfile = new/datum/computer_file/data/logfile() - // Now we will generate HTML-compliant file that can actually be viewed/printed. - logfile.filename = logname - logfile.stored_data = "\[b\]Logfile dump from NTNRC channel [channel.title]\[/b\]\[BR\]" - for(var/logstring in channel.messages) - logfile.stored_data += "[logstring]\[BR\]" - logfile.stored_data += "\[b\]Logfile dump completed.\[/b\]" - logfile.calculate_size() - if(!computer || !computer.hard_drive || !computer.hard_drive.store_file(logfile)) - if(!computer) - // This program shouldn't even be runnable without computer. - CRASH("Var computer is null!") - return 1 - if(!computer.hard_drive) - computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive connection error\" warning.") - else // In 99.9% cases this will mean our HDD is full - computer.visible_message("\The [computer] shows an \"I/O Error - Hard drive may be full. Please free some space and try again. Required space: [logfile.size]GQ\" warning.") - if(href_list["PRG_renamechannel"]) - . = 1 - if(!operator_mode || !channel) - return 1 - var/mob/living/user = usr - var/newname = sanitize(input(user, "Enter new channel name or leave blank to cancel:"), 64) - if(!newname || !channel) - return - channel.add_status_message("Channel renamed from [channel.title] to [newname] by operator.") - channel.title = newname - if(href_list["PRG_deletechannel"]) - . = 1 - if(channel && ((channel.operator == src) || netadmin_mode)) - qdel(channel) - channel = null - if(href_list["PRG_setpassword"]) - . = 1 - if(!channel || ((channel.operator != src) && !netadmin_mode)) - return 1 + var/new_password = sanitize(params["new_password"]) + if(!authed) + return - var/mob/living/user = usr - var/newpassword = sanitize(input(user, "Enter new password for this channel. Leave blank to cancel, enter 'nopassword' to remove password completely:")) - if(!channel || !newpassword || ((channel.operator != src) && !netadmin_mode)) - return 1 - - if(newpassword == "nopassword") - channel.password = "" - else - channel.password = newpassword + channel.password = new_password + return TRUE /datum/computer_file/program/chatclient/process_tick() ..() + var/datum/ntnet_conversation/channel = ntnet_global.get_chat_channel_by_id(active_channel) if(program_state != PROGRAM_STATE_KILLED) ui_header = "ntnrc_idle.gif" if(channel) // Remember the last message. If there is no message in the channel remember null. - last_message = channel.messages.len ? channel.messages[channel.messages.len - 1] : null + last_message = length(channel.messages) ? channel.messages[channel.messages.len - 1] : null else last_message = null return 1 - if(channel && channel.messages && channel.messages.len) + if(channel?.messages?.len) ui_header = last_message == channel.messages[channel.messages.len - 1] ? "ntnrc_idle.gif" : "ntnrc_new.gif" else ui_header = "ntnrc_idle.gif" -/datum/computer_file/program/chatclient/kill_program(var/forced = 0) - if(channel) +/datum/computer_file/program/chatclient/kill_program(forced = FALSE) + for(var/C in ntnet_global.chat_channels) + var/datum/ntnet_conversation/channel = C channel.remove_client(src) - channel = null - ..(forced) - -/datum/nano_module/program/computer_chatclient - name = "NTNet Relay Chat Client" - -/datum/nano_module/program/computer_chatclient/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - if(!ntnet_global || !ntnet_global.chat_channels) - return + ..() +/datum/computer_file/program/chatclient/tgui_static_data(mob/user) var/list/data = list() - if(program) - data = program.get_header_data() + data["can_admin"] = can_run(user, FALSE, access_network) + return data + +/datum/computer_file/program/chatclient/tgui_data(mob/user) + if(!ntnet_global || !ntnet_global.chat_channels) + return list() - var/datum/computer_file/program/chatclient/C = program - if(!istype(C)) - return + var/list/data = get_header_data() - data["adminmode"] = C.netadmin_mode - if(C.channel) - data["title"] = C.channel.title - var/list/messages[0] - for(var/M in C.channel.messages) - messages.Add(list(list( - "msg" = M + var/list/all_channels = list() + for(var/C in ntnet_global.chat_channels) + var/datum/ntnet_conversation/conv = C + if(conv && conv.title) + all_channels.Add(list(list( + "chan" = conv.title, + "id" = conv.id ))) - data["messages"] = messages - var/list/clients[0] - for(var/datum/computer_file/program/chatclient/cl in C.channel.clients) + data["all_channels"] = all_channels + + data["active_channel"] = active_channel + data["username"] = username + data["adminmode"] = netadmin_mode + var/datum/ntnet_conversation/channel = ntnet_global.get_chat_channel_by_id(active_channel) + if(channel) + data["title"] = channel.title + var/authed = FALSE + if(!channel.password) + authed = TRUE + if(netadmin_mode) + authed = TRUE + var/list/clients = list() + for(var/C in channel.clients) + if(C == src) + authed = TRUE + var/datum/computer_file/program/chatclient/cl = C clients.Add(list(list( "name" = cl.username ))) - data["clients"] = clients - C.operator_mode = (C.channel.operator == C) ? 1 : 0 - data["is_operator"] = C.operator_mode || C.netadmin_mode - - else // Channel selection screen - var/list/all_channels[0] - for(var/datum/ntnet_conversation/conv in ntnet_global.chat_channels) - if(conv && conv.title) - all_channels.Add(list(list( - "chan" = conv.title, - "id" = conv.id + data["authed"] = authed + //no fishing for ui data allowed + if(authed) + data["clients"] = clients + var/list/messages = list() + for(var/M in channel.messages) + messages.Add(list(list( + "msg" = M ))) - data["all_channels"] = all_channels + data["messages"] = messages + data["is_operator"] = (channel.operator == src) || netadmin_mode + else + data["clients"] = list() + data["messages"] = list() + else + data["clients"] = list() + data["authed"] = FALSE + data["messages"] = list() - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "ntnet_chat.tmpl", "NTNet Relay Chat Client", 575, 700, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm index 0efed986ae..ea88d3597e 100644 --- a/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm +++ b/code/modules/modular_computers/file_system/programs/generic/nttransfer.dm @@ -12,7 +12,7 @@ var/global/nttransfer_uid = 0 requires_ntnet_feature = NTNET_PEERTOPEER network_destination = "other device via P2P tunnel" available_on_ntnet = 1 - nanomodule_path = /datum/nano_module/program/computer_nttransfer/ + tgui_id = "NtosNetTransfer" var/error = "" // Error screen var/server_password = "" // Optional password to download the file. @@ -75,111 +75,101 @@ var/global/nttransfer_uid = 0 remote = null download_completion = 0 +/datum/computer_file/program/nttransfer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = get_header_data() -/datum/nano_module/program/computer_nttransfer - name = "NTNet P2P Transfer Client" + data["error"] = error -/datum/nano_module/program/computer_nttransfer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - if(!program) - return - var/datum/computer_file/program/nttransfer/PRG = program - if(!istype(PRG)) - return - - var/list/data = program.get_header_data() - - if(PRG.error) - data["error"] = PRG.error - else if(PRG.downloaded_file) - data["downloading"] = 1 - data["download_size"] = PRG.downloaded_file.size - data["download_progress"] = PRG.download_completion - data["download_netspeed"] = PRG.actual_netspeed - data["download_name"] = "[PRG.downloaded_file.filename].[PRG.downloaded_file.filetype]" - else if (PRG.provided_file) - data["uploading"] = 1 - data["upload_uid"] = PRG.unique_token - data["upload_clients"] = PRG.connected_clients.len - data["upload_haspassword"] = PRG.server_password ? 1 : 0 - data["upload_filename"] = "[PRG.provided_file.filename].[PRG.provided_file.filetype]" - else if (PRG.upload_menu) - var/list/all_files[0] - for(var/datum/computer_file/F in PRG.computer.hard_drive.stored_files) + data["downloading"] = !!downloaded_file + if(downloaded_file) + data["download_size"] = downloaded_file.size + data["download_progress"] = download_completion + data["download_netspeed"] = actual_netspeed + data["download_name"] = "[downloaded_file.filename].[downloaded_file.filetype]" + + data["uploading"] = !!provided_file + if(provided_file) + data["upload_uid"] = unique_token + data["upload_clients"] = connected_clients.len + data["upload_haspassword"] = server_password ? 1 : 0 + data["upload_filename"] = "[provided_file.filename].[provided_file.filetype]" + + data["upload_filelist"] = list() + if(upload_menu) + var/list/all_files = list() + for(var/datum/computer_file/F in computer.hard_drive.stored_files) all_files.Add(list(list( "uid" = F.uid, "filename" = "[F.filename].[F.filetype]", "size" = F.size ))) data["upload_filelist"] = all_files - else - var/list/all_servers[0] + + data["servers"] = list() + if(!(downloaded_file || provided_file || upload_menu)) + var/list/all_servers = list() for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers) if(!P.provided_file) continue all_servers.Add(list(list( - "uid" = P.unique_token, - "filename" = "[P.provided_file.filename].[P.provided_file.filetype]", - "size" = P.provided_file.size, - "haspassword" = P.server_password ? 1 : 0 + "uid" = P.unique_token, + "filename" = "[P.provided_file.filename].[P.provided_file.filetype]", + "size" = P.provided_file.size, + "haspassword" = P.server_password ? 1 : 0 ))) data["servers"] = all_servers - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "ntnet_transfer.tmpl", "NTNet P2P Transfer Client", 575, 700, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data -/datum/computer_file/program/nttransfer/Topic(href, href_list) +/datum/computer_file/program/nttransfer/tgui_act(action, list/params, datum/tgui/ui) if(..()) - return 1 - if(href_list["PRG_downloadfile"]) - for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers) - if("[P.unique_token]" == href_list["PRG_downloadfile"]) - remote = P - break - if(!remote || !remote.provided_file) - return - if(remote.server_password) - var/pass = sanitize(input(usr, "Code 401 Unauthorized. Please enter password:", "Password required")) - if(pass != remote.server_password) - error = "Incorrect Password" + return TRUE + switch(action) + if("PRG_downloadfile") + for(var/datum/computer_file/program/nttransfer/P in ntnet_global.fileservers) + if(P.unique_token == text2num(params["uid"])) + remote = P + break + if(!remote || !remote.provided_file) return - downloaded_file = remote.provided_file.clone() - remote.connected_clients.Add(src) - return 1 - if(href_list["PRG_reset"]) - error = "" - upload_menu = 0 - finalize_download() - if(src in ntnet_global.fileservers) - ntnet_global.fileservers.Remove(src) - for(var/datum/computer_file/program/nttransfer/T in connected_clients) - T.crash_download("Remote server has forcibly closed the connection") - provided_file = null - return 1 - if(href_list["PRG_setpassword"]) - var/pass = sanitize(input(usr, "Enter new server password. Leave blank to cancel, input 'none' to disable password.", "Server security", "none")) - if(!pass) - return - if(pass == "none") - server_password = "" - return - server_password = pass - return 1 - if(href_list["PRG_uploadfile"]) - for(var/datum/computer_file/F in computer.hard_drive.stored_files) - if("[F.uid]" == href_list["PRG_uploadfile"]) - if(F.unsendable) - error = "I/O Error: File locked." + if(remote.server_password) + var/pass = sanitize(input(usr, "Code 401 Unauthorized. Please enter password:", "Password required")) + if(pass != remote.server_password) + error = "Incorrect Password" return - provided_file = F - ntnet_global.fileservers.Add(src) + downloaded_file = remote.provided_file.clone() + remote.connected_clients.Add(src) + return TRUE + if("PRG_reset") + error = "" + upload_menu = 0 + finalize_download() + if(src in ntnet_global.fileservers) + ntnet_global.fileservers.Remove(src) + for(var/datum/computer_file/program/nttransfer/T in connected_clients) + T.crash_download("Remote server has forcibly closed the connection") + provided_file = null + return TRUE + if("PRG_setpassword") + var/pass = sanitize(input(usr, "Enter new server password. Leave blank to cancel, input 'none' to disable password.", "Server security", "none")) + if(!pass) return - error = "I/O Error: Unable to locate file on hard drive." - return 1 - if(href_list["PRG_uploadmenu"]) - upload_menu = 1 - return 0 + if(pass == "none") + server_password = "" + return + server_password = pass + return TRUE + if("PRG_uploadfile") + for(var/datum/computer_file/F in computer.hard_drive.stored_files) + if(F.uid == text2num(params["uid"])) + if(F.unsendable) + error = "I/O Error: File locked." + return + provided_file = F + ntnet_global.fileservers |= src + return + error = "I/O Error: Unable to locate file on hard drive." + return TRUE + if("PRG_uploadmenu") + upload_menu = 1 + return TRUE diff --git a/code/modules/modular_computers/file_system/programs/generic/uav.dm b/code/modules/modular_computers/file_system/programs/generic/uav.dm index 0e98d0e0ea..24d677b679 100644 --- a/code/modules/modular_computers/file_system/programs/generic/uav.dm +++ b/code/modules/modular_computers/file_system/programs/generic/uav.dm @@ -4,7 +4,7 @@ /datum/computer_file/program/uav filename = "rigger" filedesc = "UAV Control" - nanomodule_path = /datum/nano_module/uav + tguimodule_path = /datum/tgui_module/uav program_icon_state = "comm_monitor" program_key_state = "generic_key" program_menu_icon = "link" @@ -12,255 +12,3 @@ size = 12 available_on_ntnet = 1 //requires_ntnet = 1 - -/datum/nano_module/uav - name = "UAV Control program" - var/obj/item/device/uav/current_uav = null //The UAV we're watching - var/signal_strength = 0 //Our last signal strength report (cached for a few seconds) - var/signal_test_counter = 0 //How long until next signal strength check - var/list/viewers //Who's viewing a UAV through us - var/adhoc_range = 30 //How far we can operate on a UAV without NTnet - -/datum/nano_module/uav/Destroy() - if(LAZYLEN(viewers)) - for(var/weakref/W in viewers) - var/M = W.resolve() - if(M) - unlook(M) - . = ..() - -/datum/nano_module/uav/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, state = default_state) - var/list/data = host.initial_data() - - if(current_uav) - if(QDELETED(current_uav)) - set_current(null) - else if(signal_test_counter-- <= 0) - signal_strength = get_signal_to(current_uav) - if(!signal_strength) - set_current(null) - else // Don't reset counter until we find a UAV that's actually in range we can stay connected to - signal_test_counter = 20 - - data["current_uav"] = null - if(current_uav) - data["current_uav"] = list("status" = current_uav.get_status_string(), "power" = current_uav.state == 1 ? 1 : null) - data["signal_strength"] = signal_strength ? signal_strength >= 2 ? "High" : "Low" : "None" - data["in_use"] = LAZYLEN(viewers) - - var/list/paired_map = list() - var/obj/item/modular_computer/mc_host = nano_host() - if(istype(mc_host)) - for(var/puav in mc_host.paired_uavs) - var/weakref/wr = puav - var/obj/item/device/uav/U = wr.resolve() - paired_map[++paired_map.len] = list("name" = "[U ? U.nickname : "!!Missing!!"]", "uavref" = "\ref[U]") - - data["paired_uavs"] = paired_map - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "mod_uav.tmpl", "UAV Control", 600, 500, state = state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/datum/nano_module/uav/Topic(var/href, var/href_list = list(), var/datum/topic_state/state) - if((. = ..())) - return - state = state || DefaultTopicState() || global.default_state - if(CanUseTopic(usr, state, href_list) == STATUS_INTERACTIVE) - CouldUseTopic(usr) - return OnTopic(usr, href_list, state) - CouldNotUseTopic(usr) - return TRUE - -/datum/nano_module/uav/proc/OnTopic(var/mob/user, var/list/href_list) - if(href_list["switch_uav"]) - var/obj/item/device/uav/U = locate(href_list["switch_uav"]) //This is a \ref to the UAV itself - if(!istype(U)) - to_chat(usr,"Something is blocking the connection to that UAV. In-person investigation is required.") - return TOPIC_NOACTION - - if(!get_signal_to(U)) - to_chat(usr,"The screen freezes for a moment, before returning to the UAV selection menu. It's not able to connect to that UAV.") - return TOPIC_NOACTION - - set_current(U) - return TOPIC_REFRESH - - if(href_list["del_uav"]) - var/refstring = href_list["del_uav"] //This is a \ref to the UAV itself - var/obj/item/modular_computer/mc_host = nano_host() - //This is so we can really scrape up any weakrefs that can't resolve - for(var/weakref/wr in mc_host.paired_uavs) - if(wr.ref == refstring) - if(current_uav?.weakref == wr) - set_current(null) - LAZYREMOVE(mc_host.paired_uavs, wr) - - else if(href_list["view_uav"]) - if(!current_uav) - return TOPIC_NOACTION - - if(current_uav.check_eye(user) < 0) - to_chat(usr,"The screen freezes for a moment, before returning to the UAV selection menu. It's not able to connect to that UAV.") - else - viewing_uav(user) ? unlook(user) : look(user) - return TOPIC_NOACTION - - else if(href_list["power_uav"]) - if(!current_uav) - return TOPIC_NOACTION - else if(current_uav.toggle_power()) - //Clean up viewers faster - if(LAZYLEN(viewers)) - for(var/weakref/W in viewers) - var/M = W.resolve() - if(M) - unlook(M) - return TOPIC_REFRESH - -/datum/nano_module/uav/proc/DefaultTopicState() - return global.default_state - -/datum/nano_module/uav/proc/CouldNotUseTopic(mob/user) - . = ..() - unlook(user) - -/datum/nano_module/uav/proc/CouldUseTopic(mob/user) - . = ..() - if(viewing_uav(user)) - look(user) - -/datum/nano_module/uav/proc/set_current(var/obj/item/device/uav/U) - if(current_uav == U) - return - - signal_strength = 0 - current_uav = U - - if(LAZYLEN(viewers)) - for(var/weakref/W in viewers) - var/M = W.resolve() - if(M) - if(current_uav) - to_chat(M, "You're disconnected from the UAV's camera!") - unlook(M) - else - look(M) - -//// -//// Finding signal strength between us and the UAV -//// -/datum/nano_module/uav/proc/get_signal_to(var/atom/movable/AM) - // Following roughly the ntnet signal levels - // 0 is none - // 1 is weak - // 2 is strong - var/obj/item/modular_computer/host = nano_host() //Better not add this to anything other than modular computers. - if(!istype(host)) - return - var/our_signal = host.get_ntnet_status() //1 low, 2 good, 3 wired, 0 none - var/their_z = get_z(AM) - - //If we have no NTnet connection don't bother getting theirs - if(!our_signal) - if(get_z(host) == their_z && (get_dist(host, AM) < adhoc_range)) - return 1 //We can connect (with weak signal) in same z without ntnet, within 30 turfs - else - return 0 - - var/list/zlevels_in_range = using_map.get_map_levels(their_z, FALSE) - var/list/zlevels_in_long_range = using_map.get_map_levels(their_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) - zlevels_in_range - var/their_signal = 0 - for(var/relay in ntnet_global.relays) - var/obj/machinery/ntnet_relay/R = relay - if(!R.operable()) - continue - if(R.z == their_z) - their_signal = 2 - break - if(R.z in zlevels_in_range) - their_signal = 2 - break - if(R.z in zlevels_in_long_range) - their_signal = 1 - break - - if(!their_signal) //They have no NTnet at all - if(get_z(host) == their_z && (get_dist(host, AM) < adhoc_range)) - return 1 //We can connect (with weak signal) in same z without ntnet, within 30 turfs - else - return 0 - else - return max(our_signal, their_signal) - -//// -//// UAV viewer handling -//// -/datum/nano_module/uav/proc/viewing_uav(mob/user) - return (weakref(user) in viewers) - -/datum/nano_module/uav/proc/look(var/mob/user) - if(issilicon(user)) //Too complicated for me to want to mess with at the moment - to_chat(user, "Regulations prevent you from controlling several corporeal forms at the same time!") - return - - if(!current_uav) - return - - user.set_machine(nano_host()) - user.reset_view(current_uav) - current_uav.add_master(user) - LAZYDISTINCTADD(viewers, weakref(user)) - -/datum/nano_module/uav/proc/unlook(var/mob/user) - user.unset_machine() - user.reset_view() - if(current_uav) - current_uav.remove_master(user) - LAZYREMOVE(viewers, weakref(user)) - -/datum/nano_module/uav/check_eye(var/mob/user) - if(get_dist(user, nano_host()) > 1 || user.blinded || !current_uav) - unlook(user) - return -1 - - var/viewflag = current_uav.check_eye(user) - if (viewflag < 0) //camera doesn't work - unlook(user) - return -1 - - return viewflag - -//// -//// Relaying movements to the UAV -//// -/datum/nano_module/uav/relaymove(var/mob/user, direction) - if(current_uav) - return current_uav.relaymove(user, direction, signal_strength) - -//// -//// The effects when looking through a UAV -//// -/datum/nano_module/uav/apply_visual(var/mob/M) - if(!M.client) - return - if(weakref(M) in viewers) - M.overlay_fullscreen("fishbed",/obj/screen/fullscreen/fishbed) - M.overlay_fullscreen("scanlines",/obj/screen/fullscreen/scanline) - - if(signal_strength <= 1) - M.overlay_fullscreen("whitenoise",/obj/screen/fullscreen/noise) - else - M.clear_fullscreen("whitenoise", 0) - else - remove_visual(M) - -/datum/nano_module/uav/remove_visual(mob/M) - if(!M.client) - return - M.clear_fullscreen("fishbed",0) - M.clear_fullscreen("scanlines",0) - M.clear_fullscreen("whitenoise",0) diff --git a/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm b/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm index 1a4d7a1871..61f3b43da8 100644 --- a/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm +++ b/code/modules/modular_computers/file_system/programs/generic/wordprocessor.dm @@ -5,9 +5,10 @@ program_icon_state = "word" program_key_state = "atmos_key" size = 4 - requires_ntnet = 0 - available_on_ntnet = 1 - nanomodule_path = /datum/nano_module/program/computer_wordprocessor/ + requires_ntnet = FALSE + available_on_ntnet = TRUE + tgui_id = "NtosWordProcessor" + var/browsing var/open_file var/loaded_data @@ -28,7 +29,7 @@ if(F) open_file = F.filename loaded_data = F.stored_data - return 1 + return TRUE /datum/computer_file/program/wordprocessor/proc/save_file(var/filename) var/datum/computer_file/data/F = get_file(filename) @@ -46,7 +47,7 @@ HDD.store_file(backup) return 0 is_edited = 0 - return 1 + return TRUE /datum/computer_file/program/wordprocessor/proc/create_file(var/newname, var/data = "") if(!newname) @@ -64,142 +65,144 @@ if(HDD.store_file(F)) return F -/datum/computer_file/program/wordprocessor/Topic(href, href_list) +/datum/computer_file/program/wordprocessor/tgui_act(action, list/params, datum/tgui/ui) if(..()) - return 1 + return TRUE - if(href_list["PRG_txtrpeview"]) - show_browser(usr,"[open_file][pencode2html(loaded_data)]", "window=[open_file]") - return 1 + switch(action) + if("PRG_txtrpeview") + show_browser(usr,"[open_file][pencode2html(loaded_data)]", "window=[open_file]") + return TRUE - if(href_list["PRG_taghelp"]) - to_chat(usr, "The hologram of a googly-eyed paper clip helpfully tells you:") - var/help = {" - \[br\] : Creates a linebreak. - \[center\] - \[/center\] : Centers the text. - \[h1\] - \[/h1\] : First level heading. - \[h2\] - \[/h2\] : Second level heading. - \[h3\] - \[/h3\] : Third level heading. - \[b\] - \[/b\] : Bold. - \[i\] - \[/i\] : Italic. - \[u\] - \[/u\] : Underlined. - \[small\] - \[/small\] : Decreases the size of the text. - \[large\] - \[/large\] : Increases the size of the text. - \[field\] : Inserts a blank text field, which can be filled later. Useful for forms. - \[date\] : Current station date. - \[time\] : Current station time. - \[list\] - \[/list\] : Begins and ends a list. - \[*\] : A list item. - \[hr\] : Horizontal rule. - \[table\] - \[/table\] : Creates table using \[row\] and \[cell\] tags. - \[grid\] - \[/grid\] : Table without visible borders, for layouts. - \[row\] - New table row. - \[cell\] - New table cell. - \[logo\] - Inserts NT logo image. - \[redlogo\] - Inserts red NT logo image. - \[sglogo\] - Inserts Solgov insignia image."} + if("PRG_taghelp") + to_chat(usr, "The hologram of a googly-eyed paper clip helpfully tells you:") + var/help = {" + \[br\] : Creates a linebreak. + \[center\] - \[/center\] : Centers the text. + \[h1\] - \[/h1\] : First level heading. + \[h2\] - \[/h2\] : Second level heading. + \[h3\] - \[/h3\] : Third level heading. + \[b\] - \[/b\] : Bold. + \[i\] - \[/i\] : Italic. + \[u\] - \[/u\] : Underlined. + \[small\] - \[/small\] : Decreases the size of the text. + \[large\] - \[/large\] : Increases the size of the text. + \[field\] : Inserts a blank text field, which can be filled later. Useful for forms. + \[date\] : Current station date. + \[time\] : Current station time. + \[list\] - \[/list\] : Begins and ends a list. + \[*\] : A list item. + \[hr\] : Horizontal rule. + \[table\] - \[/table\] : Creates table using \[row\] and \[cell\] tags. + \[grid\] - \[/grid\] : Table without visible borders, for layouts. + \[row\] - New table row. + \[cell\] - New table cell. + \[logo\] - Inserts NT logo image. + \[redlogo\] - Inserts red NT logo image. + \[sglogo\] - Inserts Solgov insignia image."} - to_chat(usr, help) - return 1 + to_chat(usr, help) + return TRUE - if(href_list["PRG_closebrowser"]) - browsing = 0 - return 1 + if("PRG_closebrowser") + browsing = 0 + return TRUE - if(href_list["PRG_backtomenu"]) - error = null - return 1 + if("PRG_backtomenu") + error = null + return TRUE - if(href_list["PRG_loadmenu"]) - browsing = 1 - return 1 + if("PRG_loadmenu") + browsing = 1 + return TRUE - if(href_list["PRG_openfile"]) - . = 1 - if(is_edited) - if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes") - save_file(open_file) - browsing = 0 - if(!open_file(href_list["PRG_openfile"])) - error = "I/O error: Unable to open file '[href_list["PRG_openfile"]]'." + if("PRG_openfile") + if(is_edited) + if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes") + save_file(open_file) + browsing = 0 + if(!open_file(params["PRG_openfile"])) + error = "I/O error: Unable to open file '[params["PRG_openfile"]]'." + return TRUE - if(href_list["PRG_newfile"]) - . = 1 - if(is_edited) - if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes") - save_file(open_file) + if("PRG_newfile") + if(is_edited) + if(alert("Would you like to save your changes first?",,"Yes","No") == "Yes") + save_file(open_file) - var/newname = sanitize(input(usr, "Enter file name:", "New File") as text|null) - if(!newname) - return 1 - var/datum/computer_file/data/F = create_file(newname) - if(F) - open_file = F.filename - loaded_data = "" - return 1 - else - error = "I/O error: Unable to create file '[href_list["PRG_saveasfile"]]'." + var/newname = sanitize(input(usr, "Enter file name:", "New File") as text|null) + if(!newname) + return TRUE + var/datum/computer_file/data/F = create_file(newname) + if(F) + open_file = F.filename + loaded_data = "" + return TRUE + else + error = "I/O error: Unable to create file '[params["PRG_saveasfile"]]'." + return TRUE - if(href_list["PRG_saveasfile"]) - . = 1 - var/newname = sanitize(input(usr, "Enter file name:", "Save As") as text|null) - if(!newname) - return 1 - var/datum/computer_file/data/F = create_file(newname, loaded_data) - if(F) - open_file = F.filename - else - error = "I/O error: Unable to create file '[href_list["PRG_saveasfile"]]'." - return 1 + if("PRG_saveasfile") + var/newname = sanitize(input(usr, "Enter file name:", "Save As") as text|null) + if(!newname) + return TRUE + var/datum/computer_file/data/F = create_file(newname, loaded_data) + if(F) + open_file = F.filename + else + error = "I/O error: Unable to create file '[params["PRG_saveasfile"]]'." + return TRUE - if(href_list["PRG_savefile"]) - . = 1 - if(!open_file) - open_file = sanitize(input(usr, "Enter file name:", "Save As") as text|null) + if("PRG_savefile") if(!open_file) - return 0 - if(!save_file(open_file)) - error = "I/O error: Unable to save file '[open_file]'." - return 1 + open_file = sanitize(input(usr, "Enter file name:", "Save As") as text|null) + if(!open_file) + return 0 + if(!save_file(open_file)) + error = "I/O error: Unable to save file '[open_file]'." + return TRUE - if(href_list["PRG_editfile"]) - var/oldtext = html_decode(loaded_data) - oldtext = replacetext(oldtext, "\[br\]", "\n") + if("PRG_editfile") + var/oldtext = html_decode(loaded_data) + oldtext = replacetext(oldtext, "\[br\]", "\n") - var/newtext = sanitize(replacetext(input(usr, "Editing file '[open_file]'. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH) - if(!newtext) - return - loaded_data = newtext - is_edited = 1 - return 1 + var/newtext = sanitize(replacetext(input(usr, "Editing file '[open_file]'. You may use most tags used in paper formatting:", "Text Editor", oldtext) as message|null, "\n", "\[br\]"), MAX_TEXTFILE_LENGTH) + if(!newtext) + return + loaded_data = newtext + is_edited = 1 + return TRUE - if(href_list["PRG_printfile"]) - . = 1 - if(!computer.nano_printer) - error = "Missing Hardware: Your computer does not have the required hardware to complete this operation." - return 1 - if(!computer.nano_printer.print_text(pencode2html(loaded_data))) - error = "Hardware error: Printer was unable to print the file. It may be out of paper." - return 1 + if("PRG_printfile") + if(!computer.nano_printer) + error = "Missing Hardware: Your computer does not have the required hardware to complete this operation." + return TRUE + if(!computer.nano_printer.print_text(pencode2html(loaded_data))) + error = "Hardware error: Printer was unable to print the file. It may be out of paper." + return TRUE + return TRUE -/datum/nano_module/program/computer_wordprocessor - name = "Word Processor" +/datum/computer_file/program/wordprocessor/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = get_header_data() -/datum/nano_module/program/computer_wordprocessor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() - var/datum/computer_file/program/wordprocessor/PRG - PRG = program + var/obj/item/weapon/computer_hardware/hard_drive/HDD = computer.hard_drive + var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD = computer.portable_drive + data["error"] = null + if(error) + data["error"] = error - var/obj/item/weapon/computer_hardware/hard_drive/HDD - var/obj/item/weapon/computer_hardware/hard_drive/portable/RHDD - if(PRG.error) - data["error"] = PRG.error - if(PRG.browsing) - data["browsing"] = PRG.browsing - if(!PRG.computer || !PRG.computer.hard_drive) + data["browsing"] = null + data["files"] = list() + data["usbconnected"] = FALSE + data["usbfiles"] = list() + data["filedata"] = null + data["filename"] = null + + if(browsing) + data["browsing"] = browsing + if(!computer || !HDD) data["error"] = "I/O ERROR: Unable to access hard drive." else - HDD = PRG.computer.hard_drive var/list/files[0] for(var/datum/computer_file/F in HDD.stored_files) if(F.filetype == "TXT") @@ -209,7 +212,6 @@ ))) data["files"] = files - RHDD = PRG.computer.portable_drive if(RHDD) data["usbconnected"] = 1 var/list/usbfiles[0] @@ -220,16 +222,11 @@ "size" = F.size, ))) data["usbfiles"] = usbfiles - else if(PRG.open_file) - data["filedata"] = pencode2html(PRG.loaded_data) - data["filename"] = PRG.is_edited ? "[PRG.open_file]*" : PRG.open_file + else if(open_file) + data["filedata"] = pencode2html(loaded_data) + data["filename"] = is_edited ? "[open_file]*" : open_file else - data["filedata"] = pencode2html(PRG.loaded_data) + data["filedata"] = pencode2html(loaded_data) data["filename"] = "UNNAMED" - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "word_processor.tmpl", "Word Processor", 575, 700, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() \ No newline at end of file + return data \ No newline at end of file diff --git a/code/modules/modular_computers/file_system/programs/research/email_administration.dm b/code/modules/modular_computers/file_system/programs/research/email_administration.dm index 93ba0bded3..eb9dfd71a6 100644 --- a/code/modules/modular_computers/file_system/programs/research/email_administration.dm +++ b/code/modules/modular_computers/file_system/programs/research/email_administration.dm @@ -8,29 +8,34 @@ size = 12 requires_ntnet = 1 available_on_ntnet = 1 - nanomodule_path = /datum/nano_module/email_administration + tgui_id = "NtosEmailAdministration" required_access = access_network - - - -/datum/nano_module/email_administration/ - name = "Email Client" var/datum/computer_file/data/email_account/current_account = null var/datum/computer_file/data/email_message/current_message = null var/error = "" -/datum/nano_module/email_administration/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() +/datum/computer_file/program/email_administration/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = get_header_data() - if(error) - data["error"] = error - else if(istype(current_message)) - data["msg_title"] = current_message.title - data["msg_body"] = pencode2html(current_message.stored_data) - data["msg_timestamp"] = current_message.timestamp - data["msg_source"] = current_message.source - else if(istype(current_account)) + data["error"] = error + + data["cur_title"] = null + data["cur_body"] = null + data["cur_timestamp"] = null + data["cur_source"] = null + + if(istype(current_message)) + data["cur_title"] = current_message.title + data["cur_body"] = pencode2html(current_message.stored_data) + data["cur_timestamp"] = current_message.timestamp + data["cur_source"] = current_message.source + + data["current_account"] = null + data["cur_suspended"] = null + data["messages"] = null + + if(istype(current_account)) data["current_account"] = current_account.login data["cur_suspended"] = current_account.suspended var/list/all_messages = list() @@ -42,103 +47,90 @@ "uid" = message.uid ))) data["messages"] = all_messages - data["messagecount"] = all_messages.len - else - var/list/all_accounts = list() - for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) - if(!account.can_login) - continue - all_accounts.Add(list(list( - "login" = account.login, - "uid" = account.uid - ))) - data["accounts"] = all_accounts - data["accountcount"] = all_accounts.len - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "email_administration.tmpl", "Email Administration Utility", 600, 450, state = state) - if(host.update_layout()) - ui.auto_update_layout = 1 - ui.set_auto_update(1) - ui.set_initial_data(data) - ui.open() + var/list/all_accounts = list() + for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) + if(!account.can_login) + continue + all_accounts.Add(list(list( + "login" = account.login, + "uid" = account.uid + ))) + data["accounts"] = all_accounts + return data -/datum/nano_module/email_administration/Topic(href, href_list) +/datum/computer_file/program/email_administration/tgui_act(action, list/params, datum/tgui/ui) if(..()) - return 1 - - var/mob/user = usr - if(!istype(user)) - return 1 + return TRUE // High security - can only be operated when the user has an ID with access on them. - var/obj/item/weapon/card/id/I = user.GetIdCard() + var/obj/item/weapon/card/id/I = usr.GetIdCard() if(!istype(I) || !(access_network in I.access)) - return 1 + return TRUE - if(href_list["back"]) - if(error) - error = "" - else if(current_message) - current_message = null - else - current_account = null - return 1 + switch(action) + if("back") + if(error) + error = "" + else if(current_message) + current_message = null + else + current_account = null + return TRUE - if(href_list["ban"]) - if(!current_account) - return 1 + if("ban") + if(!current_account) + return TRUE - current_account.suspended = !current_account.suspended - ntnet_global.add_log_with_ids_check("EMAIL LOG: SA-EDIT Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended by SA [I.registered_name] ([I.assignment]).") - error = "Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended." - return 1 + current_account.suspended = !current_account.suspended + ntnet_global.add_log_with_ids_check("EMAIL LOG: SA-EDIT Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended by SA [I.registered_name] ([I.assignment]).") + error = "Account [current_account.login] has been [current_account.suspended ? "" : "un" ]suspended." + return TRUE - if(href_list["changepass"]) - if(!current_account) - return 1 + if("changepass") + if(!current_account) + return TRUE - var/newpass = sanitize(input(user,"Enter new password for account [current_account.login]", "Password"), 100) - if(!newpass) - return 1 - current_account.password = newpass - ntnet_global.add_log_with_ids_check("EMAIL LOG: SA-EDIT Password for account [current_account.login] has been changed by SA [I.registered_name] ([I.assignment]).") - return 1 + var/newpass = sanitize(input(usr,"Enter new password for account [current_account.login]", "Password"), 100) + if(!newpass) + return TRUE + current_account.password = newpass + ntnet_global.add_log_with_ids_check("EMAIL LOG: SA-EDIT Password for account [current_account.login] has been changed by SA [I.registered_name] ([I.assignment]).") + return TRUE - if(href_list["viewmail"]) - if(!current_account) - return 1 + if("viewmail") + if(!current_account) + return TRUE - for(var/datum/computer_file/data/email_message/received_message in (current_account.inbox | current_account.spam | current_account.deleted)) - if(received_message.uid == text2num(href_list["viewmail"])) - current_message = received_message - break - return 1 + for(var/datum/computer_file/data/email_message/received_message in (current_account.inbox | current_account.spam | current_account.deleted)) + if(received_message.uid == text2num(params["viewmail"])) + current_message = received_message + break + return TRUE - if(href_list["viewaccount"]) - for(var/datum/computer_file/data/email_account/email_account in ntnet_global.email_accounts) - if(email_account.uid == text2num(href_list["viewaccount"])) - current_account = email_account - break - return 1 + if("viewaccount") + for(var/datum/computer_file/data/email_account/email_account in ntnet_global.email_accounts) + if(email_account.uid == text2num(params["viewaccount"])) + current_account = email_account + break + return TRUE - if(href_list["newaccount"]) - var/newdomain = sanitize(input(user,"Pick domain:", "Domain name") as null|anything in using_map.usable_email_tlds) - if(!newdomain) - return 1 - var/newlogin = sanitize(input(user,"Pick account name (@[newdomain]):", "Account name"), 100) - if(!newlogin) - return 1 + if("newaccount") + var/newdomain = sanitize(input(usr,"Pick domain:", "Domain name") as null|anything in using_map.usable_email_tlds) + if(!newdomain) + return TRUE + var/newlogin = sanitize(input(usr,"Pick account name (@[newdomain]):", "Account name"), 100) + if(!newlogin) + return TRUE - var/complete_login = "[newlogin]@[newdomain]" - if(ntnet_global.does_email_exist(complete_login)) - error = "Error creating account: An account with same address already exists." - return 1 + var/complete_login = "[newlogin]@[newdomain]" + if(ntnet_global.does_email_exist(complete_login)) + error = "Error creating account: An account with same address already exists." + return TRUE - var/datum/computer_file/data/email_account/new_account = new/datum/computer_file/data/email_account() - new_account.login = complete_login - new_account.password = GenerateKey() - error = "Email [new_account.login] has been created, with generated password [new_account.password]" - return 1 + var/datum/computer_file/data/email_account/new_account = new/datum/computer_file/data/email_account() + new_account.login = complete_login + new_account.password = GenerateKey() + error = "Email [new_account.login] has been created, with generated password [new_account.password]" + return TRUE diff --git a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm index 590837f2b5..0ce6dd5c49 100644 --- a/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm +++ b/code/modules/modular_computers/file_system/programs/research/ntmonitor.dm @@ -6,19 +6,15 @@ program_menu_icon = "wrench" extended_desc = "This program monitors the local NTNet network, provides access to logging systems, and allows for configuration changes" size = 12 - requires_ntnet = 1 + requires_ntnet = TRUE required_access = access_network - available_on_ntnet = 1 - nanomodule_path = /datum/nano_module/computer_ntnetmonitor/ + available_on_ntnet = TRUE + tgui_id = "NtosNetMonitor" -/datum/nano_module/computer_ntnetmonitor - name = "NTNet Diagnostics and Monitoring" - //available_to_ai = TRUE - -/datum/nano_module/computer_ntnetmonitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) +/datum/computer_file/program/ntnetmonitor/tgui_data(mob/user) if(!ntnet_global) return - var/list/data = host.initial_data() + var/list/data = get_header_data() data["ntnetstatus"] = ntnet_global.check_function() data["ntnetrelays"] = ntnet_global.relays.len @@ -30,73 +26,68 @@ data["config_communication"] = ntnet_global.setting_communication data["config_systemcontrol"] = ntnet_global.setting_systemcontrol - data["ntnetlogs"] = ntnet_global.logs - data["ntnetmaxlogs"] = ntnet_global.setting_maxlogcount + data["ntnetlogs"] = list() + data["minlogs"] = MIN_NTNET_LOGS + data["maxlogs"] = MAX_NTNET_LOGS data["banned_nids"] = list(ntnet_global.banned_nids) - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "ntnet_monitor.tmpl", "NTNet Diagnostics and Monitoring Tool", 575, 700, state = state) - if(host.update_layout()) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + for(var/i in ntnet_global.logs) + data["ntnetlogs"] += list(list("entry" = i)) + data["ntnetmaxlogs"] = ntnet_global.setting_maxlogcount -/datum/nano_module/computer_ntnetmonitor/Topic(href, href_list, state) - var/mob/user = usr + return data + +/datum/computer_file/program/ntnetmonitor/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 - if(href_list["resetIDS"]) - if(ntnet_global) - ntnet_global.resetIDS() - return 1 - if(href_list["toggleIDS"]) - if(ntnet_global) - ntnet_global.toggleIDS() - return 1 - if(href_list["toggleWireless"]) - if(!ntnet_global) - return 1 + return + switch(action) + if("resetIDS") + if(ntnet_global) + ntnet_global.resetIDS() + return TRUE + if("toggleIDS") + if(ntnet_global) + ntnet_global.toggleIDS() + return TRUE + if("toggleWireless") + if(!ntnet_global) + return - // NTNet is disabled. Enabling can be done without user prompt - if(ntnet_global.setting_disabled) - ntnet_global.setting_disabled = 0 - return 1 + // NTNet is disabled. Enabling can be done without user prompt + if(ntnet_global.setting_disabled) + ntnet_global.setting_disabled = FALSE + return TRUE - // NTNet is enabled and user is about to shut it down. Let's ask them if they really want to do it, as wirelessly connected computers won't connect without NTNet being enabled (which may prevent people from turning it back on) - if(!user) - return 1 - var/response = alert(user, "Really disable NTNet wireless? If your computer is connected wirelessly you won't be able to turn it back on! This will affect all connected wireless devices.", "NTNet shutdown", "Yes", "No") - if(response == "Yes") - ntnet_global.setting_disabled = 1 - return 1 - if(href_list["purgelogs"]) - if(ntnet_global) - ntnet_global.purge_logs() - return 1 - if(href_list["updatemaxlogs"]) - var/logcount = text2num(input(user,"Enter amount of logs to keep in memory ([MIN_NTNET_LOGS]-[MAX_NTNET_LOGS]):")) - if(ntnet_global) - ntnet_global.update_max_log_count(logcount) - return 1 - if(href_list["toggle_function"]) - if(!ntnet_global) - return 1 - ntnet_global.toggle_function(href_list["toggle_function"]) - return 1 - if(href_list["ban_nid"]) - if(!ntnet_global) - return 1 - var/nid = input(user,"Enter NID of device which you want to block from the network:", "Enter NID") as null|num - if(nid && CanUseTopic(user, state)) - ntnet_global.banned_nids |= nid - return 1 - if(href_list["unban_nid"]) - if(!ntnet_global) - return 1 - var/nid = input(user,"Enter NID of device which you want to unblock from the network:", "Enter NID") as null|num - if(nid && CanUseTopic(user, state)) - ntnet_global.banned_nids -= nid - return 1 + var/response = alert(usr, "Really disable NTNet wireless? If your computer is connected wirelessly you won't be able to turn it back on! This will affect all connected wireless devices.", "NTNet shutdown", "Yes", "No") + if(response == "Yes" && tgui_status(usr, state) == STATUS_INTERACTIVE) + ntnet_global.setting_disabled = TRUE + return TRUE + if("purgelogs") + if(ntnet_global) + ntnet_global.purge_logs() + return TRUE + if("updatemaxlogs") + var/logcount = params["new_number"] + if(ntnet_global) + ntnet_global.update_max_log_count(logcount) + return TRUE + if("toggle_function") + if(!ntnet_global) + return + ntnet_global.toggle_function(text2num(params["id"])) + return TRUE + if("ban_nid") + if(!ntnet_global) + return + var/nid = input(usr,"Enter NID of device which you want to block from the network:", "Enter NID") as null|num + if(nid && tgui_status(usr, state) == STATUS_INTERACTIVE) + ntnet_global.banned_nids |= nid + return TRUE + if("unban_nid") + if(!ntnet_global) + return + var/nid = input(usr,"Enter NID of device which you want to unblock from the network:", "Enter NID") as null|num + if(nid && tgui_status(usr, state) == STATUS_INTERACTIVE) + ntnet_global.banned_nids -= nid + return TRUE diff --git a/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm b/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm index 597065d27b..9cac7c920c 100644 --- a/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm +++ b/code/modules/modular_computers/file_system/programs/security/digitalwarrant.dm @@ -1,5 +1,7 @@ var/warrant_uid = 0 -/datum/datacore/var/list/warrants[] = list() +/datum/datacore + var/list/warrants = list() + /datum/data/record/warrant var/warrant_id @@ -7,7 +9,6 @@ var/warrant_uid = 0 ..() warrant_id = warrant_uid++ - /datum/computer_file/program/digitalwarrant filename = "digitalwarrant" filedesc = "Warrant Assistant" @@ -20,126 +21,119 @@ var/warrant_uid = 0 available_on_ntnet = 1 required_access = access_security usage_flags = PROGRAM_ALL - nanomodule_path = /datum/nano_module/program/digitalwarrant/ + tgui_id = "NtosDigitalWarrant" -/datum/nano_module/program/digitalwarrant/ - name = "Warrant Assistant" var/datum/data/record/warrant/activewarrant -/datum/nano_module/program/digitalwarrant/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = default_state) - var/list/data = host.initial_data() +/datum/computer_file/program/digitalwarrant/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = get_header_data() + + data["warrantname"] = null + data["warrantcharges"] = null + data["warrantauth"] = null + data["type"] = null if(activewarrant) data["warrantname"] = activewarrant.fields["namewarrant"] data["warrantcharges"] = activewarrant.fields["charges"] data["warrantauth"] = activewarrant.fields["auth"] data["type"] = activewarrant.fields["arrestsearch"] - else - var/list/allwarrants = list() - for(var/datum/data/record/warrant/W in data_core.warrants) - allwarrants.Add(list(list( + + var/list/allwarrants = list() + for(var/datum/data/record/warrant/W in data_core.warrants) + allwarrants.Add(list(list( "warrantname" = W.fields["namewarrant"], "charges" = "[copytext(W.fields["charges"],1,min(length(W.fields["charges"]) + 1, 50))]...", "auth" = W.fields["auth"], "id" = W.warrant_id, "arrestsearch" = W.fields["arrestsearch"] ))) - data["allwarrants"] = allwarrants + data["allwarrants"] = allwarrants - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "digitalwarrant.tmpl", name, 500, 350, state = state) - ui.auto_update_layout = 1 - ui.set_initial_data(data) - ui.open() + return data -/datum/nano_module/program/digitalwarrant/Topic(href, href_list) +/datum/computer_file/program/digitalwarrant/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return 1 + return TRUE - if(href_list["sw_menu"]) - activewarrant = null + switch(action) + if("back") + . = TRUE + activewarrant = null - if(href_list["editwarrant"]) - . = 1 - for(var/datum/data/record/warrant/W in data_core.warrants) - if(W.warrant_id == text2num(href_list["editwarrant"])) - activewarrant = W - break + if("editwarrant") + . = TRUE + for(var/datum/data/record/warrant/W in data_core.warrants) + if(W.warrant_id == text2num(params["id"])) + activewarrant = W + break // The following actions will only be possible if the user has an ID with security access equipped. This is in line with modular computer framework's authentication methods, // which also use RFID scanning to allow or disallow access to some functions. Anyone can view warrants, editing requires ID. This also prevents situations where you show a tablet // to someone who is to be arrested, which allows them to change the stuff there. - - var/mob/user = usr - if(!istype(user)) - return - var/obj/item/weapon/card/id/I = user.GetIdCard() + var/obj/item/weapon/card/id/I = usr.GetIdCard() if(!istype(I) || !I.registered_name || !(access_security in I.access)) - to_chat(user, "Authentication error: Unable to locate ID with apropriate access to allow this operation.") + to_chat(usr, "Authentication error: Unable to locate ID with appropriate access to allow this operation.") return - if(href_list["addwarrant"]) - . = 1 - var/datum/data/record/warrant/W = new() - var/temp = sanitize(input(usr, "Do you want to create a search-, or an arrest warrant?") as null|anything in list("search","arrest")) - if(CanInteract(user, default_state)) - if(temp == "arrest") - W.fields["namewarrant"] = "Unknown" - W.fields["charges"] = "No charges present" - W.fields["auth"] = "Unauthorized" - W.fields["arrestsearch"] = "arrest" - if(temp == "search") - W.fields["namewarrant"] = "No suspect/location given" // VOREStation edit - W.fields["charges"] = "No reason given" - W.fields["auth"] = "Unauthorized" - W.fields["arrestsearch"] = "search" - activewarrant = W + switch(action) + if("addwarrant") + . = TRUE + var/datum/data/record/warrant/W = new() + var/temp = sanitize(input(usr, "Do you want to create a search-, or an arrest warrant?") as null|anything in list("search","arrest")) + if(tgui_status(usr, state) == STATUS_INTERACTIVE) + if(temp == "arrest") + W.fields["namewarrant"] = "Unknown" + W.fields["charges"] = "No charges present" + W.fields["auth"] = "Unauthorized" + W.fields["arrestsearch"] = "arrest" + if(temp == "search") + W.fields["namewarrant"] = "No suspect/location given" // VOREStation edit + W.fields["charges"] = "No reason given" + W.fields["auth"] = "Unauthorized" + W.fields["arrestsearch"] = "search" + activewarrant = W - if(href_list["savewarrant"]) - . = 1 - data_core.warrants |= activewarrant - activewarrant = null + if("savewarrant") + . = TRUE + data_core.warrants |= activewarrant + activewarrant = null - if(href_list["deletewarrant"]) - . = 1 - data_core.warrants -= activewarrant - activewarrant = null + if("deletewarrant") + . = TRUE + data_core.warrants -= activewarrant + activewarrant = null - if(href_list["editwarrantname"]) - . = 1 - var/namelist = list() - for(var/datum/data/record/t in data_core.general) - namelist += t.fields["name"] - var/new_name = sanitize(input(usr, "Please input name") as null|anything in namelist) - if(CanInteract(user, default_state)) - if (!new_name) - return - activewarrant.fields["namewarrant"] = new_name + if("editwarrantname") + . = TRUE + var/namelist = list() + for(var/datum/data/record/t in data_core.general) + namelist += t.fields["name"] + var/new_name = sanitize(input(usr, "Please input name") as null|anything in namelist) + if(tgui_status(usr, state) == STATUS_INTERACTIVE) + if (!new_name) + return + activewarrant.fields["namewarrant"] = new_name - if(href_list["editwarrantnamecustom"]) - . = 1 - var/new_name = sanitize(input("Please input name") as null|text) - if(CanInteract(user, default_state)) - if (!new_name) - return - activewarrant.fields["namewarrant"] = new_name + if("editwarrantnamecustom") + . = TRUE + var/new_name = sanitize(input("Please input name") as null|text) + if(tgui_status(usr, state) == STATUS_INTERACTIVE) + if (!new_name) + return + activewarrant.fields["namewarrant"] = new_name - if(href_list["editwarrantcharges"]) - . = 1 - var/new_charges = sanitize(input("Please input charges", "Charges", activewarrant.fields["charges"]) as null|text) - if(CanInteract(user, default_state)) - if (!new_charges) - return - activewarrant.fields["charges"] = new_charges + if("editwarrantcharges") + . = TRUE + var/new_charges = sanitize(input("Please input charges", "Charges", activewarrant.fields["charges"]) as null|text) + if(tgui_status(usr, state) == STATUS_INTERACTIVE) + if (!new_charges) + return + activewarrant.fields["charges"] = new_charges - if(href_list["editwarrantauth"]) - . = 1 - if(!(access_hos in I.access)) // VOREStation edit begin - to_chat(user, "You don't have the access to do this!") - return // VOREStation edit end - activewarrant.fields["auth"] = "[I.registered_name] - [I.assignment ? I.assignment : "(Unknown)"]" - - if(href_list["back"]) - . = 1 - activewarrant = null + if("editwarrantauth") + . = TRUE + if(!(access_hos in I.access)) // VOREStation edit begin + to_chat(usr, "You don't have the access to do this!") + return // VOREStation edit end + activewarrant.fields["auth"] = "[I.registered_name] - [I.assignment ? I.assignment : "(Unknown)"]" diff --git a/code/modules/modular_computers/laptop_vendor.dm b/code/modules/modular_computers/laptop_vendor.dm index ab60291f55..0f67e3b604 100644 --- a/code/modules/modular_computers/laptop_vendor.dm +++ b/code/modules/modular_computers/laptop_vendor.dm @@ -6,8 +6,8 @@ icon = 'icons/obj/vending.dmi' icon_state = "robotics" layer = OBJ_LAYER - 0.1 - anchored = 1 - density = 1 + anchored = TRUE + density = TRUE // The actual laptop/tablet var/obj/item/modular_computer/laptop/fabricated_laptop = null @@ -160,70 +160,77 @@ return total_price return 0 - - - - -/obj/machinery/lapvend/Topic(href, href_list) +/obj/machinery/lapvend/tgui_act(action, params) if(..()) - return 1 + return TRUE - if(href_list["pick_device"]) - if(state) // We've already picked a device type - return 0 - devtype = text2num(href_list["pick_device"]) - state = 1 - fabricate_and_recalc_price(0) - return 1 - if(href_list["clean_order"]) - reset_order() - return 1 + switch(action) + if("pick_device") + if(state) // We've already picked a device type + return FALSE + devtype = text2num(params["pick"]) + state = 1 + fabricate_and_recalc_price(FALSE) + return TRUE + if("clean_order") + reset_order() + return TRUE if((state != 1) && devtype) // Following IFs should only be usable when in the Select Loadout mode - return 0 - if(href_list["confirm_order"]) - state = 2 // Wait for ID swipe for payment processing - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_cpu"]) - dev_cpu = text2num(href_list["hw_cpu"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_battery"]) - dev_battery = text2num(href_list["hw_battery"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_disk"]) - dev_disk = text2num(href_list["hw_disk"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_netcard"]) - dev_netcard = text2num(href_list["hw_netcard"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_tesla"]) - dev_tesla = text2num(href_list["hw_tesla"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_nanoprint"]) - dev_nanoprint = text2num(href_list["hw_nanoprint"]) - fabricate_and_recalc_price(0) - return 1 - if(href_list["hw_card"]) - dev_card = text2num(href_list["hw_card"]) - fabricate_and_recalc_price(0) - return 1 - return 0 + return FALSE + switch(action) + if("confirm_order") + state = 2 // Wait for ID swipe for payment processing + fabricate_and_recalc_price(FALSE) + return TRUE + if("hw_cpu") + dev_cpu = text2num(params["cpu"]) + fabricate_and_recalc_price(FALSE) + return TRUE + if("hw_battery") + dev_battery = text2num(params["battery"]) + fabricate_and_recalc_price(FALSE) + return TRUE + if("hw_disk") + dev_disk = text2num(params["disk"]) + fabricate_and_recalc_price(FALSE) + return TRUE + if("hw_netcard") + dev_netcard = text2num(params["netcard"]) + fabricate_and_recalc_price(FALSE) + return TRUE + if("hw_tesla") + dev_tesla = text2num(params["tesla"]) + fabricate_and_recalc_price(FALSE) + return TRUE + if("hw_nanoprint") + dev_nanoprint = text2num(params["print"]) + fabricate_and_recalc_price(FALSE) + return TRUE + if("hw_card") + dev_card = text2num(params["card"]) + fabricate_and_recalc_price(FALSE) + return TRUE + return FALSE + + /obj/machinery/lapvend/attack_hand(var/mob/user) - ui_interact(user) + tgui_interact(user) -/obj/machinery/lapvend/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/lapvend/tgui_interact(mob/user, datum/tgui/ui) if(stat & (BROKEN | NOPOWER | MAINT)) if(ui) ui.close() - return 0 + return FALSE + + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "ComputerFabricator") + ui.open() + +/obj/machinery/lapvend/tgui_data(mob/user) + var/list/data = list() - var/list/data[0] data["state"] = state if(state == 1) data["devtype"] = devtype @@ -237,13 +244,7 @@ if(state == 1 || state == 2) data["totalprice"] = total_price - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "computer_fabricator.tmpl", "Personal Computer Vendor", 500, 400) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - + return data obj/machinery/lapvend/attackby(obj/item/weapon/W as obj, mob/user as mob) var/obj/item/weapon/card/id/I = W.GetID() @@ -272,7 +273,6 @@ obj/machinery/lapvend/attackby(obj/item/weapon/W as obj, mob/user as mob) return 0 return ..() - // Simplified payment processing, returns 1 on success. /obj/machinery/lapvend/proc/process_payment(var/obj/item/weapon/card/id/I, var/obj/item/ID_container) if(I==ID_container || ID_container == null) @@ -305,4 +305,4 @@ obj/machinery/lapvend/attackby(obj/item/weapon/W as obj, mob/user as mob) T.date = current_date_string T.time = stationtime2text() customer_account.transaction_log.Add(T) - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/nano/interaction/admin.dm b/code/modules/nano/interaction/admin.dm deleted file mode 100644 index dd4ce6ad17..0000000000 --- a/code/modules/nano/interaction/admin.dm +++ /dev/null @@ -1,7 +0,0 @@ -/* - This state checks that the user is an admin, end of story -*/ -/var/global/datum/topic_state/admin_state/admin_state = new() - -/datum/topic_state/admin_state/can_use_topic(var/src_object, var/mob/user) - return check_rights(R_ADMIN|R_EVENT, 0, user) ? STATUS_INTERACTIVE : STATUS_CLOSE diff --git a/code/modules/nano/interaction/base.dm b/code/modules/nano/interaction/base.dm deleted file mode 100644 index eb3e9ae5c1..0000000000 --- a/code/modules/nano/interaction/base.dm +++ /dev/null @@ -1,37 +0,0 @@ -/datum/proc/nano_host() - return src - -/datum/proc/nano_container() - return src - -/datum/proc/CanUseTopic(var/mob/user, var/datum/topic_state/state) - var/src_object = nano_host() - return state.can_use_topic(src_object, user) - -/datum/topic_state/proc/href_list(var/mob/user) - return list() - -/datum/topic_state/proc/can_use_topic(var/src_object, var/mob/user) - return STATUS_CLOSE - -/mob/proc/shared_nano_interaction() - if (src.stat || !client) - return STATUS_CLOSE // no updates, close the interface - else if (incapacitated()) - return STATUS_UPDATE // update only (orange visibility) - return STATUS_INTERACTIVE - -/mob/living/silicon/ai/shared_nano_interaction() - if(lacks_power()) - return STATUS_CLOSE - if (check_unable(1, 0)) - return STATUS_CLOSE - return ..() - -/mob/living/silicon/robot/shared_nano_interaction() - . = STATUS_INTERACTIVE - if(!has_power) - return STATUS_CLOSE - if(lockdown) - . = STATUS_DISABLED - return min(., ..()) diff --git a/code/modules/nano/interaction/conscious.dm b/code/modules/nano/interaction/conscious.dm deleted file mode 100644 index 143bc24956..0000000000 --- a/code/modules/nano/interaction/conscious.dm +++ /dev/null @@ -1,7 +0,0 @@ -/* - This state only checks if user is conscious. -*/ -/var/global/datum/topic_state/conscious_state/conscious_state = new() - -/datum/topic_state/conscious_state/can_use_topic(var/src_object, var/mob/user) - return user.stat == CONSCIOUS ? STATUS_INTERACTIVE : STATUS_CLOSE diff --git a/code/modules/nano/interaction/contained.dm b/code/modules/nano/interaction/contained.dm deleted file mode 100644 index 9ef595d020..0000000000 --- a/code/modules/nano/interaction/contained.dm +++ /dev/null @@ -1,18 +0,0 @@ -/* - This state checks if user is somewhere within src_object, as well as the default NanoUI interaction. -*/ -/var/global/datum/topic_state/contained_state/contained_state = new() - -/datum/topic_state/contained_state/can_use_topic(var/atom/src_object, var/mob/user) - if(!src_object.contains(user)) - return STATUS_CLOSE - - return user.shared_nano_interaction() - -/atom/proc/contains(var/atom/location) - if(!location) - return 0 - if(location == src) - return 1 - - return contains(location.loc) diff --git a/code/modules/nano/interaction/default.dm b/code/modules/nano/interaction/default.dm deleted file mode 100644 index dbb0781d33..0000000000 --- a/code/modules/nano/interaction/default.dm +++ /dev/null @@ -1,91 +0,0 @@ -/var/global/datum/topic_state/default/default_state = new() - -/datum/topic_state/default/href_list(var/mob/user) - return list() - -/datum/topic_state/default/can_use_topic(var/src_object, var/mob/user) - return user.default_can_use_topic(src_object) - -/mob/proc/default_can_use_topic(var/src_object) - return STATUS_CLOSE // By default no mob can do anything with NanoUI - -/mob/observer/dead/default_can_use_topic(var/src_object) - if(can_admin_interact()) - return STATUS_INTERACTIVE // Admins are more equal - if(!client || get_dist(src_object, src) > client.view) // Preventing ghosts from having a million windows open by limiting to objects in range - return STATUS_CLOSE - return STATUS_UPDATE // Ghosts can view updates - -/mob/living/silicon/pai/default_can_use_topic(var/src_object) - if((src_object == src || src_object == radio || src_object == communicator) && !stat) - return STATUS_INTERACTIVE - else - return ..() - -/mob/living/silicon/robot/default_can_use_topic(var/src_object) - . = shared_nano_interaction() - if(. <= STATUS_DISABLED) - return - - // robots can interact with things they can see within their view range - if((src_object in view(src)) && get_dist(src_object, src) <= src.client.view) - return STATUS_INTERACTIVE // interactive (green visibility) - return STATUS_DISABLED // no updates, completely disabled (red visibility) - -/mob/living/silicon/ai/default_can_use_topic(var/src_object) - . = shared_nano_interaction() - if(. != STATUS_INTERACTIVE) - return - - // Prevents the AI from using Topic on admin levels (by for example viewing through the court/thunderdome cameras) - // unless it's on the same level as the object it's interacting with. - var/turf/T = get_turf(src_object) - if(!T || !(z == T.z || (T.z in using_map.player_levels))) - return STATUS_CLOSE - - // If an object is in view then we can interact with it - if(src_object in view(client.view, src)) - return STATUS_INTERACTIVE - - // If we're installed in a chassi, rather than transfered to an inteliCard or other container, then check if we have camera view - if(is_in_chassis()) - //stop AIs from leaving windows open and using then after they lose vision - if(cameranet && !cameranet.checkTurfVis(get_turf(src_object))) - return STATUS_CLOSE - return STATUS_INTERACTIVE - else if(get_dist(src_object, src) <= client.view) // View does not return what one would expect while installed in an inteliCard - return STATUS_INTERACTIVE - - return STATUS_CLOSE - -//Some atoms such as vehicles might have special rules for how mobs inside them interact with NanoUI. -/atom/proc/contents_nano_distance(var/src_object, var/mob/living/user) - return user.shared_living_nano_distance(src_object) - -/mob/living/proc/shared_living_nano_distance(var/atom/movable/src_object) - if (!(src_object in view(4, src))) // If the src object is not in visable, disable updates - return STATUS_CLOSE - - var/dist = get_dist(src_object, src) - if (dist <= 1) - return STATUS_INTERACTIVE // interactive (green visibility) - else if (dist <= 2) - return STATUS_UPDATE // update only (orange visibility) - else if (dist <= 4) - return STATUS_DISABLED // no updates, completely disabled (red visibility) - return STATUS_CLOSE - -/mob/living/default_can_use_topic(var/src_object) - . = shared_nano_interaction(src_object) - if(. != STATUS_CLOSE) - if(loc) - . = min(., loc.contents_nano_distance(src_object, src)) - if(STATUS_INTERACTIVE) - return STATUS_UPDATE - -/mob/living/carbon/human/default_can_use_topic(var/src_object) - . = shared_nano_interaction(src_object) - if(. != STATUS_CLOSE) - . = min(., shared_living_nano_distance(src_object)) - if(. == STATUS_UPDATE && (TK in mutations)) // If we have telekinesis and remain close enough, allow interaction. - return STATUS_INTERACTIVE diff --git a/code/modules/nano/interaction/default_vr.dm b/code/modules/nano/interaction/default_vr.dm deleted file mode 100644 index 963963b3d7..0000000000 --- a/code/modules/nano/interaction/default_vr.dm +++ /dev/null @@ -1,6 +0,0 @@ -/mob/living/simple_mob/default_can_use_topic(var/src_object) - . = shared_nano_interaction(src_object) - if(. != STATUS_CLOSE) - . = min(., shared_living_nano_distance(src_object)) - -//Allows simple mobs to interact with nanoui as long as they have "has_hands = TRUE" \ No newline at end of file diff --git a/code/modules/nano/interaction/interactive.dm b/code/modules/nano/interaction/interactive.dm deleted file mode 100644 index ddd8f8f9e1..0000000000 --- a/code/modules/nano/interaction/interactive.dm +++ /dev/null @@ -1,7 +0,0 @@ -/* - This state always returns STATUS_INTERACTIVE -*/ -/var/global/datum/topic_state/interactive/interactive_state = new() - -/datum/topic_state/interactive/can_use_topic(var/src_object, var/mob/user) - return STATUS_INTERACTIVE diff --git a/code/modules/nano/interaction/inventory.dm b/code/modules/nano/interaction/inventory.dm deleted file mode 100644 index dd320f2bb2..0000000000 --- a/code/modules/nano/interaction/inventory.dm +++ /dev/null @@ -1,11 +0,0 @@ -/* - This state checks that the src_object is somewhere in the user's first-level inventory (in hands, on ear, etc.), but not further down (such as in bags). -*/ -/var/global/datum/topic_state/inventory_state/inventory_state = new() - -/datum/topic_state/inventory_state/can_use_topic(var/src_object, var/mob/user) - if(!(src_object in user)) - return STATUS_CLOSE - - - return user.shared_nano_interaction() diff --git a/code/modules/nano/interaction/inventory_deep.dm b/code/modules/nano/interaction/inventory_deep.dm deleted file mode 100644 index 9637c4df75..0000000000 --- a/code/modules/nano/interaction/inventory_deep.dm +++ /dev/null @@ -1,10 +0,0 @@ -/* - This state checks if src_object is contained anywhere in the user's inventory, including bags, etc. -*/ -/var/global/datum/topic_state/deep_inventory_state/deep_inventory_state = new() - -/datum/topic_state/deep_inventory_state/can_use_topic(var/src_object, var/mob/user) - if(!user.contains(src_object)) - return STATUS_CLOSE - - return user.shared_nano_interaction() diff --git a/code/modules/nano/interaction/inventory_vr.dm b/code/modules/nano/interaction/inventory_vr.dm deleted file mode 100644 index 21ee1ca590..0000000000 --- a/code/modules/nano/interaction/inventory_vr.dm +++ /dev/null @@ -1,32 +0,0 @@ -/* - This state checks that the src_object is on the user's glasses slot. -*/ -/var/global/datum/topic_state/glasses_state/glasses_state = new() - -/datum/topic_state/glasses_state/can_use_topic(var/src_object, var/mob/user) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.glasses == src_object) - return user.shared_nano_interaction() - - return STATUS_CLOSE - -/var/global/datum/topic_state/nif_state/nif_state = new() - -/datum/topic_state/nif_state/can_use_topic(var/src_object, var/mob/user) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.nif && H.nif.stat == NIF_WORKING && src_object == H.nif) - return user.shared_nano_interaction() - - return STATUS_CLOSE - -/var/global/datum/topic_state/commlink_state/commlink_state = new() - -/datum/topic_state/commlink_state/can_use_topic(var/src_object, var/mob/user) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(H.nif && H.nif.stat == NIF_WORKING && H.nif.comm == src_object) - return user.shared_nano_interaction() - - return STATUS_CLOSE diff --git a/code/modules/nano/interaction/outside.dm b/code/modules/nano/interaction/outside.dm deleted file mode 100644 index 9ae0e24fe0..0000000000 --- a/code/modules/nano/interaction/outside.dm +++ /dev/null @@ -1,6 +0,0 @@ -/var/global/datum/topic_state/default/outside/outside_state = new() - -/datum/topic_state/default/outside/can_use_topic(var/src_object, var/mob/user) - if(user in src_object) - return STATUS_CLOSE - return ..() diff --git a/code/modules/nano/interaction/physical.dm b/code/modules/nano/interaction/physical.dm deleted file mode 100644 index 0571008481..0000000000 --- a/code/modules/nano/interaction/physical.dm +++ /dev/null @@ -1,18 +0,0 @@ -/var/global/datum/topic_state/physical/physical_state = new() - -/datum/topic_state/physical/can_use_topic(var/src_object, var/mob/user) - . = user.shared_nano_interaction(src_object) - if(. > STATUS_CLOSE) - return min(., user.check_physical_distance(src_object)) - -/mob/proc/check_physical_distance(var/src_object) - return STATUS_CLOSE - -/mob/observer/dead/check_physical_distance(var/src_object) - return default_can_use_topic(src_object) - -/mob/living/check_physical_distance(var/src_object) - return shared_living_nano_distance(src_object) - -/mob/living/silicon/check_physical_distance(var/src_object) - return max(STATUS_UPDATE, shared_living_nano_distance(src_object)) diff --git a/code/modules/nano/interaction/remote.dm b/code/modules/nano/interaction/remote.dm deleted file mode 100644 index 8e6ded6c84..0000000000 --- a/code/modules/nano/interaction/remote.dm +++ /dev/null @@ -1,39 +0,0 @@ -/* - This state checks that user is capable, within range of the remoter, etc. and that src_object meets the basic requirements for interaction (being powered, non-broken, etc. - Whoever initializes this state is also responsible for deleting it properly. -*/ -/datum/topic_state/remote - var/datum/remoter - var/datum/remote_target - var/datum/topic_state/remoter_state - -/datum/topic_state/remote/New(var/remoter, var/remote_target, var/datum/topic_state/remoter_state = default_state) - src.remoter = remoter - src.remote_target = remote_target - src.remoter_state = remoter_state - ..() - -/datum/topic_state/remote/Destroy() - src.remoter = null - src.remoter_state = null - - // Force an UI update before we go, ensuring that any windows we may have opened for the remote target closes. - SSnanoui.update_uis(remote_target.nano_container()) - remote_target = null - return ..() - -/datum/topic_state/remote/can_use_topic(var/datum/src_object, var/mob/user) - if(!(remoter && remoter_state)) // The remoter is gone, let us leave - return STATUS_CLOSE - - if(src_object != remote_target) - error("remote - Unexpected src_object: Expected '[remote_target]'/[remote_target.type], was '[src_object]'/[src_object.type]") - - // This checks if src_object is powered, etc. - // The interactive state is otherwise simplistic and only returns STATUS_INTERACTIVE and never checks distances, etc. - . = src_object.CanUseTopic(user, interactive_state) - if(. == STATUS_CLOSE) - return - - // This is the (generally) heavy checking, making sure the user is capable, within range of the remoter source, etc. - return min(., remoter.CanUseTopic(user, remoter_state)) diff --git a/code/modules/nano/interaction/self.dm b/code/modules/nano/interaction/self.dm deleted file mode 100644 index 639e50e161..0000000000 --- a/code/modules/nano/interaction/self.dm +++ /dev/null @@ -1,9 +0,0 @@ -/* - This state checks that the src_object is the same the as user -*/ -/var/global/datum/topic_state/self_state/self_state = new() - -/datum/topic_state/self_state/can_use_topic(var/src_object, var/mob/user) - if(src_object != user) - return STATUS_CLOSE - return user.shared_nano_interaction() diff --git a/code/modules/nano/interaction/zlevel.dm b/code/modules/nano/interaction/zlevel.dm deleted file mode 100644 index 80d4c2e601..0000000000 --- a/code/modules/nano/interaction/zlevel.dm +++ /dev/null @@ -1,13 +0,0 @@ -/* - This state checks that the user is on the same Z-level as src_object -*/ - -/var/global/datum/topic_state/z_state/z_state = new() - -/datum/topic_state/z_state/can_use_topic(var/src_object, var/mob/user) - var/turf/turf_obj = get_turf(src_object) - var/turf/turf_usr = get_turf(user) - if(!turf_obj || !turf_usr) - return STATUS_CLOSE - - return turf_obj.z == turf_usr.z ? STATUS_INTERACTIVE : STATUS_CLOSE diff --git a/code/modules/nano/modules/nano_module.dm b/code/modules/nano/modules/nano_module.dm deleted file mode 100644 index 75c38c9ed3..0000000000 --- a/code/modules/nano/modules/nano_module.dm +++ /dev/null @@ -1,67 +0,0 @@ -/datum/nano_module - var/name - var/datum/host - var/datum/topic_manager/topic_manager - var/list/using_access - -/datum/nano_module/New(var/datum/host, var/topic_manager) - ..() - src.host = host.nano_host() - src.topic_manager = topic_manager - -/datum/nano_module/nano_host() - return host ? host : src - -/datum/nano_module/proc/can_still_topic(var/datum/topic_state/state = default_state) - return CanUseTopic(usr, state) == STATUS_INTERACTIVE - -/datum/nano_module/proc/check_eye(var/mob/user) - return -1 - -/datum/nano_module/proc/check_access(var/mob/user, var/access) - if(!access) - return 1 - - if(using_access) - if(access in using_access) - return 1 - else - return 0 - - if(!istype(user)) - return 0 - - var/obj/item/weapon/card/id/I = user.GetIdCard() - if(!I) - return 0 - - if(access in I.access) - return 1 - - return 0 - -/datum/nano_module/Topic(href, href_list) - if(topic_manager && topic_manager.Topic(href, href_list)) - return TRUE - . = ..() - -/datum/nano_module/proc/print_text(var/text, var/mob/user) - var/obj/item/modular_computer/MC = nano_host() - if(istype(MC)) - if(!MC.nano_printer) - to_chat(user, "Error: No printer detected. Unable to print document.") - return - - if(!MC.nano_printer.print_text(text)) - to_chat(user, "Error: Printer was unable to print the document. It may be out of paper.") - else - to_chat(user, "Error: Unable to detect compatible printer interface. Are you running NTOSv2 compatible system?") - -/datum/proc/initial_data() - return list() - -/datum/proc/update_layout() - return FALSE - -/datum/nano_module/proc/relaymove(var/mob/user, direction) - return FALSE diff --git a/code/modules/nano/nanoexternal.dm b/code/modules/nano/nanoexternal.dm deleted file mode 100644 index 08f9deffa9..0000000000 --- a/code/modules/nano/nanoexternal.dm +++ /dev/null @@ -1,44 +0,0 @@ - // This file contains all Nano procs/definitions for external classes/objects - - /** - * Called when a Nano UI window is closed - * This is how Nano handles closed windows - * It must be a verb so that it can be called using winset - * - * @return nothing - */ -/client/verb/nanoclose(var/uiref as text) - set hidden = 1 // hide this verb from the user's panel - set name = "nanoclose" - - var/datum/nanoui/ui = locate(uiref) - - if (istype(ui)) - ui.close() - - if(ui.ref) - var/href = "close=1" - src.Topic(href, params2list(href), ui.ref) // this will direct to the atom's Topic() proc via client.Topic() - else if (ui.on_close_logic) - // no atomref specified (or not found) - // so just reset the user mob's machine var - if(src && src.mob) - src.mob.unset_machine() - - /** - * The ui_interact proc is used to open and update Nano UIs - * If ui_interact is not used then the UI will not update correctly - * ui_interact is currently defined for /atom/movable - * - * @param user /mob The mob who is interacting with this ui - * @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main") - * @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui - * @param force_open boolean Force the UI to (re)open, even if it's already open - * - * @return nothing - */ -/datum/proc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/nano_ui/master_ui = null, var/datum/topic_state/state = default_state) - return - -// Used by the Nano UI Manager (/datum/nanomanager) to track UIs opened by this mob -/mob/var/list/open_uis = list() diff --git a/code/modules/nano/nanomanager.dm b/code/modules/nano/nanomanager.dm deleted file mode 100644 index 69b0e7b77e..0000000000 --- a/code/modules/nano/nanomanager.dm +++ /dev/null @@ -1,224 +0,0 @@ - - /** - * Get an open /nanoui ui for the current user, src_object and ui_key and try to update it with data - * - * @param user /mob The mob who opened/owns the ui - * @param src_object /obj|/mob The obj or mob which the ui belongs to - * @param ui_key string A string key used for the ui - * @param ui /datum/nanoui An existing instance of the ui (can be null) - * @param data list The data to be passed to the ui, if it exists - * @param force_open boolean The ui is being forced to (re)open, so close ui if it exists (instead of updating) - * - * @return /nanoui Returns the found ui, for null if none exists - */ -/datum/controller/subsystem/nanoui/proc/try_update_ui(var/mob/user, src_object, ui_key, var/datum/nanoui/ui, data, var/force_open = 0) - if (isnull(ui)) // no ui has been passed, so we'll search for one - { - ui = get_open_ui(user, src_object, ui_key) - } - if (!isnull(ui)) - // The UI is already open - if (!force_open) - ui.push_data(data) - return ui - else - ui.reinitialise(new_initial_data=data) - return ui - - return null - - /** - * Get an open /nanoui ui for the current user, src_object and ui_key - * - * @param user /mob The mob who opened/owns the ui - * @param src_object /obj|/mob The obj or mob which the ui belongs to - * @param ui_key string A string key used for the ui - * - * @return /nanoui Returns the found ui, or null if none exists - */ -/datum/controller/subsystem/nanoui/proc/get_open_ui(var/mob/user, src_object, ui_key) - var/src_object_key = "\ref[src_object]" - if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - there are no uis open") - return null - else if (isnull(open_uis[src_object_key][ui_key]) || !istype(open_uis[src_object_key][ui_key], /list)) - //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - there are no uis open for this object") - return null - - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) - if (ui.user == user) - return ui - - //testing("nanomanager/get_open_ui mob [user.name] [src_object:name] [ui_key] - ui not found") - return null - - /** - * Update all /nanoui uis attached to src_object - * - * @param src_object /obj|/mob The obj or mob which the uis are attached to - * - * @return int The number of uis updated - */ -/datum/controller/subsystem/nanoui/proc/update_uis(src_object) - var/src_object_key = "\ref[src_object]" - if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return 0 - - var/update_count = 0 - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) - ui.process(1) - update_count++ - return update_count - - /** - * Close all /nanoui uis attached to src_object - * - * @param src_object /obj|/mob The obj or mob which the uis are attached to - * - * @return int The number of uis close - */ -/datum/controller/subsystem/nanoui/proc/close_uis(src_object) - var/src_object_key = "\ref[src_object]" - if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return 0 - - var/close_count = 0 - for (var/ui_key in open_uis[src_object_key]) - for (var/datum/nanoui/ui in open_uis[src_object_key][ui_key]) - if(ui && ui.src_object && ui.user && ui.src_object.nano_host()) - ui.close() - close_count++ - return close_count - - /** - * Update /nanoui uis belonging to user - * - * @param user /mob The mob who owns the uis - * @param src_object /obj|/mob If src_object is provided, only update uis which are attached to src_object (optional) - * @param ui_key string If ui_key is provided, only update uis with a matching ui_key (optional) - * - * @return int The number of uis updated - */ -/datum/controller/subsystem/nanoui/proc/update_user_uis(var/mob/user, src_object = null, ui_key = null) - if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) - return 0 // has no open uis - - var/update_count = 0 - for (var/datum/nanoui/ui in user.open_uis) - if ((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) - ui.process(1) - update_count++ - - return update_count - - /** - * Close /nanoui uis belonging to user - * - * @param user /mob The mob who owns the uis - * @param src_object /obj|/mob If src_object is provided, only close uis which are attached to src_object (optional) - * @param ui_key string If ui_key is provided, only close uis with a matching ui_key (optional) - * - * @return int The number of uis closed - */ -/datum/controller/subsystem/nanoui/proc/close_user_uis(var/mob/user, src_object = null, ui_key = null) - if (isnull(user.open_uis) || !istype(user.open_uis, /list) || open_uis.len == 0) - //testing("nanomanager/close_user_uis mob [user.name] has no open uis") - return 0 // has no open uis - - var/close_count = 0 - for (var/datum/nanoui/ui in user.open_uis) - if ((isnull(src_object) || !isnull(src_object) && ui.src_object == src_object) && (isnull(ui_key) || !isnull(ui_key) && ui.ui_key == ui_key)) - ui.close() - close_count++ - - //testing("nanomanager/close_user_uis mob [user.name] closed [open_uis.len] of [close_count] uis") - - return close_count - - /** - * Add a /nanoui ui to the list of open uis - * This is called by the /nanoui open() proc - * - * @param ui /nanoui The ui to add - * - * @return nothing - */ -/datum/controller/subsystem/nanoui/proc/ui_opened(var/datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - open_uis[src_object_key] = list(ui.ui_key = list()) - else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) - open_uis[src_object_key][ui.ui_key] = list(); - - ui.user.open_uis |= ui - var/list/uis = open_uis[src_object_key][ui.ui_key] - uis |= ui - processing_uis |= ui - //testing("nanomanager/ui_opened mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_uis [ui.user.open_uis.len] | uis [uis.len] | processing_uis [processing_uis.len]") - - /** - * Remove a /nanoui ui from the list of open uis - * This is called by the /nanoui close() proc - * - * @param ui /nanoui The ui to remove - * - * @return int 0 if no ui was removed, 1 if removed successfully - */ -/datum/controller/subsystem/nanoui/proc/ui_closed(var/datum/nanoui/ui) - var/src_object_key = "\ref[ui.src_object]" - if (isnull(open_uis[src_object_key]) || !istype(open_uis[src_object_key], /list)) - return 0 // wasn't open - else if (isnull(open_uis[src_object_key][ui.ui_key]) || !istype(open_uis[src_object_key][ui.ui_key], /list)) - return 0 // wasn't open - - processing_uis.Remove(ui) - if(ui.user) // Sanity check in case a user has been deleted (say a blown up borg watching the alarm interface) - ui.user.open_uis.Remove(ui) - var/list/uis = open_uis[src_object_key][ui.ui_key] - uis.Remove(ui) - - //testing("nanomanager/ui_closed mob [ui.user.name] [ui.src_object:name] [ui.ui_key] - user.open_uis [ui.user.open_uis.len] | uis [uis.len] | processing_uis [processing_uis.len]") - - return 1 - - /** - * This is called on user logout - * Closes/clears all uis attached to the user's /mob - * - * @param user /mob The user's mob - * - * @return nothing - */ - -// -/datum/controller/subsystem/nanoui/proc/user_logout(var/mob/user) - //testing("nanomanager/user_logout user [user.name]") - return close_user_uis(user) - - /** - * This is called when a player transfers from one mob to another - * Transfers all open UIs to the new mob - * - * @param oldMob /mob The user's old mob - * @param newMob /mob The user's new mob - * - * @return nothing - */ -/datum/controller/subsystem/nanoui/proc/user_transferred(var/mob/oldMob, var/mob/newMob) - //testing("nanomanager/user_transferred from mob [oldMob.name] to mob [newMob.name]") - if (!oldMob || isnull(oldMob.open_uis) || !istype(oldMob.open_uis, /list) || open_uis.len == 0) - //testing("nanomanager/user_transferred mob [oldMob.name] has no open uis") - return 0 // has no open uis - - if (isnull(newMob.open_uis) || !istype(newMob.open_uis, /list)) - newMob.open_uis = list() - - for (var/datum/nanoui/ui in oldMob.open_uis) - ui.user = newMob - newMob.open_uis.Add(ui) - - oldMob.open_uis.Cut() - - return 1 // success \ No newline at end of file diff --git a/code/modules/nano/nanomapgen.dm b/code/modules/nano/nanomapgen.dm deleted file mode 100644 index d5a7cfaf69..0000000000 --- a/code/modules/nano/nanomapgen.dm +++ /dev/null @@ -1,92 +0,0 @@ -// This file is a modified version of https://raw2.github.com/Baystation12/OldCode-BS12/master/code/TakePicture.dm - -#define NANOMAP_ICON_SIZE 4 -#define NANOMAP_MAX_ICON_DIMENSION 1200 - -#define NANOMAP_TILES_PER_IMAGE (NANOMAP_MAX_ICON_DIMENSION / NANOMAP_ICON_SIZE) - -#define NANOMAP_TERMINALERR 5 -#define NANOMAP_INPROGRESS 2 -#define NANOMAP_BADOUTPUT 2 -#define NANOMAP_SUCCESS 1 -#define NANOMAP_WATCHDOGSUCCESS 4 -#define NANOMAP_WATCHDOGTERMINATE 3 - - -//Call these procs to dump your world to a series of image files (!!) -//NOTE: Does not explicitly support non 32x32 icons or stuff with large pixel_* values, so don't blame me if it doesn't work perfectly - -/client/proc/nanomapgen_DumpImage() - set name = "Generate NanoUI Map" - set category = "Server" - - if(holder) - nanomapgen_DumpTile(1, 1, text2num(input(usr,"Enter the Z level to generate"))) - -/client/proc/nanomapgen_DumpTile(var/startX = 1, var/startY = 1, var/currentZ = 1, var/endX = -1, var/endY = -1) - - if (endX < 0 || endX > world.maxx) - endX = world.maxx - - if (endY < 0 || endY > world.maxy) - endY = world.maxy - - if (currentZ < 0 || currentZ > world.maxz) - to_chat(usr, "NanoMapGen: ERROR: currentZ ([currentZ]) must be between 1 and [world.maxz]") - - sleep(3) - return NANOMAP_TERMINALERR - - if (startX > endX) - to_chat(usr, "NanoMapGen: ERROR: startX ([startX]) cannot be greater than endX ([endX])") - - sleep(3) - return NANOMAP_TERMINALERR - - if (startY > endX) - to_chat(usr, "NanoMapGen: ERROR: startY ([startY]) cannot be greater than endY ([endY])") - sleep(3) - return NANOMAP_TERMINALERR - - var/icon/Tile = icon(file("nano/mapbase1200.png")) - if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) - to_world_log("NanoMapGen: ERROR: BASE IMAGE DIMENSIONS ARE NOT [NANOMAP_MAX_ICON_DIMENSION]x[NANOMAP_MAX_ICON_DIMENSION]") - sleep(3) - return NANOMAP_TERMINALERR - - Tile.Scale((endX - startX + 1) * NANOMAP_ICON_SIZE, (endY - startY + 1) * NANOMAP_ICON_SIZE) // VOREStation Edit - Scale image to actual size mapped. - - to_world_log("NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])") - to_chat(usr, "NanoMapGen: GENERATE MAP ([startX],[startY],[currentZ]) to ([endX],[endY],[currentZ])") - - var/count = 0; - for(var/WorldX = startX, WorldX <= endX, WorldX++) - for(var/WorldY = startY, WorldY <= endY, WorldY++) - - var/atom/Turf = locate(WorldX, WorldY, currentZ) - - var/icon/TurfIcon = new(Turf.icon, Turf.icon_state) - TurfIcon.Scale(NANOMAP_ICON_SIZE, NANOMAP_ICON_SIZE) - - Tile.Blend(TurfIcon, ICON_OVERLAY, ((WorldX - 1) * NANOMAP_ICON_SIZE), ((WorldY - 1) * NANOMAP_ICON_SIZE)) - - count++ - - if (count % 8000 == 0) - to_world_log("NanoMapGen: [count] tiles done") - sleep(1) - - var/mapFilename = "nanomap_z[currentZ]-new.png" - - to_world_log("NanoMapGen: sending [mapFilename] to client") - - usr << browse(Tile, "window=picture;file=[mapFilename];display=0") - - to_world_log("NanoMapGen: Done.") - - to_chat(usr, "NanoMapGen: Done. File [mapFilename] uploaded to your cache.") - - if (Tile.Width() != NANOMAP_MAX_ICON_DIMENSION || Tile.Height() != NANOMAP_MAX_ICON_DIMENSION) - return NANOMAP_BADOUTPUT - - return NANOMAP_SUCCESS \ No newline at end of file diff --git a/code/modules/nano/nanoui.dm b/code/modules/nano/nanoui.dm deleted file mode 100644 index 900f5b9d33..0000000000 --- a/code/modules/nano/nanoui.dm +++ /dev/null @@ -1,528 +0,0 @@ -/********************************************************** -NANO UI FRAMEWORK - -nanoui class (or whatever Byond calls classes) - -nanoui is used to open and update nano browser uis -**********************************************************/ - -/datum/nanoui - // the user who opened this ui - var/mob/user - // the object this ui "belongs" to - var/datum/src_object - // the title of this ui - var/title - // the key of this ui, this is to allow multiple (different) uis for each src_object - var/ui_key - // window_id is used as the window name/identifier for browse and onclose - var/window_id - // the browser window width - var/width = 0 - // the browser window height - var/height = 0 - // whether to use extra logic when window closes - var/on_close_logic = 1 - // an extra ref to use when the window is closed, usually null - var/atom/ref = null - // options for modifying window behaviour - var/window_options = "focus=0;can_close=1;can_minimize=1;can_maximize=0;can_resize=1;titlebar=1;" // window option is set using window_id - // the list of stylesheets to apply to this ui - var/list/stylesheets = list() - // the list of javascript scripts to use for this ui - var/list/scripts = list() - // a list of templates which can be used with this ui - var/templates[0] - // the layout key for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing) - var/layout_key = "default" - // this sets whether to re-render the ui layout with each update (default 0, turning on will break the map ui if it's in use) - var/auto_update_layout = 0 - // this sets whether to re-render the ui content with each update (default 1) - var/auto_update_content = 1 - // the default state to use for this ui (this is used on the frontend, leave it as "default" unless you know what you're doing) - var/state_key = "default" - // show the map ui, this is used by the default layout - var/show_map = 0 - // the map z level to display - var/map_z_level = 1 - // initial data, containing the full data structure, must be sent to the ui (the data structure cannot be extended later on) - var/list/initial_data[0] - // set to 1 to update the ui automatically every master_controller tick - var/is_auto_updating = 0 - // the current status/visibility of the ui - var/status = STATUS_INTERACTIVE - - // Relationship between a master interface and its children. Used in update_status - var/datum/nanoui/master_ui - var/list/datum/nanoui/children = list() - var/datum/topic_state/state = null - - /** - * Create a new nanoui instance. - * - * @param nuser /mob The mob who has opened/owns this ui - * @param nsrc_object /obj|/mob The obj or mob which this ui belongs to - * @param nui_key string A string key to use for this ui. Allows for multiple unique uis on one src_oject - * @param ntemplate string The filename of the template file from /nano/templates (e.g. "my_template.tmpl") - * @param ntitle string The title of this ui - * @param nwidth int the width of the ui window - * @param nheight int the height of the ui window - * @param nref /atom A custom ref to use if "on_close_logic" is set to 1 - * - * @return /nanoui new nanoui object - */ -/datum/nanoui/New(nuser, nsrc_object, nui_key, ntemplate_filename, ntitle = 0, nwidth = 0, nheight = 0, var/atom/nref = null, var/datum/nanoui/master_ui = null, var/datum/topic_state/state = default_state) - user = nuser - src_object = nsrc_object - ui_key = nui_key - window_id = "[ui_key]\ref[src_object]" - - src.master_ui = master_ui - if(master_ui) - master_ui.children += src - src.state = state - - // add the passed template filename as the "main" template, this is required - add_template("main", ntemplate_filename) - - if (ntitle) - title = sanitize(ntitle) - if (nwidth) - width = nwidth - if (nheight) - height = nheight - if (nref) - ref = nref - - add_common_assets() - - /** - * Use this proc to add assets which are common to (and required by) all nano uis - * - * @return nothing - */ -/datum/nanoui/proc/add_common_assets() - add_script("libraries.min.js") // A JS file comprising of jQuery, doT.js and jQuery Timer libraries (compressed together) - add_script("nano_utility.js") // The NanoUtility JS, this is used to store utility functions. - add_script("nano_templates_bundle.js") // Contains all templates, generated by asset cache system at server start. - add_script("nano_template.js") // The NanoTemplate JS, this is used to render templates. - add_script("nano_state_manager.js") // The NanoStateManager JS, it handles updates from the server and passes data to the current state - add_script("nano_state.js") // The NanoState JS, this is the base state which all states must inherit from - add_script("nano_state_default.js") // The NanoStateDefault JS, this is the "default" state (used by all UIs by default), which inherits from NanoState - add_script("nano_base_callbacks.js") // The NanoBaseCallbacks JS, this is used to set up (before and after update) callbacks which are common to all UIs - add_script("nano_base_helpers.js") // The NanoBaseHelpers JS, this is used to set up template helpers which are common to all UIs - add_stylesheet("shared.css") // this CSS sheet is common to all UIs - add_stylesheet("shared_vr.css") // VOREStation Add - add_stylesheet("icons.css") // this CSS sheet is common to all UIs - - /** - * Set the current status (also known as visibility) of this ui. - * - * @param state int The status to set, see the defines at the top of this file - * @param push_update int (bool) Push an update to the ui to update it's status (an update is always sent if the status has changed to red (0)) - * - * @return nothing - */ -/datum/nanoui/proc/set_status(state, push_update) - if (state != status) // Only update if it is different - if (status == STATUS_DISABLED) - status = state - if (push_update) - update() - else - status = state - if (push_update || status == 0) - push_data(null, 1) // Update the UI, force the update in case the status is 0, data is null so that previous data is used - - /** - * Update the status (visibility) of this ui based on the user's status - * - * @param push_update int (bool) Push an update to the ui to update it's status. This is set to 0/false if an update is going to be pushed anyway (to avoid unnessary updates) - * - * @return nothing - */ -/datum/nanoui/proc/update_status(var/push_update = 0) - var/obj/host = src_object.nano_host() - var/new_status = host.CanUseTopic(user, state) - if(master_ui) - new_status = min(new_status, master_ui.status) - - set_status(new_status, push_update) - if(new_status == STATUS_CLOSE) - close() - - /** - * Set the ui to auto update (every master_controller tick) - * - * @param state int (bool) Set auto update to 1 or 0 (true/false) - * - * @return nothing - */ -/datum/nanoui/proc/set_auto_update(nstate = 1) - is_auto_updating = nstate - - /** - * Set the initial data for the ui. This is vital as the data structure set here cannot be changed when pushing new updates. - * - * @param data /list The list of data for this ui - * - * @return nothing - */ -/datum/nanoui/proc/set_initial_data(list/data) - initial_data = data - - /** - * Get config data to sent to the ui. - * - * @return /list config data - */ -/datum/nanoui/proc/get_config_data() - var/name = "[src_object]" - name = sanitize(name) - var/list/config_data = list( - "title" = title, - "srcObject" = list("name" = name), - "stateKey" = state_key, - "status" = status, - "autoUpdateLayout" = auto_update_layout, - "autoUpdateContent" = auto_update_content, - "showMap" = show_map, - "mapZLevel" = map_z_level, - "user" = list("name" = user.name) - ) - return config_data - - /** - * Get data to sent to the ui. - * - * @param data /list The list of general data for this ui (can be null to use previous data sent) - * - * @return /list data to send to the ui - */ -/datum/nanoui/proc/get_send_data(var/list/data) - var/list/config_data = get_config_data() - - var/list/send_data = list("config" = config_data) - - if (!isnull(data)) - send_data["data"] = data - - return send_data - - /** - * Set the browser window options for this ui - * - * @param nwindow_options string The new window options - * - * @return nothing - */ -/datum/nanoui/proc/set_window_options(nwindow_options) - window_options = nwindow_options - - /** - * Add a CSS stylesheet to this UI - * These must be added before the UI has been opened, adding after that will have no effect - * - * @param file string The name of the CSS file from /nano/css (e.g. "my_style.css") - * - * @return nothing - */ -/datum/nanoui/proc/add_stylesheet(file) - stylesheets.Add(file) - - /** - * Add a JavsScript script to this UI - * These must be added before the UI has been opened, adding after that will have no effect - * - * @param file string The name of the JavaScript file from /nano/js (e.g. "my_script.js") - * - * @return nothing - */ -/datum/nanoui/proc/add_script(file) - scripts.Add(file) - - /** - * Add a template for this UI - * Templates are combined with the data sent to the UI to create the rendered view - * These must be added before the UI has been opened, adding after that will have no effect - * - * @param key string The key which is used to reference this template in the frontend - * @param filename string The name of the template file from /nano/templates (e.g. "my_template.tmpl") - * - * @return nothing - */ -/datum/nanoui/proc/add_template(key, filename) - templates[key] = filename - - /** - * Set the layout key for use in the frontend Javascript - * The layout key is the basic layout key for the page - * Two files are loaded on the client based on the layout key varable: - * -> a template in /nano/templates with the filename "layout_.tmpl - * -> a CSS stylesheet in /nano/css with the filename "layout_.css - * - * @param nlayout string The layout key to use - * - * @return nothing - */ -/datum/nanoui/proc/set_layout_key(nlayout_key) - layout_key = lowertext(nlayout_key) - - /** - * Set the ui to update the layout (re-render it) on each update, turning this on will break the map ui (if it's being used) - * - * @param state int (bool) Set update to 1 or 0 (true/false) (default 0) - * - * @return nothing - */ -/datum/nanoui/proc/set_auto_update_layout(nstate) - auto_update_layout = nstate - - /** - * Set the ui to update the main content (re-render it) on each update - * - * @param state int (bool) Set update to 1 or 0 (true/false) (default 1) - * - * @return nothing - */ -/datum/nanoui/proc/set_auto_update_content(nstate) - auto_update_content = nstate - - /** - * Set the state key for use in the frontend Javascript - * - * @param nstate_key string The key of the state to use - * - * @return nothing - */ -/datum/nanoui/proc/set_state_key(nstate_key) - state_key = nstate_key - - /** - * Toggle showing the map ui - * - * @param nstate_key boolean 1 to show map, 0 to hide (default is 0) - * - * @return nothing - */ -/datum/nanoui/proc/set_show_map(nstate) - show_map = nstate - - /** - * Toggle showing the map ui - * - * @param nstate_key boolean 1 to show map, 0 to hide (default is 0) - * - * @return nothing - */ -/datum/nanoui/proc/set_map_z_level(nz) - map_z_level = nz - - /** - * Set whether or not to use the "old" on close logic (mainly unset_machine()) - * - * @param state int (bool) Set on_close_logic to 1 or 0 (true/false) - * - * @return nothing - */ -/datum/nanoui/proc/use_on_close_logic(state) - on_close_logic = state - - /** - * Return the HTML for this UI - * - * @return string HTML for the UI - */ -/datum/nanoui/proc/get_html() - - // before the UI opens, add the layout files based on the layout key - add_stylesheet("layout_[layout_key].css") - add_template("layout", "layout_[layout_key].tmpl") - - var/head_content = "" - - for (var/filename in scripts) - head_content += " " - - for (var/filename in stylesheets) - head_content += " " - - var/template_data_json = "{}" // An empty JSON object - if (templates.len > 0) - template_data_json = strip_improper(json_encode(templates)) - - var/list/send_data = get_send_data(initial_data) - var/initial_data_json = replacetext(replacetext(json_encode(send_data), """, "&#34;"), "'", "'") - initial_data_json = strip_improper(initial_data_json); - - var/url_parameters_json = json_encode(list("src" = "\ref[src]")) - - return {" - - - - - - - [head_content] - - -
-
- - - - "} - - /** - * Open this UI - * - * @return nothing - */ -/datum/nanoui/proc/open() - if(!user.client) - return - - // An attempted fix to UIs sometimes locking up spamming runtime errors due to src_object being null for whatever reason. - // This hard-deletes the UI, preventing the device that uses the UI from being locked up permanently. - if(!src_object) - del(src) - - var/window_size = "" - if (width && height) - window_size = "size=[width]x[height];" - update_status(0) - if(status == STATUS_CLOSE) - return - - user << browse(get_html(), "window=[window_id];[window_size][window_options]") - winset(user, "mapwindow.map", "focus=true") // return keyboard focus to map - on_close_winset() - //onclose(user, window_id) - SSnanoui.ui_opened(src) - - /** - * Reinitialise this UI, potentially with a different template and/or initial data - * - * @return nothing - */ -/datum/nanoui/proc/reinitialise(template, new_initial_data) - if(template) - add_template("main", template) - if(new_initial_data) - set_initial_data(new_initial_data) - open() - - /** - * Close this UI - * - * @return nothing - */ -/datum/nanoui/proc/close() - is_auto_updating = 0 - SSnanoui.ui_closed(src) - user << browse(null, "window=[window_id]") - for(var/datum/nanoui/child in children) - child.close() - children.Cut() - state = null - master_ui = null - - /** - * Set the UI window to call the nanoclose verb when the window is closed - * This allows Nano to handle closed windows - * - * @return nothing - */ -/datum/nanoui/proc/on_close_winset() - if(!user.client) - return - var/params = "\ref[src]" - - winset(user, window_id, "on-close=\"nanoclose [params]\"") - - /** - * Push data to an already open UI window - * - * @return nothing - */ -/datum/nanoui/proc/push_data(data, force_push = 0) - update_status(0) - if (status == STATUS_DISABLED && !force_push) - return // Cannot update UI, no visibility - - var/list/send_data = get_send_data(data) - - //to_chat(user,list2json_usecache(send_data)) // used for debugging //NANO DEBUG HOOK - user << output(list2params(list(strip_improper(json_encode(send_data)))),"[window_id].browser:receiveUpdateData") - - /** - * This Topic() proc is called whenever a user clicks on a link within a Nano UI - * If the UI status is currently STATUS_INTERACTIVE then call the src_object Topic() - * If the src_object Topic() returns 1 (true) then update all UIs attached to src_object - * - * @return nothing - */ -/datum/nanoui/Topic(href, href_list) - update_status(0) // update the status - if (status != STATUS_INTERACTIVE || user != usr) // If UI is not interactive or usr calling Topic is not the UI user - return - - // This is used to toggle the nano map ui - var/map_update = 0 - if(href_list["showMap"]) - set_show_map(text2num(href_list["showMap"])) - map_update = 1 - - if(href_list["mapZLevel"]) - set_map_z_level(text2num(href_list["mapZLevel"])) - map_update = 1 - - if ((src_object && src_object.Topic(href, href_list, state)) || map_update) - SSnanoui.update_uis(src_object) // update all UIs attached to src_object - - /** - * Process this UI, updating the entire UI or just the status (aka visibility) - * This process proc is called by the master_controller - * - * @param update string For this UI to update - * - * @return nothing - */ -/datum/nanoui/process(update = 0) - if (!src_object || !user) - close() - return - - if (status && (update || is_auto_updating)) - update() // Update the UI (update_status() is called whenever a UI is updated) - else - update_status(1) // Not updating UI, so lets check here if status has changed - - /** - * Update the UI - * - * @return nothing - */ -/datum/nanoui/proc/update(var/force_open = 0) - src_object.ui_interact(user, ui_key, src, force_open, master_ui, state) diff --git a/code/modules/nifsoft/nif.dm b/code/modules/nifsoft/nif.dm index d03a2adf07..a0ad5092b8 100644 --- a/code/modules/nifsoft/nif.dm +++ b/code/modules/nifsoft/nif.dm @@ -101,6 +101,9 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable //Draw me yo. update_icon() + if(!our_statclick) + our_statclick = new(null, "Open", src) + //Destructor cleans up references /obj/item/device/nif/Destroy() human = null @@ -356,6 +359,8 @@ You can also set the stat of a NIF to NIF_TEMPFAIL without any issues to disable /obj/item/device/nif/proc/notify(var/message,var/alert = 0) if(!human || stat == NIF_TEMPFAIL) return + last_notification = message // TGUI Hook + to_chat(human,"\[[bicon(src.big_icon)]NIF\] displays, \"[message]\"") if(prob(1)) human.visible_message("\The [human] [pick(look_messages)].") if(alert) diff --git a/code/modules/nifsoft/nif_statpanel.dm b/code/modules/nifsoft/nif_statpanel.dm index 0cf2307b65..00e928429f 100644 --- a/code/modules/nifsoft/nif_statpanel.dm +++ b/code/modules/nifsoft/nif_statpanel.dm @@ -22,6 +22,7 @@ nif_status = "Unknown - Error" nif_status += " (Condition: [nif_percent]%)" stat("NIF Status", nif_status) + stat("UI", nif.our_statclick) // TGUI Hook if(nif.stat == NIF_WORKING) stat("- Modules -", "LMB: Toggle, Shift+LMB: Info/Uninstall") diff --git a/code/modules/nifsoft/nif_tgui.dm b/code/modules/nifsoft/nif_tgui.dm new file mode 100644 index 0000000000..23d8102f79 --- /dev/null +++ b/code/modules/nifsoft/nif_tgui.dm @@ -0,0 +1,114 @@ +/************************************************************************\ + * This module controls everything to do with the NIF's tgui interface. * +\************************************************************************/ +/** + * Etc variables on the NIF to keep this self contained + */ +/obj/item/device/nif + var/static/list/valid_ui_themes = list( + "abductor", + "cardtable", + "hackerman", + "malfunction", + "ntos", + "paper", + "retro", + "syndicate" + ) + var/tmp/obj/effect/statclick/nif_open/our_statclick + var/tmp/last_notification + +/** + * Special stat button for the interface + */ +/obj/effect/statclick/nif_open +/obj/effect/statclick/nif_open/Click(location, control, params) + var/obj/item/device/nif/N = target + if(istype(N)) + N.tgui_interact(usr) + +/** + * The NIF State ensures that only our authorized implanted user can touch us. + */ +/obj/item/device/nif/tgui_state(mob/user) + return GLOB.tgui_nif_main_state + +/** + * Standard TGUI stub to open the NIF.js template. + */ +/obj/item/device/nif/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "NIF", name) + ui.open() + +/** + * tgui_data gives the UI any relevant data it needs. + * In our case, that's basically everything from our statpanel. + */ +/obj/item/device/nif/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["theme"] = save_data["ui_theme"] + data["last_notification"] = last_notification + + // Random biometric information + data["nutrition"] = human.nutrition + data["isSynthetic"] = human.isSynthetic() + + data["nif_percent"] = round((durability/initial(durability))*100) + data["nif_stat"] = stat + + data["modules"] = list() + if(stat == NIF_WORKING) + for(var/nifsoft in nifsofts) + if(!nifsoft) + continue + var/datum/nifsoft/NS = nifsoft + data["modules"].Add(list(list( + "name" = NS.name, + "desc" = NS.desc, + "p_drain" = NS.p_drain, + "a_drain" = NS.a_drain, + "illegal" = NS.illegal, + "wear" = NS.wear, + "cost" = NS.cost, + "activates" = NS.activates, + "active" = NS.active, + "stat_text" = NS.stat_text(), + "ref" = REF(NS), + ))) + + return data + +/** + * tgui_act handles all user input in the UI. + */ +/obj/item/device/nif/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("setTheme") + if((params["theme"] in valid_ui_themes) || params["theme"] == null) + save_data["ui_theme"] = params["theme"] + return TRUE + if("toggle_module") + var/datum/nifsoft/NS = locate(params["module"]) in nifsofts + if(!istype(NS)) + return + if(NS.activates) + if(NS.active) + NS.deactivate() + else + NS.activate() + return TRUE + if("uninstall") + var/datum/nifsoft/NS = locate(params["module"]) in nifsofts + if(!istype(NS)) + return + NS.uninstall() + return TRUE + if("dismissNotification") + last_notification = null + return TRUE \ No newline at end of file diff --git a/code/modules/nifsoft/nifsoft.dm b/code/modules/nifsoft/nifsoft.dm index 0df241799f..ca1d0199a6 100644 --- a/code/modules/nifsoft/nifsoft.dm +++ b/code/modules/nifsoft/nifsoft.dm @@ -269,7 +269,8 @@ /obj/item/weapon/storage/box/nifsofts_security name = "security nifsoft uploaders" desc = "A box of free nifsofts for security employees." - icon_state = "disk_kit" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "nifsoft_kit_sec" /obj/item/weapon/storage/box/nifsofts_security/New() ..() @@ -293,7 +294,8 @@ /obj/item/weapon/storage/box/nifsofts_engineering name = "engineering nifsoft uploaders" desc = "A box of free nifsofts for engineering employees." - icon_state = "disk_kit" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "nifsoft_kit_eng" /obj/item/weapon/storage/box/nifsofts_engineering/New() ..() @@ -316,7 +318,8 @@ /obj/item/weapon/storage/box/nifsofts_medical name = "medical nifsoft uploaders" desc = "A box of free nifsofts for medical employees." - icon_state = "disk_kit" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "nifsoft_kit_med" /obj/item/weapon/storage/box/nifsofts_medical/New() ..() @@ -340,7 +343,8 @@ /obj/item/weapon/storage/box/nifsofts_mining name = "mining nifsoft uploaders" desc = "A box of free nifsofts for mining employees." - icon_state = "disk_kit" + icon = 'icons/obj/storage_vr.dmi' + icon_state = "nifsoft_kit_mining" /obj/item/weapon/storage/box/nifsofts_mining/New() ..() diff --git a/code/modules/nifsoft/software/01_vision.dm b/code/modules/nifsoft/software/01_vision.dm index 7c7ca02079..7a792009f4 100644 --- a/code/modules/nifsoft/software/01_vision.dm +++ b/code/modules/nifsoft/software/01_vision.dm @@ -106,12 +106,12 @@ vision_flags = (NIF_V_MESONS) incompatible_with = list(NIF_MATERIAL,NIF_THERMALS,NIF_NIGHTVIS) - life() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - H.sight |= SEE_TURFS - if(H.client) - H.client.screen |= global_hud.meson +/datum/nifsoft/mesons/life() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + H.sight |= SEE_TURFS + if(H.client) + H.client.screen |= global_hud.meson /datum/nifsoft/material name = "Material Scanner" @@ -125,12 +125,12 @@ vision_flags = (NIF_V_MATERIAL) incompatible_with = list(NIF_MESONS,NIF_THERMALS,NIF_NIGHTVIS) - life() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - H.sight |= SEE_OBJS - if(H.client) - H.client.screen |= global_hud.material +/datum/nifsoft/material/life() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + H.sight |= SEE_OBJS + if(H.client) + H.client.screen |= global_hud.material /datum/nifsoft/thermals name = "Thermal Scanner" @@ -145,12 +145,12 @@ vision_flags = (NIF_V_THERMALS) incompatible_with = list(NIF_MESONS,NIF_MATERIAL,NIF_NIGHTVIS) - life() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - H.sight |= SEE_MOBS - if(H.client) - H.client.screen |= global_hud.thermal +/datum/nifsoft/thermals/life() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + H.sight |= SEE_MOBS + if(H.client) + H.client.screen |= global_hud.thermal /datum/nifsoft/nightvis name = "Low-Light Amp" @@ -164,9 +164,9 @@ vision_flags = (NIF_V_NIGHTVIS) incompatible_with = list(NIF_MESONS,NIF_MATERIAL,NIF_THERMALS) - life() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - H.see_in_dark += 7 - if(H.client) - H.client.screen |= global_hud.nvg +/datum/nifsoft/nightvis/life() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + H.see_in_dark += 7 + if(H.client) + H.client.screen |= global_hud.nvg diff --git a/code/modules/nifsoft/software/05_health.dm b/code/modules/nifsoft/software/05_health.dm index 8ea4ef90c5..dfab36df3e 100644 --- a/code/modules/nifsoft/software/05_health.dm +++ b/code/modules/nifsoft/software/05_health.dm @@ -13,63 +13,63 @@ health_flags = (NIF_H_ORGREPAIR) //These self-activate on their own, these aren't user-settable to on/off. - activate() - if((. = ..())) - mode = 1 +/datum/nifsoft/medichines_org/activate() + if((. = ..())) + mode = 1 - deactivate() - if((. = ..())) - a_drain = initial(a_drain) - mode = initial(mode) - nif.human.Stasis(0) +/datum/nifsoft/medichines_org/deactivate() + if((. = ..())) + a_drain = initial(a_drain) + mode = initial(mode) + nif.human.Stasis(0) - life() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - var/HP_percent = H.health/H.getMaxHealth() +/datum/nifsoft/medichines_org/life() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + var/HP_percent = H.health/H.getMaxHealth() - //Mode changing state machine - if(HP_percent >= 0.9) - if(mode) - nif.notify("User Status: NORMAL. Medichines deactivating.") - deactivate() - return TRUE - else if(!mode && HP_percent < 0.8) - nif.notify("User Status: INJURED. Commencing medichine routines.",TRUE) - activate() - else if(mode == 1 && HP_percent < 0.2) - nif.notify("User Status: DANGER. Seek medical attention!",TRUE) - mode = 2 - else if(mode == 2 && HP_percent < -0.4) - nif.notify("User Status: CRITICAL. Notifying medical, and starting emergency stasis!",TRUE) - mode = 3 - if(!isbelly(H.loc)) //Not notified in case of vore, for gameplay purposes. - var/turf/T = get_turf(H) - var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) - a.autosay("[H.real_name] has been put in emergency stasis, located at ([T.x],[T.y],[T.z])!", "[H.real_name]'s NIF", "Medical") - qdel(a) - - //Handle the actions in each mode - - //Injured but not critical + //Mode changing state machine + if(HP_percent >= 0.9) if(mode) - H.adjustToxLoss(-0.1 * mode) - H.adjustBruteLoss(-0.1 * mode) - H.adjustFireLoss(-0.1 * mode) - - if(mode >= 2) - nif.use_charge(a_drain) //A second drain if we're in level 2+ - - //Patient critical - emergency stasis - if(mode >= 3) - if(HP_percent <= 0) - H.Stasis(3) - if(HP_percent > 0.2) - H.Stasis(0) - nif.notify("Ending emergency stasis.",TRUE) - mode = 2 - + nif.notify("User Status: NORMAL. Medichines deactivating.") + deactivate() return TRUE + else if(!mode && HP_percent < 0.8) + nif.notify("User Status: INJURED. Commencing medichine routines.",TRUE) + activate() + else if(mode == 1 && HP_percent < 0.2) + nif.notify("User Status: DANGER. Seek medical attention!",TRUE) + mode = 2 + else if(mode == 2 && HP_percent < -0.4) + nif.notify("User Status: CRITICAL. Notifying medical, and starting emergency stasis!",TRUE) + mode = 3 + if(!isbelly(H.loc)) //Not notified in case of vore, for gameplay purposes. + var/turf/T = get_turf(H) + var/obj/item/device/radio/headset/a = new /obj/item/device/radio/headset/heads/captain(null) + a.autosay("[H.real_name] has been put in emergency stasis, located at ([T.x],[T.y],[T.z])!", "[H.real_name]'s NIF", "Medical") + qdel(a) + + //Handle the actions in each mode + + //Injured but not critical + if(mode) + H.adjustToxLoss(-0.1 * mode) + H.adjustBruteLoss(-0.1 * mode) + H.adjustFireLoss(-0.1 * mode) + + if(mode >= 2) + nif.use_charge(a_drain) //A second drain if we're in level 2+ + + //Patient critical - emergency stasis + if(mode >= 3) + if(HP_percent <= 0) + H.Stasis(3) + if(HP_percent > 0.2) + H.Stasis(0) + nif.notify("Ending emergency stasis.",TRUE) + mode = 2 + + return TRUE /datum/nifsoft/medichines_syn name = "Medichines" @@ -86,44 +86,44 @@ health_flags = (NIF_H_SYNTHREPAIR) //These self-activate on their own, these aren't user-settable to on/off. - activate() - if((. = ..())) - mode = 1 +/datum/nifsoft/medichines_syn/activate() + if((. = ..())) + mode = 1 - deactivate() - if((. = ..())) - mode = 0 - - life() - if((. = ..())) - //We're good! - if(!nif.human.bad_external_organs.len) - if(mode || active) - nif.notify("User Status: NORMAL. Medichines deactivating.") - deactivate() - return TRUE - - if(!mode && !active) - nif.notify("User Status: DAMAGED. Medichines performing minor repairs.",TRUE) - activate() - - for(var/eo in nif.human.bad_external_organs) - var/obj/item/organ/external/EO = eo - for(var/w in EO.wounds) - var/datum/wound/W = w - if(W.damage <= 5) - W.heal_damage(0.1) - EO.update_damages() - if(EO.update_icon()) - nif.human.UpdateDamageIcon(1) - nif.use_charge(0.1) - return TRUE //Return entirely, we only heal one at a time. - else if(mode == 1) - mode = 2 - nif.notify("Medichines unable to repair all damage. Perform manual repairs.",TRUE) +/datum/nifsoft/medichines_syn/deactivate() + if((. = ..())) + mode = 0 +/datum/nifsoft/medichines_syn/life() + if((. = ..())) + //We're good! + if(!nif.human.bad_external_organs.len) + if(mode || active) + nif.notify("User Status: NORMAL. Medichines deactivating.") + deactivate() return TRUE + if(!mode && !active) + nif.notify("User Status: DAMAGED. Medichines performing minor repairs.",TRUE) + activate() + + for(var/eo in nif.human.bad_external_organs) + var/obj/item/organ/external/EO = eo + for(var/w in EO.wounds) + var/datum/wound/W = w + if(W.damage <= 5) + W.heal_damage(0.1) + EO.update_damages() + if(EO.update_icon()) + nif.human.UpdateDamageIcon(1) + nif.use_charge(0.1) + return TRUE //Return entirely, we only heal one at a time. + else if(mode == 1) + mode = 2 + nif.notify("Medichines unable to repair all damage. Perform manual repairs.",TRUE) + + return TRUE + /datum/nifsoft/spare_breath name = "Respirocytes" desc = "Nanites simulating red blood cells will filter and recycle oxygen for a short time, preventing suffocation in hostile environments. NOTE: Only capable of supplying OXYGEN." @@ -137,47 +137,47 @@ var/filled = 100 //Tracks the internal tank 'refilling', which still uses power health_flags = (NIF_H_SPAREBREATH) - activate() - if(!(filled > 50)) - nif.notify("Respirocytes not saturated!",TRUE) - return FALSE - if((. = ..())) - nif.notify("Now taking air from reserves.") +/datum/nifsoft/spare_breath/activate() + if(!(filled > 50)) + nif.notify("Respirocytes not saturated!",TRUE) + return FALSE + if((. = ..())) + nif.notify("Now taking air from reserves.") - deactivate() - if((. = ..())) - nif.notify("Now taking air from environment and refilling reserves.") +/datum/nifsoft/spare_breath/deactivate() + if((. = ..())) + nif.notify("Now taking air from environment and refilling reserves.") - life() - if((. = ..())) - if(active) //Supplying air, not recharging it - switch(filled) //Text warnings - if(75) - nif.notify("Respirocytes at 75% saturation.",TRUE) - if(50) - nif.notify("Respirocytes at 50% saturation!",TRUE) - if(25) - nif.notify("Respirocytes at 25% saturation, seek a habitable environment!",TRUE) - if(5) - nif.notify("Respirocytes at 5% saturation! Failure imminent!",TRUE) +/datum/nifsoft/spare_breath/life() + if((. = ..())) + if(active) //Supplying air, not recharging it + switch(filled) //Text warnings + if(75) + nif.notify("Respirocytes at 75% saturation.",TRUE) + if(50) + nif.notify("Respirocytes at 50% saturation!",TRUE) + if(25) + nif.notify("Respirocytes at 25% saturation, seek a habitable environment!",TRUE) + if(5) + nif.notify("Respirocytes at 5% saturation! Failure imminent!",TRUE) - if(filled == 0) //Ran out - deactivate() - else //Drain a little - filled-- + if(filled == 0) //Ran out + deactivate() + else //Drain a little + filled-- - else //Recharging air, not supplying it - if(filled == 100) - return TRUE - else if(nif.use_charge(0.1) && ++filled == 100) - nif.notify("Respirocytes now fully saturated.") + else //Recharging air, not supplying it + if(filled == 100) + return TRUE + else if(nif.use_charge(0.1) && ++filled == 100) + nif.notify("Respirocytes now fully saturated.") - proc/resp_breath() - if(!active) return null - var/datum/gas_mixture/breath = new(BREATH_VOLUME) - breath.adjust_gas("oxygen", BREATH_MOLES) - breath.temperature = T20C - return breath +/datum/nifsoft/spare_breath/proc/resp_breath() + if(!active) return null + var/datum/gas_mixture/breath = new(BREATH_VOLUME) + breath.adjust_gas("oxygen", BREATH_MOLES) + breath.temperature = T20C + return breath /datum/nifsoft/mindbackup name = "Mind Backup" @@ -185,19 +185,19 @@ list_pos = NIF_BACKUP cost = 125 - activate() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - SStranscore.m_backup(H.mind,H.nif,one_time = TRUE) - persist_nif_data(H) - nif.notify("Mind backed up!") - nif.use_charge(0.1) - deactivate() - return TRUE +/datum/nifsoft/mindbackup/activate() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + SStranscore.m_backup(H.mind,H.nif,one_time = TRUE) + persist_nif_data(H) + nif.notify("Mind backed up!") + nif.use_charge(0.1) + deactivate() + return TRUE - deactivate() - if((. = ..())) - return TRUE +/datum/nifsoft/mindbackup/deactivate() + if((. = ..())) + return TRUE - stat_text() - return "Store Backup" +/datum/nifsoft/mindbackup/stat_text() + return "Store Backup" diff --git a/code/modules/nifsoft/software/06_screens.dm b/code/modules/nifsoft/software/06_screens.dm index 8dadc88192..253ed17c8d 100644 --- a/code/modules/nifsoft/software/06_screens.dm +++ b/code/modules/nifsoft/software/06_screens.dm @@ -7,25 +7,25 @@ p_drain = 0.025 var/datum/tgui_module/crew_monitor/nif/arscreen - New() - ..() - arscreen = new(nif) +/datum/nifsoft/crewmonitor/New() + ..() + arscreen = new(nif) - Destroy() - QDEL_NULL(arscreen) - return ..() +/datum/nifsoft/crewmonitor/Destroy() + QDEL_NULL(arscreen) + return ..() - activate() - if((. = ..())) - arscreen.tgui_interact(nif.human) - return TRUE +/datum/nifsoft/crewmonitor/activate() + if((. = ..())) + arscreen.tgui_interact(nif.human) + return TRUE - deactivate() - if((. = ..())) - return TRUE +/datum/nifsoft/crewmonitor/deactivate() + if((. = ..())) + return TRUE - stat_text() - return "Show Monitor" +/datum/nifsoft/crewmonitor/stat_text() + return "Show Monitor" /datum/nifsoft/alarmmonitor name = "Alarm Monitor" @@ -36,22 +36,22 @@ p_drain = 0.025 var/datum/tgui_module/alarm_monitor/engineering/nif/tgarscreen - New() - ..() - tgarscreen = new(nif) +/datum/nifsoft/alarmmonitor/New() + ..() + tgarscreen = new(nif) - Destroy() - QDEL_NULL(tgarscreen) - return ..() +/datum/nifsoft/alarmmonitor/Destroy() + QDEL_NULL(tgarscreen) + return ..() - activate() - if((. = ..())) - tgarscreen.tgui_interact(nif.human) - return TRUE +/datum/nifsoft/alarmmonitor/activate() + if((. = ..())) + tgarscreen.tgui_interact(nif.human) + return TRUE - deactivate() - if((. = ..())) - return TRUE +/datum/nifsoft/alarmmonitor/deactivate() + if((. = ..())) + return TRUE - stat_text() - return "Show Monitor" +/datum/nifsoft/alarmmonitor/stat_text() + return "Show Monitor" diff --git a/code/modules/nifsoft/software/10_combat.dm b/code/modules/nifsoft/software/10_combat.dm index a4af60ae6e..a63e323792 100644 --- a/code/modules/nifsoft/software/10_combat.dm +++ b/code/modules/nifsoft/software/10_combat.dm @@ -34,10 +34,10 @@ tick_flags = NIF_ACTIVETICK combat_flags = (NIF_C_PAINKILLERS) - life() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - H.bloodstr.add_reagent("numbenzyme",0.5) +/datum/nifsoft/painkillers/life() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + H.bloodstr.add_reagent("numbenzyme",0.5) /datum/nifsoft/hardclaws name = "Bloodletters" @@ -74,25 +74,25 @@ var/global/datum/unarmed_attack/hardclaws/unarmed_hardclaws = new() var/used = FALSE combat_flags = (NIF_C_HIDELASER) - activate() - if((. = ..())) - if(used) - nif.notify("You do not have a hidden weapon to deploy anymore!",TRUE) - deactivate() - return FALSE - if(!nif.use_charge(50)) - nif.notify("Insufficient energy to deploy weapon!",TRUE) - deactivate() - return FALSE +/datum/nifsoft/hidelaser/activate() + if((. = ..())) + if(used) + nif.notify("You do not have a hidden weapon to deploy anymore!",TRUE) + deactivate() + return FALSE + if(!nif.use_charge(50)) + nif.notify("Insufficient energy to deploy weapon!",TRUE) + deactivate() + return FALSE - var/mob/living/carbon/human/H = nif.human - H.adjustHalLoss(30) - var/obj/item/weapon/gun/energy/gun/martin/dazzle/dgun = new(get_turf(H)) - H.put_in_hands(dgun) - nif.notify("Weapon deployed!",TRUE) - used = TRUE - spawn(0) - uninstall() + var/mob/living/carbon/human/H = nif.human + H.adjustHalLoss(30) + var/obj/item/weapon/gun/energy/gun/martin/dazzle/dgun = new(get_turf(H)) + H.put_in_hands(dgun) + nif.notify("Weapon deployed!",TRUE) + used = TRUE + spawn(0) + uninstall() //The gun to go with this implant /obj/item/weapon/gun/energy/gun/martin/dazzle diff --git a/code/modules/nifsoft/software/13_soulcatcher.dm b/code/modules/nifsoft/software/13_soulcatcher.dm index 4cc7210bd4..c1de085532 100644 --- a/code/modules/nifsoft/software/13_soulcatcher.dm +++ b/code/modules/nifsoft/software/13_soulcatcher.dm @@ -21,243 +21,243 @@ var/list/brainmobs = list() var/inside_flavor = "A small completely white room with a couch, and a window to what seems to be the outside world. A small sign in the corner says 'Configure Me'." - New() - ..() - load_settings() +/datum/nifsoft/soulcatcher/New() + ..() + load_settings() - Destroy() - QDEL_LIST_NULL(brainmobs) - return ..() +/datum/nifsoft/soulcatcher/Destroy() + QDEL_LIST_NULL(brainmobs) + return ..() - activate() - if((. = ..())) - show_settings(nif.human) - spawn(0) - deactivate() +/datum/nifsoft/soulcatcher/activate() + if((. = ..())) + show_settings(nif.human) + spawn(0) + deactivate() - deactivate() - if((. = ..())) - return TRUE - - stat_text() - return "Change Settings ([brainmobs.len] minds)" - - install() - if((. = ..())) - nif.set_flag(NIF_O_SCOTHERS,NIF_FLAGS_OTHER) //Required on install, because other_flags aren't sufficient for our complicated settings. - nif.human.verbs |= /mob/living/carbon/human/proc/nsay - nif.human.verbs |= /mob/living/carbon/human/proc/nme - - uninstall() - QDEL_LIST_NULL(brainmobs) - if((. = ..()) && nif && nif.human) //Sometimes NIFs are deleted outside of a human - nif.human.verbs -= /mob/living/carbon/human/proc/nsay - nif.human.verbs -= /mob/living/carbon/human/proc/nme - - proc/save_settings() - if(!nif) - return - nif.save_data["[list_pos]"] = inside_flavor +/datum/nifsoft/soulcatcher/deactivate() + if((. = ..())) return TRUE - proc/load_settings() - if(!nif) - return - var/load = nif.save_data["[list_pos]"] - if(load) - inside_flavor = load - return TRUE +/datum/nifsoft/soulcatcher/stat_text() + return "Change Settings ([brainmobs.len] minds)" - proc/notify_into(var/message) - var/sound = nif.good_sound +/datum/nifsoft/soulcatcher/install() + if((. = ..())) + nif.set_flag(NIF_O_SCOTHERS,NIF_FLAGS_OTHER) //Required on install, because other_flags aren't sufficient for our complicated settings. + nif.human.verbs |= /mob/living/carbon/human/proc/nsay + nif.human.verbs |= /mob/living/carbon/human/proc/nme - to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] Soulcatcher displays, \"[message]\"") - nif.human << sound +/datum/nifsoft/soulcatcher/uninstall() + QDEL_LIST_NULL(brainmobs) + if((. = ..()) && nif && nif.human) //Sometimes NIFs are deleted outside of a human + nif.human.verbs -= /mob/living/carbon/human/proc/nsay + nif.human.verbs -= /mob/living/carbon/human/proc/nme +/datum/nifsoft/soulcatcher/proc/save_settings() + if(!nif) + return + nif.save_data["[list_pos]"] = inside_flavor + return TRUE + +/datum/nifsoft/soulcatcher/proc/load_settings() + if(!nif) + return + var/load = nif.save_data["[list_pos]"] + if(load) + inside_flavor = load + return TRUE + +/datum/nifsoft/soulcatcher/proc/notify_into(var/message) + var/sound = nif.good_sound + + to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] Soulcatcher displays, \"[message]\"") + nif.human << sound + + for(var/brainmob in brainmobs) + var/mob/living/carbon/brain/caught_soul/CS = brainmob + to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] Soulcatcher displays, \"[message]\"") + brainmob << sound + +/datum/nifsoft/soulcatcher/proc/say_into(var/message, var/mob/living/sender, var/mob/eyeobj) + var/sender_name = eyeobj ? eyeobj.name : sender.name + + //AR Projecting + if(eyeobj) + sender.eyeobj.visible_message("[sender_name] says, \"[message]\"") + + //Not AR Projecting + else + to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] [sender_name] speaks, \"[message]\"") for(var/brainmob in brainmobs) var/mob/living/carbon/brain/caught_soul/CS = brainmob - to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] Soulcatcher displays, \"[message]\"") - brainmob << sound + to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] [sender_name] speaks, \"[message]\"") - proc/say_into(var/message, var/mob/living/sender, var/mob/eyeobj) - var/sender_name = eyeobj ? eyeobj.name : sender.name + log_nsay(message,nif.human.real_name,sender) - //AR Projecting - if(eyeobj) - sender.eyeobj.visible_message("[sender_name] says, \"[message]\"") +/datum/nifsoft/soulcatcher/proc/emote_into(var/message, var/mob/living/sender, var/mob/eyeobj) + var/sender_name = eyeobj ? eyeobj.name : sender.name - //Not AR Projecting - else - to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] [sender_name] speaks, \"[message]\"") - for(var/brainmob in brainmobs) - var/mob/living/carbon/brain/caught_soul/CS = brainmob - to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] [sender_name] speaks, \"[message]\"") + //AR Projecting + if(eyeobj) + sender.eyeobj.visible_message("[sender_name] [message]") - log_nsay(message,nif.human.real_name,sender) + //Not AR Projecting + else + to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] [sender_name] [message]") + for(var/brainmob in brainmobs) + var/mob/living/carbon/brain/caught_soul/CS = brainmob + to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] [sender_name] [message]") - proc/emote_into(var/message, var/mob/living/sender, var/mob/eyeobj) - var/sender_name = eyeobj ? eyeobj.name : sender.name + log_nme(message,nif.human.real_name,sender) - //AR Projecting - if(eyeobj) - sender.eyeobj.visible_message("[sender_name] [message]") +/datum/nifsoft/soulcatcher/proc/show_settings(var/mob/living/carbon/human/H) + set waitfor = FALSE + var/settings_list = list( + "Catching You \[[setting_flags & NIF_SC_CATCHING_ME ? "Enabled" : "Disabled"]\]" = NIF_SC_CATCHING_ME, + "Catching Prey \[[setting_flags & NIF_SC_CATCHING_OTHERS ? "Enabled" : "Disabled"]\]" = NIF_SC_CATCHING_OTHERS, + "Ext. Hearing \[[setting_flags & NIF_SC_ALLOW_EARS ? "Enabled" : "Disabled"]\]" = NIF_SC_ALLOW_EARS, + "Ext. Vision \[[setting_flags & NIF_SC_ALLOW_EYES ? "Enabled" : "Disabled"]\]" = NIF_SC_ALLOW_EYES, + "Mind Backups \[[setting_flags & NIF_SC_BACKUPS ? "Enabled" : "Disabled"]\]" = NIF_SC_BACKUPS, + "AR Projecting \[[setting_flags & NIF_SC_PROJECTING ? "Enabled" : "Disabled"]\]" = NIF_SC_PROJECTING, + "Design Inside", + "Erase Contents") + var/choice = input(nif.human,"Select a setting to modify:","Soulcatcher NIFSoft") as null|anything in settings_list + if(choice in settings_list) + switch(choice) - //Not AR Projecting - else - to_chat(nif.human,"\[[bicon(nif.big_icon)]NIF\] [sender_name] [message]") - for(var/brainmob in brainmobs) - var/mob/living/carbon/brain/caught_soul/CS = brainmob - to_chat(CS,"\[[bicon(nif.big_icon)]NIF\] [sender_name] [message]") + if("Design Inside") + var/new_flavor = input(nif.human, "Type what the prey sees after being 'caught'. This will be \ + printed after an intro ending with: \"Around you, you see...\" to the prey. If you already \ + have prey, this will be printed to them after \"Your surroundings change to...\". Limit 2048 char.", \ + "VR Environment", html_decode(inside_flavor)) as message + new_flavor = sanitize(new_flavor, MAX_MESSAGE_LEN*2) + inside_flavor = new_flavor + nif.notify("Updating VR environment...") + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/CS = brain + to_chat(CS,"Your surroundings change to...\n[inside_flavor]") + save_settings() + return TRUE - log_nme(message,nif.human.real_name,sender) + if("Erase Contents") + var/mob/living/carbon/brain/caught_soul/brainpick = input(nif.human,"Select a mind to delete:","Erase Mind") as null|anything in brainmobs - proc/show_settings(var/mob/living/carbon/human/H) - set waitfor = FALSE - var/settings_list = list( - "Catching You \[[setting_flags & NIF_SC_CATCHING_ME ? "Enabled" : "Disabled"]\]" = NIF_SC_CATCHING_ME, - "Catching Prey \[[setting_flags & NIF_SC_CATCHING_OTHERS ? "Enabled" : "Disabled"]\]" = NIF_SC_CATCHING_OTHERS, - "Ext. Hearing \[[setting_flags & NIF_SC_ALLOW_EARS ? "Enabled" : "Disabled"]\]" = NIF_SC_ALLOW_EARS, - "Ext. Vision \[[setting_flags & NIF_SC_ALLOW_EYES ? "Enabled" : "Disabled"]\]" = NIF_SC_ALLOW_EYES, - "Mind Backups \[[setting_flags & NIF_SC_BACKUPS ? "Enabled" : "Disabled"]\]" = NIF_SC_BACKUPS, - "AR Projecting \[[setting_flags & NIF_SC_PROJECTING ? "Enabled" : "Disabled"]\]" = NIF_SC_PROJECTING, - "Design Inside", - "Erase Contents") - var/choice = input(nif.human,"Select a setting to modify:","Soulcatcher NIFSoft") as null|anything in settings_list - if(choice in settings_list) - switch(choice) + var/warning = alert(nif.human,"Are you SURE you want to erase \"[brainpick]\"?","Erase Mind","CANCEL","DELETE","CANCEL") + if(warning == "DELETE") + brainmobs -= brainpick + qdel(brainpick) + return TRUE - if("Design Inside") - var/new_flavor = input(nif.human, "Type what the prey sees after being 'caught'. This will be \ - printed after an intro ending with: \"Around you, you see...\" to the prey. If you already \ - have prey, this will be printed to them after \"Your surroundings change to...\". Limit 2048 char.", \ - "VR Environment", html_decode(inside_flavor)) as message - new_flavor = sanitize(new_flavor, MAX_MESSAGE_LEN*2) - inside_flavor = new_flavor - nif.notify("Updating VR environment...") - for(var/brain in brainmobs) - var/mob/living/carbon/brain/caught_soul/CS = brain - to_chat(CS,"Your surroundings change to...\n[inside_flavor]") - save_settings() - return TRUE + //Must just be a flag without special handling then. + else + var/flag = settings_list[choice] + return toggle_setting(flag) - if("Erase Contents") - var/mob/living/carbon/brain/caught_soul/brainpick = input(nif.human,"Select a mind to delete:","Erase Mind") as null|anything in brainmobs +/datum/nifsoft/soulcatcher/proc/toggle_setting(var/flag) + setting_flags ^= flag - var/warning = alert(nif.human,"Are you SURE you want to erase \"[brainpick]\"?","Erase Mind","CANCEL","DELETE","CANCEL") - if(warning == "DELETE") - brainmobs -= brainpick - qdel(brainpick) - return TRUE + var/notify_message + //Special treatment + switch(flag) + if(NIF_SC_BACKUPS) + if(setting_flags & NIF_SC_BACKUPS) + notify_message = "Mind backup system enabled." + else + notify_message = "Mind backup system disabled." - //Must just be a flag without special handling then. - else - var/flag = settings_list[choice] - return toggle_setting(flag) + if(NIF_SC_CATCHING_ME) + if(setting_flags & NIF_SC_CATCHING_ME) + nif.set_flag(NIF_O_SCMYSELF,NIF_FLAGS_OTHER) + else + nif.clear_flag(NIF_O_SCMYSELF,NIF_FLAGS_OTHER) + if(NIF_SC_CATCHING_OTHERS) + if(setting_flags & NIF_SC_CATCHING_OTHERS) + nif.set_flag(NIF_O_SCOTHERS,NIF_FLAGS_OTHER) + else + nif.clear_flag(NIF_O_SCOTHERS,NIF_FLAGS_OTHER) + if(NIF_SC_ALLOW_EARS) + if(setting_flags & NIF_SC_ALLOW_EARS) + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/brainmob = brain + brainmob.ext_deaf = FALSE + notify_message = "External audio input enabled." + else + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/brainmob = brain + brainmob.ext_deaf = TRUE + notify_message = "External audio input disabled." + if(NIF_SC_ALLOW_EYES) + if(setting_flags & NIF_SC_ALLOW_EYES) + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/brainmob = brain + brainmob.ext_blind = FALSE + notify_message = "External video input enabled." + else + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/brainmob = brain + brainmob.ext_blind = TRUE + notify_message = "External video input disabled." - proc/toggle_setting(var/flag) - setting_flags ^= flag + if(notify_message) + notify_into(notify_message) - var/notify_message - //Special treatment - switch(flag) - if(NIF_SC_BACKUPS) - if(setting_flags & NIF_SC_BACKUPS) - notify_message = "Mind backup system enabled." - else - notify_message = "Mind backup system disabled." + return TRUE - if(NIF_SC_CATCHING_ME) - if(setting_flags & NIF_SC_CATCHING_ME) - nif.set_flag(NIF_O_SCMYSELF,NIF_FLAGS_OTHER) - else - nif.clear_flag(NIF_O_SCMYSELF,NIF_FLAGS_OTHER) - if(NIF_SC_CATCHING_OTHERS) - if(setting_flags & NIF_SC_CATCHING_OTHERS) - nif.set_flag(NIF_O_SCOTHERS,NIF_FLAGS_OTHER) - else - nif.clear_flag(NIF_O_SCOTHERS,NIF_FLAGS_OTHER) - if(NIF_SC_ALLOW_EARS) - if(setting_flags & NIF_SC_ALLOW_EARS) - for(var/brain in brainmobs) - var/mob/living/carbon/brain/caught_soul/brainmob = brain - brainmob.ext_deaf = FALSE - notify_message = "External audio input enabled." - else - for(var/brain in brainmobs) - var/mob/living/carbon/brain/caught_soul/brainmob = brain - brainmob.ext_deaf = TRUE - notify_message = "External audio input disabled." - if(NIF_SC_ALLOW_EYES) - if(setting_flags & NIF_SC_ALLOW_EYES) - for(var/brain in brainmobs) - var/mob/living/carbon/brain/caught_soul/brainmob = brain - brainmob.ext_blind = FALSE - notify_message = "External video input enabled." - else - for(var/brain in brainmobs) - var/mob/living/carbon/brain/caught_soul/brainmob = brain - brainmob.ext_blind = TRUE - notify_message = "External video input disabled." +//Complex version for catching in-round characters +/datum/nifsoft/soulcatcher/proc/catch_mob(var/mob/M) + if(!M.mind) return - if(notify_message) - notify_into(notify_message) + //Create a new brain mob + var/mob/living/carbon/brain/caught_soul/brainmob = new(nif) + brainmob.nif = nif + brainmob.soulcatcher = src + brainmob.container = src + brainmob.stat = 0 + brainmob.silent = FALSE + dead_mob_list -= brainmob + brainmob.add_language(LANGUAGE_GALCOM) + brainmobs |= brainmob - return TRUE + //Put the mind and player into the mob + M.mind.transfer_to(brainmob) + brainmob.name = brainmob.mind.name + brainmob.real_name = brainmob.mind.name - //Complex version for catching in-round characters - proc/catch_mob(var/mob/M) - if(!M.mind) return + //If we caught our owner, special settings. + if(M == nif.human) + brainmob.ext_deaf = FALSE + brainmob.ext_blind = FALSE + brainmob.parent_mob = TRUE - //Create a new brain mob - var/mob/living/carbon/brain/caught_soul/brainmob = new(nif) - brainmob.nif = nif - brainmob.soulcatcher = src - brainmob.container = src - brainmob.stat = 0 - brainmob.silent = FALSE - dead_mob_list -= brainmob - brainmob.add_language(LANGUAGE_GALCOM) - brainmobs |= brainmob + //If they have these values, apply them + if(ishuman(M)) + var/mob/living/carbon/human/H = M + brainmob.dna = H.dna + brainmob.ooc_notes = H.ooc_notes + brainmob.timeofhostdeath = H.timeofdeath + SStranscore.m_backup(brainmob.mind,0) //It does ONE, so medical will hear about it. - //Put the mind and player into the mob - M.mind.transfer_to(brainmob) - brainmob.name = brainmob.mind.name - brainmob.real_name = brainmob.mind.name + //Else maybe they're a joining ghost + else if(isobserver(M)) + brainmob.transient = TRUE + qdel(M) //Bye ghost - //If we caught our owner, special settings. - if(M == nif.human) - brainmob.ext_deaf = FALSE - brainmob.ext_blind = FALSE - brainmob.parent_mob = TRUE + //Give them a flavortext message + var/message = "Your vision fades in a haze of static, before returning.\n\ + Around you, you see...\n\ + [inside_flavor]" - //If they have these values, apply them - if(ishuman(M)) - var/mob/living/carbon/human/H = M - brainmob.dna = H.dna - brainmob.ooc_notes = H.ooc_notes - brainmob.timeofhostdeath = H.timeofdeath - SStranscore.m_backup(brainmob.mind,0) //It does ONE, so medical will hear about it. + to_chat(brainmob,message) - //Else maybe they're a joining ghost - else if(isobserver(M)) - brainmob.transient = TRUE - qdel(M) //Bye ghost + //Reminder on how this works to host + if(brainmobs.len == 1) //Only spam this on the first one + to_chat(nif.human,"Your occupant's messages/actions can only be seen by you, and you can \ + send messages that only they can hear/see by using the NSay and NMe verbs (or the *nsay and *nme emotes).") - //Give them a flavortext message - var/message = "Your vision fades in a haze of static, before returning.\n\ - Around you, you see...\n\ - [inside_flavor]" - - to_chat(brainmob,message) - - //Reminder on how this works to host - if(brainmobs.len == 1) //Only spam this on the first one - to_chat(nif.human,"Your occupant's messages/actions can only be seen by you, and you can \ - send messages that only they can hear/see by using the NSay and NMe verbs (or the *nsay and *nme emotes).") - - //Announce to host and other minds - notify_into("New mind loaded: [brainmob.name]") - return TRUE + //Announce to host and other minds + notify_into("New mind loaded: [brainmob.name]") + return TRUE //////////////// //The caught mob diff --git a/code/modules/nifsoft/software/14_commlink.dm b/code/modules/nifsoft/software/14_commlink.dm index f156e3ffb5..26539f20f2 100644 --- a/code/modules/nifsoft/software/14_commlink.dm +++ b/code/modules/nifsoft/software/14_commlink.dm @@ -9,24 +9,24 @@ p_drain = 0.01 other_flags = (NIF_O_COMMLINK) - install() - if((. = ..())) - nif.comm = new(nif,src) +/datum/nifsoft/commlink/install() + if((. = ..())) + nif.comm = new(nif,src) - uninstall() - var/obj/item/device/nif/lnif = nif //Awkward. Parent clears it in an attempt to clean up. - if((. = ..()) && lnif) - QDEL_NULL(lnif.comm) +/datum/nifsoft/commlink/uninstall() + var/obj/item/device/nif/lnif = nif //Awkward. Parent clears it in an attempt to clean up. + if((. = ..()) && lnif) + QDEL_NULL(lnif.comm) - activate() - if((. = ..())) - nif.comm.initialize_exonet(nif.human) - nif.comm.ui_interact(nif.human,key_state = commlink_state) - spawn(0) - deactivate() +/datum/nifsoft/commlink/activate() + if((. = ..())) + nif.comm.initialize_exonet(nif.human) + nif.comm.tgui_interact(nif.human, custom_state = GLOB.tgui_commlink_state) + spawn(0) + deactivate() - stat_text() - return "Show Commlink" +/datum/nifsoft/commlink/stat_text() + return "Show Commlink" /datum/nifsoft/commlink/Topic(href, href_list) if(href_list["open"]) @@ -39,18 +39,18 @@ var/obj/item/device/nif/nif var/datum/nifsoft/commlink/nifsoft - New(var/newloc,var/soft) - ..() - nif = newloc - nifsoft = soft - QDEL_NULL(camera) //Not supported on internal one. +/obj/item/device/communicator/commlink/New(var/newloc,var/soft) + ..() + nif = newloc + nifsoft = soft + QDEL_NULL(camera) //Not supported on internal one. - Destroy() - if(nif) - nif.comm = null - nif = null - nifsoft = null - return ..() +/obj/item/device/communicator/commlink/Destroy() + if(nif) + nif.comm = null + nif = null + nifsoft = null + return ..() /obj/item/device/communicator/commlink/register_device(var/new_name) owner = new_name diff --git a/code/modules/nifsoft/software/15_misc.dm b/code/modules/nifsoft/software/15_misc.dm index f6406ed186..7cdd364b16 100644 --- a/code/modules/nifsoft/software/15_misc.dm +++ b/code/modules/nifsoft/software/15_misc.dm @@ -9,36 +9,36 @@ var/obj/machinery/power/apc/apc other_flags = (NIF_O_APCCHARGE) - activate() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - apc = locate(/obj/machinery/power/apc) in get_step(H,H.dir) - if(!apc) - apc = locate(/obj/machinery/power/apc) in get_step(H,0) - if(!apc) - nif.notify("You must be facing an APC to connect to.",TRUE) - spawn(0) - deactivate() - return FALSE - - H.visible_message("Thin snakelike tendrils grow from [H] and connect to \the [apc].","Thin snakelike tendrils grow from you and connect to \the [apc].") - - deactivate() - if((. = ..())) - apc = null - - life() - if((. = ..())) - var/mob/living/carbon/human/H = nif.human - if(apc && (get_dist(H,apc) <= 1) && H.nutrition < 440) // 440 vs 450, life() happens before we get here so it'll never be EXACTLY 450 - H.nutrition = min(H.nutrition+10, 450) - apc.drain_power(7000/450*10) //This is from the large rechargers. No idea what the math is. - return TRUE - else - nif.notify("APC charging has ended.") - H.visible_message("[H]'s snakelike tendrils whip back into their body from \the [apc].","The APC connector tendrils return to your body.") +/datum/nifsoft/apc_recharge/activate() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + apc = locate(/obj/machinery/power/apc) in get_step(H,H.dir) + if(!apc) + apc = locate(/obj/machinery/power/apc) in get_step(H,0) + if(!apc) + nif.notify("You must be facing an APC to connect to.",TRUE) + spawn(0) deactivate() - return FALSE + return FALSE + + H.visible_message("Thin snakelike tendrils grow from [H] and connect to \the [apc].","Thin snakelike tendrils grow from you and connect to \the [apc].") + +/datum/nifsoft/apc_recharge/deactivate() + if((. = ..())) + apc = null + +/datum/nifsoft/apc_recharge/life() + if((. = ..())) + var/mob/living/carbon/human/H = nif.human + if(apc && (get_dist(H,apc) <= 1) && H.nutrition < 440) // 440 vs 450, life() happens before we get here so it'll never be EXACTLY 450 + H.nutrition = min(H.nutrition+10, 450) + apc.drain_power(7000/450*10) //This is from the large rechargers. No idea what the math is. + return TRUE + else + nif.notify("APC charging has ended.") + H.visible_message("[H]'s snakelike tendrils whip back into their body from \the [apc].","The APC connector tendrils return to your body.") + deactivate() + return FALSE /datum/nifsoft/pressure name = "Pressure Seals" @@ -62,31 +62,31 @@ applies_to = NIF_SYNTHETIC other_flags = (NIF_O_HEATSINKS) - activate() - if((. = ..())) - if(used >= 1500) - nif.notify("Heat sinks not safe to operate again yet! Max 75% on activation.",TRUE) - spawn(0) - deactivate() - return FALSE - - stat_text() - return "[active ? "Active" : "Disabled"] (Stored Heat: [FLOOR((used/20), 1)]%)" - - life() - if((. = ..())) - //Not being used, all clean. - if(!active && !used) - return TRUE - - //Being used, and running out. - else if(active && ++used == 2000) - nif.notify("Heat sinks overloaded! Shutting down!",TRUE) +/datum/nifsoft/heatsinks/activate() + if((. = ..())) + if(used >= 1500) + nif.notify("Heat sinks not safe to operate again yet! Max 75% on activation.",TRUE) + spawn(0) deactivate() + return FALSE - //Being cleaned, and finishing empty. - else if(!active && --used == 0) - nif.notify("Heat sinks re-chilled.") +/datum/nifsoft/heatsinks/stat_text() + return "[active ? "Active" : "Disabled"] (Stored Heat: [FLOOR((used/20), 1)]%)" + +/datum/nifsoft/heatsinks/life() + if((. = ..())) + //Not being used, all clean. + if(!active && !used) + return TRUE + + //Being used, and running out. + else if(active && ++used == 2000) + nif.notify("Heat sinks overloaded! Shutting down!",TRUE) + deactivate() + + //Being cleaned, and finishing empty. + else if(!active && --used == 0) + nif.notify("Heat sinks re-chilled.") /datum/nifsoft/compliance name = "Compliance Module" @@ -99,24 +99,24 @@ access = 999 //Prevents anyone from buying it without an emag. var/laws = "Be nice to people!" - New(var/newloc,var/newlaws) - laws = newlaws //Sanitize before this (the disk does) - ..(newloc) +/datum/nifsoft/compliance/New(var/newloc,var/newlaws) + laws = newlaws //Sanitize before this (the disk does) + ..(newloc) - activate() - if((. = ..())) - to_chat(nif.human,"You are compelled to follow these rules: \n[laws]") +/datum/nifsoft/compliance/activate() + if((. = ..())) + to_chat(nif.human,"You are compelled to follow these rules: \n[laws]") - install() - if((. = ..())) - to_chat(nif.human,"You feel suddenly compelled to follow these rules: \n[laws]") +/datum/nifsoft/compliance/install() + if((. = ..())) + to_chat(nif.human,"You feel suddenly compelled to follow these rules: \n[laws]") - uninstall() - nif.notify("ERROR! Unable to comply!",TRUE) - return FALSE //NOPE. +/datum/nifsoft/compliance/uninstall() + nif.notify("ERROR! Unable to comply!",TRUE) + return FALSE //NOPE. - stat_text() - return "Show Laws" +/datum/nifsoft/compliance/stat_text() + return "Show Laws" /datum/nifsoft/sizechange name = "Mass Alteration" @@ -125,28 +125,28 @@ cost = 375 wear = 6 - activate() - if((. = ..())) - var/new_size = input("Put the desired size (25-200%)", "Set Size", 200) as num +/datum/nifsoft/sizechange/activate() + if((. = ..())) + var/new_size = input("Put the desired size (25-200%)", "Set Size", 200) as num - if (!ISINRANGE(new_size,25,200)) - to_chat(nif.human,"The safety features of the NIF Program prevent you from choosing this size.") - return - else - nif.human.resize(new_size/100) - to_chat(nif.human,"You set the size to [new_size]%") + if (!ISINRANGE(new_size,25,200)) + to_chat(nif.human,"The safety features of the NIF Program prevent you from choosing this size.") + return + else + nif.human.resize(new_size/100) + to_chat(nif.human,"You set the size to [new_size]%") - nif.human.visible_message("Swirling grey mist envelops [nif.human] as they change size!","Swirling streams of nanites wrap around you as you change size!") + nif.human.visible_message("Swirling grey mist envelops [nif.human] as they change size!","Swirling streams of nanites wrap around you as you change size!") - spawn(0) - deactivate() + spawn(0) + deactivate() - deactivate() - if((. = ..())) - return TRUE +/datum/nifsoft/sizechange/deactivate() + if((. = ..())) + return TRUE - stat_text() - return "Change Size" +/datum/nifsoft/sizechange/stat_text() + return "Change Size" /datum/nifsoft/worldbend name = "World Bender" @@ -155,22 +155,22 @@ cost = 100 a_drain = 0.01 - activate() - if((. = ..())) - var/list/justme = list(nif.human) - for(var/human in human_mob_list) - if(human == nif.human) - continue - var/mob/living/carbon/human/H = human - H.display_alt_appearance("animals", justme) - alt_farmanimals += nif.human +/datum/nifsoft/worldbend/activate() + if((. = ..())) + var/list/justme = list(nif.human) + for(var/human in human_mob_list) + if(human == nif.human) + continue + var/mob/living/carbon/human/H = human + H.display_alt_appearance("animals", justme) + alt_farmanimals += nif.human - deactivate() - if((. = ..())) - var/list/justme = list(nif.human) - for(var/human in human_mob_list) - if(human == nif.human) - continue - var/mob/living/carbon/human/H = human - H.hide_alt_appearance("animals", justme) - alt_farmanimals -= nif.human +/datum/nifsoft/worldbend/deactivate() + if((. = ..())) + var/list/justme = list(nif.human) + for(var/human in human_mob_list) + if(human == nif.human) + continue + var/mob/living/carbon/human/H = human + H.hide_alt_appearance("animals", justme) + alt_farmanimals -= nif.human diff --git a/code/modules/organs/internal/augment/armmounted.dm b/code/modules/organs/internal/augment/armmounted.dm index 73751be0d9..4131955e15 100644 --- a/code/modules/organs/internal/augment/armmounted.dm +++ b/code/modules/organs/internal/augment/armmounted.dm @@ -86,6 +86,13 @@ integrated_object_type = /obj/item/weapon/melee/energy/sword +/obj/item/organ/internal/augment/armmounted/hand/blade + name = "handblade implant" + desc = "A small implant that fits neatly into the hand. It deploys a small, but dangerous blade." + icon_state = "augment_handblade" + + integrated_object_type = /obj/item/weapon/melee/augment/blade + /* * Shoulder augment. */ @@ -136,6 +143,14 @@ if(istype(owner, /mob/living/carbon/human)) var/mob/living/carbon/human/H = owner H.add_modifier(/datum/modifier/melee_surge, 0.75 MINUTES) + +/obj/item/organ/internal/augment/armmounted/shoulder/blade + name = "armblade implant" + desc = "A large implant that fits into a subject's arm. It deploys a large metal blade by some painful means." + + icon_state = "augment_armblade" + + integrated_object_type = /obj/item/weapon/melee/augment/blade/arm // The toolkit / multi-tool implant. diff --git a/code/modules/organs/internal/stomach.dm b/code/modules/organs/internal/stomach.dm index c8e90d9f78..31adb758ae 100644 --- a/code/modules/organs/internal/stomach.dm +++ b/code/modules/organs/internal/stomach.dm @@ -64,4 +64,10 @@ if(owner && owner.stat != DEAD) owner.bodytemperature += round(owner.robobody_count * 0.25, 0.1) + if(ishuman(owner)) + var/mob/living/carbon/human/H = owner + + if(H.ingested?.total_volume && H.bloodstr) + H.ingested.trans_to_holder(H.bloodstr, rand(2,5)) + return diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index 8cad30e19c..7e954df0eb 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -1133,6 +1133,8 @@ Note that amputating the affected organ does in fact remove the infection from t remove_splint() get_icon() unmutate() + drop_sound = 'sound/items/drop/weldingtool.ogg' + pickup_sound = 'sound/items/pickup/weldingtool.ogg' for(var/obj/item/organ/external/T in children) T.robotize(company, keep_organs = keep_organs) diff --git a/code/modules/overmap/overmap_shuttle.dm b/code/modules/overmap/overmap_shuttle.dm index 6693778ec5..4a571c1a06 100644 --- a/code/modules/overmap/overmap_shuttle.dm +++ b/code/modules/overmap/overmap_shuttle.dm @@ -133,10 +133,20 @@ var/icon_full = "fuel_port_full" var/opened = 0 var/parent_shuttle + var/base_tank = /obj/item/weapon/tank/phoron /obj/structure/fuel_port/Initialize() . = ..() - new /obj/item/weapon/tank/phoron(src) + if(base_tank) + new base_tank(src) + +/obj/structure/fuel_port/heavy + base_tank = /obj/item/weapon/tank/phoron/pressurized + +/obj/structure/fuel_port/empty + base_tank = null //oops, no gas! + opened = 1 //shows open so you can diagnose 'oops, no gas' easily + icon_state = "fuel_port_empty" //set the default state just to be safe /obj/structure/fuel_port/attack_hand(mob/user as mob) if(!opened) diff --git a/code/modules/overmap/ships/computers/shuttle.dm b/code/modules/overmap/ships/computers/shuttle.dm index f51587d594..76c0d3e6d1 100644 --- a/code/modules/overmap/ships/computers/shuttle.dm +++ b/code/modules/overmap/ships/computers/shuttle.dm @@ -47,6 +47,6 @@ else to_chat(usr,"No valid landing sites in range.") possible_d = shuttle.get_possible_destinations() - if(CanInteract(usr, global.default_state) && (D in possible_d)) + if(CanInteract(usr, GLOB.tgui_default_state) && (D in possible_d)) shuttle.set_destination(possible_d[D]) return TRUE diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 269c0fc995..9f4a183b31 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -17,8 +17,10 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins active_power_usage = 200 circuit = /obj/item/weapon/circuitboard/fax - var/obj/item/weapon/card/id/scan = null // identification - var/authenticated = 0 + var/obj/item/weapon/card/id/scan = null + var/authenticated = null + var/rank = null + var/sendcooldown = 0 // to avoid spamming fax messages var/department = "Unknown" // our department var/destination = null // the department we're sending to @@ -33,91 +35,101 @@ var/list/adminfaxes = list() //cache for faxes that have been sent to admins /obj/machinery/photocopier/faxmachine/attack_hand(mob/user as mob) user.set_machine(src) - ui_interact(user) + tgui_interact(user) -/** - * Display the NanoUI window for the fax machine. - * - * See NanoUI documentation for details. - */ -/obj/machinery/photocopier/faxmachine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) +/obj/machinery/photocopier/faxmachine/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Fax", name) + ui.open() - var/list/data = list() - if(scan) - data["scanName"] = scan.name - else - data["scanName"] = null - data["bossName"] = using_map.boss_name +/obj/machinery/photocopier/faxmachine/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + data["scan"] = scan ? scan.name : null data["authenticated"] = authenticated + data["rank"] = rank + data["isAI"] = isAI(user) + data["isRobot"] = isrobot(user) + + data["bossName"] = using_map.boss_name data["copyItem"] = copyitem - if(copyitem) - data["copyItemName"] = copyitem.name - else - data["copyItemName"] = null data["cooldown"] = sendcooldown data["destination"] = destination - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "fax.tmpl", src.name, 500, 500) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(10) //this machine is so unimportant let's not have it update that often. + return data -/obj/machinery/photocopier/faxmachine/Topic(href, href_list) - if(href_list["send"]) - if(copyitem) - if (destination in admin_departments) - send_admin_fax(usr, destination) - else - sendfax(destination) +/obj/machinery/photocopier/faxmachine/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE - if (sendcooldown) - spawn(sendcooldown) // cooldown time - sendcooldown = 0 - - else if(href_list["remove"]) - if(copyitem) - if(get_dist(usr, src) >= 2) - to_chat(usr, "\The [copyitem] is too far away for you to remove it.") - return - copyitem.loc = usr.loc - usr.put_in_hands(copyitem) - to_chat(usr, "You take \the [copyitem] out of \the [src].") - copyitem = null - - if(href_list["scan"]) - if (scan) - if(ishuman(usr)) - scan.loc = usr.loc - if(!usr.get_active_hand()) + switch(action) + if("scan") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) usr.put_in_hands(scan) scan = null else - scan.loc = src.loc + var/obj/item/I = usr.get_active_hand() + if(istype(I, /obj/item/weapon/card/id)) + usr.drop_item() + I.forceMove(src) + scan = I + return TRUE + if("login") + var/login_type = text2num(params["login_type"]) + if(login_type == LOGIN_TYPE_NORMAL && istype(scan)) + if(check_access(scan)) + authenticated = scan.registered_name + rank = scan.assignment + else if(login_type == LOGIN_TYPE_AI && isAI(usr)) + authenticated = usr.name + rank = "AI" + else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr)) + authenticated = usr.name + var/mob/living/silicon/robot/R = usr + rank = "[R.modtype] [R.braintype]" + return TRUE + if("logout") + if(scan) + scan.forceMove(loc) + if(ishuman(usr) && !usr.get_active_hand()) + usr.put_in_hands(scan) scan = null - else - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/weapon/card/id) && usr.unEquip(I)) - I.loc = src - scan = I - authenticated = 0 + authenticated = null + return TRUE + if("remove") + if(copyitem) + if(get_dist(usr, src) >= 2) + to_chat(usr, "\The [copyitem] is too far away for you to remove it.") + return + copyitem.forceMove(loc) + usr.put_in_hands(copyitem) + to_chat(usr, "You take \the [copyitem] out of \the [src].") + copyitem = null - if(href_list["dept"]) - var/lastdestination = destination - destination = input(usr, "Which department?", "Choose a department", "") as null|anything in (alldepartments + admin_departments) - if(!destination) destination = lastdestination + if(!authenticated) + return + + switch(action) + if("send") + if(copyitem) + if (destination in admin_departments) + send_admin_fax(usr, destination) + else + sendfax(destination) - if(href_list["auth"]) - if ( (!( authenticated ) && (scan)) ) - if (check_access(scan)) - authenticated = 1 + if (sendcooldown) + spawn(sendcooldown) // cooldown time + sendcooldown = 0 - if(href_list["logout"]) - authenticated = 0 + if("dept") + var/lastdestination = destination + destination = input(usr, "Which department?", "Choose a department", "") as null|anything in (alldepartments + admin_departments) + if(!destination) + destination = lastdestination - SSnanoui.update_uis(src) + return TRUE /obj/machinery/photocopier/faxmachine/attackby(obj/item/O as obj, mob/user as mob) if(O.is_multitool() && panel_open) diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index 8c12f654f6..9182a17530 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -6,6 +6,8 @@ var/label = null var/labels_left = 30 var/mode = 0 //off or on. + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' /obj/item/weapon/hand_labeler/attack() return diff --git a/code/modules/paperwork/photocopier.dm b/code/modules/paperwork/photocopier.dm index 5bf6a9102a..07009a99c0 100644 --- a/code/modules/paperwork/photocopier.dm +++ b/code/modules/paperwork/photocopier.dm @@ -33,33 +33,71 @@ /obj/machinery/photocopier/attack_hand(mob/user as mob) user.set_machine(src) - ui_interact(user) + tgui_interact(user) -/** - * Display the NanoUI window for the photocopier. - * - * See NanoUI documentation for details. - */ -/obj/machinery/photocopier/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) - - var/list/data = list() - data["copyItem"] = copyitem - data["assPresent"] = has_buckled_mobs() - data["toner"] = toner - data["copies"] = copies - data["maxCopies"] = maxcopies - if(istype(user,/mob/living/silicon)) - data["isSilicon"] = 1 - else - data["isSilicon"] = null - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "photocopier.tmpl", src.name, 300, 250) - ui.set_initial_data(data) +/obj/machinery/photocopier/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Photocopier", name) ui.open() - ui.set_auto_update(10) + +/obj/machinery/photocopier/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["has_item"] = copyitem || has_buckled_mobs() // VOREStation Edit: Ass copying + data["isAI"] = issilicon(user) + data["can_AI_print"] = (toner >= 5) + data["has_toner"] = !!toner + data["current_toner"] = toner + data["max_toner"] = 40 + data["num_copies"] = copies + data["max_copies"] = maxcopies + + return data + +/obj/machinery/photocopier/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("make_copy") + addtimer(CALLBACK(src, .proc/copy_operation, usr), 0) + . = TRUE + if("remove") + if(copyitem) + copyitem.loc = usr.loc + usr.put_in_hands(copyitem) + to_chat(usr, "You take \the [copyitem] out of \the [src].") + copyitem = null + else if(has_buckled_mobs()) + to_chat(buckled_mobs[1], "You feel a slight pressure on your ass.") // It can't eject your asscheeks, but it'll try. + . = TRUE + if("set_copies") + copies = clamp(text2num(params["num_copies"]), 1, maxcopies) + . = TRUE + if("ai_photo") + if(!issilicon(usr)) + return + if(stat & (BROKEN|NOPOWER)) + return + + if(toner >= 5) + var/mob/living/silicon/tempAI = usr + var/obj/item/device/camera/siliconcam/camera = tempAI.aiCamera + + if(!camera) + return + var/obj/item/weapon/photo/selection = camera.selectpicture() + if (!selection) + return + + var/obj/item/weapon/photo/p = photocopy(selection) + if (p.desc == "") + p.desc += "Copied by [tempAI.name]" + else + p.desc += " - Copied by [tempAI.name]" + toner -= 5 + . = TRUE /obj/machinery/photocopier/proc/copy_operation(var/mob/user) if(copying) @@ -104,51 +142,6 @@ use_power(active_power_usage) copying = FALSE -/obj/machinery/photocopier/Topic(href, href_list) - if(href_list["copy"]) - if(stat & (BROKEN|NOPOWER)) - return - addtimer(CALLBACK(src, .proc/copy_operation, usr), 0) - - else if(href_list["remove"]) - if(copyitem) - copyitem.loc = usr.loc - usr.put_in_hands(copyitem) - to_chat(usr, "You take \the [copyitem] out of \the [src].") - copyitem = null - else if(has_buckled_mobs()) - to_chat(buckled_mobs[1], "You feel a slight pressure on your ass.") // It can't eject your asscheeks, but it'll try. - return TOPIC_REFRESH - else if(href_list["min"]) - if(copies > 1) - copies-- - else if(href_list["add"]) - if(copies < maxcopies) - copies++ - else if(href_list["aipic"]) - if(!istype(usr,/mob/living/silicon)) return - if(stat & (BROKEN|NOPOWER)) return - - if(toner >= 5) - var/mob/living/silicon/tempAI = usr - var/obj/item/device/camera/siliconcam/camera = tempAI.aiCamera - - if(!camera) - return - var/obj/item/weapon/photo/selection = camera.selectpicture() - if (!selection) - return - - var/obj/item/weapon/photo/p = photocopy(selection) - if (p.desc == "") - p.desc += "Copied by [tempAI.name]" - else - p.desc += " - Copied by [tempAI.name]" - toner -= 5 - sleep(15) - - SSnanoui.update_uis(src) - /obj/machinery/photocopier/attackby(obj/item/O as obj, mob/user as mob) if(istype(O, /obj/item/weapon/paper) || istype(O, /obj/item/weapon/photo) || istype(O, /obj/item/weapon/paper_bundle)) if(!copyitem) diff --git a/code/modules/paperwork/stamps.dm b/code/modules/paperwork/stamps.dm index 970b278e8b..5d15083229 100644 --- a/code/modules/paperwork/stamps.dm +++ b/code/modules/paperwork/stamps.dm @@ -12,6 +12,8 @@ matter = list(DEFAULT_WALL_MATERIAL = 60) pressure_resistance = 2 attack_verb = list("stamped") + drop_sound = 'sound/items/drop/device.ogg' + pickup_sound = 'sound/items/pickup/device.ogg' /obj/item/weapon/stamp/captain name = "site manager's rubber stamp" diff --git a/code/modules/pda/ai.dm b/code/modules/pda/ai.dm new file mode 100644 index 0000000000..9072eef96e --- /dev/null +++ b/code/modules/pda/ai.dm @@ -0,0 +1,58 @@ + +// Special AI/pAI PDAs that cannot explode. +/obj/item/device/pda/ai + icon_state = "NONE" + ttone = "data" + detonate = 0 + touch_silent = TRUE + programs = list( + new/datum/data/pda/app/main_menu, + new/datum/data/pda/app/notekeeper, + new/datum/data/pda/app/news, + new/datum/data/pda/app/messenger) + +/obj/item/device/pda/ai/proc/set_name_and_job(newname as text, newjob as text, newrank as null|text) + owner = newname + ownjob = newjob + if(newrank) + ownrank = newrank + else + ownrank = ownjob + name = newname + " (" + ownjob + ")" + +//AI verb and proc for sending PDA messages. +/obj/item/device/pda/ai/verb/cmd_pda_open_ui() + set category = "AI IM" + set name = "Use PDA" + set src in usr + + if(!can_use()) + return + tgui_interact(usr) + +/obj/item/device/pda/ai/can_use() + return 1 + +/obj/item/device/pda/ai/attack_self(mob/user as mob) + if ((honkamt > 0) && (prob(60)))//For clown virus. + honkamt-- + playsound(src, 'sound/items/bikehorn.ogg', 30, 1) + return + + +/obj/item/device/pda/ai/pai + ttone = "assist" + var/our_owner = null // Ref to a pAI + +/obj/item/device/pda/ai/pai/New(mob/living/silicon/pai/P) + if(istype(P)) + our_owner = REF(P) + return ..() + +/obj/item/device/pda/ai/pai/tgui_status(mob/living/silicon/pai/user, datum/tgui_state/state) + if(!istype(user) || REF(user) != our_owner) // Only allow our pAI to interface with us + return STATUS_CLOSE + return ..() + +/obj/item/device/pda/ai/shell + spam_proof = TRUE // Since empty shells get a functional PDA. diff --git a/code/modules/pda/app.dm b/code/modules/pda/app.dm new file mode 100644 index 0000000000..eb64125ee9 --- /dev/null +++ b/code/modules/pda/app.dm @@ -0,0 +1,109 @@ +// Base class for anything that can show up on home screen +/datum/data/pda + var/icon = "tasks" //options comes from http://fontawesome.io/icons/ + var/notify_icon = "exclamation-circle" + var/notify_silent = 0 + var/hidden = 0 // program not displayed in main menu + var/category = "General" // the category to list it in on the main menu + var/obj/item/device/pda/pda // if this is null, and the app is running code, something's gone wrong + +/datum/data/pda/Destroy() + pda = null + return ..() + +/datum/data/pda/proc/start() + return + +/datum/data/pda/proc/stop() + return + +/datum/data/pda/proc/program_process() + return + +/datum/data/pda/proc/program_hit_check() + return + +/datum/data/pda/proc/notify(message, blink = 1) + if(message) + //Search for holder of the PDA. + var/mob/living/L = null + if(pda.loc && isliving(pda.loc)) + L = pda.loc + //Maybe they are a pAI! + else + L = get(pda, /mob/living/silicon) + + if(L) + to_chat(L, "[bicon(pda)] [message]") + SStgui.update_user_uis(L, pda) // Update the receiving user's PDA UI so that they can see the new message + + if(!notify_silent) + pda.play_ringtone() + + if(blink && !(src in pda.notifying_programs)) + pda.overlays += image('icons/obj/pda.dmi', "pda-r") + pda.notifying_programs |= src + +/datum/data/pda/proc/unnotify() + if(src in pda.notifying_programs) + pda.notifying_programs -= src + if(!pda.notifying_programs.len) + pda.overlays -= image('icons/obj/pda.dmi', "pda-r") + +// An app has a button on the home screen and its own UI +/datum/data/pda/app + name = "App" + size = 3 + var/title = null // what is displayed in the title bar when this is the current app + var/template = "" + var/update = PDA_APP_UPDATE + var/has_back = 0 + +/datum/data/pda/app/tgui_host(mob/user) + return pda || src + +/datum/data/pda/app/New() + if(!title) + title = name + +/datum/data/pda/app/start() + if(pda.current_app) + pda.current_app.stop() + pda.current_app = src + return 1 + +/datum/data/pda/app/proc/update_ui(mob/user as mob, list/data) + + +// Utilities just have a button on the home screen, but custom code when clicked +/datum/data/pda/utility + name = "Utility" + icon = "gear" + size = 1 + category = "Utilities" + + +/datum/data/pda/utility/scanmode + var/base_name + category = "Scanners" + +/datum/data/pda/utility/scanmode/New(obj/item/weapon/cartridge/C) + ..(C) + name = "Enable [base_name]" + +/datum/data/pda/utility/scanmode/start() + if(pda.scanmode) + pda.scanmode.name = "Enable [pda.scanmode.base_name]" + + if(pda.scanmode == src) + pda.scanmode = null + else + pda.scanmode = src + name = "Disable [base_name]" + + pda.update_shortcuts() + return 1 + +/datum/data/pda/utility/scanmode/proc/scan_mob(mob/living/C as mob, mob/living/user as mob) + +/datum/data/pda/utility/scanmode/proc/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) diff --git a/code/modules/pda/cart.dm b/code/modules/pda/cart.dm new file mode 100644 index 0000000000..2ec6a4e536 --- /dev/null +++ b/code/modules/pda/cart.dm @@ -0,0 +1,311 @@ +var/list/command_cartridges = list( + /obj/item/weapon/cartridge/captain, + /obj/item/weapon/cartridge/hop, + /obj/item/weapon/cartridge/hos, + /obj/item/weapon/cartridge/ce, + /obj/item/weapon/cartridge/rd, + /obj/item/weapon/cartridge/cmo, + /obj/item/weapon/cartridge/head, + /obj/item/weapon/cartridge/lawyer // Internal Affaris, + ) + +var/list/security_cartridges = list( + /obj/item/weapon/cartridge/security, + /obj/item/weapon/cartridge/detective, + /obj/item/weapon/cartridge/hos + ) + +var/list/engineering_cartridges = list( + /obj/item/weapon/cartridge/engineering, + /obj/item/weapon/cartridge/atmos, + /obj/item/weapon/cartridge/ce + ) + +var/list/medical_cartridges = list( + /obj/item/weapon/cartridge/medical, + /obj/item/weapon/cartridge/chemistry, + /obj/item/weapon/cartridge/cmo + ) + +var/list/research_cartridges = list( + /obj/item/weapon/cartridge/signal/science, + /obj/item/weapon/cartridge/rd + ) + +var/list/cargo_cartridges = list( + /obj/item/weapon/cartridge/quartermaster, // This also covers cargo-techs, apparently, + /obj/item/weapon/cartridge/miner, + /obj/item/weapon/cartridge/hop + ) + +var/list/civilian_cartridges = list( + /obj/item/weapon/cartridge/janitor, + /obj/item/weapon/cartridge/service, + /obj/item/weapon/cartridge/hop + ) + +/obj/item/weapon/cartridge + name = "generic cartridge" + desc = "A data cartridge for portable microcomputers." + icon = 'icons/obj/pda.dmi' + icon_state = "cart" + item_state = "electronic" + w_class = ITEMSIZE_TINY + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' + + var/obj/item/radio/integrated/radio = null + + var/charges = 0 + + var/list/stored_data = list() + var/list/programs = list() + var/list/messenger_plugins = list() + +/obj/item/weapon/cartridge/Destroy() + QDEL_NULL(radio) + QDEL_LIST(programs) + QDEL_LIST(messenger_plugins) + return ..() + +/obj/item/weapon/cartridge/proc/update_programs(obj/item/device/pda/pda) + for(var/A in programs) + var/datum/data/pda/P = A + P.pda = pda + for(var/A in messenger_plugins) + var/datum/data/pda/messenger_plugin/P = A + P.pda = pda + +/obj/item/weapon/cartridge/engineering + name = "\improper Power-ON cartridge" + icon_state = "cart-e" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen) + +/obj/item/weapon/cartridge/atmos + name = "\improper BreatheDeep cartridge" + icon_state = "cart-a" + programs = list(new/datum/data/pda/utility/scanmode/gas) + +/obj/item/weapon/cartridge/medical + name = "\improper Med-U cartridge" + icon_state = "cart-m" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical) + +/obj/item/weapon/cartridge/chemistry + name = "\improper ChemWhiz cartridge" + icon_state = "cart-chem" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + new/datum/data/pda/utility/scanmode/reagent) + +/obj/item/weapon/cartridge/security + name = "\improper R.O.B.U.S.T. cartridge" + icon_state = "cart-s" + programs = list( + new/datum/data/pda/app/crew_records/security) + +/obj/item/weapon/cartridge/detective + name = "\improper D.E.T.E.C.T. cartridge" + icon_state = "cart-s" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/app/crew_records/security) + + +/obj/item/weapon/cartridge/janitor + name = "\improper CustodiPRO cartridge" + desc = "The ultimate in clean-room design." + icon_state = "cart-j" + programs = list(new/datum/data/pda/app/janitor) + +/obj/item/weapon/cartridge/lawyer + name = "\improper P.R.O.V.E. cartridge" + icon_state = "cart-s" + programs = list(new/datum/data/pda/app/crew_records/security) + +/obj/item/weapon/cartridge/clown + name = "\improper Honkworks 5.0 cartridge" + icon_state = "cart-clown" + charges = 5 + programs = list(new/datum/data/pda/utility/honk) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/clown) + +/obj/item/weapon/cartridge/mime + name = "\improper Gestur-O 1000 cartridge" + icon_state = "cart-mi" + charges = 5 + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/mime) + +/obj/item/weapon/cartridge/service + name = "\improper Serv-U Pro cartridge" + desc = "A data cartridge designed to serve YOU!" + +/obj/item/weapon/cartridge/signal + name = "generic signaler cartridge" + desc = "A data cartridge with an integrated radio signaler module." + programs = list(new/datum/data/pda/app/signaller) + +/obj/item/weapon/cartridge/signal/Initialize() + radio = new /obj/item/radio/integrated/signal(src) + ..() + +/obj/item/weapon/cartridge/signal/science + name = "\improper Signal Ace 2 cartridge" + desc = "Complete with integrated radio signaler!" + icon_state = "cart-tox" + programs = list( + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/signaller) + +/obj/item/weapon/cartridge/quartermaster + name = "\improper Space Parts & Space Vendors cartridge" + desc = "Perfect for the Quartermaster on the go!" + icon_state = "cart-q" + programs = list( + new/datum/data/pda/app/supply) + +/obj/item/weapon/cartridge/miner + name = "\improper Drill-Jockey 4.5 cartridge" + desc = "It's covered in some sort of sand." + icon_state = "cart-q" + +/obj/item/weapon/cartridge/head + name = "\improper Easy-Record DELUXE cartridge" + icon_state = "cart-h" + programs = list(new/datum/data/pda/app/status_display) + +/obj/item/weapon/cartridge/hop + name = "\improper HumanResources9001 cartridge" + icon_state = "cart-h" + programs = list( + new/datum/data/pda/app/crew_records/security, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + + new/datum/data/pda/app/status_display) + +/obj/item/weapon/cartridge/hos + name = "\improper R.O.B.U.S.T. DELUXE cartridge" + icon_state = "cart-hos" + programs = list( + new/datum/data/pda/app/crew_records/security, + + new/datum/data/pda/app/status_display) + +/obj/item/weapon/cartridge/ce + name = "\improper Power-On DELUXE cartridge" + icon_state = "cart-ce" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/status_display) + +/obj/item/weapon/cartridge/cmo + name = "\improper Med-U DELUXE cartridge" + icon_state = "cart-cmo" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/status_display) + +/obj/item/weapon/cartridge/rd + name = "\improper Signal Ace DELUXE cartridge" + icon_state = "cart-rd" + programs = list( + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/signaller, + + new/datum/data/pda/app/status_display) + +/obj/item/weapon/cartridge/rd/Initialize() + radio = new /obj/item/radio/integrated/signal(src) + . = ..() + +/obj/item/weapon/cartridge/captain + name = "\improper Value-PAK cartridge" + desc = "Now with 200% more value!" + icon_state = "cart-c" + programs = list( + new/datum/data/pda/app/power, + new/datum/data/pda/utility/scanmode/halogen, + + new/datum/data/pda/utility/scanmode/gas, + + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + + new/datum/data/pda/utility/scanmode/reagent, + + new/datum/data/pda/app/crew_records/security, + + new/datum/data/pda/app/janitor, + + new/datum/data/pda/app/supply, + + new/datum/data/pda/app/status_display) + +/obj/item/weapon/cartridge/syndicate + name = "\improper Detomatix cartridge" + icon_state = "cart" + var/initial_remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!! //don't ask about the name, testing. + charges = 4 + programs = list(new/datum/data/pda/utility/toggle_door) + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/detonate) + +/obj/item/weapon/cartridge/syndicate/New() + var/datum/data/pda/utility/toggle_door/D = programs[1] + if(istype(D)) + D.remote_door_id = initial_remote_door_id + +/obj/item/weapon/cartridge/proc/post_status(var/command, var/data1, var/data2) + + var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) + if(!frequency) return + + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = TRANSMISSION_RADIO + status_signal.data["command"] = command + + switch(command) + if("message") + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + if(loc) + var/obj/item/PDA = loc + var/mob/user = PDA.fingerprintslast + log_admin("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") + message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") + + if("alert") + status_signal.data["picture_state"] = data1 + + frequency.post_signal(src, status_signal) + +/obj/item/weapon/cartridge/frame + name = "F.R.A.M.E. cartridge" + icon_state = "cart" + charges = 5 + var/telecrystals = 0 + messenger_plugins = list(new/datum/data/pda/messenger_plugin/virus/frame) diff --git a/code/modules/pda/cart_apps.dm b/code/modules/pda/cart_apps.dm new file mode 100644 index 0000000000..e3e0166f9b --- /dev/null +++ b/code/modules/pda/cart_apps.dm @@ -0,0 +1,311 @@ +/datum/data/pda/app/status_display + name = "Status Display" + icon = "list-alt" + template = "pda_status_display" + category = "Utilities" + + var/message1 // used for status_displays + var/message2 + +/datum/data/pda/app/status_display/update_ui(mob/user as mob, list/data) + data["records"] = list( + "message1" = message1 ? message1 : "(none)", + "message2" = message2 ? message2 : "(none)") + +/datum/data/pda/app/status_display/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + switch(action) + if("Status") + switch(params["statdisp"]) + if("message") + post_status("message", message1, message2) + if("alert") + post_status("alert", params["alert"]) + if("setmsg1") + message1 = clean_input("Line 1", "Enter Message Text", message1) + if("setmsg2") + message2 = clean_input("Line 2", "Enter Message Text", message2) + else + post_status(params["statdisp"]) + return TRUE + +/datum/data/pda/app/status_display/proc/post_status(var/command, var/data1, var/data2) + var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) + if(!frequency) + return + + var/datum/signal/status_signal = new + status_signal.source = src + status_signal.transmission_method = 1 + status_signal.data["command"] = command + + switch(command) + if("message") + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + var/mob/user = pda.fingerprintslast + if(istype(pda.loc, /mob/living)) + user = pda.loc + log_admin("STATUS: [user] set status screen with [pda]. Message: [data1] [data2]") + message_admins("STATUS: [user] set status screen with [pda]. Message: [data1] [data2]") + + if("alert") + status_signal.data["picture_state"] = data1 + + spawn(0) + frequency.post_signal(src, status_signal) + + +/datum/data/pda/app/signaller + name = "Signaler System" + icon = "rss" + template = "pda_signaller" + category = "Utilities" + +/datum/data/pda/app/signaller/update_ui(mob/user as mob, list/data) + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/signal)) + var/obj/item/radio/integrated/signal/R = pda.cartridge.radio + data["frequency"] = R.frequency + data["minFrequency"] = RADIO_LOW_FREQ + data["maxFrequency"] = RADIO_HIGH_FREQ + data["code"] = R.code + +/datum/data/pda/app/signaller/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + if(pda.cartridge && istype(pda.cartridge.radio, /obj/item/radio/integrated/signal)) + var/obj/item/radio/integrated/signal/R = pda.cartridge.radio + + switch(action) + if("signal") + spawn(0) + R.send_signal("ACTIVATE") + if("freq") + var/frequency = unformat_frequency(params["freq"]) + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + R.set_frequency(frequency) + . = TRUE + if("code") + R.code = clamp(round(text2num(params["code"])), 1, 100) + . = TRUE + if("reset") + if(params["reset"] == "freq") + R.set_frequency(initial(R.frequency)) + else + R.code = initial(R.code) + . = TRUE + +/datum/data/pda/app/power + name = "Power Monitor" + icon = "exclamation-triangle" + template = "pda_power" + category = "Engineering" + + var/datum/tgui_module/power_monitor/power_monitor + +/datum/data/pda/app/power/New() + power_monitor = new(src) + . = ..() + +/datum/data/pda/app/power/Destroy() + QDEL_NULL(power_monitor) + return ..() + +/datum/data/pda/app/power/update_ui(mob/user as mob, list/data) + data.Add(power_monitor.tgui_data(user)) + +/datum/data/pda/app/power/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + if(power_monitor.tgui_act(action, params, ui, state)) + return TRUE + switch(action) + if("Back") + power_monitor.active_sensor = null + return TRUE + +/datum/data/pda/app/crew_records + var/datum/data/record/general_records = null + +/datum/data/pda/app/crew_records/update_ui(mob/user as mob, list/data) + var/list/records[0] + + if(general_records && (general_records in data_core.general)) + data["records"] = records + records["general"] = general_records.fields + return records + else + for(var/A in sortRecord(data_core.general)) + var/datum/data/record/R = A + if(R) + records += list(list(Name = R.fields["name"], "ref" = "\ref[R]")) + data["recordsList"] = records + data["records"] = null + return null + +/datum/data/pda/app/crew_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + switch(action) + if("Records") + var/datum/data/record/R = locate(params["target"]) + if(R && (R in data_core.general)) + load_records(R) + return TRUE + if("Back") + general_records = null + has_back = 0 + return TRUE + +/datum/data/pda/app/crew_records/proc/load_records(datum/data/record/R) + general_records = R + has_back = 1 + +/datum/data/pda/app/crew_records/medical + name = "Medical Records" + icon = "heartbeat" + template = "pda_medical" + category = "Medical" + + var/datum/data/record/medical_records = null + +/datum/data/pda/app/crew_records/medical/update_ui(mob/user as mob, list/data) + var/list/records = ..() + if(!records) + return + + if(medical_records && (medical_records in data_core.medical)) + records["medical"] = medical_records.fields + + return records + +/datum/data/pda/app/crew_records/medical/load_records(datum/data/record/R) + ..(R) + for(var/A in data_core.medical) + var/datum/data/record/E = A + if(E && (E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + medical_records = E + break + +/datum/data/pda/app/crew_records/security + name = "Security Records" + icon = "tags" + template = "pda_security" + category = "Security" + + var/datum/data/record/security_records = null + +/datum/data/pda/app/crew_records/security/update_ui(mob/user as mob, list/data) + var/list/records = ..() + if(!records) + return + + if(security_records && (security_records in data_core.security)) + records["security"] = security_records.fields + + return records + +/datum/data/pda/app/crew_records/security/load_records(datum/data/record/R) + ..(R) + for(var/A in data_core.security) + var/datum/data/record/E = A + if(E && (E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + security_records = E + break + +/datum/data/pda/app/supply + name = "Supply Records" + icon = "file-word-o" + template = "pda_supply" + category = "Quartermaster" + +/datum/data/pda/app/supply/update_ui(mob/user as mob, list/data) + var/supplyData[0] + var/datum/shuttle/autodock/ferry/supply/shuttle = SSsupply.shuttle + if (shuttle) + supplyData["shuttle_moving"] = shuttle.has_arrive_time() + supplyData["shuttle_eta"] = shuttle.eta_minutes() + supplyData["shuttle_loc"] = shuttle.at_station() ? "Station" : "Dock" + var/supplyOrderCount = 0 + var/supplyOrderData[0] + for(var/S in SSsupply.shoppinglist) + var/datum/supply_order/SO = S + + supplyOrderCount++ + supplyOrderData[++supplyOrderData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "ApprovedBy" = SO.ordered_by, "Comment" = html_encode(SO.comment)) + + supplyData["approved"] = supplyOrderData + supplyData["approved_count"] = supplyOrderCount + + var/requestCount = 0 + var/requestData[0] + for(var/S in SSsupply.order_history) + var/datum/supply_order/SO = S + if(SO.status != SUP_ORDER_REQUESTED) + continue + + requestCount++ + requestData[++requestData.len] = list("Number" = SO.ordernum, "Name" = html_encode(SO.object.name), "OrderedBy" = SO.ordered_by, "Comment" = html_encode(SO.comment)) + + supplyData["requests"] = requestData + supplyData["requests_count"] = requestCount + + data["supply"] = supplyData + +/datum/data/pda/app/janitor + name = "Custodial Locator" + icon = "trash-alt-o" + template = "pda_janitor" + category = "Utilities" + +/datum/data/pda/app/janitor/update_ui(mob/user as mob, list/data) + var/JaniData[0] + var/turf/cl = get_turf(pda) + + if(cl) + JaniData["user_loc"] = list("x" = cl.x, "y" = cl.y) + else + JaniData["user_loc"] = list("x" = 0, "y" = 0) + + var/MopData[0] + for(var/obj/item/weapon/mop/M in all_mops)//GLOB.janitorial_equipment) + var/turf/ml = get_turf(M) + if(ml) + if(ml.z != cl.z) + continue + var/direction = get_dir(pda, M) + MopData[++MopData.len] = list ("x" = ml.x, "y" = ml.y, "dir" = uppertext(dir2text(direction)), "status" = M.reagents.total_volume ? "Wet" : "Dry") + + var/BucketData[0] + for(var/obj/structure/mopbucket/B in all_mopbuckets)//GLOB.janitorial_equipment) + var/turf/bl = get_turf(B) + if(bl) + if(bl.z != cl.z) + continue + var/direction = get_dir(pda,B) + BucketData[++BucketData.len] = list ("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "volume" = B.reagents.total_volume, "max_volume" = B.reagents.maximum_volume) + + var/CbotData[0] + for(var/mob/living/bot/cleanbot/B in mob_list) + var/turf/bl = get_turf(B) + if(bl) + if(bl.z != cl.z) + continue + var/direction = get_dir(pda,B) + CbotData[++CbotData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "status" = B.on ? "Online" : "Offline") + + var/CartData[0] + for(var/obj/structure/janitorialcart/B in all_janitorial_carts)//GLOB.janitorial_equipment) + var/turf/bl = get_turf(B) + if(bl) + if(bl.z != cl.z) + continue + var/direction = get_dir(pda,B) + CartData[++CartData.len] = list("x" = bl.x, "y" = bl.y, "dir" = uppertext(dir2text(direction)), "volume" = B.reagents.total_volume, "max_volume" = B.reagents.maximum_volume) + + JaniData["mops"] = MopData.len ? MopData : null + JaniData["buckets"] = BucketData.len ? BucketData : null + JaniData["cleanbots"] = CbotData.len ? CbotData : null + JaniData["carts"] = CartData.len ? CartData : null + data["janitor"] = JaniData diff --git a/code/modules/pda/cart_vr.dm b/code/modules/pda/cart_vr.dm new file mode 100644 index 0000000000..1f1a1cbe52 --- /dev/null +++ b/code/modules/pda/cart_vr.dm @@ -0,0 +1,20 @@ +var/list/exploration_cartridges = list( + /obj/item/weapon/cartridge/explorer, + /obj/item/weapon/cartridge/sar + ) + +/obj/item/weapon/cartridge/explorer + name = "\improper Explorator cartridge" + icon_state = "cart-e" + programs = list( + new/datum/data/pda/utility/scanmode/reagent, + new/datum/data/pda/utility/scanmode/gas) + +/obj/item/weapon/cartridge/sar + name = "\improper Med-Exp cartridge" + icon_state = "cart-m" + programs = list( + new/datum/data/pda/app/crew_records/medical, + new/datum/data/pda/utility/scanmode/medical, + new/datum/data/pda/utility/scanmode/reagent, + new/datum/data/pda/utility/scanmode/gas) diff --git a/code/modules/pda/core_apps.dm b/code/modules/pda/core_apps.dm new file mode 100644 index 0000000000..4b561c9844 --- /dev/null +++ b/code/modules/pda/core_apps.dm @@ -0,0 +1,206 @@ +/datum/data/pda/app/main_menu + icon = "home" + template = "pda_main_menu" + hidden = 1 + +/datum/data/pda/app/main_menu/update_ui(mob/user as mob, list/data) + title = pda.name + + data["app"]["is_home"] = 1 + + data["apps"] = pda.shortcut_cache + data["categories"] = pda.shortcut_cat_order + data["pai"] = !isnull(pda.pai) // pAI inserted? + + var/list/notifying[0] + for(var/P in pda.notifying_programs) + notifying["\ref[P]"] = 1 + data["notifying"] = notifying + +/datum/data/pda/app/main_menu/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + switch(action) + if("UpdateInfo") + pda.ownjob = pda.id.assignment + pda.ownrank = pda.id.rank + pda.name = "PDA-[pda.owner] ([pda.ownjob])" + return TRUE + if("pai") + if(pda.pai) + if(pda.pai.loc != pda) + pda.pai = null + else + switch(text2num(params["option"])) + if(1) // Configure pAI device + pda.pai.attack_self(usr) + if(2) // Eject pAI device + var/turf/T = get_turf_or_move(pda.loc) + if(T) + pda.pai.loc = T + pda.pai = null + return TRUE + +/datum/data/pda/app/notekeeper + name = "Notekeeper" + icon = "sticky-note-o" + template = "pda_notekeeper" + + var/note = null + var/notehtml = "" + +/datum/data/pda/app/notekeeper/start() + . = ..() + if(!note) + note = "Congratulations, your station has chosen the [pda.model_name]!" + +/datum/data/pda/app/notekeeper/update_ui(mob/user as mob, list/data) + data["note"] = note // current pda notes + +/datum/data/pda/app/notekeeper/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + switch(action) + if("Edit") + var/n = input("Please enter message", name, notehtml) as message + if(pda.loc == usr) + note = adminscrub(n) + notehtml = html_decode(note) + note = replacetext(note, "\n", "
") + else + pda.close(usr) + return TRUE + +/datum/data/pda/app/manifest + name = "Crew Manifest" + icon = "user" + template = "pda_manifest" + +/datum/data/pda/app/manifest/update_ui(mob/user as mob, list/data) + if(data_core) + data_core.get_manifest_list() + data["manifest"] = PDA_Manifest + +/datum/data/pda/app/manifest/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + +/datum/data/pda/app/atmos_scanner + name = "Atmospheric Scan" + icon = "fire" + template = "pda_atmos_scan" + category = "Utilities" + +/datum/data/pda/app/atmos_scanner/update_ui(mob/user as mob, list/data) + var/list/results = list() + var/turf/T = get_turf(user) + if(!isnull(T)) + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles + if (total_moles) + var/o2_level = environment.gas["oxygen"]/total_moles + var/n2_level = environment.gas["nitrogen"]/total_moles + var/co2_level = environment.gas["carbon_dioxide"]/total_moles + var/phoron_level = environment.gas["phoron"]/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) + + // entry is what the element is describing + // Type identifies which unit or other special characters to use + // Val is the information reported + // Bad_high/_low are the values outside of which the entry reports as dangerous + // Poor_high/_low are the values outside of which the entry reports as unideal + // Values were extracted from the template itself + results = list( + list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), + list("entry" = "Temperature", "units" = "°C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), + list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), + list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) + ) + + if(isnull(results)) + results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) + + data["aircontents"] = results + +/datum/data/pda/app/news + name = "News" + icon = "newspaper" + template = "pda_news" + + var/newsfeed_channel + +/datum/data/pda/app/news/update_ui(mob/user as mob, list/data) + data["feeds"] = compile_news() + data["latest_news"] = get_recent_news() + if(newsfeed_channel) + data["target_feed"] = data["feeds"][newsfeed_channel] + else + data["target_feed"] = null + +/datum/data/pda/app/news/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + switch(action) + if("newsfeed") + newsfeed_channel = text2num(params["newsfeed"]) + +/datum/data/pda/app/news/proc/compile_news() + var/list/feeds = list() + for(var/datum/feed_channel/channel in news_network.network_channels) + var/list/messages = list() + if(!channel.censored) + var/index = 0 + for(var/datum/feed_message/FM in channel.messages) + index++ + var/list/msgdata = list( + "author" = FM.author, + "body" = FM.body, + "img" = null, + "message_type" = FM.message_type, + "time_stamp" = FM.time_stamp, + "caption" = FM.caption, + "index" = index + ) + if(FM.img) + msgdata["img"] = icon2base64(FM.img) + messages[++messages.len] = msgdata + + feeds[++feeds.len] = list( + "name" = channel.channel_name, + "censored" = channel.censored, + "author" = channel.author, + "messages" = messages, + "index" = feeds.len + 1 + ) + return feeds + +/datum/data/pda/app/news/proc/get_recent_news() + var/list/news = list() + + // Compile all the newscasts + for(var/datum/feed_channel/channel in news_network.network_channels) + if(!channel.censored) + for(var/datum/feed_message/FM in channel.messages) + var/body = replacetext(FM.body, "\n", "
") + news[++news.len] = list( + "channel" = channel.channel_name, + "author" = FM.author, + "body" = body, + "message_type" = FM.message_type, + "time_stamp" = FM.time_stamp, + "has_image" = (FM.img != null), + "caption" = FM.caption, + "time" = FM.post_time + ) + + // Cut out all but the youngest three + if(news.len > 3) + sortByKey(news, "time") + news.Cut(1, news.len - 2) // Last three have largest timestamps, youngest posts + news.Swap(1, 3) // List is sorted in ascending order of timestamp, we want descending + + return news \ No newline at end of file diff --git a/code/modules/pda/messenger.dm b/code/modules/pda/messenger.dm new file mode 100644 index 0000000000..38360e127a --- /dev/null +++ b/code/modules/pda/messenger.dm @@ -0,0 +1,247 @@ +/datum/data/pda/app/messenger + name = "Messenger" + icon = "comments-o" + notify_icon = "comments" + title = "SpaceMessenger V4.1.0" + template = "pda_messenger" + + var/toff = 0 //If 1, messenger disabled + var/list/tnote[0] //Current Texts + var/last_text //No text spamming + + var/m_hidden = 0 // Is the PDA hidden from the PDA list? + var/active_conversation = null // New variable that allows us to only view a single conversation. + var/list/conversations = list() // For keeping up with who we have PDA messsages from. + +/datum/data/pda/app/messenger/start() + . = ..() + unnotify() + +/datum/data/pda/app/messenger/update_ui(mob/user as mob, list/data) + data["silent"] = notify_silent // does the pda make noise when it receives a message? + data["toff"] = toff // is the messenger function turned off? + data["active_conversation"] = active_conversation // Which conversation are we following right now? + + has_back = active_conversation + if(active_conversation) + data["messages"] = tnote + for(var/c in tnote) + if(c["target"] == active_conversation) + data["convo_name"] = sanitize(c["owner"]) + data["convo_job"] = sanitize(c["job"]) + break + else + var/convopdas[0] + var/pdas[0] + for(var/A in PDAs) + var/obj/item/device/pda/P = A + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + + if(!P.owner || PM.toff || P == pda || PM.m_hidden) + continue + if(conversations.Find("\ref[P]")) + convopdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "1"))) + else + pdas.Add(list(list("Name" = "[P]", "Reference" = "\ref[P]", "Detonate" = "[P.detonate]", "inconvo" = "0"))) + + data["convopdas"] = convopdas + data["pdas"] = pdas + + var/list/plugins = list() + if(pda.cartridge) + for(var/A in pda.cartridge.messenger_plugins) + var/datum/data/pda/messenger_plugin/P = A + plugins += list(list(name = P.name, icon = P.icon, ref = "\ref[P]")) + data["plugins"] = plugins + + if(pda.cartridge) + data["charges"] = pda.cartridge.charges ? pda.cartridge.charges : 0 + +/datum/data/pda/app/messenger/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + unnotify() + + . = TRUE + switch(action) + if("Toggle Messenger") + toff = !toff + if("Toggle Ringer")//If viewing texts then erase them, if not then toggle silent status + notify_silent = !notify_silent + if("Clear")//Clears messages + if(params["option"] == "All") + tnote.Cut() + conversations.Cut() + if(params["option"] == "Convo") + var/new_tnote[0] + for(var/i in tnote) + if(i["target"] != active_conversation) + new_tnote[++new_tnote.len] = i + tnote = new_tnote + conversations.Remove(active_conversation) + + active_conversation = null + if("Message") + var/obj/item/device/pda/P = locate(params["target"]) + create_message(usr, P) + if(params["target"] in conversations) // Need to make sure the message went through, if not welp. + active_conversation = params["target"] + if("Select Conversation") + var/P = params["target"] + for(var/n in conversations) + if(P == n) + active_conversation = P + if("Messenger Plugin") + if(!params["target"] || !params["plugin"]) + return + + var/obj/item/device/pda/P = locate(params["target"]) + if(!P) + to_chat(usr, "PDA not found.") + + var/datum/data/pda/messenger_plugin/plugin = locate(params["plugin"]) + if(plugin && (plugin in pda.cartridge.messenger_plugins)) + plugin.messenger = src + plugin.user_act(usr, P) + if("Back") + active_conversation = null + +// Specifically here for the chat message. +/datum/data/pda/app/messenger/Topic(href, href_list) + if(!pda.can_use()) + return + unnotify() + + switch(href_list["choice"]) + if("Message") + var/obj/item/device/pda/P = locate(href_list["target"]) + create_message(usr, P) + if(href_list["target"] in conversations) // Need to make sure the message went through, if not welp. + active_conversation = href_list["target"] + + +/datum/data/pda/app/messenger/proc/create_message(var/mob/living/U, var/obj/item/device/pda/P) + var/t = input(U, "Please enter message", name, null) as text|null + if(!t) + return + t = sanitize(copytext(t, 1, MAX_MESSAGE_LEN)) + t = readd_quotes(t) + if(!t || !istype(P)) + return + if(!in_range(pda, U) && pda.loc != U) + return + + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + + if(!PM || PM.toff || toff) + return + + if(last_text && world.time < last_text + 5) + return + + if(!pda.can_use()) + return + + last_text = world.time + // check if telecomms I/O route 1459 is stable + //var/telecomms_intact = telecomms_process(P.owner, owner, t) + var/obj/machinery/message_server/useMS = null + if(message_servers) + for(var/A in message_servers) + var/obj/machinery/message_server/MS = A + //PDAs are now dependent on the Message Server. + if(MS.active) + useMS = MS + break + + var/datum/signal/signal = pda.telecomms_process() + + var/useTC = 0 + if(signal) + if(signal.data["done"]) + useTC = 1 + var/turf/pos = get_turf(P) + // TODO: Make the radio system cooperate with the space manager + if(pos.z in signal.data["level"]) + useTC = 2 + //Let's make this barely readable + if(signal.data["compression"] > 0) + t = Gibberish(t, signal.data["compression"] + 50) + + if(useMS && useTC) // only send the message if it's stable + if(useTC != 2) // Does our recipient have a broadcaster on their level? + to_chat(U, "ERROR: Cannot reach recipient.") + return + useMS.send_pda_message("[P.owner]","[pda.owner]","[t]") + pda.investigate_log("PDA Message - [U.key] - [pda.owner] -> [P.owner]: [t]", "pda") + + receive_message(list("sent" = 1, "owner" = "[P.owner]", "job" = "[P.ownjob]", "message" = "[t]", "target" = "\ref[P]"), "\ref[P]") + PM.receive_message(list("sent" = 0, "owner" = "[pda.owner]", "job" = "[pda.ownjob]", "message" = "[t]", "target" = "\ref[pda]"), "\ref[pda]") + + SStgui.update_user_uis(U, P) // Update the sending user's PDA UI so that they can see the new message + log_pda("(PDA: [src.name]) sent \"[t]\" to [P.name]", usr) + else + to_chat(U, "ERROR: Messaging server is not responding.") + +/datum/data/pda/app/messenger/proc/available_pdas() + var/list/names = list() + var/list/plist = list() + var/list/namecounts = list() + + if(toff) + to_chat(usr, "Turn on your receiver in order to send messages.") + return + + for(var/A in PDAs) + var/obj/item/device/pda/P = A + var/datum/data/pda/app/messenger/PM = P.find_program(/datum/data/pda/app/messenger) + + if(!P.owner || !PM || PM.hidden || P == pda || PM.toff) + continue + + var/name = P.owner + if(name in names) + namecounts[name]++ + name = text("[name] ([namecounts[name]])") + else + names.Add(name) + namecounts[name] = 1 + + plist[text("[name]")] = P + return plist + +/datum/data/pda/app/messenger/proc/can_receive() + return pda.owner && !toff && !hidden + +/datum/data/pda/app/messenger/proc/receive_message(list/data, ref) + tnote.Add(list(data)) + if(!conversations.Find(ref)) + conversations.Add(ref) + if(!data["sent"]) + var/owner = data["owner"] + var/job = data["job"] + var/message = data["message"] + notify("Message from [owner] ([job]), \"[message]\" (Reply)") + +/datum/data/pda/app/messenger/multicast +/datum/data/pda/app/messenger/multicast/receive_message(list/data, ref) + . = ..() + + var/obj/item/device/pda/multicaster/M = pda + if(!istype(M)) + return + + var/list/modified_message = data.Copy() + modified_message["owner"] = modified_message["owner"] + " \[Relayed]" + modified_message["target"] = "\ref[M]" + + var/list/targets = list() + for(var/obj/item/device/pda/pda in PDAs) + if(pda.cartridge && pda.owner && is_type_in_list(pda.cartridge, M.cartridges_to_send_to)) + targets |= pda + if(targets.len) + for(var/obj/item/device/pda/target in targets) + var/datum/data/pda/app/messenger/P = target.find_program(/datum/data/pda/app/messenger) + if(P) + P.receive_message(modified_message, "\ref[M]") \ No newline at end of file diff --git a/code/modules/pda/messenger_plugins.dm b/code/modules/pda/messenger_plugins.dm new file mode 100644 index 0000000000..90cb9460a4 --- /dev/null +++ b/code/modules/pda/messenger_plugins.dm @@ -0,0 +1,91 @@ +/datum/data/pda/messenger_plugin + var/datum/data/pda/app/messenger/messenger + +/datum/data/pda/messenger_plugin/proc/user_act(mob/user as mob, obj/item/device/pda/P) + + +/datum/data/pda/messenger_plugin/virus + name = "*Send Virus*" + +/datum/data/pda/messenger_plugin/virus/user_act(mob/user as mob, obj/item/device/pda/P) + var/datum/data/pda/app/messenger/M = P.find_program(/datum/data/pda/app/messenger) + + if(M && !M.toff && pda.cartridge.charges > 0) + pda.cartridge.charges-- + return 1 + return 0 + + +/datum/data/pda/messenger_plugin/virus/clown + icon = "star" + +/datum/data/pda/messenger_plugin/virus/clown/user_act(mob/user as mob, obj/item/device/pda/P) + . = ..(user, P) + if(.) + user.show_message("Virus sent!", 1) + P.honkamt = (rand(15,20)) + P.ttone = "honk" + + +/datum/data/pda/messenger_plugin/virus/mime + icon = "arrow-circle-down" + +/datum/data/pda/messenger_plugin/virus/mime/user_act(mob/user as mob, obj/item/device/pda/P) + . = ..(user, P) + if(.) + user.show_message("Virus sent!", 1) + var/datum/data/pda/app/M = P.find_program(/datum/data/pda/app/messenger) + if(M) + M.notify_silent = 1 + P.ttone = "silence" + + +/datum/data/pda/messenger_plugin/virus/detonate + name = "*Detonate*" + icon = "exclamation-circle" + +/datum/data/pda/messenger_plugin/virus/detonate/user_act(mob/user as mob, obj/item/device/pda/P) + . = ..(user, P) + if(.) + var/difficulty = 0 + + if(pda.cartridge) + difficulty += pda.cartridge.programs.len / 2 + else + difficulty += 2 + + if(!P.detonate || P.hidden_uplink) + user.show_message("The target PDA does not seem to respond to the detonation command.", 1) + pda.cartridge.charges++ + else if(prob(difficulty * 12)) + user.show_message("An error flashes on your [pda].", 1) + else if(prob(difficulty * 3)) + user.show_message("Energy feeds back into your [pda]!", 1) + pda.close(user) + pda.explode() + log_admin("[key_name(user)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up") + message_admins("[key_name_admin(user)] just attempted to blow up [P] with the Detomatix cartridge but failed, blowing themselves up", 1) + else + user.show_message("Success!", 1) + log_admin("[key_name(user)] just attempted to blow up [P] with the Detomatix cartridge and succeded") + message_admins("[key_name_admin(user)] just attempted to blow up [P] with the Detomatix cartridge and succeded", 1) + P.explode() + +/datum/data/pda/messenger_plugin/virus/frame + icon = "exclamation-circle" + +/datum/data/pda/messenger_plugin/virus/frame/user_act(mob/user, obj/item/device/pda/P) + . = ..(user, P) + if(.) + var/lock_code = "[rand(100,999)] [pick("Alpha","Bravo","Charlie","Delta","Echo","Foxtrot","Golf","Hotel","India","Juliet","Kilo","Lima","Mike","November","Oscar","Papa","Quebec","Romeo","Sierra","Tango","Uniform","Victor","Whiskey","X-ray","Yankee","Zulu")]" + user.show_message("Virus Sent! The unlock code to the target is: [lock_code]") + if(!P.hidden_uplink) + var/obj/item/device/uplink/hidden/uplink = new(P) + P.hidden_uplink = uplink + P.lock_code = lock_code + // else + // P.hidden_uplink.hidden_crystals += P.hidden_uplink.uses //Temporarially hide the PDA's crystals, so you can't steal telecrystals. + var/obj/item/weapon/cartridge/frame/parent_cart = pda.cartridge + P.hidden_uplink.uses = parent_cart.telecrystals + parent_cart.telecrystals = 0 + P.hidden_uplink.active = TRUE diff --git a/code/modules/pda/pda.dm b/code/modules/pda/pda.dm new file mode 100644 index 0000000000..3c9b03ce1e --- /dev/null +++ b/code/modules/pda/pda.dm @@ -0,0 +1,502 @@ + +//The advanced pea-green monochrome lcd of tomorrow. + +var/global/list/obj/item/device/pda/PDAs = list() + +/obj/item/device/pda + name = "\improper PDA" + desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge." + icon = 'icons/obj/pda.dmi' + icon_state = "pda" + item_state = "electronic" + w_class = ITEMSIZE_SMALL + slot_flags = SLOT_ID | SLOT_BELT + sprite_sheets = list(SPECIES_TESHARI = 'icons/mob/species/seromi/id.dmi') + + //Main variables + var/pdachoice = 1 + var/owner = null + var/default_cartridge = 0 // Access level defined by cartridge + var/obj/item/weapon/cartridge/cartridge = null //current cartridge + + //Secondary variables + var/model_name = "Thinktronic 5230 Personal Data Assistant" + var/datum/data/pda/utility/scanmode/scanmode = null + + var/lock_code = "" // Lockcode to unlock uplink + var/honkamt = 0 //How many honks left when infected with honk.exe + var/mimeamt = 0 //How many silence left when infected with mime.exe + var/detonate = 1 // Can the PDA be blown up? + var/ttone = "beep" //The ringtone! + var/list/ttone_sound = list("beep" = 'sound/machines/twobeep.ogg', + "boom" = 'sound/effects/explosionfar.ogg', + "slip" = 'sound/misc/slip.ogg', + "honk" = 'sound/items/bikehorn.ogg', + "SKREE" = 'sound/voice/shriek1.ogg', + // "holy" = 'sound/items/PDA/ambicha4-short.ogg', + "xeno" = 'sound/voice/hiss1.ogg') + var/hidden = 0 // Is the PDA hidden from the PDA list? + var/touch_silent = 0 //If 1, no beeps on interacting. + + var/obj/item/weapon/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both. + var/ownjob = null //related to above - this is assignment (potentially alt title) + var/ownrank = null // this one is rank, never alt title + + var/obj/item/device/paicard/pai = null // A slot for a personal AI device + + var/spam_proof = FALSE // If true, it can't be spammed by random events. + + var/datum/data/pda/app/current_app = null + var/datum/data/pda/app/lastapp = null + var/list/programs = list( + new/datum/data/pda/app/main_menu, + new/datum/data/pda/app/notekeeper, + new/datum/data/pda/app/news, + new/datum/data/pda/app/messenger, + new/datum/data/pda/app/manifest, + new/datum/data/pda/app/atmos_scanner, + new/datum/data/pda/utility/scanmode/notes, + new/datum/data/pda/utility/flashlight) + var/list/shortcut_cache = list() + var/list/shortcut_cat_order = list() + var/list/notifying_programs = list() + var/retro_mode = 0 + +/obj/item/device/pda/examine(mob/user) + . = ..() + if(Adjacent(user)) + . += "The time [stationtime2text()] is displayed in the corner of the screen." + +/obj/item/device/pda/CtrlClick() + if(issilicon(usr)) + return + + if(can_use(usr)) + remove_pen() + return + ..() + +/obj/item/device/pda/AltClick() + if(issilicon(usr)) + return + + if ( can_use(usr) ) + if(id) + remove_id() + else + to_chat(usr, "This PDA does not have an ID in it.") + +/obj/item/device/pda/proc/play_ringtone() + var/S + + if(ttone in ttone_sound) + S = ttone_sound[ttone] + else + S = 'sound/machines/twobeep.ogg' + playsound(loc, S, 50, 1) + for(var/mob/O in hearers(3, loc)) + O.show_message(text("[bicon(src)] *[ttone]*")) + +/obj/item/device/pda/proc/set_ringtone() + var/t = input("Please enter new ringtone", name, ttone) as text + if(in_range(src, usr) && loc == usr) + if(t) + if(hidden_uplink && hidden_uplink.check_trigger(usr, lowertext(t), lowertext(lock_code))) + to_chat(usr, "The PDA softly beeps.") + close(usr) + else + t = sanitize(copytext(t, 1, 20)) + ttone = t + return 1 + else + close(usr) + return 0 + +/obj/item/device/pda/New(var/mob/living/carbon/human/H) + ..() + PDAs += src + PDAs = sortAtom(PDAs) + update_programs() + if(default_cartridge) + cartridge = new default_cartridge(src) + cartridge.update_programs(src) + new /obj/item/weapon/pen(src) + pdachoice = isnull(H) ? 1 : (ishuman(H) ? H.pdachoice : 1) + switch(pdachoice) + if(1) icon = 'icons/obj/pda.dmi' + if(2) icon = 'icons/obj/pda_slim.dmi' + if(3) icon = 'icons/obj/pda_old.dmi' + if(4) icon = 'icons/obj/pda_rugged.dmi' + if(5) icon = 'icons/obj/pda_holo.dmi' + if(6) + icon = 'icons/obj/pda_wrist.dmi' + item_state = icon_state + item_icons = list( + slot_belt_str = 'icons/mob/pda_wrist.dmi', + slot_wear_id_str = 'icons/mob/pda_wrist.dmi', + slot_gloves_str = 'icons/mob/pda_wrist.dmi' + ) + desc = "A portable microcomputer by Thinktronic Systems, LTD. This model is a wrist-bound version." + slot_flags = SLOT_ID | SLOT_BELT | SLOT_GLOVES + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/seromi/pda_wrist.dmi', + SPECIES_VR_TESHARI = 'icons/mob/species/seromi/pda_wrist.dmi', + ) + else + icon = 'icons/obj/pda_old.dmi' + log_debug("Invalid switch for PDA, defaulting to old PDA icons. [pdachoice] chosen.") + start_program(find_program(/datum/data/pda/app/main_menu)) + +/obj/item/device/pda/proc/can_use() + if(!ismob(loc)) + return FALSE + + var/mob/M = loc + if(M.incapacitated(INCAPACITATION_ALL)) + return FALSE + if(src in M.contents) + return TRUE + return FALSE + +/obj/item/device/pda/GetAccess() + if(id) + return id.GetAccess() + else + return ..() + +/obj/item/device/pda/GetID() + return id + +/obj/item/device/pda/MouseDrop(obj/over_object as obj, src_location, over_location) + var/mob/M = usr + if((!istype(over_object, /obj/screen)) && can_use()) + return attack_self(M) + return + +/obj/item/device/pda/proc/close(mob/user) + SStgui.close_uis(src) + +/obj/item/device/pda/attack_self(mob/user as mob) + user.set_machine(src) + + if(active_uplink_check(user)) + return + + tgui_interact(user) + return + +/obj/item/device/pda/proc/start_program(datum/data/pda/P) + if(P && ((P in programs) || (cartridge && (P in cartridge.programs)))) + return P.start() + return 0 + +/obj/item/device/pda/proc/find_program(type) + var/datum/data/pda/A = locate(type) in programs + if(A) + return A + if(cartridge) + A = locate(type) in cartridge.programs + if(A) + return A + return null + +// force the cache to rebuild on update_ui +/obj/item/device/pda/proc/update_shortcuts() + shortcut_cache.Cut() + +/obj/item/device/pda/proc/update_programs() + for(var/A in programs) + var/datum/data/pda/P = A + P.pda = src + +/obj/item/device/pda/proc/detonate_act(var/obj/item/device/pda/P) + //TODO: sometimes these attacks show up on the message server + var/i = rand(1,100) + var/j = rand(0,1) //Possibility of losing the PDA after the detonation + var/message = "" + var/mob/living/M = null + if(ismob(P.loc)) + M = P.loc + + //switch(i) //Yes, the overlapping cases are intended. + if(i<=10) //The traditional explosion + P.explode() + j=1 + message += "Your [P] suddenly explodes!" + if(i>=10 && i<= 20) //The PDA burns a hole in the holder. + j=1 + if(M && isliving(M)) + M.apply_damage( rand(30,60) , BURN) + message += "You feel a searing heat! Your [P] is burning!" + if(i>=20 && i<=25) //EMP + empulse(P.loc, 1, 2, 4, 6, 1) + message += "Your [P] emits a wave of electromagnetic energy!" + if(i>=25 && i<=40) //Smoke + var/datum/effect/effect/system/smoke_spread/chem/S = new /datum/effect/effect/system/smoke_spread/chem + S.attach(P.loc) + S.set_up(P, 10, 0, P.loc) + playsound(P, 'sound/effects/smoke.ogg', 50, 1, -3) + S.start() + message += "Large clouds of smoke billow forth from your [P]!" + if(i>=40 && i<=45) //Bad smoke + var/datum/effect/effect/system/smoke_spread/bad/B = new /datum/effect/effect/system/smoke_spread/bad + B.attach(P.loc) + B.set_up(P, 10, 0, P.loc) + playsound(P, 'sound/effects/smoke.ogg', 50, 1, -3) + B.start() + message += "Large clouds of noxious smoke billow forth from your [P]!" + if(i>=65 && i<=75) //Weaken + if(M && isliving(M)) + M.apply_effects(0,1) + message += "Your [P] flashes with a blinding white light! You feel weaker." + if(i>=75 && i<=85) //Stun and stutter + if(M && isliving(M)) + M.apply_effects(1,0,0,0,1) + message += "Your [P] flashes with a blinding white light! You feel weaker." + if(i>=85) //Sparks + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, P.loc) + s.start() + message += "Your [P] begins to spark violently!" + if(i>45 && i<65 && prob(50)) //Nothing happens + message += "Your [P] bleeps loudly." + j = prob(10) + + if(j && detonate) //This kills the PDA + qdel(P) + if(message) + message += "It melts in a puddle of plastic." + else + message += "Your [P] shatters in a thousand pieces!" + + if(M && isliving(M)) + message = "[message]" + M.show_message(message, 1) + +/obj/item/device/pda/proc/remove_id() + if (id) + if (ismob(loc)) + var/mob/M = loc + M.put_in_hands(id) + to_chat(usr, "You remove the ID from the [name].") + playsound(src, 'sound/machines/id_swipe.ogg', 100, 1) + else + id.loc = get_turf(src) + id = null + +/obj/item/device/pda/proc/remove_pen() + var/obj/item/weapon/pen/O = locate() in src + if(O) + if(istype(loc, /mob)) + var/mob/M = loc + if(M.get_active_hand() == null) + M.put_in_hands(O) + to_chat(usr, "You remove \the [O] from \the [src].") + return + O.loc = get_turf(src) + else + to_chat(usr, "This PDA does not have a pen in it.") + +/obj/item/device/pda/verb/verb_reset_pda() + set category = "Object" + set name = "Reset PDA" + set src in usr + + if(issilicon(usr)) + return + + if(can_use(usr)) + start_program(find_program(/datum/data/pda/app/main_menu)) + notifying_programs.Cut() + overlays -= image('icons/obj/pda.dmi', "pda-r") + to_chat(usr, "You press the reset button on \the [src].") + else + to_chat(usr, "You cannot do this while restrained.") + +/obj/item/device/pda/verb/verb_remove_id() + set category = "Object" + set name = "Remove id" + set src in usr + + if(issilicon(usr)) + return + + if ( can_use(usr) ) + if(id) + remove_id() + else + to_chat(usr, "This PDA does not have an ID in it.") + else + to_chat(usr, "You cannot do this while restrained.") + + +/obj/item/device/pda/verb/verb_remove_pen() + set category = "Object" + set name = "Remove pen" + set src in usr + + if(issilicon(usr)) + return + + if ( can_use(usr) ) + remove_pen() + else + to_chat(usr, "You cannot do this while restrained.") + +/obj/item/device/pda/verb/verb_remove_cartridge() + set category = "Object" + set name = "Remove cartridge" + set src in usr + + if(issilicon(usr)) + return + + if(!can_use(usr)) + to_chat(usr, "You cannot do this while restrained.") + return + + if(isnull(cartridge)) + to_chat(usr, "There's no cartridge to eject.") + return + + cartridge.forceMove(get_turf(src)) + if(ismob(loc)) + var/mob/M = loc + M.put_in_hands(cartridge) + // mode = 0 + // scanmode = 0 + if (cartridge.radio) + cartridge.radio.hostpda = null + to_chat(usr, "You remove \the [cartridge] from the [name].") + playsound(src, 'sound/machines/id_swipe.ogg', 100, 1) + cartridge = null + update_programs() + update_shortcuts() + start_program(find_program(/datum/data/pda/app/main_menu)) + + +/obj/item/device/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use. + if(choice == 1) + if (id) + remove_id() + return 1 + else + var/obj/item/I = user.get_active_hand() + if (istype(I, /obj/item/weapon/card/id) && user.unEquip(I)) + I.loc = src + id = I + return 1 + else + var/obj/item/weapon/card/I = user.get_active_hand() + if (istype(I, /obj/item/weapon/card/id) && I:registered_name && user.unEquip(I)) + var/obj/old_id = id + I.loc = src + id = I + user.put_in_hands(old_id) + return 1 + return 0 + +// access to status display signals +/obj/item/device/pda/attackby(obj/item/C as obj, mob/user as mob) + ..() + if(istype(C, /obj/item/weapon/cartridge) && !cartridge) + cartridge = C + user.drop_item() + cartridge.loc = src + cartridge.update_programs(src) + update_shortcuts() + to_chat(usr, "You insert [cartridge] into [src].") + if(cartridge.radio) + cartridge.radio.hostpda = src + + else if(istype(C, /obj/item/weapon/card/id)) + var/obj/item/weapon/card/id/idcard = C + if(!idcard.registered_name) + to_chat(user, "\The [src] rejects the ID.") + return + if(!owner) + owner = idcard.registered_name + ownjob = idcard.assignment + ownrank = idcard.rank + name = "PDA-[owner] ([ownjob])" + to_chat(user, "Card scanned.") + else + //Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand. + if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) ) + if(id_check(user, 2)) + to_chat(user, "You put the ID into \the [src]'s slot.") + updateSelfDialog()//Update self dialog on success. + return //Return in case of failed check or when successful. + updateSelfDialog()//For the non-input related code. + else if(istype(C, /obj/item/device/paicard) && !src.pai) + user.drop_item() + C.loc = src + pai = C + to_chat(user, "You slot \the [C] into \the [src].") + SStgui.update_uis(src) // update all UIs attached to src + else if(istype(C, /obj/item/weapon/pen)) + var/obj/item/weapon/pen/O = locate() in src + if(O) + to_chat(user, "There is already a pen in \the [src].") + else + user.drop_item() + C.loc = src + to_chat(user, "You slot \the [C] into \the [src].") + return + +/obj/item/device/pda/attack(mob/living/C as mob, mob/living/user as mob) + if (istype(C, /mob/living/carbon) && scanmode) + scanmode.scan_mob(C, user) + +/obj/item/device/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity) + if(proximity && scanmode) + scanmode.scan_atom(A, user) + +/obj/item/device/pda/proc/explode() //This needs tuning. //Sure did. + if(!src.detonate) return + var/turf/T = get_turf(src.loc) + if(T) + T.hotspot_expose(700,125) + explosion(T, 0, 0, 1, rand(1,2)) + return + +/obj/item/device/pda/Destroy() + PDAs -= src + if (src.id && prob(100) && !delete_id) //IDs are kept in 90% of the cases //VOREStation Edit - 100% of the cases, excpet when specified otherwise + src.id.forceMove(get_turf(src.loc)) + else + QDEL_NULL(src.id) + + current_app = null + scanmode = null + QDEL_NULL(pai) + QDEL_LIST(programs) + QDEL_NULL(cartridge) + return ..() + +//Some spare PDAs in a box +/obj/item/weapon/storage/box/PDAs + name = "box of spare PDAs" + desc = "A box of spare PDA microcomputers." + icon = 'icons/obj/pda.dmi' + icon_state = "pdabox" + +/obj/item/weapon/storage/box/PDAs/New() + ..() + new /obj/item/device/pda(src) + new /obj/item/device/pda(src) + new /obj/item/device/pda(src) + new /obj/item/device/pda(src) + new /obj/item/weapon/cartridge/head(src) + + var/newcart = pick( /obj/item/weapon/cartridge/engineering, + /obj/item/weapon/cartridge/security, + /obj/item/weapon/cartridge/medical, + /obj/item/weapon/cartridge/signal/science, + /obj/item/weapon/cartridge/quartermaster) + new newcart(src) + +// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP +/obj/item/device/pda/emp_act(severity) + for(var/atom/A in src) + A.emp_act(severity) diff --git a/code/modules/pda/pda_subtypes.dm b/code/modules/pda/pda_subtypes.dm new file mode 100644 index 0000000000..11b684b678 --- /dev/null +++ b/code/modules/pda/pda_subtypes.dm @@ -0,0 +1,246 @@ + +/obj/item/device/pda/medical + default_cartridge = /obj/item/weapon/cartridge/medical + icon_state = "pda-m" + +/obj/item/device/pda/viro + default_cartridge = /obj/item/weapon/cartridge/medical + icon_state = "pda-v" + +/obj/item/device/pda/engineering + default_cartridge = /obj/item/weapon/cartridge/engineering + icon_state = "pda-e" + +/obj/item/device/pda/security + default_cartridge = /obj/item/weapon/cartridge/security + icon_state = "pda-s" + +/obj/item/device/pda/detective + default_cartridge = /obj/item/weapon/cartridge/detective + icon_state = "pda-det" + +/obj/item/device/pda/warden + default_cartridge = /obj/item/weapon/cartridge/security + icon_state = "pda-warden" + +/obj/item/device/pda/janitor + default_cartridge = /obj/item/weapon/cartridge/janitor + icon_state = "pda-j" + ttone = "slip" + +/obj/item/device/pda/science + default_cartridge = /obj/item/weapon/cartridge/signal/science + icon_state = "pda-tox" + ttone = "boom" + +/obj/item/device/pda/clown + default_cartridge = /obj/item/weapon/cartridge/clown + icon_state = "pda-clown" + desc = "A portable microcomputer by Thinktronic Systems, LTD. The surface is coated with polytetrafluoroethylene and banana drippings." + ttone = "honk" + +/obj/item/device/pda/mime + default_cartridge = /obj/item/weapon/cartridge/mime + icon_state = "pda-mime" + +/obj/item/device/pda/mime/New() + . = ..() + var/datum/data/pda/app/M = find_program(/datum/data/pda/app/messenger) + if(M) + M.notify_silent = TRUE + +/obj/item/device/pda/heads + default_cartridge = /obj/item/weapon/cartridge/head + icon_state = "pda-h" + +/obj/item/device/pda/heads/hop + default_cartridge = /obj/item/weapon/cartridge/hop + icon_state = "pda-hop" + +/obj/item/device/pda/heads/hos + default_cartridge = /obj/item/weapon/cartridge/hos + icon_state = "pda-hos" + +/obj/item/device/pda/heads/ce + default_cartridge = /obj/item/weapon/cartridge/ce + icon_state = "pda-ce" + +/obj/item/device/pda/heads/cmo + default_cartridge = /obj/item/weapon/cartridge/cmo + icon_state = "pda-cmo" + +/obj/item/device/pda/heads/rd + default_cartridge = /obj/item/weapon/cartridge/rd + icon_state = "pda-rd" + +/obj/item/device/pda/captain + default_cartridge = /obj/item/weapon/cartridge/captain + icon_state = "pda-c" + detonate = 0 + //toff = 1 + +/obj/item/device/pda/ert + default_cartridge = /obj/item/weapon/cartridge/captain + icon_state = "pda-h" + detonate = 0 +// hidden = 1 + +/obj/item/device/pda/cargo + default_cartridge = /obj/item/weapon/cartridge/quartermaster + icon_state = "pda-cargo" + +/obj/item/device/pda/quartermaster + default_cartridge = /obj/item/weapon/cartridge/quartermaster + icon_state = "pda-q" + +/obj/item/device/pda/shaftminer + icon_state = "pda-miner" + default_cartridge = /obj/item/weapon/cartridge/miner + +/obj/item/device/pda/syndicate + default_cartridge = /obj/item/weapon/cartridge/syndicate + icon_state = "pda-syn" +// name = "Military PDA" // Vorestation Edit +// owner = "John Doe" + hidden = 1 + +/obj/item/device/pda/chaplain + default_cartridge = /obj/item/weapon/cartridge/service + icon_state = "pda-holy" + ttone = "holy" + +/obj/item/device/pda/lawyer + default_cartridge = /obj/item/weapon/cartridge/lawyer + icon_state = "pda-lawyer" + ttone = "..." + +/obj/item/device/pda/botanist + default_cartridge = /obj/item/weapon/cartridge/service + icon_state = "pda-hydro" + +/obj/item/device/pda/roboticist + default_cartridge = /obj/item/weapon/cartridge/signal/science + icon_state = "pda-robot" + +/obj/item/device/pda/librarian + default_cartridge = /obj/item/weapon/cartridge/service + icon_state = "pda-libb" + desc = "A portable microcomputer by Thinktronic Systems, LTD. This is model is a WGW-11 series e-reader." + model_name = "Thinktronic 5290 WGW-11 Series E-reader and Personal Data Assistant" + +/obj/item/device/pda/librarian/New() + . = ..() + var/datum/data/pda/app/M = find_program(/datum/data/pda/app/messenger) + if(M) + M.notify_silent = TRUE //Quiet in the library! + +/obj/item/device/pda/clear + icon_state = "pda-transp" + desc = "A portable microcomputer by Thinktronic Systems, LTD. This is model is a special edition with a transparent case." + model_name = "Thinktronic 5230 Personal Data Assistant Deluxe Special Max Turbo Limited Edition" + +/obj/item/device/pda/chef + default_cartridge = /obj/item/weapon/cartridge/service + icon_state = "pda-chef" + +/obj/item/device/pda/bar + default_cartridge = /obj/item/weapon/cartridge/service + icon_state = "pda-bar" + +/obj/item/device/pda/atmos + default_cartridge = /obj/item/weapon/cartridge/atmos + icon_state = "pda-atmo" + +/obj/item/device/pda/chemist + default_cartridge = /obj/item/weapon/cartridge/chemistry + icon_state = "pda-chem" + +/obj/item/device/pda/geneticist + default_cartridge = /obj/item/weapon/cartridge/medical + icon_state = "pda-gene" + + +// Used for the PDA multicaster, which mirrors messages sent to it to a specific department, +/obj/item/device/pda/multicaster + ownjob = "Relay" + icon_state = "NONE" + detonate = 0 + spam_proof = TRUE // Spam messages don't actually work and its difficult to disable these. + programs = list( + new/datum/data/pda/app/messenger/multicast + ) + var/list/cartridges_to_send_to = list() + +/obj/item/device/pda/multicaster/command/New() + ..() + owner = "Command Department" + name = "Command Department (Relay)" + cartridges_to_send_to = command_cartridges + +/obj/item/device/pda/multicaster/security/New() + ..() + owner = "Security Department" + name = "Security Department (Relay)" + cartridges_to_send_to = security_cartridges + +/obj/item/device/pda/multicaster/engineering/New() + ..() + owner = "Engineering Department" + name = "Engineering Department (Relay)" + cartridges_to_send_to = engineering_cartridges + +/obj/item/device/pda/multicaster/medical/New() + ..() + owner = "Medical Department" + name = "Medical Department (Relay)" + cartridges_to_send_to = medical_cartridges + +/obj/item/device/pda/multicaster/research/New() + ..() + owner = "Research Department" + name = "Research Department (Relay)" + cartridges_to_send_to = research_cartridges + +/obj/item/device/pda/multicaster/cargo/New() + ..() + owner = "Cargo Department" + name = "Cargo Department (Relay)" + cartridges_to_send_to = cargo_cartridges + +/obj/item/device/pda/multicaster/civilian/New() + ..() + owner = "Civilian Services Department" + name = "Civilian Services Department (Relay)" + cartridges_to_send_to = civilian_cartridges + +/obj/item/device/pda/clown/Crossed(atom/movable/AM as mob|obj) //Clown PDA is slippery. + if(AM.is_incorporeal()) + return + if (istype(AM, /mob/living)) + var/mob/living/M = AM + + if(M.slip("the PDA",8) && M.real_name != src.owner && istype(src.cartridge, /obj/item/weapon/cartridge/clown)) + if(src.cartridge.charges < 5) + src.cartridge.charges++ + +//Some spare PDAs in a box +/obj/item/weapon/storage/box/PDAs + name = "box of spare PDAs" + desc = "A box of spare PDA microcomputers." + icon = 'icons/obj/pda.dmi' + icon_state = "pdabox" + +/obj/item/weapon/storage/box/PDAs/New() + ..() + new /obj/item/device/pda(src) + new /obj/item/device/pda(src) + new /obj/item/device/pda(src) + new /obj/item/device/pda(src) + new /obj/item/weapon/cartridge/head(src) + + var/newcart = pick( /obj/item/weapon/cartridge/engineering, + /obj/item/weapon/cartridge/security, + /obj/item/weapon/cartridge/medical, + /obj/item/weapon/cartridge/signal/science, + /obj/item/weapon/cartridge/quartermaster) + new newcart(src) diff --git a/code/modules/pda/pda_tgui.dm b/code/modules/pda/pda_tgui.dm new file mode 100644 index 0000000000..0d70b6db02 --- /dev/null +++ b/code/modules/pda/pda_tgui.dm @@ -0,0 +1,124 @@ +// Self contained file for all things TGUI +/obj/item/device/pda/tgui_state(mob/user) + return GLOB.tgui_inventory_state + +/obj/item/device/pda/tgui_status(mob/user, datum/tgui_state/state) + . = ..() + if(!can_use()) + . = min(., STATUS_UPDATE) + +/obj/item/device/pda/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Pda", "Personal Data Assistant", parent_ui) + ui.open() + +/obj/item/device/pda/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["owner"] = owner // Who is your daddy... + data["ownjob"] = ownjob // ...and what does he do? + + // update list of shortcuts, only if they changed + if(!shortcut_cache.len) + shortcut_cache = list() + shortcut_cat_order = list() + var/prog_list = programs.Copy() + if(cartridge) + prog_list |= cartridge.programs + + for(var/A in prog_list) + var/datum/data/pda/P = A + + if(P.hidden) + continue + var/list/cat + if(P.category in shortcut_cache) + cat = shortcut_cache[P.category] + else + cat = list() + shortcut_cache[P.category] = cat + shortcut_cat_order += P.category + cat |= list(list(name = P.name, icon = P.icon, notify_icon = P.notify_icon, ref = "\ref[P]")) + + // force the order of a few core categories + shortcut_cat_order = list("General") \ + + sortList(shortcut_cat_order - list("General", "Scanners", "Utilities")) \ + + list("Scanners", "Utilities") + + data["idInserted"] = (id ? 1 : 0) + data["idLink"] = (id ? text("[id.registered_name], [id.assignment]") : "--------") + + data["useRetro"] = retro_mode + + data["cartridge_name"] = cartridge ? cartridge.name : "" + data["stationTime"] = stationtime2text() //worldtime2stationtime(world.time) // Aaa which fucking one is canonical there's SO MANY + + data["app"] = list( + "name" = current_app.title, + "icon" = current_app.icon, + "template" = current_app.template, + "has_back" = current_app.has_back) + + current_app.update_ui(user, data) + + return data + +/obj/item/device/pda/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + if(!can_use()) + usr.unset_machine() + if(ui) + ui.close() + return FALSE + + add_fingerprint(usr) + usr.set_machine(src) + + if(!touch_silent) + playsound(src, 'sound/machines/pda_click.ogg', 20) + + . = TRUE + switch(action) + if("Home") //Go home, largely replaces the old Return + var/datum/data/pda/app/main_menu/A = find_program(/datum/data/pda/app/main_menu) + if(A) + start_program(A) + if("StartProgram") + if(params["program"]) + var/datum/data/pda/app/A = locate(params["program"]) + if(A) + start_program(A) + if("Eject")//Ejects the cart, only done from hub. + if(!isnull(cartridge)) + var/turf/T = loc + if(ismob(T)) + T = T.loc + var/obj/item/weapon/cartridge/C = cartridge + C.forceMove(T) + if(scanmode in C.programs) + scanmode = null + if(current_app in C.programs) + start_program(find_program(/datum/data/pda/app/main_menu)) + if(C.radio) + C.radio.hostpda = null + for(var/datum/data/pda/P in notifying_programs) + if(P in C.programs) + P.unnotify() + cartridge = null + update_shortcuts() + if("Authenticate")//Checks for ID + id_check(usr, 1) + if("Retro") + retro_mode = !retro_mode + if("Ringtone") + return set_ringtone() + else + if(current_app) + . = current_app.tgui_act(action, params, ui, state) + + if((honkamt > 0) && (prob(60)))//For clown virus. + honkamt-- + playsound(loc, 'sound/items/bikehorn.ogg', 30, 1) diff --git a/code/game/objects/items/devices/PDA/PDA_vr.dm b/code/modules/pda/pda_vr.dm similarity index 100% rename from code/game/objects/items/devices/PDA/PDA_vr.dm rename to code/modules/pda/pda_vr.dm diff --git a/code/modules/pda/radio.dm b/code/modules/pda/radio.dm new file mode 100644 index 0000000000..0d3b115a13 --- /dev/null +++ b/code/modules/pda/radio.dm @@ -0,0 +1,138 @@ +/obj/item/radio/integrated + name = "\improper PDA radio module" + desc = "An electronic radio system." + icon = 'icons/obj/module.dmi' + icon_state = "power_mod" + var/obj/item/device/pda/hostpda = null + + var/list/botlist = null // list of bots + var/mob/living/bot/active // the active bot; if null, show bot list + var/list/botstatus // the status signal sent by the bot + + var/bot_type //The type of bot it is. + var/bot_filter //Determines which radio filter to use. + + var/control_freq = BOT_FREQ + + var/on = 0 //Are we currently active?? + var/menu_message = "" + +/obj/item/radio/integrated/New() + ..() + if(istype(loc.loc, /obj/item/device/pda)) + hostpda = loc.loc + if(bot_filter) + spawn(5) + add_to_radio(bot_filter) + +/obj/item/radio/integrated/Destroy() + if(radio_controller) + radio_controller.remove_object(src, control_freq) + hostpda = null + return ..() + +/obj/item/radio/integrated/proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3, s_filter) + + //to_world("Post: [freq]: [key]=[value], [key2]=[value2]") + var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) + + if(!frequency) + return + + var/datum/signal/signal = new() + signal.source = src + signal.transmission_method = TRANSMISSION_RADIO + signal.data[key] = value + if(key2) + signal.data[key2] = value2 + if(key3) + signal.data[key3] = value3 + + frequency.post_signal(src, signal, radio_filter = s_filter) + +/obj/item/radio/integrated/Topic(href, href_list) + ..() + switch(href_list["op"]) + if("control") + active = locate(href_list["bot"]) + spawn(0) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + + if("scanbots") // find all bots + botlist = null + spawn(0) + post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) + + if("botlist") + active = null + + if("stop", "go", "home") + spawn(0) + post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + + if("summon") + spawn(0) + post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(hostpda), "useraccess", hostpda.GetAccess(), "user", usr, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) + +/obj/item/radio/integrated/receive_signal(datum/signal/signal) + if(bot_type && istype(signal.source, /mob/living/bot) && signal.data["type"] == bot_type) + if(!botlist) + botlist = new() + + botlist |= signal.source + + if(active == signal.source) + var/list/b = signal.data + botstatus = b.Copy() + +/obj/item/radio/integrated/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots. + if(radio_controller) + radio_controller.add_object(src, control_freq, radio_filter = bot_filter) + +/* + * Radio Cartridge, essentially a signaler. + */ +/obj/item/radio/integrated/signal + var/frequency = 1457 + var/code = 30.0 + var/last_transmission + var/datum/radio_frequency/radio_connection + +/obj/item/radio/integrated/signal/Destroy() + if(radio_controller) + radio_controller.remove_object(src, frequency) + radio_connection = null + return ..() + +/obj/item/radio/integrated/signal/Initialize() + if(!radio_controller) + return + + if(src.frequency < PUBLIC_LOW_FREQ || src.frequency > PUBLIC_HIGH_FREQ) + src.frequency = sanitize_frequency(src.frequency) + + set_frequency(frequency) + +/obj/item/radio/integrated/signal/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency) + +/obj/item/radio/integrated/signal/proc/send_signal(message="ACTIVATE") + if(last_transmission && world.time < (last_transmission + 5)) + return + last_transmission = world.time + + var/time = time2text(world.realtime,"hh:mm:ss") + var/turf/T = get_turf(src) + lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = message + + spawn(0) + radio_connection.post_signal(src, signal) diff --git a/code/modules/pda/utilities.dm b/code/modules/pda/utilities.dm new file mode 100644 index 0000000000..be8bb8f444 --- /dev/null +++ b/code/modules/pda/utilities.dm @@ -0,0 +1,184 @@ +/datum/data/pda/utility/flashlight + name = "Enable Flashlight" + icon = "lightbulb-o" + + var/fon = 0 //Is the flashlight function on? + var/f_lum = 2 //Luminosity for the flashlight function + +/datum/data/pda/utility/flashlight/start() + fon = !fon + name = fon ? "Disable Flashlight" : "Enable Flashlight" + pda.update_shortcuts() + pda.set_light(fon ? f_lum : 0) + +/datum/data/pda/utility/honk + name = "Honk Synthesizer" + icon = "smile-o" + category = "Clown" + + var/last_honk //Also no honk spamming that's bad too + +/datum/data/pda/utility/honk/start() + if(!(last_honk && world.time < last_honk + 20)) + playsound(pda.loc, 'sound/items/bikehorn.ogg', 50, 1) + last_honk = world.time + +/datum/data/pda/utility/toggle_door + name = "Toggle Door" + icon = "external-link" + var/remote_door_id = "" + +// /datum/data/pda/utility/toggle_door/start() +// for(var/obj/machinery/door/poddoor/M in airlocks) +// if(M.id_tag == remote_door_id) +// if(M.density) +// M.open() +// else +// M.close() + +/datum/data/pda/utility/scanmode/medical + base_name = "Med Scanner" + icon = "heart-o" + +/datum/data/pda/utility/scanmode/medical/scan_mob(mob/living/C as mob, mob/living/user as mob) + C.visible_message("[user] has analyzed [C]'s vitals!") + + user.show_message("Analyzing Results for [C]:") + user.show_message(" Overall Status: [C.stat > 1 ? "dead" : "[C.health - C.halloss]% healthy"]", 1) + user.show_message(text(" Damage Specifics: []-[]-[]-[]", + (C.getOxyLoss() > 50) ? "warning" : "", C.getOxyLoss(), + (C.getToxLoss() > 50) ? "warning" : "", C.getToxLoss(), + (C.getFireLoss() > 50) ? "warning" : "", C.getFireLoss(), + (C.getBruteLoss() > 50) ? "warning" : "", C.getBruteLoss() + ), 1) + user.show_message(" Key: Suffocation/Toxin/Burns/Brute", 1) + user.show_message(" Body Temperature: [C.bodytemperature-T0C]°C ([C.bodytemperature*1.8-459.67]°F)", 1) + if(C.tod && (C.stat == DEAD || (C.status_flags & FAKEDEATH))) + user.show_message(" Time of Death: [C.tod]") + if(istype(C, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = C + var/list/damaged = H.get_damaged_organs(1,1) + user.show_message("Localized Damage, Brute/Burn:",1) + if(length(damaged)>0) + for(var/obj/item/organ/external/org in damaged) + user.show_message(text(" []: []-[]", + capitalize(org.name), (org.brute_dam > 0) ? "warning" : "notice", org.brute_dam, (org.burn_dam > 0) ? "warning" : "notice", org.burn_dam),1) + else + user.show_message(" Limbs are OK.",1) + +/datum/data/pda/utility/scanmode/dna + base_name = "DNA Scanner" + icon = "link" + +/datum/data/pda/utility/scanmode/dna/scan_mob(mob/living/C as mob, mob/living/user as mob) + if(istype(C, /mob/living/carbon/human)) + var/mob/living/carbon/human/H = C + if(!istype(H.dna, /datum/dna)) + to_chat(user, "No fingerprints found on [H]") + else + to_chat(user, "[H]'s Fingerprints: [md5(H.dna.uni_identity)]") + scan_blood(C, user) + +/datum/data/pda/utility/scanmode/dna/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) + scan_blood(A, user) + +/datum/data/pda/utility/scanmode/dna/proc/scan_blood(atom/A, mob/user) + if(!A.blood_DNA) + to_chat(user, "No blood found on [A]") + if(A.blood_DNA) + qdel(A.blood_DNA) + else + to_chat(user, "Blood found on [A]. Analysing...") + spawn(15) + for(var/blood in A.blood_DNA) + to_chat(user, "Blood type: [A.blood_DNA[blood]]\nDNA: [blood]") + +/datum/data/pda/utility/scanmode/halogen + base_name = "Halogen Counter" + icon = "exclamation-circle" + +/datum/data/pda/utility/scanmode/halogen/scan_mob(mob/living/C as mob, mob/living/user as mob) + C.visible_message("[user] has analyzed [C]'s radiation levels!") + + user.show_message("Analyzing Results for [C]:") + if(C.radiation) + user.show_message("Radiation Level: [C.radiation > 0 ? "[C.radiation]" : "0"]") + else + user.show_message("No radiation detected.") + +/datum/data/pda/utility/scanmode/reagent + base_name = "Reagent Scanner" + icon = "flask" + +/datum/data/pda/utility/scanmode/reagent/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) + if(!isnull(A.reagents)) + if(A.reagents.reagent_list.len > 0) + var/reagents_length = A.reagents.reagent_list.len + to_chat(user, "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found.") + for(var/re in A.reagents.reagent_list) + to_chat(user, "\t [re]") + else + to_chat(user, "No active chemical agents found in [A].") + else + to_chat(user, "No significant chemical agents found in [A].") + +/datum/data/pda/utility/scanmode/gas + base_name = "Gas Scanner" + icon = "tachometer-alt" + +/datum/data/pda/utility/scanmode/gas/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) + pda.analyze_gases(A, user) + +/datum/data/pda/utility/scanmode/notes + base_name = "Note Scanner" + icon = "clipboard" + var/datum/data/pda/app/notekeeper/notes + +/datum/data/pda/utility/scanmode/notes/start() + . = ..() + notes = pda.find_program(/datum/data/pda/app/notekeeper) + +/datum/data/pda/utility/scanmode/notes/scan_atom(atom/A as mob|obj|turf|area, mob/user as mob) + if(notes && istype(A, /obj/item/weapon/paper)) + var/obj/item/weapon/paper/P = A + var/list/brlist = list("p", "/p", "br", "hr", "h1", "h2", "h3", "h4", "/h1", "/h2", "/h3", "/h4") + + // JMO 20140705: Makes scanned document show up properly in the notes. Not pretty for formatted documents, + // as this will clobber the HTML, but at least it lets you scan a document. You can restore the original + // notes by editing the note again. (Was going to allow you to edit, but scanned documents are too long.) + var/raw_scan = sanitize_simple(P.info, list("\t" = "", "ÿ" = "")) + var/formatted_scan = "" + // Scrub out the tags (replacing a few formatting ones along the way) + // Find the beginning and end of the first tag. + var/tag_start = findtext(raw_scan, "<") + var/tag_stop = findtext(raw_scan, ">") + // Until we run out of complete tags... + while(tag_start && tag_stop) + var/pre = copytext(raw_scan, 1, tag_start) // Get the stuff that comes before the tag + var/tag = lowertext(copytext(raw_scan, tag_start + 1, tag_stop)) // Get the tag so we can do intellegent replacement + var/tagend = findtext(tag, " ") // Find the first space in the tag if there is one. + // Anything that's before the tag can just be added as is. + formatted_scan = formatted_scan + pre + // If we have a space after the tag (and presumably attributes) just crop that off. + if(tagend) + tag = copytext(tag, 1, tagend) + if(tag in brlist) // Check if it's I vertical space tag. + formatted_scan = formatted_scan + "
" // If so, add some padding in. + raw_scan = copytext(raw_scan, tag_stop + 1) // continue on with the stuff after the tag + // Look for the next tag in what's left + tag_start = findtext(raw_scan, "<") + tag_stop = findtext(raw_scan, ">") + // Anything that is left in the page. just tack it on to the end as is + formatted_scan = formatted_scan + raw_scan + // If there is something in there already, pad it out. + if(length(notes.note) > 0) + notes.note += "

" + // Store the scanned document to the notes + notes.note += "Scanned Document. Edit to restore previous notes/delete scan.
----------
" + formatted_scan + "
" + // notehtml ISN'T set to allow user to get their old notes back. A better implementation would add a "scanned documents" + // feature to the PDA, which would better convey the availability of the feature, but this will work for now. + // Inform the user + to_chat(user, "Paper scanned and OCRed to notekeeper.")//concept of scanning paper copyright brainoblivion 2009 + + else + to_chat(user, "Error scanning [A].") diff --git a/code/modules/persistence/filth.dm b/code/modules/persistence/filth.dm index 2eea824f83..a7aa9e9cd4 100644 --- a/code/modules/persistence/filth.dm +++ b/code/modules/persistence/filth.dm @@ -6,6 +6,7 @@ random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") color = "#464f33" persistent = TRUE + anchored = 1 /obj/effect/decal/cleanable/filth/Initialize() . = ..() diff --git a/code/modules/persistence/graffiti.dm b/code/modules/persistence/graffiti.dm index 2780c3b9a4..539b7b7a3b 100644 --- a/code/modules/persistence/graffiti.dm +++ b/code/modules/persistence/graffiti.dm @@ -8,6 +8,7 @@ blend_mode = BLEND_MULTIPLY color = "#000000" alpha = 120 + anchored = 1 var/message var/graffiti_age = 0 diff --git a/code/modules/power/batteryrack.dm b/code/modules/power/batteryrack.dm index f6bc1c9ff6..dc1dbe7e58 100644 --- a/code/modules/power/batteryrack.dm +++ b/code/modules/power/batteryrack.dm @@ -25,6 +25,7 @@ var/equalise = 0 // If true try to equalise charge between cells var/icon_update = 0 // Timer in ticks for icon update. var/ui_tick = 0 + should_be_mapped = TRUE /obj/machinery/power/smes/batteryrack/New() @@ -113,6 +114,8 @@ if(equalise) // Now try to get least charged cell and use the power from it. var/obj/item/weapon/cell/CL = get_least_charged_cell() + if(!CL) + return amount -= CL.give(amount) if(!amount) return @@ -209,8 +212,43 @@ celldiff = min(min(celldiff, most.charge), least.maxcharge - least.charge) least.give(most.use(celldiff)) -/obj/machinery/power/smes/batteryrack/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] +/obj/machinery/power/smes/batteryrack/dismantle() + for(var/obj/item/weapon/cell/C in internal_cells) + C.forceMove(get_turf(src)) + internal_cells -= C + return ..() + +/obj/machinery/power/smes/batteryrack/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) + if(istype(W, /obj/item/weapon/cell)) // ID Card, try to insert it. + if(insert_cell(W, user)) + to_chat(user, "You insert \the [W] into \the [src].") + else + to_chat(user, "\The [src] has no empty slot for \the [W]") + if(!..()) + return 0 + if(default_deconstruction_crowbar(user, W)) + return + if(default_part_replacement(user, W)) + return + +/obj/machinery/power/smes/batteryrack/inputting() + return + +/obj/machinery/power/smes/batteryrack/outputting() + return + +/obj/machinery/power/smes/batteryrack/attack_hand(var/mob/user) + tgui_interact(user) + +/obj/machinery/power/smes/batteryrack/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Batteryrack", name) + ui.open() + +/obj/machinery/power/smes/batteryrack/tgui_data(mob/user) + // DO NOT CALL PARENT. + var/list/data = list() data["mode"] = mode data["transfer_max"] = max_transfer_rate @@ -238,75 +276,44 @@ cells += list(cell) data["cells_list"] = cells - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "psu.tmpl", "Cell Rack PSU", 500, 430) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data -/obj/machinery/power/smes/batteryrack/dismantle() - for(var/obj/item/weapon/cell/C in internal_cells) - C.forceMove(get_turf(src)) - internal_cells -= C - return ..() - -/obj/machinery/power/smes/batteryrack/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob) - if(!..()) - return 0 - if(default_deconstruction_crowbar(user, W)) - return - if(default_part_replacement(user, W)) - return - if(istype(W, /obj/item/weapon/cell)) // ID Card, try to insert it. - if(insert_cell(W, user)) - to_chat(user, "You insert \the [W] into \the [src].") - else - to_chat(user, "\The [src] has no empty slot for \the [W]") - -/obj/machinery/power/smes/batteryrack/attack_hand(var/mob/user) - ui_interact(user) - -/obj/machinery/power/smes/batteryrack/inputting() - return - -/obj/machinery/power/smes/batteryrack/outputting() - return - -/obj/machinery/power/smes/batteryrack/Topic(href, href_list) +/obj/machinery/power/smes/batteryrack/tgui_act(action, list/params) // ..() would respond to those topic calls, but we don't want to use them at all. // Calls to these shouldn't occur anyway, due to usage of different nanoUI, but // it's here in case someone decides to try hrefhacking/modified templates. - if(href_list["input"] || href_list["output"]) - return 1 + if(!(action in list("disable", "enable", "equaliseon", "equaliseoff", "ejectcell"))) + return TRUE if(..()) - return 1 - if( href_list["disable"] ) - update_io(0) - return 1 - else if( href_list["enable"] ) - update_io(between(1, text2num(href_list["enable"]), 3)) - return 1 - else if( href_list["equaliseon"] ) - equalise = 1 - return 1 - else if( href_list["equaliseoff"] ) - equalise = 0 - return 1 - else if( href_list["ejectcell"] ) - var/obj/item/weapon/cell/C - for(var/obj/item/weapon/cell/CL in internal_cells) - if(CL.c_uid == text2num(href_list["ejectcell"])) - C = CL - break + return TRUE - if(!istype(C)) - return 1 + switch(action) + if("disable") + update_io(0) + return TRUE + if("enable") + update_io(between(1, text2num(params["enable"]), 3)) + return TRUE + if("equaliseon") + equalise = 1 + return TRUE + if("equaliseoff") + equalise = 0 + return TRUE + if("ejectcell") + var/obj/item/weapon/cell/C + for(var/obj/item/weapon/cell/CL in internal_cells) + if(CL.c_uid == text2num(params["ejectcell"])) + C = CL + break - C.forceMove(get_turf(src)) - internal_cells -= C - update_icon() - RefreshParts() - update_maxcharge() - return 1 \ No newline at end of file + if(!istype(C)) + return TRUE + + C.forceMove(get_turf(src)) + internal_cells -= C + update_icon() + RefreshParts() + update_maxcharge() + return TRUE \ No newline at end of file diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm index ecfbf00a5f..d77f8341a6 100644 --- a/code/modules/power/cell.dm +++ b/code/modules/power/cell.dm @@ -25,6 +25,8 @@ var/last_use = 0 // A tracker for use in self-charging var/charge_delay = 0 // How long it takes for the cell to start recharging after last use matter = list(DEFAULT_WALL_MATERIAL = 700, "glass" = 50) + drop_sound = 'sound/items/drop/component.ogg' + pickup_sound = 'sound/items/pickup/component.ogg' // Overlay stuff. var/overlay_half_state = "cell-o1" // Overlay used when not fully charged but not empty. diff --git a/code/modules/power/grid_checker.dm b/code/modules/power/grid_checker.dm index 224eade429..53766098f0 100644 --- a/code/modules/power/grid_checker.dm +++ b/code/modules/power/grid_checker.dm @@ -59,7 +59,7 @@ if(opened) wires.Interact(user) - return ui_interact(user) + return tgui_interact(user) /obj/machinery/power/grid_checker/proc/power_failure(var/announce = TRUE) if(announce) diff --git a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm index 386437db0b..01fa9932d9 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_smasher.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_smasher.dm @@ -43,9 +43,12 @@ for(var/obj/item/I in contents) . += "\the [I]" +/obj/machinery/particle_smasher/atmosanalyze(var/mob/user) + return list("\The [src] reads an energy level of [energy].") + /obj/machinery/particle_smasher/attackby(obj/item/W as obj, mob/user as mob) if(W.type == /obj/item/device/analyzer) - to_chat(user, "\The [src] reads an energy level of [energy].") + return else if(istype(W, /obj/item/stack/material)) var/obj/item/stack/material/M = W if(M.uses_charge) diff --git a/code/modules/power/turbine.dm b/code/modules/power/turbine.dm index 2caa008f0c..1d5262044e 100644 --- a/code/modules/power/turbine.dm +++ b/code/modules/power/turbine.dm @@ -345,59 +345,59 @@ /obj/machinery/computer/turbine_computer/attack_hand(var/mob/user as mob) if((. = ..())) return - src.interact(user) + tgui_interact(user) -/obj/machinery/computer/turbine_computer/interact(mob/user) - return ui_interact(user) +/obj/machinery/computer/turbine_computer/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TurbineControl", name) + ui.open() -/obj/machinery/computer/turbine_computer/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) +/obj/machinery/computer/turbine_computer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) var/list/data = list() data["connected"] = (compressor && compressor.turbine) ? TRUE : FALSE data["compressor_broke"] = (!compressor || (compressor.stat & BROKEN)) ? TRUE : FALSE data["turbine_broke"] = (!compressor || !compressor.turbine || (compressor.turbine.stat & BROKEN)) ? TRUE : FALSE data["broken"] = (data["compressor_broke"] || data["turbine_broke"]) data["door_status"] = door_status ? TRUE : FALSE + + data["online"] = FALSE + data["power"] = 0 + data["rpm"] = 0 + data["temp"] = 0 + if(compressor && compressor.turbine) data["online"] = compressor.starter - data["power"] = DisplayPower(compressor.turbine.lastgen) + data["power"] = compressor.turbine.lastgen // DisplayPower data["rpm"] = compressor.rpm data["temp"] = compressor.gas_contained.temperature - // update the ui if it exists, returns null if no ui is passed/found - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - // the ui does not exist, so we'll create a new() one - // for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm - ui = new(user, src, ui_key, "turbine_computer.tmpl", name, 520, 440) - // when the ui is first opened this is the data it will use - ui.set_initial_data(data) - // open the new ui window - ui.open() - // auto update every Master Controller tick - ui.set_auto_update(TRUE) + return data -/obj/machinery/computer/turbine_computer/Topic(href, href_list) +/obj/machinery/computer/turbine_computer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) - return - if(href_list["power-on"]) - if(compressor && compressor.turbine) - compressor.starter = TRUE + return TRUE + + switch(action) + if("power-on") + if(compressor && compressor.turbine) + compressor.starter = TRUE + . = TRUE + if("power-off") + if(compressor && compressor.turbine) + compressor.starter = FALSE + . = TRUE + if("reconnect") + locate_machinery() . = TRUE - else if(href_list["power-off"]) - if(compressor && compressor.turbine) - compressor.starter = FALSE + if("doors") + door_status = !door_status + for(var/obj/machinery/door/blast/D in src.doors) + if (door_status) + spawn(0) D.close() + else + spawn(0)D.open() . = TRUE - else if(href_list["reconnect"]) - locate_machinery() - . = TRUE - else if(href_list["doors"]) - door_status = !door_status - for(var/obj/machinery/door/blast/D in src.doors) - if (door_status) - spawn(0) D.close() - else - spawn(0)D.open() - . = TRUE #undef COMPFRICTION #undef COMPSTARTERLOAD diff --git a/code/modules/projectiles/broken.dm b/code/modules/projectiles/broken.dm index 9dd3107d6e..a63f685ba1 100644 --- a/code/modules/projectiles/broken.dm +++ b/code/modules/projectiles/broken.dm @@ -32,8 +32,19 @@ if(do_after(user, 5 SECONDS)) to_chat(user, "\The [src] can possibly be restored with:") for(var/resource in material_needs) + var/obj/item/res = resource if(material_needs[resource] > 0) - to_chat(user, "- [bicon(resource)] x [material_needs[resource]] [resource]") + var/res_name = "" + if(ispath(res,/obj/item/stack/material)) + var/obj/item/stack/material/mat_stack = res + var/material/mat = get_material_by_name("[initial(mat_stack.default_type)]") + if(material_needs[resource]>1) + res_name = "[mat.use_name] [mat.sheet_plural_name]" + else + res_name = "[mat.use_name] [mat.sheet_singular_name]" + else + res_name = initial(res.name) + to_chat(user, "- x [material_needs[resource]] [res_name]") /obj/item/weapon/broken_gun/proc/setup_gun(var/obj/item/weapon/gun/path) if(ispath(path)) diff --git a/code/modules/projectiles/guns/projectile/boltaction.dm b/code/modules/projectiles/guns/projectile/boltaction.dm index 9e93d46b8a..1aa8e83957 100644 --- a/code/modules/projectiles/guns/projectile/boltaction.dm +++ b/code/modules/projectiles/guns/projectile/boltaction.dm @@ -14,6 +14,7 @@ ammo_type = /obj/item/ammo_casing/a762 load_method = SINGLE_CASING|SPEEDLOADER action_sound = 'sound/weapons/riflebolt.ogg' + pump_animation = null /obj/item/weapon/gun/projectile/shotgun/pump/rifle/practice // For target practice desc = "A bolt-action rifle with a lightweight synthetic wood stock, designed for competitive shooting. Comes shipped with practice rounds pre-loaded into the gun. Popular among professional marksmen. Uses 7.62mm rounds." @@ -66,4 +67,4 @@ desc = "An iconic manually operated lever action rifle, offering adequate stopping power due to it's still powerful cartridge while at the same time having a rather respectable firing rate due to it's mechanism. It is very probable this is a replica instead of a museum piece, but rifles of this pattern still see usage as colonist guns in some far off regions. Uses 7.62mm rounds." item_state = "levercarabine" // That isn't how carbine is spelled ya knob! :U icon_state = "levercarabine" - animated_pump = 1 + pump_animation = "levercarabine-cycling" diff --git a/code/modules/projectiles/guns/projectile/shotgun.dm b/code/modules/projectiles/guns/projectile/shotgun.dm index 799c27ac54..6e69b65a6a 100644 --- a/code/modules/projectiles/guns/projectile/shotgun.dm +++ b/code/modules/projectiles/guns/projectile/shotgun.dm @@ -14,10 +14,10 @@ ammo_type = /obj/item/ammo_casing/a12g/beanbag projectile_type = /obj/item/projectile/bullet/shotgun handle_casings = HOLD_CASINGS - var/recentpump = 0 // to prevent spammage + var/recentpump = 0 //To prevent spammage var/action_sound = 'sound/weapons/shotgunpump.ogg' - var/animated_pump = 0 //This is for cyling animations. - var/empty_sprite = 0 //This is just a dirty var so it doesn't fudge up. + var/empty_sprite = 0 //This is just a dirty var so it doesn't fudge up. + var/pump_animation = "shotgun-pump" //You put the reference to the animation in question here. Frees up namming. Ex: "shotgun_old_pump" or "sniper_cycle" /obj/item/weapon/gun/projectile/shotgun/pump/consume_next_projectile() if(chambered) @@ -41,8 +41,8 @@ loaded -= AC //Remove casing from loaded list. chambered = AC - if(animated_pump)//This affects all bolt action and shotguns. - flick("[icon_state]-cycling", src)//This plays any pumping + if(pump_animation)//This affects all bolt action and shotguns. + flick("[pump_animation]", src)//This plays any pumping update_icon() @@ -60,6 +60,7 @@ /obj/item/weapon/gun/projectile/shotgun/pump/slug ammo_type = /obj/item/ammo_casing/a12g + pump_animation = null /obj/item/weapon/gun/projectile/shotgun/pump/combat name = "combat shotgun" @@ -71,6 +72,7 @@ max_shells = 7 //match the ammo box capacity, also it can hold a round in the chamber anyways, for a total of 8. ammo_type = /obj/item/ammo_casing/a12g load_method = SINGLE_CASING|SPEEDLOADER + pump_animation = "cshotgun-pump" /obj/item/weapon/gun/projectile/shotgun/pump/combat/empty ammo_type = null diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index 8598d54115..70bacce63f 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -115,7 +115,7 @@ for(var/datum/reagent/current in reagent_list) if(current.id == id) if(current.id == "blood") - if(!isnull(data["species"]) && !isnull(current.data["species"]) && data["species"] != current.data["species"]) // Species bloodtypes are already incompatible, this just stops it from mixing into the one already in a container. + if(LAZYLEN(data) && !isnull(data["species"]) && !isnull(current.data["species"]) && data["species"] != current.data["species"]) // Species bloodtypes are already incompatible, this just stops it from mixing into the one already in a container. continue current.volume += amount diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm index b15c84e9a9..8dea8f6d14 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Food-Drinks.dm @@ -18,11 +18,14 @@ return //add the new taste data - for(var/taste in newdata) - if(taste in data) - data[taste] += newdata[taste] - else - data[taste] = newdata[taste] + if(islist(data)) + for(var/taste in newdata) + if(taste in data) + data[taste] += newdata[taste] + else + data[taste] = newdata[taste] + else + initialize_data(newdata) //cull all tastes below 10% of total var/totalFlavor = 0 @@ -136,6 +139,8 @@ description = "More commonly known as fat, the third macronutrient, with over double the energy content of carbs and protein" reagent_state = SOLID + taste_description = "greasiness" + taste_mult = 0.1 nutriment_factor = 27//The caloric ratio of carb/protein/fat is 4:4:9 color = "#CCCCCC" @@ -145,6 +150,7 @@ id = "oil" description = "Oils are liquid fats." reagent_state = LIQUID + taste_description = "oil" color = "#c79705" touch_met = 1.5 var/lastburnmessage = 0 @@ -221,8 +227,6 @@ name = "Corn Oil" id = "cornoil" description = "An oil derived from various types of corn." - taste_description = "oil" - taste_mult = 0.1 reagent_state = LIQUID /datum/reagent/nutriment/triglyceride/oil/peanut @@ -588,7 +592,7 @@ //SYNNONO MEME FOODS EXPANSION - Credit to Synnono /datum/reagent/spacespice - name = "Space Spice" + name = "Wurmwoad" id = "spacespice" description = "An exotic blend of spices for cooking. Definitely not worms." reagent_state = SOLID @@ -676,7 +680,7 @@ var/mob/living/carbon/human/H = M if(!H.can_feel_pain()) return - + var/effective_dose = (dose * M.species.spice_mod) if((effective_dose < 5) && (dose == metabolism || prob(5))) to_chat(M, "Your insides feel uncomfortably hot!") diff --git a/code/modules/reagents/Chemistry-Recipes.dm b/code/modules/reagents/Chemistry-Recipes.dm index 9adb2558b9..cfac2582e7 100644 --- a/code/modules/reagents/Chemistry-Recipes.dm +++ b/code/modules/reagents/Chemistry-Recipes.dm @@ -2674,4 +2674,4 @@ id = "browniemix" result = "browniemix" required_reagents = list("flour" = 5, "coco" = 5, "sugar" = 5) - result_amount = 15 \ No newline at end of file + result_amount = 15 diff --git a/code/modules/reagents/Chemistry-Recipes_vr.dm b/code/modules/reagents/Chemistry-Recipes_vr.dm index 9a74ec4665..496520f513 100644 --- a/code/modules/reagents/Chemistry-Recipes_vr.dm +++ b/code/modules/reagents/Chemistry-Recipes_vr.dm @@ -77,6 +77,14 @@ /datum/chemical_reaction/foam/softdrink required_reagents = list("cola" = 1, "mint" = 1) + +/datum/chemical_reaction/firefightingfoam //TODO: Make it so we can add this to the foam tanks to refill them + name = "Firefighting Foam" + id = "firefighting foam" + result = "firefoam" + required_reagents = list("water" = 1) + catalysts = list("fluorine" = 10) + result_amount = 1 /////////////////////////////////////////////////////////////////////////////////// /// Vore Drugs diff --git a/code/modules/reagents/dispenser/dispenser2_energy.dm b/code/modules/reagents/dispenser/dispenser2_energy.dm index 84c5260c3f..b0dec9f65b 100644 --- a/code/modules/reagents/dispenser/dispenser2_energy.dm +++ b/code/modules/reagents/dispenser/dispenser2_energy.dm @@ -25,7 +25,7 @@ C.reagents.add_reagent(id, to_restore) . = 1 if(.) - SSnanoui.update_uis(src) + SStgui.update_uis(src) /obj/machinery/chemical_dispenser dispense_reagents = list( diff --git a/code/modules/reagents/reagent_containers/borghypo.dm b/code/modules/reagents/reagent_containers/borghypo.dm index d8afdf4631..813a79431d 100644 --- a/code/modules/reagents/reagent_containers/borghypo.dm +++ b/code/modules/reagents/reagent_containers/borghypo.dm @@ -1,159 +1,200 @@ -/obj/item/weapon/reagent_containers/borghypo - name = "cyborg hypospray" - desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment." - icon = 'icons/obj/syringe.dmi' - item_state = "hypo" - icon_state = "borghypo" - amount_per_transfer_from_this = 5 - volume = 30 - possible_transfer_amounts = null - - var/mode = 1 - var/charge_cost = 50 - var/charge_tick = 0 - var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) - var/bypass_protection = FALSE // If true, can inject through things like spacesuits and armor. - - var/list/reagent_ids = list("tricordrazine", "inaprovaline", "anti_toxin", "tramadol", "dexalin" ,"spaceacillin") - var/list/reagent_volumes = list() - var/list/reagent_names = list() - -/obj/item/weapon/reagent_containers/borghypo/surgeon - reagent_ids = list("tricordrazine", "inaprovaline", "oxycodone", "dexalin" ,"spaceacillin") - -/obj/item/weapon/reagent_containers/borghypo/crisis - reagent_ids = list("tricordrazine", "inaprovaline", "anti_toxin", "tramadol", "dexalin" ,"spaceacillin") - -/obj/item/weapon/reagent_containers/borghypo/lost - reagent_ids = list("tricordrazine", "bicaridine", "dexalin", "anti_toxin", "tramadol", "spaceacillin") - -/obj/item/weapon/reagent_containers/borghypo/merc - name = "advanced cyborg hypospray" - desc = "An advanced nanite and chemical synthesizer and injection system, designed for heavy-duty medical equipment. This type is capable of safely bypassing \ - thick materials that other hyposprays would struggle with." - bypass_protection = TRUE // Because mercs tend to be in spacesuits. - reagent_ids = list("healing_nanites", "hyperzine", "tramadol", "oxycodone", "spaceacillin", "peridaxon", "osteodaxon", "myelamine", "synthblood") - -/obj/item/weapon/reagent_containers/borghypo/Initialize() - . = ..() - - for(var/T in reagent_ids) - reagent_volumes[T] = volume - var/datum/reagent/R = SSchemistry.chemical_reagents[T] - reagent_names += R.name - - START_PROCESSING(SSobj, src) - -/obj/item/weapon/reagent_containers/borghypo/Destroy() - STOP_PROCESSING(SSobj, src) - return ..() - -/obj/item/weapon/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg+ - if(++charge_tick < recharge_time) - return 0 - charge_tick = 0 - - if(isrobot(loc)) - var/mob/living/silicon/robot/R = loc - if(R && R.cell) - for(var/T in reagent_ids) - if(reagent_volumes[T] < volume) - R.cell.use(charge_cost) - reagent_volumes[T] = min(reagent_volumes[T] + 5, volume) - return 1 - -/obj/item/weapon/reagent_containers/borghypo/attack(var/mob/living/M, var/mob/user) - if(!istype(M)) - return - - if(!reagent_volumes[reagent_ids[mode]]) - to_chat(user, "The injector is empty.") - return - - var/mob/living/carbon/human/H = M - if(istype(H)) - var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting) - if(!affected) - to_chat(user, "\The [H] is missing that limb!") - return - /* since synths have oil/coolant streams now, it only makes sense that you should be able to inject stuff. preserved for posterity. - else if(affected.robotic >= ORGAN_ROBOT) - to_chat(user, "You cannot inject a robotic limb.") - return - */ - - if(M.can_inject(user, 1, ignore_thickness = bypass_protection)) - to_chat(user, "You inject [M] with the injector.") - to_chat(M, "You feel a tiny prick!") - - if(M.reagents) - var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]]) - M.reagents.add_reagent(reagent_ids[mode], t) - reagent_volumes[reagent_ids[mode]] -= t - add_attack_logs(user, M, "Borg injected with [reagent_ids[mode]]") - to_chat(user, "[t] units injected. [reagent_volumes[reagent_ids[mode]]] units remaining.") - return - -/obj/item/weapon/reagent_containers/borghypo/attack_self(mob/user as mob) //Change the mode - var/t = "" - for(var/i = 1 to reagent_ids.len) - if(t) - t += ", " - if(mode == i) - t += "[reagent_names[i]]" - else - t += "[reagent_names[i]]" - t = "Available reagents: [t]." - to_chat(user,t) - - return - -/obj/item/weapon/reagent_containers/borghypo/Topic(var/href, var/list/href_list) - if(href_list["reagent"]) - var/t = reagent_ids.Find(href_list["reagent"]) - if(t) - playsound(src, 'sound/effects/pop.ogg', 50, 0) - mode = t - var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]] - to_chat(usr, "Synthesizer is now producing '[R.name]'.") - -/obj/item/weapon/reagent_containers/borghypo/examine(mob/user) - . = ..() - if(get_dist(user, src) <= 2) - var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]] - . += "It is currently producing [R.name] and has [reagent_volumes[reagent_ids[mode]]] out of [volume] units left." - -/obj/item/weapon/reagent_containers/borghypo/service - name = "cyborg drink synthesizer" - desc = "A portable drink dispencer." - icon = 'icons/obj/drinks.dmi' - icon_state = "shaker" - charge_cost = 20 - recharge_time = 3 - volume = 60 - possible_transfer_amounts = list(5, 10, 20, 30) - reagent_ids = list("ale", "cider", "beer", "berryjuice", "bitters", "coffee", "cognac", "cola", "dr_gibb", "egg", "gin", "gingerale", "hot_coco", "ice", "icetea", "kahlua", "lemonjuice", "lemon_lime", "limejuice", "mead", "milk", "mint", "orangejuice", "rum", "sake", "sodawater", "soymilk", "space_up", "spacemountainwind", "specialwhiskey", "sugar", "tea", "tequilla", "tomatojuice", "tonic", "vermouth", "vodka", "water", "watermelonjuice", "whiskey", "wine") - -/obj/item/weapon/reagent_containers/borghypo/service/attack(var/mob/M, var/mob/user) - return - -/obj/item/weapon/reagent_containers/borghypo/service/afterattack(var/obj/target, var/mob/user, var/proximity) - if(!proximity) - return - - if(!target.is_open_container() || !target.reagents) - return - - if(!reagent_volumes[reagent_ids[mode]]) - to_chat(user, "[src] is out of this reagent, give it some time to refill.") - return - - if(!target.reagents.get_free_space()) - to_chat(user, "[target] is full.") - return - - var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]]) - target.reagents.add_reagent(reagent_ids[mode], t) - reagent_volumes[reagent_ids[mode]] -= t - to_chat(user, "You transfer [t] units of the solution to [target].") - return +/obj/item/weapon/reagent_containers/borghypo + name = "cyborg hypospray" + desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment." + icon = 'icons/obj/syringe.dmi' + item_state = "hypo" + icon_state = "borghypo" + amount_per_transfer_from_this = 5 + volume = 30 + possible_transfer_amounts = null + + var/mode = 1 + var/charge_cost = 50 + var/charge_tick = 0 + var/recharge_time = 5 //Time it takes for shots to recharge (in seconds) + var/bypass_protection = FALSE // If true, can inject through things like spacesuits and armor. + + var/list/reagent_ids = list("tricordrazine", "inaprovaline", "anti_toxin", "tramadol", "dexalin" ,"spaceacillin") + var/list/reagent_volumes = list() + var/list/reagent_names = list() + +/obj/item/weapon/reagent_containers/borghypo/surgeon + reagent_ids = list("tricordrazine", "inaprovaline", "oxycodone", "dexalin" ,"spaceacillin") + +/obj/item/weapon/reagent_containers/borghypo/crisis + reagent_ids = list("tricordrazine", "inaprovaline", "anti_toxin", "tramadol", "dexalin" ,"spaceacillin") + +/obj/item/weapon/reagent_containers/borghypo/lost + reagent_ids = list("tricordrazine", "bicaridine", "dexalin", "anti_toxin", "tramadol", "spaceacillin") + +/obj/item/weapon/reagent_containers/borghypo/merc + name = "advanced cyborg hypospray" + desc = "An advanced nanite and chemical synthesizer and injection system, designed for heavy-duty medical equipment. This type is capable of safely bypassing \ + thick materials that other hyposprays would struggle with." + bypass_protection = TRUE // Because mercs tend to be in spacesuits. + reagent_ids = list("healing_nanites", "hyperzine", "tramadol", "oxycodone", "spaceacillin", "peridaxon", "osteodaxon", "myelamine", "synthblood") + +/obj/item/weapon/reagent_containers/borghypo/Initialize() + . = ..() + + for(var/T in reagent_ids) + reagent_volumes[T] = volume + var/datum/reagent/R = SSchemistry.chemical_reagents[T] + reagent_names += R.name + + START_PROCESSING(SSobj, src) + +/obj/item/weapon/reagent_containers/borghypo/Destroy() + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/weapon/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg+ + if(++charge_tick < recharge_time) + return 0 + charge_tick = 0 + + if(isrobot(loc)) + var/mob/living/silicon/robot/R = loc + if(R && R.cell) + for(var/T in reagent_ids) + if(reagent_volumes[T] < volume) + R.cell.use(charge_cost) + reagent_volumes[T] = min(reagent_volumes[T] + 5, volume) + return 1 + +/obj/item/weapon/reagent_containers/borghypo/attack(var/mob/living/M, var/mob/user) + if(!istype(M)) + return + + if(!reagent_volumes[reagent_ids[mode]]) + to_chat(user, "The injector is empty.") + return + + var/mob/living/carbon/human/H = M + if(istype(H)) + var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting) + if(!affected) + to_chat(user, "\The [H] is missing that limb!") + return + /* since synths have oil/coolant streams now, it only makes sense that you should be able to inject stuff. preserved for posterity. + else if(affected.robotic >= ORGAN_ROBOT) + to_chat(user, "You cannot inject a robotic limb.") + return + */ + + if(M.can_inject(user, 1, ignore_thickness = bypass_protection)) + to_chat(user, "You inject [M] with the injector.") + to_chat(M, "You feel a tiny prick!") + + if(M.reagents) + var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]]) + M.reagents.add_reagent(reagent_ids[mode], t) + reagent_volumes[reagent_ids[mode]] -= t + add_attack_logs(user, M, "Borg injected with [reagent_ids[mode]]") + to_chat(user, "[t] units injected. [reagent_volumes[reagent_ids[mode]]] units remaining.") + return + +/obj/item/weapon/reagent_containers/borghypo/attack_self(mob/user as mob) //Change the mode + var/t = "" + for(var/i = 1 to reagent_ids.len) + if(t) + t += ", " + if(mode == i) + t += "[reagent_names[i]]" + else + t += "[reagent_names[i]]" + t = "Available reagents: [t]." + to_chat(user,t) + + return + +/obj/item/weapon/reagent_containers/borghypo/Topic(var/href, var/list/href_list) + if(href_list["reagent"]) + var/t = reagent_ids.Find(href_list["reagent"]) + if(t) + playsound(src, 'sound/effects/pop.ogg', 50, 0) + mode = t + var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]] + to_chat(usr, "Synthesizer is now producing '[R.name]'.") + +/obj/item/weapon/reagent_containers/borghypo/examine(mob/user) + . = ..() + if(get_dist(user, src) <= 2) + var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]] + . += "It is currently producing [R.name] and has [reagent_volumes[reagent_ids[mode]]] out of [volume] units left." + +/obj/item/weapon/reagent_containers/borghypo/service + name = "cyborg drink synthesizer" + desc = "A portable drink dispencer." + icon = 'icons/obj/drinks.dmi' + icon_state = "shaker" + charge_cost = 20 + recharge_time = 3 + volume = 60 + possible_transfer_amounts = list(5, 10, 20, 30) + reagent_ids = list("ale", + "cider", + "beer", + "berryjuice", + "bitters", + "coffee", + "cognac", + "cola", + "dr_gibb", + "egg", + "gin", + "gingerale", + "hot_coco", + "ice", + "icetea", + "kahlua", + "lemonjuice", + "lemon_lime", + "limejuice", + "mead", + "milk", + "mint", + "orangejuice", + "rum", + "sake", + "sodawater", + "soymilk", + "space_up", + "spacemountainwind", + "spacespice", + "specialwhiskey", + "sugar", + "tea", + "tequilla", + "tomatojuice", + "tonic", + "vermouth", + "vodka", + "water", + "watermelonjuice", + "whiskey", + "wine") + +/obj/item/weapon/reagent_containers/borghypo/service/attack(var/mob/M, var/mob/user) + return + +/obj/item/weapon/reagent_containers/borghypo/service/afterattack(var/obj/target, var/mob/user, var/proximity) + if(!proximity) + return + + if(!target.is_open_container() || !target.reagents) + return + + if(!reagent_volumes[reagent_ids[mode]]) + to_chat(user, "[src] is out of this reagent, give it some time to refill.") + return + + if(!target.reagents.get_free_space()) + to_chat(user, "[target] is full.") + return + + var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]]) + target.reagents.add_reagent(reagent_ids[mode], t) + reagent_volumes[reagent_ids[mode]] -= t + to_chat(user, "You transfer [t] units of the solution to [target].") + return diff --git a/code/modules/reagents/reagent_containers/glass.dm b/code/modules/reagents/reagent_containers/glass.dm index 6b567f47f0..9e7eb5a575 100644 --- a/code/modules/reagents/reagent_containers/glass.dm +++ b/code/modules/reagents/reagent_containers/glass.dm @@ -135,7 +135,7 @@ update_name_label() if(istype(W,/obj/item/weapon/storage/bag)) ..() - if(W && W.w_class <= w_class && (flags & OPENCONTAINER)) + if(W && W.w_class <= w_class && (flags & OPENCONTAINER) && user.a_intent != I_HELP) to_chat(user, "You dip \the [W] into \the [src].") reagents.touch_obj(W, reagents.total_volume) diff --git a/code/modules/resleeving/infomorph.dm b/code/modules/resleeving/infomorph.dm index 7fd716bc96..fed79fe078 100644 --- a/code/modules/resleeving/infomorph.dm +++ b/code/modules/resleeving/infomorph.dm @@ -112,7 +112,10 @@ var/list/infomorph_emotions = list( pda.ownjob = "Sleevecard" pda.owner = text("[]", src) pda.name = pda.owner + " (" + pda.ownjob + ")" - pda.toff = 1 + + var/datum/data/pda/app/messenger/M = pda.find_program(/datum/data/pda/app/messenger) + if(M) + M.toff = TRUE ..() @@ -144,9 +147,9 @@ var/list/infomorph_emotions = list( return 0 ..() -/mob/living/silicon/infomorph/default_can_use_topic(var/src_object) +/mob/living/silicon/infomorph/default_can_use_tgui_topic(var/src_object) if(src_object in src) - return shared_nano_interaction() + return shared_tgui_interaction() /////////// DAMAGES /mob/living/silicon/infomorph/emp_act(severity) @@ -187,7 +190,7 @@ var/list/infomorph_emotions = list( medicalActive1 = null medicalActive2 = null medical_cannotfind = 0 - SSnanoui.update_uis(src) + SStgui.update_uis(src) to_chat(usr, "You reset your record-viewing software.") /* @@ -460,94 +463,81 @@ var/global/list/default_infomorph_software = list() set category = "Card Commands" set name = "Software Interface" - ui_interact(src) + tgui_interact(src) -/mob/living/silicon/infomorph/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1, key_state = self_state) - if(user != src) - if(ui) ui.set_status(STATUS_CLOSE, 0) - return +/mob/living/silicon/infomorph/tgui_state(mob/user) + return GLOB.tgui_self_state - if(ui_key != "main") - var/datum/infomorph_software/S = software[ui_key] - if(S && !S.toggle) - S.on_ui_interact(src, ui, force_open) - else - if(ui) ui.set_status(STATUS_CLOSE, 0) - return - - var/data[0] +/mob/living/silicon/infomorph/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIInterface", "Card Software Interface") + ui.open() +/mob/living/silicon/infomorph/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + // Software we have bought - var/bought_software[0] + var/list/bought_software = list() // Software we have not bought - var/not_bought_software[0] + var/list/not_bought_software = list() for(var/key in infomorph_software_by_key) var/datum/infomorph_software/S = infomorph_software_by_key[key] - var/software_data[0] + var/list/software_data = list() software_data["name"] = S.name software_data["id"] = S.id if(key in software) software_data["on"] = S.is_active(src) - bought_software[++bought_software.len] = software_data + bought_software.Add(list(software_data)) else software_data["ram"] = S.ram_cost - not_bought_software[++not_bought_software.len] = software_data + not_bought_software.Add(list(software_data)) data["bought"] = bought_software data["not_bought"] = not_bought_software data["available_ram"] = ram // Emotions - var/emotions[0] + var/list/emotions = list() for(var/name in infomorph_emotions) - var/emote[0] + var/list/emote = list() emote["name"] = name emote["id"] = infomorph_emotions[name] - emotions[++emotions.len] = emote + emotions.Add(list(emote)) data["emotions"] = emotions data["current_emotion"] = card.current_emotion - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open, key_state) - if (!ui) - ui = new(user, src, ui_key, "pai_interface.tmpl", "Card Software Interface", 450, 600, state = key_state) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data -/mob/living/silicon/infomorph/Topic(href, href_list) - . = ..() - if(.) return +/mob/living/silicon/infomorph/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE - if(href_list["software"]) - var/soft = href_list["software"] - var/datum/infomorph_software/S = software[soft] - if(S.toggle) - S.toggle(src) - else - ui_interact(src, ui_key = soft) - return 1 + switch(action) + if("software") + var/soft = params["software"] + var/datum/infomorph_software/S = software[soft] + if(S.toggle) + S.toggle(src) + else + S.tgui_interact(src, parent_ui = ui) + return TRUE - else if(href_list["stopic"]) - var/soft = href_list["stopic"] - var/datum/infomorph_software/S = software[soft] - if(S) - return S.Topic(href, href_list) + if("purchase") + var/soft = params["purchase"] + var/datum/infomorph_software/S = infomorph_software_by_key[soft] + if(S && (ram >= S.ram_cost)) + ram -= S.ram_cost + software[S.id] = S + return TRUE - else if(href_list["purchase"]) - var/soft = href_list["purchase"] - var/datum/infomorph_software/S = infomorph_software_by_key[soft] - if(S && (ram >= S.ram_cost)) - ram -= S.ram_cost - software[S.id] = S - return 1 - - else if(href_list["image"]) - var/img = href_list["image"] - if(img) - card.setEmotion(img) - return 1 + if("image") + var/img = text2num(params["image"]) + if(img) + card.setEmotion(img) + return TRUE /mob/living/silicon/infomorph/examine(mob/user) . = ..(user, infix = ", personal AI") diff --git a/code/modules/resleeving/infomorph_software.dm b/code/modules/resleeving/infomorph_software.dm index 2d2439b614..67a2bd47f9 100644 --- a/code/modules/resleeving/infomorph_software.dm +++ b/code/modules/resleeving/infomorph_software.dm @@ -12,35 +12,38 @@ // Whether pAIs should automatically receive this module at no cost var/default = 0 - proc/on_ui_interact(mob/living/silicon/infomorph/user, datum/nanoui/ui=null, force_open=1) - return +/datum/infomorph_software/tgui_state(mob/user) + return GLOB.tgui_always_state - proc/toggle(mob/living/silicon/infomorph/user) - return +/datum/infomorph_software/tgui_status(mob/user) + if(!istype(user, /mob/living/silicon/infomorph)) + return STATUS_CLOSE + return ..() - proc/is_active(mob/living/silicon/infomorph/user) - return 0 +/datum/infomorph_software/proc/toggle(mob/living/silicon/infomorph/user) + return + +/datum/infomorph_software/proc/is_active(mob/living/silicon/infomorph/user) + return 0 /datum/infomorph_software/crew_manifest name = "Crew Manifest" - ram_cost = 15 + ram_cost = 5 id = "manifest" toggle = 0 - on_ui_interact(mob/living/silicon/infomorph/user, datum/nanoui/ui=null, force_open=1) +/datum/infomorph_software/crew_manifest/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "CrewManifest", name, parent_ui) + ui.open() + +/datum/infomorph_software/crew_manifest/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + if(data_core) data_core.get_manifest_list() - - var/data[0] - // This is dumb, but NanoUI breaks if it has no data to send - data["manifest"] = PDA_Manifest - - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_manifest.tmpl", "Crew Manifest", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + data["manifest"] = PDA_Manifest + return data /datum/infomorph_software/med_records name = "Medical Records" @@ -48,53 +51,55 @@ id = "med_records" toggle = 0 - on_ui_interact(mob/living/silicon/infomorph/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/infomorph_software/med_records/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIMedrecords", name, parent_ui) + ui.open() - var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) - var/record[0] - record["name"] = general.fields["name"] - record["ref"] = "\ref[general]" - records[++records.len] = record +/datum/infomorph_software/med_records/tgui_data(mob/living/silicon/infomorph/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/list/records = list() + for(var/datum/data/record/general in sortRecord(data_core.general)) + var/list/record = list() + record["name"] = general.fields["name"] + record["ref"] = "\ref[general]" + records.Add(list(record)) - data["records"] = records + data["records"] = records - var/datum/data/record/G = user.medicalActive1 - var/datum/data/record/M = user.medicalActive2 - data["general"] = G ? G.fields : null - data["medical"] = M ? M.fields : null - data["could_not_find"] = user.medical_cannotfind + var/datum/data/record/G = user.medicalActive1 + var/datum/data/record/M = user.medicalActive2 + data["general"] = G ? G.fields : null + data["medical"] = M ? M.fields : null + data["could_not_find"] = user.medical_cannotfind - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_medrecords.tmpl", "Medical Records", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data - Topic(href, href_list) - var/mob/living/silicon/infomorph/P = usr - if(!istype(P)) return +/datum/infomorph_software/med_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + var/mob/living/silicon/infomorph/P = usr + if(!istype(P)) + return - if(href_list["select"]) - var/datum/data/record/record = locate(href_list["select"]) - if(record) - var/datum/data/record/R = record - var/datum/data/record/M = null - if (!( data_core.general.Find(R) )) - P.medical_cannotfind = 1 - else - P.medical_cannotfind = 0 - for(var/datum/data/record/E in data_core.medical) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - M = E - P.medicalActive1 = R - P.medicalActive2 = M - else + if(action == "select") + var/datum/data/record/record = locate(params["select"]) + if(record) + var/datum/data/record/R = record + var/datum/data/record/M = null + if (!( data_core.general.Find(R) )) P.medical_cannotfind = 1 - return 1 + else + P.medical_cannotfind = 0 + for(var/datum/data/record/E in data_core.medical) + if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + M = E + P.medicalActive1 = R + P.medicalActive2 = M + else + P.medical_cannotfind = 1 + return 1 /datum/infomorph_software/sec_records name = "Security Records" @@ -102,57 +107,59 @@ id = "sec_records" toggle = 0 - on_ui_interact(mob/living/silicon/infomorph/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/infomorph_software/sec_records/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAISecrecords", name, parent_ui) + ui.open() - var/records[0] - for(var/datum/data/record/general in sortRecord(data_core.general)) - var/record[0] - record["name"] = general.fields["name"] - record["ref"] = "\ref[general]" - records[++records.len] = record +/datum/infomorph_software/sec_records/tgui_data(mob/living/silicon/infomorph/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/list/records = list() + for(var/datum/data/record/general in sortRecord(data_core.general)) + var/list/record = list() + record["name"] = general.fields["name"] + record["ref"] = "\ref[general]" + records.Add(list(record)) - data["records"] = records + data["records"] = records - var/datum/data/record/G = user.securityActive1 - var/datum/data/record/S = user.securityActive2 - data["general"] = G ? G.fields : null - data["security"] = S ? S.fields : null - data["could_not_find"] = user.security_cannotfind + var/datum/data/record/G = user.securityActive1 + var/datum/data/record/S = user.securityActive2 + data["general"] = G ? G.fields : null + data["security"] = S ? S.fields : null + data["could_not_find"] = user.security_cannotfind - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_secrecords.tmpl", "Security Records", 450, 600) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + return data - Topic(href, href_list) - var/mob/living/silicon/infomorph/P = usr - if(!istype(P)) return +/datum/infomorph_software/sec_records/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + . = ..() + var/mob/living/silicon/infomorph/P = usr + if(!istype(P)) + return - if(href_list["select"]) - var/datum/data/record/record = locate(href_list["select"]) - if(record) - var/datum/data/record/R = record - var/datum/data/record/S = null - if (!( data_core.general.Find(R) )) - P.securityActive1 = null - P.securityActive2 = null - P.security_cannotfind = 1 - else - P.security_cannotfind = 0 - for(var/datum/data/record/E in data_core.security) - if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) - S = E - P.securityActive1 = R - P.securityActive2 = S - else + if(action == "select") + var/datum/data/record/record = locate(params["select"]) + if(record) + var/datum/data/record/R = record + var/datum/data/record/S = null + if (!( data_core.general.Find(R) )) P.securityActive1 = null P.securityActive2 = null P.security_cannotfind = 1 - return 1 + else + P.security_cannotfind = 0 + for(var/datum/data/record/E in data_core.security) + if ((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"])) + S = E + P.securityActive1 = R + P.securityActive2 = S + else + P.securityActive1 = null + P.securityActive2 = null + P.security_cannotfind = 1 + return TRUE /datum/infomorph_software/door_jack name = "Door Jack" @@ -160,44 +167,45 @@ id = "door_jack" toggle = 0 - on_ui_interact(mob/living/silicon/infomorph/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/infomorph_software/door_jack/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIDoorjack", "Door Jack", parent_ui) + ui.open() - data["cable"] = user.cable != null - data["machine"] = user.cable && (user.cable.machine != null) - data["inprogress"] = user.hackdoor != null - data["progress_a"] = round(user.hackprogress) - data["progress_b"] = user.hackprogress % 10 - data["aborted"] = user.hack_aborted +/datum/infomorph_software/door_jack/tgui_data(mob/living/silicon/infomorph/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_doorjack.tmpl", "Door Jack", 300, 150) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) + data["cable"] = user.cable != null + data["machine"] = user.cable && (user.cable.machine != null) + data["inprogress"] = user.hackdoor != null + data["progress_a"] = round(user.hackprogress / 10) + data["progress_b"] = user.hackprogress % 10 + data["aborted"] = user.hack_aborted - Topic(href, href_list) - var/mob/living/silicon/infomorph/P = usr - if(!istype(P)) return + return data - if(href_list["jack"]) +/datum/infomorph_software/door_jack/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + var/mob/living/silicon/infomorph/P = usr + if(!istype(P) || ..()) + return TRUE + + switch(action) + if("jack") if(P.cable && P.cable.machine) P.hackdoor = P.cable.machine P.hackloop() return 1 - else if(href_list["cancel"]) + if("cancel") P.hackdoor = null return 1 - else if(href_list["cable"]) + if("cable") var/turf/T = get_turf(P) P.hack_aborted = 0 P.cable = new /obj/item/weapon/pai_cable(T) - if(ishuman(P.card.loc)) - var/mob/living/carbon/human/H = P.card.loc - H.put_in_any_hand_if_possible(P.cable) - T.visible_message("A port on \the [P] opens to reveal \the [P.cable].") + for(var/mob/M in viewers(T)) + M.show_message("A port on [P] opens to reveal [P.cable], which promptly falls to the floor.", 3, + "You hear the soft click of something light and hard falling to the ground.", 2) return 1 /mob/living/silicon/infomorph/proc/hackloop() @@ -239,43 +247,54 @@ /datum/infomorph_software/atmosphere_sensor name = "Atmosphere Sensor" - ram_cost = 15 + ram_cost = 5 id = "atmos_sense" toggle = 0 - on_ui_interact(mob/living/silicon/infomorph/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/infomorph_software/atmosphere_sensor/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "pAIAtmos", name, parent_ui) + ui.open() - var/turf/T = get_turf_or_move(user.loc) - if(!T) - data["reading"] = 0 - data["pressure"] = 0 - data["temperature"] = 0 - data["temperatureC"] = 0 - data["gas"] = list() - else - var/datum/gas_mixture/env = T.return_air() - data["reading"] = 1 - var/pres = env.return_pressure() * 10 - data["pressure"] = "[round(pres/10)].[pres%10]" - data["temperature"] = round(env.temperature) - data["temperatureC"] = round(env.temperature-T0C) +/datum/infomorph_software/atmosphere_sensor/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() - var/t_moles = env.total_moles - var/gases[0] - for(var/g in env.gas) - var/gas[0] - gas["name"] = gas_data.name[g] - gas["percent"] = round((env.gas[g] / t_moles) * 100) - gases[++gases.len] = gas - data["gas"] = gases + var/list/results = list() + var/turf/T = get_turf(user) + if(!isnull(T)) + var/datum/gas_mixture/environment = T.return_air() + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles + if (total_moles) + var/o2_level = environment.gas["oxygen"]/total_moles + var/n2_level = environment.gas["nitrogen"]/total_moles + var/co2_level = environment.gas["carbon_dioxide"]/total_moles + var/phoron_level = environment.gas["phoron"]/total_moles + var/unknown_level = 1-(o2_level+n2_level+co2_level+phoron_level) - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_atmosphere.tmpl", "Atmosphere Sensor", 350, 300) - ui.set_initial_data(data) - ui.open() + // entry is what the element is describing + // Type identifies which unit or other special characters to use + // Val is the information reported + // Bad_high/_low are the values outside of which the entry reports as dangerous + // Poor_high/_low are the values outside of which the entry reports as unideal + // Values were extracted from the template itself + results = list( + list("entry" = "Pressure", "units" = "kPa", "val" = "[round(pressure,0.1)]", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80), + list("entry" = "Temperature", "units" = "°C", "val" = "[round(environment.temperature-T0C,0.1)]", "bad_high" = 35, "poor_high" = 25, "poor_low" = 15, "bad_low" = 5), + list("entry" = "Oxygen", "units" = "kPa", "val" = "[round(o2_level*100,0.1)]", "bad_high" = 140, "poor_high" = 135, "poor_low" = 19, "bad_low" = 17), + list("entry" = "Nitrogen", "units" = "kPa", "val" = "[round(n2_level*100,0.1)]", "bad_high" = 105, "poor_high" = 85, "poor_low" = 50, "bad_low" = 40), + list("entry" = "Carbon Dioxide", "units" = "kPa", "val" = "[round(co2_level*100,0.1)]", "bad_high" = 10, "poor_high" = 5, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Phoron", "units" = "kPa", "val" = "[round(phoron_level*100,0.01)]", "bad_high" = 0.5, "poor_high" = 0, "poor_low" = 0, "bad_low" = 0), + list("entry" = "Other", "units" = "kPa", "val" = "[round(unknown_level, 0.01)]", "bad_high" = 1, "poor_high" = 0.5, "poor_low" = 0, "bad_low" = 0) + ) + + if(isnull(results)) + results = list(list("entry" = "pressure", "units" = "kPa", "val" = "0", "bad_high" = 120, "poor_high" = 110, "poor_low" = 95, "bad_low" = 80)) + + data["aircontents"] = results + + return data /datum/infomorph_software/ar_hud name = "AR HUD" @@ -302,45 +321,56 @@ is_active(mob/living/silicon/infomorph/user) return user.translator.listening + /datum/infomorph_software/signaller - name = "Remote Signaller" - ram_cost = 15 + name = "Remote Signaler" + ram_cost = 5 id = "signaller" toggle = 0 - on_ui_interact(mob/living/silicon/infomorph/user, datum/nanoui/ui=null, force_open=1) - var/data[0] +/datum/infomorph_software/signaller/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Signaler", "Signaler", parent_ui) + ui.open() - data["frequency"] = format_frequency(user.sradio.frequency) - data["code"] = user.sradio.code +/datum/infomorph_software/signaller/tgui_data(mob/living/silicon/infomorph/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/obj/item/radio/integrated/signal/R = user.sradio - ui = SSnanoui.try_update_ui(user, user, id, ui, data, force_open) - if(!ui) - // Don't copy-paste this unless you're making a pAI software module! - ui = new(user, user, id, "pai_signaller.tmpl", "Signaller", 320, 150) - ui.set_initial_data(data) - ui.open() + data["frequency"] = R.frequency + data["minFrequency"] = RADIO_LOW_FREQ + data["maxFrequency"] = RADIO_HIGH_FREQ + data["code"] = R.code - Topic(href, href_list) - var/mob/living/silicon/infomorph/P = usr - if(!istype(P)) return + return data - if(href_list["send"]) - P.sradio.send_signal("ACTIVATE") - for(var/mob/O in hearers(1, P.loc)) - O.show_message("[bicon(P)] *beep* *beep*", 3, "*beep* *beep*", 2) - return 1 +/datum/infomorph_software/signaller/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE - else if(href_list["freq"]) - var/new_frequency = (P.sradio.frequency + text2num(href_list["freq"])) - if(new_frequency < PUBLIC_LOW_FREQ || new_frequency > PUBLIC_HIGH_FREQ) - new_frequency = sanitize_frequency(new_frequency) - P.sradio.set_frequency(new_frequency) - return 1 + var/mob/living/silicon/infomorph/user = usr + if(istype(user)) + var/obj/item/radio/integrated/signal/R = user.sradio - else if(href_list["code"]) - P.sradio.code += text2num(href_list["code"]) - P.sradio.code = round(P.sradio.code) - P.sradio.code = min(100, P.sradio.code) - P.sradio.code = max(1, P.sradio.code) - return 1 + switch(action) + if("signal") + spawn(0) + R.send_signal("ACTIVATE") + for(var/mob/O in hearers(1, R.loc)) + O.show_message("[bicon(R)] *beep* *beep*", 3, "*beep* *beep*", 2) + if("freq") + var/frequency = unformat_frequency(params["freq"]) + frequency = sanitize_frequency(frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + R.set_frequency(frequency) + . = TRUE + if("code") + R.code = clamp(round(text2num(params["code"])), 1, 100) + . = TRUE + if("reset") + if(params["reset"] == "freq") + R.set_frequency(initial(R.frequency)) + else + R.code = initial(R.code) + . = TRUE diff --git a/code/modules/resleeving/sleevecard.dm b/code/modules/resleeving/sleevecard.dm index 7da45bbdb1..1fc6b8deca 100644 --- a/code/modules/resleeving/sleevecard.dm +++ b/code/modules/resleeving/sleevecard.dm @@ -1,6 +1,9 @@ /obj/item/device/radio/sleevecard canhear_range = 0 +/obj/item/device/radio/sleevecard/tgui_state(mob/user) + return GLOB.tgui_always_state + /obj/item/device/sleevecard name = "sleevecard" desc = "This KHI-upgraded pAI module has enough capacity to run a whole mind of human-level intelligence." diff --git a/code/modules/shuttles/shuttle_console_multi.dm b/code/modules/shuttles/shuttle_console_multi.dm index 9226caebfb..2e45441b1a 100644 --- a/code/modules/shuttles/shuttle_console_multi.dm +++ b/code/modules/shuttles/shuttle_console_multi.dm @@ -26,7 +26,7 @@ switch(action) if("pick") var/dest_key = input("Choose shuttle destination", "Shuttle Destination") as null|anything in shuttle.get_destinations() - if(dest_key && CanInteract(usr, global.default_state)) + if(dest_key && CanInteract(usr, GLOB.tgui_default_state)) shuttle.set_destination(dest_key, usr) return TRUE diff --git a/code/modules/shuttles/shuttle_emergency.dm b/code/modules/shuttles/shuttle_emergency.dm index b6302a50d1..8a947154a7 100644 --- a/code/modules/shuttles/shuttle_emergency.dm +++ b/code/modules/shuttles/shuttle_emergency.dm @@ -175,84 +175,3 @@ /obj/machinery/computer/shuttle_control/emergency/attackby(obj/item/weapon/W as obj, mob/user as mob) read_authorization(W) ..() - -/obj/machinery/computer/shuttle_control/emergency/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - var/data[0] - var/datum/shuttle/autodock/ferry/emergency/shuttle = SSshuttles.shuttles[shuttle_tag] - if (!istype(shuttle)) - return - - var/shuttle_state - switch(shuttle.moving_status) - if(SHUTTLE_IDLE) shuttle_state = "idle" - if(SHUTTLE_WARMUP) shuttle_state = "warmup" - if(SHUTTLE_INTRANSIT) shuttle_state = "in_transit" - - var/shuttle_status - switch (shuttle.process_state) - if(IDLE_STATE) - if (shuttle.in_use) - shuttle_status = "Busy." - else if (!shuttle.location) - shuttle_status = "Standing by at [station_name()]." - else - shuttle_status = "Standing by at [using_map.dock_name]." - if(WAIT_LAUNCH, FORCE_LAUNCH) - shuttle_status = "Shuttle has received command and will depart shortly." - if(WAIT_ARRIVE) - shuttle_status = "Proceeding to destination." - if(WAIT_FINISH) - shuttle_status = "Arriving at destination now." - - //build a list of authorizations - var/list/auth_list[req_authorizations] - - if (!emagged) - var/i = 1 - for (var/dna_hash in authorized) - auth_list[i++] = list("auth_name"=authorized[dna_hash], "auth_hash"=dna_hash) - - while (i <= req_authorizations) //fill up the rest of the list with blank entries - auth_list[i++] = list("auth_name"="", "auth_hash"=null) - else - for (var/i = 1; i <= req_authorizations; i++) - auth_list[i] = list("auth_name"="ERROR", "auth_hash"=null) - - var/has_auth = has_authorization() - - data = list( - "shuttle_status" = shuttle_status, - "shuttle_state" = shuttle_state, - "has_docking" = shuttle.active_docking_controller? 1 : 0, - "docking_status" = shuttle.active_docking_controller? shuttle.active_docking_controller.get_docking_status() : null, - "docking_override" = shuttle.active_docking_controller? shuttle.active_docking_controller.override_enabled : null, - "can_launch" = shuttle.can_launch(src), - "can_cancel" = shuttle.can_cancel(src), - "can_force" = shuttle.can_force(src), - "auth_list" = auth_list, - "has_auth" = has_auth, - "user" = debug? user : null, - ) - - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - - if (!ui) - ui = new(user, src, ui_key, "escape_shuttle_control_console.tmpl", "Shuttle Control", 470, 420) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(1) - -/obj/machinery/computer/shuttle_control/emergency/Topic(href, href_list) - if(..()) - return 1 - - if(href_list["removeid"]) - var/dna_hash = href_list["removeid"] - authorized -= dna_hash - - if(!emagged && href_list["scanid"]) - //They selected an empty entry. Try to scan their id. - if (ishuman(usr)) - var/mob/living/carbon/human/H = usr - if (!read_authorization(H.get_active_hand())) //try to read what's in their hand first - read_authorization(H.wear_id) diff --git a/code/modules/telesci/telesci_computer.dm b/code/modules/telesci/telesci_computer.dm index 1119e565ef..d2a6a0a35a 100644 --- a/code/modules/telesci/telesci_computer.dm +++ b/code/modules/telesci/telesci_computer.dm @@ -6,7 +6,7 @@ circuit = /obj/item/weapon/circuitboard/telesci_console var/sending = 1 var/obj/machinery/telepad/telepad = null - var/temp_msg = "Telescience control console initialized.
Welcome." + var/temp_msg = "Telescience control console initialized. Welcome." // VARIABLES // var/teles_left // How many teleports left until it becomes uncalibrated @@ -83,16 +83,21 @@ /obj/machinery/computer/telescience/attack_hand(mob/user) if(..()) return - ui_interact(user) + tgui_interact(user) -/obj/machinery/computer/telescience/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1) - user.set_machine(src) +/obj/machinery/computer/telescience/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "TelesciConsole", name) + ui.open() - var/data[0] +/obj/machinery/computer/telescience/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = list() if(!telepad) in_use = 0 //Yeah so if you deconstruct teleporter while its in the process of shooting it wont disable the console data["noTelepad"] = 1 else + data["noTelepad"] = 0 data["insertedGps"] = inserted_gps data["rotation"] = rotation data["currentZ"] = z_co @@ -110,6 +115,7 @@ //We'll base our options on connected z's or overmap data["sectorOptions"] = using_map.get_map_levels(z, TRUE, overmap_range) + data["lastTeleData"] = null if(last_tele_data) data["lastTeleData"] = list() data["lastTeleData"]["src_x"] = last_tele_data.src_x @@ -117,12 +123,61 @@ data["lastTeleData"]["distance"] = last_tele_data.distance data["lastTeleData"]["time"] = last_tele_data.time - ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open) - if (!ui) - ui = new(user, src, ui_key, "telescience_console.tmpl", src.name, 400, 450) - ui.set_initial_data(data) - ui.open() - ui.set_auto_update(5) + return data + +/obj/machinery/computer/telescience/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + if(!telepad || telepad.panel_open) + return TRUE + + switch(action) + if("setrotation") + rotation = CLAMP(text2num(params["val"]), -900, 900) + rotation = round(rotation, 0.01) + + if("setdistance") + distance = CLAMP(text2num(params["val"]), 1, get_max_allowed_distance()) + distance = FLOOR(distance, 1) + + if("setz") + var/new_z = text2num(params["setz"]) + if(new_z in using_map.player_levels) + z_co = new_z + + if("ejectGPS") + if(inserted_gps) + inserted_gps.forceMove(loc) + inserted_gps = null + + if("setMemory") + if(last_target && inserted_gps) + // TODO - What was this even supposed to do?? + //inserted_gps.locked_location = last_target + temp_msg = "Location saved." + else + temp_msg = "ERROR! No data was stored." + + if("send") + sending = 1 + teleport(usr) + + if("receive") + sending = 0 + teleport(usr) + + if("recal") + recalibrate() + sparks() + temp_msg = "NOTICE: Calibration successful." + + if("eject") + eject() + temp_msg = "NOTICE: Bluespace crystals ejected." + else + return FALSE + + return TRUE /obj/machinery/computer/telescience/proc/sparks() if(telepad) @@ -198,7 +253,7 @@ playsound(telepad, 'sound/weapons/flash.ogg', 50, 1) // Wait depending on the time the projectile took to get there teleporting = 1 - temp_msg = "Powering up bluespace crystals.
Please wait." + temp_msg = "Powering up bluespace crystals. Please wait." spawn(spawn_time) // in deciseconds if(!telepad) @@ -218,12 +273,12 @@ if(!A || (A.flags & BLUE_SHIELDED)) telefail() - temp_msg = "ERROR!
Target is shielded from bluespace intersection!" + temp_msg = "ERROR! Target is shielded from bluespace intersection!" return - temp_msg = "Teleport successful.
" + temp_msg = "Teleport successful. " if(teles_left < 10) - temp_msg += "Calibration required soon.
" + temp_msg += "Calibration required soon. " temp_msg += "Data printed below." var/sparks = get_turf(target) @@ -286,11 +341,11 @@ /obj/machinery/computer/telescience/proc/teleport(mob/user) distance = CLAMP(distance, 0, get_max_allowed_distance()) if(rotation == null || distance == null || z_co == null) - temp_msg = "ERROR!
Set a distance, rotation and sector." + temp_msg = "ERROR! Set a distance, rotation and sector." return if(distance <= 0) telefail() - temp_msg = "ERROR!
No distance selected!" + temp_msg = "ERROR! No distance selected!" return if(!(z_co in using_map.player_levels)) telefail() @@ -300,7 +355,7 @@ doteleport(user) else telefail() - temp_msg = "ERROR!
Calibration required." + temp_msg = "ERROR! Calibration required." return return @@ -310,64 +365,6 @@ crystals -= I distance = 0 -/obj/machinery/computer/telescience/Topic(href, href_list) - if(..()) - return - if(!telepad || telepad.panel_open) - updateDialog() - return - - if(href_list["setrotation"]) - var/new_rot = input("Please input desired bearing in degrees.", name, rotation) as num - if(..()) // Check after we input a value, as they could've moved after they entered something - return - rotation = CLAMP(new_rot, -900, 900) - rotation = round(rotation, 0.01) - - if(href_list["setdistance"]) - var/new_pow = input("Please input desired distance in meters.", name, rotation) as num - if(..()) // Check after we input a value, as they could've moved after they entered something - return - distance = CLAMP(new_pow, 1, get_max_allowed_distance()) - distance = FLOOR(distance, 1) - - if(href_list["setz"]) - var/new_z = text2num(href_list["setz"]) - if(new_z in using_map.player_levels) - z_co = new_z - - if(href_list["ejectGPS"]) - if(inserted_gps) - inserted_gps.forceMove(loc) - inserted_gps = null - - if(href_list["setMemory"]) - if(last_target && inserted_gps) - // TODO - What was this even supposed to do?? - //inserted_gps.locked_location = last_target - temp_msg = "Location saved." - else - temp_msg = "ERROR!
No data was stored." - - if(href_list["send"]) - sending = 1 - teleport(usr) - - if(href_list["receive"]) - sending = 0 - teleport(usr) - - if(href_list["recal"]) - recalibrate() - sparks() - temp_msg = "NOTICE:
Calibration successful." - - if(href_list["eject"]) - eject() - temp_msg = "NOTICE:
Bluespace crystals ejected." - - updateDialog() - /obj/machinery/computer/telescience/proc/recalibrate() teles_left = rand(40, 50) distance_off = rand(-4, 4) diff --git a/code/modules/tgui/modules/_base.dm b/code/modules/tgui/modules/_base.dm index 1a1027072d..7cece1c097 100644 --- a/code/modules/tgui/modules/_base.dm +++ b/code/modules/tgui/modules/_base.dm @@ -123,4 +123,7 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff . += new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status . += new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags . += new /obj/screen/plane_master{plane = PLANE_AUGMENTED} //Augmented reality - //VOREStation Add End \ No newline at end of file + //VOREStation Add End + +/datum/tgui_module/proc/relaymove(mob/user, direction) + return FALSE diff --git a/code/modules/tgui/modules/agentcard.dm b/code/modules/tgui/modules/agentcard.dm new file mode 100644 index 0000000000..7d35a9bc4a --- /dev/null +++ b/code/modules/tgui/modules/agentcard.dm @@ -0,0 +1,135 @@ +/datum/tgui_module/agentcard + name = "Agent Card" + tgui_id = "AgentCard" + +/datum/tgui_module/agentcard/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + var/obj/item/weapon/card/id/syndicate/S = tgui_host() + if(!istype(S)) + return list() + + var/list/entries = list() + entries += list(list("name" = "Age", "value" = S.age)) + entries += list(list("name" = "Appearance", "value" = "Set")) + entries += list(list("name" = "Assignment", "value" = S.assignment)) + entries += list(list("name" = "Blood Type", "value" = S.blood_type)) + entries += list(list("name" = "DNA Hash", "value" = S.dna_hash)) + entries += list(list("name" = "Fingerprint Hash", "value" = S.fingerprint_hash)) + entries += list(list("name" = "Name", "value" = S.registered_name)) + entries += list(list("name" = "Photo", "value" = "Update")) + entries += list(list("name" = "Sex", "value" = S.sex)) + entries += list(list("name" = "Factory Reset", "value" = "Use With Care")) + data["entries"] = entries + + data["electronic_warfare"] = S.electronic_warfare + + return data + +/datum/tgui_module/agentcard/tgui_status(mob/user, datum/tgui_state/state) + var/obj/item/weapon/card/id/syndicate/S = tgui_host() + if(!istype(S)) + return STATUS_CLOSE + if(user != S.registered_user) + return STATUS_CLOSE + return ..() + +/datum/tgui_module/agentcard/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + var/obj/item/weapon/card/id/syndicate/S = tgui_host() + switch(action) + if("electronic_warfare") + S.electronic_warfare = !S.electronic_warfare + to_chat(usr, "Electronic warfare [S.electronic_warfare ? "enabled" : "disabled"].") + . = TRUE + if("age") + var/new_age = input(usr,"What age would you like to put on this card?","Agent Card Age", S.age) as null|num + if(!isnull(new_age) && tgui_status(usr, state) == STATUS_INTERACTIVE) + if(new_age < 0) + S.age = initial(S.age) + else + S.age = new_age + to_chat(usr, "Age has been set to '[S.age]'.") + . = TRUE + if("appearance") + var/datum/card_state/choice = input(usr, "Select the appearance for this card.", "Agent Card Appearance") as null|anything in id_card_states() + if(choice && tgui_status(usr, state) == STATUS_INTERACTIVE) + S.icon_state = choice.icon_state + S.item_state = choice.item_state + to_chat(usr, "Appearance changed to [choice].") + . = TRUE + if("assignment") + var/new_job = sanitize(input(usr,"What assignment would you like to put on this card?\nChanging assignment will not grant or remove any access levels.","Agent Card Assignment", S.assignment) as null|text) + if(!isnull(new_job) && tgui_status(usr, state) == STATUS_INTERACTIVE) + S.assignment = new_job + to_chat(usr, "Occupation changed to '[new_job]'.") + S.update_name() + . = TRUE + if("bloodtype") + var/default = S.blood_type + if(default == initial(S.blood_type) && ishuman(usr)) + var/mob/living/carbon/human/H = usr + if(H.dna) + default = H.dna.b_type + var/new_blood_type = sanitize(input(usr,"What blood type would you like to be written on this card?","Agent Card Blood Type",default) as null|text) + if(!isnull(new_blood_type) && tgui_status(usr, state) == STATUS_INTERACTIVE) + S.blood_type = new_blood_type + to_chat(usr, "Blood type changed to '[new_blood_type]'.") + . = TRUE + if("dnahash") + var/default = S.dna_hash + if(default == initial(S.dna_hash) && ishuman(usr)) + var/mob/living/carbon/human/H = usr + if(H.dna) + default = H.dna.unique_enzymes + var/new_dna_hash = sanitize(input(usr,"What DNA hash would you like to be written on this card?","Agent Card DNA Hash",default) as null|text) + if(!isnull(new_dna_hash) && tgui_status(usr, state) == STATUS_INTERACTIVE) + S.dna_hash = new_dna_hash + to_chat(usr, "DNA hash changed to '[new_dna_hash]'.") + . = TRUE + if("fingerprinthash") + var/default = S.fingerprint_hash + if(default == initial(S.fingerprint_hash) && ishuman(usr)) + var/mob/living/carbon/human/H = usr + if(H.dna) + default = md5(H.dna.uni_identity) + var/new_fingerprint_hash = sanitize(input(usr,"What fingerprint hash would you like to be written on this card?","Agent Card Fingerprint Hash",default) as null|text) + if(!isnull(new_fingerprint_hash) && tgui_status(usr, state) == STATUS_INTERACTIVE) + S.fingerprint_hash = new_fingerprint_hash + to_chat(usr, "Fingerprint hash changed to '[new_fingerprint_hash]'.") + . = TRUE + if("name") + var/new_name = sanitizeName(input(usr,"What name would you like to put on this card?","Agent Card Name", S.registered_name) as null|text) + if(!isnull(new_name) && tgui_status(usr, state) == STATUS_INTERACTIVE) + S.registered_name = new_name + S.update_name() + to_chat(usr, "Name changed to '[new_name]'.") + . = TRUE + if("photo") + S.set_id_photo(usr) + to_chat(usr, "Photo changed.") + . = TRUE + if("sex") + var/new_sex = sanitize(input(usr,"What sex would you like to put on this card?","Agent Card Sex", S.sex) as null|text) + if(!isnull(new_sex) && tgui_status(usr, state) == STATUS_INTERACTIVE) + S.sex = new_sex + to_chat(usr, "Sex changed to '[new_sex]'.") + . = TRUE + if("factoryreset") + if(alert("This will factory reset the card, including access and owner. Continue?", "Factory Reset", "No", "Yes") == "Yes" && tgui_status(usr, state) == STATUS_INTERACTIVE) + S.age = initial(S.age) + S.access = syndicate_access.Copy() + S.assignment = initial(S.assignment) + S.blood_type = initial(S.blood_type) + S.dna_hash = initial(S.dna_hash) + S.electronic_warfare = initial(S.electronic_warfare) + S.fingerprint_hash = initial(S.fingerprint_hash) + S.icon_state = initial(S.icon_state) + S.name = initial(S.name) + S.registered_name = initial(S.registered_name) + S.unset_registered_user() + S.sex = initial(S.sex) + to_chat(usr, "All information has been deleted from \the [src].") + . = TRUE diff --git a/code/modules/tgui/modules/atmos_control.dm b/code/modules/tgui/modules/atmos_control.dm index 83d3db4d1e..4fd477a531 100644 --- a/code/modules/tgui/modules/atmos_control.dm +++ b/code/modules/tgui/modules/atmos_control.dm @@ -34,6 +34,11 @@ ui.set_map_z_level(params["mapZLevel"]) return TRUE +/datum/tgui_module/atmos_control/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/nanomaps), + ) + /datum/tgui_module/atmos_control/tgui_interact(mob/user, datum/tgui/ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) diff --git a/code/modules/tgui/modules/communications.dm b/code/modules/tgui/modules/communications.dm new file mode 100644 index 0000000000..1af2a003cc --- /dev/null +++ b/code/modules/tgui/modules/communications.dm @@ -0,0 +1,486 @@ +#define COMM_SCREEN_MAIN 1 +#define COMM_SCREEN_STAT 2 +#define COMM_SCREEN_MESSAGES 3 + +#define COMM_AUTHENTICATION_NONE 0 +#define COMM_AUTHENTICATION_MIN 1 +#define COMM_AUTHENTICATION_MAX 2 + +#define COMM_MSGLEN_MINIMUM 6 +#define COMM_CCMSGLEN_MINIMUM 20 + +/datum/tgui_module/communications + name = "Command & Communications" + tgui_id = "CommunicationsConsole" + + var/emagged = FALSE + + var/current_viewing_message_id = 0 + var/current_viewing_message = null + + var/authenticated = COMM_AUTHENTICATION_NONE + var/menu_state = COMM_SCREEN_MAIN + var/ai_menu_state = COMM_SCREEN_MAIN + var/aicurrmsg + + var/message_cooldown + var/centcomm_message_cooldown + var/tmp_alertlevel = 0 + + var/stat_msg1 + var/stat_msg2 + var/display_type = "blank" + + var/datum/announcement/priority/crew_announcement + + var/datum/lore/atc_controller/ATC + + var/list/req_access = list() + +/datum/tgui_module/communications/New(host) + . = ..() + ATC = atc + crew_announcement = new() + crew_announcement.newscast = TRUE + +/datum/tgui_module/communications/tgui_interact(mob/user, datum/tgui/ui) + if(using_map && !(get_z(user) in using_map.contact_levels)) + to_chat(user, "Unable to establish a connection: You're too far away from the station!") + return FALSE + . = ..() + +/datum/tgui_module/communications/proc/is_authenticated(mob/user, message = TRUE) + if(authenticated == COMM_AUTHENTICATION_MAX) + return COMM_AUTHENTICATION_MAX + else if(isobserver(user)) + var/mob/observer/dead/D = user + if(D.can_admin_interact()) + return COMM_AUTHENTICATION_MAX + else if(authenticated) + return COMM_AUTHENTICATION_MIN + else + if(message) + to_chat(user, "Access denied.") + return COMM_AUTHENTICATION_NONE + +/datum/tgui_module/communications/proc/change_security_level(new_level) + tmp_alertlevel = new_level + var/old_level = security_level + if(!tmp_alertlevel) tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel < SEC_LEVEL_GREEN) tmp_alertlevel = SEC_LEVEL_GREEN + if(tmp_alertlevel > SEC_LEVEL_BLUE) tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this + set_security_level(tmp_alertlevel) + if(security_level != old_level) + //Only notify the admins if an actual change happened + log_game("[key_name(usr)] has changed the security level to [get_security_level()].") + message_admins("[key_name_admin(usr)] has changed the security level to [get_security_level()].") + switch(security_level) + if(SEC_LEVEL_GREEN) + feedback_inc("alert_comms_green",1) + if(SEC_LEVEL_YELLOW) + feedback_inc("alert_comms_yellow",1) + if(SEC_LEVEL_VIOLET) + feedback_inc("alert_comms_violet",1) + if(SEC_LEVEL_ORANGE) + feedback_inc("alert_comms_orange",1) + if(SEC_LEVEL_BLUE) + feedback_inc("alert_comms_blue",1) + tmp_alertlevel = 0 + +/datum/tgui_module/communications/tgui_data(mob/user) + var/list/data = ..() + data["is_ai"] = isAI(user) || isrobot(user) + data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state + data["emagged"] = emagged + data["authenticated"] = is_authenticated(user, 0) + data["authmax"] = data["authenticated"] == COMM_AUTHENTICATION_MAX ? TRUE : FALSE + data["atcsquelch"] = ATC.squelched + data["boss_short"] = using_map.boss_short + + data["stat_display"] = list( + "type" = display_type, + // "icon" = display_icon, + "line_1" = (stat_msg1 ? stat_msg1 : "-----"), + "line_2" = (stat_msg2 ? stat_msg2 : "-----"), + + "presets" = list( + list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"), + list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."), + list("name" = "message", "label" = "Message", "desc" = "A custom message.") + ), + ) + + data["security_level"] = security_level + switch(security_level) + if(SEC_LEVEL_BLUE) + data["security_level_color"] = "blue"; + if(SEC_LEVEL_ORANGE) + data["security_level_color"] = "orange"; + if(SEC_LEVEL_VIOLET) + data["security_level_color"] = "violet"; + if(SEC_LEVEL_YELLOW) + data["security_level_color"] = "yellow"; + if(SEC_LEVEL_GREEN) + data["security_level_color"] = "green"; + if(SEC_LEVEL_RED) + data["security_level_color"] = "red"; + else + data["security_level_color"] = "purple"; + data["str_security_level"] = capitalize(get_security_level()) + data["levels"] = list( + list("id" = SEC_LEVEL_GREEN, "name" = "Green", "icon" = "dove"), + list("id" = SEC_LEVEL_YELLOW, "name" = "Yellow", "icon" = "exclamation-triangle"), + list("id" = SEC_LEVEL_BLUE, "name" = "Blue", "icon" = "eye"), + list("id" = SEC_LEVEL_ORANGE, "name" = "Orange", "icon" = "wrench"), + list("id" = SEC_LEVEL_VIOLET, "name" = "Violet", "icon" = "biohazard"), + ) + + var/datum/comm_message_listener/l = obtain_message_listener() + data["messages"] = l.messages + data["message_deletion_allowed"] = l != global_message_listener + data["message_current_id"] = current_viewing_message_id + data["message_current"] = current_viewing_message + + // data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null + data["msg_cooldown"] = message_cooldown ? (round((message_cooldown - world.time) / 10)) : 0 + data["cc_cooldown"] = centcomm_message_cooldown ? (round((centcomm_message_cooldown - world.time) / 10)) : 0 + + data["esc_callable"] = emergency_shuttle.location() && !emergency_shuttle.online() ? TRUE : FALSE + data["esc_recallable"] = emergency_shuttle.location() && emergency_shuttle.online() ? TRUE : FALSE + data["esc_status"] = FALSE + if(emergency_shuttle.has_eta()) + var/timeleft = emergency_shuttle.estimate_arrival_time() + data["esc_status"] = emergency_shuttle.online() ? "ETA:" : "RECALLING:" + data["esc_status"] += " [timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]" + return data + +/datum/tgui_module/communications/proc/setCurrentMessage(mob/user, value) + current_viewing_message_id = value + + var/datum/comm_message_listener/l = obtain_message_listener() + for(var/list/m in l.messages) + if(m["id"] == current_viewing_message_id) + current_viewing_message = m + +/datum/tgui_module/communications/proc/setMenuState(mob/user, value) + if(isAI(user) || isrobot(user)) + ai_menu_state = value + else + menu_state = value + +/datum/tgui_module/communications/proc/obtain_message_listener() + if(istype(host, /datum/computer_file/program/comm)) + var/datum/computer_file/program/comm/P = host + return P.message_core + return global_message_listener + +/proc/post_status(atom/source, command, data1, data2, mob/user = null) + var/datum/radio_frequency/frequency = radio_controller.return_frequency(1435) + + if(!frequency) + return + + var/datum/signal/status_signal = new + status_signal.source = source + status_signal.transmission_method = TRANSMISSION_RADIO + status_signal.data["command"] = command + + switch(command) + if("message") + status_signal.data["msg1"] = data1 + status_signal.data["msg2"] = data2 + log_admin("STATUS: [user] set status screen message: [data1] [data2]") + //message_admins("STATUS: [user] set status screen with [PDA]. Message: [data1] [data2]") + if("alert") + status_signal.data["picture_state"] = data1 + + frequency.post_signal(null, status_signal) + +/datum/tgui_module/communications/tgui_act(action, params) + if(..()) + return TRUE + if(using_map && !(get_z(usr) in using_map.contact_levels)) + to_chat(usr, "Unable to establish a connection: You're too far away from the station!") + return FALSE + + . = TRUE + if(action == "auth") + if(!ishuman(usr)) + to_chat(usr, "Access denied.") + return FALSE + // Logout function. + if(authenticated != COMM_AUTHENTICATION_NONE) + authenticated = COMM_AUTHENTICATION_NONE + crew_announcement.announcer = null + setMenuState(usr, COMM_SCREEN_MAIN) + return + // Login function. + if(check_access(usr, access_heads)) + authenticated = COMM_AUTHENTICATION_MIN + if(check_access(usr, access_captain)) + authenticated = COMM_AUTHENTICATION_MAX + var/mob/M = usr + var/obj/item/weapon/card/id = M.GetIdCard() + if(istype(id)) + crew_announcement.announcer = GetNameAndAssignmentFromId(id) + if(authenticated == COMM_AUTHENTICATION_NONE) + to_chat(usr, "You need to wear your ID.") + + // All functions below this point require authentication. + if(!is_authenticated(usr)) + return FALSE + + switch(action) + // main interface + if("main") + setMenuState(usr, COMM_SCREEN_MAIN) + + if("newalertlevel") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from changing the alert level.") + return + else if(isobserver(usr)) + var/mob/observer/dead/D = usr + if(D.can_admin_interact()) + change_security_level(text2num(params["level"])) + return TRUE + else if(!ishuman(usr)) + to_chat(usr, "Security measures prevent you from changing the alert level.") + return + + if(check_access(usr, access_captain)) + change_security_level(text2num(params["level"])) + else + to_chat(usr, "You are not authorized to do this.") + setMenuState(usr, COMM_SCREEN_MAIN) + + if("announce") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(message_cooldown > world.time) + to_chat(usr, "Please allow at least one minute to pass between announcements.") + return + var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement") as null|message + if(!input || message_cooldown > world.time || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + return + if(length(input) < COMM_MSGLEN_MINIMUM) + to_chat(usr, "Message '[input]' is too short. [COMM_MSGLEN_MINIMUM] character minimum.") + return + crew_announcement.Announce(input) + message_cooldown = world.time + 600 //One minute + + if("callshuttle") + if(!is_authenticated(usr)) + return + + call_shuttle_proc(usr) + if(emergency_shuttle.online()) + post_status(src, "shuttle", user = usr) + setMenuState(usr, COMM_SCREEN_MAIN) + + if("cancelshuttle") + if(isAI(usr) || isrobot(usr)) + to_chat(usr, "Firewalls prevent you from recalling the shuttle.") + return + var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No") + if(response == "Yes") + cancel_call_proc(usr) + setMenuState(usr, COMM_SCREEN_MAIN) + + if("messagelist") + current_viewing_message = null + current_viewing_message_id = null + if(params["msgid"]) + setCurrentMessage(usr, text2num(params["msgid"])) + setMenuState(usr, COMM_SCREEN_MESSAGES) + + if("toggleatc") + ATC.squelched = !ATC.squelched + + if("delmessage") + var/datum/comm_message_listener/l = obtain_message_listener() + if(params["msgid"]) + setCurrentMessage(usr, text2num(params["msgid"])) + var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No") + if(response == "Yes") + if(current_viewing_message) + if(l != global_message_listener) + l.Remove(current_viewing_message) + current_viewing_message = null + setMenuState(usr, COMM_SCREEN_MESSAGES) + + if("status") + setMenuState(usr, COMM_SCREEN_STAT) + + // Status display stuff + if("setstat") + display_type = params["statdisp"] + switch(display_type) + if("message") + post_status(src, "message", stat_msg1, stat_msg2, user = usr) + if("alert") + post_status(src, "alert", params["alert"], user = usr) + else + post_status(src, params["statdisp"], user = usr) + + if("setmsg1") + stat_msg1 = reject_bad_text(sanitize(input("Line 1", "Enter Message Text", stat_msg1) as text|null, 40), 40) + setMenuState(usr, COMM_SCREEN_STAT) + + if("setmsg2") + stat_msg2 = reject_bad_text(sanitize(input("Line 2", "Enter Message Text", stat_msg2) as text|null, 40), 40) + setMenuState(usr, COMM_SCREEN_STAT) + + // OMG CENTCOMM LETTERHEAD + if("MessageCentCom") + if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX) + if(centcomm_message_cooldown > world.time) + to_chat(usr, "Arrays recycling. Please stand by.") + return + var/input = sanitize(input("Please choose a message to transmit to [using_map.boss_short] via quantum entanglement. \ + Please be aware that this process is very expensive, and abuse will lead to... termination. \ + Transmission does not guarantee a response. \ + There is a 30 second delay before you may send another message, be clear, full and concise.", "Central Command Quantum Messaging") as null|message) + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + return + if(length(input) < COMM_CCMSGLEN_MINIMUM) + to_chat(usr, "Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.") + return + CentCom_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_game("[key_name(usr)] has made an IA [using_map.boss_short] announcement: [input]") + centcomm_message_cooldown = world.time + 300 // 30 seconds + setMenuState(usr, COMM_SCREEN_MAIN) + + // OMG SYNDICATE ...LETTERHEAD + if("MessageSyndicate") + if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (emagged)) + if(centcomm_message_cooldown > world.time) + to_chat(usr, "Arrays recycling. Please stand by.") + return + var/input = sanitize(input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response. There is a 30 second delay before you may send another message, be clear, full and concise.", "To abort, send an empty message.", "")) + if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)) + return + if(length(input) < COMM_CCMSGLEN_MINIMUM) + to_chat(usr, "Message '[input]' is too short. [COMM_CCMSGLEN_MINIMUM] character minimum.") + return + Syndicate_announce(input, usr) + to_chat(usr, "Message transmitted.") + log_game("[key_name(usr)] has made an illegal announcement: [input]") + centcomm_message_cooldown = world.time + 300 // 30 seconds + + if("RestoreBackup") + to_chat(usr, "Backup routing data restored!") + emagged = FALSE + setMenuState(usr, COMM_SCREEN_MAIN) + +/datum/tgui_module/communications/ntos + ntos = TRUE + +/* Etc global procs */ +/proc/enable_prison_shuttle(var/mob/user) + for(var/obj/machinery/computer/prison_shuttle/PS in machines) + PS.allowedtocall = !(PS.allowedtocall) + +/proc/call_shuttle_proc(var/mob/user) + if ((!( ticker ) || !emergency_shuttle.location())) + return + + if(!universe.OnShuttleCall(usr)) + to_chat(user, "Cannot establish a bluespace connection.") + return + + if(deathsquad.deployed) + to_chat(user, "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated.") + return + + if(emergency_shuttle.deny_shuttle) + to_chat(user, "The emergency shuttle may not be sent at this time. Please try again later.") + return + + if(world.time < 6000) // Ten minute grace period to let the game get going without lolmetagaming. -- TLE + to_chat(user, "The emergency shuttle is refueling. Please wait another [round((6000-world.time)/600)] minute\s before trying again.") + return + + if(emergency_shuttle.going_to_centcom()) + to_chat(user, "The emergency shuttle may not be called while returning to [using_map.boss_short].") + return + + if(emergency_shuttle.online()) + to_chat(user, "The emergency shuttle is already on its way.") + return + + if(ticker.mode.name == "blob") + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") + return + + emergency_shuttle.call_evac() + log_game("[key_name(user)] has called the shuttle.") + message_admins("[key_name_admin(user)] has called the shuttle.", 1) + admin_chat_message(message = "Emergency evac beginning! Called by [key_name(user)]!", color = "#CC2222") //VOREStation Add + + return + +/proc/init_shift_change(var/mob/user, var/force = 0) + if ((!( ticker ) || !emergency_shuttle.location())) + return + + if(emergency_shuttle.going_to_centcom()) + to_chat(user, "The shuttle may not be called while returning to [using_map.boss_short].") + return + + if(emergency_shuttle.online()) + to_chat(user, "The shuttle is already on its way.") + return + + // if force is 0, some things may stop the shuttle call + if(!force) + if(emergency_shuttle.deny_shuttle) + to_chat(user, "[using_map.boss_short] does not currently have a shuttle available in your sector. Please try again later.") + return + + if(deathsquad.deployed == 1) + to_chat(user, "[using_map.boss_short] will not allow the shuttle to be called. Consider all contracts terminated.") + return + + if(world.time < 54000) // 30 minute grace period to let the game get going + to_chat(user, "The shuttle is refueling. Please wait another [round((54000-world.time)/60)] minutes before trying again.") + return + + if(ticker.mode.auto_recall_shuttle) + //New version pretends to call the shuttle but cause the shuttle to return after a random duration. + emergency_shuttle.auto_recall = 1 + + if(ticker.mode.name == "blob" || ticker.mode.name == "epidemic") + to_chat(user, "Under directive 7-10, [station_name()] is quarantined until further notice.") + return + + emergency_shuttle.call_transfer() + + //delay events in case of an autotransfer + if (isnull(user)) + SSevents.delay_events(EVENT_LEVEL_MODERATE, 9000) //15 minutes + SSevents.delay_events(EVENT_LEVEL_MAJOR, 9000) + + log_game("[user? key_name(user) : "Autotransfer"] has called the shuttle.") + message_admins("[user? key_name_admin(user) : "Autotransfer"] has called the shuttle.", 1) + admin_chat_message(message = "Autotransfer shuttle dispatched, shift ending soon.", color = "#2277BB") //VOREStation Add + + return + +/proc/cancel_call_proc(var/mob/user) + if (!( ticker ) || !emergency_shuttle.can_recall()) + return + if((ticker.mode.name == "blob")||(ticker.mode.name == "Meteor")) + return + + if(!emergency_shuttle.going_to_centcom()) //check that shuttle isn't already heading to CentCom + emergency_shuttle.recall() + log_game("[key_name(user)] has recalled the shuttle.") + message_admins("[key_name_admin(user)] has recalled the shuttle.", 1) + return + +/proc/is_relay_online() + for(var/obj/machinery/telecomms/relay/M in world) + if(M.stat == 0) + return 1 + return 0 diff --git a/code/modules/tgui/modules/crew_manifest.dm b/code/modules/tgui/modules/crew_manifest.dm new file mode 100644 index 0000000000..7c4daf7f2a --- /dev/null +++ b/code/modules/tgui/modules/crew_manifest.dm @@ -0,0 +1,14 @@ +/datum/tgui_module/crew_manifest + name = "Crew Manifest" + tgui_id = "CrewManifest" + +/datum/tgui_module/crew_manifest/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + if(data_core) + data_core.get_manifest_list() + data["manifest"] = PDA_Manifest + return data + +/datum/tgui_module/crew_manifest/robot +/datum/tgui_module/crew_manifest/robot/tgui_state(mob/user) + return GLOB.tgui_self_state \ No newline at end of file diff --git a/code/modules/tgui/modules/crew_monitor.dm b/code/modules/tgui/modules/crew_monitor.dm index 9d4af68652..d24cd3414a 100644 --- a/code/modules/tgui/modules/crew_monitor.dm +++ b/code/modules/tgui/modules/crew_monitor.dm @@ -2,6 +2,11 @@ name = "Crew monitor" tgui_id = "CrewMonitor" +/datum/tgui_module/crew_monitor/ui_assets(mob/user) + return list( + get_asset_datum(/datum/asset/simple/nanomaps), + ) + /datum/tgui_module/crew_monitor/tgui_act(action, params, datum/tgui/ui) if(..()) return TRUE diff --git a/code/modules/tgui/modules/ntos-only/cardmod.dm b/code/modules/tgui/modules/ntos-only/cardmod.dm new file mode 100644 index 0000000000..c5aecd8808 --- /dev/null +++ b/code/modules/tgui/modules/ntos-only/cardmod.dm @@ -0,0 +1,235 @@ +// This really should be used for both regular ID computers and NTOS, but +// the data structures are just different enough right now that I can't be assed +/datum/tgui_module/cardmod + name = "ID card modification program" + ntos = TRUE + tgui_id = "IdentificationComputer" + var/mod_mode = 1 + var/is_centcom = 0 + +/datum/tgui_module/cardmod/tgui_static_data(mob/user) + var/list/data = ..() + if(data_core) + data_core.get_manifest_list() + data["manifest"] = PDA_Manifest + return data + +/datum/tgui_module/cardmod/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/datum/computer_file/program/card_mod/program = host + if(!istype(program)) + return 0 + var/list/data = ..() + data["station_name"] = station_name() + data["mode"] = mod_mode + data["printing"] = FALSE + if(program && program.computer) + data["have_id_slot"] = !!program.computer.card_slot + data["have_printer"] = !!program.computer.nano_printer + data["authenticated"] = program.can_run(user) + if(!program.computer.card_slot) + mod_mode = 0 //We can't modify IDs when there is no card reader + else + data["have_id_slot"] = 0 + data["have_printer"] = 0 + data["authenticated"] = 0 + data["centcom_access"] = is_centcom + + + data["has_modify"] = null + data["account_number"] = null + data["id_rank"] = null + data["target_owner"] = null + data["target_name"] = null + if(program && program.computer && program.computer.card_slot) + var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card + data["has_modify"] = !!id_card + data["account_number"] = id_card ? id_card.associated_account_number : null + data["id_rank"] = id_card && id_card.assignment ? id_card.assignment : "Unassigned" + data["target_owner"] = id_card && id_card.registered_name ? id_card.registered_name : "-----" + data["target_name"] = id_card ? id_card.name : "-----" + + var/list/departments = list() + for(var/D in SSjob.get_all_department_datums()) + var/datum/department/dept = D + if(!dept.assignable) // No AI ID cards for you. + continue + if(dept.centcom_only && !is_centcom) + continue + departments.Add(list(list( + "department_name" = dept.name, + "jobs" = format_jobs(SSjob.get_job_titles_in_department(dept.name)), + ))) + + data["departments"] = departments + + var/list/all_centcom_access = list() + var/list/regions = list() + if(program.computer.card_slot && program.computer.card_slot.stored_card) + var/obj/item/weapon/card/id/id_card = program.computer.card_slot.stored_card + if(is_centcom) + for(var/access in get_all_centcom_access()) + all_centcom_access.Add(list(list( + "desc" = replacetext(get_centcom_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in id_card.access) ? 1 : 0))) + data["all_centcom_access"] = all_centcom_access + else + for(var/i in ACCESS_REGION_SECURITY to ACCESS_REGION_SUPPLY) + var/list/accesses = list() + for(var/access in get_region_accesses(i)) + if(get_access_desc(access)) + accesses.Add(list(list( + "desc" = replacetext(get_access_desc(access), " ", " "), + "ref" = access, + "allowed" = (access in id_card.access) ? 1 : 0))) + + regions.Add(list(list( + "name" = get_region_accesses_name(i), + "accesses" = accesses))) + data["regions"] = regions + + data["regions"] = regions + data["all_centcom_access"] = all_centcom_access + + return data + +/datum/tgui_module/cardmod/proc/format_jobs(list/jobs) + var/datum/computer_file/program/card_mod/program = host + if(!istype(program)) + return null + + var/obj/item/weapon/card/id/id_card = program.computer.card_slot ? program.computer.card_slot.stored_card : null + var/list/formatted = list() + for(var/job in jobs) + formatted.Add(list(list( + "display_name" = replacetext(job, " ", " "), + "target_rank" = id_card && id_card.assignment ? id_card.assignment : "Unassigned", + "job" = job))) + + return formatted + +/datum/tgui_module/cardmod/tgui_act(action, list/params, datum/tgui/ui) + if(..()) + return TRUE + + + var/datum/computer_file/program/card_mod/program = host + if(!istype(program)) + return TRUE + var/obj/item/modular_computer/computer = tgui_host() + if(!istype(computer)) + return TRUE + + var/obj/item/weapon/card/id/user_id_card = usr.GetIdCard() + var/obj/item/weapon/card/id/id_card + if(computer.card_slot) + id_card = computer.card_slot.stored_card + + switch(action) + if("mode") + mod_mode = clamp(text2num(params["mode_target"]), 0, 1) + . = TRUE + if("print") + if(computer && computer.nano_printer) //This option should never be called if there is no printer + if(!mod_mode) + if(program.can_run(usr, 1)) + var/contents = {"

Access Report

+ Prepared By: [user_id_card.registered_name ? user_id_card.registered_name : "Unknown"]
+ For: [id_card.registered_name ? id_card.registered_name : "Unregistered"]
+
+ Assignment: [id_card.assignment]
+ Account Number: #[id_card.associated_account_number]
+ Blood Type: [id_card.blood_type]

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

Crew Manifest

+
+ [data_core ? data_core.get_manifest(0) : ""] + "} + if(!computer.nano_printer.print_text(contents,text("crew manifest ([])", stationtime2text()))) + to_chat(usr, "Hardware error: Printer was unable to print the file. It may be out of paper.") + return + else + computer.visible_message("\The [computer] prints out paper.") + . = TRUE + if("modify") + if(computer && computer.card_slot) + if(id_card) + data_core.manifest_modify(id_card.registered_name, id_card.assignment) + computer.proc_eject_id(usr) + . = TRUE + if("terminate") + if(computer && program.can_run(usr, 1)) + id_card.assignment = "Dismissed" //VOREStation Edit: setting adjustment + id_card.access = list() + callHook("terminate_employee", list(id_card)) + . = TRUE + if("reg") + if(computer && program.can_run(usr, 1)) + var/temp_name = sanitizeName(params["reg"], allow_numbers = TRUE) + if(temp_name) + id_card.registered_name = temp_name + else + computer.visible_message("[computer] buzzes rudely.") + . = TRUE + if("account") + if(computer && program.can_run(usr, 1)) + var/account_num = text2num(params["account"]) + id_card.associated_account_number = account_num + . = TRUE + if("assign") + if(computer && program.can_run(usr, 1) && id_card) + var/t1 = params["assign_target"] + if(t1 == "Custom") + var/temp_t = sanitize(input("Enter a custom job assignment.","Assignment", id_card.assignment), 45) + //let custom jobs function as an impromptu alt title, mainly for sechuds + if(temp_t) + id_card.assignment = temp_t + else + var/list/access = list() + if(is_centcom) + access = get_centcom_access(t1) + else + var/datum/job/jobdatum + for(var/jobtype in typesof(/datum/job)) + var/datum/job/J = new jobtype + if(ckey(J.title) == ckey(t1)) + jobdatum = J + break + if(!jobdatum) + to_chat(usr, "No log exists for this job: [t1]") + return + + access = jobdatum.get_access() + + id_card.access = access + id_card.assignment = t1 + id_card.rank = t1 + + callHook("reassign_employee", list(id_card)) + . = TRUE + if("access") + if(computer && program.can_run(usr, 1)) + var/access_type = text2num(params["access_target"]) + var/access_allowed = text2num(params["allowed"]) + if(access_type in get_access_ids(ACCESS_TYPE_STATION|ACCESS_TYPE_CENTCOM)) + id_card.access -= access_type + if(!access_allowed) + id_card.access += access_type + . = TRUE + + if(id_card) + id_card.name = text("[id_card.registered_name]'s ID Card ([id_card.assignment])") + diff --git a/code/modules/tgui/modules/ntos-only/configurator.dm b/code/modules/tgui/modules/ntos-only/configurator.dm new file mode 100644 index 0000000000..263409f5e3 --- /dev/null +++ b/code/modules/tgui/modules/ntos-only/configurator.dm @@ -0,0 +1,48 @@ +/datum/tgui_module/computer_configurator + name = "NTOS Computer Configuration Tool" + ntos = TRUE + tgui_id = "Configuration" + var/obj/item/modular_computer/movable = null + +/datum/tgui_module/computer_configurator/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + movable = tgui_host() + // No computer connection, we can't get data from that. + if(!istype(movable)) + return 0 + + var/list/data = ..() + + data["disk_size"] = movable.hard_drive.max_capacity + data["disk_used"] = movable.hard_drive.used_capacity + data["power_usage"] = movable.last_power_usage + data["battery_exists"] = movable.battery_module ? 1 : 0 + if(movable.battery_module) + data["battery_rating"] = movable.battery_module.battery.maxcharge + data["battery_percent"] = round(movable.battery_module.battery.percent()) + + if(movable.battery_module && movable.battery_module.battery) + data["battery"] = list("max" = movable.battery_module.battery.maxcharge, "charge" = round(movable.battery_module.battery.charge)) + + var/list/hardware = movable.get_all_components() + var/list/all_entries[0] + for(var/obj/item/weapon/computer_hardware/H in hardware) + all_entries.Add(list(list( + "name" = H.name, + "desc" = H.desc, + "enabled" = H.enabled, + "critical" = H.critical, + "powerusage" = H.power_usage + ))) + + data["hardware"] = all_entries + return data + +/datum/tgui_module/computer_configurator/tgui_act(action, params) + if(..()) + return + switch(action) + if("PC_toggle_component") + var/obj/item/weapon/computer_hardware/H = movable.find_hardware_by_name(params["name"]) + if(H && istype(H)) + H.enabled = !H.enabled + . = TRUE \ No newline at end of file diff --git a/code/modules/tgui/modules/ntos-only/email.dm b/code/modules/tgui/modules/ntos-only/email.dm new file mode 100644 index 0000000000..8d8d1fbbc7 --- /dev/null +++ b/code/modules/tgui/modules/ntos-only/email.dm @@ -0,0 +1,476 @@ +/datum/tgui_module/email_client + name = "Email Client" + tgui_id = "NtosEmailClient" + + var/stored_login = "" + var/stored_password = "" + var/error = "" + + var/msg_title = "" + var/msg_body = "" + var/msg_recipient = "" + var/datum/computer_file/msg_attachment = null + var/folder = "Inbox" + var/addressbook = FALSE + var/new_message = FALSE + + var/last_message_count = 0 // How many messages were there during last check. + var/read_message_count = 0 // How many messages were there when user has last accessed the UI. + + var/datum/computer_file/downloading = null + var/download_progress = 0 + var/download_speed = 0 + + var/datum/computer_file/data/email_account/current_account = null + var/datum/computer_file/data/email_message/current_message = null + +/datum/tgui_module/email_client/proc/log_in() + for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) + if(!account.can_login) + continue + if(account.login == stored_login) + if(account.password == stored_password) + if(account.suspended) + error = "This account has been suspended. Please contact the system administrator for assistance." + return 0 + current_account = account + return 1 + else + error = "Invalid Password" + return 0 + error = "Invalid Login" + return 0 + +// Returns 0 if no new messages were received, 1 if there is an unread message but notification has already been sent. +// and 2 if there is a new message that appeared in this tick (and therefore notification should be sent by the program). +/datum/tgui_module/email_client/proc/check_for_new_messages(var/messages_read = FALSE) + if(!current_account) + return 0 + + var/list/allmails = current_account.all_emails() + + if(allmails.len > last_message_count) + . = 2 + else if(allmails.len > read_message_count) + . = 1 + else + . = 0 + + last_message_count = allmails.len + if(messages_read) + read_message_count = allmails.len + +/datum/tgui_module/email_client/proc/log_out() + current_account = null + downloading = null + download_progress = 0 + last_message_count = 0 + read_message_count = 0 + +/datum/tgui_module/email_client/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + // Password has been changed by other client connected to this email account + if(current_account) + if(current_account.password != stored_password) + log_out() + error = "Invalid Password" + // Banned. + if(current_account.suspended) + log_out() + error = "This account has been suspended. Please contact the system administrator for assistance." + + // So, TGUI has a bug/feature where it just conveniently doesn't bother to clear out old data if it only gets + // a partial data update; as such, we have to make sure to null all of these out ourselves so the UI works properly. + data["accounts"] = null + data["addressbook"] = null + data["cur_attachment_filename"] = null + data["cur_attachment_size"] = null + data["cur_body"] = null + data["cur_hasattachment"] = null + data["cur_source"] = null + data["cur_timestamp"] = null + data["cur_title"] = null + data["cur_uid"] = null + data["current_account"] = null + data["down_filename"] = null + data["down_progress"] = null + data["down_size"] = null + data["down_speed"] = null + data["downloading"] = null + data["error"] = null + data["folder"] = null + data["label_deleted"] = null + data["label_inbox"] = null + data["label_spam"] = null + data["messagecount"] = null + data["messages"] = null + data["msg_attachment_filename"] = null + data["msg_attachment_size"] = null + data["msg_body"] = null + data["msg_hasattachment"] = null + data["msg_recipient"] = null + data["msg_title"] = null + data["new_message"] = null + data["stored_login"] = null + data["stored_password"] = null + + if(error) + data["error"] = error + else if(downloading) + data["downloading"] = 1 + data["down_filename"] = "[downloading.filename].[downloading.filetype]" + data["down_progress"] = download_progress + data["down_size"] = downloading.size + data["down_speed"] = download_speed + + else if(istype(current_account)) + data["current_account"] = current_account.login + if(addressbook) + var/list/all_accounts = list() + for(var/datum/computer_file/data/email_account/account in ntnet_global.email_accounts) + if(!account.can_login) + continue + all_accounts.Add(list(list( + "login" = account.login + ))) + data["addressbook"] = 1 + data["accounts"] = all_accounts + else if(new_message) + data["new_message"] = 1 + data["msg_title"] = msg_title + data["msg_body"] = pencode2html(msg_body) + data["msg_recipient"] = msg_recipient + if(msg_attachment) + data["msg_hasattachment"] = 1 + data["msg_attachment_filename"] = "[msg_attachment.filename].[msg_attachment.filetype]" + data["msg_attachment_size"] = msg_attachment.size + else if (current_message) + data["cur_title"] = current_message.title + data["cur_body"] = pencode2html(current_message.stored_data) + data["cur_timestamp"] = current_message.timestamp + data["cur_source"] = current_message.source + data["cur_uid"] = current_message.uid + if(istype(current_message.attachment)) + data["cur_hasattachment"] = 1 + data["cur_attachment_filename"] = "[current_message.attachment.filename].[current_message.attachment.filetype]" + data["cur_attachment_size"] = current_message.attachment.size + else + data["label_inbox"] = "Inbox ([current_account.inbox.len])" + data["label_spam"] = "Spam ([current_account.spam.len])" + data["label_deleted"] = "Deleted ([current_account.deleted.len])" + var/list/message_source + if(folder == "Inbox") + message_source = current_account.inbox + else if(folder == "Spam") + message_source = current_account.spam + else if(folder == "Deleted") + message_source = current_account.deleted + + if(message_source) + data["folder"] = folder + var/list/all_messages = list() + for(var/datum/computer_file/data/email_message/message in message_source) + all_messages.Add(list(list( + "title" = message.title, + "body" = pencode2html(message.stored_data), + "source" = message.source, + "timestamp" = message.timestamp, + "uid" = message.uid + ))) + data["messages"] = all_messages + data["messagecount"] = all_messages.len + else + data["stored_login"] = stored_login + data["stored_password"] = stars(stored_password, 0) + + return data + +/datum/tgui_module/email_client/proc/find_message_by_fuid(var/fuid) + if(!istype(current_account)) + return + + // params works with strings, so this makes it a bit easier for us + if(istext(fuid)) + fuid = text2num(fuid) + + for(var/datum/computer_file/data/email_message/message in current_account.all_emails()) + if(message.uid == fuid) + return message + +/datum/tgui_module/email_client/proc/clear_message() + new_message = FALSE + msg_title = "" + msg_body = "" + msg_recipient = "" + msg_attachment = null + current_message = null + +/datum/tgui_module/email_client/proc/relayed_process(var/netspeed) + download_speed = netspeed + if(!downloading) + return + download_progress = min(download_progress + netspeed, downloading.size) + if(download_progress >= downloading.size) + var/obj/item/modular_computer/MC = tgui_host() + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" + downloading = null + download_progress = 0 + return 1 + + if(MC.hard_drive.store_file(downloading)) + error = "File successfully downloaded to local device." + else + error = "Error saving file: I/O Error: The hard drive may be full or nonfunctional." + downloading = null + download_progress = 0 + return 1 + + +/datum/tgui_module/email_client/tgui_act(action, params) + if(..()) + return TRUE + + var/mob/living/user = usr + check_for_new_messages(1) // Any actual interaction (button pressing) is considered as acknowledging received message, for the purpose of notification icons. + + switch(action) + if("login") + log_in() + return 1 + + if("logout") + log_out() + return 1 + + if("reset") + error = "" + return 1 + + if("new_message") + new_message = TRUE + return 1 + + if("cancel") + if(addressbook) + addressbook = FALSE + else + clear_message() + return 1 + + if("addressbook") + addressbook = TRUE + return 1 + + if("set_recipient") + msg_recipient = sanitize(params["set_recipient"]) + addressbook = FALSE + return 1 + + if("edit_title") + var/newtitle = sanitize(params["val"], 100) + if(newtitle) + msg_title = newtitle + return 1 + + // This uses similar editing mechanism as the FileManager program, therefore it supports various paper tags and remembers formatting. + if("edit_body") + var/oldtext = html_decode(msg_body) + oldtext = replacetext(oldtext, "\[editorbr\]", "\n") + + var/newtext = sanitize(replacetext(input(usr, "Enter your message. You may use most tags from paper formatting", "Message Editor", oldtext) as message|null, "\n", "\[editorbr\]"), 20000) + if(newtext) + msg_body = newtext + return 1 + + if("edit_recipient") + var/newrecipient = sanitize(params["val"], 100) + if(newrecipient) + msg_recipient = newrecipient + return 1 + + if("edit_login") + var/newlogin = sanitize(params["val"], 100) + if(newlogin) + stored_login = newlogin + return 1 + + if("edit_password") + var/newpass = sanitize(params["val"], 100) + if(newpass) + stored_password = newpass + return 1 + + if("delete") + if(!istype(current_account)) + return 1 + var/datum/computer_file/data/email_message/M = find_message_by_fuid(params["delete"]) + if(!istype(M)) + return 1 + if(folder == "Deleted") + current_account.deleted.Remove(M) + qdel(M) + else + current_account.deleted.Add(M) + current_account.inbox.Remove(M) + current_account.spam.Remove(M) + if(current_message == M) + current_message = null + return 1 + + if("send") + if(!current_account) + return 1 + if((msg_title == "") || (msg_body == "") || (msg_recipient == "")) + error = "Error sending mail: Title or message body is empty!" + return 1 + + var/datum/computer_file/data/email_message/message = new() + message.title = msg_title + message.stored_data = msg_body + message.source = current_account.login + message.attachment = msg_attachment + if(!current_account.send_mail(msg_recipient, message)) + error = "Error sending email: this address doesn't exist." + return 1 + else + error = "Email successfully sent." + clear_message() + return 1 + + if("set_folder") + folder = params["set_folder"] + return 1 + + if("reply") + var/datum/computer_file/data/email_message/M = find_message_by_fuid(params["reply"]) + if(!istype(M)) + return 1 + + new_message = TRUE + msg_recipient = M.source + msg_title = "Re: [M.title]" + msg_body = "\[editorbr\]\[editorbr\]\[editorbr\]\[br\]==============================\[br\]\[editorbr\]" + msg_body += "Received by [current_account.login] at [M.timestamp]\[br\]\[editorbr\][M.stored_data]" + return 1 + + if("view") + var/datum/computer_file/data/email_message/M = find_message_by_fuid(params["view"]) + if(istype(M)) + current_message = M + return 1 + + if("changepassword") + var/oldpassword = sanitize(input(user,"Please enter your old password:", "Password Change"), 100) + if(!oldpassword) + return 1 + var/newpassword1 = sanitize(input(user,"Please enter your new password:", "Password Change"), 100) + if(!newpassword1) + return 1 + var/newpassword2 = sanitize(input(user,"Please re-enter your new password:", "Password Change"), 100) + if(!newpassword2) + return 1 + + if(!istype(current_account)) + error = "Please log in before proceeding." + return 1 + + if(current_account.password != oldpassword) + error = "Incorrect original password" + return 1 + + if(newpassword1 != newpassword2) + error = "The entered passwords do not match." + return 1 + + current_account.password = newpassword1 + stored_password = newpassword1 + error = "Your password has been successfully changed!" + return 1 + + // The following entries are Modular Computer framework only, and therefore won't do anything in other cases (like AI View) + + if("save") + // Fully dependant on modular computers here. + var/obj/item/modular_computer/MC = tgui_host() + + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error exporting file. Are you using a functional and NTOS-compliant device?" + return 1 + + var/filename = sanitize(input(user,"Please specify file name:", "Message export"), 100) + if(!filename) + return 1 + + var/datum/computer_file/data/email_message/M = find_message_by_fuid(params["save"]) + var/datum/computer_file/data/mail = istype(M) ? M.export() : null + if(!istype(mail)) + return 1 + mail.filename = filename + if(!MC.hard_drive || !MC.hard_drive.store_file(mail)) + error = "Internal I/O error when writing file, the hard drive may be full." + else + error = "Email exported successfully" + return 1 + + if("addattachment") + var/obj/item/modular_computer/MC = tgui_host() + msg_attachment = null + + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" + return 1 + + var/list/filenames = list() + for(var/datum/computer_file/CF in MC.hard_drive.stored_files) + if(CF.unsendable) + continue + filenames.Add(CF.filename) + var/picked_file = input(user, "Please pick a file to send as attachment (max 32GQ)") as null|anything in filenames + + if(!picked_file) + return 1 + + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error uploading file. Are you using a functional and NTOSv2-compliant device?" + return 1 + + for(var/datum/computer_file/CF in MC.hard_drive.stored_files) + if(CF.unsendable) + continue + if(CF.filename == picked_file) + msg_attachment = CF.clone() + break + if(!istype(msg_attachment)) + msg_attachment = null + error = "Unknown error when uploading attachment." + return 1 + + if(msg_attachment.size > 32) + error = "Error uploading attachment: File exceeds maximal permitted file size of 32GQ." + msg_attachment = null + else + error = "File [msg_attachment.filename].[msg_attachment.filetype] has been successfully uploaded." + return 1 + + if("downloadattachment") + if(!current_account || !current_message || !current_message.attachment) + return 1 + var/obj/item/modular_computer/MC = tgui_host() + if(!istype(MC) || !MC.hard_drive || !MC.hard_drive.check_functionality()) + error = "Error downloading file. Are you using a functional and NTOSv2-compliant device?" + return 1 + + downloading = current_message.attachment.clone() + download_progress = 0 + return 1 + + if("canceldownload") + downloading = null + download_progress = 0 + return 1 + + if("remove_attachment") + msg_attachment = null + return 1 \ No newline at end of file diff --git a/code/modules/tgui/modules/ntos-only/uav.dm b/code/modules/tgui/modules/ntos-only/uav.dm new file mode 100644 index 0000000000..f27a08eda9 --- /dev/null +++ b/code/modules/tgui/modules/ntos-only/uav.dm @@ -0,0 +1,241 @@ +/datum/tgui_module/uav + name = "UAV Control" + tgui_id = "UAV" + ntos = TRUE + var/obj/item/device/uav/current_uav = null //The UAV we're watching + var/signal_strength = 0 //Our last signal strength report (cached for a few seconds) + var/signal_test_counter = 0 //How long until next signal strength check + var/list/viewers //Who's viewing a UAV through us + var/adhoc_range = 30 //How far we can operate on a UAV without NTnet + +/datum/tgui_module/uav/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + if(current_uav) + if(QDELETED(current_uav)) + set_current(null) + else if(signal_test_counter-- <= 0) + signal_strength = get_signal_to(current_uav) + if(!signal_strength) + set_current(null) + else // Don't reset counter until we find a UAV that's actually in range we can stay connected to + signal_test_counter = 20 + + data["current_uav"] = null + if(current_uav) + data["current_uav"] = list("status" = current_uav.get_status_string(), "power" = current_uav.state == 1 ? 1 : null) + data["signal_strength"] = signal_strength ? signal_strength >= 2 ? "High" : "Low" : "None" + data["in_use"] = LAZYLEN(viewers) + + var/list/paired_map = list() + var/obj/item/modular_computer/mc_host = tgui_host() + if(istype(mc_host)) + for(var/puav in mc_host.paired_uavs) + var/weakref/wr = puav + var/obj/item/device/uav/U = wr.resolve() + paired_map.Add(list(list("name" = "[U ? U.nickname : "!!Missing!!"]", "uavref" = "\ref[U]"))) + + data["paired_uavs"] = paired_map + return data + +/datum/tgui_module/uav/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("switch_uav") + var/obj/item/device/uav/U = locate(params["switch_uav"]) //This is a \ref to the UAV itself + if(!istype(U)) + to_chat(usr,"Something is blocking the connection to that UAV. In-person investigation is required.") + return FALSE + + if(!get_signal_to(U)) + to_chat(usr,"The screen freezes for a moment, before returning to the UAV selection menu. It's not able to connect to that UAV.") + return FALSE + + set_current(U) + return TRUE + + if("del_uav") + var/refstring = params["del_uav"] //This is a \ref to the UAV itself + var/obj/item/modular_computer/mc_host = tgui_host() + //This is so we can really scrape up any weakrefs that can't resolve + for(var/weakref/wr in mc_host.paired_uavs) + if(wr.ref == refstring) + if(current_uav?.weakref == wr) + set_current(null) + LAZYREMOVE(mc_host.paired_uavs, wr) + return TRUE + + if("view_uav") + if(!current_uav) + return FALSE + + if(current_uav.check_eye(usr) < 0) + to_chat(usr,"The screen freezes for a moment, before returning to the UAV selection menu. It's not able to connect to that UAV.") + else + viewing_uav(usr) ? unlook(usr) : look(usr) + return TRUE + + if("power_uav") + if(!current_uav) + return FALSE + else if(current_uav.toggle_power()) + //Clean up viewers faster + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + unlook(M) + return TRUE + +/datum/tgui_module/uav/proc/set_current(var/obj/item/device/uav/U) + if(current_uav == U) + return + + signal_strength = 0 + current_uav = U + + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + if(current_uav) + to_chat(M, "You're disconnected from the UAV's camera!") + unlook(M) + else + look(M) + +//// +//// Finding signal strength between us and the UAV +//// +/datum/tgui_module/uav/proc/get_signal_to(atom/movable/AM) + // Following roughly the ntnet signal levels + // 0 is none + // 1 is weak + // 2 is strong + var/obj/item/modular_computer/host = tgui_host() //Better not add this to anything other than modular computers. + if(!istype(host)) + return + var/our_signal = host.get_ntnet_status() //1 low, 2 good, 3 wired, 0 none + var/their_z = get_z(AM) + + //If we have no NTnet connection don't bother getting theirs + if(!our_signal) + if(get_z(host) == their_z && (get_dist(host, AM) < adhoc_range)) + return 1 //We can connect (with weak signal) in same z without ntnet, within 30 turfs + else + return 0 + + var/list/zlevels_in_range = using_map.get_map_levels(their_z, FALSE) + var/list/zlevels_in_long_range = using_map.get_map_levels(their_z, TRUE, om_range = DEFAULT_OVERMAP_RANGE) - zlevels_in_range + var/their_signal = 0 + for(var/relay in ntnet_global.relays) + var/obj/machinery/ntnet_relay/R = relay + if(!R.operable()) + continue + if(R.z == their_z) + their_signal = 2 + break + if(R.z in zlevels_in_range) + their_signal = 2 + break + if(R.z in zlevels_in_long_range) + their_signal = 1 + break + + if(!their_signal) //They have no NTnet at all + if(get_z(host) == their_z && (get_dist(host, AM) < adhoc_range)) + return 1 //We can connect (with weak signal) in same z without ntnet, within 30 turfs + else + return 0 + else + return max(our_signal, their_signal) + +/* All handling viewers */ +/datum/tgui_module/uav/Destroy() + if(LAZYLEN(viewers)) + for(var/weakref/W in viewers) + var/M = W.resolve() + if(M) + unlook(M) + . = ..() + +/datum/tgui_module/uav/tgui_status(mob/user) + . = ..() + if(. > STATUS_DISABLED) + if(viewing_uav(user)) + look(user) + return + unlook(user) + +/datum/tgui_module/uav/tgui_close(mob/user) + . = ..() + unlook(user) + +/datum/tgui_module/uav/proc/viewing_uav(mob/user) + return (weakref(user) in viewers) + +/datum/tgui_module/uav/proc/look(mob/user) + if(issilicon(user)) //Too complicated for me to want to mess with at the moment + to_chat(user, "Regulations prevent you from controlling several corporeal forms at the same time!") + return + + if(!current_uav) + return + + if(user.machine != tgui_host()) + user.set_machine(tgui_host()) + user.reset_view(current_uav) + current_uav.add_master(user) + LAZYDISTINCTADD(viewers, weakref(user)) + +/datum/tgui_module/uav/proc/unlook(mob/user) + user.unset_machine() + user.reset_view() + if(current_uav) + current_uav.remove_master(user) + LAZYREMOVE(viewers, weakref(user)) + +/datum/tgui_module/uav/check_eye(mob/user) + if(get_dist(user, tgui_host()) > 1 || user.blinded || !current_uav) + unlook(user) + return -1 + + var/viewflag = current_uav.check_eye(user) + if(viewflag < 0) //camera doesn't work + unlook(user) + return -1 + + return viewflag + +//// +//// Relaying movements to the UAV +//// +/datum/tgui_module/uav/relaymove(var/mob/user, direction) + if(current_uav) + return current_uav.relaymove(user, direction, signal_strength) + +//// +//// The effects when looking through a UAV +//// +/datum/tgui_module/uav/apply_visual(mob/M) + if(!M.client) + return + if(weakref(M) in viewers) + M.overlay_fullscreen("fishbed",/obj/screen/fullscreen/fishbed) + M.overlay_fullscreen("scanlines",/obj/screen/fullscreen/scanline) + + if(signal_strength <= 1) + M.overlay_fullscreen("whitenoise",/obj/screen/fullscreen/noise) + else + M.clear_fullscreen("whitenoise", 0) + else + remove_visual(M) + +/datum/tgui_module/uav/remove_visual(mob/M) + if(!M.client) + return + M.clear_fullscreen("fishbed",0) + M.clear_fullscreen("scanlines",0) + M.clear_fullscreen("whitenoise",0) diff --git a/code/modules/tgui/modules/teleporter.dm b/code/modules/tgui/modules/teleporter.dm new file mode 100644 index 0000000000..05529390fe --- /dev/null +++ b/code/modules/tgui/modules/teleporter.dm @@ -0,0 +1,85 @@ +/datum/tgui_module/teleport_control + name = "Teleporter Control" + tgui_id = "Teleporter" + var/locked_name = "Not Locked" + var/obj/item/locked = null + var/obj/machinery/teleport/station/station = null + var/obj/machinery/teleport/hub/hub = null + +/datum/tgui_module/teleport_control/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["locked_name"] = locked_name || "No Target" + data["station_connected"] = !!station + data["hub_connected"] = !!hub + data["calibrated"] = hub?.accurate + data["teleporter_on"] = station?.engaged + + return data + +/datum/tgui_module/teleport_control/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) + if(..()) + return TRUE + + switch(action) + if("select_target") + var/list/L = list() + var/list/areaindex = list() + + for(var/obj/item/device/radio/beacon/R in all_beacons) + var/turf/T = get_turf(R) + if(!T) + continue + if(!(T.z in using_map.player_levels)) + continue + var/tmpname = T.loc.name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = R + + for(var/obj/item/weapon/implant/tracking/I in all_tracking_implants) + if(!I.implanted || !ismob(I.loc)) + continue + else + var/mob/M = I.loc + if(M.stat == 2) + if(M.timeofdeath + 6000 < world.time) + continue + var/turf/T = get_turf(M) + if(T) + continue + if(!(T in using_map.station_levels)) + continue + var/tmpname = M.real_name + if(areaindex[tmpname]) + tmpname = "[tmpname] ([++areaindex[tmpname]])" + else + areaindex[tmpname] = 1 + L[tmpname] = I + + var/desc = input("Please select a location to lock in.", "Locking Menu") in L|null + if(!desc) + return FALSE + if(tgui_status(usr, state) != STATUS_INTERACTIVE) + return FALSE + + locked = L[desc] + locked_name = desc + return TRUE + + if("test_fire") + station?.testfire() + return TRUE + + if("toggle_on") + if(!station) + return FALSE + + if(station.engaged) + station.disengage() + else + station.engage() + + return TRUE diff --git a/code/modules/tgui/states.dm b/code/modules/tgui/states.dm index 111ee9e30b..b2208e4f99 100644 --- a/code/modules/tgui/states.dm +++ b/code/modules/tgui/states.dm @@ -123,3 +123,7 @@ if((TK in mutations) && (get_dist(src, src_object) <= 2)) return STATUS_INTERACTIVE return ..() + +// Topic Extensions for old UIs +/datum/proc/CanUseTopic(var/mob/user, var/datum/tgui_state/state) + return tgui_status(user, state) \ No newline at end of file diff --git a/code/modules/tgui/states/deep_inventory.dm b/code/modules/tgui/states/deep_inventory.dm index 137f262a0e..1441987453 100644 --- a/code/modules/tgui/states/deep_inventory.dm +++ b/code/modules/tgui/states/deep_inventory.dm @@ -10,3 +10,11 @@ GLOBAL_DATUM_INIT(tgui_deep_inventory_state, /datum/tgui_state/deep_inventory_st if(!user.contains(src_object)) return STATUS_CLOSE return user.shared_tgui_interaction(src_object) + +/atom/proc/contains(var/atom/location) + if(!location) + return 0 + if(location == src) + return 1 + + return contains(location.loc) diff --git a/code/modules/tgui/states/default.dm b/code/modules/tgui/states/default.dm index ba8d132e0b..a69295452b 100644 --- a/code/modules/tgui/states/default.dm +++ b/code/modules/tgui/states/default.dm @@ -68,12 +68,12 @@ GLOBAL_DATUM_INIT(tgui_default_state, /datum/tgui_state/default, new) /mob/living/silicon/pai/default_can_use_tgui_topic(src_object) // pAIs can only use themselves and the owner's radio. - if((src_object == src || src_object == radio) && !stat) + if((src_object == src || src_object == radio || src_object == communicator) && !stat) return STATUS_INTERACTIVE else return ..() /mob/observer/dead/default_can_use_tgui_topic() - if(check_rights(R_ADMIN, 0, src)) + if(can_admin_interact()) return STATUS_INTERACTIVE // Admins are more equal return STATUS_UPDATE // Ghosts can view updates diff --git a/code/modules/tgui/states/inventory_vr.dm b/code/modules/tgui/states/inventory_vr.dm index b8e3ea224c..bc0fe2c3e3 100644 --- a/code/modules/tgui/states/inventory_vr.dm +++ b/code/modules/tgui/states/inventory_vr.dm @@ -16,6 +16,21 @@ GLOBAL_DATUM_INIT(tgui_nif_state, /datum/tgui_state/nif_state, new) return STATUS_CLOSE +// This is slightly distinct from the module state, as it wants to update if not working +GLOBAL_DATUM_INIT(tgui_nif_main_state, /datum/tgui_state/nif_main_state, new) +/datum/tgui_state/nif_main_state/can_use_topic(var/src_object, var/mob/user) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(!H.nif || src_object != H.nif) + return STATUS_CLOSE + + if(H.nif.stat == NIF_WORKING) + return user.shared_tgui_interaction() + else + return min(user.shared_tgui_interaction(), STATUS_UPDATE) + + return STATUS_CLOSE + GLOBAL_DATUM_INIT(tgui_commlink_state, /datum/tgui_state/commlink_state, new) /datum/tgui_state/commlink_state/can_use_topic(var/src_object, var/mob/user) if(ishuman(user)) diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 2f928ae6fa..eb128a129b 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -114,7 +114,6 @@ closing = TRUE for(var/datum/tgui/child in children) child.close(can_be_suspended, logout) - children.Cut() // If we don't have window_id, open proc did not have the opportunity // to finish, therefore it's safe to skip this whole block. if(window) @@ -126,6 +125,8 @@ src_object.tgui_close(user) SStgui.on_close(src) state = null + if(parent_ui) + parent_ui.children -= src parent_ui = null qdel(src) diff --git a/code/modules/vore/appearance/sprite_accessories_taur_vr.dm b/code/modules/vore/appearance/sprite_accessories_taur_vr.dm index 26c760efdb..4257afa1d6 100644 --- a/code/modules/vore/appearance/sprite_accessories_taur_vr.dm +++ b/code/modules/vore/appearance/sprite_accessories_taur_vr.dm @@ -126,7 +126,7 @@ var/icon/suit_sprites = null //File for suit sprites, if any. var/icon/under_sprites = null - var/icon_sprite_tag // This is where we put stuff like _Horse, so we can assign icons easier. + var/icon_sprite_tag // This is where we put stuff like _Horse, so we can assign icons easier. var/can_ride = 1 //whether we're real rideable taur or just in that category @@ -190,6 +190,12 @@ extra_overlay = "fatwolf_markings" //icon_sprite_tag = "fatwolf2c" +/datum/sprite_accessory/tail/taur/wolf/skunk + name = "Skunk (Taur)" + icon_state = "skunk_s" + extra_overlay = "skunk_markings" + icon_sprite_tag = "skunk" + /datum/sprite_accessory/tail/taur/wolf/synthwolf name = "SynthWolf dual-color (Taur)" icon_state = "synthwolf_s" diff --git a/code/modules/vore/appearance/sprite_accessories_vr.dm b/code/modules/vore/appearance/sprite_accessories_vr.dm index fcb77a11e7..777b5fe390 100644 --- a/code/modules/vore/appearance/sprite_accessories_vr.dm +++ b/code/modules/vore/appearance/sprite_accessories_vr.dm @@ -43,6 +43,21 @@ // Ears avaliable to anyone +/datum/sprite_accessory/ears/hyena + name = "hyena ears, dual-color" + desc = "" + icon_state = "hyena" + do_colouration = 1 + color_blend_mode = ICON_MULTIPLY + extra_overlay = "hyena-inner" + +/datum/sprite_accessory/ears/moth + name = "moth antennae" + desc = "" + icon_state = "moth" + do_colouration = 1 + color_blend_mode = ICON_MULTIPLY + /datum/sprite_accessory/ears/squirrel_orange name = "squirel, orange" desc = "" diff --git a/code/modules/vore/fluffstuff/custom_items_vr.dm b/code/modules/vore/fluffstuff/custom_items_vr.dm index e445cda2fc..b9165442f3 100644 --- a/code/modules/vore/fluffstuff/custom_items_vr.dm +++ b/code/modules/vore/fluffstuff/custom_items_vr.dm @@ -1278,3 +1278,13 @@ /obj/item/clothing/accessory/solgov/department/security/aurora name = "Old security insignia" desc = "Insignia denoting assignment to the security department. These fit Expeditionary Corps uniforms. This one seems to be from the 2100s..." + +//Tigercat2000 - Shadow Larkens +/obj/item/modular_computer/laptop/preset/custom_loadout/advanced/shadowlarkens + name = "Shadow's laptop computer" + desc = "A laptop with a different color scheme than usual!" + icon = 'icons/vore/custom_items_vr.dmi' + overlay_icon = 'icons/obj/modular_laptop.dmi' + icon_state_unpowered = "shadowlaptop-open" + icon_state = "shadowlaptop-open" + icon_state_closed = "shadowlaptop-closed" \ No newline at end of file diff --git a/config/alienwhitelist.txt b/config/alienwhitelist.txt index acd699571d..fa189b4b0b 100644 --- a/config/alienwhitelist.txt +++ b/config/alienwhitelist.txt @@ -64,3 +64,4 @@ xioen - Xenochimera xonkon - Protean zalvine - Shadekin Empathy zammyman215 - Vox +stackerrobot - Protean diff --git a/html/changelog.html b/html/changelog.html index 7153910a42..1ddf4203de 100644 --- a/html/changelog.html +++ b/html/changelog.html @@ -53,6 +53,52 @@ -->
+

06 September 2020

+

Atermonera updated:

+
    +
  • You can use Move Up/Down to traverse ladders. No popup 'Which way do you want to go?' windows required for bidirectional ladders!.
  • +
+

Killian updated:

+
    +
  • Added a bunch more random spawners for mapping use.
  • +
  • You can now inject reagents directly into a synth's 'blood' stream using syringes and hypos (inc. borg hypos). Taking oil/coolant samples is still impossible.
  • +
+

Kot updated:

+
    +
  • Mecha drills can now butcher dead mobs better. Like gibs and gore and guts stuff.
  • +
+

Lorilili updated:

+
    +
  • You can now pet borgs on help intent and punch on hurt intent. Old behavior is now grab intent.
  • +
+

Lorilili (Port from Aurora) updated:

+
    +
  • Added knee-high and thigh-high jackboots.
  • +
  • Replaced laceup and leather shoes with oxford shoes.
  • +
  • Replaced standard shoe and high-top sprites with newer ones.
  • +
+

Mechoid updated:

+
    +
  • Mapped distillery into Chemistry, beside the Grinder.
  • +
  • Moves wrapper, spacecleaner, and labeller from the grinder table to the Chem locker.
  • +
  • Added distilling recipe for Synthetic Plasma, which acts as a blood-restoration of 1.2 units per synthplas. Also orderable in cargo.
  • +
+

Rykka Stormheart updated:

+
    +
  • Adds Ambience Chance and Repeating Ambience Preferences into Globals, underneath Client FPS
  • +
  • Random-Ambience-Frequency controls how long before it checks if it can play ambience to you again. Setting it to 0 disables the random re-play of ambience.
  • +
  • Ambience Chance affects the % chance to play ambience to your client. It defaults to 35%, but can be set from 0 to 100 to disable it or always play every time you move into an area or have the Random Ambience check called.
  • +
+

Shadow Larkens updated:

+
    +
  • Added the ability to right click and lower preferences for jobs in the Occupation Menu.
  • +
+

SplinterGP updated:

+
    +
  • Adds new hailer masks with quotes with sound, allowing you to use a hailer on a face mask to combine them.
  • +
  • Adds new sound effects for hailer face masks.
  • +
+

20 August 2020

Atermonera updated:

    @@ -65,6 +111,11 @@
  • You can now alt-click to turn on the washing machine.
  • You can now alt-click on a mech while piloting it to toggle strafing.
+

Blinskot updated:

+
    +
  • You can now alt-click on a mech while piloting it to toggle strafing.
  • +
  • You can now alt-click to turn on the washing machine.
  • +

Cerebulon updated: