1
0
Files
dotfiles/pkgs/tetrio/module.nix
T

724 lines
34 KiB
Nix

{
config,
lib,
pkgs,
...
}:
let
inherit (lib) mkOption mkIf types;
inherit (types) either;
clamp =
x: min: max:
if x < min then
min
else if x > max then
max
else
x;
cfg = config.programs.tetrio-desktop;
in
{
options.programs.tetrio-desktop = {
enable = lib.mkEnableOption "tetrio";
package = lib.mkPackageOption pkgs "tetrio-desktop" { };
plus = {
enable = lib.mkEnableOption "tetrio-plus";
package = lib.mkOption {
type = types.path;
description = "The package to use for TETR.IO PLUS";
default = pkgs.tetrio-plus;
};
skin = mkOption {
type = types.nullOr (
types.submodule {
options = {
package = mkOption {
type = types.package;
description = "The package to use for the skin";
example = "mpkgs.tetrio.skin.simple-connected";
};
nearest = mkOption {
type = types.bool;
description = "Force nearest-neighbor scaling";
default = false;
};
};
}
);
default = null;
description = "In-game tetrimino skin";
};
hideOnStartup = lib.mkOption {
type = types.bool;
default = true;
description = "Disable the TETR.IO PLUS configuration panel showing";
};
};
settings = {
handling =
let
buffering = types.enum [
"off"
"hold"
"tap"
];
in
{
auto_repeat_rate = mkOption {
type = types.number;
apply = x: clamp x 0 5;
description = "Automatic Repeat Rate: the speed at which tetrominoes move when holding down movement keys, measured in frames per movement.";
default = 2;
};
delayed_auto_shift = mkOption {
type = types.number;
apply = x: clamp x 1 20;
description = "Delayed Auto Shift: the time between the initial keypress and the start of its automatic repeat movement, measured in frames.";
default = 10;
};
das_cut_delay = mkOption {
type = types.number;
apply = x: clamp x 0 20;
description = "DAS Cut Delay (frames): if not 0, any ongoing DAS movement will pause for a set amount of time after dropping/rotating a piece, measured in frames.";
default = 1;
};
soft_drop_factor = mkOption {
type = types.number;
apply = x: clamp x 5 41;
description = "Soft Drop Factor: the factor with which soft drops change the gravity speed.";
default = 6;
};
prevent_hard_drops = mkOption {
type = types.bool;
description = "If enabled, when a piece locks on its own, the hard drop key becomes unavailable for a few frames. This prevents accidental hard drops.";
default = true;
};
direction_cancel_das = mkOption {
type = types.bool;
description = "If enabled, DAS charge is cancelled when you change directions.";
default = false;
};
soft_drop_over_movement = mkOption {
type = types.bool;
description = "If enabled, at very high speeds soft drop will always take precedence over horizontal movement, for a more consistent game feel.";
default = true;
};
rotation_buffering = mkOption {
type = buffering;
description = "When to buffer rotations for the next piece (\"hold\": only if you hold the key as the next piece spawns).";
default = "tap";
};
hold_buffering = mkOption {
type = buffering;
description = "When to buffer holding for the next piece (\"hold\": only if you hold the key as the next piece spawns).";
default = "tap";
};
};
audio =
let
percentage = x: x / 100.0;
in
{
music = {
volume = mkOption {
type = types.number;
apply = x: percentage (clamp x 0 200);
description = "Volume (%) at which BGM and jingles play.";
default = 100;
};
reset_on_retry = mkOption {
type = types.bool;
apply = x: !x;
description = "If enabled, the background music will reset whenever you retry a game.";
default = false;
};
preferences = mkOption {
type =
with types;
attrsOf (enum [
"off"
"--"
"-"
""
"+"
"++"
]);
apply =
x:
pkgs.lib.mapAttrs (
_: value:
{
"off" = "ban";
"--" = "minmin";
"-" = "min";
"" = "base";
"+" = "plus";
"++" = "plusplus";
}
."${value}"
) x;
default = { };
};
};
sfx = {
volume = mkOption {
type = types.number;
apply = x: percentage (clamp x 0 200);
description = "Volume (%) at which sound effects play.";
default = 100;
};
next_pieces = mkOption {
type = types.bool;
description = "Whether to play a sound effect that signifies the next piece that'll come up.";
default = false;
};
other_players = mkOption {
type = types.bool;
description = "Whether to hear the sounds of other people playing in Multiplayer.";
default = true;
};
attacks = mkOption {
type = types.bool;
description = "Whether to hear the sounds of attacks going towards and from you.";
default = true;
};
speed_changes = mkOption {
type = types.bool;
description = "Whether to hear a sound when your Climb Speed changes in Quick Play.";
default = true;
};
};
scroll_adjust_volume = mkOption {
type = types.bool;
description = "Whether to allow adjusting the volume via scrolling in-game (or everywhere while holding ALT).";
default = true;
};
mute_unfocused = mkOption {
type = types.bool;
description = "If enabled, the background music will be muted when TETR.IO is minimized or tabbed away.";
default = true;
};
stereo = mkOption {
type = types.number;
apply = x: percentage (clamp x 0 100);
description = "Intensity (%) of stereo effects. 0% -> Centered; 100% -> Directional";
default = 50;
};
disable = mkOption {
type = types.bool;
description = "If enabled, no audio will ever play. This will speed up the game, with a rather obvious drawback.";
default = false;
};
};
visual =
let
toFloat = x: if builtins.typeOf x == "str" then builtins.fromJSON x else x;
percentage =
x: min: max:
builtins.toString ((clamp (toFloat x) min max) / 100.0);
in
{
graphics = {
pipeline = mkOption {
type = types.enum [
"compatability"
"webgl-1"
"webgl-2"
];
apply =
x:
{
"compatability" = "legacy";
"webgl-1" = "webgl1";
"webgl-2" = "webgl2";
}
."${x}";
description = "The WebGL mode to use for rendering";
default = "webgl-2";
};
tier = mkOption {
type = types.enum [
"minimal"
"low"
"medium"
"high"
"ultra"
];
description = "The graphics tier to use for rendering";
default = "ultra";
};
cache = mkOption {
type = types.bool;
apply = x: if x then "medium" else "low";
description = "Whether or not to enable caching of resources";
default = true;
};
particle_count = mkOption {
type = with types; either str number;
apply = x: percentage x 10 150;
description = "How many particles to display (%).";
default = 150;
};
powersave = mkOption {
type = types.bool;
description = "If enabled, prioritize power saving over performance.";
default = false;
};
low_resolution = mkOption {
type = types.bool;
description = "If enabled, render blurrier graphics. Not very pretty but helps performance.";
default = false;
};
low_precision_counters = mkOption {
type = types.bool;
description = "If enabled, don't show as much precision on in-game counters. Speeds up the game significantly.";
default = false;
};
};
show_unfocus_warning = mkOption {
type = types.bool;
description = "If enabled, a warning is shown when TETR.IO is out of focus.";
default = true;
};
action_text = mkOption {
type = types.enum [
"off"
"some"
"all"
];
description = "Determines how much action text to display when performing special attacks.";
default = "some";
};
board_bounciness = mkOption {
type = with types; either str number;
apply = x: percentage x 0 200;
description = "How much (%) the board reacts when you move pieces around.";
default = 40;
};
damage_shakiness = mkOption {
type = with types; either str number;
apply = x: percentage x 0 110;
description = "How much (%) the board reacts when you receive damage.";
default = 100;
};
grid_opacity = mkOption {
type = with types; either str number;
apply = x: percentage x 0 110;
description = "How visible the grid is (%). 0% makes the grid invisible.";
default = 10;
};
board_opacity = mkOption {
type = with types; either str number;
apply = x: percentage x 0 110;
description = "How visible the board is (%). 0% makes the board invisible.";
default = 85;
};
shadow_opacity = mkOption {
type = with types; either str number;
apply = x: percentage x 0 110;
description = "How visible the shadow piece is (%). 0% makes the shadow piece invisible.";
default = 15;
};
background_opacity = mkOption {
type = with types; either str number;
apply = x: percentage x 0 100;
description = "How visible the background images are. 0% makes the background entirely black.";
default = 5;
};
show_background_in_menus = mkOption {
type = types.bool;
apply = x: !x;
description = "If enabled, do not show the background when in menus. This will majorly improve performance in menus, but you will no longer see the background.";
default = false;
};
board_zoom = mkOption {
type = with types; either str number;
apply = x: percentage x 0 110;
description = "How large the board displays. Values over 100% may cause some elements to not be visible.";
default = 100;
};
spin_board = mkOption {
type = types.bool;
description = "When enabled, the board reacts to T-Spins by rotating a little with it.";
default = true;
};
fire_meter = mkOption {
type = types.bool;
description = "If enabled, fill a fire meter when doing well, illuminating boards that have a lot of fire.";
default = true;
};
danger_warning = mkOption {
type = types.bool;
description = "If enabled, makes the board red and play a warning sound when you're in danger.";
default = true;
};
colored_shadow = mkOption {
type = types.bool;
description = "If enabled, colors the shadow piece.";
default = true;
};
color_locked_hold_piece = mkOption {
type = types.bool;
description = "If enabled, the HOLD piece will be grayed out if it may not be used.";
default = true;
};
};
multiplayer = {
chat = {
enable = mkOption {
type = types.bool;
apply = x: !x;
description = "If disabled, chat will be hidden when ingame.";
default = true;
};
filter = mkOption {
type = types.bool;
description = "If enabled, profanity in chat will be filtered. Note that such filters are never perfect.";
default = true;
};
show_emotes = mkOption {
type = types.bool;
description = "If enabled, show emotes in chat.";
default = true;
};
show_animated_emotes = mkOption {
type = types.bool;
description = "If enabled, show animated emotes in chat.";
default = true;
};
invert = mkOption {
type = types.bool;
description = "If enabled, chat text shows in black (good for light backgrounds).";
default = false;
};
darken = mkOption {
type = types.bool;
description = "If enabled, show a background behind chat messages when typing.";
default = true;
};
};
notifications =
let
notification.optional = types.enum [
"off"
"in-game"
"desktop"
];
notification.required = types.enum [
"in-game"
"desktop"
];
apply =
x:
{
"off" = "off";
"in-game" = "ingame";
"desktop" = "both";
}
."${x}";
in
{
when = {
friend_online = mkOption {
type = notification.optional;
inherit apply;
description = "Notify me when a friend goes online";
default = "in-game";
};
friend_offline = mkOption {
type = notification.optional;
inherit apply;
description = "Notify me when a friend goes offline";
default = "off";
};
friend_dm_recieved = mkOption {
type = notification.optional;
inherit apply;
description = "Notify me when a friend sends me a direct message";
default = "desktop";
};
dm_recieved = mkOption {
type = notification.optional;
inherit apply;
description = "Notify me when a non-friend sends me a direct message";
default = "desktop";
};
room_invite = mkOption {
type = notification.required;
inherit apply;
description = "Notify me when someone invites me to a room";
default = "desktop";
};
other = mkOption {
type = notification.required;
inherit apply;
description = "Other notifications";
default = "desktop";
};
};
enable_desktop_notifications = mkOption {
type = types.bool;
description = "If enabled, show notifications outside of the game.";
default = true;
};
suppress_while_playing = mkOption {
type = types.bool;
description = "If enabled, unimportant notifications don't show ingame, but will be shown after the game.";
default = false;
};
full_volume = mkOption {
type = types.bool;
description = "If enabled, notification sounds always play at full volume.";
default = true;
};
};
hide_room_ids = mkOption {
type = types.bool;
description = "If enabled, room IDs will not be shown, to protect you from streamsniping.";
default = false;
};
show_network_warnings = mkOption {
type = types.bool;
apply = x: !x;
description = "If disabled, network warning icons will not be shown.";
default = true;
};
notify_elim = mkOption {
type = types.bool;
description = "If enabled, show a popup when you KO someone or get KO'd.";
default = true;
};
duels_side_by_side = mkOption {
type = types.bool;
description = "Whether to display a duel side-by-side. This causes your own board to move slightly to the left.";
default = true;
};
simplify_thumbnails = mkOption {
type = types.bool;
description = "If enabled, always shows the simpler thumbnails for other players. This increases performance in games between 2 and 8 players, but looks less nice.";
default = false;
};
animate_super_lobby_background = mkOption {
type = types.bool;
apply = x: !x;
description = "If enabled, the background of Super Lobbies (rooms of 100+ players) animate.";
default = true;
};
animate_background_in_quickplay = mkOption {
type = types.bool;
apply = x: !x;
description = "If enabled, the background of Quick Play animate.";
default = true;
};
welcome_guide = mkOption {
type = types.bool;
description = "If enabled, show the simple guide with the keybinds in multiplayer lobbies.";
default = true;
};
};
keep_replay_tools_open = mkOption {
type = types.bool;
description = "If enabled, the replay tools do not collapse when you're not using them.";
default = false;
};
skip_login_screen = mkOption {
type = types.enum [
"never"
"by-url"
"always"
];
apply =
x:
{
"never" = "never";
"by-url" = "quickjoin";
"always" = "always";
}
."${x}";
description = "When to skip the login screen (\"by-url\": only when joining matches or viewing replays by url)";
default = "always";
};
discord_rpc = mkOption {
type = types.bool;
description = "If enabled, show your current activity in Discord.";
default = true;
};
flash_taskbar_icon = mkOption {
type = types.bool;
description = "If enabled, the taskbar icon will flash when something important happens.";
default = true;
};
devtools = mkOption {
type = types.bool;
description = "If enabled, allow opening the developer toolbox.";
default = false;
};
advertisments.i_support_the_devs.i_cannot_play_with_ads.and_i_really_want_to.disable = mkOption {
type = types.bool;
description = "If enabled, most/all third-party ads will not show.";
default = false;
};
};
};
config = mkIf cfg.enable (
lib.mkMerge [
{
home.packages = [
(cfg.package.override {
withTetrioPlus = cfg.plus.enable;
tetrio-plus = cfg.plus.package;
})
];
home.initialFile.".config/tetrio-desktop/Local Storage/leveldb" = {
mode = "0644";
source = pkgs.callPackage ./leveldb.nix {
value = {
handling =
let
set = cfg.settings.handling;
in
{
arr = set.auto_repeat_rate;
das = set.delayed_auto_shift;
dcd = set.das_cut_delay;
sdf = set.soft_drop_factor;
safelock = set.prevent_hard_drops;
cancel = set.direction_cancel_das;
may20g = set.soft_drop_over_movement;
irs = set.rotation_buffering;
ihs = set.hold_buffering;
};
volume =
let
set = cfg.settings.audio;
in
{
music = set.music.volume;
bgmtweak = set.music.preferences;
sfx = set.sfx.volume;
stereo = set.stereo;
scrollable = set.scroll_adjust_volume;
oof = set.mute_unfocused;
next = set.sfx.next_pieces;
others = set.sfx.other_players;
attacks = set.sfx.attacks;
zenithrank = set.sfx.speed_changes;
disable = set.disable;
};
video =
let
set = cfg.settings.visual;
mul = cfg.settings.multiplayer;
in
{
actionText = set.action_text;
bounciness = set.board_bounciness;
shakiness = set.damage_shakiness;
gridopacity = set.grid_opacity;
boardopacity = set.board_opacity;
shadowopacity = set.shadow_opacity;
zoom = set.board_zoom;
sidebyside = mul.duels_side_by_side;
spin = set.spin_board;
kos = mul.notify_elim;
siren = set.danger_warning;
colorshadow = set.colored_shadow;
graphics = set.graphics.tier;
caching = set.graphics.cache;
webgl = set.graphics.pipeline;
particles = set.graphics.particle_count;
background = set.background_opacity;
powersave = set.graphics.powersave;
lowres = set.graphics.low_resolution;
lowrescounters = set.graphics.low_precision_counters;
alwaystiny = mul.simplify_thumbnails;
nosuperlobbyanim = mul.animate_super_lobby_background;
nozenithanim = mul.animate_background_in_quickplay;
nobg = set.show_background_in_menus;
chatfilter = mul.chat.filter;
nochat = mul.chat.enable;
hideroomids = mul.hide_room_ids;
emotes = mul.chat.show_emotes;
emotes_anim = mul.chat.show_animated_emotes;
invert = mul.chat.invert;
chatbg = mul.chat.darken;
replaytoolsnocollapse = cfg.settings.keep_replay_tools_open;
hidenetwork = mul.show_network_warnings;
focuswarning = set.show_unfocus_warning;
guide = mul.welcome_guide;
desktopnotifications = mul.notifications.enable_desktop_notifications;
};
notifications =
let
set = cfg.settings.multiplayer.notifications;
in
{
suppress = set.suppress_while_playing;
forcesound = set.full_volume;
online = set.when.friend_online;
offline = set.when.friend_offline;
dm = set.when.friend_dm_recieved;
dm_pending = set.when.dm_recieved;
invite = set.when.room_invite;
other = set.when.other;
};
electron = {
loginskip = cfg.settings.skip_login_screen;
adblock =
cfg.settings.advertisments.i_support_the_devs.i_cannot_play_with_ads.and_i_really_want_to.disable;
taskbarflash = cfg.settings.flash_taskbar_icon;
autoupdate = false;
anglecompat = false;
presence = cfg.settings.discord_rpc;
devtools = cfg.settings.devtools;
};
};
};
recursive = true;
};
}
(mkIf (cfg.plus.enable) {
home.file.".config/tetrio-desktop/tetrioplus/tpkey-tetrioPlusEnabled.json" = {
text = ''
{ "value": true }
'';
force = true;
};
home.file.".config/tetrio-desktop/tetrioplus/tpkey-hideTetrioPlusOnStartup.json" = {
text = ''
{ "value": ${lib.trivial.boolToString cfg.plus.hideOnStartup} }
'';
force = true;
};
})
(mkIf (cfg.plus.enable && cfg.plus ? skin) {
home.file.".config/tetrio-desktop/tetrioplus/tpkey-skin.json" = {
source = cfg.plus.skin.package;
force = true;
};
home.file.".config/tetrio-desktop/tetrioplus/tpkey-forceNearestScaling.json" = {
text = ''
{ "value": ${lib.trivial.boolToString cfg.plus.skin.nearest} }
'';
force = true;
};
})
]
);
}