diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm index 61a57a16fd..54ea62d02e 100644 --- a/code/controllers/subsystems/ticker.dm +++ b/code/controllers/subsystems/ticker.dm @@ -450,7 +450,7 @@ var/global/datum/controller/subsystem/ticker/ticker if(captainless) for(var/mob/M in player_list) if(!istype(M,/mob/new_player)) - to_chat(M, "Colony Directorship not forced on anyone.") + to_chat(M, "Site Management is not forced on anyone.") /datum/controller/subsystem/ticker/proc/declare_completion() diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm index 1307b8f985..69f198bbd4 100644 --- a/code/datums/supplypacks/medical.dm +++ b/code/datums/supplypacks/medical.dm @@ -32,6 +32,13 @@ containertype = /obj/structure/closet/crate/nanomed containername = "BloodPack crate" +/datum/supply_pack/med/synthplas + name = "BloodPack (Synthplas) crate" + contains = list(/obj/item/weapon/reagent_containers/blood/synthplas = 6) + cost = 80 + containertype = /obj/structure/closet/crate/nanomed + containername = "SynthPlas crate" + /datum/supply_pack/med/bodybag name = "Body bag crate" contains = list(/obj/item/weapon/storage/box/bodybags = 3) diff --git a/code/game/gamemodes/events.dm b/code/game/gamemodes/events.dm index d10d41c05a..256afb8ed8 100644 --- a/code/game/gamemodes/events.dm +++ b/code/game/gamemodes/events.dm @@ -297,9 +297,9 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is M.add_ion_law("HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE") if(5) to_chat(M, "
") - to_chat(M, "THE COLONY DIRECTOR, HOP AND HOS ARE [who2]...LAWS UPDATED") + to_chat(M, "THE SITE MANAGER, HOP AND HOS ARE [who2]...LAWS UPDATED") to_chat(M, "
") - M.add_ion_law("THE COLONY DIRECTOR, HOP AND HOS ARE [who2]") + M.add_ion_law("THE SITE MANAGER, HOP AND HOS ARE [who2]") if(6) to_chat(M, "
") to_chat(M, "THE STATION IS BUILT FOR [who2]...LAWS UPDATED") diff --git a/code/game/objects/effects/effect_system.dm b/code/game/objects/effects/effect_system.dm index 48223c67e2..1e804bbb26 100644 --- a/code/game/objects/effects/effect_system.dm +++ b/code/game/objects/effects/effect_system.dm @@ -438,11 +438,15 @@ steam.start() -- spawns the effect if(src.processing) src.processing = 0 spawn(0) - var/turf/T = get_turf(src.holder) + var/turf/T if(istype(holder, /atom/movable)) var/atom/movable/AM = holder if(AM.locs && AM.locs.len) - T = pick(AM.locs) + T = get_turf(pick(AM.locs)) + else + T = get_turf(AM) + else //when would this ever be attached a non-atom/movable? + T = get_turf(src.holder) if(T != src.oldposition) if(isturf(T)) var/obj/effect/effect/ion_trails/I = new /obj/effect/effect/ion_trails(src.oldposition) diff --git a/code/game/objects/structures/crates_lockers/closets/secure/security.dm b/code/game/objects/structures/crates_lockers/closets/secure/security.dm index 65f6009807..376ee1e65a 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/security.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/security.dm @@ -63,7 +63,7 @@ /obj/structure/closet/secure_closet/hos name = "head of security's locker" req_access = list(access_hos) - storage_capacity = 2.5 * MOB_MEDIUM + storage_capacity = 2.6 * MOB_MEDIUM closet_appearance = /decl/closet_appearance/secure_closet/security/hos starts_with = list( @@ -99,7 +99,8 @@ /obj/item/clothing/suit/storage/hooded/wintercoat/security, /obj/item/clothing/shoes/boots/winter/security, /obj/item/device/flashlight/maglight, - /obj/item/clothing/mask/gas/half) + /obj/item/clothing/mask/gas/half, + /obj/item/clothing/mask/gas/sechailer/swat/hos) /obj/structure/closet/secure_closet/hos/Initialize() if(prob(50)) @@ -113,6 +114,7 @@ /obj/structure/closet/secure_closet/warden name = "warden's locker" + storage_capacity = 42 req_access = list(access_armory) closet_appearance = /decl/closet_appearance/secure_closet/security/warden @@ -143,7 +145,8 @@ /obj/item/clothing/shoes/boots/winter/security, /obj/item/device/flashlight/maglight, /obj/item/device/megaphone, - /obj/item/clothing/mask/gas/half) + /obj/item/clothing/mask/gas/half, + /obj/item/clothing/mask/gas/sechailer/swat/warden) /obj/structure/closet/secure_closet/warden/Initialize() if(prob(50)) diff --git a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm index 200d4993b3..5eb36f6dc3 100644 --- a/code/game/objects/structures/crates_lockers/closets/wardrobe.dm +++ b/code/game/objects/structures/crates_lockers/closets/wardrobe.dm @@ -406,7 +406,8 @@ /obj/item/clothing/gloves/swat, /obj/item/clothing/mask/balaclava/tactical, /obj/item/clothing/mask/balaclava, - /obj/item/clothing/mask/bandana/skull = 2) + /obj/item/clothing/mask/bandana/skull = 2, + /obj/item/clothing/mask/gas/sechailer/swat) /obj/structure/closet/wardrobe/suit diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno.dm b/code/modules/client/preference_setup/loadout/loadout_xeno.dm index eabf443bef..7124b55337 100644 --- a/code/modules/client/preference_setup/loadout/loadout_xeno.dm +++ b/code/modules/client/preference_setup/loadout/loadout_xeno.dm @@ -397,6 +397,7 @@ /datum/gear/suit/teshcoat/New() ..() + gear_tweaks += gear_tweak_free_color_choice /datum/gear/suit/teshcoatwhite @@ -417,4 +418,17 @@ /datum/gear/accessory/teshneckscarf/New() ..() - gear_tweaks += gear_tweak_free_color_choice \ No newline at end of file + gear_tweaks += gear_tweak_free_color_choice + +/datum/gear/shoes/toelessjack + display_name = "toe-less jackboots" + path = /obj/item/clothing/shoes/boots/jackboots/toeless + + +/datum/gear/shoes/toelessknee + display_name = "toe-less jackboots, knee-length" + path = /obj/item/clothing/shoes/boots/jackboots/toeless/knee + +/datum/gear/shoes/toelessthigh + display_name = "toe-less jackboots, thigh-length" + path = /obj/item/clothing/shoes/boots/jackboots/toeless/thigh \ No newline at end of file diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm index 594600b601..116a54e494 100644 --- a/code/modules/clothing/masks/gasmask.dm +++ b/code/modules/clothing/masks/gasmask.dm @@ -42,6 +42,23 @@ w_class = ITEMSIZE_SMALL armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0) +//Turn it into a hailer mask +/obj/item/clothing/mask/gas/half/attackby(obj/item/I, mob/user) + if(istype(I, /obj/item/device/hailer)) + playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) + user.drop_item(src) + var/obj/item/clothing/mask/gas/sechailer/N = new /obj/item/clothing/mask/gas/sechailer(src.loc) + N.fingerprints = src.fingerprints + N.fingerprintshidden = src.fingerprintshidden + N.fingerprintslast = src.fingerprintslast + N.suit_fibers = src.suit_fibers + N.hailer = I + I.loc = N + if(!isturf(N.loc)) + user.put_in_hands(N) + qdel(src) + ..() + //Plague Dr suit can be found in clothing/suits/bio.dm /obj/item/clothing/mask/gas/plaguedoctor name = "plague doctor mask" diff --git a/code/modules/clothing/masks/hailer.dm b/code/modules/clothing/masks/hailer.dm new file mode 100644 index 0000000000..a3a37772b3 --- /dev/null +++ b/code/modules/clothing/masks/hailer.dm @@ -0,0 +1,165 @@ +/obj/item/clothing/mask/gas/sechailer + name = "hailer face mask" + desc = "A compact, durable gas mask that can be connected to an air supply. This one possesses a security hailer." + description_info = "This mask has a hailer attached, you can activate it on the button or use the Halt! verb, for switching phrases you can alt+click it or change it using the change phrase verb." + icon_state = "halfgas" + armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0) + action_button_name = "HALT!" + body_parts_covered = FACE + var/obj/item/device/hailer/hailer + var/cooldown = 0 + var/phrase = 1 + var/aggressiveness = 1 + var/safety = 1 + var/list/phrase_list = list( + "halt" = "HALT! HALT! HALT! HALT!", + "bobby" = "Stop in the name of the Law.", + "compliance" = "Compliance is in your best interest.", + "justice" = "Prepare for justice!", + "running" = "Running will only increase your sentence.", + "dontmove" = "Don't move, Creep!", + "floor" = "Down on the floor, Creep!", + "robocop" = "Dead or alive you're coming with me.", + "god" = "God made today for the crooks we could not catch yesterday.", + "freeze" = "Freeze, Scum Bag!", + "imperial" = "Stop right there, criminal scum!", + "bash" = "Stop or I'll bash you.", + "harry" = "Go ahead, make my day.", + "asshole" = "Stop breaking the law, asshole.", + "stfu" = "You have the right to shut the fuck up", + "shutup" = "Shut up crime!", + "super" = "Face the wrath of the golden bolt.", + "dredd" = "I am, the LAW!" + ) + +/obj/item/clothing/mask/gas/sechailer/swat/hos + name = "\improper HOS SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a tan stripe." + icon_state = "hosmask" + + +/obj/item/clothing/mask/gas/sechailer/swat/warden + name = "\improper Warden SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a blue stripe." + icon_state = "wardenmask" + +/obj/item/clothing/mask/gas/sechailer/swat + name = "\improper SWAT mask" + desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000." + icon_state = "officermask" + body_parts_covered = HEAD|FACE|EYES + flags_inv = HIDEFACE|BLOCKHAIR + aggressiveness = 3 + phrase = 12 + + +/obj/item/clothing/mask/gas/sechailer/ui_action_click() + halt() + +/obj/item/clothing/mask/gas/sechailer/AltClick(mob/user) + selectphrase() + +/obj/item/clothing/mask/gas/sechailer/verb/selectphrase() + set name = "Select gas mask phrase" + set category = "Object" + set desc = "Alter the message shouted by your complionator gas mask." + + var/key = phrase_list[phrase] + var/message = phrase_list[key] + + if (!safety) + to_chat(usr, "You set the restrictor to: FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT.") + return + switch(aggressiveness) + if(1) + phrase = (phrase < 6) ? (phrase + 1) : 1 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(usr,"You set the restrictor to: [message]") + if(2) + phrase = (phrase < 11 && phrase >= 7) ? (phrase + 1) : 7 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(usr,"You set the restrictor to: [message]") + if(3) + phrase = (phrase < 18 && phrase >= 12 ) ? (phrase + 1) : 12 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(usr,"You set the restrictor to: [message]") + if(4) + phrase = (phrase < 18 && phrase >= 1 ) ? (phrase + 1) : 1 + key = phrase_list[phrase] + message = phrase_list[key] + to_chat(usr,"You set the restrictor to: [message]") + else + to_chat(usr, "It's broken.") + +/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user) + if(safety) + safety = 0 + to_chat(user, "You silently fry [src]'s vocal circuit with the cryptographic sequencer.") + else + return + +/obj/item/clothing/mask/gas/sechailer/attackby(obj/item/I, mob/user) + if(I.is_screwdriver()) + switch(aggressiveness) + if(1) + to_chat(user, "You set the aggressiveness restrictor to the second position.") + aggressiveness = 2 + phrase = 7 + if(2) + to_chat(user, "You set the aggressiveness restrictor to the third position.") + aggressiveness = 3 + phrase = 13 + if(3) + to_chat(user, "You set the aggressiveness restrictor to the fourth position.") + aggressiveness = 4 + phrase = 1 + if(4) + to_chat(user, "You set the aggressiveness restrictor to the first position.") + aggressiveness = 1 + phrase = 1 + if(5) + to_chat(user, "You adjust the restrictor but nothing happens, probably because its broken.") + if(I.is_wirecutter()) + if(aggressiveness != 5) + to_chat(user, "You broke it!") + aggressiveness = 5 + if(I.is_crowbar()) + if(!hailer) + to_chat(user, "This mask has an integrated hailer, you can't remove it!") + else + var/obj/N = new /obj/item/clothing/mask/gas/half(src.loc) + playsound(src, 'sound/items/Screwdriver.ogg', 50, 1) + N.fingerprints = src.fingerprints + N.fingerprintshidden = src.fingerprintshidden + N.fingerprintslast = src.fingerprintslast + N.suit_fibers = src.suit_fibers + if(!isturf(N.loc)) + user.put_in_hands(hailer) + user.put_in_hands(N) + else + hailer.loc = N.loc + qdel(src) + return + ..() + +/obj/item/clothing/mask/gas/sechailer/verb/halt() + set name = "HALT!" + set category = "Objects" + set desc = "Activate your face mask hailer." + var/key = phrase_list[phrase] + var/message = phrase_list[key] + + if(cooldown < world.time - 35) // A cooldown, to stop people being jerks + if(!safety) + message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT." + usr.visible_message("[usr]'s Compli-o-Nator: [message]") + playsound(src, 'sound/voice/binsult.ogg', 50, 0, 4) //Future sound channel = something like SFX + cooldown = world.time + return + + usr.visible_message("[usr]'s Compli-o-Nator: [message]") + playsound(src, "sound/voice/complionator/[key].ogg", 50, 0, 4) //future sound channel = something like SFX + cooldown = world.time \ No newline at end of file diff --git a/code/modules/clothing/under/xenos/seromi.dm b/code/modules/clothing/under/xenos/seromi.dm index 8da5e52755..71f00f293a 100644 --- a/code/modules/clothing/under/xenos/seromi.dm +++ b/code/modules/clothing/under/xenos/seromi.dm @@ -39,19 +39,19 @@ /obj/item/clothing/under/seromi/smock/dress/science name = "small research dress" - icon_state = "seromi_dress_science" + icon_state = "tesh_dress_science" /obj/item/clothing/under/seromi/smock/dress/security name = "small security dress" - icon_state = "seromi_dress_security" + icon_state = "tesh_dress_security" /obj/item/clothing/under/seromi/smock/dress/engine name = "small engineering dress" - icon_state = "seromi_dress_engine" + icon_state = "tesh_dress_engine" /obj/item/clothing/under/seromi/smock/dress/medical name = "small medical dress" - icon_state = "seromi_dress_medical" + icon_state = "tesh_dress_medical" /obj/item/clothing/under/seromi/smock/uniform name = "small command uniform" diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index 6354baa1f3..3600dd23aa 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -133,9 +133,9 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is M.add_ion_law("HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE") if(5) to_chat(M, "
") - to_chat(M, "THE COLONY DIRECTOR, HOP AND HOS ARE [who2]...LAWS UPDATED") + to_chat(M, "THE SITE MANAGER, HOP AND HOS ARE [who2]...LAWS UPDATED") to_chat(M, "
") - M.add_ion_law("THE COLONY DIRECTOR, HOP AND HOS ARE [who2]") + M.add_ion_law("THE SITE MANAGER, HOP AND HOS ARE [who2]") if(6) to_chat(M, "
") to_chat(M, "THE STATION IS BUILT FOR [who2]...LAWS UPDATED") diff --git a/code/modules/mining/drilling/scanner.dm b/code/modules/mining/drilling/scanner.dm index 31da4a9753..50bf8bdd35 100644 --- a/code/modules/mining/drilling/scanner.dm +++ b/code/modules/mining/drilling/scanner.dm @@ -6,7 +6,7 @@ item_state = "electronic" origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1) matter = list(DEFAULT_WALL_MATERIAL = 150) - var/scan_time = 5 SECONDS + var/scan_time = 2 SECONDS /obj/item/weapon/mining_scanner/attack_self(mob/user as mob) to_chat(user, "You begin sweeping \the [src] about, scanning for metal deposits.") diff --git a/code/modules/mob/new_player/skill.dm b/code/modules/mob/new_player/skill.dm index b2d061d5a6..a24da0e4ba 100644 --- a/code/modules/mob/new_player/skill.dm +++ b/code/modules/mob/new_player/skill.dm @@ -63,7 +63,7 @@ var/global/list/SKILL_PRE = list("Engineer" = SKILL_ENGINEER, "Roboticist" = SKI /datum/skill/knowledge/law ID = "law" name = "Corporate Law" - desc = "Your knowledge of corporate law and procedures. This includes Corporate Regulations, as well as general station rulings and procedures. A low level in this skill is typical for security officers, a high level in this skill is typical for Colony Directors." + desc = "Your knowledge of corporate law and procedures. This includes Corporate Regulations, as well as general station rulings and procedures. A low level in this skill is typical for security officers, a high level in this skill is typical for Site Managers." field = "Security" secondary = 1 diff --git a/code/modules/overmap/ships/computers/ship.dm b/code/modules/overmap/ships/computers/ship.dm index f350aa11f6..9ce2029978 100644 --- a/code/modules/overmap/ships/computers/ship.dm +++ b/code/modules/overmap/ships/computers/ship.dm @@ -71,6 +71,10 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov if(linked) apply_visual(user) user.reset_view(linked) + if(isliving(user)) + var/mob/living/L = user + L.looking_elsewhere = 1 + L.handle_vision() user.set_viewsize(world.view + extra_view) GLOB.moved_event.register(user, src, /obj/machinery/computer/ship/proc/unlook) // TODO GLOB.stat_set_event.register(user, src, /obj/machinery/computer/ship/proc/unlook) @@ -78,6 +82,10 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov /obj/machinery/computer/ship/proc/unlook(var/mob/user) user.reset_view() + if(isliving(user)) + var/mob/living/L = user + L.looking_elsewhere = 0 + L.handle_vision() user.set_viewsize() // reset to default GLOB.moved_event.unregister(user, src, /obj/machinery/computer/ship/proc/unlook) // TODO GLOB.stat_set_event.unregister(user, src, /obj/machinery/computer/ship/proc/unlook) @@ -109,4 +117,4 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov var/M = W.resolve() if(M) unlook(M) - . = ..() \ No newline at end of file + . = ..() diff --git a/code/modules/overmap/spacetravel.dm b/code/modules/overmap/spacetravel.dm index 94da01879f..9ce971f356 100644 --- a/code/modules/overmap/spacetravel.dm +++ b/code/modules/overmap/spacetravel.dm @@ -45,8 +45,29 @@ proc/get_deepspace(x,y) for(var/atom/movable/AM in contents) if(!AM.lost_in_space()) return FALSE + if(has_buckled_mobs()) + for(var/mob/M in buckled_mobs) + if(!M.lost_in_space()) + return FALSE + return TRUE +/obj/item/device/uav/lost_in_space() + if(state == 1) + return FALSE + return ..() + +/obj/machinery/power/supermatter/lost_in_space() + return FALSE + +/obj/singularity/lost_in_space() + return FALSE + +/obj/vehicle/lost_in_space() + if(load && !load.lost_in_space()) + return FALSE + return ..() + /mob/lost_in_space() return isnull(client) diff --git a/code/modules/projectiles/guns/energy/hooklauncher.dm b/code/modules/projectiles/guns/energy/hooklauncher.dm index 2c170544a0..79ddf7443d 100644 --- a/code/modules/projectiles/guns/energy/hooklauncher.dm +++ b/code/modules/projectiles/guns/energy/hooklauncher.dm @@ -5,12 +5,14 @@ /obj/item/weapon/gun/energy/hooklauncher name = "gravity whip" desc = "A large, strange gauntlet." + icon = 'icons/obj/gun2.dmi' icon_state = "gravwhip" item_state = "gravwhip" fire_sound_text = "laser blast" fire_delay = 15 charge_cost = 300 + charge_meter = FALSE cell_type = /obj/item/weapon/cell/device/weapon projectile_type = /obj/item/projectile/energy/hook @@ -29,7 +31,6 @@ cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien battery_lock = TRUE charge_cost = 400 - charge_meter = FALSE projectile_type = /obj/item/projectile/energy/hook/ring diff --git a/code/modules/projectiles/guns/magic.dm b/code/modules/projectiles/guns/magic.dm new file mode 100644 index 0000000000..0b0678894f --- /dev/null +++ b/code/modules/projectiles/guns/magic.dm @@ -0,0 +1,60 @@ +/* + * "Magic" "Guns" + */ + +/obj/item/weapon/gun/magic + name = "staff of nothing" + desc = "This staff is boring to watch because even though it came first you've seen everything it can do in other staves for years." + icon = 'icons/obj/wizard.dmi' + icon_state = "staffofnothing" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_magic.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_magic.dmi', + ) + fire_sound = 'sound/weapons/emitter.ogg' + w_class = ITEMSIZE_HUGE + projectile_type = null + var/checks_antimagic = TRUE + var/max_charges = 6 + var/charges = 0 + var/recharge_rate = 4 + var/charge_tick = 0 + var/can_charge = TRUE + +/obj/item/weapon/gun/magic/consume_next_projectile() + if(checks_antimagic && locate(/obj/item/weapon/nullrod) in usr) return null + if(!ispath(projectile_type)) return null + if(charges <= 0) return null + + charges -= 1 + + return new projectile_type(src) + +/obj/item/weapon/gun/magic/Initialize() + . = ..() + charges = max_charges + if(can_charge) + START_PROCESSING(SSobj, src) + +/obj/item/weapon/gun/magic/Destroy() + if(can_charge) + STOP_PROCESSING(SSobj, src) + return ..() + +/obj/item/weapon/gun/magic/process() + if (charges >= max_charges) + charge_tick = 0 + return + charge_tick++ + if(charge_tick < recharge_rate) + return 0 + charge_tick = 0 + charges++ + return 1 + +/obj/item/weapon/gun/magic/handle_click_empty(mob/user) + if (user) + user.visible_message("*wzhzhzh*", "The [name] whizzles quietly.") + else + src.visible_message("*wzhzh*") + playsound(src, 'sound/weapons/empty.ogg', 100, 1) diff --git a/code/modules/projectiles/guns/magic/fireball.dm b/code/modules/projectiles/guns/magic/fireball.dm new file mode 100644 index 0000000000..4abe3e286e --- /dev/null +++ b/code/modules/projectiles/guns/magic/fireball.dm @@ -0,0 +1,17 @@ + +/obj/item/weapon/gun/magic/firestaff + name = "flaming staff" + desc = "A long, everburning torch." + icon = 'icons/obj/wizard.dmi' + icon_state = "staffoffire" + item_state = "staff" + fire_sound = 'sound/weapons/emitter.ogg' + w_class = ITEMSIZE_HUGE + checks_antimagic = TRUE + max_charges = 6 + charges = 0 + recharge_rate = 4 + charge_tick = 0 + can_charge = TRUE + + projectile_type = /obj/item/projectile/energy/fireball diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 8ef1bfe2a6..6cd885f0c1 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -739,6 +739,8 @@ shot_from = launcher.name silenced = launcher.silenced + if(user) + firer = user return launch_projectile(target, target_zone, user, params, angle_override, forced_spread) diff --git a/code/modules/projectiles/projectile/hook.dm b/code/modules/projectiles/projectile/hook.dm index ee5fd6c471..3e98319ebd 100644 --- a/code/modules/projectiles/projectile/hook.dm +++ b/code/modules/projectiles/projectile/hook.dm @@ -27,13 +27,15 @@ var/list/help_messages = list("slaps", "pokes", "nudges", "bumps", "pinches") var/done_mob_unique = FALSE // Has the projectile already done something to a mob? + var/datum/beam/chain = null + /obj/item/projectile/energy/hook/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0) var/expected_distance = get_dist(target, loc) range = expected_distance // So the hook hits the ground if no mob is hit. target_distance = expected_distance if(firer) // Needed to ensure later checks in impact and on hit function. launcher_intent = firer.a_intent - firer.Beam(src,icon_state=beam_state,icon='icons/effects/beam.dmi',time=60, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=1) + chain = firer.Beam(src,icon_state=beam_state,icon='icons/effects/beam.dmi',time=60, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=1) if(launcher_intent) switch(launcher_intent) @@ -58,10 +60,22 @@ ..() // Does the regular launching stuff. +/obj/item/projectile/energy/hook/after_move() + if(chain) + var/origin_turf = get_turf(firer) + var/target_turf = get_turf(src) + if(!chain.static_beam && (origin_turf != chain.origin_oldloc || target_turf != chain.target_oldloc)) + chain.origin_oldloc = origin_turf //so we don't keep checking against their initial positions, leading to endless Reset()+Draw() calls + chain.target_oldloc = target_turf + chain.Reset() + chain.Draw() + return + /obj/item/projectile/energy/hook/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null) if(..()) perform_intent_unique(target) + /obj/item/projectile/energy/hook/on_impact(var/atom/A) perform_intent_unique(get_turf(A)) diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm new file mode 100644 index 0000000000..9fcc1c1979 --- /dev/null +++ b/code/modules/projectiles/projectile/magic.dm @@ -0,0 +1,20 @@ + +/obj/item/projectile/energy/fireball + name = "fireball" + icon_state = "fireball2" + damage = 15 + damage_type = BURN + check_armour = "bomb" + armor_penetration = 25 // It's a great ball of fire. + + combustion = TRUE + +/obj/item/projectile/energy/fireball/on_hit(var/atom/target, var/blocked = 0) + new /obj/effect/explosion(get_turf(target)) + explosion(target, -1, 0, 2) + ..() + +/obj/item/projectile/energy/fireball/on_impact(var/atom/target) + new /obj/effect/explosion(get_turf(target)) + explosion(target, -1, 0, 2) + ..() diff --git a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm index 9997ac0f1f..7776ff80f5 100644 --- a/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm +++ b/code/modules/reagents/Chemistry-Reagents/Chemistry-Reagents-Core.dm @@ -110,7 +110,7 @@ remove_self(volume) /datum/reagent/blood/synthblood - name = "Synthetic blood" + name = "synthetic blood" id = "synthblood" color = "#999966" volume_mod = 2 @@ -119,8 +119,16 @@ ..() if(data && !data["blood_type"]) data["blood_type"] = "O-" + if(data && data["species"]) + data["species"] = null return +/datum/reagent/blood/synthblood/dilute + name = "synthetic plasma" + id = "synthblood_dilute" + color = "#cacaaf" + volume_mod = 1.2 + // pure concentrated antibodies /datum/reagent/antibodies data = list("antibodies"=list()) diff --git a/code/modules/reagents/distilling/Distilling-Recipes.dm b/code/modules/reagents/distilling/Distilling-Recipes.dm index a8ac2ee975..0736ac7e61 100644 --- a/code/modules/reagents/distilling/Distilling-Recipes.dm +++ b/code/modules/reagents/distilling/Distilling-Recipes.dm @@ -128,6 +128,17 @@ temp_range = list(T0C + 100, T0C + 120) +/datum/chemical_reaction/distilling/synthplas + name = "Distilling Synthplas" + id = "distill_synthplas" + result = "synthblood_dilute" + required_reagents = list("protein" = 2, "antibodies" = 1, "bicaridine" = 1) + result_amount = 3 + + reaction_rate = HALF_LIFE(15) + + temp_range = list(T0C + 110, T0C + 130) + // Alcohol /datum/chemical_reaction/distilling/beer name = "Distilling Beer" diff --git a/code/modules/reagents/distilling/distilling.dm b/code/modules/reagents/distilling/distilling.dm index 645f13c76b..5d37f8510c 100644 --- a/code/modules/reagents/distilling/distilling.dm +++ b/code/modules/reagents/distilling/distilling.dm @@ -19,7 +19,7 @@ var/target_temp = T20C - var/max_temp = T20C + 300 + var/max_temp = T0C + 300 var/min_temp = T0C - 10 var/current_temp = T20C @@ -86,6 +86,16 @@ update_icon() +/obj/machinery/portable_atmospherics/powered/reagent_distillery/RefreshParts() + var/total_laser_rating = 0 + for(var/obj/item/weapon/stock_parts/micro_laser/ML in component_parts) + total_laser_rating += ML.rating + + max_temp = initial(max_temp) + (50 * (total_laser_rating - 1)) + min_temp = max(1, initial(min_temp) - (30 * (total_laser_rating - 1))) + + return + /obj/machinery/portable_atmospherics/powered/reagent_distillery/proc/setup_overlay_vars() overlay_output_beaker = image(icon = src.icon, icon_state = "[base_state]-output") overlay_input_beaker = image(icon = src.icon, icon_state = "[base_state]-input") diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 223583f7dc..693330fef7 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -27,6 +27,7 @@ var/label_text = "" var/blood_type = null + var/reag_id = "blood" /obj/item/weapon/reagent_containers/blood/Initialize() . = ..() @@ -35,7 +36,7 @@ if(blood_type != null) label_text = "[blood_type]" update_iv_label() - reagents.add_reagent("blood", 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=blood_type,"resistances"=null,"trace_chem"=null)) + reagents.add_reagent(reag_id, 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=blood_type,"resistances"=null,"trace_chem"=null)) update_icon() /obj/item/weapon/reagent_containers/blood/on_reagent_change() @@ -95,6 +96,14 @@ /obj/item/weapon/reagent_containers/blood/OMinus blood_type = "O-" +/obj/item/weapon/reagent_containers/blood/synthplas + blood_type = "O-" + reag_id = "synthblood_dilute" + +/obj/item/weapon/reagent_containers/blood/synthblood + blood_type = "O-" + reag_id = "synthblood" + /obj/item/weapon/reagent_containers/blood/empty name = "Empty BloodPack" desc = "Seems pretty useless... Maybe if there were a way to fill it?" diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghypo.dm similarity index 97% rename from code/modules/reagents/reagent_containers/borghydro.dm rename to code/modules/reagents/reagent_containers/borghypo.dm index a08ea1b19b..d8afdf4631 100644 --- a/code/modules/reagents/reagent_containers/borghydro.dm +++ b/code/modules/reagents/reagent_containers/borghypo.dm @@ -1,157 +1,159 @@ -/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 - 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", "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/hypospray.dm b/code/modules/reagents/reagent_containers/hypospray.dm index e674d2493e..b32deb21a3 100644 --- a/code/modules/reagents/reagent_containers/hypospray.dm +++ b/code/modules/reagents/reagent_containers/hypospray.dm @@ -42,9 +42,11 @@ 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(!H.stat) if(H != user) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index f7c1ffc615..5875d7f407 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -169,9 +169,11 @@ 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 + */ var/cycle_time = injtime*0.33 //33% of the time slept between 5u doses var/warmup_time = 0 //0 for containers diff --git a/config/example/config.txt b/config/example/config.txt index 76adb8bd56..dca21e1d4c 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -536,3 +536,6 @@ ENABLE_NIGHT_SHIFTS # ALLOW_BYOND_LINKS # ALLOW_DISCORD_LINKS ALLOW_URL_LINKS + +# Control which submaps are loaded for the Dynamic Engine system +ENGINE_MAP Supermatter Engine,Edison's Bane diff --git a/html/changelogs/Killian - synth injections.yml b/html/changelogs/Killian - synth injections.yml new file mode 100644 index 0000000000..3f32aa9aac --- /dev/null +++ b/html/changelogs/Killian - synth injections.yml @@ -0,0 +1,36 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Killian + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - tweak: "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." diff --git a/html/changelogs/Mechoid - distil4chem.yml b/html/changelogs/Mechoid - distil4chem.yml new file mode 100644 index 0000000000..13b193984e --- /dev/null +++ b/html/changelogs/Mechoid - distil4chem.yml @@ -0,0 +1,38 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: Mechoid + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Added Distillery to Chemistry, beside the Grinder." + - rscadd: "Added distilling recipe for Synthetic Plasma, which acts as a blood-restoration of 1.2 units per synthplas. Also orderable in cargo." + - tweak: "Moves wrapper, spacecleaner, and labeller from the grinder table to the Chem locker." diff --git a/html/changelogs/splintergp - hailermasks.yml b/html/changelogs/splintergp - hailermasks.yml new file mode 100644 index 0000000000..d69f84d5b1 --- /dev/null +++ b/html/changelogs/splintergp - hailermasks.yml @@ -0,0 +1,37 @@ +################################ +# Example Changelog File +# +# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb. +# +# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.) +# When it is, any changes listed below will disappear. +# +# Valid Prefixes: +# bugfix +# wip (For works in progress) +# tweak +# soundadd +# sounddel +# rscadd (general adding of nice things) +# rscdel (general deleting of nice things) +# imageadd +# imagedel +# maptweak +# spellcheck (typo fixes) +# experiment +################################# + +# Your name. +author: SplinterGP + +# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again. +delete-after: True + +# Any changes you've made. See valid prefix list above. +# INDENT WITH TWO SPACES. NOT TABS. SPACES. +# SCREW THIS UP AND IT WON'T WORK. +# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries. +# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog. +changes: + - rscadd: "Adds new hailer masks with quotes with sound, allowing you to use a hailer on a face mask to attach a security hailer into one." + - soundadd: "Adds new sound effects for hailer face masks." diff --git a/icons/mob/back.dmi b/icons/mob/back.dmi index 5555d0e1ad..88f6dbf028 100644 Binary files a/icons/mob/back.dmi and b/icons/mob/back.dmi differ diff --git a/icons/mob/items/lefthand_magic.dmi b/icons/mob/items/lefthand_magic.dmi new file mode 100644 index 0000000000..11552b79ff Binary files /dev/null and b/icons/mob/items/lefthand_magic.dmi differ diff --git a/icons/mob/items/lefthand_melee.dmi b/icons/mob/items/lefthand_melee.dmi index fa92a768f3..6a128a9e57 100644 Binary files a/icons/mob/items/lefthand_melee.dmi and b/icons/mob/items/lefthand_melee.dmi differ diff --git a/icons/mob/items/righthand_magic.dmi b/icons/mob/items/righthand_magic.dmi new file mode 100644 index 0000000000..490deebcf4 Binary files /dev/null and b/icons/mob/items/righthand_magic.dmi differ diff --git a/icons/mob/items/righthand_melee.dmi b/icons/mob/items/righthand_melee.dmi index 916f610112..12f370d048 100644 Binary files a/icons/mob/items/righthand_melee.dmi and b/icons/mob/items/righthand_melee.dmi differ diff --git a/icons/mob/mask.dmi b/icons/mob/mask.dmi index 0fe183dd90..5761446f43 100644 Binary files a/icons/mob/mask.dmi and b/icons/mob/mask.dmi differ diff --git a/icons/mob/species/seromi/masks.dmi b/icons/mob/species/seromi/masks.dmi index 83493f851a..afe787be11 100644 Binary files a/icons/mob/species/seromi/masks.dmi and b/icons/mob/species/seromi/masks.dmi differ diff --git a/icons/mob/species/tajaran/mask.dmi b/icons/mob/species/tajaran/mask.dmi index 0b68e98a67..15f0bf0fe9 100644 Binary files a/icons/mob/species/tajaran/mask.dmi and b/icons/mob/species/tajaran/mask.dmi differ diff --git a/icons/mob/species/vox/masks.dmi b/icons/mob/species/vox/masks.dmi index 6afaa10c22..29b7d76ef9 100644 Binary files a/icons/mob/species/vox/masks.dmi and b/icons/mob/species/vox/masks.dmi differ diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi index 195662d14b..88fa471cfd 100644 Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ diff --git a/icons/obj/gun2.dmi b/icons/obj/gun2.dmi index 38a4148c20..a5c13c95c4 100644 Binary files a/icons/obj/gun2.dmi and b/icons/obj/gun2.dmi differ diff --git a/icons/obj/machines/drone_fab.dmi b/icons/obj/machines/drone_fab.dmi index 066b51c258..2bbef003b8 100644 Binary files a/icons/obj/machines/drone_fab.dmi and b/icons/obj/machines/drone_fab.dmi differ diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi index 40cec418bc..cb79babe03 100644 Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ diff --git a/icons/obj/wizard.dmi b/icons/obj/wizard.dmi index b7d095f87a..0878311519 100644 Binary files a/icons/obj/wizard.dmi and b/icons/obj/wizard.dmi differ diff --git a/maps/southern_cross/southern_cross-1.dmm b/maps/southern_cross/southern_cross-1.dmm index 9ead71a710..4eee56489e 100644 --- a/maps/southern_cross/southern_cross-1.dmm +++ b/maps/southern_cross/southern_cross-1.dmm @@ -6736,7 +6736,7 @@ "cDl" = (/obj/effect/floor_decal/borderfloorwhite{dir = 8},/obj/effect/floor_decal/corner/paleblue/border{dir = 8},/obj/effect/floor_decal/borderfloorwhite/corner2{dir = 10},/obj/effect/floor_decal/corner/paleblue/bordercorner2{dir = 10},/turf/simulated/floor/tiled/white,/area/medical/foyer) "cDm" = (/obj/machinery/atmospherics/unary/vent_scrubber/on{dir = 1},/turf/simulated/floor/tiled/white,/area/medical/foyer) "cDn" = (/obj/structure/bed/chair{dir = 8},/obj/effect/floor_decal/borderfloorwhite{dir = 4},/obj/effect/floor_decal/corner/paleblue/border{dir = 4},/obj/effect/floor_decal/borderfloorwhite/corner2{dir = 5},/obj/effect/floor_decal/corner/paleblue/bordercorner2{dir = 5},/turf/simulated/floor/tiled/white,/area/medical/foyer) -"cDo" = (/obj/structure/table/glass,/obj/item/weapon/packageWrap,/obj/item/weapon/hand_labeler,/obj/item/weapon/reagent_containers/spray/cleaner{desc = "Someone has crossed out the 'Space' from Space Cleaner and written in Chemistry. Scrawled on the back is, 'Okay, whoever filled this with polytrinic acid, it was only funny the first time. It was hard enough replacing the CMO's first cat!'"; name = "Chemistry Cleaner"},/obj/effect/floor_decal/borderfloorwhite{dir = 8},/obj/effect/floor_decal/corner/beige/border{dir = 8},/turf/simulated/floor/tiled/white,/area/medical/chemistry) +"cDo" = (/obj/structure/table/glass,/obj/effect/floor_decal/borderfloorwhite{dir = 8},/obj/effect/floor_decal/corner/beige/border{dir = 8},/obj/machinery/portable_atmospherics/powered/reagent_distillery,/obj/item/weapon/reagent_containers/glass/beaker/large,/turf/simulated/floor/tiled/white,/area/medical/chemistry) "cDp" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/structure/disposalpipe/segment,/turf/simulated/floor/tiled/white,/area/medical/chemistry) "cDq" = (/obj/structure/closet/secure_closet/chemical,/obj/item/weapon/storage/box/pillbottles,/obj/machinery/alarm{dir = 8; pixel_x = 22},/obj/effect/floor_decal/borderfloorwhite{dir = 4},/obj/effect/floor_decal/corner/beige/border{dir = 4},/obj/item/weapon/storage/box/syringes,/obj/item/weapon/tool/screwdriver,/turf/simulated/floor/tiled/white,/area/medical/chemistry) "cDr" = (/obj/structure/bed/chair/wheelchair,/obj/item/device/radio/intercom/department/medbay{dir = 4; pixel_x = -21},/obj/effect/floor_decal/borderfloorwhite{dir = 8},/obj/effect/floor_decal/corner/paleblue/border{dir = 8},/turf/simulated/floor/tiled/white,/area/medical/medbay_primary_storage) @@ -6824,7 +6824,7 @@ "cEV" = (/obj/item/stack/material/phoron,/obj/item/stack/material/phoron,/obj/item/stack/material/phoron,/obj/item/stack/material/phoron,/obj/item/stack/material/phoron,/obj/structure/table/glass,/obj/machinery/light,/obj/effect/floor_decal/borderfloorwhite/corner{dir = 8},/obj/effect/floor_decal/corner/beige/bordercorner{dir = 8},/turf/simulated/floor/tiled/white,/area/medical/chemistry) "cEW" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/structure/cable/green{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/structure/disposalpipe/segment,/obj/effect/floor_decal/steeldecal/steel_decals4{dir = 8},/obj/effect/floor_decal/steeldecal/steel_decals4{dir = 5},/turf/simulated/floor/tiled/white,/area/medical/chemistry) "cEX" = (/obj/structure/cable/green{d1 = 4; d2 = 8; icon_state = "4-8"},/obj/machinery/camera/network/medbay{c_tag = "MED - Chemistry"; dir = 1},/obj/effect/floor_decal/borderfloorwhite/corner,/obj/effect/floor_decal/corner/beige/bordercorner,/turf/simulated/floor/tiled/white,/area/medical/chemistry) -"cEY" = (/obj/structure/closet/wardrobe/chemistry_white,/obj/item/device/radio/headset/headset_med,/obj/machinery/power/apc{name = "south bump"; pixel_y = -24},/obj/machinery/light_switch{pixel_x = 11; pixel_y = -24},/obj/item/device/radio/intercom{dir = 4; name = "Station Intercom (General)"; pixel_x = 21},/obj/structure/cable/green{d2 = 8; icon_state = "0-8"},/obj/item/weapon/storage/box/pillbottles,/obj/effect/floor_decal/borderfloorwhite{dir = 6},/obj/effect/floor_decal/corner/beige/border{dir = 6},/turf/simulated/floor/tiled/white,/area/medical/chemistry) +"cEY" = (/obj/structure/closet/wardrobe/chemistry_white,/obj/item/device/radio/headset/headset_med,/obj/machinery/power/apc{name = "south bump"; pixel_y = -24},/obj/machinery/light_switch{pixel_x = 11; pixel_y = -24},/obj/item/device/radio/intercom{dir = 4; name = "Station Intercom (General)"; pixel_x = 21},/obj/structure/cable/green{d2 = 8; icon_state = "0-8"},/obj/item/weapon/storage/box/pillbottles,/obj/effect/floor_decal/borderfloorwhite{dir = 6},/obj/effect/floor_decal/corner/beige/border{dir = 6},/obj/item/weapon/reagent_containers/spray/cleaner{desc = "Someone has crossed out the 'Space' from Space Cleaner and written in Chemistry. Scrawled on the back is, 'Okay, whoever filled this with polytrinic acid, it was only funny the first time. It was hard enough replacing the CMO's first cat!'"; name = "Chemistry Cleaner"},/obj/item/weapon/hand_labeler,/obj/item/weapon/packageWrap,/turf/simulated/floor/tiled/white,/area/medical/chemistry) "cEZ" = (/obj/machinery/alarm{dir = 4; pixel_x = -22},/turf/simulated/floor,/area/maintenance/cargo) "cFa" = (/obj/structure/cable/green{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/effect/floor_decal/steeldecal/steel_decals4{dir = 8},/obj/effect/floor_decal/steeldecal/steel_decals4{dir = 5},/turf/simulated/floor/tiled/white,/area/medical/medbay_primary_storage) "cFb" = (/obj/structure/table/standard,/obj/item/roller,/obj/item/roller{pixel_y = 8},/obj/item/roller{pixel_y = 16},/obj/machinery/power/apc{dir = 4; name = "east bump"; pixel_x = 24},/obj/machinery/light_switch{name = "light switch "; pixel_x = 36},/obj/structure/cable/green{d2 = 8; icon_state = "0-8"},/obj/structure/disposalpipe/segment,/obj/effect/floor_decal/borderfloorwhite{dir = 4},/obj/effect/floor_decal/corner/paleblue/border{dir = 4},/turf/simulated/floor/tiled/white,/area/medical/medbay_primary_storage) diff --git a/polaris.dme b/polaris.dme index e0272c5c80..946bd6db04 100644 --- a/polaris.dme +++ b/polaris.dme @@ -1593,6 +1593,7 @@ #include "code\modules\clothing\masks\boxing.dm" #include "code\modules\clothing\masks\breath.dm" #include "code\modules\clothing\masks\gasmask.dm" +#include "code\modules\clothing\masks\hailer.dm" #include "code\modules\clothing\masks\miscellaneous.dm" #include "code\modules\clothing\masks\voice.dm" #include "code\modules\clothing\rings\material.dm" @@ -2629,6 +2630,7 @@ #include "code\modules\projectiles\brokenguns\projectile.dm" #include "code\modules\projectiles\guns\energy.dm" #include "code\modules\projectiles\guns\launcher.dm" +#include "code\modules\projectiles\guns\magic.dm" #include "code\modules\projectiles\guns\modular_guns.dm" #include "code\modules\projectiles\guns\projectile.dm" #include "code\modules\projectiles\guns\vox.dm" @@ -2645,6 +2647,7 @@ #include "code\modules\projectiles\guns\launcher\pneumatic.dm" #include "code\modules\projectiles\guns\launcher\rocket.dm" #include "code\modules\projectiles\guns\launcher\syringe_gun.dm" +#include "code\modules\projectiles\guns\magic\fireball.dm" #include "code\modules\projectiles\guns\magnetic\bore.dm" #include "code\modules\projectiles\guns\magnetic\gasthrower.dm" #include "code\modules\projectiles\guns\magnetic\magnetic.dm" @@ -2670,6 +2673,7 @@ #include "code\modules\projectiles\projectile\explosive.dm" #include "code\modules\projectiles\projectile\force.dm" #include "code\modules\projectiles\projectile\hook.dm" +#include "code\modules\projectiles\projectile\magic.dm" #include "code\modules\projectiles\projectile\magnetic.dm" #include "code\modules\projectiles\projectile\pellets.dm" #include "code\modules\projectiles\projectile\scatter.dm" @@ -2729,7 +2733,7 @@ #include "code\modules\reagents\hoses\hose.dm" #include "code\modules\reagents\hoses\hose_connector.dm" #include "code\modules\reagents\reagent_containers\blood_pack.dm" -#include "code\modules\reagents\reagent_containers\borghydro.dm" +#include "code\modules\reagents\reagent_containers\borghypo.dm" #include "code\modules\reagents\reagent_containers\dropper.dm" #include "code\modules\reagents\reagent_containers\glass.dm" #include "code\modules\reagents\reagent_containers\hypospray.dm" diff --git a/sound/voice/complionator/asshole.ogg b/sound/voice/complionator/asshole.ogg new file mode 100644 index 0000000000..9ee40942c4 Binary files /dev/null and b/sound/voice/complionator/asshole.ogg differ diff --git a/sound/voice/complionator/bash.ogg b/sound/voice/complionator/bash.ogg new file mode 100644 index 0000000000..e79a2cd1e1 Binary files /dev/null and b/sound/voice/complionator/bash.ogg differ diff --git a/sound/voice/complionator/bobby.ogg b/sound/voice/complionator/bobby.ogg new file mode 100644 index 0000000000..4bc8f8df94 Binary files /dev/null and b/sound/voice/complionator/bobby.ogg differ diff --git a/sound/voice/complionator/compliance.ogg b/sound/voice/complionator/compliance.ogg new file mode 100644 index 0000000000..fcfb608202 Binary files /dev/null and b/sound/voice/complionator/compliance.ogg differ diff --git a/sound/voice/complionator/dontmove.ogg b/sound/voice/complionator/dontmove.ogg new file mode 100644 index 0000000000..09cefea9d6 Binary files /dev/null and b/sound/voice/complionator/dontmove.ogg differ diff --git a/sound/voice/complionator/dredd.ogg b/sound/voice/complionator/dredd.ogg new file mode 100644 index 0000000000..ab434dbfde Binary files /dev/null and b/sound/voice/complionator/dredd.ogg differ diff --git a/sound/voice/complionator/floor.ogg b/sound/voice/complionator/floor.ogg new file mode 100644 index 0000000000..996dd0f54a Binary files /dev/null and b/sound/voice/complionator/floor.ogg differ diff --git a/sound/voice/complionator/freeze.ogg b/sound/voice/complionator/freeze.ogg new file mode 100644 index 0000000000..d4224be839 Binary files /dev/null and b/sound/voice/complionator/freeze.ogg differ diff --git a/sound/voice/complionator/god.ogg b/sound/voice/complionator/god.ogg new file mode 100644 index 0000000000..8e1a54d268 Binary files /dev/null and b/sound/voice/complionator/god.ogg differ diff --git a/sound/voice/complionator/halt.ogg b/sound/voice/complionator/halt.ogg new file mode 100644 index 0000000000..81563b928f Binary files /dev/null and b/sound/voice/complionator/halt.ogg differ diff --git a/sound/voice/complionator/harry.ogg b/sound/voice/complionator/harry.ogg new file mode 100644 index 0000000000..eae132675e Binary files /dev/null and b/sound/voice/complionator/harry.ogg differ diff --git a/sound/voice/complionator/imperial.ogg b/sound/voice/complionator/imperial.ogg new file mode 100644 index 0000000000..903cf1197c Binary files /dev/null and b/sound/voice/complionator/imperial.ogg differ diff --git a/sound/voice/complionator/justice.ogg b/sound/voice/complionator/justice.ogg new file mode 100644 index 0000000000..2aef43b150 Binary files /dev/null and b/sound/voice/complionator/justice.ogg differ diff --git a/sound/voice/complionator/robocop.ogg b/sound/voice/complionator/robocop.ogg new file mode 100644 index 0000000000..4dec421ed0 Binary files /dev/null and b/sound/voice/complionator/robocop.ogg differ diff --git a/sound/voice/complionator/running.ogg b/sound/voice/complionator/running.ogg new file mode 100644 index 0000000000..3711ce6ccf Binary files /dev/null and b/sound/voice/complionator/running.ogg differ diff --git a/sound/voice/complionator/shutup.ogg b/sound/voice/complionator/shutup.ogg new file mode 100644 index 0000000000..ecd2b02015 Binary files /dev/null and b/sound/voice/complionator/shutup.ogg differ diff --git a/sound/voice/complionator/stfu.ogg b/sound/voice/complionator/stfu.ogg new file mode 100644 index 0000000000..9c3fcf675b Binary files /dev/null and b/sound/voice/complionator/stfu.ogg differ diff --git a/sound/voice/complionator/super.ogg b/sound/voice/complionator/super.ogg new file mode 100644 index 0000000000..8bae406dea Binary files /dev/null and b/sound/voice/complionator/super.ogg differ diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 9fd4db9fd2..fe11b8a34c 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -369,7 +369,7 @@ rules: # max-depth: error ## Enforce a maximum line length max-len: [error, { - code: 80, + code: 120, # Bump to 140 if this is still too in the way ## Ignore imports ignorePattern: '^(import\s.+\sfrom\s|.*require\()', ignoreUrls: true, diff --git a/tgui/docs/tutorial-and-examples.md b/tgui/docs/tutorial-and-examples.md index 00d5546d91..c0e32ebf8e 100644 --- a/tgui/docs/tutorial-and-examples.md +++ b/tgui/docs/tutorial-and-examples.md @@ -77,7 +77,7 @@ input. The input's `action` and `params` are passed to the proc. ```dm /obj/machinery/my_machine/tgui_act(action, params) if(..()) - return + return TRUE if(action == "change_color") var/new_color = params["color"] if(!(color in allowed_coors)) @@ -305,7 +305,7 @@ upon code review): /obj/copypasta/tgui_act(action, params) if(..()) - return + return TRUE switch(action) if("copypasta") var/newvar = params["var"] diff --git a/tgui/packages/common/string.js b/tgui/packages/common/string.js index d05dbfb6fc..a3da0885a1 100644 --- a/tgui/packages/common/string.js +++ b/tgui/packages/common/string.js @@ -91,7 +91,7 @@ export const toTitleCase = str => { return str; } // Handle string - const WORDS_UPPER = ['Id', 'Tv']; + const WORDS_UPPER = ['Id', 'Tv', 'Rcd']; const WORDS_LOWER = [ 'A', 'An', 'And', 'As', 'At', 'But', 'By', 'For', 'For', 'From', 'In', 'Into', 'Near', 'Nor', 'Of', 'On', 'Onto', 'Or', 'The', 'To', 'With', @@ -105,7 +105,7 @@ export const toTitleCase = str => { } for (let word of WORDS_UPPER) { const regex = new RegExp('\\b' + word + '\\b', 'g'); - currentStr = currentStr.replace(regex, str => str.toLowerCase()); + currentStr = currentStr.replace(regex, str => str.toUpperCase()); } return currentStr; }; @@ -120,7 +120,7 @@ export const decodeHtmlEntities = str => { if (!str) { return str; } - const translate_re = /&(nbsp|amp|quot|lt|gt|apos);/g; + const translate_re = /&(nbsp|amp|quot|lt|gt|apos|trade);/g; const translate = { nbsp: ' ', amp: '&', @@ -128,6 +128,7 @@ export const decodeHtmlEntities = str => { lt: '<', gt: '>', apos: '\'', + trade: 'â„¢', }; return str // Newline tags diff --git a/tgui/packages/tgui/components/Box.js b/tgui/packages/tgui/components/Box.js index 7e8aca3c12..a53fa1e8fd 100644 --- a/tgui/packages/tgui/components/Box.js +++ b/tgui/packages/tgui/components/Box.js @@ -73,7 +73,10 @@ const mapColorPropTo = attrName => (style, value) => { const styleMapperByPropName = { // Direct mapping + display: mapRawPropTo('display'), position: mapRawPropTo('position'), + float: mapRawPropTo('float'), + clear: mapRawPropTo('clear'), overflow: mapRawPropTo('overflow'), overflowX: mapRawPropTo('overflow-x'), overflowY: mapRawPropTo('overflow-y'), @@ -99,6 +102,8 @@ const styleMapperByPropName = { opacity: mapRawPropTo('opacity'), textAlign: mapRawPropTo('text-align'), verticalAlign: mapRawPropTo('vertical-align'), + textTransform: mapRawPropTo('text-transform'), + wordWrap: mapRawPropTo('word-wrap'), // Boolean props inline: mapBooleanPropTo('display', 'inline-block'), bold: mapBooleanPropTo('font-weight', 'bold'), @@ -136,6 +141,18 @@ const styleMapperByPropName = { color: mapColorPropTo('color'), textColor: mapColorPropTo('color'), backgroundColor: mapColorPropTo('background-color'), + // Flex props + order: mapRawPropTo('order'), + flexDirection: mapRawPropTo('flex-direction'), + flexGrow: mapRawPropTo('flex-grow'), + flexShrink: mapRawPropTo('flex-shrink'), + flexWrap: mapRawPropTo('flex-wrap'), + flexFlow: mapRawPropTo('flex-flow'), + flexBasis: mapRawPropTo('flex-basis'), + flex: mapRawPropTo('flex'), + alignItems: mapRawPropTo('align-items'), + justifyContent: mapRawPropTo('justify-content'), + alignSelf: mapRawPropTo('align-self'), // Utility props fillPositionedParent: (style, value) => { if (value) { @@ -151,6 +168,9 @@ const styleMapperByPropName = { export const computeBoxProps = props => { const computedProps = {}; const computedStyles = {}; + if (props.double) { + computedStyles["transform"] = "scale(2);"; + } // Compute props for (let propName of Object.keys(props)) { if (propName === 'style') { diff --git a/tgui/packages/tgui/components/Button.js b/tgui/packages/tgui/components/Button.js index bb8b4bcbf0..ced7bd13bd 100644 --- a/tgui/packages/tgui/components/Button.js +++ b/tgui/packages/tgui/components/Button.js @@ -25,10 +25,13 @@ export const Button = props => { selected, tooltip, tooltipPosition, + tooltipScale, ellipsis, content, iconRotation, iconSpin, + iconColor, + iconSize, children, onclick, onClick, @@ -89,14 +92,17 @@ export const Button = props => { + spin={iconSpin} + color={iconColor} + fontSize={iconSize} /> )} {content} {children} {tooltip && ( + position={tooltipPosition} + scale={tooltipScale} /> )} ); diff --git a/tgui/packages/tgui/components/DraggableControl.js b/tgui/packages/tgui/components/DraggableControl.js index c540401b37..a352eda039 100644 --- a/tgui/packages/tgui/components/DraggableControl.js +++ b/tgui/packages/tgui/components/DraggableControl.js @@ -170,6 +170,7 @@ export class DraggableControl extends Component { onDrag, children, // Input props + forcedInputWidth, height, lineHeight, fontSize, @@ -206,6 +207,7 @@ export class DraggableControl extends Component { style={{ display: !editing ? 'none' : undefined, height: height, + width: forcedInputWidth, 'line-height': lineHeight, 'font-size': fontSize, }} diff --git a/tgui/packages/tgui/components/Dropdown.js b/tgui/packages/tgui/components/Dropdown.js index c561a9557e..6296d7c55c 100644 --- a/tgui/packages/tgui/components/Dropdown.js +++ b/tgui/packages/tgui/components/Dropdown.js @@ -52,16 +52,18 @@ export class Dropdown extends Component { {option} )); - ops.unshift(( -
{ - this.setSelected(null); - }}> - -- {placeholder} -- -
- )); + if (placeholder) { + ops.unshift(( +
{ + this.setSelected(null); + }}> + -- {placeholder} -- +
+ )); + } return ops; } @@ -73,6 +75,7 @@ export class Dropdown extends Component { noscroll, nochevron, width, + maxHeight, onClick, selected, disabled, @@ -92,6 +95,7 @@ export class Dropdown extends Component { tabIndex="-1" style={{ 'width': width, + 'max-height': maxHeight, }} className={classes([ noscroll && 'Dropdown__menu-noscroll' || 'Dropdown__menu', diff --git a/tgui/packages/tgui/components/Flex.js b/tgui/packages/tgui/components/Flex.js index 02d2fac314..1a65d9a3ea 100644 --- a/tgui/packages/tgui/components/Flex.js +++ b/tgui/packages/tgui/components/Flex.js @@ -13,9 +13,11 @@ export const computeFlexProps = props => { direction, wrap, align, + alignContent, justify, inline, spacing = 0, + spacingPrecise = 0, ...rest } = props; return { @@ -28,6 +30,7 @@ export const computeFlexProps = props => { ), inline && 'Flex--inline', spacing > 0 && 'Flex--spacing--' + spacing, + spacingPrecise > 0 && 'Flex--spacingPrecise--' + spacingPrecise, className, ]), style: { @@ -35,6 +38,7 @@ export const computeFlexProps = props => { 'flex-direction': direction, 'flex-wrap': wrap, 'align-items': align, + 'align-content': alignContent, 'justify-content': justify, }, ...rest, diff --git a/tgui/packages/tgui/components/Input.js b/tgui/packages/tgui/components/Input.js index 3c73d88a8f..b7f2dcb1f1 100644 --- a/tgui/packages/tgui/components/Input.js +++ b/tgui/packages/tgui/components/Input.js @@ -76,6 +76,11 @@ export class Input extends Component { const input = this.inputRef.current; if (input) { input.value = toInputValue(nextValue); + if (this.props.autofocus) { + input.focus(); + input.selectionStart = 0; + input.selectionEnd = input.value.length; + } } } @@ -104,6 +109,7 @@ export class Input extends Component { value, maxLength, placeholder, + autofocus, ...boxProps } = props; // Box props diff --git a/tgui/packages/tgui/components/Knob.js b/tgui/packages/tgui/components/Knob.js index 4861e71bf3..b8c11cddcc 100644 --- a/tgui/packages/tgui/components/Knob.js +++ b/tgui/packages/tgui/components/Knob.js @@ -21,6 +21,7 @@ export const Knob = props => { const { // Draggable props (passthrough) animated, + forcedInputWidth, format, maxValue, minValue, @@ -40,6 +41,7 @@ export const Knob = props => { size, bipolar, children, + popUpPosition, ...rest } = props; return ( @@ -47,6 +49,7 @@ export const Knob = props => { dragMatrix={[0, -1]} {...{ animated, + forcedInputWidth, format, maxValue, minValue, @@ -107,7 +110,10 @@ export const Knob = props => { {dragging && ( -
+
{displayElement}
)} diff --git a/tgui/packages/tgui/components/LabeledList.js b/tgui/packages/tgui/components/LabeledList.js index e2b515014a..43dbd0d5a3 100644 --- a/tgui/packages/tgui/components/LabeledList.js +++ b/tgui/packages/tgui/components/LabeledList.js @@ -20,6 +20,7 @@ export const LabeledListItem = props => { labelColor = 'label', color, textAlign, + verticalAlign, buttons, content, children, @@ -33,6 +34,7 @@ export const LabeledListItem = props => { { as="td" color={color} textAlign={textAlign} + verticalAlign={verticalAlign} className={classes([ 'LabeledList__cell', 'LabeledList__content', diff --git a/tgui/packages/tgui/components/Modal.js b/tgui/packages/tgui/components/Modal.js index 916150de40..ee700ebd0e 100644 --- a/tgui/packages/tgui/components/Modal.js +++ b/tgui/packages/tgui/components/Modal.js @@ -6,10 +6,21 @@ export const Modal = props => { const { className, children, + onEnter, ...rest } = props; + let handleKeyDown; + if (onEnter) { + handleKeyDown = e => { + let key = e.which || e.keyCode; + if (key === 13) { + onEnter(e); + } + }; + } return ( - +
{ icon, tooltip, color, + onClick, } = props; - const rx = -256 * (zoom - 1) + x * (2 * zoom) - 1.5 * zoom - 3; - const ry = 512 * zoom - (y * 2 * zoom) + zoom - 1.5; + const rx = (x * 4) - 5; + const ry = (y * 4) - 4; + return ( -
- - - - -
+ + + + ); }; diff --git a/tgui/packages/tgui/components/Section.js b/tgui/packages/tgui/components/Section.js index af59bc4022..c5df9f6b5d 100644 --- a/tgui/packages/tgui/components/Section.js +++ b/tgui/packages/tgui/components/Section.js @@ -14,7 +14,11 @@ export const Section = props => { level = 1, buttons, fill, + stretchContents, + noTopPadding, children, + scrollable, + flexGrow, ...rest } = props; const hasTitle = !isFalsy(title) || !isFalsy(buttons); @@ -25,6 +29,8 @@ export const Section = props => { 'Section', 'Section--level--' + level, fill && 'Section--fill', + scrollable && 'Section--scrollable', + flexGrow && 'Section--flex', className, ...computeBoxClassName(rest), ])} @@ -40,7 +46,11 @@ export const Section = props => {
)} {hasContent && ( -
+
{children}
)} diff --git a/tgui/packages/tgui/components/Tooltip.js b/tgui/packages/tgui/components/Tooltip.js index c46f3c45f7..1cc6ed51cc 100644 --- a/tgui/packages/tgui/components/Tooltip.js +++ b/tgui/packages/tgui/components/Tooltip.js @@ -4,6 +4,7 @@ export const Tooltip = props => { const { content, position = 'bottom', + scale, } = props; // Empirically calculated length of the string, // at which tooltip text starts to overflow. @@ -14,6 +15,7 @@ export const Tooltip = props => { 'Tooltip', long && 'Tooltip--long', position && 'Tooltip--' + position, + scale && 'Tooltip--scale--' + Math.floor(scale), ])} data-tooltip={content} /> ); diff --git a/tgui/packages/tgui/components/index.js b/tgui/packages/tgui/components/index.js index 0d2b2ddab6..71fe763ef8 100644 --- a/tgui/packages/tgui/components/index.js +++ b/tgui/packages/tgui/components/index.js @@ -26,4 +26,4 @@ export { Section } from './Section'; export { Slider } from './Slider'; export { Table } from './Table'; export { Tabs } from './Tabs'; -export { Tooltip } from './Tooltip'; +export { Tooltip } from './Tooltip'; \ No newline at end of file diff --git a/tgui/packages/tgui/constants.js b/tgui/packages/tgui/constants.js index 20ba00cb75..c8155e1229 100644 --- a/tgui/packages/tgui/constants.js +++ b/tgui/packages/tgui/constants.js @@ -4,6 +4,10 @@ export const UI_UPDATE = 1; export const UI_DISABLED = 0; export const UI_CLOSE = -1; +// Atmospheric helpers +/** 0.0 Degrees Celsius in Kelvin */ +export const T0C = 273.15; + // All game related colors are stored here export const COLORS = { // Department colors @@ -48,77 +52,105 @@ export const CSS_COLORS = [ 'label', ]; + +// If you ever add a new radio channel, you can either manually update this, or +// go use /client/verb/generate_tgui_radio_constants() in communications.dm. export const RADIO_CHANNELS = [ { - name: 'Syndicate', - freq: 1213, - color: '#a52a2a', + "name": "Mercenary", + "freq": 1213, + "color": "#6D3F40", }, { - name: 'Red Team', - freq: 1215, - color: '#ff4444', + "name": "Raider", + "freq": 1277, + "color": "#6D3F40", }, { - name: 'Blue Team', - freq: 1217, - color: '#3434fd', + "name": "Special Ops", + "freq": 1341, + "color": "#5C5C8A", }, { - name: 'CentCom', - freq: 1337, - color: '#2681a5', + "name": "AI Private", + "freq": 1343, + "color": "#FF00FF", }, { - name: 'Supply', - freq: 1347, - color: '#b88646', + "name": "Response Team", + "freq": 1345, + "color": "#5C5C8A", }, { - name: 'Service', - freq: 1349, - color: '#6ca729', + "name": "Supply", + "freq": 1347, + "color": "#5F4519", }, { - name: 'Science', - freq: 1351, - color: '#c68cfa', + "name": "Service", + "freq": 1349, + "color": "#6eaa2c", }, { - name: 'Command', - freq: 1353, - color: '#5177ff', + "name": "Science", + "freq": 1351, + "color": "#993399", }, { - name: 'Medical', - freq: 1355, - color: '#57b8f0', + "name": "Command", + "freq": 1353, + "color": "#193A7A", }, { - name: 'Engineering', - freq: 1357, - color: '#f37746', + "name": "Medical", + "freq": 1355, + "color": "#008160", }, { - name: 'Security', - freq: 1359, - color: '#dd3535', + "name": "Engineering", + "freq": 1357, + "color": "#A66300", }, { - name: 'AI Private', - freq: 1447, - color: '#d65d95', + "name": "Security", + "freq": 1359, + "color": "#A30000", }, { - name: 'Common', - freq: 1459, - color: '#1ecc43', + "name": "Explorer", + "freq": 1361, + "color": "#555555", + }, + { + "name": "Talon", + "freq": 1363, + "color": "#555555", + }, + { + "name": "Common", + "freq": 1459, + "color": "#008000", + }, + { + "name": "Entertainment", + "freq": 1461, + "color": "#339966", + }, + { + "name": "Security(I)", + "freq": 1475, + "color": "#008000", + }, + { + "name": "Medical(I)", + "freq": 1485, + "color": "#008000", }, ]; const GASES = [ { - 'id': 'o2', + 'id': 'oxygen', 'name': 'Oxygen', 'label': 'Oâ‚‚', 'color': 'blue', @@ -130,15 +162,15 @@ const GASES = [ 'color': 'red', }, { - 'id': 'co2', + 'id': 'carbon dioxide', 'name': 'Carbon Dioxide', 'label': 'COâ‚‚', 'color': 'grey', }, { - 'id': 'plasma', - 'name': 'Plasma', - 'label': 'Plasma', + 'id': 'phoron', + 'name': 'Phoron', + 'label': 'Phoron', 'color': 'pink', }, { @@ -201,6 +233,24 @@ const GASES = [ 'label': 'Hâ‚‚', 'color': 'white', }, + { + 'id': 'other', + 'name': 'Other', + 'label': 'Other', + 'color': 'white', + }, + { + 'id': 'pressure', + 'name': 'Pressure', + 'label': 'Pressure', + 'color': 'average', + }, + { + 'id': 'temperature', + 'name': 'Temperature', + 'label': 'Temperature', + 'color': 'yellow', + }, ]; export const getGasLabel = (gasId, fallbackValue) => { diff --git a/tgui/packages/tgui/format.js b/tgui/packages/tgui/format.js index 1f8420860e..2d5333d4f8 100644 --- a/tgui/packages/tgui/format.js +++ b/tgui/packages/tgui/format.js @@ -94,3 +94,23 @@ export const formatMoney = (value, precision = 0) => { } return result; }; + +export const formatCommaNumber = value => { + if (!Number.isFinite(value)) { + return value; + } + // From http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript + let parts = value.toString().split("."); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ","); + return parts.join("."); +}; + +// Function from https://stackoverflow.com/a/34841026. CC BY-SA 4.0. +export const formatTime = seconds => { + let hours = Math.floor(seconds / 3600); + let minutes = Math.floor(seconds / 60) % 60; + return [hours, minutes, seconds % 60] + .map(v => v < 10 ? "0" + v : v) + .filter((v, i) => v !== "00" || i > 0) + .join(":"); +}; diff --git a/tgui/packages/tgui/hotkeys.js b/tgui/packages/tgui/hotkeys.js index 0beff6b028..2af94daef9 100644 --- a/tgui/packages/tgui/hotkeys.js +++ b/tgui/packages/tgui/hotkeys.js @@ -17,6 +17,10 @@ export const KEY_CTRL = 17; export const KEY_ALT = 18; export const KEY_ESCAPE = 27; export const KEY_SPACE = 32; +export const KEY_ARROWLEFT = 37; +export const KEY_ARROWUP = 38; +export const KEY_ARROWRIGHT = 39; +export const KEY_ARROWDOWN = 40; export const KEY_0 = 48; export const KEY_1 = 49; export const KEY_2 = 50; diff --git a/tgui/packages/tgui/index.js b/tgui/packages/tgui/index.js index 4b8c3f4b61..16720fa2a6 100644 --- a/tgui/packages/tgui/index.js +++ b/tgui/packages/tgui/index.js @@ -43,6 +43,10 @@ let reactRoot; let initialRender = true; const renderLayout = () => { + let loadingRoot = document.getElementById('tgui-loading'); + if (loadingRoot) { + loadingRoot.style.display = 'none'; + } perf.mark('render/start'); const state = store.getState(); const { suspended, assets } = selectBackend(state); diff --git a/tgui/packages/tgui/interfaces/AICard.js b/tgui/packages/tgui/interfaces/AICard.js new file mode 100644 index 0000000000..68ecd1af16 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AICard.js @@ -0,0 +1,122 @@ +import { useBackend } from "../backend"; +import { Button, ProgressBar, LabeledList, Box, Section } from "../components"; +import { Window } from "../layouts"; + +export const AICard = (props, context) => { + const { act, data } = useBackend(context); + + const { + has_ai, + integrity, + backup_capacitor, + flushing, + has_laws, + laws, + wireless, + radio, + } = data; + + if (has_ai === 0) { + return ( + + +
+ +

No AI detected.

+
+
+
+
+ ); + } else { + + let integrityColor = null; // Handles changing color of the integrity bar + if (integrity >= 75) { integrityColor = 'green'; } + else if (integrity >= 25) { integrityColor = 'yellow'; } + else { integrityColor = 'red'; } + + let powerColor = null; + if (backup_capacitor >= 75) { powerColor = 'green'; } + if (backup_capacitor >= 25) { powerColor = 'yellow'; } + else { powerColor = 'red'; } + + return ( + + +
+ +

{name}

+
+ + + + + + + + + + + +

{flushing === 1 ? "Wipe of AI in progress..." : ""}

+
+
+ +
+ {!!has_laws && ( + + {laws.map((value, key) => ( + + {value} + + ))} + + ) || ( // Else, no laws. + +

No laws detected.

+
+ )} +
+ +
+ + +
+
+
+ ); + } +}; diff --git a/tgui/packages/tgui/interfaces/APC.js b/tgui/packages/tgui/interfaces/APC.js new file mode 100644 index 0000000000..1ee7419b47 --- /dev/null +++ b/tgui/packages/tgui/interfaces/APC.js @@ -0,0 +1,288 @@ +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Box, Button, Dimmer, Icon, LabeledList, NoticeBox, ProgressBar, Section, Flex } from '../components'; +import { Window } from '../layouts'; +import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; +import { FullscreenNotice } from './common/FullscreenNotice'; + +export const APC = (props, context) => { + const { act, data } = useBackend(context); + + let body = ; + + if (data.gridCheck) { + body = ; + } else if (data.failTime) { + body = ; + } + + return ( + + + {body} + + + ); +}; + +const powerStatusMap = { + 2: { + color: 'good', + externalPowerText: 'External Power', + chargingText: 'Fully Charged', + }, + 1: { + color: 'average', + externalPowerText: 'Low External Power', + chargingText: 'Charging', + }, + 0: { + color: 'bad', + externalPowerText: 'No External Power', + chargingText: 'Not Charging', + }, +}; + +const malfMap = { + 1: { + icon: 'terminal', + content: 'Override Programming', + action: 'hack', + }, + // 2: { + // icon: 'caret-square-down', + // content: 'Shunt Core Process', + // action: 'occupy', + // }, + // 3: { + // icon: 'caret-square-left', + // content: 'Return to Main Core', + // action: 'deoccupy', + // }, + // 4: { + // icon: 'caret-square-down', + // content: 'Shunt Core Process', + // action: 'occupy', + // }, +}; + +const ApcContent = (props, context) => { + const { act, data } = useBackend(context); + const locked = data.locked && !data.siliconUser; + const normallyLocked = data.normallyLocked; + const externalPowerStatus = powerStatusMap[data.externalPower] + || powerStatusMap[0]; + const chargingStatus = powerStatusMap[data.chargingStatus] + || powerStatusMap[0]; + const channelArray = data.powerChannels || []; + // const malfStatus = malfMap[data.malfStatus] || null; + const adjustedCellChange = data.powerCellStatus / 100; + + return ( + + + Fault in ID authenticator. + Please contact maintenance for service. + + )} /> +
+ + act('breaker')} /> + )}> + [ {externalPowerStatus.externalPowerText} ] + + + + + act('charge')} /> + )}> + [ {chargingStatus.chargingText} ] + + +
+
+ + {channelArray.map(channel => { + const { topicParams } = channel; + return ( + + = 2 ? 'good' : 'bad'}> + {channel.status >= 2 ? 'On' : 'Off'} + +
+
act('overload')} /> + )}> + + act('cover')} /> + )} /> + +
+ + ); +}; + +const GridCheck = (props, context) => { + return ( + + + + + + Power surge detected, grid check in effect... + + + ); +}; + +const ApcFailure = (props, context) => { + const { data, act } = useBackend(context); + + let rebootOptions = ( + + + + + + + + + + ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/AssemblyProx.js b/tgui/packages/tgui/interfaces/AssemblyProx.js new file mode 100644 index 0000000000..d16bc83cad --- /dev/null +++ b/tgui/packages/tgui/interfaces/AssemblyProx.js @@ -0,0 +1,65 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, ProgressBar, NumberInput, Section } from "../components"; +import { Window } from "../layouts"; +import { formatTime } from '../format'; + +export const AssemblyProx = (props, context) => { + const { act, data } = useBackend(context); + const { + timing, + time, + range, + maxRange, + scanning, + } = data; + return ( + + +
+ + act("timing")}> + {timing ? "Counting Down" : "Disabled"} + + }> + formatTime(round(val))} + onDrag={(e, val) => act("set_time", { time: val })} /> + + +
+
+ + + act("range", { range: val })} /> + + + + Movement sensor is active when armed! + + +
+
+
+ ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/AssemblyTimer.js b/tgui/packages/tgui/interfaces/AssemblyTimer.js new file mode 100644 index 0000000000..4a60e668b6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AssemblyTimer.js @@ -0,0 +1,41 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, ProgressBar, NumberInput, Section } from "../components"; +import { Window } from "../layouts"; +import { formatTime } from '../format'; + +export const AssemblyTimer = (props, context) => { + const { act, data } = useBackend(context); + const { + timing, + time, + } = data; + return ( + + +
+ + act("timing")}> + {timing ? "Counting Down" : "Disabled"} + + }> + formatTime(round(val))} + onDrag={(e, val) => act("set_time", { time: val })} /> + + +
+
+
+ ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/AtmosAlertConsole.js b/tgui/packages/tgui/interfaces/AtmosAlertConsole.js new file mode 100644 index 0000000000..cbc290db32 --- /dev/null +++ b/tgui/packages/tgui/interfaces/AtmosAlertConsole.js @@ -0,0 +1,50 @@ +import { useBackend } from '../backend'; +import { Button, Section } from '../components'; +import { Window } from '../layouts'; + +export const AtmosAlertConsole = (props, context) => { + const { act, data } = useBackend(context); + const priorityAlerts = data.priority_alarms || []; + const minorAlerts = data.minor_alarms || []; + return ( + + +
+
    + {priorityAlerts.length === 0 && ( +
  • + No Priority Alerts +
  • + )} + {priorityAlerts.map(alert => ( +
  • +
  • + ))} + {minorAlerts.length === 0 && ( +
  • + No Minor Alerts +
  • + )} + {minorAlerts.map(alert => ( +
  • +
  • + ))} +
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/AtmosControl.js b/tgui/packages/tgui/interfaces/AtmosControl.js new file mode 100644 index 0000000000..30f55f045f --- /dev/null +++ b/tgui/packages/tgui/interfaces/AtmosControl.js @@ -0,0 +1,121 @@ +import { sortBy } from 'common/collections'; +import { Window } from '../layouts'; +import { Fragment } from 'inferno'; +import { Button, Box, NumberInput, Tabs, Icon, Section, NanoMap } from '../components'; +import { useBackend, useLocalState } from '../backend'; +import { createLogger } from '../logging'; +const logger = createLogger("fuck"); + +export const AtmosControl = (props, context) => { + return ( + + + + + + ); +}; + +export const AtmosControlContent = (props, context) => { + const { act, data, config } = useBackend(context); + + let sortedAlarms = sortBy( + alarm => alarm.name + )(data.alarms || []); + + // sortedAlarms = sortedAlarms.slice(1, 3); + + const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0); + const [mapZoom, setZoom] = useLocalState(context, 'mapZoom', 1); + + let body; + // Alarms View + if (tabIndex === 0) { + body = ( +
+ {sortedAlarms.map(alarm => ( +
+ ); + } else if (tabIndex === 1) { + // Please note, if you ever change the zoom values, + // you MUST update styles/components/Tooltip.scss + // and change the @for scss to match. + body = ( + + Zoom Level: + setZoom(value)} /> + Z-Level: + {data.map_levels + .sort((a, b) => Number(a) - Number(b)) + .map(level => ( + + + + {recipe.requirements && ( + Object + .keys(recipe.requirements) + .map(mat => toTitleCase(mat) + ": " + recipe.requirements[mat]) + .join(", ") + ) || ( + + No resources required. + + )} + + + ))} + + + + ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/BeaconLocator.js b/tgui/packages/tgui/interfaces/BeaconLocator.js new file mode 100644 index 0000000000..3996f9033d --- /dev/null +++ b/tgui/packages/tgui/interfaces/BeaconLocator.js @@ -0,0 +1,69 @@ +import { toFixed, round } from 'common/math'; +import { useBackend } from '../backend'; +import { Box, Button, Icon, LabeledList, NumberInput, Section } from '../components'; +import { Window } from '../layouts'; + +export const BeaconLocator = (props, context) => { + const { act, data } = useBackend(context); + + const { + scan_ticks, + degrees, + rawfreq, + minFrequency, + maxFrequency, + } = data; + + return ( + + +
+ {scan_ticks && ( + + Scanning... + + ) || null} + {degrees && ( + + + + + Locked on. Follow the arrow. + + ) || ( + + No lock. + + )} + + + + toFixed(value, 1)} + onDrag={(e, value) => act('setFrequency', { + freq: round(value * 10), + })} /> + + +
+
+
+ ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/Biogenerator.js b/tgui/packages/tgui/interfaces/Biogenerator.js new file mode 100644 index 0000000000..dc4225c1f7 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Biogenerator.js @@ -0,0 +1,212 @@ +import { createSearch } from 'common/string'; +import { Fragment } from 'inferno'; +import { useBackend, useLocalState } from "../backend"; +import { Box, Button, Collapsible, Dropdown, Flex, Input, NoticeBox, Section } from '../components'; +import { Window } from "../layouts"; +import { refocusLayout } from '../layouts'; +import { logger } from '../logging'; + +const sortTypes = { + 'Alphabetical': (a, b) => a - b, + 'By availability': (a, b) => -(a.affordable - b.affordable), + 'By price': (a, b) => a.price - b.price, +}; + +export const Biogenerator = (props, context) => { + const { act, data } = useBackend(context); + return ( + + + {data.processing && ( +
+ The biogenerator is processing reagents! +
+ ) || ( + +
+ {data.points} points available. + + +
+ + +
+ )} +
+
+ ); +}; + + +const BiogeneratorItems = (props, context) => { + const { act, data } = useBackend(context); + const { + points, + items, + } = data; + // Search thingies + const [ + searchText, + _setSearchText, + ] = useLocalState(context, 'search', ''); + const [ + sortOrder, + _setSortOrder, + ] = useLocalState(context, 'sort', 'Alphabetical'); + const [ + descending, + _setDescending, + ] = useLocalState(context, 'descending', false); + const searcher = createSearch(searchText, item => { + return item[0]; + }); + + let has_contents = false; + let contents = Object.entries(items).map((kv, _i) => { + let items_in_cat = Object.entries(kv[1]) + .filter(searcher) + .map(kv2 => { + kv2[1].affordable = points >= (kv2[1].price / data.build_eff); + return kv2[1]; + }) + .sort(sortTypes[sortOrder]); + if (items_in_cat.length === 0) { + return; + } + if (descending) { + items_in_cat = items_in_cat.reverse(); + } + + has_contents = true; + return ( + + ); + }); + return ( + +
refocusLayout()}> + {has_contents + ? contents : ( + + No items matching your criteria was found! + + )} +
+
+ ); +}; + +const BiogeneratorSearch = (props, context) => { + const [ + _searchText, + setSearchText, + ] = useLocalState(context, 'search', ''); + const [ + _sortOrder, + setSortOrder, + ] = useLocalState(context, 'sort', ''); + const [ + descending, + setDescending, + ] = useLocalState(context, 'descending', false); + return ( + + + + setSearchText(value)} + /> + + + setSortOrder(v)} /> + + + + + + )}> + + + {occupant.name} + + + + + + {stats[occupant.stat][1]} + + + °C,  + °F + + + units ( + %) + + {/* VOREStation Add */} + + {round(data.occupant.weight) + "lbs, " + + round(data.occupant.weight/2.20463) + "kgs"} + + {/* VOREStation Add End */} + + + ); +}; + +const BodyScannerMainReagents = props => { + const { + occupant, + } = props; + + return ( + +
+ {occupant.reagents ? ( + + + + Reagent + + + Amount + + + {occupant.reagents.map(reagent => ( + + {reagent.name} + + {reagent.amount} Units { + reagent.overdose + ? OVERDOSING + : null + } + + + ))} +
+ ) : No Blood Reagents Detected} +
+
+ {occupant.ingested ? ( + + + + Reagent + + + Amount + + + {occupant.ingested.map(reagent => ( + + {reagent.name} + + {reagent.amount} Units { + reagent.overdose + ? OVERDOSING + : null + } + + + ))} +
+ ) : No Stomach Reagents Detected} +
+
+ ); +}; + +const BodyScannerMainAbnormalities = props => { + const { + occupant, + } = props; + + let hasAbnormalities = occupant.hasBorer + || occupant.blind + || occupant.colourblind + || occupant.nearsighted + || occupant.hasVirus; + + /* VOREStation Add */ + hasAbnormalities = hasAbnormalities + || occupant.humanPrey + || occupant.livingPrey + || occupant.objectPrey; + /* VOREStation Add End */ + + if (!hasAbnormalities) { + return ( +
+ + No abnormalities found. + +
+ ); + } + + return ( +
+ {abnormalities.map((a, i) => { + if (occupant[a[0]]) { + return ( + + {a[2](occupant)} + + ); + } + })} +
+ ); +}; + +const BodyScannerMainDamage = props => { + const { + occupant, + } = props; + return ( +
+ + {mapTwoByTwo(damages, (d1, d2, i) => ( + + + + {d1[0]}: + + + {!!d2 && d2[0] + ":"} + + + + + + + + {!!d2 && ( + + )} + + + + ))} +
+
+ ); +}; + +const BodyScannerMainDamageBar = props => { + return ( + + {round(props.value, 0)} + + ); +}; + +const BodyScannerMainOrgansExternal = props => { + if (props.organs.length === 0) { + return ( +
+ + N/A + +
+ ); + } + + return ( +
+ + + + Name + + + Damage + + + Injuries + + + {props.organs.map((o, i) => ( + + + {o.name} + + + 0 && "0.5rem"} + value={o.totalLoss / 100} + ranges={damageRange}> + + {!!o.bruteLoss && ( + + + {round(o.bruteLoss, 0)}  + + )} + {!!o.fireLoss && ( + + + {round(o.fireLoss, 0)} + + )} + + + {round(o.totalLoss, 0)} + + + + + + {reduceOrganStatus([ + o.internalBleeding && "Internal bleeding", + !!o.status.bleeding && "External bleeding", + o.lungRuptured && "Ruptured lung", + o.destroyed && "Destroyed", + !!o.status.broken && o.status.broken, + germStatus(o.germ_level), + !!o.open && "Open incision", + ])} + + + {reduceOrganStatus([ + !!o.status.splinted && "Splinted", + !!o.status.robotic && "Robotic", + !!o.status.dead && ( + + DEAD + + ), + ])} + {reduceOrganStatus(o.implants.map( + s => s.known + ? s.name + : "Unknown object" + ))} + + + + ))} +
+
+ ); +}; + +const BodyScannerMainOrgansInternal = props => { + if (props.organs.length === 0) { + return ( +
+ + N/A + +
+ ); + } + + return ( +
+ + + + Name + + + Damage + + + Injuries + + + {props.organs.map((o, i) => ( + + + {o.name} + + + 0 && "0.5rem"} + ranges={damageRange}> + {round(o.damage, 0)} + + + + + {reduceOrganStatus([ + germStatus(o.germ_level), + ])} + + + {reduceOrganStatus([ + (o.robotic === 1) && "Robotic", + (o.robotic === 2) && "Assisted", + !!o.dead && ( + + DEAD + + ), + ])} + + + + ))} +
+
+ ); +}; + +const BodyScannerEmpty = () => { + return ( +
+ + +
+ No occupant detected. +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/BotanyEditor.js b/tgui/packages/tgui/interfaces/BotanyEditor.js new file mode 100644 index 0000000000..8fe3dcc534 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BotanyEditor.js @@ -0,0 +1,85 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, NoticeBox } from "../components"; +import { Window } from "../layouts"; + +export const BotanyEditor = (props, context) => { + const { act, data } = useBackend(context); + + const { + activity, + degradation, + disk, + sourceName, + locus, + loaded, + } = data; + + if (activity) { + return ( + + + Scanning... + + + ); + } + + return ( + + +
+ {disk && ( + + + + {sourceName} + + + {degradation}% + + + {locus} + + + + + ) || ( + No disk loaded. + )} +
+
+ {loaded && ( + + + + {loaded} + + + + + + ) || ( + No target seed packet loaded. + )} +
+
+
+ ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/BotanyIsolator.js b/tgui/packages/tgui/interfaces/BotanyIsolator.js new file mode 100644 index 0000000000..2e56a31ef8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BotanyIsolator.js @@ -0,0 +1,111 @@ +import { useBackend } from "../backend"; +import { Box, Button, LabeledList, Section, NoticeBox } from "../components"; +import { Window } from "../layouts"; + +export const BotanyIsolator = (props, context) => { + const { act, data } = useBackend(context); + + const { + geneMasks, + activity, + degradation, + disk, + loaded, + hasGenetics, + sourceName, + } = data; + + if (activity) { + return ( + + + Scanning... + + + ); + } + + return ( + + +
+ {hasGenetics && ( + + + + {sourceName} + + + {degradation}% + + {disk && (geneMasks.length && geneMasks.map(mask => ( + + + + ))) || null} + + {disk && ( + + + + + ) || ( + No disk inserted. + )} + + ) || ( + + No Data Buffered. + {disk && ( + + ) || ( + No disk inserted. + )} + + )} +
+
+ {loaded && ( + + + + {loaded} + + + + + + ) || ( + No packet loaded. + )} +
+
+
+ ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/BrigTimer.js b/tgui/packages/tgui/interfaces/BrigTimer.js new file mode 100644 index 0000000000..f6461a51e0 --- /dev/null +++ b/tgui/packages/tgui/interfaces/BrigTimer.js @@ -0,0 +1,69 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from '../backend'; +import { Button, Section, NumberInput, Flex } from '../components'; +import { Window } from '../layouts'; +import { formatTime } from '../format'; + +export const BrigTimer = (props, context) => { + const { act, data } = useBackend(context); + return ( + + +
+
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/CameraConsole.js b/tgui/packages/tgui/interfaces/CameraConsole.js index 81ffd5d7b9..78ca9c4bf9 100644 --- a/tgui/packages/tgui/interfaces/CameraConsole.js +++ b/tgui/packages/tgui/interfaces/CameraConsole.js @@ -57,6 +57,7 @@ export const CameraConsole = (props, context) => { export const CameraConsoleContent = (props, context) => { const { act, data, config } = useBackend(context); + const { mapRef, activeCamera } = data; const cameras = selectCameras(data.cameras); const [ @@ -80,16 +81,16 @@ export const CameraConsoleContent = (props, context) => {
{ + const { act, data } = useBackend(context); + const { + connected, + can_relabel, + pressure, + releasePressure, + defaultReleasePressure, + minReleasePressure, + maxReleasePressure, + valveOpen, + holding, + } = data; + return ( + + +
act('relabel')} /> + )}> + + + { + if (value < 10000) { + return toFixed(value) + ' kPa'; + } + return formatSiUnit(value * 1000, 1, 'Pa'); + }} /> + + + + act('pressure', { + pressure: value, + })} /> +
+
act('eject')} /> + )}> + {!!holding && ( + + + {holding.name} + + + kPa + + + )} + {!holding && ( + + No Holding Tank + + )} +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.js b/tgui/packages/tgui/interfaces/ChemDispenser.js new file mode 100644 index 0000000000..2a6bef7356 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemDispenser.js @@ -0,0 +1,172 @@ +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, LabeledList, ProgressBar, Slider, Section } from "../components"; +import { BeakerContents } from "../interfaces/common/BeakerContents"; +import { Window } from "../layouts"; + +const dispenseAmounts = [5, 10, 20, 30, 40, 60]; +const removeAmounts = [1, 5, 10]; + +export const ChemDispenser = (props, context) => { + return ( + + + + + + + + ); +}; + +const ChemDispenserSettings = (properties, context) => { + const { act, data } = useBackend(context); + const { + amount, + } = data; + return ( +
+ + + + {dispenseAmounts.map((a, i) => ( + +
+ ); +}; + +const ChemDispenserChemicals = (properties, context) => { + const { act, data } = useBackend(context); + const { + chemicals = [], + } = data; + const flexFillers = []; + for (let i = 0; i < (chemicals.length + 1) % 3; i++) { + flexFillers.push(true); + } + return ( +
+ + {chemicals.map((c, i) => ( + +
+ ); +}; + +const ChemDispenserBeaker = (properties, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerCurrentVolume, + beakerMaxVolume, + beakerContents = [], + } = data; + return ( +
+ {!!isBeakerLoaded && ( + + {beakerCurrentVolume} / {beakerMaxVolume} units + + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/ChemMaster.js b/tgui/packages/tgui/interfaces/ChemMaster.js new file mode 100644 index 0000000000..49ab98250b --- /dev/null +++ b/tgui/packages/tgui/interfaces/ChemMaster.js @@ -0,0 +1,452 @@ +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, Section } from "../components"; +import { Window } from "../layouts"; +import { BeakerContents } from './common/BeakerContents'; +import { ComplexModal, modalOpen, modalRegisterBodyOverride } from './common/ComplexModal'; + +const transferAmounts = [1, 5, 10, 30, 60]; +const bottleStyles = [ + "bottle.png", + "small_bottle.png", + "wide_bottle.png", + "round_bottle.png", + "reagent_bottle.png", +]; + +const analyzeModalBodyOverride = (modal, context) => { + const { act, data } = useBackend(context); + const result = modal.args.analysis; + return ( +
+ + + + {result.name} + + + {(result.desc || "").length > 0 ? result.desc : "N/A"} + + {result.blood_type && ( + + + {result.blood_type} + + + {result.blood_dna} + + + )} + {!data.condi && ( +
+ ); +}; + +export const ChemMaster = (props, context) => { + const { data } = useBackend(context); + const { + condi, + beaker, + beaker_reagents = [], + buffer_reagents = [], + mode, + } = data; + return ( + + + + 0} + /> + + 0} + /> + {/* */} + + + ); +}; + +const ChemMasterBeaker = (props, context) => { + const { act, data } = useBackend(context); + const { + beaker, + beakerReagents, + bufferNonEmpty, + } = props; + return ( +
act('eject')} + /> + :
+ ); +}; + +const ChemMasterBuffer = (props, context) => { + const { act } = useBackend(context); + const { + mode, + bufferReagents = [], + } = props; + return ( +
+ Transferring to  +
+ ); +}; + +const ChemMasterProduction = (props, context) => { + const { act, data } = useBackend(context); + if (!props.bufferNonEmpty) { + return ( +
act('ejectp')} + /> + }> + + +
+ Buffer is empty. +
+
+
+ ); + } + + return ( +
act('ejectp')} + /> + }> + {!props.isCondiment ? ( + + ) : ( + + )} +
+ ); +}; + +const ChemMasterProductionChemical = (props, context) => { + const { act, data } = useBackend(context); + return ( + + + + + + + + + ); +}; + +const ChemMasterProductionCondiment = (props, context) => { + const { act } = useBackend(context); + return ( + + + }> + + + {open ? "Open" : "Closed"} + + + {locked ? "Locked" : "Unlocked"} + + + + {!locked && ( +
+ +
+ ) || null} + {(!locked && open) && ( +
+ {rgbpanel && ( + + + + + + + + + )} +
+ ) || null} + + + ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/CloningConsole.js b/tgui/packages/tgui/interfaces/CloningConsole.js new file mode 100644 index 0000000000..4b23f36cfc --- /dev/null +++ b/tgui/packages/tgui/interfaces/CloningConsole.js @@ -0,0 +1,461 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, NoticeBox, ProgressBar, Section, Tabs } from "../components"; +import { COLORS } from '../constants'; +import { ComplexModal, modalRegisterBodyOverride } from '../interfaces/common/ComplexModal'; +import { Window } from "../layouts"; + +const viewRecordModalBodyOverride = (modal, context) => { + const { act, data } = useBackend(context); + const { + activerecord, + realname, + health, + unidentity, + strucenzymes, + } = modal.args; + const damages = health.split(' - '); + return ( +
+ + + {realname} + + + {damages.length > 1 ? ( + + + {damages[0]} + +  |  + + {damages[2]} + +  |  + + {damages[3]} + +  |  + + {damages[1]} + + + ) : ( + + Unknown + + )} + + + {unidentity} + + + {strucenzymes} + + + act('disk', { + option: 'load', + })} + /> +
+ ); +}; + +export const CloningConsole = (props, context) => { + const { act, data } = useBackend(context); + const { + menu, + } = data; + modalRegisterBodyOverride('view_rec', viewRecordModalBodyOverride); + return ( + + + + + + +
+ +
+
+
+ ); +}; + +const CloningConsoleNavigation = (props, context) => { + const { act, data } = useBackend(context); + const { + menu, + } = data; + return ( + + act('menu', { + num: 1, + })}> + Main + + act('menu', { + num: 2, + })}> + Records + + + ); +}; + +const CloningConsoleBody = (props, context) => { + const { data } = useBackend(context); + const { + menu, + } = data; + let body; + if (menu === 1) { + body = ; + } else if (menu === 2) { + body = ; + } + return body; +}; + +const CloningConsoleMain = (props, context) => { + const { act, data } = useBackend(context); + const { + loading, + scantemp, + occupant, + locked, + can_brainscan, + scan_mode, + numberofpods, + pods, + selected_pod, + } = data; + const isLocked = locked && !!occupant; + return ( + +
+ + Scanner Lock:  + +
+
+ {numberofpods + ? pods.map((pod, i) => { + let podAction; + if (pod.status === "cloning") { + podAction = ( + + + {round(pod.progress, 0) + "%"} + + + ); + } else if (pod.status === "mess") { + podAction = ( + + ERROR + + ); + } else { + podAction = ( +
+
+ ); +}; + +const CloningConsoleRecords = (props, context) => { + const { act, data } = useBackend(context); + const { + records, + } = data; + if (!records.length) { + return ( + + +
+ No records found. +
+
+ ); + } + return ( + + {records.map((record, i) => ( + + )}> + {hasOccupant ? ( + + + {occupant.name || "Unknown"} + + + 0 ? 'good' : 'average'}> + + + + + {statNames[occupant.stat][1]} + + + + {' K'} + + + {(damageTypes.map(damageType => ( + + + + + + )))} + + ) : ( + + +
+ No occupant detected. +
+
+ )} + +
act('ejectBeaker')} + disabled={!isBeakerLoaded}> + Eject Beaker + + )}> + + + + + + K + + + + + +
+
+ ); +}; + +const CryoBeaker = (props, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerLabel, + beakerVolume, + } = data; + if (isBeakerLoaded) { + return ( + + {beakerLabel + ? beakerLabel + : ( + + No label + + )} + + {beakerVolume ? ( + Math.round(v) + " units remaining"} + /> + ) : "Beaker is empty"} + + + ); + } else { + return ( + + No beaker loaded + + ); + } +}; diff --git a/tgui/packages/tgui/interfaces/DNAForensics.js b/tgui/packages/tgui/interfaces/DNAForensics.js new file mode 100644 index 0000000000..0bc2c39ef2 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAForensics.js @@ -0,0 +1,64 @@ +import { round } from 'common/math'; +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components"; +import { Window } from "../layouts"; + +export const DNAForensics = (props, context) => { + const { act, data } = useBackend(context); + const { + scan_progress, + scanning, + bloodsamp, + bloodsamp_desc, + } = data; + return ( + + +
+ + + + }> + + + + + +
+
+ {bloodsamp && ( + + {bloodsamp} + + {bloodsamp_desc} + + + ) || ( + + No blood sample inserted. + + )} +
+
+
+ ); +}; \ No newline at end of file diff --git a/tgui/packages/tgui/interfaces/DNAModifier.js b/tgui/packages/tgui/interfaces/DNAModifier.js new file mode 100644 index 0000000000..2b95dbfd45 --- /dev/null +++ b/tgui/packages/tgui/interfaces/DNAModifier.js @@ -0,0 +1,714 @@ +import { Fragment } from 'inferno'; +import { useBackend } from "../backend"; +import { Box, Button, Dimmer, Flex, Icon, Knob, LabeledList, ProgressBar, Section, Tabs } from "../components"; +import { Window } from "../layouts"; +import { ComplexModal } from './common/ComplexModal'; + +const stats = [ + ['good', 'Alive'], + ['average', 'Unconscious'], + ['bad', 'DEAD'], +]; + +const operations = [ + ['ui', 'Modify U.I.', 'dna'], + ['se', 'Modify S.E.', 'dna'], + ['buffer', 'Transfer Buffers', 'syringe'], + ['rejuvenators', 'Rejuvenators', 'flask'], +]; + +const rejuvenatorsDoses = [5, 10, 20, 30, 50]; + +export const DNAModifier = (props, context) => { + const { act, data } = useBackend(context); + const { + irradiating, + dnaBlockSize, + occupant, + } = data; + context.dnaBlockSize = dnaBlockSize; + context.isDNAInvalid = !occupant.isViableSubject + || !occupant.uniqueIdentity + || !occupant.structuralEnzymes; + let radiatingModal; + if (irradiating) { + radiatingModal = ; + } + return ( + + + {radiatingModal} + + + + + + ); +}; + +const DNAModifierOccupant = (props, context) => { + const { act, data } = useBackend(context); + const { + locked, + hasOccupant, + occupant, + } = data; + return ( +
+ + Door Lock: + +
+ ); +}; + +const DNAModifierMain = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedMenuKey, + hasOccupant, + occupant, + } = data; + if (!hasOccupant) { + return ( +
+ + +
+ No occupant in DNA modifier. +
+
+
+ ); + } else if (context.isDNAInvalid) { + return ( +
+ + +
+ No operation possible on this subject. +
+
+
+ ); + } + let body; + if (selectedMenuKey === "ui") { + body = ( + + + + + ); + } else if (selectedMenuKey === "se") { + body = ( + + + + + ); + } else if (selectedMenuKey === "buffer") { + body = ; + } else if (selectedMenuKey === "rejuvenators") { + body = ; + } + return ( +
+ + {operations.map((op, i) => ( + act('selectMenuKey', { key: op[0] })}> + + {op[1]} + + ))} + + {body} +
+ ); +}; + +const DNAModifierMainUI = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedUIBlock, + selectedUISubBlock, + selectedUITarget, + occupant, + } = data; + return ( +
+ + + + value.toString(16).toUpperCase()} + ml="0" + onChange={(e, val) => act('changeUITarget', { value: val })} + /> + + +
+ ); +}; + +const DNAModifierMainSE = (props, context) => { + const { act, data } = useBackend(context); + const { + selectedSEBlock, + selectedSESubBlock, + occupant, + } = data; + return ( +
+ +
+ ); +}; + +const DNAModifierMainRadiationEmitter = (props, context) => { + const { act, data } = useBackend(context); + const { + radiationIntensity, + radiationDuration, + } = data; + return ( +
+ + + act('radiationIntensity', { value: val })} + /> + + + act('radiationDuration', { value: val })} + /> + + +
+ ); +}; + +const DNAModifierMainBuffers = (props, context) => { + const { act, data } = useBackend(context); + const { + buffers, + } = data; + let bufferElements = buffers.map((buffer, i) => ( + + )); + return ( + +
+ {bufferElements} +
+ +
+ ); +}; + +const DNAModifierMainBuffersElement = (props, context) => { + const { act, data } = useBackend(context); + const { + id, + name, + buffer, + } = props; + const isInjectorReady = data.isInjectorReady; + const realName = name + (buffer.data ? ' - ' + buffer.label : ''); + return ( + +
+ act('bufferOption', { + option: 'clear', + id: id, + })} + /> +
+
+ ); +}; + +const DNAModifierMainBuffersDisk = (props, context) => { + const { act, data } = useBackend(context); + const { + hasDisk, + disk, + } = data; + return ( +
+ act('wipeDisk')} + /> +
+ ); +}; + +const DNAModifierMainRejuvenators = (props, context) => { + const { act, data } = useBackend(context); + const { + isBeakerLoaded, + beakerVolume, + beakerLabel, + } = data; + return ( +
act('ejectBeaker')} + /> + }> + {isBeakerLoaded ? ( + + + {rejuvenatorsDoses.map((a, i) => ( +
+ ); +}; + +const DNAModifierIrradiating = (props, context) => { + return ( + +
+ +

+ +  Irradiating occupant  + +

+
+ +

+ For {props.duration} second{props.duration === 1 ? "" : "s"} +

+
+
+ ); +}; + +const DNAModifierBlocks = (props, context) => { + const { act, data } = useBackend(context); + const { + dnaString, + selectedBlock, + selectedSubblock, + blockSize, + action, + } = props; + + const characters = dnaString.split(''); + let curBlock = 0; + let dnaBlocks = []; + for (let block = 0; block < characters.length; block += blockSize) { + const realBlock = block / blockSize + 1; + let subBlocks = []; + for (let subblock = 0; subblock < blockSize; subblock++) { + const realSubblock = subblock + 1; + subBlocks.push( +