From 41105d3907a49a8a7a8ea3534bc5a623d47c7d92 Mon Sep 17 00:00:00 2001 From: cuberound <122645057+cuberound@users.noreply.github.com> Date: Sun, 21 Jan 2024 21:07:04 +0100 Subject: [PATCH 01/26] Ap rocket stun and damage consistency and renames vars in rocket.dm (#5183) # About the pull request renames one latter vars in rocket.dm. Makes AP rocket behavior more consistent, AP rocked stuns you for TWICE as longs when it lands on turf under the mob and does not hit it directly. to compensate that I increased the stun duration by 50% to avrage of the turf hit and mob hit. the PR also reduces max distance damage, as it was higer for no aperent reason but I will not fight agains it if maintainer decides that the values should be diferent, I do not care. # Explain why it's good for the game expecting 2,6 sec stun and geting 5.3 when someone knows to aim for the tile under you and not you is not fun. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: balance: AP rocket applies 3 weaken and 3 paralyze no matter if you hit the mob directly(from 2), at max range (from 2) or tile under it (from 4) balance: indirect AP rocket does the same damage to walls and mobs /:cl: --- code/datums/ammo/rocket.dm | 257 +++++++++++++++++++------------------ 1 file changed, 130 insertions(+), 127 deletions(-) diff --git a/code/datums/ammo/rocket.dm b/code/datums/ammo/rocket.dm index 66a9f65bdcdd..7581d434c4b4 100644 --- a/code/datums/ammo/rocket.dm +++ b/code/datums/ammo/rocket.dm @@ -28,26 +28,26 @@ smoke = null . = ..() -/datum/ammo/rocket/on_hit_mob(mob/M, obj/projectile/P) - cell_explosion(get_turf(M), 150, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - smoke.set_up(1, get_turf(M)) - if(ishuman_strict(M)) // No yautya or synths. Makes humans gib on direct hit. - M.ex_act(350, P.dir, P.weapon_cause_data, 100) +/datum/ammo/rocket/on_hit_mob(mob/mob, obj/projectile/projectile) + cell_explosion(get_turf(mob), 150, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + smoke.set_up(1, get_turf(mob)) + if(ishuman_strict(mob)) // No yautya or synths. Makes humans gib on direct hit. + mob.ex_act(350, projectile.dir, projectile.weapon_cause_data, 100) smoke.start() -/datum/ammo/rocket/on_hit_obj(obj/O, obj/projectile/P) - cell_explosion(get_turf(O), 150, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - smoke.set_up(1, get_turf(O)) +/datum/ammo/rocket/on_hit_obj(obj/object, obj/projectile/projectile) + cell_explosion(get_turf(object), 150, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + smoke.set_up(1, get_turf(object)) smoke.start() -/datum/ammo/rocket/on_hit_turf(turf/T, obj/projectile/P) - cell_explosion(T, 150, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - smoke.set_up(1, T) +/datum/ammo/rocket/on_hit_turf(turf/turf, obj/projectile/projectile) + cell_explosion(turf, 150, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + smoke.set_up(1, turf) smoke.start() -/datum/ammo/rocket/do_at_max_range(obj/projectile/P) - cell_explosion(get_turf(P), 150, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - smoke.set_up(1, get_turf(P)) +/datum/ammo/rocket/do_at_max_range(obj/projectile/projectile) + cell_explosion(get_turf(projectile), 150, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + smoke.set_up(1, get_turf(projectile)) smoke.start() /datum/ammo/rocket/ap @@ -62,62 +62,65 @@ damage = 10 penetration= ARMOR_PENETRATION_TIER_10 -/datum/ammo/rocket/ap/on_hit_mob(mob/M, obj/projectile/P) - var/turf/T = get_turf(M) - M.ex_act(150, P.dir, P.weapon_cause_data, 100) - M.apply_effect(2, PARALYZE) - if(ishuman_strict(M)) // No yautya or synths. Makes humans gib on direct hit. - M.ex_act(300, P.dir, P.weapon_cause_data, 100) - cell_explosion(T, 100, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - smoke.set_up(1, T) + +/datum/ammo/rocket/ap/on_hit_mob(mob/mob, obj/projectile/projectile) + var/turf/turf = get_turf(mob) + mob.ex_act(150, projectile.dir, projectile.weapon_cause_data, 100) + mob.apply_effect(3, WEAKEN) + mob.apply_effect(3, PARALYZE) + if(ishuman_strict(mob)) // No yautya or synths. Makes humans gib on direct hit. + mob.ex_act(300, projectile.dir, projectile.weapon_cause_data, 100) + cell_explosion(turf, 100, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + smoke.set_up(1, turf) smoke.start() -/datum/ammo/rocket/ap/on_hit_obj(obj/O, obj/projectile/P) - var/turf/T = get_turf(O) - O.ex_act(150, P.dir, P.weapon_cause_data, 100) - cell_explosion(T, 100, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - smoke.set_up(1, T) +/datum/ammo/rocket/ap/on_hit_obj(obj/object, obj/projectile/projectile) + var/turf/turf = get_turf(object) + object.ex_act(150, projectile.dir, projectile.weapon_cause_data, 100) + cell_explosion(turf, 100, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + smoke.set_up(1, turf) smoke.start() -/datum/ammo/rocket/ap/on_hit_turf(turf/T, obj/projectile/P) +/datum/ammo/rocket/ap/on_hit_turf(turf/turf, obj/projectile/projectile) var/hit_something = 0 - for(var/mob/M in T) - M.ex_act(150, P.dir, P.weapon_cause_data, 100) - M.apply_effect(4, PARALYZE) + for(var/mob/mob in turf) + mob.ex_act(150, projectile.dir, projectile.weapon_cause_data, 100) + mob.apply_effect(3, WEAKEN) + mob.apply_effect(3, PARALYZE) hit_something = 1 continue if(!hit_something) - for(var/obj/O in T) - if(O.density) - O.ex_act(150, P.dir, P.weapon_cause_data, 100) + for(var/obj/object in turf) + if(object.density) + object.ex_act(150, projectile.dir, projectile.weapon_cause_data, 100) hit_something = 1 continue if(!hit_something) - T.ex_act(150, P.dir, P.weapon_cause_data, 200) + turf.ex_act(150, projectile.dir, projectile.weapon_cause_data, 200) - cell_explosion(T, 100, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - smoke.set_up(1, T) + cell_explosion(turf, 100, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + smoke.set_up(1, turf) smoke.start() -/datum/ammo/rocket/ap/do_at_max_range(obj/projectile/P) - var/turf/T = get_turf(P) +/datum/ammo/rocket/ap/do_at_max_range(obj/projectile/projectile) + var/turf/turf = get_turf(projectile) var/hit_something = 0 - for(var/mob/M in T) - M.ex_act(250, P.dir, P.weapon_cause_data, 100) - M.apply_effect(2, WEAKEN) - M.apply_effect(2, PARALYZE) + for(var/mob/mob in turf) + mob.ex_act(150, projectile.dir, projectile.weapon_cause_data, 100) + mob.apply_effect(3, WEAKEN) + mob.apply_effect(3, PARALYZE) hit_something = 1 - continue + break if(!hit_something) - for(var/obj/O in T) - if(O.density) - O.ex_act(250, P.dir, P.weapon_cause_data, 100) + for(var/obj/object in turf) + if(object.density) + object.ex_act(150, projectile.dir, projectile.weapon_cause_data, 100) hit_something = 1 - continue + break if(!hit_something) - T.ex_act(250, P.dir, P.weapon_cause_data) - cell_explosion(T, 100, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - smoke.set_up(1, T) + turf.ex_act(150, projectile.dir, projectile.weapon_cause_data) + cell_explosion(turf, 100, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + smoke.set_up(1, turf) smoke.start() /datum/ammo/rocket/ap/anti_tank @@ -127,16 +130,16 @@ shrapnel_chance = 5 shrapnel_type = /obj/item/large_shrapnel/at_rocket_dud -/datum/ammo/rocket/ap/anti_tank/on_hit_obj(obj/O, obj/projectile/P) - if(istype(O, /obj/vehicle/multitile)) - var/obj/vehicle/multitile/M = O - M.next_move = world.time + vehicle_slowdown_time - playsound(M, 'sound/effects/meteorimpact.ogg', 35) - M.at_munition_interior_explosion_effect(cause_data = create_cause_data("Anti-Tank Rocket")) - M.interior_crash_effect() - var/turf/T = get_turf(M.loc) - M.ex_act(150, P.dir, P.weapon_cause_data, 100) - smoke.set_up(1, T) +/datum/ammo/rocket/ap/anti_tank/on_hit_obj(obj/object, obj/projectile/projectile) + if(istype(object, /obj/vehicle/multitile)) + var/obj/vehicle/multitile/mob = object + mob.next_move = world.time + vehicle_slowdown_time + playsound(mob, 'sound/effects/meteorimpact.ogg', 35) + mob.at_munition_interior_explosion_effect(cause_data = create_cause_data("Anti-Tank Rocket")) + mob.interior_crash_effect() + var/turf/turf = get_turf(mob.loc) + mob.ex_act(150, projectile.dir, projectile.weapon_cause_data, 100) + smoke.set_up(1, turf) smoke.start() return return ..() @@ -153,21 +156,21 @@ damage = 25 shell_speed = AMMO_SPEED_TIER_3 -/datum/ammo/rocket/ltb/on_hit_mob(mob/M, obj/projectile/P) - cell_explosion(get_turf(M), 220, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - cell_explosion(get_turf(M), 200, 100, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) +/datum/ammo/rocket/ltb/on_hit_mob(mob/mob, obj/projectile/projectile) + cell_explosion(get_turf(mob), 220, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + cell_explosion(get_turf(mob), 200, 100, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) -/datum/ammo/rocket/ltb/on_hit_obj(obj/O, obj/projectile/P) - cell_explosion(get_turf(O), 220, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - cell_explosion(get_turf(O), 200, 100, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) +/datum/ammo/rocket/ltb/on_hit_obj(obj/object, obj/projectile/projectile) + cell_explosion(get_turf(object), 220, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + cell_explosion(get_turf(object), 200, 100, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) -/datum/ammo/rocket/ltb/on_hit_turf(turf/T, obj/projectile/P) - cell_explosion(get_turf(T), 220, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - cell_explosion(get_turf(T), 200, 100, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) +/datum/ammo/rocket/ltb/on_hit_turf(turf/turf, obj/projectile/projectile) + cell_explosion(get_turf(turf), 220, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + cell_explosion(get_turf(turf), 200, 100, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) -/datum/ammo/rocket/ltb/do_at_max_range(obj/projectile/P) - cell_explosion(get_turf(P), 220, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) - cell_explosion(get_turf(P), 200, 100, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, P.weapon_cause_data) +/datum/ammo/rocket/ltb/do_at_max_range(obj/projectile/projectile) + cell_explosion(get_turf(projectile), 220, 50, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) + cell_explosion(get_turf(projectile), 200, 100, EXPLOSION_FALLOFF_SHAPE_LINEAR, null, projectile.weapon_cause_data) /datum/ammo/rocket/wp name = "white phosphorous rocket" @@ -185,30 +188,30 @@ BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_incendiary) )) -/datum/ammo/rocket/wp/drop_flame(turf/T, datum/cause_data/cause_data) - playsound(T, 'sound/weapons/gun_flamethrower3.ogg', 75, 1, 7) - if(!istype(T)) return - smoke.set_up(1, T) +/datum/ammo/rocket/wp/drop_flame(turf/turf, datum/cause_data/cause_data) + playsound(turf, 'sound/weapons/gun_flamethrower3.ogg', 75, 1, 7) + if(!istype(turf)) return + smoke.set_up(1, turf) smoke.start() - var/datum/reagent/napalm/blue/R = new() - new /obj/flamer_fire(T, cause_data, R, 3) + var/datum/reagent/napalm/blue/reagent = new() + new /obj/flamer_fire(turf, cause_data, reagent, 3) var/datum/effect_system/smoke_spread/phosphorus/landingSmoke = new /datum/effect_system/smoke_spread/phosphorus - landingSmoke.set_up(3, 0, T, null, 6, cause_data) + landingSmoke.set_up(3, 0, turf, null, 6, cause_data) landingSmoke.start() landingSmoke = null -/datum/ammo/rocket/wp/on_hit_mob(mob/M, obj/projectile/P) - drop_flame(get_turf(M), P.weapon_cause_data) +/datum/ammo/rocket/wp/on_hit_mob(mob/mob, obj/projectile/projectile) + drop_flame(get_turf(mob), projectile.weapon_cause_data) -/datum/ammo/rocket/wp/on_hit_obj(obj/O, obj/projectile/P) - drop_flame(get_turf(O), P.weapon_cause_data) +/datum/ammo/rocket/wp/on_hit_obj(obj/object, obj/projectile/projectile) + drop_flame(get_turf(object), projectile.weapon_cause_data) -/datum/ammo/rocket/wp/on_hit_turf(turf/T, obj/projectile/P) - drop_flame(T, P.weapon_cause_data) +/datum/ammo/rocket/wp/on_hit_turf(turf/turf, obj/projectile/projectile) + drop_flame(turf, projectile.weapon_cause_data) -/datum/ammo/rocket/wp/do_at_max_range(obj/projectile/P) - drop_flame(get_turf(P), P.weapon_cause_data) +/datum/ammo/rocket/wp/do_at_max_range(obj/projectile/projectile) + drop_flame(get_turf(projectile), projectile.weapon_cause_data) /datum/ammo/rocket/wp/upp name = "extreme-intensity incendiary rocket" @@ -226,25 +229,25 @@ BULLET_TRAIT_ENTRY(/datum/element/bullet_trait_incendiary) )) -/datum/ammo/rocket/wp/upp/drop_flame(turf/T, datum/cause_data/cause_data) - playsound(T, 'sound/weapons/gun_flamethrower3.ogg', 75, 1, 7) - if(!istype(T)) return - smoke.set_up(1, T) +/datum/ammo/rocket/wp/upp/drop_flame(turf/turf, datum/cause_data/cause_data) + playsound(turf, 'sound/weapons/gun_flamethrower3.ogg', 75, 1, 7) + if(!istype(turf)) return + smoke.set_up(1, turf) smoke.start() - var/datum/reagent/napalm/upp/R = new() - new /obj/flamer_fire(T, cause_data, R, 3) + var/datum/reagent/napalm/upp/reagent = new() + new /obj/flamer_fire(turf, cause_data, reagent, 3) -/datum/ammo/rocket/wp/upp/on_hit_mob(mob/M, obj/projectile/P) - drop_flame(get_turf(M), P.weapon_cause_data) +/datum/ammo/rocket/wp/upp/on_hit_mob(mob/mob, obj/projectile/projectile) + drop_flame(get_turf(mob), projectile.weapon_cause_data) -/datum/ammo/rocket/wp/upp/on_hit_obj(obj/O, obj/projectile/P) - drop_flame(get_turf(O), P.weapon_cause_data) +/datum/ammo/rocket/wp/upp/on_hit_obj(obj/object, obj/projectile/projectile) + drop_flame(get_turf(object), projectile.weapon_cause_data) -/datum/ammo/rocket/wp/upp/on_hit_turf(turf/T, obj/projectile/P) - drop_flame(T, P.weapon_cause_data) +/datum/ammo/rocket/wp/upp/on_hit_turf(turf/turf, obj/projectile/projectile) + drop_flame(turf, projectile.weapon_cause_data) -/datum/ammo/rocket/wp/upp/do_at_max_range(obj/projectile/P) - drop_flame(get_turf(P), P.weapon_cause_data) +/datum/ammo/rocket/wp/upp/do_at_max_range(obj/projectile/projectile) + drop_flame(get_turf(projectile), projectile.weapon_cause_data) /datum/ammo/rocket/wp/quad name = "thermobaric rocket" @@ -254,45 +257,45 @@ max_range = 32 shell_speed = AMMO_SPEED_TIER_3 -/datum/ammo/rocket/wp/quad/on_hit_mob(mob/M, obj/projectile/P) - drop_flame(get_turf(M), P.weapon_cause_data) - explosion(P.loc, -1, 2, 4, 5, , , ,P.weapon_cause_data) +/datum/ammo/rocket/wp/quad/on_hit_mob(mob/mob, obj/projectile/projectile) + drop_flame(get_turf(mob), projectile.weapon_cause_data) + explosion(projectile.loc, -1, 2, 4, 5, , , ,projectile.weapon_cause_data) -/datum/ammo/rocket/wp/quad/on_hit_obj(obj/O, obj/projectile/P) - drop_flame(get_turf(O), P.weapon_cause_data) - explosion(P.loc, -1, 2, 4, 5, , , ,P.weapon_cause_data) +/datum/ammo/rocket/wp/quad/on_hit_obj(obj/object, obj/projectile/projectile) + drop_flame(get_turf(object), projectile.weapon_cause_data) + explosion(projectile.loc, -1, 2, 4, 5, , , ,projectile.weapon_cause_data) -/datum/ammo/rocket/wp/quad/on_hit_turf(turf/T, obj/projectile/P) - drop_flame(T, P.weapon_cause_data) - explosion(P.loc, -1, 2, 4, 5, , , ,P.weapon_cause_data) +/datum/ammo/rocket/wp/quad/on_hit_turf(turf/turf, obj/projectile/projectile) + drop_flame(turf, projectile.weapon_cause_data) + explosion(projectile.loc, -1, 2, 4, 5, , , ,projectile.weapon_cause_data) -/datum/ammo/rocket/wp/quad/do_at_max_range(obj/projectile/P) - drop_flame(get_turf(P), P.weapon_cause_data) - explosion(P.loc, -1, 2, 4, 5, , , ,P.weapon_cause_data) +/datum/ammo/rocket/wp/quad/do_at_max_range(obj/projectile/projectile) + drop_flame(get_turf(projectile), projectile.weapon_cause_data) + explosion(projectile.loc, -1, 2, 4, 5, , , ,projectile.weapon_cause_data) /datum/ammo/rocket/custom name = "custom rocket" -/datum/ammo/rocket/custom/proc/prime(atom/A, obj/projectile/P) - var/obj/item/weapon/gun/launcher/rocket/launcher = P.shot_from +/datum/ammo/rocket/custom/proc/prime(atom/atom, obj/projectile/projectile) + var/obj/item/weapon/gun/launcher/rocket/launcher = projectile.shot_from var/obj/item/ammo_magazine/rocket/custom/rocket = launcher.current_mag if(rocket.locked && rocket.warhead && rocket.warhead.detonator) if(rocket.fuel && rocket.fuel.reagents.get_reagent_amount(rocket.fuel_type) >= rocket.fuel_requirement) - rocket.forceMove(P.loc) - rocket.warhead.cause_data = P.weapon_cause_data + rocket.forceMove(projectile.loc) + rocket.warhead.cause_data = projectile.weapon_cause_data rocket.warhead.prime() qdel(rocket) - smoke.set_up(1, get_turf(A)) + smoke.set_up(1, get_turf(atom)) smoke.start() -/datum/ammo/rocket/custom/on_hit_mob(mob/M, obj/projectile/P) - prime(M, P) +/datum/ammo/rocket/custom/on_hit_mob(mob/mob, obj/projectile/projectile) + prime(mob, projectile) -/datum/ammo/rocket/custom/on_hit_obj(obj/O, obj/projectile/P) - prime(O, P) +/datum/ammo/rocket/custom/on_hit_obj(obj/object, obj/projectile/projectile) + prime(object, projectile) -/datum/ammo/rocket/custom/on_hit_turf(turf/T, obj/projectile/P) - prime(T, P) +/datum/ammo/rocket/custom/on_hit_turf(turf/turf, obj/projectile/projectile) + prime(turf, projectile) -/datum/ammo/rocket/custom/do_at_max_range(obj/projectile/P) - prime(null, P) +/datum/ammo/rocket/custom/do_at_max_range(obj/projectile/projectile) + prime(null, projectile) From 93a7a20d7f880e9c494a232bb0f485cbc54d59b1 Mon Sep 17 00:00:00 2001 From: cm13-github <128137806+cm13-github@users.noreply.github.com> Date: Sun, 21 Jan 2024 20:14:47 +0000 Subject: [PATCH 02/26] Automatic changelog for PR #5183 [ci skip] --- html/changelogs/AutoChangeLog-pr-5183.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-5183.yml diff --git a/html/changelogs/AutoChangeLog-pr-5183.yml b/html/changelogs/AutoChangeLog-pr-5183.yml new file mode 100644 index 000000000000..32d87f6caa1d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-5183.yml @@ -0,0 +1,5 @@ +author: "cuberound" +delete-after: True +changes: + - balance: "AP rocket applies 3 weaken and 3 paralyze no matter if you hit the mob directly(from 2), at max range (from 2) or tile under it (from 4)" + - balance: "indirect AP rocket does the same damage to walls and mobs" \ No newline at end of file From b8a21173737692f3b9b16373f385712579cecc7b Mon Sep 17 00:00:00 2001 From: Changelogs Date: Mon, 22 Jan 2024 01:16:09 +0000 Subject: [PATCH 03/26] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-5183.yml | 5 ----- html/changelogs/archive/2024-01.yml | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-5183.yml diff --git a/html/changelogs/AutoChangeLog-pr-5183.yml b/html/changelogs/AutoChangeLog-pr-5183.yml deleted file mode 100644 index 32d87f6caa1d..000000000000 --- a/html/changelogs/AutoChangeLog-pr-5183.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "cuberound" -delete-after: True -changes: - - balance: "AP rocket applies 3 weaken and 3 paralyze no matter if you hit the mob directly(from 2), at max range (from 2) or tile under it (from 4)" - - balance: "indirect AP rocket does the same damage to walls and mobs" \ No newline at end of file diff --git a/html/changelogs/archive/2024-01.yml b/html/changelogs/archive/2024-01.yml index 0f2314f98bbc..9e79038eada2 100644 --- a/html/changelogs/archive/2024-01.yml +++ b/html/changelogs/archive/2024-01.yml @@ -283,3 +283,8 @@ - rscadd: Made larvae and facehuggers able to use custom emotes. Vicacrov: - bugfix: Fixes engine/weapon attach points layering below the dropship walls. +2024-01-22: + cuberound: + - balance: AP rocket applies 3 weaken and 3 paralyze no matter if you hit the mob + directly(from 2), at max range (from 2) or tile under it (from 4) + - balance: indirect AP rocket does the same damage to walls and mobs From 6a24a442f02dffb3743880d923eda561aa2ce733 Mon Sep 17 00:00:00 2001 From: forest2001 <41653574+realforest2001@users.noreply.github.com> Date: Mon, 22 Jan 2024 11:35:39 +0000 Subject: [PATCH 04/26] Databasing whitelists (#5402) # About the pull request In theory, this should now make whitelists entirely controllable by their senators/the overseer(s) without direct management input. Major thanks to HarryOB for the major base of this code, all I did is make the UI and some code. # Explain why it's good for the game # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: add: Added Whitelist Panel for whitelist senators to manage their own whitelists. refactor: Whitelist.txt is no longer functional, owing to the new system. del: Removed "Alien Whitelist" checks regarding base SS13 races. /:cl: --------- Co-authored-by: harryob <55142896+harryob@users.noreply.github.com> Co-authored-by: harryob --- code/__DEFINES/mode.dm | 42 ++- code/datums/emergency_calls/clf.dm | 2 +- code/datums/emergency_calls/cmb.dm | 2 +- code/datums/emergency_calls/contractor.dm | 4 +- code/datums/emergency_calls/inspection.dm | 2 +- code/datums/emergency_calls/pmc.dm | 2 +- code/datums/emergency_calls/upp.dm | 2 +- code/datums/emergency_calls/xeno_cultists.dm | 2 +- code/datums/entities/player.dm | 42 ++- code/game/gamemodes/cm_initialize.dm | 12 +- code/game/gamemodes/extended/infection.dm | 2 +- code/game/jobs/job/antag/other/pred.dm | 11 +- .../jobs/job/civilians/support/synthetic.dm | 8 +- .../jobs/job/civilians/support/working_joe.dm | 6 + code/game/jobs/job/command/cic/captain.dm | 8 +- code/game/jobs/job/job.dm | 14 +- code/game/jobs/role_authority.dm | 56 +--- code/game/jobs/whitelist.dm | 234 +++++++++++--- code/game/verbs/records.dm | 12 +- code/modules/admin/admin_verbs.dm | 12 +- code/modules/admin/banjob.dm | 2 - code/modules/admin/holder2.dm | 1 - code/modules/admin/tabs/debug_tab.dm | 9 - code/modules/admin/topic/topic.dm | 7 +- code/modules/clans/client.dm | 4 +- code/modules/client/client_procs.dm | 47 ++- code/modules/client/preferences.dm | 33 +- code/modules/mob/dead/observer/observer.dm | 9 +- code/modules/mob/new_player/new_player.dm | 48 --- .../mob/new_player/preferences_setup.dm | 18 +- .../tgui/interfaces/WhitelistPanel.jsx | 288 ++++++++++++++++++ 31 files changed, 672 insertions(+), 269 deletions(-) create mode 100644 tgui/packages/tgui/interfaces/WhitelistPanel.jsx diff --git a/code/__DEFINES/mode.dm b/code/__DEFINES/mode.dm index 0f04006859e9..2b018c0a2810 100644 --- a/code/__DEFINES/mode.dm +++ b/code/__DEFINES/mode.dm @@ -156,6 +156,7 @@ GLOBAL_LIST_INIT(ROLES_UNASSIGNED, list(JOB_SQUAD_MARINE)) GLOBAL_LIST_INIT(whitelist_hierarchy, list(WHITELIST_NORMAL, WHITELIST_COUNCIL, WHITELIST_LEADER)) //================================================= + #define WHITELIST_YAUTJA (1<<0) ///Old holders of YAUTJA_ELDER #define WHITELIST_YAUTJA_LEGACY (1<<1) @@ -170,15 +171,20 @@ GLOBAL_LIST_INIT(whitelist_hierarchy, list(WHITELIST_NORMAL, WHITELIST_COUNCIL, ///Old holders of COMMANDER_COUNCIL for 3 months #define WHITELIST_COMMANDER_COUNCIL_LEGACY (1<<7) #define WHITELIST_COMMANDER_LEADER (1<<8) +///Former CO senator/whitelist overseer award +#define WHITELIST_COMMANDER_COLONEL (1<<9) -#define WHITELIST_JOE (1<<9) -#define WHITELIST_SYNTHETIC (1<<10) -#define WHITELIST_SYNTHETIC_COUNCIL (1<<11) +#define WHITELIST_JOE (1<<10) +#define WHITELIST_SYNTHETIC (1<<11) +#define WHITELIST_SYNTHETIC_COUNCIL (1<<12) ///Old holders of SYNTHETIC_COUNCIL for 3 months -#define WHITELIST_SYNTHETIC_COUNCIL_LEGACY (1<<12) -#define WHITELIST_SYNTHETIC_LEADER (1<<13) +#define WHITELIST_SYNTHETIC_COUNCIL_LEGACY (1<<13) +#define WHITELIST_SYNTHETIC_LEADER (1<<14) + +///Senior Enlisted Advisor, auto granted by R_MENTOR +#define WHITELIST_MENTOR (1<<15) + -#define WHITELIST_MENTOR (1<<14) #define WHITELISTS_GENERAL (WHITELIST_YAUTJA|WHITELIST_COMMANDER|WHITELIST_SYNTHETIC|WHITELIST_MENTOR|WHITELIST_JOE) #define WHITELISTS_COUNCIL (WHITELIST_YAUTJA_COUNCIL|WHITELIST_COMMANDER_COUNCIL|WHITELIST_SYNTHETIC_COUNCIL) #define WHITELISTS_LEGACY_COUNCIL (WHITELIST_YAUTJA_COUNCIL_LEGACY|WHITELIST_COMMANDER_COUNCIL_LEGACY|WHITELIST_SYNTHETIC_COUNCIL_LEGACY) @@ -186,7 +192,29 @@ GLOBAL_LIST_INIT(whitelist_hierarchy, list(WHITELIST_NORMAL, WHITELIST_COUNCIL, #define WHITELIST_EVERYTHING (WHITELISTS_GENERAL|WHITELISTS_COUNCIL|WHITELISTS_LEADER) -#define isCouncil(A) (GLOB.RoleAuthority.roles_whitelist[A.ckey] & WHITELIST_YAUTJA_COUNCIL) || (GLOB.RoleAuthority.roles_whitelist[A.ckey] & WHITELIST_SYNTHETIC_COUNCIL) || (GLOB.RoleAuthority.roles_whitelist[A.ckey] & WHITELIST_COMMANDER_COUNCIL) +#define COUNCIL_LIST list(WHITELIST_COMMANDER_COUNCIL, WHITELIST_SYNTHETIC_COUNCIL, WHITELIST_YAUTJA_COUNCIL) +#define SENATOR_LIST list(WHITELIST_COMMANDER_LEADER, WHITELIST_SYNTHETIC_LEADER, WHITELIST_YAUTJA_LEADER) +#define isCouncil(A) (A.check_whitelist_status_list(COUNCIL_LIST)) +#define isSenator(A) (A.check_whitelist_status_list(SENATOR_LIST)) + +DEFINE_BITFIELD(whitelist_status, list( + "WHITELIST_YAUTJA" = WHITELIST_YAUTJA, + "WHITELIST_YAUTJA_LEGACY" = WHITELIST_YAUTJA_LEGACY, + "WHITELIST_YAUTJA_COUNCIL" = WHITELIST_YAUTJA_COUNCIL, + "WHITELIST_YAUTJA_COUNCIL_LEGACY" = WHITELIST_YAUTJA_COUNCIL_LEGACY, + "WHITELIST_YAUTJA_LEADER" = WHITELIST_YAUTJA_LEADER, + "WHITELIST_COMMANDER" = WHITELIST_COMMANDER, + "WHITELIST_COMMANDER_COUNCIL" = WHITELIST_COMMANDER_COUNCIL, + "WHITELIST_COMMANDER_COUNCIL_LEGACY" = WHITELIST_COMMANDER_COUNCIL_LEGACY, + "WHITELIST_COMMANDER_COLONEL" = WHITELIST_COMMANDER_COLONEL, + "WHITELIST_COMMANDER_LEADER" = WHITELIST_COMMANDER_LEADER, + "WHITELIST_JOE" = WHITELIST_JOE, + "WHITELIST_SYNTHETIC" = WHITELIST_SYNTHETIC, + "WHITELIST_SYNTHETIC_COUNCIL" = WHITELIST_SYNTHETIC_COUNCIL, + "WHITELIST_SYNTHETIC_COUNCIL_LEGACY" = WHITELIST_SYNTHETIC_COUNCIL_LEGACY, + "WHITELIST_SYNTHETIC_LEADER" = WHITELIST_SYNTHETIC_LEADER, + "WHITELIST_MENTOR" = WHITELIST_MENTOR, +)) //================================================= diff --git a/code/datums/emergency_calls/clf.dm b/code/datums/emergency_calls/clf.dm index 837ecb340dda..88c95ef31bc4 100644 --- a/code/datums/emergency_calls/clf.dm +++ b/code/datums/emergency_calls/clf.dm @@ -45,7 +45,7 @@ leader = H to_chat(H, SPAN_ROLE_HEADER("You are a Cell Leader of the local resistance group, the Colonial Liberation Front!")) arm_equipment(H, /datum/equipment_preset/clf/leader, TRUE, TRUE) - else if(synths < max_synths && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SYNTH) && GLOB.RoleAuthority.roles_whitelist[H.ckey] & WHITELIST_SYNTHETIC) + else if(synths < max_synths && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SYNTH) && H.client.check_whitelist_status(WHITELIST_SYNTHETIC)) synths++ to_chat(H, SPAN_ROLE_HEADER("You are a Multi-Purpose Synthetic for the local resistance group, the Colonial Liberation Front!")) arm_equipment(H, /datum/equipment_preset/clf/synth, TRUE, TRUE) diff --git a/code/datums/emergency_calls/cmb.dm b/code/datums/emergency_calls/cmb.dm index 5dba3ee8fc7d..56b8b169b313 100644 --- a/code/datums/emergency_calls/cmb.dm +++ b/code/datums/emergency_calls/cmb.dm @@ -35,7 +35,7 @@ leader = mob to_chat(mob, SPAN_ROLE_HEADER("You are the Colonial Marshal!")) arm_equipment(mob, /datum/equipment_preset/cmb/leader, TRUE, TRUE) - else if(synths < max_synths && HAS_FLAG(mob?.client.prefs.toggles_ert, PLAY_SYNTH) && GLOB.RoleAuthority.roles_whitelist[mob.ckey] & WHITELIST_SYNTHETIC) + else if(synths < max_synths && HAS_FLAG(mob?.client.prefs.toggles_ert, PLAY_SYNTH) && mob.client.check_whitelist_status(WHITELIST_SYNTHETIC)) synths++ to_chat(mob, SPAN_ROLE_HEADER("You are a CMB Investigative Synthetic!")) arm_equipment(mob, /datum/equipment_preset/cmb/synth, TRUE, TRUE) diff --git a/code/datums/emergency_calls/contractor.dm b/code/datums/emergency_calls/contractor.dm index 0e0c975f0d13..05d6ab7b4671 100644 --- a/code/datums/emergency_calls/contractor.dm +++ b/code/datums/emergency_calls/contractor.dm @@ -29,7 +29,7 @@ leader = mob to_chat(mob, SPAN_ROLE_HEADER("You are a Contractor Team Leader of Vanguard's Arrow Incorporated!")) arm_equipment(mob, /datum/equipment_preset/contractor/duty/leader, TRUE, TRUE) - else if(synths < max_synths && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_SYNTH) && GLOB.RoleAuthority.roles_whitelist[mob.ckey] & WHITELIST_SYNTHETIC) + else if(synths < max_synths && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_SYNTH) && mob.client.check_whitelist_status(WHITELIST_SYNTHETIC)) synths++ to_chat(mob, SPAN_ROLE_HEADER("You are a Contractor Support Synthetic of Vanguard's Arrow Incorporated!")) arm_equipment(mob, /datum/equipment_preset/contractor/duty/synth, TRUE, TRUE) @@ -123,7 +123,7 @@ leader = H to_chat(H, SPAN_ROLE_HEADER("You are a Covert Contractor Team Leader of Vanguard's Arrow Incorporated!")) arm_equipment(H, /datum/equipment_preset/contractor/covert/leader, TRUE, TRUE) - else if(synths < max_synths && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SYNTH) && GLOB.RoleAuthority.roles_whitelist[H.ckey] & WHITELIST_SYNTHETIC) + else if(synths < max_synths && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SYNTH) && H.client.check_whitelist_status(WHITELIST_SYNTHETIC)) synths++ to_chat(H, SPAN_ROLE_HEADER("You are a Contractor Support Synthetic of Vanguard's Arrow Incorporated!")) arm_equipment(H, /datum/equipment_preset/contractor/covert/synth, TRUE, TRUE) diff --git a/code/datums/emergency_calls/inspection.dm b/code/datums/emergency_calls/inspection.dm index 2cd121093ea4..f0400528ac31 100644 --- a/code/datums/emergency_calls/inspection.dm +++ b/code/datums/emergency_calls/inspection.dm @@ -202,7 +202,7 @@ leader = mob to_chat(mob, SPAN_ROLE_HEADER("You are the Colonial Marshal!")) arm_equipment(mob, /datum/equipment_preset/cmb/leader, TRUE, TRUE) - else if(synths < max_synths && HAS_FLAG(mob?.client.prefs.toggles_ert, PLAY_SYNTH) && GLOB.RoleAuthority.roles_whitelist[mob.ckey] & WHITELIST_SYNTHETIC) + else if(synths < max_synths && HAS_FLAG(mob?.client.prefs.toggles_ert, PLAY_SYNTH) && mob.client.check_whitelist_status(WHITELIST_SYNTHETIC)) synths++ to_chat(mob, SPAN_ROLE_HEADER("You are a CMB Investigative Synthetic!")) arm_equipment(mob, /datum/equipment_preset/cmb/synth, TRUE, TRUE) diff --git a/code/datums/emergency_calls/pmc.dm b/code/datums/emergency_calls/pmc.dm index 2d21dc768f4a..2f43e94828d6 100644 --- a/code/datums/emergency_calls/pmc.dm +++ b/code/datums/emergency_calls/pmc.dm @@ -34,7 +34,7 @@ leader = mob to_chat(mob, SPAN_ROLE_HEADER("You are a Weyland-Yutani PMC Squad Leader!")) arm_equipment(mob, /datum/equipment_preset/pmc/pmc_leader, TRUE, TRUE) - else if(synths < max_synths && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_SYNTH) && GLOB.RoleAuthority.roles_whitelist[mob.ckey] & WHITELIST_SYNTHETIC) + else if(synths < max_synths && HAS_FLAG(mob.client.prefs.toggles_ert, PLAY_SYNTH) && mob.client.check_whitelist_status(WHITELIST_SYNTHETIC)) synths++ to_chat(mob, SPAN_ROLE_HEADER("You are a Weyland-Yutani PMC Support Synthetic!")) arm_equipment(mob, /datum/equipment_preset/pmc/synth, TRUE, TRUE) diff --git a/code/datums/emergency_calls/upp.dm b/code/datums/emergency_calls/upp.dm index 80ef111be3e4..c075c4be25db 100644 --- a/code/datums/emergency_calls/upp.dm +++ b/code/datums/emergency_calls/upp.dm @@ -61,7 +61,7 @@ leader = H arm_equipment(H, /datum/equipment_preset/upp/leader, TRUE, TRUE) to_chat(H, SPAN_ROLE_HEADER("You are an Officer of the Union of Progressive People, a powerful socialist state that rivals the United Americas!")) - else if(synths < max_synths && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SYNTH) && GLOB.RoleAuthority.roles_whitelist[H.ckey] & WHITELIST_SYNTHETIC) + else if(synths < max_synths && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SYNTH) && H.client.check_whitelist_status(WHITELIST_SYNTHETIC)) synths++ to_chat(H, SPAN_ROLE_HEADER("You are a Combat Synthetic of the Union of Progressive People, a powerful socialist state that rivals the United Americas!")) arm_equipment(H, /datum/equipment_preset/upp/synth, TRUE, TRUE) diff --git a/code/datums/emergency_calls/xeno_cultists.dm b/code/datums/emergency_calls/xeno_cultists.dm index 5da5c9c17e59..f112511b5e22 100644 --- a/code/datums/emergency_calls/xeno_cultists.dm +++ b/code/datums/emergency_calls/xeno_cultists.dm @@ -25,7 +25,7 @@ leader = H to_chat(H, SPAN_ROLE_HEADER("You are the leader of this xeno cult! Bring glory to Queen Mother!")) arm_equipment(H, /datum/equipment_preset/other/xeno_cultist/leader, TRUE, TRUE) - else if(synths < max_synths && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SYNTH) && GLOB.RoleAuthority.roles_whitelist[H.ckey] & WHITELIST_SYNTHETIC) + else if(synths < max_synths && HAS_FLAG(H.client.prefs.toggles_ert, PLAY_SYNTH) && H.client.check_whitelist_status(WHITELIST_SYNTHETIC)) synths++ to_chat(H, SPAN_ROLE_HEADER("You are the xeno cult's synthetic! Tend to the Hive and the captured hosts, make sure the Hive grows!")) arm_equipment(H, /datum/equipment_preset/synth/survivor/cultist_synth, TRUE, TRUE) diff --git a/code/datums/entities/player.dm b/code/datums/entities/player.dm index e5fa811002a2..2973a174858f 100644 --- a/code/datums/entities/player.dm +++ b/code/datums/entities/player.dm @@ -5,6 +5,9 @@ var/last_known_ip var/last_known_cid + var/whitelist_status + var/whitelist_flags + var/discord_link_id var/last_login @@ -63,6 +66,7 @@ BSQL_PROTECT_DATUM(/datum/entity/player) "is_permabanned" = DB_FIELDTYPE_INT, "permaban_reason" = DB_FIELDTYPE_STRING_MAX, "permaban_date" = DB_FIELDTYPE_STRING_LARGE, + "whitelist_status" = DB_FIELDTYPE_STRING_MAX, "discord_link_id" = DB_FIELDTYPE_BIGINT, "permaban_admin_id" = DB_FIELDTYPE_BIGINT, "is_time_banned" = DB_FIELDTYPE_INT, @@ -321,20 +325,6 @@ BSQL_PROTECT_DATUM(/datum/entity/player) value.delete() job_bans -= value -/datum/entity/player/proc/load_refs() - if(refs_loaded) - return - while(!notes_loaded || !jobbans_loaded) - stoplag() - for(var/key in job_bans) - var/datum/entity/player_job_ban/value = job_bans[key] - if(istype(value)) - value.load_refs() - for(var/datum/entity/player_note/note in notes) - if(istype(note)) - note.load_refs() - refs_loaded = TRUE - /datum/entity_meta/player/on_read(datum/entity/player/player) player.job_bans = list() player.notes = list() @@ -391,7 +381,12 @@ BSQL_PROTECT_DATUM(/datum/entity/player) if(discord_link_id) discord_link = DB_ENTITY(/datum/entity/discord_link, discord_link_id) + if(whitelist_status) + var/list/whitelists = splittext(whitelist_status, "|") + for(var/whitelist in whitelists) + if(whitelist in GLOB.bitfields["whitelist_status"]) + whitelist_flags |= GLOB.bitfields["whitelist_status"]["[whitelist]"] /datum/entity/player/proc/on_read_notes(list/datum/entity/player_note/_notes) notes_loaded = TRUE @@ -657,6 +652,23 @@ BSQL_PROTECT_DATUM(/datum/entity/player) stat.stat_number += num stat.save() +/datum/entity/player/proc/check_whitelist_status(flag_to_check) + if(whitelist_flags & flag_to_check) + return TRUE + + return FALSE + +/datum/entity/player/proc/set_whitelist_status(field_to_set) + whitelist_flags = field_to_set + + var/list/output = list() + for(var/bitfield in GLOB.bitfields["whitelist_status"]) + if(field_to_set & GLOB.bitfields["whitelist_status"]["[bitfield]"]) + output += bitfield + whitelist_status = output.Join("|") + + save() + /datum/entity_link/player_to_banning_admin parent_entity = /datum/entity/player child_entity = /datum/entity/player @@ -685,6 +697,7 @@ BSQL_PROTECT_DATUM(/datum/entity/player) var/last_known_cid var/last_known_ip var/discord_link_id + var/whitelist_status /datum/entity_view_meta/players root_record_type = /datum/entity/player @@ -702,4 +715,5 @@ BSQL_PROTECT_DATUM(/datum/entity/player) "last_known_ip", "last_known_cid", "discord_link_id", + "whitelist_status" ) diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm index c017733de7fd..af66052ddf05 100644 --- a/code/game/gamemodes/cm_initialize.dm +++ b/code/game/gamemodes/cm_initialize.dm @@ -155,7 +155,7 @@ Additional game mode variables. else if(!istype(player,/mob/dead)) continue //Otherwise we just want to grab the ghosts. - if(GLOB.RoleAuthority.roles_whitelist[player.ckey] & WHITELIST_PREDATOR) //Are they whitelisted? + if(player?.client.check_whitelist_status(WHITELIST_PREDATOR)) //Are they whitelisted? if(!player.client.prefs) player.client.prefs = new /datum/preferences(player.client) //Somehow they don't have one. @@ -188,7 +188,7 @@ Additional game mode variables. if(show_warning) to_chat(pred_candidate, SPAN_WARNING("Something went wrong!")) return - if(!(GLOB.RoleAuthority.roles_whitelist[pred_candidate.ckey] & WHITELIST_PREDATOR)) + if(!(pred_candidate?.client.check_whitelist_status(WHITELIST_PREDATOR))) if(show_warning) to_chat(pred_candidate, SPAN_WARNING("You are not whitelisted! You may apply on the forums to be whitelisted as a predator.")) return @@ -201,9 +201,9 @@ Additional game mode variables. to_chat(pred_candidate, SPAN_WARNING("You already were a Yautja! Give someone else a chance.")) return - if(show_warning && tgui_alert(pred_candidate, "Confirm joining the hunt. You will join as \a [lowertext(J.get_whitelist_status(GLOB.RoleAuthority.roles_whitelist, pred_candidate.client))] predator", "Confirmation", list("Yes", "No"), 10 SECONDS) != "Yes") + if(show_warning && tgui_alert(pred_candidate, "Confirm joining the hunt. You will join as \a [lowertext(J.get_whitelist_status(pred_candidate.client))] predator", "Confirmation", list("Yes", "No"), 10 SECONDS) != "Yes") return - if(J.get_whitelist_status(GLOB.RoleAuthority.roles_whitelist, pred_candidate.client) == WHITELIST_NORMAL) + if(J.get_whitelist_status(pred_candidate.client) == WHITELIST_NORMAL) var/pred_max = calculate_pred_max if(pred_current_num >= pred_max) if(show_warning) to_chat(pred_candidate, SPAN_WARNING("Only [pred_max] predators may spawn this round, but Councillors and Ancients do not count.")) @@ -1001,7 +1001,7 @@ Additional game mode variables. to_chat(joe_candidate, SPAN_WARNING("Something went wrong!")) return - if(!(GLOB.RoleAuthority.roles_whitelist[joe_candidate.ckey] & WHITELIST_JOE)) + if(!joe_job.check_whitelist_status(joe_candidate)) if(show_warning) to_chat(joe_candidate, SPAN_WARNING("You are not whitelisted! You may apply on the forums to be whitelisted as a synth.")) return @@ -1012,7 +1012,7 @@ Additional game mode variables. return // council doesn't count towards this conditional. - if(joe_job.get_whitelist_status(GLOB.RoleAuthority.roles_whitelist, joe_candidate.client) == WHITELIST_NORMAL) + if(joe_job.get_whitelist_status(joe_candidate.client) == WHITELIST_NORMAL) var/joe_max = joe_job.total_positions if((joe_job.current_positions >= joe_max) && !MODE_HAS_TOGGLEABLE_FLAG(MODE_BYPASS_JOE)) if(show_warning) diff --git a/code/game/gamemodes/extended/infection.dm b/code/game/gamemodes/extended/infection.dm index 1e0032a8e6fa..58d92355269d 100644 --- a/code/game/gamemodes/extended/infection.dm +++ b/code/game/gamemodes/extended/infection.dm @@ -61,7 +61,7 @@ possible_synth_survivors -= A continue - if(GLOB.RoleAuthority.roles_whitelist[ckey(A.key)] & WHITELIST_SYNTHETIC) + if(A.current.client?.check_whitelist_status(WHITELIST_SYNTHETIC)) if(A in possible_survivors) continue //they are already applying to be a survivor else diff --git a/code/game/jobs/job/antag/other/pred.dm b/code/game/jobs/job/antag/other/pred.dm index 77439276d04a..3808c5637da9 100644 --- a/code/game/jobs/job/antag/other/pred.dm +++ b/code/game/jobs/job/antag/other/pred.dm @@ -31,11 +31,7 @@ SSticker.mode.attempt_to_join_as_predator(player) -/datum/job/antag/predator/get_whitelist_status(list/roles_whitelist, client/player) // Might be a problem waiting here, but we've got no choice - . = ..() - if(!.) - return - +/datum/job/antag/predator/get_whitelist_status(client/player) // Might be a problem waiting here, but we've got no choice if(!player.clan_info) return CLAN_RANK_BLOODED @@ -49,10 +45,7 @@ if(!("[JOB_PREDATOR][rank]" in gear_preset_whitelist)) return CLAN_RANK_BLOODED - if(\ - (roles_whitelist[player.ckey] & (WHITELIST_YAUTJA_LEADER|WHITELIST_YAUTJA_COUNCIL|WHITELIST_YAUTJA_COUNCIL_LEGACY)) &&\ - get_desired_status(player.prefs.yautja_status, WHITELIST_COUNCIL) == WHITELIST_NORMAL\ - ) + if(player.check_whitelist_status(WHITELIST_YAUTJA_LEADER|WHITELIST_YAUTJA_COUNCIL|WHITELIST_YAUTJA_COUNCIL_LEGACY) && get_desired_status(player.prefs.yautja_status, WHITELIST_COUNCIL) == WHITELIST_NORMAL) return CLAN_RANK_BLOODED return rank diff --git a/code/game/jobs/job/civilians/support/synthetic.dm b/code/game/jobs/job/civilians/support/synthetic.dm index 70060fb36a15..12e50ef6c809 100644 --- a/code/game/jobs/job/civilians/support/synthetic.dm +++ b/code/game/jobs/job/civilians/support/synthetic.dm @@ -19,16 +19,16 @@ "[JOB_SYNTH][WHITELIST_LEADER]" = /datum/equipment_preset/synth/uscm/councillor ) -/datum/job/civilian/synthetic/get_whitelist_status(list/roles_whitelist, client/player) +/datum/job/civilian/synthetic/get_whitelist_status(client/player) . = ..() if(!.) return - if(roles_whitelist[player.ckey] & WHITELIST_SYNTHETIC_LEADER) + if(player.check_whitelist_status(WHITELIST_SYNTHETIC_LEADER)) return get_desired_status(player.prefs.synth_status, WHITELIST_LEADER) - else if(roles_whitelist[player.ckey] & (WHITELIST_SYNTHETIC_COUNCIL|WHITELIST_SYNTHETIC_COUNCIL_LEGACY)) + if(player.check_whitelist_status(WHITELIST_SYNTHETIC_COUNCIL|WHITELIST_SYNTHETIC_COUNCIL_LEGACY)) return get_desired_status(player.prefs.synth_status, WHITELIST_COUNCIL) - else if(roles_whitelist[player.ckey] & WHITELIST_SYNTHETIC) + if(player.check_whitelist_status(WHITELIST_SYNTHETIC)) return get_desired_status(player.prefs.synth_status, WHITELIST_NORMAL) /datum/job/civilian/synthetic/set_spawn_positions(count) diff --git a/code/game/jobs/job/civilians/support/working_joe.dm b/code/game/jobs/job/civilians/support/working_joe.dm index bc8f8c439900..d890b3684084 100644 --- a/code/game/jobs/job/civilians/support/working_joe.dm +++ b/code/game/jobs/job/civilians/support/working_joe.dm @@ -17,6 +17,12 @@ job_options = list(STANDARD_VARIANT = "JOE", HAZMAT_VARIANT = "HAZ") var/standard = TRUE +/datum/job/civilian/working_joe/check_whitelist_status(mob/user) + if(user.client.check_whitelist_status(WHITELIST_SYNTHETIC)) + return TRUE + + return ..() + /datum/job/civilian/working_joe/handle_job_options(option) if(option != HAZMAT_VARIANT) standard = TRUE diff --git a/code/game/jobs/job/command/cic/captain.dm b/code/game/jobs/job/command/cic/captain.dm index 72f861351912..30fa455beeee 100644 --- a/code/game/jobs/job/command/cic/captain.dm +++ b/code/game/jobs/job/command/cic/captain.dm @@ -19,16 +19,16 @@ entry_message_body = "You are the Commanding Officer of the [MAIN_SHIP_NAME] as well as the operation. Your goal is to lead the Marines on their mission as well as protect and command the ship and her crew. Your job involves heavy roleplay and requires you to behave like a high-ranking officer and to stay in character at all times. As the Commanding Officer your only superior is High Command itself. You must abide by the Commanding Officer Code of Conduct. Failure to do so may result in punitive action against you. Godspeed." return ..() -/datum/job/command/commander/get_whitelist_status(list/roles_whitelist, client/player) +/datum/job/command/commander/get_whitelist_status(client/player) . = ..() if(!.) return - if(roles_whitelist[player.ckey] & WHITELIST_COMMANDER_LEADER) + if(player.check_whitelist_status(WHITELIST_COMMANDER_LEADER|WHITELIST_COMMANDER_COLONEL)) return get_desired_status(player.prefs.commander_status, WHITELIST_LEADER) - else if(roles_whitelist[player.ckey] & (WHITELIST_COMMANDER_COUNCIL|WHITELIST_COMMANDER_COUNCIL_LEGACY)) + if(player.check_whitelist_status(WHITELIST_COMMANDER_COUNCIL|WHITELIST_COMMANDER_COUNCIL_LEGACY)) return get_desired_status(player.prefs.commander_status, WHITELIST_COUNCIL) - else if(roles_whitelist[player.ckey] & WHITELIST_COMMANDER) + if(player.check_whitelist_status(WHITELIST_COMMANDER)) return get_desired_status(player.prefs.commander_status, WHITELIST_NORMAL) /datum/job/command/commander/announce_entry_message(mob/living/carbon/human/H) diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 094b899c1691..1a04c3cafeb5 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -66,10 +66,7 @@ return "" return "[CONFIG_GET(string/wikiarticleurl)]/[replacetext(title, " ", "_")]" -/datum/job/proc/get_whitelist_status(list/roles_whitelist, client/player) - if(!roles_whitelist) - return FALSE - +/datum/job/proc/get_whitelist_status(client/player) return WHITELIST_NORMAL /datum/timelock @@ -252,7 +249,7 @@ var/mob/living/carbon/human/human = M var/job_whitelist = title - var/whitelist_status = get_whitelist_status(GLOB.RoleAuthority.roles_whitelist, human.client) + var/whitelist_status = get_whitelist_status(human.client) if(whitelist_status) job_whitelist = "[title][whitelist_status]" @@ -314,3 +311,10 @@ /// Intended to be overwritten to handle any requirements for specific job variations that can be selected /datum/job/proc/filter_job_option(mob/job_applicant) return job_options + +/datum/job/proc/check_whitelist_status(mob/user) + if(!(flags_startup_parameters & ROLE_WHITELISTED)) + return TRUE + + if(user.client.check_whitelist_status(flags_whitelist)) + return TRUE diff --git a/code/game/jobs/role_authority.dm b/code/game/jobs/role_authority.dm index be2b75e0e7f6..58c9ad5b5092 100644 --- a/code/game/jobs/role_authority.dm +++ b/code/game/jobs/role_authority.dm @@ -36,7 +36,6 @@ GLOBAL_VAR_INIT(players_preassigned, 0) var/list/roles_by_path //Master list generated when role aithority is created, listing every role by path, including variable roles. Great for manually equipping with. var/list/roles_by_name //Master list generated when role authority is created, listing every default role by name, including those that may not be regularly selected. var/list/roles_for_mode //Derived list of roles only for the game mode, generated when the round starts. - var/list/roles_whitelist //Associated list of lists, by ckey. Checks to see if a person is whitelisted for a specific role. var/list/castes_by_path //Master list generated when role aithority is created, listing every caste by path. var/list/castes_by_name //Master list generated when role authority is created, listing every default caste by name. @@ -116,57 +115,6 @@ GLOBAL_VAR_INIT(players_preassigned, 0) squads += S squads_by_type[S.type] = S - load_whitelist() - - -/datum/authority/branch/role/proc/load_whitelist(filename = "config/role_whitelist.txt") - var/L[] = file2list(filename) - var/P[] - var/W[] = new //We want a temporary whitelist list, in case we need to reload. - - var/i - var/r - var/ckey - var/role - roles_whitelist = list() - for(i in L) - if(!i) continue - i = trim(i) - if(!length(i)) continue - else if (copytext(i, 1, 2) == "#") continue - - P = splittext(i, "+") - if(!P.len) continue - ckey = ckey(P[1]) //Converting their key to canonical form. ckey() does this by stripping all spaces, underscores and converting to lower case. - - role = NO_FLAGS - r = 1 - while(++r <= P.len) - switch(ckey(P[r])) - if("yautja") role |= WHITELIST_YAUTJA - if("yautjalegacy") role |= WHITELIST_YAUTJA_LEGACY - if("yautjacouncil") role |= WHITELIST_YAUTJA_COUNCIL - if("yautjacouncillegacy") role |= WHITELIST_YAUTJA_COUNCIL_LEGACY - if("yautjaleader") role |= WHITELIST_YAUTJA_LEADER - if("commander") role |= WHITELIST_COMMANDER - if("commandercouncil") role |= WHITELIST_COMMANDER_COUNCIL - if("commandercouncillegacy") role |= WHITELIST_COMMANDER_COUNCIL_LEGACY - if("commanderleader") role |= WHITELIST_COMMANDER_LEADER - if("workingjoe") role |= WHITELIST_JOE - if("synthetic") role |= (WHITELIST_SYNTHETIC|WHITELIST_JOE) - if("syntheticcouncil") role |= WHITELIST_SYNTHETIC_COUNCIL - if("syntheticcouncillegacy") role |= WHITELIST_SYNTHETIC_COUNCIL_LEGACY - if("syntheticleader") role |= WHITELIST_SYNTHETIC_LEADER - if("advisor") role |= WHITELIST_MENTOR - if("allgeneral") role |= WHITELISTS_GENERAL - if("allcouncil") role |= (WHITELISTS_COUNCIL|WHITELISTS_GENERAL) - if("alllegacycouncil") role |= (WHITELISTS_LEGACY_COUNCIL|WHITELISTS_GENERAL) - if("everything", "allleader") role |= WHITELIST_EVERYTHING - - W[ckey] = role - - roles_whitelist = W - //#undef FACTION_TO_JOIN /* @@ -415,7 +363,7 @@ I hope it's easier to tell what the heck this proc is even doing, unlike previou return FALSE if(!J.can_play_role(M.client)) return FALSE - if(J.flags_startup_parameters & ROLE_WHITELISTED && !(roles_whitelist[M.ckey] & J.flags_whitelist)) + if(!J.check_whitelist_status(M)) return FALSE if(J.total_positions != -1 && J.get_total_positions(latejoin) <= J.current_positions) return FALSE @@ -518,7 +466,7 @@ I hope it's easier to tell what the heck this proc is even doing, unlike previou new_job.handle_job_options(new_human.client.prefs.pref_special_job_options[new_job.title]) var/job_whitelist = new_job.title - var/whitelist_status = new_job.get_whitelist_status(roles_whitelist, new_human.client) + var/whitelist_status = new_job.get_whitelist_status(new_human.client) if(whitelist_status) job_whitelist = "[new_job.title][whitelist_status]" diff --git a/code/game/jobs/whitelist.dm b/code/game/jobs/whitelist.dm index 3a4b94145ca1..09db84fec2c2 100644 --- a/code/game/jobs/whitelist.dm +++ b/code/game/jobs/whitelist.dm @@ -1,12 +1,3 @@ -#define WHITELISTFILE "data/whitelist.txt" - -GLOBAL_LIST_FILE_LOAD(whitelist, WHITELISTFILE) - -/proc/check_whitelist(mob/M /*, rank*/) - if(!CONFIG_GET(flag/usewhitelist) || !GLOB.whitelist) - return 0 - return ("[M.ckey]" in GLOB.whitelist) - /proc/can_play_special_job(client/client, job) if(client.admin_holder && (client.admin_holder.rights & R_ADMIN)) return TRUE @@ -18,35 +9,208 @@ GLOBAL_LIST_FILE_LOAD(whitelist, WHITELISTFILE) return J.can_play_role(client) return TRUE -GLOBAL_LIST_FILE_LOAD(alien_whitelist, "config/alienwhitelist.txt") - -//todo: admin aliens -/proc/is_alien_whitelisted(mob/M, species) - if(!CONFIG_GET(flag/usealienwhitelist)) //If there's not config to use the whitelist. - return 1 - if(species == "human" || species == "Human") - return 1 -// if(check_rights(R_ADMIN, 0)) //Admins are not automatically considered to be whitelisted anymore. ~N -// return 1 //This actually screwed up a bunch of procs, but I only noticed it with the wrong spawn point. - if(!CONFIG_GET(flag/usealienwhitelist) || !GLOB.alien_whitelist) - return 0 - if(M && species) - for (var/s in GLOB.alien_whitelist) - if(findtext(lowertext(s),"[lowertext(M.key)] - [species]")) - return 1 - //if(findtext(lowertext(s),"[lowertext(M.key)] - [species] Elder")) //Unnecessary. - // return 1 - if(findtext(lowertext(s),"[lowertext(M.key)] - All")) - return 1 - return 0 - /// returns a list of strings containing the whitelists held by a specific ckey /proc/get_whitelisted_roles(ckey) - if(GLOB.RoleAuthority.roles_whitelist[ckey] & WHITELIST_PREDATOR) + var/datum/entity/player/player = get_player_from_key(ckey) + if(player.check_whitelist_status(WHITELIST_YAUTJA)) LAZYADD(., "predator") - if(GLOB.RoleAuthority.roles_whitelist[ckey] & WHITELIST_COMMANDER) + if(player.check_whitelist_status(WHITELIST_COMMANDER)) LAZYADD(., "commander") - if(GLOB.RoleAuthority.roles_whitelist[ckey] & WHITELIST_SYNTHETIC) + if(player.check_whitelist_status(WHITELIST_SYNTHETIC)) LAZYADD(., "synthetic") -#undef WHITELISTFILE +/client/load_player_data_info(datum/entity/player/player) + . = ..() + + if(isSenator(src)) + add_verb(src, /client/proc/whitelist_panel) + +/client + var/datum/whitelist_panel/wl_panel + +/client/proc/whitelist_panel() + set name = "Whitelist Panel" + set category = "Admin.Panels" + + if(wl_panel) + qdel(wl_panel) + wl_panel = new + wl_panel.tgui_interact(mob) + +#define WL_PANEL_RIGHT_CO (1<<0) +#define WL_PANEL_RIGHT_SYNTH (1<<1) +#define WL_PANEL_RIGHT_YAUTJA (1<<2) +#define WL_PANEL_RIGHT_MENTOR (1<<3) +#define WL_PANEL_RIGHT_OVERSEER (1<<4) +#define WL_PANEL_ALL_COUNCILS (WL_PANEL_RIGHT_CO|WL_PANEL_RIGHT_SYNTH|WL_PANEL_RIGHT_YAUTJA) +#define WL_PANEL_ALL_RIGHTS (WL_PANEL_RIGHT_CO|WL_PANEL_RIGHT_SYNTH|WL_PANEL_RIGHT_YAUTJA|WL_PANEL_RIGHT_MENTOR|WL_PANEL_RIGHT_OVERSEER) + +/datum/whitelist_panel + var/viewed_player = list() + var/current_menu = "Panel" + var/user_rights = 0 + var/target_rights = 0 + var/new_rights = 0 + +/datum/whitelist_panel/proc/get_user_rights(mob/user) + if(!user.client) + return + var/client/person = user.client + if(CLIENT_HAS_RIGHTS(person, R_PERMISSIONS)) + return WL_PANEL_ALL_RIGHTS + var/rights + if(person.check_whitelist_status(WHITELIST_COMMANDER_LEADER)) + rights |= WL_PANEL_RIGHT_CO + if(person.check_whitelist_status(WHITELIST_SYNTHETIC_LEADER)) + rights |= WL_PANEL_RIGHT_SYNTH + if(person.check_whitelist_status(WHITELIST_YAUTJA_LEADER)) + rights |= WL_PANEL_RIGHT_YAUTJA + if(rights == WL_PANEL_ALL_COUNCILS) + return WL_PANEL_ALL_RIGHTS + return rights + +/datum/whitelist_panel/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "WhitelistPanel", "Whitelist Panel") + ui.open() + +/datum/whitelist_panel/ui_state(mob/user) + return GLOB.always_state + +/datum/whitelist_panel/ui_close(mob/user) + . = ..() + if(user?.client.wl_panel) + qdel(user.client.wl_panel) + +/datum/whitelist_panel/vv_edit_var(var_name, var_value) + return FALSE + +/datum/whitelist_panel/ui_data(mob/user) + var/list/data = list() + + data["current_menu"] = current_menu + data["user_rights"] = user_rights + data["viewed_player"] = viewed_player + data["target_rights"] = target_rights + data["new_rights"] = new_rights + + return data + +GLOBAL_LIST_INIT(co_flags, list( + list(name = "Commander", bitflag = WHITELIST_COMMANDER, permission = WL_PANEL_RIGHT_CO), + list(name = "Council", bitflag = WHITELIST_COMMANDER_COUNCIL, permission = WL_PANEL_RIGHT_CO), + list(name = "Legacy Council", bitflag = WHITELIST_COMMANDER_COUNCIL_LEGACY, permission = WL_PANEL_RIGHT_CO), + list(name = "Senator", bitflag = WHITELIST_COMMANDER_LEADER, permission = WL_PANEL_RIGHT_OVERSEER), + list(name = "Colonel", bitflag = WHITELIST_COMMANDER_COLONEL, permission = WL_PANEL_RIGHT_OVERSEER) +)) +GLOBAL_LIST_INIT(syn_flags, list( + list(name = "Synthetic", bitflag = WHITELIST_SYNTHETIC, permission = WL_PANEL_RIGHT_SYNTH), + list(name = "Council", bitflag = WHITELIST_SYNTHETIC_COUNCIL, permission = WL_PANEL_RIGHT_SYNTH), + list(name = "Legacy Council", bitflag = WHITELIST_SYNTHETIC_COUNCIL_LEGACY, permission = WL_PANEL_RIGHT_SYNTH), + list(name = "Senator", bitflag = WHITELIST_SYNTHETIC_LEADER, permission = WL_PANEL_RIGHT_OVERSEER) +)) +GLOBAL_LIST_INIT(yaut_flags, list( + list(name = "Yautja", bitflag = WHITELIST_YAUTJA, permission = WL_PANEL_RIGHT_YAUTJA), + list(name = "Legacy Holder", bitflag = WHITELIST_YAUTJA_LEGACY, permission = WL_PANEL_RIGHT_OVERSEER), + list(name = "Council", bitflag = WHITELIST_YAUTJA_COUNCIL, permission = WL_PANEL_RIGHT_YAUTJA), + list(name = "Legacy Council", bitflag = WHITELIST_YAUTJA_COUNCIL_LEGACY, permission = WL_PANEL_RIGHT_YAUTJA), + list(name = "Senator", bitflag = WHITELIST_YAUTJA_LEADER, permission = WL_PANEL_RIGHT_OVERSEER) +)) +GLOBAL_LIST_INIT(misc_flags, list( + list(name = "Senior Enlisted Advisor", bitflag = WHITELIST_MENTOR, permission = WL_PANEL_RIGHT_MENTOR), + list(name = "Working Joe", bitflag = WHITELIST_JOE, permission = WL_PANEL_RIGHT_SYNTH), +)) + +/datum/whitelist_panel/ui_static_data(mob/user) + . = list() + .["co_flags"] = GLOB.co_flags + .["syn_flags"] = GLOB.syn_flags + .["yaut_flags"] = GLOB.yaut_flags + .["misc_flags"] = GLOB.misc_flags + + var/list/datum/view_record/players/players_view = DB_VIEW(/datum/view_record/players, DB_COMP("whitelist_status", DB_NOTEQUAL, "")) + + var/list/whitelisted_players = list() + for(var/datum/view_record/players/whitelistee in players_view) + var/list/current_player = list() + current_player["ckey"] = whitelistee.ckey + var/list/unreadable_list = splittext(whitelistee.whitelist_status, "|") + var/readable_list = unreadable_list.Join(" | ") + current_player["status"] = readable_list + whitelisted_players += list(current_player) + .["whitelisted_players"] = whitelisted_players + +/datum/whitelist_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + var/mob/user = ui.user + switch(action) + if("go_back") + go_back() + if("select_player") + select_player(user, params["player"]) + return + if("add_player") + select_player(user, TRUE) + return + if("update_number") + new_rights = text2num(params["wl_flag"]) + return + if("update_perms") + var/player_key = params["player"] + var/reason = tgui_input_text(user, "What is the reason for this change?", "Update Reason") + if(!reason) + return + var/datum/entity/player/player = get_player_from_key(player_key) + player.set_whitelist_status(new_rights) + to_chat(user, SPAN_HELPFUL("Whitelists for [player_key] updated.")) + message_admins("Whitelists for [player_key] updated by [key_name(user)]. Reason: '[reason]'.") + log_admin("WHITELISTS: Flags for [player_key] changed from [target_rights] to [new_rights]. Reason: '[reason]'.") + go_back() + update_static_data(user, ui) + return + if("refresh_data") + update_static_data(user, ui) + to_chat(user, SPAN_NOTICE("Whitelist data refreshed.")) + +/datum/whitelist_panel/proc/select_player(mob/user, player_key) + var/target_key = player_key + if(IsAdminAdvancedProcCall()) + return PROC_BLOCKED + if(!target_key) + return FALSE + + if(target_key == TRUE) + var/new_player = tgui_input_text(user, "Enter the new ckey you wish to add. Do not include spaces or special characters.", "New Whitelistee") + if(!new_player) + return FALSE + target_key = new_player + + var/datum/entity/player/player = get_player_from_key(target_key) + var/list/current_player = list() + current_player["ckey"] = target_key + current_player["status"] = player.whitelist_status + + target_rights = player.whitelist_flags + new_rights = player.whitelist_flags + viewed_player = current_player + current_menu = "Update" + user_rights = get_user_rights(user) + return + +/datum/whitelist_panel/proc/go_back() + viewed_player = list() + user_rights = 0 + current_menu = "Panel" + target_rights = 0 + new_rights = 0 + + +#undef WL_PANEL_RIGHT_CO +#undef WL_PANEL_RIGHT_SYNTH +#undef WL_PANEL_RIGHT_YAUTJA +#undef WL_PANEL_RIGHT_MENTOR +#undef WL_PANEL_RIGHT_OVERSEER +#undef WL_PANEL_ALL_RIGHTS diff --git a/code/game/verbs/records.dm b/code/game/verbs/records.dm index 6b80d19bbabf..743a09d0ab6e 100644 --- a/code/game/verbs/records.dm +++ b/code/game/verbs/records.dm @@ -97,13 +97,13 @@ return target = ckey(target) - if(GLOB.RoleAuthority.roles_whitelist[src.ckey] & WHITELIST_COMMANDER_COUNCIL) + if(check_whitelist_status(WHITELIST_COMMANDER_COUNCIL)) options |= "Commanding Officer" edit_C = TRUE - if(GLOB.RoleAuthority.roles_whitelist[src.ckey] & WHITELIST_SYNTHETIC_COUNCIL) + if(check_whitelist_status(WHITELIST_SYNTHETIC_COUNCIL)) options |= "Synthetic" edit_S = TRUE - if(GLOB.RoleAuthority.roles_whitelist[src.ckey] & WHITELIST_YAUTJA_COUNCIL) + if(check_whitelist_status(WHITELIST_YAUTJA_COUNCIL)) options |= "Yautja" edit_Y = TRUE @@ -116,17 +116,17 @@ if("Merit") show_other_record(NOTE_MERIT, choice, target, TRUE) if("Commanding Officer") - if(MA || (GLOB.RoleAuthority.roles_whitelist[src.ckey] & WHITELIST_COMMANDER_LEADER)) + if(MA || check_whitelist_status(WHITELIST_COMMANDER_LEADER)) show_other_record(NOTE_COMMANDER, choice, target, TRUE, TRUE) else show_other_record(NOTE_COMMANDER, choice, target, edit_C) if("Synthetic") - if(MA || (GLOB.RoleAuthority.roles_whitelist[src.ckey] & WHITELIST_SYNTHETIC_LEADER)) + if(MA || check_whitelist_status(WHITELIST_SYNTHETIC_LEADER)) show_other_record(NOTE_SYNTHETIC, choice, target, TRUE, TRUE) else show_other_record(NOTE_SYNTHETIC, choice, target, edit_S) if("Yautja") - if(MA || (GLOB.RoleAuthority.roles_whitelist[src.ckey] & WHITELIST_YAUTJA_LEADER)) + if(MA || check_whitelist_status(WHITELIST_YAUTJA_LEADER)) show_other_record(NOTE_YAUTJA, choice, target, TRUE, TRUE) else show_other_record(NOTE_YAUTJA, choice, target, edit_Y) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 4623df8a5dc5..56002d139599 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -190,7 +190,6 @@ GLOBAL_LIST_INIT(admin_verbs_debug, list( /client/proc/cmd_admin_delete, /client/proc/cmd_debug_del_all, /client/proc/reload_admins, - /client/proc/reload_whitelist, /client/proc/restart_controller, /client/proc/debug_controller, /client/proc/cmd_debug_toggle_should_check_for_win, @@ -243,7 +242,8 @@ GLOBAL_LIST_INIT(admin_verbs_possess, list( )) GLOBAL_LIST_INIT(admin_verbs_permissions, list( - /client/proc/ToRban + /client/proc/ToRban, + /client/proc/whitelist_panel, )) GLOBAL_LIST_INIT(admin_verbs_color, list( @@ -347,15 +347,9 @@ GLOBAL_LIST_INIT(roundstart_mod_verbs, list( add_verb(src, GLOB.admin_verbs_spawn) if(CLIENT_HAS_RIGHTS(src, R_STEALTH)) add_verb(src, GLOB.admin_verbs_stealth) - if(GLOB.RoleAuthority && (GLOB.RoleAuthority.roles_whitelist[ckey] & WHITELIST_YAUTJA_LEADER)) + if(check_whitelist_status(WHITELIST_YAUTJA_LEADER)) add_verb(src, GLOB.clan_verbs) -/client/proc/add_admin_whitelists() - if(CLIENT_IS_MENTOR(src)) - GLOB.RoleAuthority.roles_whitelist[ckey] |= WHITELIST_MENTOR - if(CLIENT_IS_STAFF(src)) - GLOB.RoleAuthority.roles_whitelist[ckey] |= WHITELIST_JOE - /client/proc/remove_admin_verbs() remove_verb(src, list( GLOB.admin_verbs_default, diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm index 13c3b4664a15..dd6516b2ede3 100644 --- a/code/modules/admin/banjob.dm +++ b/code/modules/admin/banjob.dm @@ -42,8 +42,6 @@ GLOBAL_LIST_EMPTY(jobban_keylist) if(guest_jobbans(rank)) if(CONFIG_GET(flag/guest_jobban) && IsGuestKey(M.key)) return "Guest Job-ban" - if(CONFIG_GET(flag/usewhitelist) && !check_whitelist(M)) - return "Whitelisted Job" var/datum/entity/player_job_ban/PJB = M.client.player_data.job_bans[rank] return PJB ? PJB.text : null diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index e31d372743c5..82f25a6ebe3e 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -47,7 +47,6 @@ GLOBAL_PROTECT(href_token) owner = C owner.admin_holder = src owner.add_admin_verbs() - owner.add_admin_whitelists() owner.tgui_say.load() owner.update_special_keybinds() GLOB.admins |= C diff --git a/code/modules/admin/tabs/debug_tab.dm b/code/modules/admin/tabs/debug_tab.dm index df11917f087a..03068c577045 100644 --- a/code/modules/admin/tabs/debug_tab.dm +++ b/code/modules/admin/tabs/debug_tab.dm @@ -161,15 +161,6 @@ message_admins("[usr.ckey] manually reloaded admins.") load_admins() -/client/proc/reload_whitelist() - set name = "Reload Whitelist" - set category = "Debug" - if(alert("Are you sure you want to do this?",, "Yes", "No") != "Yes") return - if(!check_rights(R_SERVER) || !GLOB.RoleAuthority) return - - message_admins("[usr.ckey] manually reloaded the role whitelist.") - GLOB.RoleAuthority.load_whitelist() - /client/proc/bulk_fetcher() set name = "Bulk Fetch Items" set category = "Debug" diff --git a/code/modules/admin/topic/topic.dm b/code/modules/admin/topic/topic.dm index 886ba888ff0a..b1cab3603af8 100644 --- a/code/modules/admin/topic/topic.dm +++ b/code/modules/admin/topic/topic.dm @@ -894,12 +894,7 @@ qdel(M.skills) M.skills = null //no skill restriction - if(is_alien_whitelisted(M,"Yautja Elder")) - M.change_real_name(M, "Elder [y_name]") - H.equip_to_slot_or_del(new /obj/item/clothing/suit/armor/yautja/hunter/full(H), WEAR_JACKET) - H.equip_to_slot_or_del(new /obj/item/weapon/twohanded/yautja/glaive(H), WEAR_L_HAND) - else - M.change_real_name(M, y_name) + M.change_real_name(M, y_name) M.name = "Unknown" // Yautja names are not visible for oomans if(H) diff --git a/code/modules/clans/client.dm b/code/modules/clans/client.dm index c46adf89711a..c4948b2a6923 100644 --- a/code/modules/clans/client.dm +++ b/code/modules/clans/client.dm @@ -5,11 +5,11 @@ set waitfor = FALSE . = ..() - if(GLOB.RoleAuthority && (GLOB.RoleAuthority.roles_whitelist[ckey] & WHITELIST_PREDATOR)) + if(GLOB.RoleAuthority && check_whitelist_status(WHITELIST_PREDATOR)) clan_info = GET_CLAN_PLAYER(player.id) clan_info.sync() - if(GLOB.RoleAuthority.roles_whitelist[ckey] & WHITELIST_YAUTJA_LEADER) + if(check_whitelist_status(WHITELIST_YAUTJA_LEADER)) clan_info.clan_rank = GLOB.clan_ranks_ordered[CLAN_RANK_ADMIN] clan_info.permissions |= CLAN_PERMISSION_ALL else diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 3dfe2d38d81f..44820444a955 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -319,12 +319,9 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list( player_entity = setup_player_entity(ckey) - if(!CONFIG_GET(flag/no_localhost_rank)) - var/static/list/localhost_addresses = list("127.0.0.1", "::1") - if(isnull(address) || (address in localhost_addresses)) - var/datum/admins/admin = new("!localhost!", RL_HOST, ckey) - admin.associate(src) - GLOB.RoleAuthority.roles_whitelist[ckey] = WHITELIST_EVERYTHING + if(check_localhost_status()) + var/datum/admins/admin = new("!localhost!", RL_HOST, ckey) + admin.associate(src) //Admin Authorisation admin_holder = GLOB.admin_datums[ckey] @@ -878,3 +875,41 @@ GLOBAL_LIST_INIT(whitelisted_client_procs, list( if(!selected_action.player_hidden && selected_action.hidden) //Inform the player that even if they are unhiding it, itll still not be visible to_chat(user, SPAN_NOTICE("[selected_action] is forcefully hidden, bypassing player unhiding.")) + + +/client/proc/check_whitelist_status(flag_to_check) + if(check_localhost_status()) + return TRUE + + if((flag_to_check & WHITELIST_MENTOR) && CLIENT_IS_MENTOR(src)) + return TRUE + + if((flag_to_check & WHITELIST_JOE) && CLIENT_IS_STAFF(src)) + return TRUE + + if(!player_data) + load_player_data() + if(!player_data) + return FALSE + + return player_data.check_whitelist_status(flag_to_check) + +/client/proc/check_whitelist_status_list(flags_to_check) /// Logical OR list, not match all. + var/success = FALSE + if(!player_data) + load_player_data() + for(var/bitfield in flags_to_check) + success = player_data.check_whitelist_status(bitfield) + if(success) + break + return success + +/client/proc/check_localhost_status() + if(CONFIG_GET(flag/no_localhost_rank)) + return FALSE + + var/static/list/localhost_addresses = list("127.0.0.1", "::1") + if(isnull(address) || (address in localhost_addresses)) + return TRUE + + return FALSE diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 29676ddb4ac8..221736b5c84f 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -308,13 +308,13 @@ GLOBAL_LIST_INIT(bgstate_options, list( dat += "
" dat += "Human - " dat += "Xenomorph - " - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_COMMANDER) + if(owner.check_whitelist_status(WHITELIST_COMMANDER)) dat += "Commanding Officer - " - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_SYNTHETIC) + if(owner.check_whitelist_status(WHITELIST_SYNTHETIC)) dat += "Synthetic - " - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_PREDATOR) + if(owner.check_whitelist_status(WHITELIST_PREDATOR)) dat += "Yautja - " - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_MENTOR) + if(owner.check_whitelist_status(WHITELIST_MENTOR)) dat += "Mentor - " dat += "Settings - " dat += "Special Roles" @@ -484,7 +484,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( n++ if(MENU_CO) - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_COMMANDER) + if(owner.check_whitelist_status(WHITELIST_COMMANDER)) dat += "
" dat += "

Commander Settings:

" dat += "Commander Whitelist Status: [commander_status]
" @@ -494,7 +494,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( else dat += "You do not have the whitelist for this role." if(MENU_SYNTHETIC) - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_SYNTHETIC) + if(owner.check_whitelist_status(WHITELIST_SYNTHETIC)) dat += "
" dat += "

Synthetic Settings:

" dat += "Synthetic Name: [synthetic_name]
" @@ -504,7 +504,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( else dat += "You do not have the whitelist for this role." if(MENU_YAUTJA) - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_PREDATOR) + if(owner.check_whitelist_status(WHITELIST_PREDATOR)) dat += "
" dat += "

Yautja Information:

" dat += "Yautja Name: [predator_name]
" @@ -518,7 +518,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( dat += "
" dat += "

Equipment Setup:

" - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_YAUTJA_LEGACY) + if(owner.check_whitelist_status(WHITELIST_YAUTJA_LEGACY)) dat += "Legacy Gear: [predator_use_legacy]
" dat += "Translator Type: [predator_translator_type]
" dat += "Mask Style: ([predator_mask_type])
" @@ -542,7 +542,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( else dat += "You do not have the whitelist for this role." if(MENU_MENTOR) - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_MENTOR) + if(owner.check_whitelist_status(WHITELIST_MENTOR)) dat += "Nothing here. For now." else dat += "You do not have the whitelist for this role." @@ -636,7 +636,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( dat += "Spawn as Engineer: [toggles_ert & PLAY_ENGINEER ? "Yes" : "No"]
" dat += "Spawn as Specialist: [toggles_ert & PLAY_HEAVY ? "Yes" : "No"]
" dat += "Spawn as Smartgunner: [toggles_ert & PLAY_SMARTGUNNER ? "Yes" : "No"]
" - if(GLOB.RoleAuthority.roles_whitelist[user.ckey] & WHITELIST_SYNTHETIC) + if(owner.check_whitelist_status(WHITELIST_SYNTHETIC)) dat += "Spawn as Synth: [toggles_ert & PLAY_SYNTH ? "Yes" : "No"]
" dat += "Spawn as Miscellaneous: [toggles_ert & PLAY_MISC ? "Yes" : "No"]
" dat += "
" @@ -684,7 +684,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( if(jobban_isbanned(user, job.title)) HTML += "[job.disp_title]BANNED" continue - else if(job.flags_startup_parameters & ROLE_WHITELISTED && !(GLOB.RoleAuthority.roles_whitelist[user.ckey] & job.flags_whitelist)) + else if(!job.check_whitelist_status(user)) HTML += "[job.disp_title]WHITELISTED" continue else if(!job.can_play_role(user.client)) @@ -796,7 +796,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( if(jobban_isbanned(user, job.title)) HTML += "[job.disp_title]BANNED" continue - else if(job.flags_startup_parameters & ROLE_WHITELISTED && !(GLOB.RoleAuthority.roles_whitelist[user.ckey] & job.flags_whitelist)) + else if(!job.check_whitelist_status(user)) HTML += "[job.disp_title]WHITELISTED" continue else if(!job.can_play_role(user.client)) @@ -962,7 +962,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( pref_job_slots[J.title] = JOB_SLOT_CURRENT_SLOT /datum/preferences/proc/process_link(mob/user, list/href_list) - var/whitelist_flags = GLOB.RoleAuthority.roles_whitelist[user.ckey] + switch(href_list["preference"]) if("job") @@ -1198,6 +1198,9 @@ GLOBAL_LIST_INIT(bgstate_options, list( if ("all") randomize_appearance() if("input") + var/datum/entity/player/player = get_player_from_key(user.ckey) + var/whitelist_flags = player.whitelist_flags + switch(href_list["preference"]) if("name") if(human_name_ban) @@ -1286,7 +1289,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( predator_caster_material = new_pred_caster_mat if("pred_cape_type") var/datum/job/J = GLOB.RoleAuthority.roles_by_name[JOB_PREDATOR] - var/whitelist_status = GLOB.clan_ranks_ordered[J.get_whitelist_status(GLOB.RoleAuthority.roles_whitelist, owner)] + var/whitelist_status = GLOB.clan_ranks_ordered[J.get_whitelist_status(owner)] var/list/options = list("None" = "None") for(var/cape_name in GLOB.all_yautja_capes) @@ -1325,7 +1328,7 @@ GLOBAL_LIST_INIT(bgstate_options, list( if(whitelist_flags & (WHITELIST_COMMANDER_COUNCIL|WHITELIST_COMMANDER_COUNCIL_LEGACY)) options += list("Council" = WHITELIST_COUNCIL) - if(whitelist_flags & WHITELIST_COMMANDER_LEADER) + if(whitelist_flags & (WHITELIST_COMMANDER_LEADER|WHITELIST_COMMANDER_COLONEL)) options += list("Leader" = WHITELIST_LEADER) var/new_commander_status = tgui_input_list(user, "Choose your new Commander Whitelist Status.", "Commander Status", options) diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 22bd9aa0cf61..a55080dc837d 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -137,8 +137,6 @@ var/datum/action/observer_action/new_action = new path() new_action.give_to(src) - RegisterSignal(SSdcs, COMSIG_GLOB_PREDATOR_ROUND_TOGGLED, PROC_REF(toggle_predator_action)) - if(SSticker.mode && SSticker.mode.flags_round_type & MODE_PREDATOR) addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), src, "This is a PREDATOR ROUND! If you are whitelisted, you may Join the Hunt!"), 2 SECONDS) @@ -319,7 +317,9 @@ /mob/dead/observer/Login() ..() - toggle_predator_action() + if(client.check_whitelist_status(WHITELIST_PREDATOR)) + RegisterSignal(SSdcs, COMSIG_GLOB_PREDATOR_ROUND_TOGGLED, PROC_REF(toggle_predator_action)) + toggle_predator_action() client.move_delay = MINIMAL_MOVEMENT_INTERVAL @@ -1284,9 +1284,6 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp if(!key_to_use) return - if(!(GLOB.RoleAuthority.roles_whitelist[key_to_use] & WHITELIST_PREDATOR)) - return - if(!SSticker.mode) SSticker.OnRoundstart(CALLBACK(src, PROC_REF(toggle_predator_action))) return diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm index ccf649fb0509..810eb4006634 100644 --- a/code/modules/mob/new_player/new_player.dm +++ b/code/modules/mob/new_player/new_player.dm @@ -168,16 +168,6 @@ tutorial_menu() return - if(client.prefs.species != "Human") - if(!is_alien_whitelisted(src, client.prefs.species) && CONFIG_GET(flag/usealienwhitelist)) - to_chat(src, "You are currently not whitelisted to play [client.prefs.species].") - return - - var/datum/species/S = GLOB.all_species[client.prefs.species] - if(!(S.flags & IS_WHITELISTED)) - to_chat(src, alert("Your current species,[client.prefs.species], is not available for play on the station.")) - return - LateChoices() if("late_join_xeno") @@ -217,16 +207,6 @@ to_chat(usr, SPAN_WARNING("There is an administrative lock on entering the game! (The dropship likely crashed into the Almayer. This should take at most 20 minutes.)")) return - if(client.prefs.species != "Human") - if(!is_alien_whitelisted(src, client.prefs.species) && CONFIG_GET(flag/usealienwhitelist)) - to_chat(src, alert("You are currently not whitelisted to play [client.prefs.species].")) - return 0 - - var/datum/species/S = GLOB.all_species[client.prefs.species] - if(!(S.flags & IS_WHITELISTED)) - to_chat(src, alert("Your current species,[client.prefs.species], is not available for play on the station.")) - return 0 - AttemptLateSpawn(href_list["job_selected"]) return @@ -382,14 +362,6 @@ var/mob/living/carbon/human/new_character - var/datum/species/chosen_species - if(client.prefs.species) - chosen_species = GLOB.all_species[client.prefs.species] - if(chosen_species) - // Have to recheck admin due to no usr at roundstart. Latejoins are fine though. - if(is_species_whitelisted(chosen_species) || has_admin_rights()) - new_character = new(loc, client.prefs.species) - if(!new_character) new_character = new(loc) @@ -456,26 +428,6 @@ ui.close() continue -/mob/new_player/proc/has_admin_rights() - return client.admin_holder.rights & R_ADMIN - -/mob/new_player/proc/is_species_whitelisted(datum/species/S) - if(!S) return 1 - return is_alien_whitelisted(src, S.name) || !CONFIG_GET(flag/usealienwhitelist) || !(S.flags & IS_WHITELISTED) - -/mob/new_player/get_species() - var/datum/species/chosen_species - if(client.prefs.species) - chosen_species = GLOB.all_species[client.prefs.species] - - if(!chosen_species) - return "Human" - - if(is_species_whitelisted(chosen_species) || has_admin_rights()) - return chosen_species.name - - return "Human" - /mob/new_player/get_gender() if(!client || !client.prefs) ..() return client.prefs.gender diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index a4525e16a6c6..7e8439cee244 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -247,10 +247,8 @@ if(JOB_SQUAD_TEAM_LEADER) return /datum/equipment_preset/uscm/tl_equipped if(JOB_CO) - if(length(GLOB.RoleAuthority.roles_whitelist)) - var/datum/job/J = GLOB.RoleAuthority.roles_by_name[JOB_CO] - return J.gear_preset_whitelist["[JOB_CO][J.get_whitelist_status(GLOB.RoleAuthority.roles_whitelist, owner)]"] - return /datum/equipment_preset/uscm_ship/commander + var/datum/job/J = GLOB.RoleAuthority.roles_by_name[JOB_CO] + return J.gear_preset_whitelist["[JOB_CO][J.get_whitelist_status(owner)]"] if(JOB_SO) return /datum/equipment_preset/uscm_ship/so if(JOB_XO) @@ -268,10 +266,8 @@ if(JOB_COMBAT_REPORTER) return /datum/equipment_preset/uscm_ship/reporter if(JOB_SYNTH) - if(length(GLOB.RoleAuthority.roles_whitelist)) - var/datum/job/J = GLOB.RoleAuthority.roles_by_name[JOB_SYNTH] - return J.gear_preset_whitelist["[JOB_SYNTH][J.get_whitelist_status(GLOB.RoleAuthority.roles_whitelist, owner)]"] - return /datum/equipment_preset/synth/uscm + var/datum/job/J = GLOB.RoleAuthority.roles_by_name[JOB_SYNTH] + return J.gear_preset_whitelist["[JOB_SYNTH][J.get_whitelist_status(owner)]"] if(JOB_WORKING_JOE) return /datum/equipment_preset/synth/working_joe if(JOB_POLICE) @@ -317,10 +313,8 @@ return pick(SSmapping.configs[GROUND_MAP].CO_survivor_types) return /datum/equipment_preset/uscm_ship/commander if(JOB_PREDATOR) - if(length(GLOB.RoleAuthority.roles_whitelist)) - var/datum/job/J = GLOB.RoleAuthority.roles_by_name[JOB_PREDATOR] - return J.gear_preset_whitelist["[JOB_PREDATOR][J.get_whitelist_status(GLOB.RoleAuthority.roles_whitelist, owner)]"] - return /datum/equipment_preset/yautja/blooded + var/datum/job/J = GLOB.RoleAuthority.roles_by_name[JOB_PREDATOR] + return J.gear_preset_whitelist["[JOB_PREDATOR][J.get_whitelist_status(owner)]"] return /datum/equipment_preset/uscm/private_equipped diff --git a/tgui/packages/tgui/interfaces/WhitelistPanel.jsx b/tgui/packages/tgui/interfaces/WhitelistPanel.jsx new file mode 100644 index 000000000000..128045bf2694 --- /dev/null +++ b/tgui/packages/tgui/interfaces/WhitelistPanel.jsx @@ -0,0 +1,288 @@ +import { useBackend } from '../backend'; +import { Button, Stack, Section, Flex } from '../components'; +import { Window } from '../layouts'; + +const PAGES = { + 'Panel': () => PlayerList, + 'Update': () => StatusUpdate, +}; + +export const WhitelistPanel = (props, context) => { + const { data } = useBackend(context); + const { current_menu } = data; + const PageComponent = PAGES[current_menu](); + + return ( + + + + + + ); +}; + +const PlayerList = (props, context) => { + const { data, act } = useBackend(context); + const { whitelisted_players } = data; + + return ( +
+ + +
+ ); +}; + +const StatusUpdate = (props, context) => { + const { act, data } = useBackend(context); + const { + co_flags, + syn_flags, + yaut_flags, + misc_flags, + viewed_player, + user_rights, + target_rights, + new_rights, + } = data; + return ( +
+ +
+
+ + {syn_flags.map((bit, i) => { + const isWhitelisted = target_rights && target_rights & bit.bitflag; + return ( +
+
+ + {yaut_flags.map((bit, i) => { + const isWhitelisted = target_rights && target_rights & bit.bitflag; + return ( +
+
+ + {misc_flags.map((bit, i) => { + const isWhitelisted = target_rights && target_rights & bit.bitflag; + return ( +
+ + @@ -82,9 +82,9 @@ export class TguiSay extends Component<{}, State> { )} diff --git a/tgui/packages/tgui-say/package.json b/tgui/packages/tgui-say/package.json index 5ee9e432606f..6fc66ade82f1 100644 --- a/tgui/packages/tgui-say/package.json +++ b/tgui/packages/tgui-say/package.json @@ -3,8 +3,9 @@ "name": "tgui-say", "version": "1.0.0", "dependencies": { + "@types/react": "^18.2.39", "common": "workspace:*", - "inferno": "^7.4.8", + "react": "^18.2.0", "tgui": "workspace:*", "tgui-polyfill": "workspace:*" } diff --git a/tgui/packages/tgui-say/timers.ts b/tgui/packages/tgui-say/timers.ts new file mode 100644 index 000000000000..7b9fffc87fc2 --- /dev/null +++ b/tgui/packages/tgui-say/timers.ts @@ -0,0 +1,19 @@ +import { debounce, throttle } from 'common/timer'; + +const SECONDS = 1000; + +/** Timers: Prevents overloading the server, throttles messages */ +export const byondMessages = { + // Debounce: Prevents spamming the server + channelIncrementMsg: debounce( + (visible: boolean) => Byond.sendMessage('thinking', { visible }), + 0.4 * SECONDS + ), + forceSayMsg: debounce( + (entry: string) => Byond.sendMessage('force', { entry, channel: 'Say' }), + 1 * SECONDS, + true + ), + // Throttle: Prevents spamming the server + typingMsg: throttle(() => Byond.sendMessage('typing'), 4 * SECONDS), +} as const; diff --git a/tgui/packages/tgui-say/types/index.tsx b/tgui/packages/tgui-say/types/index.tsx index abd29630ff68..02da4b1ddea7 100644 --- a/tgui/packages/tgui-say/types/index.tsx +++ b/tgui/packages/tgui-say/types/index.tsx @@ -1,4 +1,4 @@ -import { RefObject } from 'inferno'; +import { RefObject } from 'react'; export type Modal = { events: Events; diff --git a/tgui/packages/tgui/backend.ts b/tgui/packages/tgui/backend.ts index dd4cae9e6227..746e511a5027 100644 --- a/tgui/packages/tgui/backend.ts +++ b/tgui/packages/tgui/backend.ts @@ -21,6 +21,12 @@ import { resumeRenderer, suspendRenderer } from './renderer'; const logger = createLogger('backend'); +export let globalStore; + +export const setGlobalStore = (store) => { + globalStore = store; +}; + export const backendUpdate = createAction('backend/update'); export const backendSetSharedState = createAction('backend/setSharedState'); export const backendSuspendStart = createAction('backend/suspendStart'); @@ -267,6 +273,10 @@ type BackendState = { shared: Record; suspending: boolean; suspended: boolean; + debug?: { + debugLayout: boolean; + kitchenSink: boolean; + }; }; /** @@ -280,9 +290,9 @@ export const selectBackend = (state: any): BackendState => * * Includes the `act` function for performing DM actions. */ -export const useBackend = (context: any) => { - const { store } = context; - const state = selectBackend(store.getState()); +export const useBackend = () => { + const state: BackendState = globalStore?.getState()?.backend; + return { ...state, act: sendAct, @@ -308,18 +318,16 @@ type StateWithSetter = [T, (nextState: T) => void]; * @param initialState Initializes your global variable with this value. */ export const useLocalState = ( - context: any, key: string, initialState: T ): StateWithSetter => { - const { store } = context; - const state = selectBackend(store.getState()); - const sharedStates = state.shared ?? {}; + const state = globalStore?.getState()?.backend; + const sharedStates = state?.shared ?? {}; const sharedState = key in sharedStates ? sharedStates[key] : initialState; return [ sharedState, (nextState) => { - store.dispatch( + globalStore.dispatch( backendSetSharedState({ key, nextState: @@ -347,27 +355,31 @@ export const useLocalState = ( * @param initialState Initializes your global variable with this value. */ export const useSharedState = ( - context: any, key: string, initialState: T ): StateWithSetter => { - const { store } = context; - const state = selectBackend(store.getState()); - const sharedStates = state.shared ?? {}; + const state = globalStore?.getState()?.backend; + const sharedStates = state?.shared ?? {}; const sharedState = key in sharedStates ? sharedStates[key] : initialState; return [ sharedState, (nextState) => { - // prettier-ignore Byond.sendMessage({ type: 'setSharedState', key, - value: JSON.stringify( - typeof nextState === 'function' - ? nextState(sharedState) - : nextState - ) || '', + value: + JSON.stringify( + typeof nextState === 'function' ? nextState(sharedState) : nextState + ) || '', }); }, ]; }; + +export const useDispatch = () => { + return globalStore.dispatch; +}; + +export const useSelector = (selector: (state: any) => any) => { + return selector(globalStore?.getState()); +}; diff --git a/tgui/packages/tgui/components/AnimatedNumber.tsx b/tgui/packages/tgui/components/AnimatedNumber.tsx index ed3be2e5ef3d..956dc0a8232c 100644 --- a/tgui/packages/tgui/components/AnimatedNumber.tsx +++ b/tgui/packages/tgui/components/AnimatedNumber.tsx @@ -5,7 +5,7 @@ */ import { clamp, toFixed } from 'common/math'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; const isSafeNumber = (value: number) => { // prettier-ignore diff --git a/tgui/packages/tgui/components/Autofocus.tsx b/tgui/packages/tgui/components/Autofocus.tsx index 28945dd7aa48..a0b3f6f76590 100644 --- a/tgui/packages/tgui/components/Autofocus.tsx +++ b/tgui/packages/tgui/components/Autofocus.tsx @@ -1,19 +1,17 @@ -import { Component, createRef } from 'inferno'; +import { createRef, PropsWithChildren, useEffect } from 'react'; -export class Autofocus extends Component { - ref = createRef(); +export const Autofocus = (props: PropsWithChildren) => { + const ref = createRef(); - componentDidMount() { + useEffect(() => { setTimeout(() => { - this.ref.current?.focus(); + ref.current?.focus(); }, 1); - } + }, []); - render() { - return ( -
- {this.props.children} -
- ); - } -} + return ( +
+ {props.children} +
+ ); +}; diff --git a/tgui/packages/tgui/components/Blink.jsx b/tgui/packages/tgui/components/Blink.jsx index bd781336b463..70d2f1393eb2 100644 --- a/tgui/packages/tgui/components/Blink.jsx +++ b/tgui/packages/tgui/components/Blink.jsx @@ -1,4 +1,4 @@ -import { Component } from 'inferno'; +import { Component } from 'react'; const DEFAULT_BLINKING_INTERVAL = 1000; const DEFAULT_BLINKING_TIME = 1000; diff --git a/tgui/packages/tgui/components/BodyZoneSelector.tsx b/tgui/packages/tgui/components/BodyZoneSelector.tsx index eb845458cd23..5daf2960a570 100644 --- a/tgui/packages/tgui/components/BodyZoneSelector.tsx +++ b/tgui/packages/tgui/components/BodyZoneSelector.tsx @@ -1,4 +1,4 @@ -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; import { resolveAsset } from '../assets'; import { Box } from './Box'; diff --git a/tgui/packages/tgui/components/Box.tsx b/tgui/packages/tgui/components/Box.tsx index ed10072b6239..8da3235553fa 100644 --- a/tgui/packages/tgui/components/Box.tsx +++ b/tgui/packages/tgui/components/Box.tsx @@ -4,59 +4,58 @@ * @license MIT */ -import { BooleanLike, classes, pureComponentHooks } from 'common/react'; -import { createVNode, InfernoNode, SFC } from 'inferno'; -import { ChildFlags, VNodeFlags } from 'inferno-vnode-flags'; +import { BooleanLike, classes } from 'common/react'; +import { createElement, ReactNode } from 'react'; import { CSS_COLORS } from '../constants'; export type BoxProps = { [key: string]: any; - as?: string; - className?: string | BooleanLike; - children?: InfernoNode; - position?: string | BooleanLike; - overflow?: string | BooleanLike; - overflowX?: string | BooleanLike; - overflowY?: string | BooleanLike; - top?: string | BooleanLike; - bottom?: string | BooleanLike; - left?: string | BooleanLike; - right?: string | BooleanLike; - width?: string | BooleanLike; - minWidth?: string | BooleanLike; - maxWidth?: string | BooleanLike; - height?: string | BooleanLike; - minHeight?: string | BooleanLike; - maxHeight?: string | BooleanLike; - fontSize?: string | BooleanLike; - fontFamily?: string; - lineHeight?: string | BooleanLike; - opacity?: number; - textAlign?: string | BooleanLike; - verticalAlign?: string | BooleanLike; - inline?: BooleanLike; - bold?: BooleanLike; - italic?: BooleanLike; - nowrap?: BooleanLike; - preserveWhitespace?: BooleanLike; - m?: string | BooleanLike; - mx?: string | BooleanLike; - my?: string | BooleanLike; - mt?: string | BooleanLike; - mb?: string | BooleanLike; - ml?: string | BooleanLike; - mr?: string | BooleanLike; - p?: string | BooleanLike; - px?: string | BooleanLike; - py?: string | BooleanLike; - pt?: string | BooleanLike; - pb?: string | BooleanLike; - pl?: string | BooleanLike; - pr?: string | BooleanLike; - color?: string | BooleanLike; - textColor?: string | BooleanLike; - backgroundColor?: string | BooleanLike; - fillPositionedParent?: boolean; + readonly as?: string; + readonly className?: string | BooleanLike; + readonly children?: ReactNode; + readonly position?: string | BooleanLike; + readonly overflow?: string | BooleanLike; + readonly overflowX?: string | BooleanLike; + readonly overflowY?: string | BooleanLike; + readonly top?: string | BooleanLike; + readonly bottom?: string | BooleanLike; + readonly left?: string | BooleanLike; + readonly right?: string | BooleanLike; + readonly width?: string | BooleanLike; + readonly minWidth?: string | BooleanLike; + readonly maxWidth?: string | BooleanLike; + readonly height?: string | BooleanLike; + readonly minHeight?: string | BooleanLike; + readonly maxHeight?: string | BooleanLike; + readonly fontSize?: string | BooleanLike; + readonly fontFamily?: string; + readonly lineHeight?: string | BooleanLike; + readonly opacity?: number; + readonly textAlign?: string | BooleanLike; + readonly verticalAlign?: string | BooleanLike; + readonly inline?: BooleanLike; + readonly bold?: BooleanLike; + readonly italic?: BooleanLike; + readonly nowrap?: BooleanLike; + readonly preserveWhitespace?: BooleanLike; + readonly m?: string | BooleanLike; + readonly mx?: string | BooleanLike; + readonly my?: string | BooleanLike; + readonly mt?: string | BooleanLike; + readonly mb?: string | BooleanLike; + readonly ml?: string | BooleanLike; + readonly mr?: string | BooleanLike; + readonly p?: string | BooleanLike; + readonly px?: string | BooleanLike; + readonly py?: string | BooleanLike; + readonly pt?: string | BooleanLike; + readonly pb?: string | BooleanLike; + readonly pl?: string | BooleanLike; + readonly pr?: string | BooleanLike; + readonly color?: string | BooleanLike; + readonly textColor?: string | BooleanLike; + readonly backgroundColor?: string | BooleanLike; + readonly fillPositionedParent?: boolean; }; /** @@ -65,15 +64,12 @@ export type BoxProps = { export const unit = (value: unknown): string | undefined => { if (typeof value === 'string') { // Transparently convert pixels into rem units - if (value.endsWith('px') && !Byond.IS_LTE_IE8) { + if (value.endsWith('px')) { return parseFloat(value) / 12 + 'rem'; } return value; } if (typeof value === 'number') { - if (Byond.IS_LTE_IE8) { - return value * 12 + 'px'; - } return value + 'rem'; } }; @@ -128,70 +124,66 @@ const mapColorPropTo = (attrName) => (style, value) => { } }; -const styleMapperByPropName = { - // Direct mapping - position: mapRawPropTo('position'), - overflow: mapRawPropTo('overflow'), - overflowX: mapRawPropTo('overflow-x'), - overflowY: mapRawPropTo('overflow-y'), - top: mapUnitPropTo('top', unit), +// String / number props +const stringStyleMap = { bottom: mapUnitPropTo('bottom', unit), + fontFamily: mapRawPropTo('fontFamily'), + fontSize: mapUnitPropTo('fontSize', unit), + height: mapUnitPropTo('height', unit), left: mapUnitPropTo('left', unit), + maxHeight: mapUnitPropTo('maxHeight', unit), + maxWidth: mapUnitPropTo('maxWidth', unit), + minHeight: mapUnitPropTo('minHeight', unit), + minWidth: mapUnitPropTo('minWidth', unit), + opacity: mapRawPropTo('opacity'), + overflow: mapRawPropTo('overflow'), + overflowX: mapRawPropTo('overflowX'), + overflowY: mapRawPropTo('overflowY'), + position: mapRawPropTo('position'), right: mapUnitPropTo('right', unit), + textAlign: mapRawPropTo('textAlign'), + top: mapUnitPropTo('top', unit), + verticalAlign: mapRawPropTo('verticalAlign'), width: mapUnitPropTo('width', unit), - minWidth: mapUnitPropTo('min-width', unit), - maxWidth: mapUnitPropTo('max-width', unit), - height: mapUnitPropTo('height', unit), - minHeight: mapUnitPropTo('min-height', unit), - maxHeight: mapUnitPropTo('max-height', unit), - fontSize: mapUnitPropTo('font-size', unit), - fontFamily: mapRawPropTo('font-family'), + lineHeight: (style, value) => { if (typeof value === 'number') { - style['line-height'] = value; + style['lineHeight'] = value; } else if (typeof value === 'string') { - style['line-height'] = unit(value); + style['lineHeight'] = unit(value); } }, - opacity: mapRawPropTo('opacity'), - textAlign: mapRawPropTo('text-align'), - verticalAlign: mapRawPropTo('vertical-align'), - // Boolean props - inline: mapBooleanPropTo('display', 'inline-block'), - bold: mapBooleanPropTo('font-weight', 'bold'), - italic: mapBooleanPropTo('font-style', 'italic'), - nowrap: mapBooleanPropTo('white-space', 'nowrap'), - preserveWhitespace: mapBooleanPropTo('white-space', 'pre-wrap'), // Margin m: mapDirectionalUnitPropTo('margin', halfUnit, [ - 'top', - 'bottom', - 'left', - 'right', + 'Top', + 'Bottom', + 'Left', + 'Right', ]), - mx: mapDirectionalUnitPropTo('margin', halfUnit, ['left', 'right']), - my: mapDirectionalUnitPropTo('margin', halfUnit, ['top', 'bottom']), - mt: mapUnitPropTo('margin-top', halfUnit), - mb: mapUnitPropTo('margin-bottom', halfUnit), - ml: mapUnitPropTo('margin-left', halfUnit), - mr: mapUnitPropTo('margin-right', halfUnit), + mx: mapDirectionalUnitPropTo('margin', halfUnit, ['Left', 'Right']), + my: mapDirectionalUnitPropTo('margin', halfUnit, ['Top', 'Bottom']), + mt: mapUnitPropTo('marginTop', halfUnit), + mb: mapUnitPropTo('marginBottom', halfUnit), + ml: mapUnitPropTo('marginLeft', halfUnit), + mr: mapUnitPropTo('marginRight', halfUnit), // Padding p: mapDirectionalUnitPropTo('padding', halfUnit, [ - 'top', - 'bottom', - 'left', - 'right', + 'Top', + 'Bottom', + 'Left', + 'Right', ]), - px: mapDirectionalUnitPropTo('padding', halfUnit, ['left', 'right']), - py: mapDirectionalUnitPropTo('padding', halfUnit, ['top', 'bottom']), - pt: mapUnitPropTo('padding-top', halfUnit), - pb: mapUnitPropTo('padding-bottom', halfUnit), - pl: mapUnitPropTo('padding-left', halfUnit), - pr: mapUnitPropTo('padding-right', halfUnit), + px: mapDirectionalUnitPropTo('padding', halfUnit, ['Left', 'Right']), + py: mapDirectionalUnitPropTo('padding', halfUnit, ['Top', 'Bottom']), + pt: mapUnitPropTo('paddingTop', halfUnit), + pb: mapUnitPropTo('paddingBottom', halfUnit), + pl: mapUnitPropTo('paddingLeft', halfUnit), + pr: mapUnitPropTo('paddingRight', halfUnit), // Color props color: mapColorPropTo('color'), textColor: mapColorPropTo('color'), - backgroundColor: mapColorPropTo('background-color'), + backgroundColor: mapColorPropTo('backgroundColor'), + // Utility props fillPositionedParent: (style, value) => { if (value) { @@ -202,44 +194,42 @@ const styleMapperByPropName = { style['right'] = 0; } }, -}; +} as const; + +// Boolean props +const booleanStyleMap = { + bold: mapBooleanPropTo('fontWeight', 'bold'), + inline: mapBooleanPropTo('display', 'inline-block'), + italic: mapBooleanPropTo('fontStyle', 'italic'), + nowrap: mapBooleanPropTo('whiteSpace', 'nowrap'), + preserveWhitespace: mapBooleanPropTo('whiteSpace', 'pre-wrap'), +} as const; + +export const computeBoxProps = (props) => { + const computedProps: Record = {}; + const computedStyles: Record = {}; -export const computeBoxProps = (props: BoxProps) => { - const computedProps: HTMLAttributes = {}; - const computedStyles = {}; // Compute props for (let propName of Object.keys(props)) { if (propName === 'style') { continue; } - // IE8: onclick workaround - if (Byond.IS_LTE_IE8 && propName === 'onClick') { - computedProps.onclick = props[propName]; - continue; - } + const propValue = props[propName]; - const mapPropToStyle = styleMapperByPropName[propName]; + + const mapPropToStyle = + stringStyleMap[propName] || booleanStyleMap[propName]; + if (mapPropToStyle) { mapPropToStyle(computedStyles, propValue); } else { computedProps[propName] = propValue; } } - // Concatenate styles - let style = ''; - for (let attrName of Object.keys(computedStyles)) { - const attrValue = computedStyles[attrName]; - style += attrName + ':' + attrValue + ';'; - } - if (props.style) { - for (let attrName of Object.keys(props.style)) { - const attrValue = props.style[attrName]; - style += attrName + ':' + attrValue + ';'; - } - } - if (style.length > 0) { - computedProps.style = style; - } + + // Merge computed styles and any directly provided styles + computedProps.style = { ...computedStyles, ...props.style }; + return computedProps; }; @@ -252,27 +242,26 @@ export const computeBoxClassName = (props: BoxProps) => { ]); }; -export const Box: SFC = (props: BoxProps) => { +export const Box = (props: BoxProps) => { const { as = 'div', className, children, ...rest } = props; - // Render props - if (typeof children === 'function') { - return children(computeBoxProps(props)); - } - const computedClassName = - typeof className === 'string' - ? className + ' ' + computeBoxClassName(rest) - : computeBoxClassName(rest); + + // Compute class name and styles + const computedClassName = className + ? `${className} ${computeBoxClassName(rest)}` + : computeBoxClassName(rest); const computedProps = computeBoxProps(rest); - // Render a wrapper element - return createVNode( - VNodeFlags.HtmlElement, - as, - computedClassName, - children, - ChildFlags.UnknownChildren, - computedProps, - undefined + + if (as === 'img') { + computedProps.style['-ms-interpolation-mode'] = 'nearest-neighbor'; + } + + // Render the component + return createElement( + typeof as === 'string' ? as : 'div', + { + ...computedProps, + className: computedClassName, + }, + children ); }; - -Box.defaultHooks = pureComponentHooks; diff --git a/tgui/packages/tgui/components/Button.jsx b/tgui/packages/tgui/components/Button.jsx index ef434d04c7aa..4264b0767115 100644 --- a/tgui/packages/tgui/components/Button.jsx +++ b/tgui/packages/tgui/components/Button.jsx @@ -6,7 +6,7 @@ import { KEY_ENTER, KEY_ESCAPE, KEY_SPACE } from 'common/keycodes'; import { classes, pureComponentHooks } from 'common/react'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; import { createLogger } from '../logging'; import { Box, computeBoxClassName, computeBoxProps } from './Box'; import { Icon } from './Icon'; @@ -273,7 +273,7 @@ export class ButtonInput extends Component { className="NumberInput__input" style={{ 'display': !this.state.inInput ? 'none' : undefined, - 'text-align': 'left', + 'textAlign': 'left', }} onBlur={(e) => { if (!this.state.inInput) { diff --git a/tgui/packages/tgui/components/ByondUi.jsx b/tgui/packages/tgui/components/ByondUi.jsx index 4623fe577015..dc263b9482ff 100644 --- a/tgui/packages/tgui/components/ByondUi.jsx +++ b/tgui/packages/tgui/components/ByondUi.jsx @@ -6,7 +6,7 @@ import { shallowDiffers } from 'common/react'; import { debounce } from 'common/timer'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; import { createLogger } from '../logging'; import { computeBoxProps } from './Box'; diff --git a/tgui/packages/tgui/components/Chart.jsx b/tgui/packages/tgui/components/Chart.jsx index fac444bd1da9..b8a9c62f8929 100644 --- a/tgui/packages/tgui/components/Chart.jsx +++ b/tgui/packages/tgui/components/Chart.jsx @@ -6,7 +6,7 @@ import { map, zipWith } from 'common/collections'; import { pureComponentHooks } from 'common/react'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; import { Box } from './Box'; const normalizeData = (data, scale, rangeX, rangeY) => { diff --git a/tgui/packages/tgui/components/Collapsible.jsx b/tgui/packages/tgui/components/Collapsible.jsx index f91eeddb4568..805fc7d129c2 100644 --- a/tgui/packages/tgui/components/Collapsible.jsx +++ b/tgui/packages/tgui/components/Collapsible.jsx @@ -4,7 +4,7 @@ * @license MIT */ -import { Component } from 'inferno'; +import { Component } from 'react'; import { Box } from './Box'; import { Button } from './Button'; diff --git a/tgui/packages/tgui/components/DraggableControl.jsx b/tgui/packages/tgui/components/DraggableControl.jsx index 8ada6f2fa4d0..018118dd7966 100644 --- a/tgui/packages/tgui/components/DraggableControl.jsx +++ b/tgui/packages/tgui/components/DraggableControl.jsx @@ -6,7 +6,7 @@ import { clamp } from 'common/math'; import { pureComponentHooks } from 'common/react'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; import { AnimatedNumber } from './AnimatedNumber'; const DEFAULT_UPDATE_RATE = 400; diff --git a/tgui/packages/tgui/components/Dropdown.jsx b/tgui/packages/tgui/components/Dropdown.jsx deleted file mode 100644 index e6fe8a840a5f..000000000000 --- a/tgui/packages/tgui/components/Dropdown.jsx +++ /dev/null @@ -1,161 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { classes } from 'common/react'; -import { Component } from 'inferno'; -import { Box } from './Box'; -import { Icon } from './Icon'; - -export class Dropdown extends Component { - constructor(props) { - super(props); - this.state = { - selected: props.selected, - open: false, - }; - this.handleClick = () => { - if (this.state.open) { - this.setOpen(false); - } - }; - } - - componentWillUnmount() { - window.removeEventListener('click', this.handleClick); - } - - setOpen(open) { - this.setState({ open: open }); - if (open) { - setTimeout(() => { - window.addEventListener('click', this.handleClick); - }); - this.menuRef.focus(); - } else { - window.removeEventListener('click', this.handleClick); - } - } - - setSelected(selected) { - this.setState({ - selected: selected, - }); - this.setOpen(false); - this.props.onSelected(selected); - } - - buildMenu() { - const { options = [] } = this.props; - const ops = options.map((option) => { - let displayText, value; - - if (typeof option === 'string') { - displayText = option; - value = option; - } else { - displayText = option.displayText; - value = option.value; - } - - return ( - { - this.setSelected(value); - }}> - {displayText} - - ); - }); - return ops.length ? ops : 'No Options Found'; - } - - render() { - const { props } = this; - const { - icon, - iconRotation, - iconSpin, - clipSelectedText = true, - color = 'default', - dropdownStyle, - over, - noscroll, - nochevron, - width, - openWidth = width, - onClick, - onOpen, - selected, - disabled, - displayText, - ...boxProps - } = props; - const { className, ...rest } = boxProps; - - const adjustedOpen = over ? !this.state.open : this.state.open; - - const menu = this.state.open ? ( -
{ - this.menuRef = menu; - }} - tabIndex="-1" - style={{ - 'width': openWidth, - }} - className={classes([ - (noscroll && 'Dropdown__menu-noscroll') || 'Dropdown__menu', - over && 'Dropdown__over', - ])}> - {this.buildMenu()} -
- ) : null; - - return ( -
- { - if (disabled && !this.state.open) { - return; - } - this.setOpen(!this.state.open); - - if (props.onOpen) { - props.onOpen(event); - } - }}> - {icon && ( - - )} - - {displayText ? displayText : this.state.selected} - - {!!nochevron || ( - - - - )} - - {menu} -
- ); - } -} diff --git a/tgui/packages/tgui/components/Dropdown.tsx b/tgui/packages/tgui/components/Dropdown.tsx new file mode 100644 index 000000000000..b7491c84dca6 --- /dev/null +++ b/tgui/packages/tgui/components/Dropdown.tsx @@ -0,0 +1,215 @@ +import { classes } from 'common/react'; +import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; + +import { BoxProps, unit } from './Box'; +import { Button } from './Button'; +import { Icon } from './Icon'; +import { Popper } from './Popper'; + +type DropdownEntry = { + displayText: ReactNode; + value: string | number; +}; + +type DropdownOption = string | DropdownEntry; + +type Props = { + /** An array of strings which will be displayed in the + dropdown when open. See Dropdown.tsx for more advanced usage with DropdownEntry */ + readonly options: DropdownOption[]; + /** Called when a value is picked from the list, `value` is the value that was picked */ + readonly onSelected: (value: any) => void; +} & Partial<{ + /** Whether to display previous / next buttons */ + buttons: boolean; + /** Whether to clip the selected text */ + clipSelectedText: boolean; + /** Color of dropdown button */ + color: string; + /** Disables the dropdown */ + disabled: boolean; + /** Text to always display in place of the selected text */ + displayText: ReactNode; + /** Icon to display in dropdown button */ + icon: string; + /** Angle of the icon */ + iconRotation: number; + /** Whether or not the icon should spin */ + iconSpin: boolean; + /** Width of the dropdown menu. Default: 15rem */ + menuWidth: string; + /** Whether or not the arrow on the right hand side of the dropdown button is visible */ + noChevron: boolean; + /** Called when dropdown button is clicked */ + onClick: (event) => void; + /** Dropdown renders over instead of below */ + over: boolean; + /** Currently selected entry */ + selected: string | number; +}> & + BoxProps; + +const getOptionValue = (option: DropdownOption) => { + return typeof option === 'string' ? option : option.value; +}; + +export const Dropdown = (props: Props) => { + const { + buttons, + className, + clipSelectedText = true, + color = 'default', + disabled, + displayText, + icon, + iconRotation, + iconSpin, + menuWidth = '15rem', + noChevron, + onClick, + onSelected, + options = [], + over, + selected, + width, + } = props; + + const [open, setOpen] = useState(false); + const adjustedOpen = over ? !open : open; + const innerRef = useRef(null); + + /** Update the selected value when clicking the left/right buttons */ + const updateSelected = useCallback( + (direction: 'previous' | 'next') => { + if (options.length < 1 || disabled) { + return; + } + const startIndex = 0; + const endIndex = options.length - 1; + + let selectedIndex = options.findIndex( + (option) => getOptionValue(option) === selected + ); + + if (selectedIndex < 0) { + selectedIndex = direction === 'next' ? endIndex : startIndex; + } + + let newIndex = selectedIndex; + if (direction === 'next') { + newIndex = selectedIndex === endIndex ? startIndex : selectedIndex++; + } else { + newIndex = selectedIndex === startIndex ? endIndex : selectedIndex--; + } + + onSelected?.(getOptionValue(options[newIndex])); + }, + [disabled, onSelected, options, selected] + ); + + /** Allows the menu to be scrollable on open */ + useEffect(() => { + if (!open) return; + + innerRef.current?.focus(); + }, [open]); + + return ( + setOpen(false)} + placement={over ? 'top-start' : 'bottom-start'} + content={ +
+ {options.length === 0 && ( +
No options
+ )} + + {options.map((option, index) => { + const value = getOptionValue(option); + + return ( +
{ + setOpen(false); + onSelected?.(value); + }}> + {typeof option === 'string' ? option : option.displayText} +
+ ); + })} +
+ }> +
+
+
{ + if (disabled && !open) { + return; + } + setOpen(!open); + onClick?.(event); + }}> + {icon && ( + + )} + + {displayText || selected} + + {!noChevron && ( + + + + )} +
+ {buttons && ( + <> +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/components/FitText.tsx b/tgui/packages/tgui/components/FitText.tsx index 751a2f8980b9..0632626aeb12 100644 --- a/tgui/packages/tgui/components/FitText.tsx +++ b/tgui/packages/tgui/components/FitText.tsx @@ -1,4 +1,4 @@ -import { Component, createRef, RefObject } from 'inferno'; +import { Component, createRef, HTMLAttributes, PropsWithChildren, RefObject } from 'react'; const DEFAULT_ACCEPTABLE_DIFFERENCE = 5; @@ -7,7 +7,7 @@ type Props = { readonly maxWidth: number; readonly maxFontSize: number; readonly native?: HTMLAttributes; -}; +} & PropsWithChildren; type State = { fontSize: number; @@ -19,8 +19,8 @@ export class FitText extends Component { fontSize: 0, }; - constructor() { - super(); + constructor(props: Props) { + super(props); this.resize = this.resize.bind(this); @@ -80,9 +80,10 @@ export class FitText extends Component { {this.props.children} diff --git a/tgui/packages/tgui/components/Flex.tsx b/tgui/packages/tgui/components/Flex.tsx index f67738280bac..da063f0296f5 100644 --- a/tgui/packages/tgui/components/Flex.tsx +++ b/tgui/packages/tgui/components/Flex.tsx @@ -4,16 +4,19 @@ * @license MIT */ -import { BooleanLike, classes, pureComponentHooks } from 'common/react'; +import { classes } from 'common/react'; import { BoxProps, computeBoxClassName, computeBoxProps, unit } from './Box'; -export type FlexProps = BoxProps & { - direction?: string | BooleanLike; - wrap?: string | BooleanLike; - align?: string | BooleanLike; - justify?: string | BooleanLike; - inline?: BooleanLike; -}; +export type FlexProps = Partial<{ + align: string | boolean; + direction: string; + inline: boolean; + justify: string; + scrollable: boolean; + style: Partial; + wrap: string | boolean; +}> & + BoxProps; export const computeFlexClassName = (props: FlexProps) => { return classes([ @@ -27,13 +30,14 @@ export const computeFlexClassName = (props: FlexProps) => { export const computeFlexProps = (props: FlexProps) => { const { className, direction, wrap, align, justify, inline, ...rest } = props; + return computeBoxProps({ style: { ...rest.style, - 'flex-direction': direction, - 'flex-wrap': wrap === true ? 'wrap' : wrap, - 'align-items': align, - 'justify-content': justify, + flexDirection: direction, + flexWrap: wrap === true ? 'wrap' : wrap, + alignItems: align, + justifyContent: justify, }, ...rest, }); @@ -49,15 +53,15 @@ export const Flex = (props) => { ); }; -Flex.defaultHooks = pureComponentHooks; - -export type FlexItemProps = BoxProps & { - grow?: number; - order?: number; - shrink?: number; - basis?: string | BooleanLike; - align?: string | BooleanLike; -}; +export type FlexItemProps = BoxProps & + Partial<{ + grow: number | boolean; + order: number; + shrink: number | boolean; + basis: string | number; + align: string | boolean; + style: Partial; + }>; export const computeFlexItemClassName = (props: FlexItemProps) => { return classes([ @@ -68,33 +72,26 @@ export const computeFlexItemClassName = (props: FlexItemProps) => { }; export const computeFlexItemProps = (props: FlexItemProps) => { - // prettier-ignore - const { - className, - style, - grow, - order, - shrink, - basis, - align, - ...rest - } = props; - // prettier-ignore - const computedBasis = basis + const { className, style, grow, order, shrink, basis, align, ...rest } = + props; + + const computedBasis = + basis ?? // IE11: Set basis to specified width if it's known, which fixes certain // bugs when rendering tables inside the flex. - ?? props.width + props.width ?? // If grow is used, basis should be set to 0 to be consistent with // flex css shorthand `flex: 1`. - ?? (grow !== undefined ? 0 : undefined); + (grow !== undefined ? 0 : undefined); + return computeBoxProps({ style: { ...style, - 'flex-grow': grow !== undefined && Number(grow), - 'flex-shrink': shrink !== undefined && Number(shrink), - 'flex-basis': unit(computedBasis), - 'order': order, - 'align-self': align, + flexGrow: grow !== undefined && Number(grow), + flexShrink: shrink !== undefined && Number(shrink), + flexBasis: unit(computedBasis), + order: order, + alignSelf: align, }, ...rest, }); @@ -110,6 +107,4 @@ const FlexItem = (props) => { ); }; -FlexItem.defaultHooks = pureComponentHooks; - Flex.Item = FlexItem; diff --git a/tgui/packages/tgui/components/InfinitePlane.jsx b/tgui/packages/tgui/components/InfinitePlane.jsx index 74f60f1e4d4c..e298537b46df 100644 --- a/tgui/packages/tgui/components/InfinitePlane.jsx +++ b/tgui/packages/tgui/components/InfinitePlane.jsx @@ -2,7 +2,7 @@ import { computeBoxProps } from './Box'; import { Stack } from './Stack'; import { ProgressBar } from './ProgressBar'; import { Button } from './Button'; -import { Component } from 'inferno'; +import { Component } from 'react'; const ZOOM_MIN_VAL = 0.5; const ZOOM_MAX_VAL = 1.5; diff --git a/tgui/packages/tgui/components/Input.jsx b/tgui/packages/tgui/components/Input.jsx index ac7ce6eef360..0eac3d05ebf1 100644 --- a/tgui/packages/tgui/components/Input.jsx +++ b/tgui/packages/tgui/components/Input.jsx @@ -6,7 +6,7 @@ import { KEY_ENTER, KEY_ESCAPE } from 'common/keycodes'; import { classes } from 'common/react'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; import { Box } from './Box'; // prettier-ignore diff --git a/tgui/packages/tgui/components/KeyListener.tsx b/tgui/packages/tgui/components/KeyListener.tsx index 62509cae96d6..d401642a3cff 100644 --- a/tgui/packages/tgui/components/KeyListener.tsx +++ b/tgui/packages/tgui/components/KeyListener.tsx @@ -1,4 +1,4 @@ -import { Component } from 'inferno'; +import { Component } from 'react'; import { KeyEvent } from '../events'; import { listenForKeyEvents } from '../hotkeys'; @@ -11,8 +11,8 @@ type KeyListenerProps = Partial<{ export class KeyListener extends Component { dispose: () => void; - constructor() { - super(); + constructor(props) { + super(props); this.dispose = listenForKeyEvents((key) => { if (this.props.onKey) { diff --git a/tgui/packages/tgui/components/LabeledList.tsx b/tgui/packages/tgui/components/LabeledList.tsx index 0c4c608f25f3..8bb3ab82fd64 100644 --- a/tgui/packages/tgui/components/LabeledList.tsx +++ b/tgui/packages/tgui/components/LabeledList.tsx @@ -4,35 +4,32 @@ * @license MIT */ -import { BooleanLike, classes, pureComponentHooks } from 'common/react'; -import { InfernoNode } from 'inferno'; +import { BooleanLike, classes } from 'common/react'; +import { PropsWithChildren, ReactNode } from 'react'; import { Box, unit } from './Box'; import { Divider } from './Divider'; +import { Tooltip } from './Tooltip'; -type LabeledListProps = { - readonly children?: any; -}; - -export const LabeledList = (props: LabeledListProps) => { +export const LabeledList = (props: PropsWithChildren) => { const { children } = props; return {children}
; }; -LabeledList.defaultHooks = pureComponentHooks; - -type LabeledListItemProps = { - readonly className?: string | BooleanLike; - readonly label?: string | InfernoNode | BooleanLike; - readonly labelColor?: string | BooleanLike; - readonly labelWrap?: boolean; - readonly color?: string | BooleanLike; - readonly textAlign?: string | BooleanLike; - readonly buttons?: InfernoNode; +type LabeledListItemProps = Partial<{ + buttons: ReactNode; + className: string | BooleanLike; + color: string; + key: string | number; + label: string | ReactNode | BooleanLike; + labelColor: string; + labelWrap: boolean; + textAlign: string; /** @deprecated */ - readonly content?: any; - readonly children?: InfernoNode; - readonly verticalAlign?: string; -}; + content: any; + children: ReactNode; + verticalAlign: string; + tooltip: string; +}>; const LabeledListItem = (props: LabeledListItemProps) => { const { @@ -46,20 +43,46 @@ const LabeledListItem = (props: LabeledListItemProps) => { content, children, verticalAlign = 'baseline', + tooltip, } = props; + + let innerLabel; + if (label) { + innerLabel = label; + if (typeof label === 'string') innerLabel += ':'; + } + + if (tooltip !== undefined) { + innerLabel = ( + + + {innerLabel} + + + ); + } + + let labelChild = ( + + {innerLabel} + + ); + return ( - - {label ? (typeof label === 'string' ? label + ':' : label) : null} - + {labelChild} { ); }; -LabeledListItem.defaultHooks = pureComponentHooks; - type LabeledListDividerProps = { readonly size?: number; }; @@ -90,8 +111,8 @@ const LabeledListDivider = (props: LabeledListDividerProps) => { @@ -99,7 +120,5 @@ const LabeledListDivider = (props: LabeledListDividerProps) => { ); }; -LabeledListDivider.defaultHooks = pureComponentHooks; - LabeledList.Item = LabeledListItem; LabeledList.Divider = LabeledListDivider; diff --git a/tgui/packages/tgui/components/NumberInput.jsx b/tgui/packages/tgui/components/NumberInput.jsx index e264d811d37b..a5890d5cd8aa 100644 --- a/tgui/packages/tgui/components/NumberInput.jsx +++ b/tgui/packages/tgui/components/NumberInput.jsx @@ -5,8 +5,9 @@ */ import { clamp } from 'common/math'; -import { classes, pureComponentHooks } from 'common/react'; -import { Component, createRef } from 'inferno'; +import { classes } from 'common/react'; +import { Component, createRef } from 'react'; + import { AnimatedNumber } from './AnimatedNumber'; import { Box } from './Box'; @@ -165,14 +166,15 @@ export class NumberInput extends Component { displayValue = intermediateValue; } - // prettier-ignore const contentElement = ( -
- { - (animated && !dragging && !suppressingFlicker) ? - () : - (format ? format(displayValue) : displayValue) - } +
+ {animated && !dragging && !suppressingFlicker ? ( + + ) : format ? ( + format(displayValue) + ) : ( + displayValue + )} {unit ? ' ' + unit : ''}
@@ -194,10 +196,12 @@ export class NumberInput extends Component {
@@ -208,8 +212,8 @@ export class NumberInput extends Component { style={{ display: !editing ? 'none' : undefined, height: height, - 'line-height': lineHeight, - 'font-size': fontSize, + lineHeight: lineHeight, + fontSize: fontSize, }} onBlur={(e) => { if (!editing) { @@ -274,7 +278,6 @@ export class NumberInput extends Component { } } -NumberInput.defaultHooks = pureComponentHooks; NumberInput.defaultProps = { minValue: -Infinity, maxValue: +Infinity, diff --git a/tgui/packages/tgui/components/Popper.tsx b/tgui/packages/tgui/components/Popper.tsx index 08875d7f8d6c..682fd65a740d 100644 --- a/tgui/packages/tgui/components/Popper.tsx +++ b/tgui/packages/tgui/components/Popper.tsx @@ -1,84 +1,88 @@ -import { createPopper } from '@popperjs/core'; -import { ArgumentsOf } from 'common/types'; -import { Component, findDOMfromVNode, InfernoNode, render } from 'inferno'; +import { Placement } from '@popperjs/core'; +import { PropsWithChildren, ReactNode, useEffect, useRef, useState } from 'react'; +import { usePopper } from 'react-popper'; -type PopperProps = { - readonly popperContent: InfernoNode; - readonly options?: ArgumentsOf[2]; - readonly additionalStyles?: CSSProperties; +type RequiredProps = { + /** The content to display in the popper */ + readonly content: ReactNode; + /** Whether the popper is open */ + readonly isOpen: boolean; }; -export class Popper extends Component { - static id: number = 0; +type OptionalProps = Partial<{ + /** Called when the user clicks outside the popper */ + onClickOutside: () => void; + /** Where to place the popper relative to the reference element */ + placement: Placement; +}>; - renderedContent: HTMLDivElement; - popperInstance: ReturnType; +type Props = RequiredProps & OptionalProps; - constructor() { - super(); +/** + * ## Popper + * Popper lets you position elements so that they don't go out of the bounds of the window. + * @url https://popper.js.org/react-popper/ for more information. + */ +export const Popper = (props: PropsWithChildren) => { + const { children, content, isOpen, onClickOutside, placement } = props; - Popper.id += 1; - } + const [referenceElement, setReferenceElement] = + useState(null); + const [popperElement, setPopperElement] = useState( + null + ); - componentDidMount() { - const { additionalStyles, options } = this.props; + // One would imagine we could just use useref here, but it's against react-popper documentation and causes a positioning bug + // We still need them to call focus and clickoutside events :( + const popperRef = useRef(null); + const parentRef = useRef(null); - this.renderedContent = document.createElement('div'); + const { styles, attributes } = usePopper(referenceElement, popperElement, { + placement, + }); - if (additionalStyles) { - for (const [attribute, value] of Object.entries(additionalStyles)) { - this.renderedContent.style[attribute] = value; - } + /** Close the popper when the user clicks outside */ + const handleClickOutside = (event: MouseEvent) => { + if ( + !popperRef.current?.contains(event.target as Node) && + !parentRef.current?.contains(event.target as Node) + ) { + onClickOutside?.(); } + }; - this.renderPopperContent(() => { - document.body.appendChild(this.renderedContent); - - // HACK: We don't want to create a wrapper, as it could break the layout - // of consumers, so we do the inferno equivalent of `findDOMNode(this)`. - // This is usually bad as refs are usually better, but refs did - // not work in this case, as they weren't propagating correctly. - // A previous attempt was made as a render prop that passed an ID, - // but this made consuming use too unwieldly. - // This code is copied from `findDOMNode` in inferno-extras. - // Because this component is written in TypeScript, we will know - // immediately if this internal variable is removed. - const domNode = findDOMfromVNode(this.$LI, true); - if (!domNode) { - return; - } - - this.popperInstance = createPopper( - domNode, - this.renderedContent, - options - ); - }); - } - - componentDidUpdate() { - this.renderPopperContent(() => this.popperInstance?.update()); - } - - componentWillUnmount() { - this.popperInstance?.destroy(); - render(null, this.renderedContent, () => { - this.renderedContent.remove(); - }); - } + useEffect(() => { + if (isOpen) { + document.addEventListener('mousedown', handleClickOutside); + } else { + document.removeEventListener('mousedown', handleClickOutside); + } - renderPopperContent(callback: () => void) { - // `render` errors when given false, so we convert it to `null`, - // which is supported. - render( - this.props.popperContent || null, - this.renderedContent, - callback, - this.context - ); - } + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [isOpen]); - render() { - return this.props.children; - } -} + return ( + <> +
{ + setReferenceElement(node); + parentRef.current = node; + }}> + {children} +
+ {isOpen && ( +
{ + setPopperElement(node); + popperRef.current = node; + }} + style={{ ...styles.popper, zIndex: 5 }} + {...attributes.popper}> + {content} +
+ )} + + ); +}; diff --git a/tgui/packages/tgui/components/ProgressBar.jsx b/tgui/packages/tgui/components/ProgressBar.tsx similarity index 50% rename from tgui/packages/tgui/components/ProgressBar.jsx rename to tgui/packages/tgui/components/ProgressBar.tsx index 86116732c3f2..929ee232fd1a 100644 --- a/tgui/packages/tgui/components/ProgressBar.jsx +++ b/tgui/packages/tgui/components/ProgressBar.tsx @@ -4,12 +4,31 @@ * @license MIT */ -import { clamp01, scale, keyOfMatchingRange, toFixed } from 'common/math'; -import { classes, pureComponentHooks } from 'common/react'; -import { computeBoxClassName, computeBoxProps } from './Box'; +import { clamp01, keyOfMatchingRange, scale, toFixed } from 'common/math'; +import { classes } from 'common/react'; +import { PropsWithChildren } from 'react'; + import { CSS_COLORS } from '../constants'; +import { BoxProps, computeBoxClassName, computeBoxProps } from './Box'; + +type Props = { + readonly value: number; +} & Partial<{ + backgroundColor: string; + className: string; + color: string; + height: string | number; + maxValue: number; + minValue: number; + ranges: Record; + style: Partial; + title: string; + width: string | number; +}> & + Partial & + PropsWithChildren; -export const ProgressBar = (props) => { +export const ProgressBar = (props: Props) => { const { className, value, @@ -22,32 +41,28 @@ export const ProgressBar = (props) => { } = props; const scaledValue = scale(value, minValue, maxValue); const hasContent = children !== undefined; - // prettier-ignore - const effectiveColor = color - || keyOfMatchingRange(value, ranges) - || 'default'; + + const effectiveColor = + color || keyOfMatchingRange(value, ranges) || 'default'; // We permit colors to be in hex format, rgb()/rgba() format, // a name for a color- class, or a base CSS class. const outerProps = computeBoxProps(rest); - // prettier-ignore - const outerClasses = [ - 'ProgressBar', - className, - computeBoxClassName(rest), - ]; + + const outerClasses = ['ProgressBar', className, computeBoxClassName(rest)]; const fillStyles = { - 'width': clamp01(scaledValue) * 100 + '%', + width: clamp01(scaledValue) * 100 + '%', }; - if (CSS_COLORS.includes(effectiveColor) || effectiveColor === 'default') { + if ( + CSS_COLORS.includes(effectiveColor as any) || + effectiveColor === 'default' + ) { // If the color is a color- class, just use that. outerClasses.push('ProgressBar--color--' + effectiveColor); } else { // Otherwise, set styles directly. - // prettier-ignore - outerProps.style = (outerProps.style || "") - + `border-color: ${effectiveColor};`; - fillStyles['background-color'] = effectiveColor; + outerProps.style = { ...outerProps.style, borderColor: effectiveColor }; + fillStyles['backgroundColor'] = effectiveColor; } return ( @@ -62,5 +77,3 @@ export const ProgressBar = (props) => {
); }; - -ProgressBar.defaultHooks = pureComponentHooks; diff --git a/tgui/packages/tgui/components/RestrictedInput.jsx b/tgui/packages/tgui/components/RestrictedInput.jsx index 21f357239df7..082fc606d998 100644 --- a/tgui/packages/tgui/components/RestrictedInput.jsx +++ b/tgui/packages/tgui/components/RestrictedInput.jsx @@ -1,6 +1,6 @@ import { classes } from 'common/react'; import { clamp } from 'common/math'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; import { Box } from './Box'; import { KEY_ESCAPE, KEY_ENTER } from 'common/keycodes'; diff --git a/tgui/packages/tgui/components/Section.tsx b/tgui/packages/tgui/components/Section.tsx index 27b7898e1067..9b5170e3774e 100644 --- a/tgui/packages/tgui/components/Section.tsx +++ b/tgui/packages/tgui/components/Section.tsx @@ -4,100 +4,79 @@ * @license MIT */ -import { canRender, classes } from 'common/react'; -import { Component, createRef, InfernoNode, RefObject } from 'inferno'; -import { addScrollableNode, removeScrollableNode } from '../events'; import { BoxProps, computeBoxClassName, computeBoxProps } from './Box'; +import { ReactNode, RefObject, createRef, useEffect } from 'react'; +import { addScrollableNode, removeScrollableNode } from '../events'; +import { canRender, classes } from 'common/react'; -type SectionProps = BoxProps & { - readonly className?: string; - readonly title?: InfernoNode; - readonly buttons?: InfernoNode; - readonly fill?: boolean; - readonly fitted?: boolean; - readonly scrollable?: boolean; - readonly scrollableHorizontal?: boolean; - /** @deprecated This property no longer works, please remove it. */ - readonly level?: boolean; - /** @deprecated Please use `scrollable` property */ - readonly overflowY?: any; +export type SectionProps = Partial<{ + buttons: ReactNode; + fill: boolean; + fitted: boolean; + scrollable: boolean; + scrollableHorizontal: boolean; + title: ReactNode; /** @member Allows external control of scrolling. */ - readonly scrollableRef?: RefObject; + scrollableRef: RefObject; /** @member Callback function for the `scroll` event */ - readonly onScroll?: (this: GlobalEventHandlers, ev: Event) => any; -}; + onScroll: ((this: GlobalEventHandlers, ev: Event) => any) | null; +}> & + BoxProps; -export class Section extends Component { - scrollableRef: RefObject; - scrollable: boolean; - onScroll?: (this: GlobalEventHandlers, ev: Event) => any; - scrollableHorizontal: boolean; +export const Section = (props: SectionProps) => { + const { + className, + title, + buttons, + fill, + fitted, + scrollable, + scrollableHorizontal, + children, + onScroll, + ...rest + } = props; - constructor(props) { - super(props); - this.scrollableRef = props.scrollableRef || createRef(); - this.scrollable = props.scrollable; - this.onScroll = props.onScroll; - this.scrollableHorizontal = props.scrollableHorizontal; - } + const scrollableRef = props.scrollableRef || createRef(); + const hasTitle = canRender(title) || canRender(buttons); - componentDidMount() { - if (this.scrollable || this.scrollableHorizontal) { - addScrollableNode(this.scrollableRef.current as HTMLElement); - if (this.onScroll && this.scrollableRef.current) { - this.scrollableRef.current.onscroll = this.onScroll; + useEffect(() => { + if (scrollable || scrollableHorizontal) { + addScrollableNode(scrollableRef.current as HTMLElement); + if (onScroll && scrollableRef.current) { + scrollableRef.current.onscroll = onScroll; } } - } - - componentWillUnmount() { - if (this.scrollable || this.scrollableHorizontal) { - removeScrollableNode(this.scrollableRef.current as HTMLElement); - } - } + return () => { + if (scrollable || scrollableHorizontal) { + removeScrollableNode(scrollableRef.current as HTMLElement); + } + }; + }, []); - render() { - const { - className, - title, - buttons, - fill, - fitted, - scrollable, - scrollableHorizontal, - children, - onScroll, - ...rest - } = this.props; - const hasTitle = canRender(title) || canRender(buttons); - return ( -
- {hasTitle && ( -
- {title} -
{buttons}
-
- )} -
-
- {children} -
+ return ( +
+ {hasTitle && ( +
+ {title} +
{buttons}
+
+ )} +
+
+ {children}
- ); - } -} +
+ ); +}; diff --git a/tgui/packages/tgui/components/Stack.tsx b/tgui/packages/tgui/components/Stack.tsx index 77c92796a085..3eb31bd32560 100644 --- a/tgui/packages/tgui/components/Stack.tsx +++ b/tgui/packages/tgui/components/Stack.tsx @@ -5,22 +5,25 @@ */ import { classes } from 'common/react'; -import { RefObject } from 'inferno'; +import { RefObject } from 'react'; import { computeFlexClassName, computeFlexItemClassName, computeFlexItemProps, computeFlexProps, FlexItemProps, FlexProps } from './Flex'; -type StackProps = FlexProps & { - readonly vertical?: boolean; - readonly fill?: boolean; -}; +type Props = Partial<{ + vertical: boolean; + fill: boolean; + zebra: boolean; +}> & + FlexProps; -export const Stack = (props: StackProps) => { - const { className, vertical, fill, ...rest } = props; +export const Stack = (props: Props) => { + const { className, vertical, fill, zebra, ...rest } = props; return (
{ ); }; -type StackItemProps = FlexProps & { - readonly innerRef?: RefObject; -}; +type StackItemProps = FlexItemProps & + Partial<{ + innerRef: RefObject; + }>; const StackItem = (props: StackItemProps) => { const { className, innerRef, ...rest } = props; @@ -53,9 +57,10 @@ const StackItem = (props: StackItemProps) => { Stack.Item = StackItem; -type StackDividerProps = FlexItemProps & { - readonly hidden?: boolean; -}; +type StackDividerProps = FlexItemProps & + Partial<{ + hidden: boolean; + }>; const StackDivider = (props: StackDividerProps) => { const { className, hidden, ...rest } = props; diff --git a/tgui/packages/tgui/components/TextArea.jsx b/tgui/packages/tgui/components/TextArea.jsx index 76db8272aa38..1ca70488c9d9 100644 --- a/tgui/packages/tgui/components/TextArea.jsx +++ b/tgui/packages/tgui/components/TextArea.jsx @@ -6,14 +6,14 @@ */ import { classes } from 'common/react'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; import { Box } from './Box'; import { toInputValue } from './Input'; import { KEY_ENTER, KEY_ESCAPE, KEY_TAB } from 'common/keycodes'; export class TextArea extends Component { - constructor(props, context) { - super(props, context); + constructor(props) { + super(props); this.textareaRef = props.innerRef || createRef(); this.state = { editing: false, diff --git a/tgui/packages/tgui/components/TimeDisplay.jsx b/tgui/packages/tgui/components/TimeDisplay.jsx index 6b87ee5260ac..bbdd747701cc 100644 --- a/tgui/packages/tgui/components/TimeDisplay.jsx +++ b/tgui/packages/tgui/components/TimeDisplay.jsx @@ -1,5 +1,5 @@ import { formatTime } from '../format'; -import { Component } from 'inferno'; +import { Component } from 'react'; // AnimatedNumber Copypaste const isSafeNumber = (value) => { diff --git a/tgui/packages/tgui/components/Tooltip.tsx b/tgui/packages/tgui/components/Tooltip.tsx index f2004f96302a..b565e6fda475 100644 --- a/tgui/packages/tgui/components/Tooltip.tsx +++ b/tgui/packages/tgui/components/Tooltip.tsx @@ -1,9 +1,10 @@ import { createPopper, Placement, VirtualElement } from '@popperjs/core'; -import { Component, findDOMfromVNode, InfernoNode, render } from 'inferno'; +import { Component, ReactNode } from 'react'; +import { findDOMNode, render } from 'react-dom'; type TooltipProps = { - readonly children?: InfernoNode; - readonly content: InfernoNode; + readonly children?: ReactNode; + readonly content: ReactNode; readonly position?: Placement; }; @@ -50,14 +51,16 @@ export class Tooltip extends Component { getDOMNode() { // HACK: We don't want to create a wrapper, as it could break the layout - // of consumers, so we do the inferno equivalent of `findDOMNode(this)`. - // My attempt to avoid this was a render prop that passed in - // callbacks to onmouseenter and onmouseleave, but this was unwiedly - // to consumers, specifically buttons. - // This code is copied from `findDOMNode` in inferno-extras. + // of consumers, so we use findDOMNode. + // This is usually bad as refs are usually better, but refs did + // not work in this case, as they weren't propagating correctly. + // A previous attempt was made as a render prop that passed an ID, + // but this made consuming use too unwieldly. // Because this component is written in TypeScript, we will know // immediately if this internal variable is removed. - return findDOMfromVNode(this.$LI, true); + // + // eslint-disable-next-line react/no-find-dom-node + return findDOMNode(this) as Element; } componentDidMount() { @@ -103,33 +106,28 @@ export class Tooltip extends Component { return; } - render( - {this.props.content}, - renderedTooltip, - () => { - let singletonPopper = Tooltip.singletonPopper; - if (singletonPopper === undefined) { - singletonPopper = createPopper( - Tooltip.virtualElement, - renderedTooltip!, - { - ...DEFAULT_OPTIONS, - placement: this.props.position || 'auto', - } - ); - - Tooltip.singletonPopper = singletonPopper; - } else { - singletonPopper.setOptions({ + render({this.props.content}, renderedTooltip, () => { + let singletonPopper = Tooltip.singletonPopper; + if (singletonPopper === undefined) { + singletonPopper = createPopper( + Tooltip.virtualElement, + renderedTooltip!, + { ...DEFAULT_OPTIONS, placement: this.props.position || 'auto', - }); + } + ); - singletonPopper.update(); - } - }, - this.context - ); + Tooltip.singletonPopper = singletonPopper; + } else { + singletonPopper.setOptions({ + ...DEFAULT_OPTIONS, + placement: this.props.position || 'auto', + }); + + singletonPopper.update(); + } + }); } componentDidUpdate() { diff --git a/tgui/packages/tgui/components/TrackOutsideClicks.tsx b/tgui/packages/tgui/components/TrackOutsideClicks.tsx index 8ba46ffb5bd7..13cfb8443edb 100644 --- a/tgui/packages/tgui/components/TrackOutsideClicks.tsx +++ b/tgui/packages/tgui/components/TrackOutsideClicks.tsx @@ -1,14 +1,14 @@ -import { Component, createRef } from 'inferno'; +import { Component, createRef, PropsWithChildren } from 'react'; type Props = { readonly onOutsideClick: () => void; -}; +} & PropsWithChildren; export class TrackOutsideClicks extends Component { ref = createRef(); - constructor() { - super(); + constructor(props) { + super(props); this.handleOutsideClick = this.handleOutsideClick.bind(this); diff --git a/tgui/packages/tgui/debug/KitchenSink.jsx b/tgui/packages/tgui/debug/KitchenSink.jsx index e25751722c52..23cf96698101 100644 --- a/tgui/packages/tgui/debug/KitchenSink.jsx +++ b/tgui/packages/tgui/debug/KitchenSink.jsx @@ -20,10 +20,10 @@ const r = require.context('../stories', false, /\.stories\.jsx$/); */ const getStories = () => r.keys().map((path) => r(path)); -export const KitchenSink = (props, context) => { +export const KitchenSink = (props) => { const { panel } = props; - const [theme] = useLocalState(context, 'kitchenSinkTheme'); - const [pageIndex, setPageIndex] = useLocalState(context, 'pageIndex', 0); + const [theme] = useLocalState('kitchenSinkTheme'); + const [pageIndex, setPageIndex] = useLocalState('pageIndex', 0); const stories = getStories(); const story = stories[pageIndex]; const Layout = panel ? Pane : Window; diff --git a/tgui/packages/tgui/debug/hooks.js b/tgui/packages/tgui/debug/hooks.js index d324dd68d88f..fc4901c49657 100644 --- a/tgui/packages/tgui/debug/hooks.js +++ b/tgui/packages/tgui/debug/hooks.js @@ -4,7 +4,7 @@ * @license MIT */ -import { useSelector } from 'common/redux'; +import { useSelector } from '../backend'; import { selectDebug } from './selectors'; -export const useDebug = (context) => useSelector(context, selectDebug); +export const useDebug = () => useSelector(selectDebug); diff --git a/tgui/packages/tgui/drag.ts b/tgui/packages/tgui/drag.ts index 4b6f82e1207e..a34c49ac4622 100644 --- a/tgui/packages/tgui/drag.ts +++ b/tgui/packages/tgui/drag.ts @@ -203,7 +203,7 @@ const constraintPosition = ( }; // Start dragging the window -export const dragStartHandler = (event: MouseEvent) => { +export const dragStartHandler = (event) => { logger.log('drag start'); dragging = true; dragPointOffset = vecSubtract( @@ -218,7 +218,7 @@ export const dragStartHandler = (event: MouseEvent) => { }; // End dragging the window -const dragEndHandler = (event: MouseEvent) => { +const dragEndHandler = (event) => { logger.log('drag end'); dragMoveHandler(event); document.removeEventListener('mousemove', dragMoveHandler); diff --git a/tgui/packages/tgui/index.tsx b/tgui/packages/tgui/index.tsx index 97640d062a86..153a8486bbab 100644 --- a/tgui/packages/tgui/index.tsx +++ b/tgui/packages/tgui/index.tsx @@ -32,8 +32,9 @@ import { setupHotReloading } from 'tgui-dev-server/link/client.cjs'; import { setupHotKeys } from './hotkeys'; import { captureExternalLinks } from './links'; import { createRenderer } from './renderer'; -import { configureStore, StoreProvider } from './store'; +import { configureStore } from './store'; import { setupGlobalEvents } from './events'; +import { setGlobalStore } from './backend'; perf.mark('inception', window.performance?.timing?.navigationStart); perf.mark('init'); @@ -41,13 +42,11 @@ perf.mark('init'); const store = configureStore(); const renderApp = createRenderer(() => { + setGlobalStore(store); + const { getRoutedComponent } = require('./routes'); - const Component = getRoutedComponent(store); - return ( - - - - ); + const Component = getRoutedComponent(); + return ; }); const setupApp = () => { diff --git a/tgui/packages/tgui/interfaces/AcidVest.jsx b/tgui/packages/tgui/interfaces/AcidVest.jsx index adf0e37a2eb6..fffbb178764f 100644 --- a/tgui/packages/tgui/interfaces/AcidVest.jsx +++ b/tgui/packages/tgui/interfaces/AcidVest.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Button, LabeledList, Section, Slider, Flex, Box } from '../components'; import { Window } from '../layouts'; -export const AcidVest = (_props, context) => { - const { act, data } = useBackend(context); +export const AcidVest = (_props) => { + const { act, data } = useBackend(); const damageList = data.configList.Damage; const vitalsList = data.configList.Vitals; diff --git a/tgui/packages/tgui/interfaces/Adminhelp.tsx b/tgui/packages/tgui/interfaces/Adminhelp.tsx index 817e25e37cb6..c2a888cdc33c 100644 --- a/tgui/packages/tgui/interfaces/Adminhelp.tsx +++ b/tgui/packages/tgui/interfaces/Adminhelp.tsx @@ -10,8 +10,8 @@ type AdminhelpData = { urgentAhelpPromptMessage: string; }; -export const Adminhelp = (props, context) => { - const { act, data } = useBackend(context); +export const Adminhelp = (props) => { + const { act, data } = useBackend(); const { adminCount, urgentAhelpEnabled, @@ -19,20 +19,14 @@ export const Adminhelp = (props, context) => { urgentAhelpPromptMessage, } = data; const [requestForAdmin, setRequestForAdmin] = useLocalState( - context, 'request_for_admin', false ); const [currentlyInputting, setCurrentlyInputting] = useLocalState( - context, 'confirm_request', false ); - const [ahelpMessage, setAhelpMessage] = useLocalState( - context, - 'ahelp_message', - '' - ); + const [ahelpMessage, setAhelpMessage] = useLocalState('ahelp_message', ''); const confirmationText = 'alert admins'; return ( diff --git a/tgui/packages/tgui/interfaces/AlertModal.tsx b/tgui/packages/tgui/interfaces/AlertModal.tsx index ee53a0f1b637..60f60c94c524 100644 --- a/tgui/packages/tgui/interfaces/AlertModal.tsx +++ b/tgui/packages/tgui/interfaces/AlertModal.tsx @@ -17,8 +17,8 @@ type AlertModalData = { const KEY_DECREMENT = -1; const KEY_INCREMENT = 1; -export const AlertModal = (props, context) => { - const { act, data } = useBackend(context); +export const AlertModal = (props) => { + const { act, data } = useBackend(); const { autofocus, buttons = [], @@ -27,7 +27,7 @@ export const AlertModal = (props, context) => { timeout, title, } = data; - const [selected, setSelected] = useLocalState(context, 'selected', 0); + const [selected, setSelected] = useLocalState('selected', 0); // Dynamically sets window dimensions const windowHeight = 115 + @@ -89,8 +89,8 @@ export const AlertModal = (props, context) => { * Technically this handles more than 2 buttons, but you * should just be using a list input in that case. */ -const ButtonDisplay = (props, context) => { - const { data } = useBackend(context); +const ButtonDisplay = (props) => { + const { data } = useBackend(); const { buttons = [], large_buttons, swapped_buttons } = data; const { selected } = props; @@ -127,8 +127,8 @@ const ButtonDisplay = (props, context) => { /** * Displays a button with variable sizing. */ -const AlertButton = (props, context) => { - const { act, data } = useBackend(context); +const AlertButton = (props) => { + const { act, data } = useBackend(); const { large_buttons } = data; const { button, selected } = props; const buttonWidth = button.length > 7 ? button.length : 7; diff --git a/tgui/packages/tgui/interfaces/AlmayerControl.jsx b/tgui/packages/tgui/interfaces/AlmayerControl.jsx index 6cc44737626c..7a5133a3b19c 100644 --- a/tgui/packages/tgui/interfaces/AlmayerControl.jsx +++ b/tgui/packages/tgui/interfaces/AlmayerControl.jsx @@ -1,10 +1,10 @@ -import { Fragment } from 'inferno'; +import { Fragment } from 'react'; import { useBackend } from '../backend'; import { Button, Section, Flex, NoticeBox, Collapsible, Divider, Box } from '../components'; import { Window } from '../layouts'; -export const AlmayerControl = (_props, context) => { - const { act, data } = useBackend(context); +export const AlmayerControl = (_props) => { + const { act, data } = useBackend(); const worldTime = data.worldtime; const messages = data.messages; @@ -225,7 +225,7 @@ export const AlmayerControl = (_props, context) => { {messages && ( - + <> @@ -251,7 +251,7 @@ export const AlmayerControl = (_props, context) => { })} - + )} diff --git a/tgui/packages/tgui/interfaces/AltitudeControlConsole.jsx b/tgui/packages/tgui/interfaces/AltitudeControlConsole.jsx index 48550514fd4d..fd92602a3492 100644 --- a/tgui/packages/tgui/interfaces/AltitudeControlConsole.jsx +++ b/tgui/packages/tgui/interfaces/AltitudeControlConsole.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Button, ProgressBar, Box, Section } from '../components'; import { Window } from '../layouts'; import { createLogger } from '../logging'; -export const AltitudeControlConsole = (_props, context) => { - const { act, data } = useBackend(context); +export const AltitudeControlConsole = () => { + const { act, data } = useBackend(); const logger = createLogger('Debug'); logger.warn(data); return ( diff --git a/tgui/packages/tgui/interfaces/AntiAirConsole.jsx b/tgui/packages/tgui/interfaces/AntiAirConsole.jsx index 8bfc80bb763e..cd9b68ac782f 100644 --- a/tgui/packages/tgui/interfaces/AntiAirConsole.jsx +++ b/tgui/packages/tgui/interfaces/AntiAirConsole.jsx @@ -2,8 +2,8 @@ import { useBackend, useLocalState } from '../backend'; import { Stack, Section, Tabs, Button, NoticeBox, Box, Dimmer, Icon } from '../components'; import { Window } from '../layouts'; -export const AntiAirConsole = (props, context) => { - const { act, data } = useBackend(context); +export const AntiAirConsole = (props) => { + const { act, data } = useBackend(); return ( @@ -13,13 +13,12 @@ export const AntiAirConsole = (props, context) => { ); }; -const GeneralPanel = (props, context) => { - const { act, data } = useBackend(context); +const GeneralPanel = (props) => { + const { act, data } = useBackend(); const sections = data.sections; const [selectedSection, setSelectedSection] = useLocalState( - context, 'selected_section', null ); diff --git a/tgui/packages/tgui/interfaces/Apc.jsx b/tgui/packages/tgui/interfaces/Apc.jsx index 434d91f4e428..24decabb1cdc 100644 --- a/tgui/packages/tgui/interfaces/Apc.jsx +++ b/tgui/packages/tgui/interfaces/Apc.jsx @@ -3,7 +3,7 @@ import { Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../co import { Window } from '../layouts'; import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox'; -export const Apc = (props, context) => { +export const Apc = (props) => { return ( @@ -31,8 +31,8 @@ const powerStatusMap = { }, }; -const ApcContent = (props, context) => { - const { act, data } = useBackend(context); +const ApcContent = (props) => { + const { act, data } = useBackend(); const locked = data.locked && !data.siliconUser; const externalPowerStatus = powerStatusMap[data.externalPower] || powerStatusMap[0]; diff --git a/tgui/packages/tgui/interfaces/AresInterface.jsx b/tgui/packages/tgui/interfaces/AresInterface.jsx index aae115d150b0..f2ced09014f6 100644 --- a/tgui/packages/tgui/interfaces/AresInterface.jsx +++ b/tgui/packages/tgui/interfaces/AresInterface.jsx @@ -20,8 +20,8 @@ const PAGES = { 'emergency': () => Emergency, }; -export const AresInterface = (props, context) => { - const { data } = useBackend(context); +export const AresInterface = (props) => { + const { data } = useBackend(); const { current_menu, sudo } = data; const PageComponent = PAGES[current_menu](); @@ -41,8 +41,8 @@ export const AresInterface = (props, context) => { ); }; -const Login = (props, context) => { - const { act } = useBackend(context); +const Login = (props) => { + const { act } = useBackend(); return ( { ); }; -const MainMenu = (props, context) => { - const { data, act } = useBackend(context); +const MainMenu = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -352,8 +352,8 @@ const MainMenu = (props, context) => { ); }; -const AnnouncementLogs = (props, context) => { - const { data, act } = useBackend(context); +const AnnouncementLogs = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -448,8 +448,8 @@ const AnnouncementLogs = (props, context) => { ); }; -const BioscanLogs = (props, context) => { - const { data, act } = useBackend(context); +const BioscanLogs = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -544,8 +544,8 @@ const BioscanLogs = (props, context) => { ); }; -const BombardmentLogs = (props, context) => { - const { data, act } = useBackend(context); +const BombardmentLogs = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -644,8 +644,8 @@ const BombardmentLogs = (props, context) => { ); }; -const ApolloLog = (props, context) => { - const { data, act } = useBackend(context); +const ApolloLog = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, last_page, current_menu, apollo_log } = data; return ( @@ -700,8 +700,8 @@ const ApolloLog = (props, context) => { ); }; -const AccessLogs = (props, context) => { - const { data, act } = useBackend(context); +const AccessLogs = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, last_page, current_menu, access_log } = data; return ( @@ -756,8 +756,8 @@ const AccessLogs = (props, context) => { ); }; -const DeletionLogs = (props, context) => { - const { data, act } = useBackend(context); +const DeletionLogs = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, last_page, current_menu, records_deletion } = data; @@ -842,8 +842,8 @@ const DeletionLogs = (props, context) => { ); }; -const ARESTalk = (props, context) => { - const { data, act } = useBackend(context); +const ARESTalk = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -942,8 +942,8 @@ const ARESTalk = (props, context) => { ); }; -const DeletedTalks = (props, context) => { - const { data, act } = useBackend(context); +const DeletedTalks = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -1032,8 +1032,8 @@ const DeletedTalks = (props, context) => { ); }; -const ReadingTalks = (props, context) => { - const { data, act } = useBackend(context); +const ReadingTalks = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -1093,8 +1093,8 @@ const ReadingTalks = (props, context) => { ); }; -const Requisitions = (props, context) => { - const { data, act } = useBackend(context); +const Requisitions = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -1185,8 +1185,8 @@ const Requisitions = (props, context) => { ); }; -const FlightLogs = (props, context) => { - const { data, act } = useBackend(context); +const FlightLogs = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -1280,8 +1280,8 @@ const FlightLogs = (props, context) => { ); }; -const Security = (props, context) => { - const { data, act } = useBackend(context); +const Security = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, @@ -1375,8 +1375,8 @@ const Security = (props, context) => { ); }; -const Emergency = (props, context) => { - const { data, act } = useBackend(context); +const Emergency = (props) => { + const { data, act } = useBackend(); const { logged_in, access_text, diff --git a/tgui/packages/tgui/interfaces/Autodispenser.jsx b/tgui/packages/tgui/interfaces/Autodispenser.jsx index 5d029b72b894..249965e6d46c 100644 --- a/tgui/packages/tgui/interfaces/Autodispenser.jsx +++ b/tgui/packages/tgui/interfaces/Autodispenser.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Section, ProgressBar, Box, Flex, NoticeBox, Button, LabeledList, NumberInput } from '../components'; import { Window } from '../layouts'; -export const Autodispenser = (_props, context) => { - const { act, data } = useBackend(context); +export const Autodispenser = () => { + const { act, data } = useBackend(); const { energy, status, diff --git a/tgui/packages/tgui/interfaces/Autolathe.jsx b/tgui/packages/tgui/interfaces/Autolathe.jsx index ceb343eae0d9..6b21f7b6ece7 100644 --- a/tgui/packages/tgui/interfaces/Autolathe.jsx +++ b/tgui/packages/tgui/interfaces/Autolathe.jsx @@ -3,11 +3,11 @@ import { Section, Flex, ProgressBar, Box, Button, Tabs, Stack, Input } from '../ import { capitalize } from 'common/string'; import { Window } from '../layouts'; import { ElectricalPanel } from './common/ElectricalPanel'; -import { Fragment } from 'inferno'; +import { Fragment } from 'react'; import { createLogger } from '../logging'; -export const Autolathe = (_props, context) => { - const { act, data } = useBackend(context); +export const Autolathe = () => { + const { act, data } = useBackend(); const { materials, @@ -30,8 +30,8 @@ export const Autolathe = (_props, context) => { ); }; -const MaterialsData = (props, context) => { - const { act, data } = useBackend(context); +const MaterialsData = (props) => { + const { act, data } = useBackend(); const { materials, capacity, @@ -63,8 +63,8 @@ const MaterialsData = (props, context) => { ); }; -const CurrentlyMaking = (props, context) => { - const { act, data } = useBackend(context); +const CurrentlyMaking = (props) => { + const { act, data } = useBackend(); const { currently_making } = data; const MakingName = @@ -82,8 +82,8 @@ const CurrentlyMaking = (props, context) => { ); }; -const QueueList = (props, context) => { - const { act, data } = useBackend(context); +const QueueList = (props) => { + const { act, data } = useBackend(); const { materials, capacity, @@ -132,8 +132,8 @@ const QueueList = (props, context) => { }; // the below all has to be in one section due to the categories and search params -const PrintablesSection = (props, context) => { - const { act, data } = useBackend(context); +const PrintablesSection = (props) => { + const { act, data } = useBackend(); const logger = createLogger('autolathe'); @@ -146,11 +146,7 @@ const PrintablesSection = (props, context) => { queuemax, } = data; - const [currentSearch, setSearch] = useLocalState( - context, - 'current_search', - '' - ); + const [currentSearch, setSearch] = useLocalState('current_search', ''); const categories = []; printables @@ -158,7 +154,6 @@ const PrintablesSection = (props, context) => { .map((x) => x.recipe_category); const [currentCategory, setCategory] = useLocalState( - context, 'current_category', 'All' ); @@ -226,7 +221,7 @@ const PrintablesSection = (props, context) => { {(val.has_multipliers && ( - + <> @@ -250,7 +245,7 @@ const PrintablesSection = (props, context) => { )} - + )) || null} diff --git a/tgui/packages/tgui/interfaces/Binoculars.jsx b/tgui/packages/tgui/interfaces/Binoculars.jsx index d846a62a6545..7306cea90bbc 100644 --- a/tgui/packages/tgui/interfaces/Binoculars.jsx +++ b/tgui/packages/tgui/interfaces/Binoculars.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Section, Box } from '../components'; import { Window } from '../layouts'; -export const Binoculars = (_props, context) => { - const { data } = useBackend(context); +export const Binoculars = () => { + const { data } = useBackend(); const x_coord = data.xcoord; const y_coord = data.ycoord; diff --git a/tgui/packages/tgui/interfaces/BioSyntheticPrinter.jsx b/tgui/packages/tgui/interfaces/BioSyntheticPrinter.jsx index 283ff26a7b5f..8f39b0c5991b 100644 --- a/tgui/packages/tgui/interfaces/BioSyntheticPrinter.jsx +++ b/tgui/packages/tgui/interfaces/BioSyntheticPrinter.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Section, Button, Box, LabeledList, ProgressBar, NoticeBox, Divider } from '../components'; import { Window } from '../layouts'; -export const BioSyntheticPrinter = (_props, context) => { - const { act, data } = useBackend(context); +export const BioSyntheticPrinter = () => { + const { act, data } = useBackend(); const Working = data.working; diff --git a/tgui/packages/tgui/interfaces/BotanyEditor.jsx b/tgui/packages/tgui/interfaces/BotanyEditor.jsx index 425ffc9a5d5f..09a655e92814 100644 --- a/tgui/packages/tgui/interfaces/BotanyEditor.jsx +++ b/tgui/packages/tgui/interfaces/BotanyEditor.jsx @@ -1,10 +1,10 @@ -import { Fragment } from 'inferno'; +import { Fragment } from 'react'; import { useBackend } from '../backend'; import { Section, Button, Stack, LabeledList, NoticeBox } from '../components'; import { Window } from '../layouts'; -export const BotanyEditor = (_props, context) => { - const { act, data } = useBackend(context); +export const BotanyEditor = () => { + const { act, data } = useBackend(); const { disk, seed, degradation, sourceName, locus } = data; @@ -36,7 +36,7 @@ export const BotanyEditor = (_props, context) => {
- + <> {!disk && ( No disk! Genetic data cannot be applied. @@ -45,7 +45,7 @@ export const BotanyEditor = (_props, context) => { {!seed && ( No seeds to apply genetic data to! )} - + {!!disk && ( {sourceName} diff --git a/tgui/packages/tgui/interfaces/BotanyExtractor.jsx b/tgui/packages/tgui/interfaces/BotanyExtractor.jsx index eca098a9d9f6..fe675384684e 100644 --- a/tgui/packages/tgui/interfaces/BotanyExtractor.jsx +++ b/tgui/packages/tgui/interfaces/BotanyExtractor.jsx @@ -1,10 +1,10 @@ -import { Fragment } from 'inferno'; +import { Fragment } from 'react'; import { useBackend } from '../backend'; import { Section, Button, LabeledList, Box, Stack, NoticeBox } from '../components'; import { Window } from '../layouts'; -export const BotanyExtractor = (_props, context) => { - const { act, data } = useBackend(context); +export const BotanyExtractor = () => { + const { act, data } = useBackend(); const { disk, seed, geneMasks, degradation, hasGenetics, sourceName } = data; @@ -49,7 +49,7 @@ export const BotanyExtractor = (_props, context) => {
{(!!hasGenetics && ( - + <> {!disk && ( No disk! Genetic data cannot be extracted. @@ -86,7 +86,7 @@ export const BotanyExtractor = (_props, context) => { disabled={!hasGenetics} onClick={() => act('clear_buffer')} /> - + )) || No genetic data stored!}
diff --git a/tgui/packages/tgui/interfaces/BrigCell.jsx b/tgui/packages/tgui/interfaces/BrigCell.jsx index f0eb325e0e60..23c7bf91d1b1 100644 --- a/tgui/packages/tgui/interfaces/BrigCell.jsx +++ b/tgui/packages/tgui/interfaces/BrigCell.jsx @@ -3,8 +3,8 @@ import { addZeros } from 'common/math'; import { Window } from '../layouts'; import { Box, ColorBox, NoticeBox, Flex, ProgressBar, Button, LabeledList, Divider } from '../components'; -export const BrigCell = (props, context) => { - const { data, act } = useBackend(context); +export const BrigCell = (props) => { + const { data, act } = useBackend(); const { viewing_incident, incidents, bit_active } = data; return ( @@ -90,8 +90,8 @@ const getTimeLeft = function (data) { return Math.max(0, timeLeft); }; -const IncidentDetails = (props, context) => { - const { data, act } = useBackend(context); +const IncidentDetails = (props) => { + const { data, act } = useBackend(); const { suspect, can_pardon, diff --git a/tgui/packages/tgui/interfaces/CameraConsole.jsx b/tgui/packages/tgui/interfaces/CameraConsole.jsx index 89f4cc4e6d92..b790100e980c 100644 --- a/tgui/packages/tgui/interfaces/CameraConsole.jsx +++ b/tgui/packages/tgui/interfaces/CameraConsole.jsx @@ -37,8 +37,8 @@ export const selectCameras = (cameras, searchText = '') => { ])(cameras); }; -export const CameraConsole = (props, context) => { - const { act, data } = useBackend(context); +export const CameraConsole = (props) => { + const { act, data } = useBackend(); const { mapRef, activeCamera } = data; const cameras = selectCameras(data.cameras); const [prevCameraName, nextCameraName] = prevNextCamera( @@ -89,9 +89,9 @@ export const CameraConsole = (props, context) => { ); }; -export const CameraConsoleContent = (props, context) => { - const { act, data } = useBackend(context); - const [searchText, setSearchText] = useLocalState(context, 'searchText', ''); +export const CameraConsoleContent = (props) => { + const { act, data } = useBackend(); + const [searchText, setSearchText] = useLocalState('searchText', ''); const { activeCamera } = data; const cameras = selectCameras(data.cameras, searchText); return ( diff --git a/tgui/packages/tgui/interfaces/CanvasLayer.jsx b/tgui/packages/tgui/interfaces/CanvasLayer.jsx index e647ae765b1c..cf87e2f601fb 100644 --- a/tgui/packages/tgui/interfaces/CanvasLayer.jsx +++ b/tgui/packages/tgui/interfaces/CanvasLayer.jsx @@ -1,5 +1,5 @@ import { Box, Icon, Tooltip } from '../components'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; // this file should probably not be in interfaces, should move it later. export class CanvasLayer extends Component { diff --git a/tgui/packages/tgui/interfaces/CardMod.jsx b/tgui/packages/tgui/interfaces/CardMod.jsx index 6b27125f0995..6580cb18dfeb 100644 --- a/tgui/packages/tgui/interfaces/CardMod.jsx +++ b/tgui/packages/tgui/interfaces/CardMod.jsx @@ -1,12 +1,12 @@ -import { Fragment } from 'inferno'; +import { Fragment } from 'react'; import { useBackend, useLocalState } from '../backend'; import { Box, Button, Stack, Input, Section, Tabs, Table, NumberInput } from '../components'; import { Window } from '../layouts'; import { AccessList } from './common/AccessList'; import { map } from 'common/collections'; -export const CardMod = (props, context) => { - const [tab2, setTab2] = useLocalState(context, 'tab2', 1); +export const CardMod = (props) => { + const [tab2, setTab2] = useLocalState('tab2', 1); return ( @@ -27,8 +27,8 @@ export const CardMod = (props, context) => { ); }; -export const CrewManifest = (props, context) => { - const { act, data } = useBackend(context); +export const CrewManifest = (props) => { + const { act, data } = useBackend(); const { manifest = {} } = data; return (
{ ); }; -export const CardContent = (props, context) => { - const { act, data } = useBackend(context); - const [tab, setTab] = useLocalState(context, 'tab', 1); +export const CardContent = (props) => { + const { act, data } = useBackend(); + const [tab, setTab] = useLocalState('tab', 1); const { authenticated, regions = [], @@ -75,13 +75,12 @@ export const CardContent = (props, context) => { id_account, } = data; const [selectedDepartment, setSelectedDepartment] = useLocalState( - context, 'department', Object.keys(jobs)[0] ); const departmentJobs = jobs[selectedDepartment] || []; return ( - + <>
{ ) } buttons={ - + <>
{!!has_id && !!authenticated && ( @@ -229,6 +228,6 @@ export const CardContent = (props, context) => { )} )} -
+ ); }; diff --git a/tgui/packages/tgui/interfaces/CasSim.tsx b/tgui/packages/tgui/interfaces/CasSim.tsx index cac23cde1833..f88bf65feab9 100644 --- a/tgui/packages/tgui/interfaces/CasSim.tsx +++ b/tgui/packages/tgui/interfaces/CasSim.tsx @@ -9,10 +9,9 @@ interface CasSimData { detonation_cooldown: number; } -export const CasSim = (_props, context) => { - const { act, data } = useBackend(context); +export const CasSim = () => { + const { act, data } = useBackend(); const [simulationView, setSimulationView] = useLocalState( - context, 'simulation_view', false ); diff --git a/tgui/packages/tgui/interfaces/Centrifuge.jsx b/tgui/packages/tgui/interfaces/Centrifuge.jsx index 45ed7bb4b6d7..94a1e7594fd3 100644 --- a/tgui/packages/tgui/interfaces/Centrifuge.jsx +++ b/tgui/packages/tgui/interfaces/Centrifuge.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Section, Flex, Button, Box, Input, NoticeBox } from '../components'; import { Window } from '../layouts'; -export const Centrifuge = (_props, context) => { - const { act, data } = useBackend(context); +export const Centrifuge = () => { + const { act, data } = useBackend(); return ( diff --git a/tgui/packages/tgui/interfaces/Changelog.jsx b/tgui/packages/tgui/interfaces/Changelog.jsx index 73982f2718a9..5d2fa9447b47 100644 --- a/tgui/packages/tgui/interfaces/Changelog.jsx +++ b/tgui/packages/tgui/interfaces/Changelog.jsx @@ -1,6 +1,6 @@ import { classes } from 'common/react'; import { useBackend } from '../backend'; -import { Component, Fragment } from 'inferno'; +import { Component, Fragment } from 'react'; import { Box, Button, Dropdown, Icon, Section, Stack, Table } from '../components'; import { Window } from '../layouts'; import { resolveAsset } from '../assets'; @@ -60,7 +60,7 @@ export class Changelog extends Component { } getData = (date, attemptNumber = 1) => { - const { act } = useBackend(this.context); + const { act } = useBackend(); const self = this; const maxAttempts = 6; @@ -92,7 +92,7 @@ export class Changelog extends Component { componentDidMount() { const { data: { dates = [] }, - } = useBackend(this.context); + } = useBackend(); if (dates) { dates.forEach((date) => @@ -107,7 +107,7 @@ export class Changelog extends Component { const { data, selectedDate, selectedIndex } = this.state; const { data: { dates }, - } = useBackend(this.context); + } = useBackend(); const { dateChoices } = this; const dateDropdown = dateChoices.length > 0 && ( diff --git a/tgui/packages/tgui/interfaces/ChemDispenser.jsx b/tgui/packages/tgui/interfaces/ChemDispenser.jsx index d4dadb58cfbe..77e4cac58a5f 100644 --- a/tgui/packages/tgui/interfaces/ChemDispenser.jsx +++ b/tgui/packages/tgui/interfaces/ChemDispenser.jsx @@ -3,8 +3,8 @@ import { useBackend } from '../backend'; import { AnimatedNumber, Box, Button, LabeledList, NoticeBox, ProgressBar, Section } from '../components'; import { Window } from '../layouts'; -export const ChemDispenser = (props, context) => { - const { act, data } = useBackend(context); +export const ChemDispenser = (props) => { + const { act, data } = useBackend(); const beakerTransferAmounts = data.beakerTransferAmounts || []; const beakerContents = data.beakerContents || []; return ( diff --git a/tgui/packages/tgui/interfaces/ChooseFruit.jsx b/tgui/packages/tgui/interfaces/ChooseFruit.jsx index 576897b16262..1a1ee9f85341 100644 --- a/tgui/packages/tgui/interfaces/ChooseFruit.jsx +++ b/tgui/packages/tgui/interfaces/ChooseFruit.jsx @@ -3,15 +3,11 @@ import { useBackend, useLocalState } from '../backend'; import { Tabs, Box, Section, Stack, Button } from '../components'; import { Window } from '../layouts'; -export const ChooseFruit = (props, context) => { - const { act, data } = useBackend(context); +export const ChooseFruit = (props) => { + const { act, data } = useBackend(); const { fruits, selected_fruit } = data; - const [compact, setCompact] = useLocalState( - context, - 'choosefruit_compact', - false - ); + const [compact, setCompact] = useLocalState('choosefruit_compact', false); let heightScale = 80; if (compact) heightScale = 45; diff --git a/tgui/packages/tgui/interfaces/ChooseResin.jsx b/tgui/packages/tgui/interfaces/ChooseResin.jsx index 47f91110c0ca..4a031ce9d91f 100644 --- a/tgui/packages/tgui/interfaces/ChooseResin.jsx +++ b/tgui/packages/tgui/interfaces/ChooseResin.jsx @@ -5,15 +5,11 @@ import { Window } from '../layouts'; export const INFINITE_BUILD_AMOUNT = -1; -export const ChooseResin = (props, context) => { - const { act, data } = useBackend(context); +export const ChooseResin = (props) => { + const { act, data } = useBackend(); const { constructions, selected_resin } = data; - const [compact, setCompact] = useLocalState( - context, - 'chooseresin_compact', - false - ); + const [compact, setCompact] = useLocalState('chooseresin_compact', false); let heightScale = 80; if (compact) heightScale = 45; diff --git a/tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx b/tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx index 93edd6f1b2c8..ec5f775c35f4 100644 --- a/tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx +++ b/tgui/packages/tgui/interfaces/ColorMatrixEditor.tsx @@ -10,8 +10,8 @@ type Data = { const PREFIXES = ['r', 'g', 'b', 'a', 'c'] as const; -export const ColorMatrixEditor = (props, context) => { - const { act, data } = useBackend(context); +export const ColorMatrixEditor = (props) => { + const { act, data } = useBackend(); const { mapRef, currentColor } = data; return ( diff --git a/tgui/packages/tgui/interfaces/CommandTablet.jsx b/tgui/packages/tgui/interfaces/CommandTablet.jsx index 8b334d1dac62..429cc943db94 100644 --- a/tgui/packages/tgui/interfaces/CommandTablet.jsx +++ b/tgui/packages/tgui/interfaces/CommandTablet.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Button, Section, Flex, NoticeBox } from '../components'; import { Window } from '../layouts'; -export const CommandTablet = (_props, context) => { - const { act, data } = useBackend(context); +export const CommandTablet = () => { + const { act, data } = useBackend(); const evacstatus = data.evac_status; diff --git a/tgui/packages/tgui/interfaces/CrewConsole.jsx b/tgui/packages/tgui/interfaces/CrewConsole.jsx index 15c49d28061e..e84827ca7c57 100644 --- a/tgui/packages/tgui/interfaces/CrewConsole.jsx +++ b/tgui/packages/tgui/interfaces/CrewConsole.jsx @@ -100,8 +100,8 @@ export const CrewConsole = () => { ); }; -const CrewTable = (props, context) => { - const { act, data } = useBackend(context); +const CrewTable = (props) => { + const { act, data } = useBackend(); const sensors = sortBy((s) => s.ijob)(data.sensors ?? []); return ( @@ -127,8 +127,8 @@ const CrewTable = (props, context) => { ); }; -const CrewTableEntry = (props, context) => { - const { act, data } = useBackend(context); +const CrewTableEntry = (props) => { + const { act, data } = useBackend(); const { link_allowed } = data; const { sensor_data } = props; const { diff --git a/tgui/packages/tgui/interfaces/Cryo.jsx b/tgui/packages/tgui/interfaces/Cryo.jsx index 240352836476..338717f2d0ca 100644 --- a/tgui/packages/tgui/interfaces/Cryo.jsx +++ b/tgui/packages/tgui/interfaces/Cryo.jsx @@ -32,8 +32,8 @@ export const Cryo = () => { ); }; -const CryoContent = (props, context) => { - const { act, data } = useBackend(context); +const CryoContent = (props) => { + const { act, data } = useBackend(); return ( <>
diff --git a/tgui/packages/tgui/interfaces/DemoSim.tsx b/tgui/packages/tgui/interfaces/DemoSim.tsx index 87dfa81236be..4b2a1487146e 100644 --- a/tgui/packages/tgui/interfaces/DemoSim.tsx +++ b/tgui/packages/tgui/interfaces/DemoSim.tsx @@ -10,10 +10,9 @@ interface DemoSimData { detonation_cooldown: number; } -export const DemoSim = (_props, context) => { - const { act, data } = useBackend(context); +export const DemoSim = () => { + const { act, data } = useBackend(); const [simulationView, setSimulationView] = useLocalState( - context, 'simulation_view', false ); diff --git a/tgui/packages/tgui/interfaces/Disposals.jsx b/tgui/packages/tgui/interfaces/Disposals.jsx index c64a55a91334..7fa8098869b9 100644 --- a/tgui/packages/tgui/interfaces/Disposals.jsx +++ b/tgui/packages/tgui/interfaces/Disposals.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Section, ProgressBar, Button, Box } from '../components'; import { Window } from '../layouts'; -export const Disposals = (_props, context) => { - const { act, data } = useBackend(context); +export const Disposals = () => { + const { act, data } = useBackend(); const { pressure, mode, flush } = data; diff --git a/tgui/packages/tgui/interfaces/DrawnMap.jsx b/tgui/packages/tgui/interfaces/DrawnMap.jsx index cd5a9539f847..0e789b631d39 100644 --- a/tgui/packages/tgui/interfaces/DrawnMap.jsx +++ b/tgui/packages/tgui/interfaces/DrawnMap.jsx @@ -1,5 +1,5 @@ import { Box } from '../components'; -import { Component, createRef } from 'inferno'; +import { Component, createRef } from 'react'; export class DrawnMap extends Component { constructor(props) { diff --git a/tgui/packages/tgui/interfaces/DropshipFlightControl.tsx b/tgui/packages/tgui/interfaces/DropshipFlightControl.tsx index 64486c66ad82..2ae9a17fe35f 100644 --- a/tgui/packages/tgui/interfaces/DropshipFlightControl.tsx +++ b/tgui/packages/tgui/interfaces/DropshipFlightControl.tsx @@ -27,8 +27,8 @@ interface DropshipNavigationProps extends NavigationProps { playing_launch_announcement_alarm: boolean; } -const DropshipDoorControl = (_, context) => { - const { data, act } = useBackend(context); +const DropshipDoorControl = () => { + const { data, act } = useBackend(); const in_flight = data.shuttle_mode === 'called' || data.shuttle_mode === 'pre-arrival'; const disable_door_controls = in_flight; @@ -111,10 +111,9 @@ const DropshipDoorControl = (_, context) => { ); }; -export const DropshipDestinationSelection = (_, context) => { - const { data, act } = useBackend(context); +export const DropshipDestinationSelection = () => { + const { data, act } = useBackend(); const [siteselection, setSiteSelection] = useSharedState( - context, 'target_site', undefined ); @@ -149,8 +148,8 @@ interface DestinationProps { readonly availableOnly?: boolean; } -const DestinationSelector = (props: DestinationProps, context) => { - const { data } = useBackend(context); +const DestinationSelector = (props: DestinationProps) => { + const { data } = useBackend(); return ( <> {props.options @@ -186,8 +185,8 @@ const DestinationSelector = (props: DestinationProps, context) => { ); }; -export const TouchdownCooldown = (_, context) => { - const { data } = useBackend(context); +export const TouchdownCooldown = () => { + const { data } = useBackend(); return (
@@ -210,13 +209,12 @@ export const TouchdownCooldown = (_, context) => { ); }; -const AutopilotConfig = (props, context) => { - const { data, act } = useBackend(context); +const AutopilotConfig = (props) => { + const { data, act } = useBackend(); const [automatedHangar, setAutomatedHangar] = useSharedState< string | undefined - >(context, 'autopilot_hangar', undefined); + >('autopilot_hangar', undefined); const [automatedLZ, setAutomatedLZ] = useSharedState( - context, 'autopilot_groundside', undefined ); @@ -281,8 +279,8 @@ const AutopilotConfig = (props, context) => { ); }; -const StopLaunchAnnouncementAlarm = (_, context) => { - const { act } = useBackend(context); +const StopLaunchAnnouncementAlarm = () => { + const { act } = useBackend(); return (
{props.data @@ -57,9 +57,9 @@ const PlaytimeTable = (props: { readonly data: PlaytimeRecord[] }, context) => { ); }; -export const Playtime = (props, context) => { - const { data } = useBackend(context); - const [selected, setSelected] = useLocalState(context, 'selected', 'human'); +export const Playtime = (props) => { + const { data } = useBackend(); + const [selected, setSelected] = useLocalState('selected', 'human'); const humanTime = data.stored_human_playtime.length > 0 ? data.stored_human_playtime[0].playtime diff --git a/tgui/packages/tgui/interfaces/PodLauncher.jsx b/tgui/packages/tgui/interfaces/PodLauncher.jsx index 751fe1d386af..22f44abaf53c 100644 --- a/tgui/packages/tgui/interfaces/PodLauncher.jsx +++ b/tgui/packages/tgui/interfaces/PodLauncher.jsx @@ -2,7 +2,7 @@ import { toFixed } from 'common/math'; import { storage } from 'common/storage'; import { multiline } from 'common/string'; import { createUuid } from 'common/uuid'; -import { Component, Fragment } from 'inferno'; +import { Component, Fragment } from 'react'; import { useBackend, useLocalState } from '../backend'; import { Box, Button, ByondUi, Divider, Input, Knob, LabeledControls, NumberInput, Section, Flex, Slider } from '../components'; import { Window } from '../layouts'; @@ -11,7 +11,7 @@ const pod_grey = { color: 'grey', }; -export const PodLauncher = (props, context) => { +export const PodLauncher = (props) => { return ( @@ -19,7 +19,7 @@ export const PodLauncher = (props, context) => { ); }; -const PodLauncherContent = (props, context) => { +const PodLauncherContent = (props) => { return ( @@ -132,8 +132,8 @@ const EFFECTS_LOAD = [ selected: (data) => { return data['launch_choice'] === data.glob_launch_options.LAUNCH_ALL; }, - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('set_launch_option', { launch_option: 'LAUNCH_ALL' }); }, }, @@ -143,8 +143,8 @@ const EFFECTS_LOAD = [ selected: (data) => { return data['launch_choice'] === data.glob_launch_options.LAUNCH_RANDOM; }, - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('set_launch_option', { launch_option: 'LAUNCH_RANDOM' }); }, }, @@ -157,8 +157,8 @@ const EFFECTS_LOAD = [ selected: (data) => { return !data['launch_random_item']; }, - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('launch_random_item', { should_do: false }); }, }, @@ -168,8 +168,8 @@ const EFFECTS_LOAD = [ selected: (data) => { return data['launch_random_item']; }, - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('launch_random_item', { should_do: true }); }, }, @@ -180,8 +180,8 @@ const EFFECTS_LOAD = [ title: 'Clone', icon: 'clone', selected: (data) => data['launch_clone'], - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('launch_clone', { should_do: !data['launch_clone'] }); }, }, @@ -189,8 +189,8 @@ const EFFECTS_LOAD = [ title: 'Recall', icon: 'redo', selected: (data) => data['should_recall'], - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('set_should_recall', { should_do: !data['should_recall'] }); }, }, @@ -203,8 +203,8 @@ const EFFECTS_NORMAL = [ selected: (data) => { return data['gib_on_land']; }, - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('set_gib_on_land', { should_do: !data['gib_on_land'] }); }, }, @@ -214,8 +214,8 @@ const EFFECTS_NORMAL = [ selected: (data) => { return data['stealth']; }, - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('set_stealth', { should_do: !data['stealth'] }); }, }, @@ -225,8 +225,8 @@ const EFFECTS_NORMAL = [ selected: (data) => { return data['can_be_opened']; }, - onClick: (context) => { - const { act, data } = useBackend(context); + onClick: () => { + const { act, data } = useBackend(); act('set_can_be_opened', { should_do: !data['can_be_opened'] }); }, }, @@ -246,13 +246,9 @@ const EFFECTS_ALL = [ }, ]; -const ViewTabHolder = (props, context) => { - const { act, data } = useBackend(context); - const [tabPageIndex, setTabPageIndex] = useLocalState( - context, - 'tabPageIndex', - 1 - ); +const ViewTabHolder = (props) => { + const { act, data } = useBackend(); + const [tabPageIndex, setTabPageIndex] = useLocalState('tabPageIndex', 1); const { glob_tab_indexes, custom_dropoff, map_ref } = data; const TabPageComponent = TABPAGES[tabPageIndex].component(); return ( @@ -342,7 +338,7 @@ const ViewTabHolder = (props, context) => { ); }; -const TabPod = (props, context) => { +const TabPod = (props) => { return ( Note: You can right click on this @@ -352,8 +348,8 @@ const TabPod = (props, context) => { ); }; -const TabBay = (props, context) => { - const { act, data } = useBackend(context); +const TabBay = (props) => { + const { act, data } = useBackend(); return ( <>
{props.items @@ -43,16 +43,12 @@ const ContentsTable = ( ); }; -const Contents = ( - props: { - readonly isLocal: boolean; - readonly items: StorageItem[]; - readonly title: string; - }, - context -) => { +const Contents = (props: { + readonly isLocal: boolean; + readonly items: StorageItem[]; + readonly title: string; +}) => { const [tabIndex, setTabIndex] = useLocalState( - context, `contentsTab_${props.isLocal}`, 'all' ); @@ -109,11 +105,11 @@ const Contents = ( ); }; -const ContentItem = ( - props: { readonly isLocal: boolean; readonly item: StorageItem }, - context -) => { - const { data, act } = useBackend(context); +const ContentItem = (props: { + readonly isLocal: boolean; + readonly item: StorageItem; +}) => { + const { data, act } = useBackend(); const { item } = props; const itemref = { 'index': item.index, 'amount': 1, isLocal: props.isLocal }; return ( @@ -149,8 +145,8 @@ const ContentItem = ( ); }; -export const SmartFridge = (_, context) => { - const { data } = useBackend(context); +export const SmartFridge = () => { + const { data } = useBackend(); return ( diff --git a/tgui/packages/tgui/interfaces/Smes.jsx b/tgui/packages/tgui/interfaces/Smes.jsx index 47f6e11991f1..7dce4bf1949f 100644 --- a/tgui/packages/tgui/interfaces/Smes.jsx +++ b/tgui/packages/tgui/interfaces/Smes.jsx @@ -6,8 +6,8 @@ import { Window } from '../layouts'; // Common power multiplier const POWER_MUL = 1e3; -export const Smes = (props, context) => { - const { act, data } = useBackend(context); +export const Smes = (props) => { + const { act, data } = useBackend(); const { capacityPercent, capacity, diff --git a/tgui/packages/tgui/interfaces/SquadInfo.tsx b/tgui/packages/tgui/interfaces/SquadInfo.tsx index 4bd0068850e2..112c1b9b2d99 100644 --- a/tgui/packages/tgui/interfaces/SquadInfo.tsx +++ b/tgui/packages/tgui/interfaces/SquadInfo.tsx @@ -47,11 +47,8 @@ interface SquadProps { objective: { primary?: string; secondary?: string }; } -const FireTeamLeadLabel = ( - props: { readonly ftl: SquadMarineEntry }, - context -) => { - const { data } = useBackend(context); +const FireTeamLeadLabel = (props: { readonly ftl: SquadMarineEntry }) => { + const { data } = useBackend(); const { ftl } = props; return ( <> @@ -75,11 +72,11 @@ const FireTeamLeadLabel = ( ); }; -const FireTeamLead = ( - props: { readonly fireteam: FireTeamEntry; readonly ft: string }, - context -) => { - const { data, act } = useBackend(context); +const FireTeamLead = (props: { + readonly fireteam: FireTeamEntry; + readonly ft: string; +}) => { + const { data, act } = useBackend(); const fireteamLead = props.fireteam.tl; const isNotAssigned = fireteamLead === undefined || @@ -114,7 +111,7 @@ interface FireteamBoxProps extends BoxProps { readonly isEmpty: boolean; } -const FireteamBox = (props: FireteamBoxProps, context) => { +const FireteamBox = (props: FireteamBoxProps) => { return (
{props.name}
@@ -123,8 +120,8 @@ const FireteamBox = (props: FireteamBoxProps, context) => { ); }; -const FireTeam = (props: { readonly ft: string }, context) => { - const { data, act } = useBackend(context); +const FireTeam = (props: { readonly ft: string }) => { + const { data, act } = useBackend(); const fireteam: FireTeamEntry = data.fireteams[props.ft]; const members: SquadMarineEntry[] = @@ -193,15 +190,12 @@ const FireTeam = (props: { readonly ft: string }, context) => { ); }; -const FireTeamMember = ( - props: { - readonly member: SquadMarineEntry; - readonly team: string; - readonly fireteam?: FireTeamEntry; - }, - context -) => { - const { data, act } = useBackend(context); +const FireTeamMember = (props: { + readonly member: SquadMarineEntry; + readonly team: string; + readonly fireteam?: FireTeamEntry; +}) => { + const { data, act } = useBackend(); const assignFT1 = { target_ft: 'FT1', target_marine: props.member.name }; const assignFT2 = { target_ft: 'FT2', target_marine: props.member.name }; const assignFT3 = { target_ft: 'FT3', target_marine: props.member.name }; @@ -273,8 +267,8 @@ const FireTeamMember = ( ); }; -const SquadObjectives = (props, context) => { - const { data } = useBackend(context); +const SquadObjectives = (props) => { + const { data } = useBackend(); const primaryObjective = data.objective?.primary ?? 'Unset'; const secondaryObjective = data.objective?.secondary ?? 'Unset'; return ( @@ -289,8 +283,8 @@ const SquadObjectives = (props, context) => { ); }; -export const SquadInfo = (_, context) => { - const { data } = useBackend(context); +export const SquadInfo = () => { + const { data } = useBackend(); const fireteams = ['FT1', 'FT2', 'FT3', 'Unassigned']; return ( diff --git a/tgui/packages/tgui/interfaces/SquadMod.jsx b/tgui/packages/tgui/interfaces/SquadMod.jsx index 15ce727a5341..d2b7722bd8a0 100644 --- a/tgui/packages/tgui/interfaces/SquadMod.jsx +++ b/tgui/packages/tgui/interfaces/SquadMod.jsx @@ -2,8 +2,8 @@ import { useBackend } from '../backend'; import { Button, Stack, Section, NoticeBox } from '../components'; import { Window } from '../layouts'; -export const SquadMod = (props, context) => { - const { act, data } = useBackend(context); +export const SquadMod = (props) => { + const { act, data } = useBackend(); const { squads = [], human, id_name, has_id } = data; return ( diff --git a/tgui/packages/tgui/interfaces/StatbrowserOptions.jsx b/tgui/packages/tgui/interfaces/StatbrowserOptions.jsx index d4a1a482336e..2b1712453360 100644 --- a/tgui/packages/tgui/interfaces/StatbrowserOptions.jsx +++ b/tgui/packages/tgui/interfaces/StatbrowserOptions.jsx @@ -2,14 +2,10 @@ import { useBackend, useLocalState } from '../backend'; import { Flex, NumberInput, Section } from '../components'; import { Window } from '../layouts'; -export const StatbrowserOptions = (props, context) => { - const { act, data } = useBackend(context); +export const StatbrowserOptions = (props) => { + const { act, data } = useBackend(); const { current_fontsize } = data; - const [fontsize, setFontsize] = useLocalState( - context, - 'fontsize', - current_fontsize - ); + const [fontsize, setFontsize] = useLocalState('fontsize', current_fontsize); return ( @@ -33,7 +29,7 @@ export const StatbrowserOptions = (props, context) => { ); }; -const Options = (props, context) => { +const Options = (props) => { const { children } = props; return ( @@ -49,7 +45,7 @@ const Options = (props, context) => { ); }; -const Option = (props, context) => { +const Option = (props) => { const { category, input } = props; return ( @@ -60,7 +56,7 @@ const Option = (props, context) => { ); }; -const NumberOption = (props, context) => { +const NumberOption = (props) => { const { category, ...rest } = props; return