From dadde925bbd7f985545e3432ef8a8e55203bda77 Mon Sep 17 00:00:00 2001 From: Nadhif Radityo Date: Mon, 20 May 2024 12:01:55 +0700 Subject: [PATCH] feat(project): Final revisions --- .VSCodeCounter/2024-05-20_05-37-25/details.md | 101 ++++ .../2024-05-20_05-37-25/diff-details.md | 15 + .VSCodeCounter/2024-05-20_05-37-25/diff.csv | 2 + .VSCodeCounter/2024-05-20_05-37-25/diff.md | 19 + .VSCodeCounter/2024-05-20_05-37-25/diff.txt | 22 + .../2024-05-20_05-37-25/results.csv | 88 +++ .../2024-05-20_05-37-25/results.json | 1 + .VSCodeCounter/2024-05-20_05-37-25/results.md | 57 ++ .../2024-05-20_05-37-25/results.txt | 146 +++++ .VSCodeCounter/2024-05-22_07-57-47/details.md | 102 ++++ .../2024-05-22_07-57-47/diff-details.md | 34 ++ .VSCodeCounter/2024-05-22_07-57-47/diff.csv | 21 + .VSCodeCounter/2024-05-22_07-57-47/diff.md | 35 ++ .VSCodeCounter/2024-05-22_07-57-47/diff.txt | 57 ++ .../2024-05-22_07-57-47/results.csv | 89 +++ .../2024-05-22_07-57-47/results.json | 1 + .VSCodeCounter/2024-05-22_07-57-47/results.md | 57 ++ .../2024-05-22_07-57-47/results.txt | 147 +++++ .vscode/launch.json | 11 +- .vscode/settings.json | 5 + data/assets/potions/potion1.png.txt | 24 + .../generator/gamesave/generatelaboratory.mjs | 43 ++ data/generator/gamesave/laboratory.csv | 284 +++++++++ data/generator/monsters/fetchmonsters.mjs | 24 +- main.py | 9 +- pyrightconfig.json | 7 + src/game/arena/ui_logic.py | 12 +- src/game/battle/ui_logic.py | 56 +- src/game/generateinit.mjs | 6 +- src/game/inventory/__init__.py | 6 +- src/game/inventory/inventory.py | 27 + src/game/laboratory/__init__.py | 5 + src/game/laboratory/laboratory.py | 31 + src/game/menu/__init__.py | 12 +- src/game/menu/menu.py | 23 +- src/game/menu/ui_arena.py | 2 +- src/game/menu/ui_battle.py | 8 +- src/game/menu/ui_debug.py | 29 +- src/game/menu/ui_laboratory.py | 412 +++++++++++++ src/game/menu/ui_shop.py | 365 +++++++++++ src/game/menu/ui_user.py | 38 +- src/game/monster/__init__.py | 3 +- src/game/monster/monster.py | 5 + src/game/shop/__init__.py | 5 + src/game/shop/shop.py | 31 + src/game/state/__init__.py | 3 +- src/game/state/state.py | 2 + src/game/state/visual.py | 565 +++++++++++++++--- src/game/user/__init__.py | 3 +- src/game/user/user.py | 179 +++--- src/utils/console/__init__.py | 4 +- src/utils/console/console.py | 47 +- src/utils/coroutines/__init__.py | 7 +- src/utils/coroutines/coroutines.py | 45 +- src/utils/generateinit.mjs | 6 +- src/utils/primordials/__init__.py | 3 +- src/utils/primordials/primordials.py | 49 +- test/testabortsignalsuspendable.py | 34 ++ test/testgetch.py | 128 ++++ test/testprimordialstrim.py | 18 + tmp.prof | Bin 0 -> 109834 bytes 61 files changed, 3290 insertions(+), 280 deletions(-) create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/details.md create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/diff-details.md create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/diff.csv create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/diff.md create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/diff.txt create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/results.csv create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/results.json create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/results.md create mode 100644 .VSCodeCounter/2024-05-20_05-37-25/results.txt create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/details.md create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/diff-details.md create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/diff.csv create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/diff.md create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/diff.txt create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/results.csv create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/results.json create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/results.md create mode 100644 .VSCodeCounter/2024-05-22_07-57-47/results.txt create mode 100644 .vscode/settings.json create mode 100644 data/assets/potions/potion1.png.txt create mode 100644 data/generator/gamesave/generatelaboratory.mjs create mode 100644 data/generator/gamesave/laboratory.csv create mode 100644 pyrightconfig.json create mode 100644 src/game/menu/ui_laboratory.py create mode 100644 src/game/menu/ui_shop.py create mode 100644 test/testabortsignalsuspendable.py create mode 100644 test/testgetch.py create mode 100644 test/testprimordialstrim.py create mode 100644 tmp.prof diff --git a/.VSCodeCounter/2024-05-20_05-37-25/details.md b/.VSCodeCounter/2024-05-20_05-37-25/details.md new file mode 100644 index 0000000..4aa6a17 --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/details.md @@ -0,0 +1,101 @@ +# Details + +Date : 2024-05-20 05:37:25 + +Directory e:\\Projects\\Python\\if1210-2024-tubes-k08-a + +Total : 86 files, 10364 codes, 181 comments, 706 blanks, all 11251 lines + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [F012 - Shop Management.py](/F012%20-%20Shop%20Management.py) | Python | 264 | 1 | 32 | 297 | +| [F13 Monster Management.py](/F13%20Monster%20Management.py) | Python | 47 | 1 | 7 | 55 | +| [README.md](/README.md) | Markdown | 10 | 0 | 6 | 16 | +| [data/assets/README.md](/data/assets/README.md) | Markdown | 11 | 0 | 10 | 21 | +| [data/generator/gamesave/createnpc.mjs](/data/generator/gamesave/createnpc.mjs) | JavaScript | 59 | 2 | 6 | 67 | +| [data/generator/monsters/copyasciifiles.mjs](/data/generator/monsters/copyasciifiles.mjs) | JavaScript | 22 | 0 | 3 | 25 | +| [data/generator/monsters/fetchmonsters.mjs](/data/generator/monsters/fetchmonsters.mjs) | JavaScript | 90 | 0 | 7 | 97 | +| [data/generator/monsters/fetchsprites.mjs](/data/generator/monsters/fetchsprites.mjs) | JavaScript | 40 | 0 | 3 | 43 | +| [data/generator/monsters/generatesplash.mjs](/data/generator/monsters/generatesplash.mjs) | JavaScript | 158 | 0 | 4 | 162 | +| [data/generator/monsters/package-lock.json](/data/generator/monsters/package-lock.json) | JSON | 708 | 0 | 1 | 709 | +| [data/generator/monsters/package.json](/data/generator/monsters/package.json) | JSON | 13 | 0 | 1 | 14 | +| [data/generator/worldmap/download.mjs](/data/generator/worldmap/download.mjs) | JavaScript | 75 | 0 | 4 | 79 | +| [data/generator/worldmap/generate.mjs](/data/generator/worldmap/generate.mjs) | JavaScript | 0 | 0 | 1 | 1 | +| [data/generator/worldmap/package-lock.json](/data/generator/worldmap/package-lock.json) | JSON | 629 | 0 | 1 | 630 | +| [data/generator/worldmap/package.json](/data/generator/worldmap/package.json) | JSON | 11 | 0 | 1 | 12 | +| [exit.py](/exit.py) | Python | 15 | 0 | 0 | 15 | +| [f05 level.py](/f05%20level.py) | Python | 15 | 4 | 4 | 23 | +| [main.py](/main.py) | Python | 79 | 3 | 3 | 85 | +| [pyrightconfig.json](/pyrightconfig.json) | JSON | 7 | 0 | 1 | 8 | +| [requirements.txt](/requirements.txt) | pip requirements | 2 | 0 | 1 | 3 | +| [src/data.py](/src/data.py) | Python | 92 | 13 | 29 | 134 | +| [src/encryption.py](/src/encryption.py) | Python | 15 | 3 | 5 | 23 | +| [src/game/F04.py](/src/game/F04.py) | Python | 28 | 0 | 2 | 30 | +| [src/game/F10.py](/src/game/F10.py) | Python | 125 | 1 | 12 | 138 | +| [src/game/F11.py](/src/game/F11.py) | Python | 88 | 1 | 9 | 98 | +| [src/game/README.md](/src/game/README.md) | Markdown | 33 | 0 | 13 | 46 | +| [src/game/arena/__init__.py](/src/game/arena/__init__.py) | Python | 7 | 0 | 4 | 11 | +| [src/game/arena/arena.py](/src/game/arena/arena.py) | Python | 23 | 0 | 6 | 29 | +| [src/game/arena/ui_logic.py](/src/game/arena/ui_logic.py) | Python | 245 | 7 | 4 | 256 | +| [src/game/battle/__init__.py](/src/game/battle/__init__.py) | Python | 15 | 0 | 4 | 19 | +| [src/game/battle/battle.py](/src/game/battle/battle.py) | Python | 55 | 0 | 12 | 67 | +| [src/game/battle/ui_logic.py](/src/game/battle/ui_logic.py) | Python | 371 | 20 | 4 | 395 | +| [src/game/database/__init__.py](/src/game/database/__init__.py) | Python | 31 | 0 | 2 | 33 | +| [src/game/database/database.py](/src/game/database/database.py) | Python | 122 | 0 | 7 | 129 | +| [src/game/generateinit.mjs](/src/game/generateinit.mjs) | JavaScript | 30 | 0 | 3 | 33 | +| [src/game/inventory/__init__.py](/src/game/inventory/__init__.py) | Python | 10 | 0 | 2 | 12 | +| [src/game/inventory/_inventory.py](/src/game/inventory/_inventory.py) | Python | 80 | 0 | 11 | 91 | +| [src/game/inventory/inventory.py](/src/game/inventory/inventory.py) | Python | 102 | 1 | 12 | 115 | +| [src/game/laboratory/__init__.py](/src/game/laboratory/__init__.py) | Python | 0 | 0 | 2 | 2 | +| [src/game/laboratory/laboratory.py](/src/game/laboratory/laboratory.py) | Python | 0 | 0 | 1 | 1 | +| [src/game/menu/__init__.py](/src/game/menu/__init__.py) | Python | 18 | 0 | 10 | 28 | +| [src/game/menu/menu.py](/src/game/menu/menu.py) | Python | 136 | 0 | 5 | 141 | +| [src/game/menu/ui_arena.py](/src/game/menu/ui_arena.py) | Python | 165 | 3 | 7 | 175 | +| [src/game/menu/ui_battle.py](/src/game/menu/ui_battle.py) | Python | 300 | 3 | 17 | 320 | +| [src/game/menu/ui_debug.py](/src/game/menu/ui_debug.py) | Python | 158 | 7 | 6 | 171 | +| [src/game/menu/ui_user.py](/src/game/menu/ui_user.py) | Python | 83 | 0 | 4 | 87 | +| [src/game/monster/__init__.py](/src/game/monster/__init__.py) | Python | 4 | 0 | 2 | 6 | +| [src/game/monster/monster.py](/src/game/monster/monster.py) | Python | 30 | 0 | 4 | 34 | +| [src/game/potion/__init__.py](/src/game/potion/__init__.py) | Python | 10 | 0 | 2 | 12 | +| [src/game/potion/potion.py](/src/game/potion/potion.py) | Python | 60 | 0 | 12 | 72 | +| [src/game/setup.py](/src/game/setup.py) | Python | 9 | 0 | 2 | 11 | +| [src/game/shop/__init__.py](/src/game/shop/__init__.py) | Python | 0 | 0 | 2 | 2 | +| [src/game/shop/shop.py](/src/game/shop/shop.py) | Python | 0 | 0 | 1 | 1 | +| [src/game/state/__init__.py](/src/game/state/__init__.py) | Python | 83 | 0 | 4 | 87 | +| [src/game/state/state.py](/src/game/state/state.py) | Python | 263 | 3 | 13 | 279 | +| [src/game/state/visual.py](/src/game/state/visual.py) | Python | 1,226 | 0 | 29 | 1,255 | +| [src/game/user/__init__.py](/src/game/user/__init__.py) | Python | 9 | 0 | 2 | 11 | +| [src/game/user/user.py](/src/game/user/user.py) | Python | 129 | 20 | 25 | 174 | +| [src/lcg.py](/src/lcg.py) | Python | 32 | 16 | 13 | 61 | +| [src/utils/codec/__init__.py](/src/utils/codec/__init__.py) | Python | 3 | 0 | 2 | 5 | +| [src/utils/codec/codec.py](/src/utils/codec/codec.py) | Python | 39 | 0 | 5 | 44 | +| [src/utils/console/__init__.py](/src/utils/console/__init__.py) | Python | 243 | 0 | 6 | 249 | +| [src/utils/console/console.py](/src/utils/console/console.py) | Python | 1,576 | 15 | 50 | 1,641 | +| [src/utils/console/driver_std.py](/src/utils/console/driver_std.py) | Python | 218 | 0 | 14 | 232 | +| [src/utils/console/views.py](/src/utils/console/views.py) | Python | 84 | 0 | 5 | 89 | +| [src/utils/coroutines/__init__.py](/src/utils/coroutines/__init__.py) | Python | 56 | 0 | 2 | 58 | +| [src/utils/coroutines/coroutines.py](/src/utils/coroutines/coroutines.py) | Python | 524 | 1 | 56 | 581 | +| [src/utils/csv/__init__.py](/src/utils/csv/__init__.py) | Python | 3 | 0 | 2 | 5 | +| [src/utils/csv/csv.py](/src/utils/csv/csv.py) | Python | 65 | 10 | 7 | 82 | +| [src/utils/generateinit.mjs](/src/utils/generateinit.mjs) | JavaScript | 30 | 0 | 3 | 33 | +| [src/utils/math/__init__.py](/src/utils/math/__init__.py) | Python | 31 | 0 | 2 | 33 | +| [src/utils/math/math.py](/src/utils/math/math.py) | Python | 110 | 18 | 16 | 144 | +| [src/utils/mixin/__init__.py](/src/utils/mixin/__init__.py) | Python | 7 | 0 | 2 | 9 | +| [src/utils/mixin/mixin.py](/src/utils/mixin/mixin.py) | Python | 69 | 0 | 7 | 76 | +| [src/utils/primordials/__init__.py](/src/utils/primordials/__init__.py) | Python | 62 | 0 | 2 | 64 | +| [src/utils/primordials/primordials.py](/src/utils/primordials/primordials.py) | Python | 387 | 14 | 67 | 468 | +| [src/utils/setup.py](/src/utils/setup.py) | Python | 9 | 0 | 2 | 11 | +| [test/csv_reader.py](/test/csv_reader.py) | Python | 7 | 0 | 2 | 9 | +| [test/testabortsignalsuspendable.py](/test/testabortsignalsuspendable.py) | Python | 30 | 0 | 5 | 35 | +| [test/testconsole_textformatter.py](/test/testconsole_textformatter.py) | Python | 68 | 0 | 10 | 78 | +| [test/testconsole_views.py](/test/testconsole_views.py) | Python | 53 | 0 | 12 | 65 | +| [test/testgetch.py](/test/testgetch.py) | Python | 117 | 7 | 5 | 129 | +| [test/testmixin.py](/test/testmixin.py) | Python | 42 | 1 | 8 | 51 | +| [test/testprimordialstrim.py](/test/testprimordialstrim.py) | Python | 16 | 0 | 3 | 19 | +| [test/testutils.py](/test/testutils.py) | Python | 38 | 0 | 7 | 45 | +| [test/userauthtest.py](/test/userauthtest.py) | Python | 35 | 6 | 4 | 45 | + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-20_05-37-25/diff-details.md b/.VSCodeCounter/2024-05-20_05-37-25/diff-details.md new file mode 100644 index 0000000..c75e9fa --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/diff-details.md @@ -0,0 +1,15 @@ +# Diff Details + +Date : 2024-05-20 05:37:25 + +Directory e:\\Projects\\Python\\if1210-2024-tubes-k08-a + +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-20_05-37-25/diff.csv b/.VSCodeCounter/2024-05-20_05-37-25/diff.csv new file mode 100644 index 0000000..b7d8d75 --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/diff.csv @@ -0,0 +1,2 @@ +"filename", "language", "", "comment", "blank", "total" +"Total", "-", , 0, 0, 0 \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-20_05-37-25/diff.md b/.VSCodeCounter/2024-05-20_05-37-25/diff.md new file mode 100644 index 0000000..0f78514 --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/diff.md @@ -0,0 +1,19 @@ +# Diff Summary + +Date : 2024-05-20 05:37:25 + +Directory e:\\Projects\\Python\\if1210-2024-tubes-k08-a + +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-20_05-37-25/diff.txt b/.VSCodeCounter/2024-05-20_05-37-25/diff.txt new file mode 100644 index 0000000..f1039e9 --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/diff.txt @@ -0,0 +1,22 @@ +Date : 2024-05-20 05:37:25 +Directory : e:\Projects\Python\if1210-2024-tubes-k08-a +Total : 0 files, 0 codes, 0 comments, 0 blanks, all 0 lines + +Languages ++----------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++----------+------------+------------+------------+------------+------------+ ++----------+------------+------------+------------+------------+------------+ + +Directories ++------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++------+------------+------------+------------+------------+------------+ ++------+------------+------------+------------+------------+------------+ + +Files ++----------+----------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++----------+----------+------------+------------+------------+------------+ +| Total | | 0 | 0 | 0 | 0 | ++----------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-20_05-37-25/results.csv b/.VSCodeCounter/2024-05-20_05-37-25/results.csv new file mode 100644 index 0000000..2c6dccf --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/results.csv @@ -0,0 +1,88 @@ +"filename", "language", "Python", "pip requirements", "Markdown", "JSON", "JavaScript", "comment", "blank", "total" +"e:\Projects\Python\if1210-2024-tubes-k08-a\F012 - Shop Management.py", "Python", 264, 0, 0, 0, 0, 1, 32, 297 +"e:\Projects\Python\if1210-2024-tubes-k08-a\F13 Monster Management.py", "Python", 47, 0, 0, 0, 0, 1, 7, 55 +"e:\Projects\Python\if1210-2024-tubes-k08-a\README.md", "Markdown", 0, 0, 10, 0, 0, 0, 6, 16 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\assets\README.md", "Markdown", 0, 0, 11, 0, 0, 0, 10, 21 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\gamesave\createnpc.mjs", "JavaScript", 0, 0, 0, 0, 59, 2, 6, 67 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\copyasciifiles.mjs", "JavaScript", 0, 0, 0, 0, 22, 0, 3, 25 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\fetchmonsters.mjs", "JavaScript", 0, 0, 0, 0, 90, 0, 7, 97 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\fetchsprites.mjs", "JavaScript", 0, 0, 0, 0, 40, 0, 3, 43 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\generatesplash.mjs", "JavaScript", 0, 0, 0, 0, 158, 0, 4, 162 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\package-lock.json", "JSON", 0, 0, 0, 708, 0, 0, 1, 709 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\package.json", "JSON", 0, 0, 0, 13, 0, 0, 1, 14 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\download.mjs", "JavaScript", 0, 0, 0, 0, 75, 0, 4, 79 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\generate.mjs", "JavaScript", 0, 0, 0, 0, 0, 0, 1, 1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\package-lock.json", "JSON", 0, 0, 0, 629, 0, 0, 1, 630 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\package.json", "JSON", 0, 0, 0, 11, 0, 0, 1, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\exit.py", "Python", 15, 0, 0, 0, 0, 0, 0, 15 +"e:\Projects\Python\if1210-2024-tubes-k08-a\f05 level.py", "Python", 15, 0, 0, 0, 0, 4, 4, 23 +"e:\Projects\Python\if1210-2024-tubes-k08-a\main.py", "Python", 79, 0, 0, 0, 0, 3, 3, 85 +"e:\Projects\Python\if1210-2024-tubes-k08-a\pyrightconfig.json", "JSON", 0, 0, 0, 7, 0, 0, 1, 8 +"e:\Projects\Python\if1210-2024-tubes-k08-a\requirements.txt", "pip requirements", 0, 2, 0, 0, 0, 0, 1, 3 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\data.py", "Python", 92, 0, 0, 0, 0, 13, 29, 134 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\encryption.py", "Python", 15, 0, 0, 0, 0, 3, 5, 23 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F04.py", "Python", 28, 0, 0, 0, 0, 0, 2, 30 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F10.py", "Python", 125, 0, 0, 0, 0, 1, 12, 138 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F11.py", "Python", 88, 0, 0, 0, 0, 1, 9, 98 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\README.md", "Markdown", 0, 0, 33, 0, 0, 0, 13, 46 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\__init__.py", "Python", 7, 0, 0, 0, 0, 0, 4, 11 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\arena.py", "Python", 23, 0, 0, 0, 0, 0, 6, 29 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\ui_logic.py", "Python", 245, 0, 0, 0, 0, 7, 4, 256 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\__init__.py", "Python", 15, 0, 0, 0, 0, 0, 4, 19 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\battle.py", "Python", 55, 0, 0, 0, 0, 0, 12, 67 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\ui_logic.py", "Python", 371, 0, 0, 0, 0, 20, 4, 395 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\database\__init__.py", "Python", 31, 0, 0, 0, 0, 0, 2, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\database\database.py", "Python", 122, 0, 0, 0, 0, 0, 7, 129 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\generateinit.mjs", "JavaScript", 0, 0, 0, 0, 30, 0, 3, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\__init__.py", "Python", 10, 0, 0, 0, 0, 0, 2, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\_inventory.py", "Python", 80, 0, 0, 0, 0, 0, 11, 91 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\inventory.py", "Python", 102, 0, 0, 0, 0, 1, 12, 115 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\laboratory\__init__.py", "Python", 0, 0, 0, 0, 0, 0, 2, 2 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\laboratory\laboratory.py", "Python", 0, 0, 0, 0, 0, 0, 1, 1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\__init__.py", "Python", 18, 0, 0, 0, 0, 0, 10, 28 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\menu.py", "Python", 136, 0, 0, 0, 0, 0, 5, 141 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_arena.py", "Python", 165, 0, 0, 0, 0, 3, 7, 175 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_battle.py", "Python", 300, 0, 0, 0, 0, 3, 17, 320 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_debug.py", "Python", 158, 0, 0, 0, 0, 7, 6, 171 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_user.py", "Python", 83, 0, 0, 0, 0, 0, 4, 87 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\__init__.py", "Python", 4, 0, 0, 0, 0, 0, 2, 6 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\monster.py", "Python", 30, 0, 0, 0, 0, 0, 4, 34 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\potion\__init__.py", "Python", 10, 0, 0, 0, 0, 0, 2, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\potion\potion.py", "Python", 60, 0, 0, 0, 0, 0, 12, 72 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\setup.py", "Python", 9, 0, 0, 0, 0, 0, 2, 11 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\__init__.py", "Python", 0, 0, 0, 0, 0, 0, 2, 2 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\shop.py", "Python", 0, 0, 0, 0, 0, 0, 1, 1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\__init__.py", "Python", 83, 0, 0, 0, 0, 0, 4, 87 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\state.py", "Python", 263, 0, 0, 0, 0, 3, 13, 279 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\visual.py", "Python", 1226, 0, 0, 0, 0, 0, 29, 1255 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\__init__.py", "Python", 9, 0, 0, 0, 0, 0, 2, 11 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\user.py", "Python", 129, 0, 0, 0, 0, 20, 25, 174 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\lcg.py", "Python", 32, 0, 0, 0, 0, 16, 13, 61 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\codec\__init__.py", "Python", 3, 0, 0, 0, 0, 0, 2, 5 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\codec\codec.py", "Python", 39, 0, 0, 0, 0, 0, 5, 44 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\__init__.py", "Python", 243, 0, 0, 0, 0, 0, 6, 249 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\console.py", "Python", 1576, 0, 0, 0, 0, 15, 50, 1641 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\driver_std.py", "Python", 218, 0, 0, 0, 0, 0, 14, 232 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\views.py", "Python", 84, 0, 0, 0, 0, 0, 5, 89 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\__init__.py", "Python", 56, 0, 0, 0, 0, 0, 2, 58 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\coroutines.py", "Python", 524, 0, 0, 0, 0, 1, 56, 581 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\csv\__init__.py", "Python", 3, 0, 0, 0, 0, 0, 2, 5 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\csv\csv.py", "Python", 65, 0, 0, 0, 0, 10, 7, 82 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\generateinit.mjs", "JavaScript", 0, 0, 0, 0, 30, 0, 3, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\math\__init__.py", "Python", 31, 0, 0, 0, 0, 0, 2, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\math\math.py", "Python", 110, 0, 0, 0, 0, 18, 16, 144 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\mixin\__init__.py", "Python", 7, 0, 0, 0, 0, 0, 2, 9 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\mixin\mixin.py", "Python", 69, 0, 0, 0, 0, 0, 7, 76 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\primordials\__init__.py", "Python", 62, 0, 0, 0, 0, 0, 2, 64 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\primordials\primordials.py", "Python", 387, 0, 0, 0, 0, 14, 67, 468 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\setup.py", "Python", 9, 0, 0, 0, 0, 0, 2, 11 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\csv_reader.py", "Python", 7, 0, 0, 0, 0, 0, 2, 9 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testabortsignalsuspendable.py", "Python", 30, 0, 0, 0, 0, 0, 5, 35 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testconsole_textformatter.py", "Python", 68, 0, 0, 0, 0, 0, 10, 78 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testconsole_views.py", "Python", 53, 0, 0, 0, 0, 0, 12, 65 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testgetch.py", "Python", 117, 0, 0, 0, 0, 7, 5, 129 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testmixin.py", "Python", 42, 0, 0, 0, 0, 1, 8, 51 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testprimordialstrim.py", "Python", 16, 0, 0, 0, 0, 0, 3, 19 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testutils.py", "Python", 38, 0, 0, 0, 0, 0, 7, 45 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\userauthtest.py", "Python", 35, 0, 0, 0, 0, 6, 4, 45 +"Total", "-", 8436, 2, 54, 1368, 504, 181, 706, 11251 \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-20_05-37-25/results.json b/.VSCodeCounter/2024-05-20_05-37-25/results.json new file mode 100644 index 0000000..3d46149 --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/results.json @@ -0,0 +1 @@ +{"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/userauthtest.py":{"language":"Python","code":35,"comment":6,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testutils.py":{"language":"Python","code":38,"comment":0,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testprimordialstrim.py":{"language":"Python","code":16,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testgetch.py":{"language":"Python","code":117,"comment":7,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testconsole_views.py":{"language":"Python","code":53,"comment":0,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testmixin.py":{"language":"Python","code":42,"comment":1,"blank":8},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testconsole_textformatter.py":{"language":"Python","code":68,"comment":0,"blank":10},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testabortsignalsuspendable.py":{"language":"Python","code":30,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/csv_reader.py":{"language":"Python","code":7,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/requirements.txt":{"language":"pip requirements","code":2,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/README.md":{"language":"Markdown","code":10,"comment":0,"blank":6},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/pyrightconfig.json":{"language":"JSON","code":7,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/main.py":{"language":"Python","code":79,"comment":3,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/f05%20level.py":{"language":"Python","code":15,"comment":4,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/F13%20Monster%20Management.py":{"language":"Python","code":47,"comment":1,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/F012%20-%20Shop%20Management.py":{"language":"Python","code":264,"comment":1,"blank":32},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/exit.py":{"language":"Python","code":15,"comment":0,"blank":0},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/worldmap/package-lock.json":{"language":"JSON","code":629,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/worldmap/package.json":{"language":"JSON","code":11,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/worldmap/download.mjs":{"language":"JavaScript","code":75,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/package.json":{"language":"JSON","code":13,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/generatesplash.mjs":{"language":"JavaScript","code":158,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/fetchsprites.mjs":{"language":"JavaScript","code":40,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/package-lock.json":{"language":"JSON","code":708,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/fetchmonsters.mjs":{"language":"JavaScript","code":90,"comment":0,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/copyasciifiles.mjs":{"language":"JavaScript","code":22,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/worldmap/generate.mjs":{"language":"JavaScript","code":0,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/gamesave/createnpc.mjs":{"language":"JavaScript","code":59,"comment":2,"blank":6},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/setup.py":{"language":"Python","code":9,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/assets/README.md":{"language":"Markdown","code":11,"comment":0,"blank":10},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/primordials/__init__.py":{"language":"Python","code":62,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/primordials/primordials.py":{"language":"Python","code":387,"comment":14,"blank":67},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/mixin/__init__.py":{"language":"Python","code":7,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/math/__init__.py":{"language":"Python","code":31,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/mixin/mixin.py":{"language":"Python","code":69,"comment":0,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/math/math.py":{"language":"Python","code":110,"comment":18,"blank":16},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/generateinit.mjs":{"language":"JavaScript","code":30,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/csv/csv.py":{"language":"Python","code":65,"comment":10,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/csv/__init__.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/coroutines/__init__.py":{"language":"Python","code":56,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/coroutines/coroutines.py":{"language":"Python","code":524,"comment":1,"blank":56},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/console/views.py":{"language":"Python","code":84,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/console/__init__.py":{"language":"Python","code":243,"comment":0,"blank":6},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/console/driver_std.py":{"language":"Python","code":218,"comment":0,"blank":14},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/codec/__init__.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/codec/codec.py":{"language":"Python","code":39,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/lcg.py":{"language":"Python","code":32,"comment":16,"blank":13},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/console/console.py":{"language":"Python","code":1576,"comment":15,"blank":50},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/user/__init__.py":{"language":"Python","code":9,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/user/user.py":{"language":"Python","code":129,"comment":20,"blank":25},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/state/__init__.py":{"language":"Python","code":83,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/setup.py":{"language":"Python","code":9,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/shop/__init__.py":{"language":"Python","code":0,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/state/visual.py":{"language":"Python","code":1226,"comment":0,"blank":29},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/state/state.py":{"language":"Python","code":263,"comment":3,"blank":13},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/README.md":{"language":"Markdown","code":33,"comment":0,"blank":13},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/potion/__init__.py":{"language":"Python","code":10,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/potion/potion.py":{"language":"Python","code":60,"comment":0,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/monster/monster.py":{"language":"Python","code":30,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/monster/__init__.py":{"language":"Python","code":4,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_user.py":{"language":"Python","code":83,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/shop/shop.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/__init__.py":{"language":"Python","code":18,"comment":0,"blank":10},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_debug.py":{"language":"Python","code":158,"comment":7,"blank":6},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_battle.py":{"language":"Python","code":300,"comment":3,"blank":17},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/laboratory/__init__.py":{"language":"Python","code":0,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/menu.py":{"language":"Python","code":136,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_arena.py":{"language":"Python","code":165,"comment":3,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/inventory/_inventory.py":{"language":"Python","code":80,"comment":0,"blank":11},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/inventory/inventory.py":{"language":"Python","code":102,"comment":1,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/generateinit.mjs":{"language":"JavaScript","code":30,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/inventory/__init__.py":{"language":"Python","code":10,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/laboratory/laboratory.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/F11.py":{"language":"Python","code":88,"comment":1,"blank":9},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/F04.py":{"language":"Python","code":28,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/F10.py":{"language":"Python","code":125,"comment":1,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/database/__init__.py":{"language":"Python","code":31,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/battle/battle.py":{"language":"Python","code":55,"comment":0,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/database/database.py":{"language":"Python","code":122,"comment":0,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/battle/ui_logic.py":{"language":"Python","code":371,"comment":20,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/battle/__init__.py":{"language":"Python","code":15,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/arena/__init__.py":{"language":"Python","code":7,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/arena/ui_logic.py":{"language":"Python","code":245,"comment":7,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/arena/arena.py":{"language":"Python","code":23,"comment":0,"blank":6},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/encryption.py":{"language":"Python","code":15,"comment":3,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/data.py":{"language":"Python","code":92,"comment":13,"blank":29}} \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-20_05-37-25/results.md b/.VSCodeCounter/2024-05-20_05-37-25/results.md new file mode 100644 index 0000000..9207025 --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/results.md @@ -0,0 +1,57 @@ +# Summary + +Date : 2024-05-20 05:37:25 + +Directory e:\\Projects\\Python\\if1210-2024-tubes-k08-a + +Total : 86 files, 10364 codes, 181 comments, 706 blanks, all 11251 lines + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| Python | 68 | 8,436 | 179 | 637 | 9,252 | +| JSON | 5 | 1,368 | 0 | 5 | 1,373 | +| JavaScript | 9 | 504 | 2 | 34 | 540 | +| Markdown | 3 | 54 | 0 | 29 | 83 | +| pip requirements | 1 | 2 | 0 | 1 | 3 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 86 | 10,364 | 181 | 706 | 11,251 | +| . (Files) | 8 | 439 | 9 | 54 | 502 | +| data | 12 | 1,816 | 2 | 42 | 1,860 | +| data\\assets | 1 | 11 | 0 | 10 | 21 | +| data\\generator | 11 | 1,805 | 2 | 32 | 1,839 | +| data\\generator\\gamesave | 1 | 59 | 2 | 6 | 67 | +| data\\generator\\monsters | 6 | 1,031 | 0 | 19 | 1,050 | +| data\\generator\\worldmap | 4 | 715 | 0 | 7 | 722 | +| src | 57 | 7,703 | 156 | 554 | 8,413 | +| src (Files) | 3 | 139 | 32 | 47 | 218 | +| src\\game | 36 | 4,048 | 66 | 257 | 4,371 | +| src\\game (Files) | 6 | 313 | 2 | 41 | 356 | +| src\\game\\arena | 3 | 275 | 7 | 14 | 296 | +| src\\game\\battle | 3 | 441 | 20 | 20 | 481 | +| src\\game\\database | 2 | 153 | 0 | 9 | 162 | +| src\\game\\inventory | 3 | 192 | 1 | 25 | 218 | +| src\\game\\laboratory | 2 | 0 | 0 | 3 | 3 | +| src\\game\\menu | 6 | 860 | 13 | 49 | 922 | +| src\\game\\monster | 2 | 34 | 0 | 6 | 40 | +| src\\game\\potion | 2 | 70 | 0 | 14 | 84 | +| src\\game\\shop | 2 | 0 | 0 | 3 | 3 | +| src\\game\\state | 3 | 1,572 | 3 | 46 | 1,621 | +| src\\game\\user | 2 | 138 | 20 | 27 | 185 | +| src\\utils | 18 | 3,516 | 58 | 250 | 3,824 | +| src\\utils (Files) | 2 | 39 | 0 | 5 | 44 | +| src\\utils\\codec | 2 | 42 | 0 | 7 | 49 | +| src\\utils\\console | 4 | 2,121 | 15 | 75 | 2,211 | +| src\\utils\\coroutines | 2 | 580 | 1 | 58 | 639 | +| src\\utils\\csv | 2 | 68 | 10 | 9 | 87 | +| src\\utils\\math | 2 | 141 | 18 | 18 | 177 | +| src\\utils\\mixin | 2 | 76 | 0 | 9 | 85 | +| src\\utils\\primordials | 2 | 449 | 14 | 69 | 532 | +| test | 9 | 406 | 14 | 56 | 476 | + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-20_05-37-25/results.txt b/.VSCodeCounter/2024-05-20_05-37-25/results.txt new file mode 100644 index 0000000..bd8081f --- /dev/null +++ b/.VSCodeCounter/2024-05-20_05-37-25/results.txt @@ -0,0 +1,146 @@ +Date : 2024-05-20 05:37:25 +Directory : e:\Projects\Python\if1210-2024-tubes-k08-a +Total : 86 files, 10364 codes, 181 comments, 706 blanks, all 11251 lines + +Languages ++------------------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++------------------+------------+------------+------------+------------+------------+ +| Python | 68 | 8,436 | 179 | 637 | 9,252 | +| JSON | 5 | 1,368 | 0 | 5 | 1,373 | +| JavaScript | 9 | 504 | 2 | 34 | 540 | +| Markdown | 3 | 54 | 0 | 29 | 83 | +| pip requirements | 1 | 2 | 0 | 1 | 3 | ++------------------+------------+------------+------------+------------+------------+ + +Directories ++---------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++---------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 86 | 10,364 | 181 | 706 | 11,251 | +| . (Files) | 8 | 439 | 9 | 54 | 502 | +| data | 12 | 1,816 | 2 | 42 | 1,860 | +| data\assets | 1 | 11 | 0 | 10 | 21 | +| data\generator | 11 | 1,805 | 2 | 32 | 1,839 | +| data\generator\gamesave | 1 | 59 | 2 | 6 | 67 | +| data\generator\monsters | 6 | 1,031 | 0 | 19 | 1,050 | +| data\generator\worldmap | 4 | 715 | 0 | 7 | 722 | +| src | 57 | 7,703 | 156 | 554 | 8,413 | +| src (Files) | 3 | 139 | 32 | 47 | 218 | +| src\game | 36 | 4,048 | 66 | 257 | 4,371 | +| src\game (Files) | 6 | 313 | 2 | 41 | 356 | +| src\game\arena | 3 | 275 | 7 | 14 | 296 | +| src\game\battle | 3 | 441 | 20 | 20 | 481 | +| src\game\database | 2 | 153 | 0 | 9 | 162 | +| src\game\inventory | 3 | 192 | 1 | 25 | 218 | +| src\game\laboratory | 2 | 0 | 0 | 3 | 3 | +| src\game\menu | 6 | 860 | 13 | 49 | 922 | +| src\game\monster | 2 | 34 | 0 | 6 | 40 | +| src\game\potion | 2 | 70 | 0 | 14 | 84 | +| src\game\shop | 2 | 0 | 0 | 3 | 3 | +| src\game\state | 3 | 1,572 | 3 | 46 | 1,621 | +| src\game\user | 2 | 138 | 20 | 27 | 185 | +| src\utils | 18 | 3,516 | 58 | 250 | 3,824 | +| src\utils (Files) | 2 | 39 | 0 | 5 | 44 | +| src\utils\codec | 2 | 42 | 0 | 7 | 49 | +| src\utils\console | 4 | 2,121 | 15 | 75 | 2,211 | +| src\utils\coroutines | 2 | 580 | 1 | 58 | 639 | +| src\utils\csv | 2 | 68 | 10 | 9 | 87 | +| src\utils\math | 2 | 141 | 18 | 18 | 177 | +| src\utils\mixin | 2 | 76 | 0 | 9 | 85 | +| src\utils\primordials | 2 | 449 | 14 | 69 | 532 | +| test | 9 | 406 | 14 | 56 | 476 | ++---------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++---------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++---------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ +| e:\Projects\Python\if1210-2024-tubes-k08-a\F012 - Shop Management.py | Python | 264 | 1 | 32 | 297 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\F13 Monster Management.py | Python | 47 | 1 | 7 | 55 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\README.md | Markdown | 10 | 0 | 6 | 16 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\assets\README.md | Markdown | 11 | 0 | 10 | 21 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\gamesave\createnpc.mjs | JavaScript | 59 | 2 | 6 | 67 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\copyasciifiles.mjs | JavaScript | 22 | 0 | 3 | 25 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\fetchmonsters.mjs | JavaScript | 90 | 0 | 7 | 97 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\fetchsprites.mjs | JavaScript | 40 | 0 | 3 | 43 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\generatesplash.mjs | JavaScript | 158 | 0 | 4 | 162 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\package-lock.json | JSON | 708 | 0 | 1 | 709 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\package.json | JSON | 13 | 0 | 1 | 14 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\download.mjs | JavaScript | 75 | 0 | 4 | 79 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\generate.mjs | JavaScript | 0 | 0 | 1 | 1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\package-lock.json | JSON | 629 | 0 | 1 | 630 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\package.json | JSON | 11 | 0 | 1 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\exit.py | Python | 15 | 0 | 0 | 15 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\f05 level.py | Python | 15 | 4 | 4 | 23 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\main.py | Python | 79 | 3 | 3 | 85 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\pyrightconfig.json | JSON | 7 | 0 | 1 | 8 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\requirements.txt | pip requirements | 2 | 0 | 1 | 3 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\data.py | Python | 92 | 13 | 29 | 134 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\encryption.py | Python | 15 | 3 | 5 | 23 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F04.py | Python | 28 | 0 | 2 | 30 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F10.py | Python | 125 | 1 | 12 | 138 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F11.py | Python | 88 | 1 | 9 | 98 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\README.md | Markdown | 33 | 0 | 13 | 46 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\__init__.py | Python | 7 | 0 | 4 | 11 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\arena.py | Python | 23 | 0 | 6 | 29 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\ui_logic.py | Python | 245 | 7 | 4 | 256 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\__init__.py | Python | 15 | 0 | 4 | 19 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\battle.py | Python | 55 | 0 | 12 | 67 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\ui_logic.py | Python | 371 | 20 | 4 | 395 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\database\__init__.py | Python | 31 | 0 | 2 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\database\database.py | Python | 122 | 0 | 7 | 129 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\generateinit.mjs | JavaScript | 30 | 0 | 3 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\__init__.py | Python | 10 | 0 | 2 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\_inventory.py | Python | 80 | 0 | 11 | 91 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\inventory.py | Python | 102 | 1 | 12 | 115 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\laboratory\__init__.py | Python | 0 | 0 | 2 | 2 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\laboratory\laboratory.py | Python | 0 | 0 | 1 | 1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\__init__.py | Python | 18 | 0 | 10 | 28 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\menu.py | Python | 136 | 0 | 5 | 141 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_arena.py | Python | 165 | 3 | 7 | 175 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_battle.py | Python | 300 | 3 | 17 | 320 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_debug.py | Python | 158 | 7 | 6 | 171 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_user.py | Python | 83 | 0 | 4 | 87 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\__init__.py | Python | 4 | 0 | 2 | 6 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\monster.py | Python | 30 | 0 | 4 | 34 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\potion\__init__.py | Python | 10 | 0 | 2 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\potion\potion.py | Python | 60 | 0 | 12 | 72 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\setup.py | Python | 9 | 0 | 2 | 11 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\__init__.py | Python | 0 | 0 | 2 | 2 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\shop.py | Python | 0 | 0 | 1 | 1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\__init__.py | Python | 83 | 0 | 4 | 87 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\state.py | Python | 263 | 3 | 13 | 279 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\visual.py | Python | 1,226 | 0 | 29 | 1,255 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\__init__.py | Python | 9 | 0 | 2 | 11 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\user.py | Python | 129 | 20 | 25 | 174 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\lcg.py | Python | 32 | 16 | 13 | 61 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\codec\__init__.py | Python | 3 | 0 | 2 | 5 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\codec\codec.py | Python | 39 | 0 | 5 | 44 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\__init__.py | Python | 243 | 0 | 6 | 249 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\console.py | Python | 1,576 | 15 | 50 | 1,641 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\driver_std.py | Python | 218 | 0 | 14 | 232 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\views.py | Python | 84 | 0 | 5 | 89 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\__init__.py | Python | 56 | 0 | 2 | 58 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\coroutines.py | Python | 524 | 1 | 56 | 581 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\csv\__init__.py | Python | 3 | 0 | 2 | 5 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\csv\csv.py | Python | 65 | 10 | 7 | 82 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\generateinit.mjs | JavaScript | 30 | 0 | 3 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\math\__init__.py | Python | 31 | 0 | 2 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\math\math.py | Python | 110 | 18 | 16 | 144 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\mixin\__init__.py | Python | 7 | 0 | 2 | 9 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\mixin\mixin.py | Python | 69 | 0 | 7 | 76 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\primordials\__init__.py | Python | 62 | 0 | 2 | 64 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\primordials\primordials.py | Python | 387 | 14 | 67 | 468 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\setup.py | Python | 9 | 0 | 2 | 11 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\csv_reader.py | Python | 7 | 0 | 2 | 9 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testabortsignalsuspendable.py | Python | 30 | 0 | 5 | 35 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testconsole_textformatter.py | Python | 68 | 0 | 10 | 78 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testconsole_views.py | Python | 53 | 0 | 12 | 65 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testgetch.py | Python | 117 | 7 | 5 | 129 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testmixin.py | Python | 42 | 1 | 8 | 51 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testprimordialstrim.py | Python | 16 | 0 | 3 | 19 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testutils.py | Python | 38 | 0 | 7 | 45 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\userauthtest.py | Python | 35 | 6 | 4 | 45 | +| Total | | 10,364 | 181 | 706 | 11,251 | ++---------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/details.md b/.VSCodeCounter/2024-05-22_07-57-47/details.md new file mode 100644 index 0000000..c2c38e6 --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/details.md @@ -0,0 +1,102 @@ +# Details + +Date : 2024-05-22 07:57:47 + +Directory e:\\Projects\\Python\\if1210-2024-tubes-k08-a + +Total : 87 files, 11034 codes, 181 comments, 743 blanks, all 11958 lines + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [F012 - Shop Management.py](/F012%20-%20Shop%20Management.py) | Python | 264 | 1 | 32 | 297 | +| [F13 Monster Management.py](/F13%20Monster%20Management.py) | Python | 47 | 1 | 7 | 55 | +| [README.md](/README.md) | Markdown | 36 | 0 | 6 | 42 | +| [data/assets/README.md](/data/assets/README.md) | Markdown | 11 | 0 | 10 | 21 | +| [data/generator/gamesave/createnpc.mjs](/data/generator/gamesave/createnpc.mjs) | JavaScript | 59 | 2 | 6 | 67 | +| [data/generator/monsters/copyasciifiles.mjs](/data/generator/monsters/copyasciifiles.mjs) | JavaScript | 22 | 0 | 3 | 25 | +| [data/generator/monsters/fetchmonsters.mjs](/data/generator/monsters/fetchmonsters.mjs) | JavaScript | 90 | 0 | 7 | 97 | +| [data/generator/monsters/fetchsprites.mjs](/data/generator/monsters/fetchsprites.mjs) | JavaScript | 40 | 0 | 3 | 43 | +| [data/generator/monsters/generatesplash.mjs](/data/generator/monsters/generatesplash.mjs) | JavaScript | 158 | 0 | 4 | 162 | +| [data/generator/monsters/package-lock.json](/data/generator/monsters/package-lock.json) | JSON | 708 | 0 | 1 | 709 | +| [data/generator/monsters/package.json](/data/generator/monsters/package.json) | JSON | 13 | 0 | 1 | 14 | +| [data/generator/worldmap/download.mjs](/data/generator/worldmap/download.mjs) | JavaScript | 75 | 0 | 4 | 79 | +| [data/generator/worldmap/generate.mjs](/data/generator/worldmap/generate.mjs) | JavaScript | 0 | 0 | 1 | 1 | +| [data/generator/worldmap/package-lock.json](/data/generator/worldmap/package-lock.json) | JSON | 629 | 0 | 1 | 630 | +| [data/generator/worldmap/package.json](/data/generator/worldmap/package.json) | JSON | 11 | 0 | 1 | 12 | +| [exit.py](/exit.py) | Python | 15 | 0 | 0 | 15 | +| [f05 level.py](/f05%20level.py) | Python | 15 | 4 | 4 | 23 | +| [main.py](/main.py) | Python | 79 | 3 | 3 | 85 | +| [pyrightconfig.json](/pyrightconfig.json) | JSON | 7 | 0 | 1 | 8 | +| [requirements.txt](/requirements.txt) | pip requirements | 2 | 0 | 1 | 3 | +| [src/data.py](/src/data.py) | Python | 92 | 13 | 29 | 134 | +| [src/encryption.py](/src/encryption.py) | Python | 15 | 3 | 5 | 23 | +| [src/game/F04.py](/src/game/F04.py) | Python | 28 | 0 | 1 | 29 | +| [src/game/F10.py](/src/game/F10.py) | Python | 125 | 1 | 12 | 138 | +| [src/game/F11.py](/src/game/F11.py) | Python | 88 | 1 | 9 | 98 | +| [src/game/README.md](/src/game/README.md) | Markdown | 33 | 0 | 13 | 46 | +| [src/game/arena/__init__.py](/src/game/arena/__init__.py) | Python | 7 | 0 | 4 | 11 | +| [src/game/arena/arena.py](/src/game/arena/arena.py) | Python | 23 | 0 | 6 | 29 | +| [src/game/arena/ui_logic.py](/src/game/arena/ui_logic.py) | Python | 245 | 7 | 4 | 256 | +| [src/game/battle/__init__.py](/src/game/battle/__init__.py) | Python | 15 | 0 | 4 | 19 | +| [src/game/battle/battle.py](/src/game/battle/battle.py) | Python | 55 | 0 | 12 | 67 | +| [src/game/battle/ui_logic.py](/src/game/battle/ui_logic.py) | Python | 373 | 20 | 4 | 397 | +| [src/game/database/__init__.py](/src/game/database/__init__.py) | Python | 31 | 0 | 2 | 33 | +| [src/game/database/database.py](/src/game/database/database.py) | Python | 122 | 0 | 7 | 129 | +| [src/game/generateinit.mjs](/src/game/generateinit.mjs) | JavaScript | 30 | 0 | 3 | 33 | +| [src/game/inventory/__init__.py](/src/game/inventory/__init__.py) | Python | 10 | 0 | 2 | 12 | +| [src/game/inventory/_inventory.py](/src/game/inventory/_inventory.py) | Python | 80 | 0 | 11 | 91 | +| [src/game/inventory/inventory.py](/src/game/inventory/inventory.py) | Python | 102 | 1 | 12 | 115 | +| [src/game/laboratory/__init__.py](/src/game/laboratory/__init__.py) | Python | 0 | 0 | 2 | 2 | +| [src/game/laboratory/laboratory.py](/src/game/laboratory/laboratory.py) | Python | 0 | 0 | 1 | 1 | +| [src/game/menu/__init__.py](/src/game/menu/__init__.py) | Python | 21 | 0 | 12 | 33 | +| [src/game/menu/menu.py](/src/game/menu/menu.py) | Python | 148 | 0 | 5 | 153 | +| [src/game/menu/ui_arena.py](/src/game/menu/ui_arena.py) | Python | 165 | 3 | 7 | 175 | +| [src/game/menu/ui_battle.py](/src/game/menu/ui_battle.py) | Python | 300 | 3 | 17 | 320 | +| [src/game/menu/ui_debug.py](/src/game/menu/ui_debug.py) | Python | 169 | 7 | 7 | 183 | +| [src/game/menu/ui_shop.py](/src/game/menu/ui_shop.py) | Python | 203 | 0 | 11 | 214 | +| [src/game/menu/ui_user.py](/src/game/menu/ui_user.py) | Python | 89 | 0 | 4 | 93 | +| [src/game/monster/__init__.py](/src/game/monster/__init__.py) | Python | 5 | 0 | 2 | 7 | +| [src/game/monster/monster.py](/src/game/monster/monster.py) | Python | 34 | 0 | 5 | 39 | +| [src/game/potion/__init__.py](/src/game/potion/__init__.py) | Python | 10 | 0 | 2 | 12 | +| [src/game/potion/potion.py](/src/game/potion/potion.py) | Python | 60 | 0 | 12 | 72 | +| [src/game/setup.py](/src/game/setup.py) | Python | 9 | 0 | 2 | 11 | +| [src/game/shop/__init__.py](/src/game/shop/__init__.py) | Python | 5 | 0 | 2 | 7 | +| [src/game/shop/shop.py](/src/game/shop/shop.py) | Python | 27 | 0 | 5 | 32 | +| [src/game/state/__init__.py](/src/game/state/__init__.py) | Python | 84 | 0 | 4 | 88 | +| [src/game/state/state.py](/src/game/state/state.py) | Python | 265 | 3 | 13 | 281 | +| [src/game/state/visual.py](/src/game/state/visual.py) | Python | 1,542 | 0 | 45 | 1,587 | +| [src/game/user/__init__.py](/src/game/user/__init__.py) | Python | 10 | 0 | 2 | 12 | +| [src/game/user/user.py](/src/game/user/user.py) | Python | 155 | 20 | 28 | 203 | +| [src/lcg.py](/src/lcg.py) | Python | 32 | 16 | 13 | 61 | +| [src/utils/codec/__init__.py](/src/utils/codec/__init__.py) | Python | 3 | 0 | 2 | 5 | +| [src/utils/codec/codec.py](/src/utils/codec/codec.py) | Python | 39 | 0 | 5 | 44 | +| [src/utils/console/__init__.py](/src/utils/console/__init__.py) | Python | 243 | 0 | 6 | 249 | +| [src/utils/console/console.py](/src/utils/console/console.py) | Python | 1,601 | 15 | 50 | 1,666 | +| [src/utils/console/driver_std.py](/src/utils/console/driver_std.py) | Python | 218 | 0 | 14 | 232 | +| [src/utils/console/views.py](/src/utils/console/views.py) | Python | 84 | 0 | 5 | 89 | +| [src/utils/coroutines/__init__.py](/src/utils/coroutines/__init__.py) | Python | 55 | 0 | 2 | 57 | +| [src/utils/coroutines/coroutines.py](/src/utils/coroutines/coroutines.py) | Python | 524 | 1 | 56 | 581 | +| [src/utils/csv/__init__.py](/src/utils/csv/__init__.py) | Python | 3 | 0 | 2 | 5 | +| [src/utils/csv/csv.py](/src/utils/csv/csv.py) | Python | 65 | 10 | 7 | 82 | +| [src/utils/generateinit.mjs](/src/utils/generateinit.mjs) | JavaScript | 30 | 0 | 3 | 33 | +| [src/utils/math/__init__.py](/src/utils/math/__init__.py) | Python | 31 | 0 | 2 | 33 | +| [src/utils/math/math.py](/src/utils/math/math.py) | Python | 110 | 18 | 16 | 144 | +| [src/utils/mixin/__init__.py](/src/utils/mixin/__init__.py) | Python | 7 | 0 | 2 | 9 | +| [src/utils/mixin/mixin.py](/src/utils/mixin/mixin.py) | Python | 69 | 0 | 7 | 76 | +| [src/utils/primordials/__init__.py](/src/utils/primordials/__init__.py) | Python | 62 | 0 | 2 | 64 | +| [src/utils/primordials/primordials.py](/src/utils/primordials/primordials.py) | Python | 387 | 14 | 67 | 468 | +| [src/utils/setup.py](/src/utils/setup.py) | Python | 9 | 0 | 2 | 11 | +| [test/csv_reader.py](/test/csv_reader.py) | Python | 7 | 0 | 2 | 9 | +| [test/testabortsignalsuspendable.py](/test/testabortsignalsuspendable.py) | Python | 30 | 0 | 5 | 35 | +| [test/testconsole_textformatter.py](/test/testconsole_textformatter.py) | Python | 68 | 0 | 10 | 78 | +| [test/testconsole_views.py](/test/testconsole_views.py) | Python | 53 | 0 | 12 | 65 | +| [test/testgetch.py](/test/testgetch.py) | Python | 117 | 7 | 5 | 129 | +| [test/testmixin.py](/test/testmixin.py) | Python | 42 | 1 | 8 | 51 | +| [test/testprimordialstrim.py](/test/testprimordialstrim.py) | Python | 16 | 0 | 3 | 19 | +| [test/testutils.py](/test/testutils.py) | Python | 38 | 0 | 7 | 45 | +| [test/userauthtest.py](/test/userauthtest.py) | Python | 35 | 6 | 4 | 45 | + +[Summary](results.md) / Details / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/diff-details.md b/.VSCodeCounter/2024-05-22_07-57-47/diff-details.md new file mode 100644 index 0000000..e87801c --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/diff-details.md @@ -0,0 +1,34 @@ +# Diff Details + +Date : 2024-05-22 07:57:47 + +Directory e:\\Projects\\Python\\if1210-2024-tubes-k08-a + +Total : 19 files, 670 codes, 0 comments, 37 blanks, all 707 lines + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details + +## Files +| filename | language | code | comment | blank | total | +| :--- | :--- | ---: | ---: | ---: | ---: | +| [README.md](/README.md) | Markdown | 26 | 0 | 0 | 26 | +| [src/game/F04.py](/src/game/F04.py) | Python | 0 | 0 | -1 | -1 | +| [src/game/battle/ui_logic.py](/src/game/battle/ui_logic.py) | Python | 2 | 0 | 0 | 2 | +| [src/game/menu/__init__.py](/src/game/menu/__init__.py) | Python | 3 | 0 | 2 | 5 | +| [src/game/menu/menu.py](/src/game/menu/menu.py) | Python | 12 | 0 | 0 | 12 | +| [src/game/menu/ui_debug.py](/src/game/menu/ui_debug.py) | Python | 11 | 0 | 1 | 12 | +| [src/game/menu/ui_shop.py](/src/game/menu/ui_shop.py) | Python | 203 | 0 | 11 | 214 | +| [src/game/menu/ui_user.py](/src/game/menu/ui_user.py) | Python | 6 | 0 | 0 | 6 | +| [src/game/monster/__init__.py](/src/game/monster/__init__.py) | Python | 1 | 0 | 0 | 1 | +| [src/game/monster/monster.py](/src/game/monster/monster.py) | Python | 4 | 0 | 1 | 5 | +| [src/game/shop/__init__.py](/src/game/shop/__init__.py) | Python | 5 | 0 | 0 | 5 | +| [src/game/shop/shop.py](/src/game/shop/shop.py) | Python | 27 | 0 | 4 | 31 | +| [src/game/state/__init__.py](/src/game/state/__init__.py) | Python | 1 | 0 | 0 | 1 | +| [src/game/state/state.py](/src/game/state/state.py) | Python | 2 | 0 | 0 | 2 | +| [src/game/state/visual.py](/src/game/state/visual.py) | Python | 316 | 0 | 16 | 332 | +| [src/game/user/__init__.py](/src/game/user/__init__.py) | Python | 1 | 0 | 0 | 1 | +| [src/game/user/user.py](/src/game/user/user.py) | Python | 26 | 0 | 3 | 29 | +| [src/utils/console/console.py](/src/utils/console/console.py) | Python | 25 | 0 | 0 | 25 | +| [src/utils/coroutines/__init__.py](/src/utils/coroutines/__init__.py) | Python | -1 | 0 | 0 | -1 | + +[Summary](results.md) / [Details](details.md) / [Diff Summary](diff.md) / Diff Details \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/diff.csv b/.VSCodeCounter/2024-05-22_07-57-47/diff.csv new file mode 100644 index 0000000..c76b950 --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/diff.csv @@ -0,0 +1,21 @@ +"filename", "language", "Markdown", "Python", "comment", "blank", "total" +"e:\Projects\Python\if1210-2024-tubes-k08-a\README.md", "Markdown", 26, 0, 0, 0, 26 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F04.py", "Python", 0, 0, 0, -1, -1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\ui_logic.py", "Python", 0, 2, 0, 0, 2 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\__init__.py", "Python", 0, 3, 0, 2, 5 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\menu.py", "Python", 0, 12, 0, 0, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_debug.py", "Python", 0, 11, 0, 1, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_shop.py", "Python", 0, 203, 0, 11, 214 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_user.py", "Python", 0, 6, 0, 0, 6 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\__init__.py", "Python", 0, 1, 0, 0, 1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\monster.py", "Python", 0, 4, 0, 1, 5 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\__init__.py", "Python", 0, 5, 0, 0, 5 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\shop.py", "Python", 0, 27, 0, 4, 31 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\__init__.py", "Python", 0, 1, 0, 0, 1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\state.py", "Python", 0, 2, 0, 0, 2 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\visual.py", "Python", 0, 316, 0, 16, 332 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\__init__.py", "Python", 0, 1, 0, 0, 1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\user.py", "Python", 0, 26, 0, 3, 29 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\console.py", "Python", 0, 25, 0, 0, 25 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\__init__.py", "Python", 0, -1, 0, 0, -1 +"Total", "-", 26, 644, 0, 37, 707 \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/diff.md b/.VSCodeCounter/2024-05-22_07-57-47/diff.md new file mode 100644 index 0000000..720d93c --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/diff.md @@ -0,0 +1,35 @@ +# Diff Summary + +Date : 2024-05-22 07:57:47 + +Directory e:\\Projects\\Python\\if1210-2024-tubes-k08-a + +Total : 19 files, 670 codes, 0 comments, 37 blanks, all 707 lines + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| Python | 18 | 644 | 0 | 37 | 681 | +| Markdown | 1 | 26 | 0 | 0 | 26 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 19 | 670 | 0 | 37 | 707 | +| . (Files) | 1 | 26 | 0 | 0 | 26 | +| src | 18 | 644 | 0 | 37 | 681 | +| src\\game | 16 | 620 | 0 | 37 | 657 | +| src\\game (Files) | 1 | 0 | 0 | -1 | -1 | +| src\\game\\battle | 1 | 2 | 0 | 0 | 2 | +| src\\game\\menu | 5 | 235 | 0 | 14 | 249 | +| src\\game\\monster | 2 | 5 | 0 | 1 | 6 | +| src\\game\\shop | 2 | 32 | 0 | 4 | 36 | +| src\\game\\state | 3 | 319 | 0 | 16 | 335 | +| src\\game\\user | 2 | 27 | 0 | 3 | 30 | +| src\\utils | 2 | 24 | 0 | 0 | 24 | +| src\\utils\\console | 1 | 25 | 0 | 0 | 25 | +| src\\utils\\coroutines | 1 | -1 | 0 | 0 | -1 | + +[Summary](results.md) / [Details](details.md) / Diff Summary / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/diff.txt b/.VSCodeCounter/2024-05-22_07-57-47/diff.txt new file mode 100644 index 0000000..5d4d464 --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/diff.txt @@ -0,0 +1,57 @@ +Date : 2024-05-22 07:57:47 +Directory : e:\Projects\Python\if1210-2024-tubes-k08-a +Total : 19 files, 670 codes, 0 comments, 37 blanks, all 707 lines + +Languages ++----------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++----------+------------+------------+------------+------------+------------+ +| Python | 18 | 644 | 0 | 37 | 681 | +| Markdown | 1 | 26 | 0 | 0 | 26 | ++----------+------------+------------+------------+------------+------------+ + +Directories ++-----------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++-----------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 19 | 670 | 0 | 37 | 707 | +| . (Files) | 1 | 26 | 0 | 0 | 26 | +| src | 18 | 644 | 0 | 37 | 681 | +| src\game | 16 | 620 | 0 | 37 | 657 | +| src\game (Files) | 1 | 0 | 0 | -1 | -1 | +| src\game\battle | 1 | 2 | 0 | 0 | 2 | +| src\game\menu | 5 | 235 | 0 | 14 | 249 | +| src\game\monster | 2 | 5 | 0 | 1 | 6 | +| src\game\shop | 2 | 32 | 0 | 4 | 36 | +| src\game\state | 3 | 319 | 0 | 16 | 335 | +| src\game\user | 2 | 27 | 0 | 3 | 30 | +| src\utils | 2 | 24 | 0 | 0 | 24 | +| src\utils\console | 1 | 25 | 0 | 0 | 25 | +| src\utils\coroutines | 1 | -1 | 0 | 0 | -1 | ++-----------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++-----------------------------------------------------------------------------+----------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++-----------------------------------------------------------------------------+----------+------------+------------+------------+------------+ +| e:\Projects\Python\if1210-2024-tubes-k08-a\README.md | Markdown | 26 | 0 | 0 | 26 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F04.py | Python | 0 | 0 | -1 | -1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\ui_logic.py | Python | 2 | 0 | 0 | 2 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\__init__.py | Python | 3 | 0 | 2 | 5 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\menu.py | Python | 12 | 0 | 0 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_debug.py | Python | 11 | 0 | 1 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_shop.py | Python | 203 | 0 | 11 | 214 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_user.py | Python | 6 | 0 | 0 | 6 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\__init__.py | Python | 1 | 0 | 0 | 1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\monster.py | Python | 4 | 0 | 1 | 5 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\__init__.py | Python | 5 | 0 | 0 | 5 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\shop.py | Python | 27 | 0 | 4 | 31 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\__init__.py | Python | 1 | 0 | 0 | 1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\state.py | Python | 2 | 0 | 0 | 2 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\visual.py | Python | 316 | 0 | 16 | 332 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\__init__.py | Python | 1 | 0 | 0 | 1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\user.py | Python | 26 | 0 | 3 | 29 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\console.py | Python | 25 | 0 | 0 | 25 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\__init__.py | Python | -1 | 0 | 0 | -1 | +| Total | | 670 | 0 | 37 | 707 | ++-----------------------------------------------------------------------------+----------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/results.csv b/.VSCodeCounter/2024-05-22_07-57-47/results.csv new file mode 100644 index 0000000..29f718a --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/results.csv @@ -0,0 +1,89 @@ +"filename", "language", "Python", "Markdown", "JSON", "pip requirements", "JavaScript", "comment", "blank", "total" +"e:\Projects\Python\if1210-2024-tubes-k08-a\F012 - Shop Management.py", "Python", 264, 0, 0, 0, 0, 1, 32, 297 +"e:\Projects\Python\if1210-2024-tubes-k08-a\F13 Monster Management.py", "Python", 47, 0, 0, 0, 0, 1, 7, 55 +"e:\Projects\Python\if1210-2024-tubes-k08-a\README.md", "Markdown", 0, 36, 0, 0, 0, 0, 6, 42 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\assets\README.md", "Markdown", 0, 11, 0, 0, 0, 0, 10, 21 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\gamesave\createnpc.mjs", "JavaScript", 0, 0, 0, 0, 59, 2, 6, 67 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\copyasciifiles.mjs", "JavaScript", 0, 0, 0, 0, 22, 0, 3, 25 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\fetchmonsters.mjs", "JavaScript", 0, 0, 0, 0, 90, 0, 7, 97 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\fetchsprites.mjs", "JavaScript", 0, 0, 0, 0, 40, 0, 3, 43 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\generatesplash.mjs", "JavaScript", 0, 0, 0, 0, 158, 0, 4, 162 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\package-lock.json", "JSON", 0, 0, 708, 0, 0, 0, 1, 709 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\package.json", "JSON", 0, 0, 13, 0, 0, 0, 1, 14 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\download.mjs", "JavaScript", 0, 0, 0, 0, 75, 0, 4, 79 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\generate.mjs", "JavaScript", 0, 0, 0, 0, 0, 0, 1, 1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\package-lock.json", "JSON", 0, 0, 629, 0, 0, 0, 1, 630 +"e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\package.json", "JSON", 0, 0, 11, 0, 0, 0, 1, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\exit.py", "Python", 15, 0, 0, 0, 0, 0, 0, 15 +"e:\Projects\Python\if1210-2024-tubes-k08-a\f05 level.py", "Python", 15, 0, 0, 0, 0, 4, 4, 23 +"e:\Projects\Python\if1210-2024-tubes-k08-a\main.py", "Python", 79, 0, 0, 0, 0, 3, 3, 85 +"e:\Projects\Python\if1210-2024-tubes-k08-a\pyrightconfig.json", "JSON", 0, 0, 7, 0, 0, 0, 1, 8 +"e:\Projects\Python\if1210-2024-tubes-k08-a\requirements.txt", "pip requirements", 0, 0, 0, 2, 0, 0, 1, 3 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\data.py", "Python", 92, 0, 0, 0, 0, 13, 29, 134 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\encryption.py", "Python", 15, 0, 0, 0, 0, 3, 5, 23 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F04.py", "Python", 28, 0, 0, 0, 0, 0, 1, 29 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F10.py", "Python", 125, 0, 0, 0, 0, 1, 12, 138 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F11.py", "Python", 88, 0, 0, 0, 0, 1, 9, 98 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\README.md", "Markdown", 0, 33, 0, 0, 0, 0, 13, 46 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\__init__.py", "Python", 7, 0, 0, 0, 0, 0, 4, 11 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\arena.py", "Python", 23, 0, 0, 0, 0, 0, 6, 29 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\ui_logic.py", "Python", 245, 0, 0, 0, 0, 7, 4, 256 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\__init__.py", "Python", 15, 0, 0, 0, 0, 0, 4, 19 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\battle.py", "Python", 55, 0, 0, 0, 0, 0, 12, 67 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\ui_logic.py", "Python", 373, 0, 0, 0, 0, 20, 4, 397 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\database\__init__.py", "Python", 31, 0, 0, 0, 0, 0, 2, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\database\database.py", "Python", 122, 0, 0, 0, 0, 0, 7, 129 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\generateinit.mjs", "JavaScript", 0, 0, 0, 0, 30, 0, 3, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\__init__.py", "Python", 10, 0, 0, 0, 0, 0, 2, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\_inventory.py", "Python", 80, 0, 0, 0, 0, 0, 11, 91 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\inventory.py", "Python", 102, 0, 0, 0, 0, 1, 12, 115 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\laboratory\__init__.py", "Python", 0, 0, 0, 0, 0, 0, 2, 2 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\laboratory\laboratory.py", "Python", 0, 0, 0, 0, 0, 0, 1, 1 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\__init__.py", "Python", 21, 0, 0, 0, 0, 0, 12, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\menu.py", "Python", 148, 0, 0, 0, 0, 0, 5, 153 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_arena.py", "Python", 165, 0, 0, 0, 0, 3, 7, 175 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_battle.py", "Python", 300, 0, 0, 0, 0, 3, 17, 320 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_debug.py", "Python", 169, 0, 0, 0, 0, 7, 7, 183 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_shop.py", "Python", 203, 0, 0, 0, 0, 0, 11, 214 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_user.py", "Python", 89, 0, 0, 0, 0, 0, 4, 93 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\__init__.py", "Python", 5, 0, 0, 0, 0, 0, 2, 7 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\monster.py", "Python", 34, 0, 0, 0, 0, 0, 5, 39 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\potion\__init__.py", "Python", 10, 0, 0, 0, 0, 0, 2, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\potion\potion.py", "Python", 60, 0, 0, 0, 0, 0, 12, 72 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\setup.py", "Python", 9, 0, 0, 0, 0, 0, 2, 11 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\__init__.py", "Python", 5, 0, 0, 0, 0, 0, 2, 7 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\shop.py", "Python", 27, 0, 0, 0, 0, 0, 5, 32 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\__init__.py", "Python", 84, 0, 0, 0, 0, 0, 4, 88 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\state.py", "Python", 265, 0, 0, 0, 0, 3, 13, 281 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\visual.py", "Python", 1542, 0, 0, 0, 0, 0, 45, 1587 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\__init__.py", "Python", 10, 0, 0, 0, 0, 0, 2, 12 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\user.py", "Python", 155, 0, 0, 0, 0, 20, 28, 203 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\lcg.py", "Python", 32, 0, 0, 0, 0, 16, 13, 61 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\codec\__init__.py", "Python", 3, 0, 0, 0, 0, 0, 2, 5 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\codec\codec.py", "Python", 39, 0, 0, 0, 0, 0, 5, 44 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\__init__.py", "Python", 243, 0, 0, 0, 0, 0, 6, 249 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\console.py", "Python", 1601, 0, 0, 0, 0, 15, 50, 1666 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\driver_std.py", "Python", 218, 0, 0, 0, 0, 0, 14, 232 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\views.py", "Python", 84, 0, 0, 0, 0, 0, 5, 89 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\__init__.py", "Python", 55, 0, 0, 0, 0, 0, 2, 57 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\coroutines.py", "Python", 524, 0, 0, 0, 0, 1, 56, 581 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\csv\__init__.py", "Python", 3, 0, 0, 0, 0, 0, 2, 5 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\csv\csv.py", "Python", 65, 0, 0, 0, 0, 10, 7, 82 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\generateinit.mjs", "JavaScript", 0, 0, 0, 0, 30, 0, 3, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\math\__init__.py", "Python", 31, 0, 0, 0, 0, 0, 2, 33 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\math\math.py", "Python", 110, 0, 0, 0, 0, 18, 16, 144 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\mixin\__init__.py", "Python", 7, 0, 0, 0, 0, 0, 2, 9 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\mixin\mixin.py", "Python", 69, 0, 0, 0, 0, 0, 7, 76 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\primordials\__init__.py", "Python", 62, 0, 0, 0, 0, 0, 2, 64 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\primordials\primordials.py", "Python", 387, 0, 0, 0, 0, 14, 67, 468 +"e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\setup.py", "Python", 9, 0, 0, 0, 0, 0, 2, 11 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\csv_reader.py", "Python", 7, 0, 0, 0, 0, 0, 2, 9 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testabortsignalsuspendable.py", "Python", 30, 0, 0, 0, 0, 0, 5, 35 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testconsole_textformatter.py", "Python", 68, 0, 0, 0, 0, 0, 10, 78 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testconsole_views.py", "Python", 53, 0, 0, 0, 0, 0, 12, 65 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testgetch.py", "Python", 117, 0, 0, 0, 0, 7, 5, 129 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testmixin.py", "Python", 42, 0, 0, 0, 0, 1, 8, 51 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testprimordialstrim.py", "Python", 16, 0, 0, 0, 0, 0, 3, 19 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\testutils.py", "Python", 38, 0, 0, 0, 0, 0, 7, 45 +"e:\Projects\Python\if1210-2024-tubes-k08-a\test\userauthtest.py", "Python", 35, 0, 0, 0, 0, 6, 4, 45 +"Total", "-", 9080, 80, 1368, 2, 504, 181, 743, 11958 \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/results.json b/.VSCodeCounter/2024-05-22_07-57-47/results.json new file mode 100644 index 0000000..4df1688 --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/results.json @@ -0,0 +1 @@ +{"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testmixin.py":{"language":"Python","code":42,"comment":1,"blank":8},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testprimordialstrim.py":{"language":"Python","code":16,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testgetch.py":{"language":"Python","code":117,"comment":7,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testutils.py":{"language":"Python","code":38,"comment":0,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testconsole_textformatter.py":{"language":"Python","code":68,"comment":0,"blank":10},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testabortsignalsuspendable.py":{"language":"Python","code":30,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/testconsole_views.py":{"language":"Python","code":53,"comment":0,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/csv_reader.py":{"language":"Python","code":7,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/README.md":{"language":"Markdown","code":36,"comment":0,"blank":6},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/pyrightconfig.json":{"language":"JSON","code":7,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/test/userauthtest.py":{"language":"Python","code":35,"comment":6,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/main.py":{"language":"Python","code":79,"comment":3,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/requirements.txt":{"language":"pip requirements","code":2,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/F13%20Monster%20Management.py":{"language":"Python","code":47,"comment":1,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/exit.py":{"language":"Python","code":15,"comment":0,"blank":0},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/f05%20level.py":{"language":"Python","code":15,"comment":4,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/lcg.py":{"language":"Python","code":32,"comment":16,"blank":13},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/F04.py":{"language":"Python","code":28,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/F10.py":{"language":"Python","code":125,"comment":1,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/F11.py":{"language":"Python","code":88,"comment":1,"blank":9},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/encryption.py":{"language":"Python","code":15,"comment":3,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/F012%20-%20Shop%20Management.py":{"language":"Python","code":264,"comment":1,"blank":32},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/codec/codec.py":{"language":"Python","code":39,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/laboratory/__init__.py":{"language":"Python","code":0,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/data.py":{"language":"Python","code":92,"comment":13,"blank":29},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/csv/__init__.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/console/driver_std.py":{"language":"Python","code":218,"comment":0,"blank":14},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/worldmap/package-lock.json":{"language":"JSON","code":629,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/worldmap/download.mjs":{"language":"JavaScript","code":75,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/worldmap/generate.mjs":{"language":"JavaScript","code":0,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/csv/csv.py":{"language":"Python","code":65,"comment":10,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/console/__init__.py":{"language":"Python","code":243,"comment":0,"blank":6},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/database/__init__.py":{"language":"Python","code":31,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/worldmap/package.json":{"language":"JSON","code":11,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/laboratory/laboratory.py":{"language":"Python","code":0,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/user/__init__.py":{"language":"Python","code":10,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/codec/__init__.py":{"language":"Python","code":3,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/console/views.py":{"language":"Python","code":84,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/user/user.py":{"language":"Python","code":155,"comment":20,"blank":28},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/database/database.py":{"language":"Python","code":122,"comment":0,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/package.json":{"language":"JSON","code":13,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/generatesplash.mjs":{"language":"JavaScript","code":158,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/package-lock.json":{"language":"JSON","code":708,"comment":0,"blank":1},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/fetchsprites.mjs":{"language":"JavaScript","code":40,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/copyasciifiles.mjs":{"language":"JavaScript","code":22,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/console/console.py":{"language":"Python","code":1601,"comment":15,"blank":50},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/state/__init__.py":{"language":"Python","code":84,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/setup.py":{"language":"Python","code":9,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/monsters/fetchmonsters.mjs":{"language":"JavaScript","code":90,"comment":0,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/state/state.py":{"language":"Python","code":265,"comment":3,"blank":13},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/generator/gamesave/createnpc.mjs":{"language":"JavaScript","code":59,"comment":2,"blank":6},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/shop/shop.py":{"language":"Python","code":27,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/state/visual.py":{"language":"Python","code":1542,"comment":0,"blank":45},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/shop/__init__.py":{"language":"Python","code":5,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/primordials/primordials.py":{"language":"Python","code":387,"comment":14,"blank":67},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/setup.py":{"language":"Python","code":9,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/primordials/__init__.py":{"language":"Python","code":62,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/mixin/mixin.py":{"language":"Python","code":69,"comment":0,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/README.md":{"language":"Markdown","code":33,"comment":0,"blank":13},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/potion/potion.py":{"language":"Python","code":60,"comment":0,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/mixin/__init__.py":{"language":"Python","code":7,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/potion/__init__.py":{"language":"Python","code":10,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/math/__init__.py":{"language":"Python","code":31,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/math/math.py":{"language":"Python","code":110,"comment":18,"blank":16},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/generateinit.mjs":{"language":"JavaScript","code":30,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/monster/__init__.py":{"language":"Python","code":5,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_user.py":{"language":"Python","code":89,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/__init__.py":{"language":"Python","code":21,"comment":0,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_debug.py":{"language":"Python","code":169,"comment":7,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_shop.py":{"language":"Python","code":203,"comment":0,"blank":11},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/monster/monster.py":{"language":"Python","code":34,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_arena.py":{"language":"Python","code":165,"comment":3,"blank":7},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/menu.py":{"language":"Python","code":148,"comment":0,"blank":5},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/menu/ui_battle.py":{"language":"Python","code":300,"comment":3,"blank":17},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/inventory/__init__.py":{"language":"Python","code":10,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/inventory/_inventory.py":{"language":"Python","code":80,"comment":0,"blank":11},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/coroutines/__init__.py":{"language":"Python","code":55,"comment":0,"blank":2},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/utils/coroutines/coroutines.py":{"language":"Python","code":524,"comment":1,"blank":56},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/inventory/inventory.py":{"language":"Python","code":102,"comment":1,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/arena/__init__.py":{"language":"Python","code":7,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/generateinit.mjs":{"language":"JavaScript","code":30,"comment":0,"blank":3},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/data/assets/README.md":{"language":"Markdown","code":11,"comment":0,"blank":10},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/battle/ui_logic.py":{"language":"Python","code":373,"comment":20,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/arena/ui_logic.py":{"language":"Python","code":245,"comment":7,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/battle/battle.py":{"language":"Python","code":55,"comment":0,"blank":12},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/battle/__init__.py":{"language":"Python","code":15,"comment":0,"blank":4},"file:///e%3A/Projects/Python/if1210-2024-tubes-k08-a/src/game/arena/arena.py":{"language":"Python","code":23,"comment":0,"blank":6}} \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/results.md b/.VSCodeCounter/2024-05-22_07-57-47/results.md new file mode 100644 index 0000000..e4e9973 --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/results.md @@ -0,0 +1,57 @@ +# Summary + +Date : 2024-05-22 07:57:47 + +Directory e:\\Projects\\Python\\if1210-2024-tubes-k08-a + +Total : 87 files, 11034 codes, 181 comments, 743 blanks, all 11958 lines + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) + +## Languages +| language | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| Python | 69 | 9,080 | 179 | 674 | 9,933 | +| JSON | 5 | 1,368 | 0 | 5 | 1,373 | +| JavaScript | 9 | 504 | 2 | 34 | 540 | +| Markdown | 3 | 80 | 0 | 29 | 109 | +| pip requirements | 1 | 2 | 0 | 1 | 3 | + +## Directories +| path | files | code | comment | blank | total | +| :--- | ---: | ---: | ---: | ---: | ---: | +| . | 87 | 11,034 | 181 | 743 | 11,958 | +| . (Files) | 8 | 465 | 9 | 54 | 528 | +| data | 12 | 1,816 | 2 | 42 | 1,860 | +| data\\assets | 1 | 11 | 0 | 10 | 21 | +| data\\generator | 11 | 1,805 | 2 | 32 | 1,839 | +| data\\generator\\gamesave | 1 | 59 | 2 | 6 | 67 | +| data\\generator\\monsters | 6 | 1,031 | 0 | 19 | 1,050 | +| data\\generator\\worldmap | 4 | 715 | 0 | 7 | 722 | +| src | 58 | 8,347 | 156 | 591 | 9,094 | +| src (Files) | 3 | 139 | 32 | 47 | 218 | +| src\\game | 37 | 4,668 | 66 | 294 | 5,028 | +| src\\game (Files) | 6 | 313 | 2 | 40 | 355 | +| src\\game\\arena | 3 | 275 | 7 | 14 | 296 | +| src\\game\\battle | 3 | 443 | 20 | 20 | 483 | +| src\\game\\database | 2 | 153 | 0 | 9 | 162 | +| src\\game\\inventory | 3 | 192 | 1 | 25 | 218 | +| src\\game\\laboratory | 2 | 0 | 0 | 3 | 3 | +| src\\game\\menu | 7 | 1,095 | 13 | 63 | 1,171 | +| src\\game\\monster | 2 | 39 | 0 | 7 | 46 | +| src\\game\\potion | 2 | 70 | 0 | 14 | 84 | +| src\\game\\shop | 2 | 32 | 0 | 7 | 39 | +| src\\game\\state | 3 | 1,891 | 3 | 62 | 1,956 | +| src\\game\\user | 2 | 165 | 20 | 30 | 215 | +| src\\utils | 18 | 3,540 | 58 | 250 | 3,848 | +| src\\utils (Files) | 2 | 39 | 0 | 5 | 44 | +| src\\utils\\codec | 2 | 42 | 0 | 7 | 49 | +| src\\utils\\console | 4 | 2,146 | 15 | 75 | 2,236 | +| src\\utils\\coroutines | 2 | 579 | 1 | 58 | 638 | +| src\\utils\\csv | 2 | 68 | 10 | 9 | 87 | +| src\\utils\\math | 2 | 141 | 18 | 18 | 177 | +| src\\utils\\mixin | 2 | 76 | 0 | 9 | 85 | +| src\\utils\\primordials | 2 | 449 | 14 | 69 | 532 | +| test | 9 | 406 | 14 | 56 | 476 | + +Summary / [Details](details.md) / [Diff Summary](diff.md) / [Diff Details](diff-details.md) \ No newline at end of file diff --git a/.VSCodeCounter/2024-05-22_07-57-47/results.txt b/.VSCodeCounter/2024-05-22_07-57-47/results.txt new file mode 100644 index 0000000..504469f --- /dev/null +++ b/.VSCodeCounter/2024-05-22_07-57-47/results.txt @@ -0,0 +1,147 @@ +Date : 2024-05-22 07:57:47 +Directory : e:\Projects\Python\if1210-2024-tubes-k08-a +Total : 87 files, 11034 codes, 181 comments, 743 blanks, all 11958 lines + +Languages ++------------------+------------+------------+------------+------------+------------+ +| language | files | code | comment | blank | total | ++------------------+------------+------------+------------+------------+------------+ +| Python | 69 | 9,080 | 179 | 674 | 9,933 | +| JSON | 5 | 1,368 | 0 | 5 | 1,373 | +| JavaScript | 9 | 504 | 2 | 34 | 540 | +| Markdown | 3 | 80 | 0 | 29 | 109 | +| pip requirements | 1 | 2 | 0 | 1 | 3 | ++------------------+------------+------------+------------+------------+------------+ + +Directories ++---------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| path | files | code | comment | blank | total | ++---------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ +| . | 87 | 11,034 | 181 | 743 | 11,958 | +| . (Files) | 8 | 465 | 9 | 54 | 528 | +| data | 12 | 1,816 | 2 | 42 | 1,860 | +| data\assets | 1 | 11 | 0 | 10 | 21 | +| data\generator | 11 | 1,805 | 2 | 32 | 1,839 | +| data\generator\gamesave | 1 | 59 | 2 | 6 | 67 | +| data\generator\monsters | 6 | 1,031 | 0 | 19 | 1,050 | +| data\generator\worldmap | 4 | 715 | 0 | 7 | 722 | +| src | 58 | 8,347 | 156 | 591 | 9,094 | +| src (Files) | 3 | 139 | 32 | 47 | 218 | +| src\game | 37 | 4,668 | 66 | 294 | 5,028 | +| src\game (Files) | 6 | 313 | 2 | 40 | 355 | +| src\game\arena | 3 | 275 | 7 | 14 | 296 | +| src\game\battle | 3 | 443 | 20 | 20 | 483 | +| src\game\database | 2 | 153 | 0 | 9 | 162 | +| src\game\inventory | 3 | 192 | 1 | 25 | 218 | +| src\game\laboratory | 2 | 0 | 0 | 3 | 3 | +| src\game\menu | 7 | 1,095 | 13 | 63 | 1,171 | +| src\game\monster | 2 | 39 | 0 | 7 | 46 | +| src\game\potion | 2 | 70 | 0 | 14 | 84 | +| src\game\shop | 2 | 32 | 0 | 7 | 39 | +| src\game\state | 3 | 1,891 | 3 | 62 | 1,956 | +| src\game\user | 2 | 165 | 20 | 30 | 215 | +| src\utils | 18 | 3,540 | 58 | 250 | 3,848 | +| src\utils (Files) | 2 | 39 | 0 | 5 | 44 | +| src\utils\codec | 2 | 42 | 0 | 7 | 49 | +| src\utils\console | 4 | 2,146 | 15 | 75 | 2,236 | +| src\utils\coroutines | 2 | 579 | 1 | 58 | 638 | +| src\utils\csv | 2 | 68 | 10 | 9 | 87 | +| src\utils\math | 2 | 141 | 18 | 18 | 177 | +| src\utils\mixin | 2 | 76 | 0 | 9 | 85 | +| src\utils\primordials | 2 | 449 | 14 | 69 | 532 | +| test | 9 | 406 | 14 | 56 | 476 | ++---------------------------------------------------------------------------------------+------------+------------+------------+------------+------------+ + +Files ++---------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ +| filename | language | code | comment | blank | total | ++---------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ +| e:\Projects\Python\if1210-2024-tubes-k08-a\F012 - Shop Management.py | Python | 264 | 1 | 32 | 297 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\F13 Monster Management.py | Python | 47 | 1 | 7 | 55 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\README.md | Markdown | 36 | 0 | 6 | 42 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\assets\README.md | Markdown | 11 | 0 | 10 | 21 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\gamesave\createnpc.mjs | JavaScript | 59 | 2 | 6 | 67 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\copyasciifiles.mjs | JavaScript | 22 | 0 | 3 | 25 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\fetchmonsters.mjs | JavaScript | 90 | 0 | 7 | 97 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\fetchsprites.mjs | JavaScript | 40 | 0 | 3 | 43 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\generatesplash.mjs | JavaScript | 158 | 0 | 4 | 162 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\package-lock.json | JSON | 708 | 0 | 1 | 709 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\monsters\package.json | JSON | 13 | 0 | 1 | 14 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\download.mjs | JavaScript | 75 | 0 | 4 | 79 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\generate.mjs | JavaScript | 0 | 0 | 1 | 1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\package-lock.json | JSON | 629 | 0 | 1 | 630 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\data\generator\worldmap\package.json | JSON | 11 | 0 | 1 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\exit.py | Python | 15 | 0 | 0 | 15 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\f05 level.py | Python | 15 | 4 | 4 | 23 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\main.py | Python | 79 | 3 | 3 | 85 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\pyrightconfig.json | JSON | 7 | 0 | 1 | 8 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\requirements.txt | pip requirements | 2 | 0 | 1 | 3 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\data.py | Python | 92 | 13 | 29 | 134 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\encryption.py | Python | 15 | 3 | 5 | 23 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F04.py | Python | 28 | 0 | 1 | 29 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F10.py | Python | 125 | 1 | 12 | 138 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\F11.py | Python | 88 | 1 | 9 | 98 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\README.md | Markdown | 33 | 0 | 13 | 46 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\__init__.py | Python | 7 | 0 | 4 | 11 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\arena.py | Python | 23 | 0 | 6 | 29 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\arena\ui_logic.py | Python | 245 | 7 | 4 | 256 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\__init__.py | Python | 15 | 0 | 4 | 19 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\battle.py | Python | 55 | 0 | 12 | 67 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\battle\ui_logic.py | Python | 373 | 20 | 4 | 397 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\database\__init__.py | Python | 31 | 0 | 2 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\database\database.py | Python | 122 | 0 | 7 | 129 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\generateinit.mjs | JavaScript | 30 | 0 | 3 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\__init__.py | Python | 10 | 0 | 2 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\_inventory.py | Python | 80 | 0 | 11 | 91 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\inventory\inventory.py | Python | 102 | 1 | 12 | 115 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\laboratory\__init__.py | Python | 0 | 0 | 2 | 2 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\laboratory\laboratory.py | Python | 0 | 0 | 1 | 1 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\__init__.py | Python | 21 | 0 | 12 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\menu.py | Python | 148 | 0 | 5 | 153 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_arena.py | Python | 165 | 3 | 7 | 175 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_battle.py | Python | 300 | 3 | 17 | 320 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_debug.py | Python | 169 | 7 | 7 | 183 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_shop.py | Python | 203 | 0 | 11 | 214 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\menu\ui_user.py | Python | 89 | 0 | 4 | 93 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\__init__.py | Python | 5 | 0 | 2 | 7 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\monster\monster.py | Python | 34 | 0 | 5 | 39 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\potion\__init__.py | Python | 10 | 0 | 2 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\potion\potion.py | Python | 60 | 0 | 12 | 72 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\setup.py | Python | 9 | 0 | 2 | 11 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\__init__.py | Python | 5 | 0 | 2 | 7 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\shop\shop.py | Python | 27 | 0 | 5 | 32 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\__init__.py | Python | 84 | 0 | 4 | 88 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\state.py | Python | 265 | 3 | 13 | 281 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\state\visual.py | Python | 1,542 | 0 | 45 | 1,587 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\__init__.py | Python | 10 | 0 | 2 | 12 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\game\user\user.py | Python | 155 | 20 | 28 | 203 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\lcg.py | Python | 32 | 16 | 13 | 61 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\codec\__init__.py | Python | 3 | 0 | 2 | 5 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\codec\codec.py | Python | 39 | 0 | 5 | 44 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\__init__.py | Python | 243 | 0 | 6 | 249 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\console.py | Python | 1,601 | 15 | 50 | 1,666 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\driver_std.py | Python | 218 | 0 | 14 | 232 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\console\views.py | Python | 84 | 0 | 5 | 89 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\__init__.py | Python | 55 | 0 | 2 | 57 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\coroutines\coroutines.py | Python | 524 | 1 | 56 | 581 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\csv\__init__.py | Python | 3 | 0 | 2 | 5 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\csv\csv.py | Python | 65 | 10 | 7 | 82 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\generateinit.mjs | JavaScript | 30 | 0 | 3 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\math\__init__.py | Python | 31 | 0 | 2 | 33 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\math\math.py | Python | 110 | 18 | 16 | 144 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\mixin\__init__.py | Python | 7 | 0 | 2 | 9 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\mixin\mixin.py | Python | 69 | 0 | 7 | 76 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\primordials\__init__.py | Python | 62 | 0 | 2 | 64 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\primordials\primordials.py | Python | 387 | 14 | 67 | 468 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\src\utils\setup.py | Python | 9 | 0 | 2 | 11 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\csv_reader.py | Python | 7 | 0 | 2 | 9 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testabortsignalsuspendable.py | Python | 30 | 0 | 5 | 35 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testconsole_textformatter.py | Python | 68 | 0 | 10 | 78 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testconsole_views.py | Python | 53 | 0 | 12 | 65 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testgetch.py | Python | 117 | 7 | 5 | 129 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testmixin.py | Python | 42 | 1 | 8 | 51 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testprimordialstrim.py | Python | 16 | 0 | 3 | 19 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\testutils.py | Python | 38 | 0 | 7 | 45 | +| e:\Projects\Python\if1210-2024-tubes-k08-a\test\userauthtest.py | Python | 35 | 6 | 4 | 45 | +| Total | | 11,034 | 181 | 743 | 11,958 | ++---------------------------------------------------------------------------------------+------------------+------------+------------+------------+------------+ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 495ee28..1c30cb3 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,13 +4,22 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Download worldmap", + "program": "${workspaceFolder}/.private/worldmap/download.mjs", + "request": "launch", + "skipFiles": [ + "/**" + ], + "type": "node" + }, { "name": "Python Debugger: Remote Attach", "type": "debugpy", "request": "attach", "connect": { "host": "localhost", - "port": 5678 + "port": 5677 }, "pathMappings": [ { diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0055431 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "python.analysis.typeCheckingMode": "off", + "python.analysis.autoImportCompletions": false, + "python.analysis.logLevel": "Trace" +} diff --git a/data/assets/potions/potion1.png.txt b/data/assets/potions/potion1.png.txt new file mode 100644 index 0000000..30e5d6a --- /dev/null +++ b/data/assets/potions/potion1.png.txt @@ -0,0 +1,24 @@ + + + + §sbXuK|`§a| §skJml|:§sfIKN|-§fiZc|`§a| + §slqSx|>§sy8/V|w§soaaq|l§a| §sVVpp|`§sfY6V|_§s4eXm|&§swcfN|%§sfIKJ|^§a| + §sp62z|S§s////|@§s2Nrc|&§swMfK|0§syc7R|&§s4uDs|&§s//7/|@§s9/r7|@§sanN8|=§a| + §sjZSZ|*§s6fDy|@§sxavp|N§sysPf|%§swL7Z|Q§st4nk|X§s1c7r|&§svcjJ|M§sb3iG|-§a| + §snq2z|w§s0sjw|&§s6t/3|&§s8fL2|@§s2Mjv|&§sy9Da|&§skJ+o|p§a| + §sTmJ1|`§swMnR|Q§s7PHx|@§s5Onq|@§s2t/j|&§s7/T0|@§s6u/z|@§sz97o|&§sdYWO|:§a| + §soq23|i§spbXB|B§siZWg|d§soau0|8§sl6Su|X§sp7XA|g§smaq2|H§spLbC|g§s0Nnf|&§s0tfc|w§sqr/N|-§a| + §s0NPa|S§s6+3u|@§s3+Tp|&§ssLzH|N§sobG+|$§so7TD|B§snKu2|m§su8fQ|Q§s5Ozx|@§s6u/y|@§s/f7/|@§s7/P2|&§sv8za|,§a| + §sq73D|>§s6+zy|W§s293g|&§s3eHk|&§s7O/y|@§s8PP1|@§s+/3/|@§s7/T4|@§s9Pb5|@§s/P3+|@§s8vT2|@§s3eDj|&§sm6at|X§s+vv8|@§s8vT1|&§sXoKF|_§a| + §sWp2Q|{§spNnP|%§sptjM|%§sla2u|X§sfY2Z|w§sztbc|&§s4eTn|&§s+fr7|@§s////|@§s6+7y|@§s3ePn|&§s1dvg|&§suMLK|Q§sVmRw|v§swtDV|&§s3fPu|@§so8zF|Q§sHlxX|'§a| + §sQI+B|+§sYbin|X§sTqCR|w§sUKaV|q§sO4B1|3§stbq/|M§fwcs|Q§ss7/I|W§s9/n6|@§s////|@§s1dzh|&§sp7bB|g§sq7vG|M§suszX|%§sZnJ8|z§sWoaD|F§sfsC1|#§sXa6e|O§sS4+E|u§a| + §sSZOH|x§sTqKS|w§sR5eJ|S§sHHVm|L§sfJWV|w§s////|@§s+vv7|@§s9/j5|@§s////|@@§s9ff4|@§f9vc|@§s4eft|&§szN7n|&§srLnC|B§sLF9b|!§sWaua|p§sUKSU|w§hTw|q§sKFxW|_§a| + §sRYZ8|*§sO5KD|]§sKXBn|L§sFkJA|'§su7/K|m§szuXl|&§sq83H|Q§s////|@§s/f39|@§s////|@§s+/v8|@§s7fP1|@§slL67|g§sw9Tc|&§dxdE|&§sU4SB|5§sSZ6O|w§sVaeX|9§sOo6B|y§sJ2tk|+§a| + §sNmxk|-§sKldV|^§sJU5M|,§a| §smaGt|F§sbK+p|K§sFXpn|)§spsnK|Q§s5Oz1|@§s1eTr|&§s2+fy|&§spcbK|W§sGntq|(§scKml|b§swsvZ|%§sSX17|1§sKmRf|+§sMHFo|r§sLnRq|c§sI0VH|_§a| + §sPFJa|'§sNm1p|L§sHnBj|z§sM3Ns|)§saX+I|i§sZXN/|^§slqGu|E§sXYuL|S§sGGFW|!§sJ2Zg|/§sS2Rr|+§sFCMp|`§sHj09|`§sIUdH|`§sIktL|'§a| + §sIlFO|^§sJGZf|/§sJWVe|*§sH1JO|>§sDSIk|`§a| §sH0FE|,§sJGNc|*§sIF1W|!§sG0tJ|=§a| + §sJVBM|:§sJFhT|,§sI1JP|,§sJkZG|`§a| §sIlRR|=§sJW5l|/§sJnBm|s§sIUtJ|^§sIDk9|`§a| + §sIzc3|`§a| §sIUpK|_§a| + + + \ No newline at end of file diff --git a/data/generator/gamesave/generatelaboratory.mjs b/data/generator/gamesave/generatelaboratory.mjs new file mode 100644 index 0000000..dbedb46 --- /dev/null +++ b/data/generator/gamesave/generatelaboratory.mjs @@ -0,0 +1,43 @@ +import fs from "fs/promises" +import fs0 from "fs" +import path from "path" +import url from "url" + +const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); + +(async () => { + const monsters = JSON.parse(await fs.readFile(path.join(__dirname, "../monsters/database_monster.json"), "utf-8")) + .filter(m => fs0.existsSync(path.join(__dirname, "../../assets/", m.spriteDefault))); + const families = [...new Set(monsters.map(m => m.family))]; + const upgradeOptions = []; + for(const family of families) { + let currentLevel = 1; + let lastMonster = null; + while(true) { + const currentMonster = monsters.find(m => m.family == family && m.level == currentLevel); + if(currentMonster == null) + break; + if(lastMonster == null) { + currentLevel++; + lastMonster = currentMonster; + continue; + } + let cost = 0; + cost += (currentMonster.healthPoints - lastMonster.healthPoints) * 2.2; + cost += (currentMonster.attackPower - lastMonster.attackPower) * 14.2; + cost += (currentMonster.defensePower - lastMonster.defensePower) * 11.7; + cost = Math.round(cost / 75) * 75; + cost = Math.max(300, cost); + upgradeOptions.push({ + fromMonsterId: lastMonster.id, + toMonsterId: currentMonster.id, + cost: cost + }); + currentLevel++; + lastMonster = currentMonster; + } + } + + const csv = upgradeOptions.map((u, i) => [i, u.fromMonsterId, u.toMonsterId, u.cost].join(";")).join("\n"); + await fs.writeFile(path.join(__dirname, "laboratory.csv"), csv); +})(); diff --git a/data/generator/gamesave/laboratory.csv b/data/generator/gamesave/laboratory.csv new file mode 100644 index 0000000..53fc5f5 --- /dev/null +++ b/data/generator/gamesave/laboratory.csv @@ -0,0 +1,284 @@ +0;0;1;525 +1;1;2;750 +2;3;4;600 +3;4;5;825 +4;6;7;600 +5;7;8;825 +6;9;10;300 +7;10;11;825 +8;12;13;300 +9;13;14;1500 +10;15;16;750 +11;16;17;825 +12;18;19;975 +13;20;21;1125 +14;22;23;1125 +15;171;24;675 +16;26;27;975 +17;28;29;600 +18;29;30;975 +19;31;32;600 +20;32;33;975 +21;172;34;750 +22;34;35;975 +23;36;37;1350 +24;173;38;675 +25;38;39;975 +26;40;41;1350 +27;41;168;450 +28;42;43;525 +29;45;46;900 +30;47;48;600 +31;49;50;1200 +32;51;52;975 +33;53;54;1200 +34;55;56;975 +35;57;58;1425 +36;59;60;750 +37;62;63;600 +38;63;64;600 +39;65;66;600 +40;66;67;750 +41;68;69;600 +42;69;70;600 +43;71;72;1350 +44;73;74;600 +45;74;75;900 +46;76;77;600 +47;80;81;975 +48;81;461;525 +49;83;84;975 +50;85;86;975 +51;87;88;1050 +52;89;90;1125 +53;91;92;600 +54;92;93;600 +55;94;207;1275 +56;95;96;975 +57;97;98;975 +58;99;100;900 +59;107;462;900 +60;110;111;1200 +61;111;463;375 +62;439;112;2625 +63;112;241;300 +64;113;464;1275 +65;115;116;975 +66;116;229;825 +67;117;118;1050 +68;119;120;1200 +69;237;123;825 +70;238;124;825 +71;124;465;750 +72;239;125;825 +73;125;466;300 +74;128;129;3150 +75;136;232;750 +76;232;473;300 +77;137;138;1050 +78;139;140;1125 +79;445;142;975 +80;146;147;750 +81;147;148;1425 +82;151;152;525 +83;152;153;750 +84;154;155;600 +85;157;158;600 +86;158;159;825 +87;160;161;1500 +88;162;163;1200 +89;164;165;675 +90;166;167;1125 +91;169;170;1200 +92;174;175;900 +93;175;467;825 +94;176;177;975 +95;178;179;600 +96;179;180;825 +97;297;182;450 +98;182;183;1125 +99;437;184;750 +100;186;187;600 +101;187;188;675 +102;189;423;900 +103;190;191;1725 +104;192;468;675 +105;193;194;1500 +106;197;429;1275 +107;199;428;300 +108;359;201;1800 +109;203;204;1050 +110;206;471;525 +111;208;209;1200 +112;214;460;675 +113;215;216;1350 +114;217;218;825 +115;219;220;1725 +116;220;472;675 +117;222;223;1425 +118;457;225;1125 +119;227;228;1125 +120;230;231;1050 +121;245;246;750 +122;246;247;1425 +123;251;252;600 +124;252;253;825 +125;254;255;750 +126;255;256;975 +127;257;258;675 +128;258;259;975 +129;260;261;1350 +130;262;263;1425 +131;265;266;975 +132;267;268;750 +133;269;270;750 +134;270;271;825 +135;272;273;1050 +136;273;274;975 +137;275;276;1050 +138;277;278;900 +139;279;280;450 +140;282;283;1200 +141;284;285;1425 +142;286;287;900 +143;287;288;2400 +144;290;291;300 +145;292;293;750 +146;293;294;825 +147;295;296;2250 +148;298;475;900 +149;299;300;825 +150;303;304;600 +151;304;305;600 +152;306;307;975 +153;308;309;1200 +154;405;314;750 +155;314;406;525 +156;315;316;1125 +157;317;318;1050 +158;319;320;1050 +159;321;322;975 +160;324;325;825 +161;327;328;300 +162;328;329;1200 +163;330;331;975 +164;332;333;1200 +165;338;339;1575 +166;340;341;1125 +167;342;343;1050 +168;344;345;1050 +169;346;347;1050 +170;348;349;2325 +171;352;353;1125 +172;354;355;975 +173;355;476;600 +174;432;357;1050 +175;362;363;750 +176;363;364;750 +177;370;371;750 +178;371;372;1275 +179;373;374;750 +180;374;375;1350 +181;386;387;750 +182;387;388;750 +183;389;390;750 +184;390;391;750 +185;392;393;525 +186;393;394;825 +187;395;396;675 +188;396;397;1275 +189;398;399;1125 +190;400;401;1725 +191;402;403;675 +192;403;404;1050 +193;407;408;1200 +194;409;410;825 +195;411;412;1050 +196;414;415;1650 +197;417;418;1275 +198;419;420;1050 +199;421;422;1200 +200;424;425;1500 +201;426;427;525 +202;430;431;975 +203;433;434;1275 +204;435;436;1350 +205;442;443;600 +206;443;444;1425 +207;446;447;1425 +208;448;449;1500 +209;450;451;1275 +210;452;453;1425 +211;455;456;825 +212;458;459;1125 +213;494;495;600 +214;495;496;675 +215;497;498;975 +216;498;499;900 +217;500;501;750 +218;503;504;900 +219;505;506;750 +220;506;507;900 +221;508;509;1125 +222;510;511;1275 +223;512;513;1275 +224;514;515;1275 +225;516;517;1350 +226;518;519;600 +227;519;520;975 +228;521;522;1350 +229;523;524;825 +230;524;525;825 +231;526;527;375 +232;528;529;1725 +233;531;532;675 +234;532;533;900 +235;534;535;750 +236;535;536;1050 +237;539;540;450 +238;540;541;1050 +239;542;543;450 +240;543;544;1050 +241;545;546;1200 +242;550;551;375 +243;551;552;1275 +244;553;554;1425 +245;556;557;1125 +246;558;559;675 +247;561;562;900 +248;563;564;975 +249;565;566;975 +250;567;568;1275 +251;569;570;1125 +252;571;572;1200 +253;573;574;600 +254;574;575;450 +255;576;577;600 +256;577;578;1200 +257;579;580;1050 +258;581;582;600 +259;582;583;975 +260;584;585;1050 +261;587;588;1350 +262;589;590;1350 +263;591;592;1200 +264;594;595;975 +265;596;597;1350 +266;598;599;900 +267;599;600;375 +268;601;602;1050 +269;602;603;900 +270;604;605;825 +271;606;607;450 +272;607;608;525 +273;609;610;825 +274;610;611;750 +275;612;613;1725 +276;615;616;1200 +277;618;619;1125 +278;621;622;1425 +279;623;624;1050 +280;628;629;975 +281;632;633;750 +282;633;634;900 +283;635;636;525 \ No newline at end of file diff --git a/data/generator/monsters/fetchmonsters.mjs b/data/generator/monsters/fetchmonsters.mjs index a8d48fb..df98cb9 100644 --- a/data/generator/monsters/fetchmonsters.mjs +++ b/data/generator/monsters/fetchmonsters.mjs @@ -12,6 +12,7 @@ const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); const pokedexPage = parse(await (await fetch(p`/pokedex/all`)).text()); const pokedexRows = pokedexPage.querySelectorAll("#pokedex > tbody > tr"); + // const monsters = JSON.parse(await fs.readFile(path.join(__dirname, "database_monster.json"))); const monsters = []; for(const pokedexRow of pokedexRows) { const monsterId = parseInt(pokedexRow.querySelector("td:nth-child(1)").innerText) - 1; @@ -31,7 +32,7 @@ const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); attackPower: monsterAttack, defensePower: monsterDefense, level: 1, - familiy: monsterStrId, + family: monsterStrId, description: "", spriteDefault: `monsters/${monsterStrId}.png.txt`, spriteFront: `monsters/${monsterStrId}_normal.gif.txt`, @@ -46,9 +47,9 @@ const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); let i = 0; for(const monsterEvolutionId of monsterEvolutionIds) { const monster = monsters.find(m => m.strId == monsterEvolutionId); - if(monster.familiy != null) continue; + if(monster == null) continue; monster.level = ++i; - monster.familiy = monsterEvolutionIds[0]; + monster.family = monsterEvolutionIds[0]; } } @@ -69,7 +70,7 @@ const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); m.id, m.name, `"${m.description}"`, - m.familiy, + m.family, m.level, m.healthPoints, m.attackPower, @@ -78,19 +79,6 @@ const __dirname = path.dirname(url.fileURLToPath(import.meta.url)); m.spriteFront, m.spriteBack ].join(";")).join("\n"); - const monsterJson = monsters.map(m => ({ - id: m.id, - name: m.name, - description: m.description, - familiy: m.familiy, - level: m.level, - healthPoints: m.healthPoints, - attackPower: m.attackPower, - defensePower: m.defensePower, - spriteDefault: m.spriteDefault, - spriteFront: m.spriteFront, - spriteBack: m.spriteBack, - })) await fs.writeFile(path.join(__dirname, "database_monster.csv"), csv); - await fs.writeFile(path.join(__dirname, "database_monster.json"), JSON.stringify(monsterJson, null, 4)); + await fs.writeFile(path.join(__dirname, "database_monster.json"), JSON.stringify(monsters, null, 4)); })(); diff --git a/main.py b/main.py index f5f56f2..43bc302 100644 --- a/main.py +++ b/main.py @@ -6,12 +6,19 @@ from game.menu import * from game.battle import * from os import path +from sys import argv import traceback def main(state, args): if state is SuspendableInitial: currentDirectory = path.dirname(__file__) - savesDirectory = path.join(currentDirectory, "./data/saves/test-save/") + saveName = string_trim(array_join(argv, " ")) + if string_starts_with(saveName, "\"") and string_ends_with(saveName, "\""): + saveName = string_slice(saveName, 1, -1) + saveName = string_trim(saveName) + if saveName == "": + saveName = "default-save" + savesDirectory = path.join(currentDirectory, path.join("./data/saves/", saveName)) assetsDirectory = path.join(currentDirectory, "./data/assets/") gameState = gamestate_new(savesDirectory) visual = gamestate_get_visual(gameState) diff --git a/pyrightconfig.json b/pyrightconfig.json new file mode 100644 index 0000000..c81501e --- /dev/null +++ b/pyrightconfig.json @@ -0,0 +1,7 @@ +{ + "verboseOutput": true, + "include": [ + "src", + "test" + ] +} diff --git a/src/game/arena/ui_logic.py b/src/game/arena/ui_logic.py index 4da6c43..33f3902 100644 --- a/src/game/arena/ui_logic.py +++ b/src/game/arena/ui_logic.py @@ -20,7 +20,7 @@ def _arena_ui_decode_handler(handler: str) -> Suspendable: args=tuple, promise=Promise ) -__MenuArenaCache = NamedTuple("MenuArena", [ +__MenuArenaCache = NamedTuple("MenuArenaCache", [ ("gameState", GameState), # expect not changed ("parent", View), # expect not changed ("abortSignal", AbortSignal), # expect not changed @@ -93,7 +93,7 @@ def _arena_ui_handler_default(state, args): trainerId = selection trainerMonsters = array_filter(inventory_monster_get_user_monsters(gameState, trainerId), lambda m, *_: m.healthPoints > 0) initialTrainerMonster = trainerMonsters[int(gamestate_rand(gameState) * len(trainerMonsters))] - return "arena:next_stage_new_battle", cache, trainerId, initialTrainerMonster + return "arena:next_stage_new_battle", cache, trainerId, initialTrainerMonster.id if state == "arena:wait_battle": cache, *_ = args return SuspendableReturn, "arena:battle_finished", cache.battleRef["promise"] @@ -136,13 +136,13 @@ def printStats(cache): input("Next stage", selectable=True) input("Selesai", selectable=True) selection = meta(action="select", signal=cache.abortSignal) - return SuspendableReturn, "arena:next_stage_choose", cache, selection, oldArenaId + return SuspendableReturn, "arena:next_stage_choose", selection if state == "arena:next_stage_choose": cache, selection, *_ = args if cache.abortSignal is not None and selection is cache.abortSignal: return "arena:forcefully_aborted", cache if selection == "Next stage": - return "arena:next_stage_check_trainer", cache, oldArenaId + return "arena:next_stage_check_trainer", cache if selection == "Selesai": return SuspendableReturn, "arena:end" if state == "arena:next_stage_check_trainer": @@ -185,8 +185,8 @@ def printStats(cache): selection = meta(action="select", signal=cache.abortSignal) return SuspendableReturn, "arena:next_stage_new_trainer", selection if state == "arena:next_stage_new_trainer": - gameState = cache.gameState cache, selection, *_ = args + gameState = cache.gameState print, input, meta = cache.mainConsole meta("selectableAllowEscape", False) if cache.abortSignal is not None and selection is cache.abortSignal: @@ -207,7 +207,7 @@ def printStats(cache): player1Id=arena.playerId, player2Id=trainerId, monster1Id=None, - monster2Id=monsterId.id, + monster2Id=monsterId, verdict=-1, handler="default_arena_trainer$" )) diff --git a/src/game/battle/ui_logic.py b/src/game/battle/ui_logic.py index 815b451..0f31b08 100644 --- a/src/game/battle/ui_logic.py +++ b/src/game/battle/ui_logic.py @@ -4,6 +4,7 @@ from game.state import * from game.battle import * from game.inventory import * +from game.potion import * from .battle import _battle_end_player_escaped, _battle_end_player_draw, _battle_end_player_won, _battle_action_attack, _battle_set from typing import NamedTuple, Optional, TypedDict, Callable @@ -19,7 +20,7 @@ def _battle_ui_decode_handler(handler: str) -> Suspendable: args=tuple, promise=Promise ) -__MenuBattleCache = NamedTuple("MenuBattle", [ +__MenuBattleCache = NamedTuple("MenuBattleCache", [ ("gameState", GameState), # expect not changed ("parent", View), # expect not changed ("turn", int), # expect not changed @@ -91,6 +92,8 @@ def _battle_ui_handler_wild_monster(state, args): return "battle:forcefully_aborted" if selection == "Attack" and ((cache.turn == 1 and battle.monster1Id is None) or (cache.turn == 2 and battle.monster2Id is None)): return "battle:main#choose_monster", cache, "battle:start_menu#choose_monster_selection" + if selection == "Kabur" and ((cache.turn == 1 and battle.monster1Id is None) or (cache.turn == 2 and battle.monster2Id is None)): + return "battle:action#escape", cache, True return "battle:main#respond", cache, selection if state == "battle:start_menu#choose_monster_selection": cache, choose, *_ = args @@ -302,6 +305,8 @@ def _battle_ui_handler_wild_monster(state, args): if state == "battle:main#choose_monster_selection": cache, intent, selection, modalView, *_ = args view_remove_child(cache.mainView, modalView) + if cache.abortSignal is not None and selection is cache.abortSignal: + return "battle:forcefully_aborted" if selection == None: return intent, cache, "Cancelled" return intent, cache, selection @@ -330,11 +335,56 @@ def _battle_ui_handler_wild_monster(state, args): if state == "battle:action#use_potion": cache, *_ = args print, input, meta = cache.dialogConsole + gameState = cache.gameState + battle = cache.battle + selfMonsterId = battle.monster1Id if cache.turn == 1 else battle.monster2Id if cache.turn == 2 else None + userId = battle.player1Id if cache.turn == 1 else battle.player2Id if cache.turn == 2 else None + userItems = inventory_item_get_user_items(gameState, userId) + userItems = array_filter(userItems, lambda i, *_: i.quantity > 0) + if len(userItems) == 0: + meta(action="clear") + print("Kamu gaada potion yang bisa dipakai") + input("Lanjut", selectable=True) + selection = meta(action="select", signal=cache.abortSignal) + return SuspendableReturn, "battle:main#page2", selection + visual = gamestate_get_visual(gameState) + modalView = visual_show_simple_dialog(visual, "Pilih Potion", "", + x=pos_from_center(), y=pos_from_center(), + width=dim_from_factor(0.5), height=dim_from_factor(0.5), + parent=cache.mainView) + modalConsole = visual_with_mock(visual, modalView) + print, input, meta = modalConsole meta(action="clear") - print("Not implemented") + print("Pilih potion yang ingin digunakan") + for userItem in userItems: + potion = potion_get(gameState, userItem.referenceId) + input(f"{potion.name}", potion.description, id=userItem.id, selectable=True) + selection = meta(action="select", signal=cache.abortSignal) + # Purposefuly undefined behaviour: We expect the database to not change between selection. + return "battle:main#use_potion_selection", cache, selection, modalView + if state == "battle:main#use_potion_selection": + cache, selection, modalView, *_ = args + gameState = cache.gameState + view_remove_child(cache.mainView, modalView) + if cache.abortSignal is not None and selection is cache.abortSignal: + return "battle:forcefully_aborted" + if selection is None: + return SuspendableReturn, "battle:main#page2" + userItem = inventory_item_get(gameState, selection) + userItem = inventory_item_set(gameState, userItem.id, namedtuple_with(userItem, + quantity=userItem.quantity - 1 + )) + potion = potion_get(gameState, userItem.referenceId) + battle = cache.battle + selfMonsterId = battle.monster1Id if cache.turn == 1 else battle.monster2Id if cache.turn == 2 else None + selfMonster = inventory_monster_get(gameState, selfMonsterId) + selfMonster = inventory_monster_use_potion(gameState, selfMonster, potion) + print, input, meta = cache.dialogConsole + meta(action="clear") + print(f"Kamu memakai potion '{potion.name}'") input("Lanjut", selectable=True) selection = meta(action="select", signal=cache.abortSignal) - return SuspendableReturn, "battle:main", selection + return SuspendableReturn, "battle:main#page2", selection if state == "battle:action#change_monster": cache, *_ = args return "battle:main#choose_monster", cache, "battle:action#change_monster_selection" diff --git a/src/game/generateinit.mjs b/src/game/generateinit.mjs index 663b2c7..3b74982 100644 --- a/src/game/generateinit.mjs +++ b/src/game/generateinit.mjs @@ -16,13 +16,15 @@ const __dirname = process.cwd(); for(const file of files) { const moduleName = path.basename(file).split(".")[0]; const contents = await fs.readFile(file); - const regex = /^(?:(_{0,1}[a-zA-Z][^\s]*)\s*=|def\s*(_{0,1}[a-zA-Z][^\s(]*))/gm; + const regex = /^(?:(_{0,1}[a-zA-Z][^\.\s]*)\s*=|def\s*(_{0,1}[a-zA-Z][^\.\s(]*))/gm; const exports = []; let matcher; while((matcher = regex.exec(contents)) != null) exports.push(matcher[1] || matcher[2]); - if(exports.length == 0) + if(exports.length == 0) { + // result += `__import__("${moduleName}", globals(), locals(), [], level=1)`; continue + } result += `from .${moduleName} import ${exports.join(", ")}\n\n`; result += `${exports.map(e => `${e.startsWith("_") ? e.slice(1) : e} = ${e}`).join("\n")}\n\n` } diff --git a/src/game/inventory/__init__.py b/src/game/inventory/__init__.py index f4b60bd..ca23f5c 100644 --- a/src/game/inventory/__init__.py +++ b/src/game/inventory/__init__.py @@ -1,4 +1,4 @@ -from .inventory import _inventory_monster_get, _inventory_monster_set, _inventory_monster_new, _inventory_monster_get_user_monsters, _inventory_monster_get_calculated_health_points, _inventory_monster_get_calculated_attack_power, _inventory_monster_get_calculated_defense_power, _inventory_monster_use_potion, _inventory_monster_tick +from .inventory import _inventory_monster_get, _inventory_monster_set, _inventory_monster_new, _inventory_monster_get_user_monsters, _inventory_monster_get_calculated_health_points, _inventory_monster_get_calculated_attack_power, _inventory_monster_get_calculated_defense_power, _inventory_monster_use_potion, _inventory_monster_tick, _inventory_item_get, _inventory_item_set, _inventory_item_new, _inventory_item_get_user_items inventory_monster_get = _inventory_monster_get inventory_monster_set = _inventory_monster_set @@ -9,3 +9,7 @@ inventory_monster_get_calculated_defense_power = _inventory_monster_get_calculated_defense_power inventory_monster_use_potion = _inventory_monster_use_potion inventory_monster_tick = _inventory_monster_tick +inventory_item_get = _inventory_item_get +inventory_item_set = _inventory_item_set +inventory_item_new = _inventory_item_new +inventory_item_get_user_items = _inventory_item_get_user_items diff --git a/src/game/inventory/inventory.py b/src/game/inventory/inventory.py index 7ca51cc..71f00d2 100644 --- a/src/game/inventory/inventory.py +++ b/src/game/inventory/inventory.py @@ -112,3 +112,30 @@ def __tick_potion(gameTime: float, activePotion: tuple[float, float, PotionSchem return (additionalHealthPoints, additionalAttackPower, additionalDefensePower, newActivePotion) # immediate permanent buffs: set type="HealthIndefinite", duration=1 + +def _inventory_item_get(gameState: GameState, inventoryItemId: int) -> InventoryItemSchemaType: + inventoryItemDatabase = gamestate_get_inventory_item_database(gameState) + return database_get_entry_at(inventoryItemDatabase, inventoryItemId) + +def _inventory_item_set(gameState: GameState, inventoryItemId: int, modifier: Union[InventoryItemSchemaType, Callable[[InventoryItemSchemaType], InventoryItemSchemaType]]) -> InventoryItemSchemaType: + inventoryItemDatabase = gamestate_get_inventory_item_database(gameState) + inventoryItem = modifier(database_get_entry_at(inventoryItemDatabase, inventoryItemId)) if callable(modifier) else modifier + database_set_entry_at(inventoryItemDatabase, inventoryItemId, inventoryItem) + return inventoryItem + +def _inventory_item_new(gameState: GameState) -> InventoryItemSchemaType: + inventoryItemDatabase = gamestate_get_inventory_item_database(gameState) + inventoryItemId = database_get_entries_length(inventoryItemDatabase) + inventoryItem = InventoryItemSchemaType( + id=inventoryItemId, + ownerId=None, + referenceId=None, + quantity=None, + ) + database_set_entry_at(inventoryItemDatabase, inventoryItemId, inventoryItem) + return inventoryItem + +def _inventory_item_get_user_items(gameState: GameState, userId: int) -> list[InventoryItemSchemaType]: + inventoryItemDatabase = gamestate_get_inventory_item_database(gameState) + inventoryItemEntries = database_get_entries(inventoryItemDatabase) + return array_filter(inventoryItemEntries, lambda m, *_: m.ownerId == userId) diff --git a/src/game/laboratory/__init__.py b/src/game/laboratory/__init__.py index 8b13789..49eccce 100644 --- a/src/game/laboratory/__init__.py +++ b/src/game/laboratory/__init__.py @@ -1 +1,6 @@ +from .laboratory import _laboratory_get, _laboratory_set, _laboratory_new, _laboratory_get_all_upgrades +laboratory_get = _laboratory_get +laboratory_set = _laboratory_set +laboratory_new = _laboratory_new +laboratory_get_all_upgrades = _laboratory_get_all_upgrades diff --git a/src/game/laboratory/laboratory.py b/src/game/laboratory/laboratory.py index e69de29..852fec9 100644 --- a/src/game/laboratory/laboratory.py +++ b/src/game/laboratory/laboratory.py @@ -0,0 +1,31 @@ +from utils.primordials import * +from game.state import * +from game.database import * +from typing import Callable + +def _laboratory_get(gameState: GameState, laboratoryId: int) -> LaboratorySchemaType: + laboratoryDatabase = gamestate_get_laboratory_database(gameState) + return database_get_entry_at(laboratoryDatabase, laboratoryId) + +def _laboratory_set(gameState: GameState, laboratoryId: int, modifier: Callable[[LaboratorySchemaType], LaboratorySchemaType]) -> LaboratorySchemaType: + laboratoryDatabase = gamestate_get_laboratory_database(gameState) + laboratory = modifier(database_get_entry_at(laboratoryDatabase, laboratoryId)) + database_set_entry_at(laboratoryDatabase, laboratoryId, laboratory) + return laboratory + +def _laboratory_new(gameState: GameState) -> LaboratorySchemaType: + laboratoryDatabase = gamestate_get_laboratory_database(gameState) + laboratoryId = database_get_entries_length(laboratoryDatabase) + laboratory = LaboratorySchemaType( + id=laboratoryId, + fromMonsterId=None, + toMonsterId=None, + cost=None, + ) + database_set_entry_at(laboratoryDatabase, laboratoryId, laboratory) + return laboratory + +def _laboratory_get_all_upgrades(gameState: GameState) -> list[LaboratorySchemaType]: + laboratoryDatabase = gamestate_get_laboratory_database(gameState) + laboratoryEntries = database_get_entries(laboratoryDatabase) + return laboratoryEntries diff --git a/src/game/menu/__init__.py b/src/game/menu/__init__.py index 14f2b1b..0b33d18 100644 --- a/src/game/menu/__init__.py +++ b/src/game/menu/__init__.py @@ -13,12 +13,22 @@ menu_show_battle = _menu_show_battle -from .ui_debug import _menu_show_debug_test, _menu_toggle_coroutines_stats, _menu_show_debug_battle, _menu_show_debug_arena +from .ui_debug import _menu_show_debug_test, _menu_toggle_coroutines_stats, _menu_show_debug_battle, _menu_show_debug_arena, _menu_show_debug_shop, _menu_show_debug_laboratory menu_show_debug_test = _menu_show_debug_test menu_toggle_coroutines_stats = _menu_toggle_coroutines_stats menu_show_debug_battle = _menu_show_debug_battle menu_show_debug_arena = _menu_show_debug_arena +menu_show_debug_shop = _menu_show_debug_shop +menu_show_debug_laboratory = _menu_show_debug_laboratory + +from .ui_laboratory import _menu_show_laboratory + +menu_show_laboratory = _menu_show_laboratory + +from .ui_shop import _menu_show_shop + +menu_show_shop = _menu_show_shop from .ui_user import _menu_show_login, _menu_show_register, _menu_show_logout diff --git a/src/game/menu/menu.py b/src/game/menu/menu.py index 92acddb..5a32ec4 100644 --- a/src/game/menu/menu.py +++ b/src/game/menu/menu.py @@ -21,6 +21,11 @@ def progress(value): def _menu_show_loading_splash(state, args): if state is SuspendableInitial: gameState, splashes = args + if type(splashes) is dict: + newSplashes = [] + for key, value in splashes.items(): + array_push(newSplashes, (key, value)) + splashes = newSplashes visual = gamestate_get_visual(gameState) loadingScreen = visual_show_splash(visual, "loading1.gif.txt") loadingScreen["play"](60, True) @@ -78,7 +83,10 @@ def _menu_show_main_menu(state, args): gameState, console, user = args print, input, meta = console print(f"Halo Agent {user.username}. Kamu memanggil command HELP. Kamu memilih jalan yang benar, semoga kamu tidak sesat kemudian. Berikut adalah hal-hal yang dapat kamu lakukan sekarang:") - input("Play", "Memulai permainan", selectable=True) + input("Battle Wild Monster", "Memulai battle dengan monster liar", selectable=True) + input("Battle Arena", "Memulai battle dengan trainer", selectable=True) + input("Inventory", "Melihat isi inventory mu", selectable=True) + input("Shop", "Membuka shop untuk beli monster/item", selectable=True) input("Logout", "Keluar dari akun yang sedang digunakan", selectable=True) input("Exit", selectable=True) selection = meta(action="select") @@ -87,8 +95,14 @@ def _menu_show_main_menu(state, args): gameState, console, selection = args if selection is None: return SuspendableInitial, gameState, console - if selection == "Play": - return SuspendableReturn, "agent:play" + if selection == "Battle Wild Monster": + return SuspendableReturn, "agent:battle_wild_monster" + if selection == "Battle Arena": + return SuspendableReturn, "agent:battle_arena" + if selection == "Inventory": + return SuspendableReturn, "agent:inventory" + if selection == "Shop": + return SuspendableReturn, "agent:shop" if selection == "Logout": return SuspendableReturn, "agent:logout" if selection == "Exit": @@ -98,6 +112,7 @@ def _menu_show_main_menu(state, args): print, input, meta = console print("Selamat datang, Admin. Berikut adalah hal-hal yang dapat kamu lakukan:") input("Shop Management", "Melakukan manajemen pada SHOP sebagai tempat jual beli peralatan Agent", selectable=True) + input("Monster Management", "Melakukan manajemen pada MONSTER", selectable=True) input("Debug Test", "Kumpulan debug untuk testing program", selectable=True) input("Logout", "Keluar dari akun yang sedang digunakan", selectable=True) input("Exit", selectable=True) @@ -109,6 +124,8 @@ def _menu_show_main_menu(state, args): return SuspendableInitial, gameState, console if selection == "Shop Management": return SuspendableReturn, "admin:shop_management" + if selection == "Monster Management": + return SuspendableReturn, "admin:monster_management" if selection == "Debug Test": return SuspendableReturn, "admin:debug_test" if selection == "Logout": diff --git a/src/game/menu/ui_arena.py b/src/game/menu/ui_arena.py index dd58c79..f2f1945 100644 --- a/src/game/menu/ui_arena.py +++ b/src/game/menu/ui_arena.py @@ -12,7 +12,7 @@ args=tuple, promise=Promise ) -__MenuArenaCache = NamedTuple("MenuArena", [ +__MenuArenaCache = NamedTuple("MenuArenaCache", [ ("gameState", GameState), # expect not changed ("parent", View), # expect not changed ("abortSignal", AbortSignal), # expect not changed diff --git a/src/game/menu/ui_battle.py b/src/game/menu/ui_battle.py index b9807b9..be1bf12 100644 --- a/src/game/menu/ui_battle.py +++ b/src/game/menu/ui_battle.py @@ -13,7 +13,7 @@ args=tuple, promise=Promise ) -__MenuBattleCache = NamedTuple("MenuBattle", [ +__MenuBattleCache = NamedTuple("MenuBattleCache", [ ("gameState", GameState), # expect not changed ("parent", View), # expect not changed ("turn", int), # expect not changed @@ -164,7 +164,7 @@ def _menu_show_battle(state, args): opponentMonsterFrame = visual_show_simple_dialog(visual, None, "", x=pos_from_end(None), y=pos_from_absolute(2), - width=dim_from_factor(0.5), height=dim_from_factor(0.5), + width=dim_from_factor(0.5), height=dim_from_factor(0.6), border=(0, 0, 0, 0), padding=(0, 0, 0, 0), parent=battleView) @@ -189,7 +189,7 @@ def loop(): selfMonsterFrame = visual_show_simple_dialog(visual, None, "", x=pos_from_absolute(0), y=pos_sub(pos_from_end(None), pos_from_absolute(2)), - width=dim_from_factor(0.5), height=dim_from_factor(0.5), + width=dim_from_factor(0.5), height=dim_from_factor(0.6), border=(0, 0, 0, 0), padding=(0, 0, 0, 0), parent=battleView) @@ -203,7 +203,7 @@ def loop(): if frame < totalFrame: middleFrame = frame if frame <= totalFrame / 2 else totalFrame - frame view_set_x(selfMonsterFrame, pos_from_factor(middleFrame / totalFrame * 0.5)) - view_set_y(selfMonsterFrame, pos_sub(pos_from_end(None), pos_from_factor(middleFrame / totalFrame * 0.5))) + view_set_y(selfMonsterFrame, pos_sub(pos_sub(pos_from_end(None), pos_from_absolute(2)), pos_from_factor(middleFrame / totalFrame * 0.5))) return view_set_x(selfMonsterFrame, pos_from_absolute(0)) view_set_y(selfMonsterFrame, pos_sub(pos_from_end(None), pos_from_absolute(2))) diff --git a/src/game/menu/ui_debug.py b/src/game/menu/ui_debug.py index 78b98b7..c4f2ff6 100644 --- a/src/game/menu/ui_debug.py +++ b/src/game/menu/ui_debug.py @@ -8,6 +8,8 @@ from game.arena import * from .ui_battle import _menu_show_battle from .ui_arena import _menu_show_arena +from .ui_shop import _menu_show_shop +from .ui_laboratory import _menu_show_laboratory import time def _menu_show_debug_test(state, args): @@ -24,6 +26,8 @@ def _menu_show_debug_test(state, args): input("Coroutines Stats", selectable=True) input("Battle Test", selectable=True) input("Arena Test", selectable=True) + input("Shop Test", selectable=True) + input("Laboratory Test", selectable=True) selection = meta(action="select") return 2, gameState, console, selection if state == 2: @@ -36,13 +40,17 @@ def _menu_show_debug_test(state, args): return SuspendableReturn, None, promise_from_suspendable(_menu_show_debug_battle, gameState) if selection == "Arena Test": return SuspendableReturn, None, promise_from_suspendable(_menu_show_debug_arena, gameState) + if selection == "Shop Test": + return SuspendableReturn, None, promise_from_suspendable(_menu_show_debug_shop, gameState) + if selection == "Laboratory Test": + return SuspendableReturn, None, promise_from_suspendable(_menu_show_debug_laboratory, gameState) return SuspendableExhausted def _menu_toggle_coroutines_stats(state, args): if state is SuspendableInitial: gameState, = args visual = gamestate_get_visual(gameState) - if "__coroutines_stats_view" in visual and visual["__coroutines_stats_view"] is not None: + if "__debug_coroutines_stats" in visual and visual["__debug_coroutines_stats"] is not None: return 2, gameState return 1, gameState if state == 1: @@ -53,7 +61,7 @@ def _menu_toggle_coroutines_stats(state, args): tempView = view_new(driver) statsView = visual_show_simple_dialog(visual, None, "", x=pos_from_absolute(0), y=pos_from_absolute(0), - width=dim_from_absolute(25), height=dim_from_absolute(7), + width=dim_from_absolute(25), height=dim_from_absolute(8), padding=(0, 0, 0, 0), parent=tempView) refController = dict( statsView=statsView, @@ -84,6 +92,7 @@ def update(): Pollables length: {len(looper["pollables"])} Immediates length: {len(looper["immediates"])} Microtasks length: {len(looper["microtasks"])} + Timer ID Counter: {looper["timerIdCounter"]} """), "\n"), lambda l, *_: string_trim(l)), "\n")) if frame % 7 == 0: next_tick(update) @@ -166,5 +175,21 @@ def _menu_show_debug_arena(state, args): return SuspendableReturn, arenaView return SuspendableExhausted +def _menu_show_debug_shop(state, args): + if state is SuspendableInitial: + gameState, = args + background = None + shopView = promise_from_suspendable(_menu_show_shop, gameState, background, None) + return SuspendableReturn, shopView + return SuspendableExhausted + +def _menu_show_debug_laboratory(state, args): + if state is SuspendableInitial: + gameState, = args + background = None + laboratoryView = promise_from_suspendable(_menu_show_laboratory, gameState, background, None) + return SuspendableReturn, laboratoryView + return SuspendableExhausted + def __now() -> float: return time.monotonic() * 1000 diff --git a/src/game/menu/ui_laboratory.py b/src/game/menu/ui_laboratory.py new file mode 100644 index 0000000..faac075 --- /dev/null +++ b/src/game/menu/ui_laboratory.py @@ -0,0 +1,412 @@ +from utils.primordials import * +from utils.coroutines import * +from utils.console import * +from game.state import * +from game.laboratory import * +from game.monster import * +from game.user import * +from game.inventory import * +from .menu import _menu_show_loading_splash +from typing import NamedTuple + +__MenuLaboratoryCache = NamedTuple("MenuLaboratoryCache", [ + ("gameState", GameState), # expect not changed + ("parent", View), # expect not changed + ("abortSignal", AbortSignal), # expect not changed + ("upgradeOptions", list[LaboratorySchemaType]), + ("mainView", View), # expect not changed + ("upgradeView", View), # expect not changed + ("beforePreviewFrame", View), # expect not changed + ("beforePreviewAnimation", View), + ("beforeDescriptionView", View), # expect not changed + ("afterPreviewFrame", View), # expect not changed + ("afterPreviewAnimation", View), + ("afterDescriptionView", View), # expect not changed + ("tableView", View), # expect not changed + ("upgradeDialogView", View), # expect not changed + ("upgradeDialogConsole", ConsoleMock), # expect not changed + ("lastInputPosition", int), + ("inputPosition", int), + ("lastTableOffset", int), + ("tableOffset", int), + ("tableLoadIndex", int), +]) + +def _menu_show_laboratory(state, args): + cache: __MenuLaboratoryCache = None + tableMaxShown = 3 + if state is SuspendableInitial: + gameState, parent, abortSignal = args + userId = gamestate_get_user_id(gameState) + userMonsters = inventory_monster_get_user_monsters(gameState, userId) + userMonsters = array_map(userMonsters, lambda m, *_: m.referenceId) + upgradeOptions = laboratory_get_all_upgrades(gameState) + upgradeOptions = array_filter(upgradeOptions, lambda u, *_: array_includes(userMonsters, u.fromMonsterId)) + cache = __MenuLaboratoryCache( + gameState=gameState, + parent=parent, + abortSignal=abortSignal, + upgradeOptions=upgradeOptions, + mainView=None, + upgradeView=None, + beforePreviewFrame=None, + beforePreviewAnimation=None, + beforeDescriptionView=None, + afterPreviewFrame=None, + afterPreviewAnimation=None, + afterDescriptionView=None, + tableView=None, + upgradeDialogView=None, + upgradeDialogConsole=None, + lastInputPosition=None, + inputPosition=-1, + lastTableOffset=None, + tableOffset=0, + tableLoadIndex=None, + ) + return "initInterface", cache, "checkTableLoad" + if state == "initInterface": + cache, intent, *_ = args + gameState = cache.gameState + visual = gamestate_get_visual(gameState) + driver = visual_get_driver(visual) + dumpView = view_new(driver) + + mainView = visual_show_simple_dialog(visual, "LABORATORY", "", + x=pos_from_center(), y=pos_from_center(), + width=dim_from_factor(0.8), #height=dim_from_absolute(3 + 2 * (2 + tableMaxShown) + 2 + 24), + height=dim_from_factor(0.8), + parent=cache.parent) + mainView["setContent"](" OWCA: 0") + + tableView = visual_show_table(visual, + [ + ("No.", dim_from_absolute(5), "Center"), + ("Nama", dim_from_factor(0.3), "Right", (1, 0, 1, 0)), + ("HP", dim_sub(dim_from_factor(0.167), dim_from_absolute(2)), "Center"), + ("ATK", dim_sub(dim_from_factor(0.167), dim_from_absolute(2)), "Center"), + ("DEF", dim_sub(dim_from_factor(0.167), dim_from_absolute(2)), "Center"), + ("Harga", dim_from_fill(0), "Center")], + [], + x=pos_from_absolute(0), y=pos_from_end(None), + width=dim_from_fill(0), height=dim_from_absolute(3 + 2 * (2 + tableMaxShown)), + parent=mainView["contentView"]) + tableView["selectable"](True) + + upgradeView = visual_show_simple_dialog(visual, None, "", + x=pos_from_absolute(0), y=pos_from_absolute(2), + width=dim_from_fill(0), height=dim_sub(dim_from_fill(0), dim_from_view_height(tableView)), + border=(0, 0, 0, 0), padding=(0, 0, 0, 0), + parent=mainView["contentView"]) + + beforePreviewFrame = visual_show_simple_dialog(visual, None, "", + x=pos_from_absolute(0), y=pos_from_absolute(0), + width=dim_from_factor(0.5), height=dim_from_factor(0.7), + border=(1, 1, 1, 1), padding=(0, 0, 0, 0), + parent=upgradeView) + + beforePreviewAnimation = None + + beforeDescriptionView = visual_show_simple_dialog(visual, None, "", + x=pos_from_absolute(0), y=pos_from_view_bottom(beforePreviewFrame), + width=dim_from_factor(0.5), height=dim_from_fill(0), + parent=upgradeView) + + afterPreviewFrame = visual_show_simple_dialog(visual, None, "", + x=pos_from_view_right(beforePreviewFrame), y=pos_from_absolute(0), + width=dim_from_fill(0), height=dim_from_factor(0.7), + border=(1, 1, 1, 1), padding=(0, 0, 0, 0), + parent=upgradeView) + + afterPreviewAnimation = None + + afterDescriptionView = visual_show_simple_dialog(visual, None, "", + x=pos_from_view_right(beforePreviewFrame), y=pos_from_view_bottom(afterPreviewFrame), + width=dim_from_fill(0), height=dim_from_fill(0), + parent=upgradeView) + + upgradeDialogView = visual_show_simple_dialog(visual, "UPGRADE", "", + x=pos_from_center(), y=pos_from_center(), + width=dim_from_factor(0.5), height=dim_from_factor(0.5), + parent=dumpView) + upgradeDialogConsole = visual_with_mock(visual, upgradeDialogView, + hasTitle=True) + + cache = namedtuple_with(cache, + mainView=mainView, + upgradeView=upgradeView, + beforePreviewFrame=beforePreviewFrame, + beforePreviewAnimation=beforePreviewAnimation, + beforeDescriptionView=beforeDescriptionView, + afterPreviewFrame=afterPreviewFrame, + afterPreviewAnimation=afterPreviewAnimation, + afterDescriptionView=afterDescriptionView, + tableView=tableView, + upgradeDialogView=upgradeDialogView, + upgradeDialogConsole=upgradeDialogConsole, + ) + return intent, cache + if state == "checkTableLoad": + cache, *_ = args + gameState = cache.gameState + tableLoadIndex = (cache.tableOffset + tableMaxShown - 1) // tableMaxShown + if cache.tableLoadIndex is not None and tableLoadIndex <= cache.tableLoadIndex: + visual = gamestate_get_visual(gameState) + if cache.parent is not None: + visual_set_view(visual, visual_get_root_view(visual, cache.parent)) + else: + visual_set_view(visual, cache.mainView) + return "updateTableEntries", cache + startIndex = tableLoadIndex * tableMaxShown + endIndex = startIndex + tableMaxShown + def getSpriteFor(upgradeOption: LaboratorySchemaType): + beforeMonster, afterMonster = __resolve_laboratory_upgrade(gameState, upgradeOption) + return [beforeMonster.spriteFront, afterMonster.spriteFront] + loadUpgrades = array_slice(cache.upgradeOptions, startIndex, endIndex) + loadUpgradeSprites = array_map(loadUpgrades, lambda i, *_: getSpriteFor(i)) + loadUpgradeSprites = array_flat(loadUpgradeSprites) + loadUpgradeSprites = array_map(loadUpgradeSprites, lambda s, *_: (f"sprite {s}", s)) + loadUpgradeSprites = dict_set(dict(), loadUpgradeSprites) + promise = promise_from_suspendable(_menu_show_loading_splash, gameState, loadUpgradeSprites) + cache = namedtuple_with(cache, + tableLoadIndex=tableLoadIndex + ) + return "checkTableLoad", cache, promise + if state == "updateTableEntries": + cache, *_ = args + if cache.tableOffset == cache.lastTableOffset: + return "updateUpgradeView", cache + gameState = cache.gameState + tableView = cache.tableView + startIndex = cache.tableOffset + endIndex = startIndex + tableMaxShown + upgradeRows = array_slice(cache.upgradeOptions, startIndex, endIndex) + def getRowFor(i: int, upgradeOption: LaboratorySchemaType): + beforeMonster, afterMonster = __resolve_laboratory_upgrade(gameState, upgradeOption) + hpRatio = (afterMonster.healthPoints - beforeMonster.healthPoints) / beforeMonster.healthPoints + atkRatio = (afterMonster.attackPower - beforeMonster.attackPower) / beforeMonster.attackPower + defRatio = (afterMonster.defensePower - beforeMonster.defensePower) / beforeMonster.defensePower + rowName = f"{beforeMonster.name} ({beforeMonster.level}) --> ({afterMonster.level}) {afterMonster.name}" + rowHp = f"{beforeMonster.healthPoints} {'+' if hpRatio >= 0 else '-'}{hpRatio * 100:.2f}%" + rowAtk = f"{beforeMonster.attackPower} {'+' if atkRatio >= 0 else '-'}{atkRatio * 100:.2f}%" + rowDef = f"{beforeMonster.defensePower} {'+' if defRatio >= 0 else '-'}{defRatio * 100:.2f}%" + return [f"{startIndex + i + 1}", rowName, rowHp, rowAtk, rowDef, f"{upgradeOption.cost}"] + upgradeRows = array_map(upgradeRows, lambda u, i, *_: getRowFor(i, u)) + tableView["updateRows"]([ + ["▲▲▲", "▲▲▲▲▲▲", "▲▲▲▲▲▲", "▲▲▲▲▲▲", "▲▲▲▲▲▲", "▲▲▲▲▲▲"], + *upgradeRows, + ["▼▼▼", "▼▼▼▼▼▼", "▼▼▼▼▼▼", "▼▼▼▼▼▼", "▼▼▼▼▼▼", "▼▼▼▼▼▼"], + ]) + cache = namedtuple_with(cache, + lastTableOffset=cache.tableOffset + ) + return "updateUpgradeView", cache + if state == "updateUpgradeView": + cache, *_ = args + gameState = cache.gameState + mainView = cache.mainView + userMoney = user_get_current(gameState).money + mainView["setContent"](f" OWCA: {userMoney}") + if cache.inputPosition == cache.lastInputPosition: + return "waitInput", cache + visual = gamestate_get_visual(gameState) + inputPosition = cache.inputPosition + beforePreviewFrame = cache.beforePreviewFrame + beforePreviewAnimation = cache.beforePreviewAnimation + beforeDescriptionView = cache.beforeDescriptionView + afterPreviewFrame = cache.afterPreviewFrame + afterPreviewAnimation = cache.afterPreviewAnimation + afterDescriptionView = cache.afterDescriptionView + if beforePreviewAnimation is not None: + beforePreviewAnimation["stop"]() + view_remove_child(beforePreviewFrame, beforePreviewAnimation) + beforePreviewAnimation = None + if afterPreviewAnimation is not None: + afterPreviewAnimation["stop"]() + view_remove_child(afterPreviewFrame, afterPreviewAnimation) + afterPreviewAnimation = None + if inputPosition < -1: + inputPosition = -1 + if inputPosition >= len(cache.upgradeOptions): + inputPosition = len(cache.upgradeOptions) - 1 + if inputPosition != -1: + upgradeOption = cache.upgradeOptions[inputPosition] + beforeMonster, afterMonster = __resolve_laboratory_upgrade(gameState, upgradeOption) + beforePreviewAnimation = visual_show_splash(visual, beforeMonster.spriteFront, parent=beforePreviewFrame) + beforePreviewAnimation["play"](60, True) + beforeDescriptionView["setContent"](f"Nama: {beforeMonster.name} ({beforeMonster.level})\nHP: {beforeMonster.healthPoints}\nATK: {beforeMonster.attackPower}\nDEF: {beforeMonster.defensePower}") + afterPreviewAnimation = visual_show_splash(visual, afterMonster.spriteFront, parent=afterPreviewFrame) + afterPreviewAnimation["play"](60, True) + afterDescriptionView["setContent"](f"Nama: {afterMonster.name} ({afterMonster.level})\nHP: {afterMonster.healthPoints}\nATK: {afterMonster.attackPower}\nDEF: {afterMonster.defensePower}") + cache = namedtuple_with(cache, + lastInputPosition=inputPosition, + beforePreviewAnimation=beforePreviewAnimation, + afterPreviewAnimation=afterPreviewAnimation, + ) + return "waitInput", cache + if state == "waitInput": + cache, *_ = args + gameState = cache.gameState + visual = gamestate_get_visual(gameState) + tableView = cache.tableView + def executor(resolve, _): + def cleanup(): + tableView["onHover"](None) + tableView["onEnter"](None) + visual_remove_key_listener(visual, tableView, onKey) + def onHover(index): + cleanup() + resolve((index, "hover")) + def onEnter(index): + cleanup() + resolve((index, "enter")) + def onKey(event: KeyEvent): + if event.key == "ControlESC": + cleanup() + resolve((None, "escape")) + tableView["onHover"](onHover) + tableView["onEnter"](onEnter) + visual_add_key_listener(visual, tableView, onKey) + promise = promise_new(executor) + return "processInput", cache, promise + if state == "processInput": + cache, inputAction, *_ = args + tableView = cache.tableView + position, action = inputAction + if action == "escape": + return SuspendableReturn, None + tableOffset = cache.tableOffset + inputPosition = tableOffset + (position - 1) + if position == 0: + tableView["setSelection"](1) + tableOffset = max(0, tableOffset - 1) + inputPosition = tableOffset + position + cache = namedtuple_with(cache, + tableOffset=tableOffset, + inputPosition=inputPosition, + ) + return "checkTableLoad", cache + if position == min(len(cache.upgradeOptions) + 1, tableMaxShown + 1): + tableView["setSelection"](min(len(cache.upgradeOptions), tableMaxShown)) + tableOffset = min(max(0, len(cache.upgradeOptions) - tableMaxShown), tableOffset + 1) + inputPosition = tableOffset + (min(len(cache.upgradeOptions), tableMaxShown) - 1) + cache = namedtuple_with(cache, + tableOffset=tableOffset, + inputPosition=inputPosition, + ) + return "checkTableLoad", cache + cache = namedtuple_with(cache, + inputPosition=inputPosition + ) + if action == "enter": + return "upgradeDialog", cache + return "updateUpgradeView", cache + if state == "upgradeDialog": + cache, *_ = args + gameState = cache.gameState + mainView = cache.mainView + tableView = cache.tableView + upgradeDialogView = cache.upgradeDialogView + tableView["selectable"](False) + view_add_child(mainView["contentView"], upgradeDialogView) + userMoney = user_get_current(gameState).money + upgradeOption = cache.upgradeOptions[cache.inputPosition] + beforeMonster, afterMonster = __resolve_laboratory_upgrade(gameState, upgradeOption) + print, input, meta = cache.upgradeDialogConsole + meta(action="clear") + print(f"==== Upgrade {beforeMonster.name} ({beforeMonster.level}) --> ({afterMonster.level}) {afterMonster.name} ====") + if userMoney < upgradeOption.cost: + print("Uangmu tidak cukup untuk melakukan upgrade ini.") + input("Lanjut", selectable=True) + selection = meta(action="select") + return "upgradeDialogConfirmChoose", cache, None, "Batal", selection + print("Pilih monster yang ingin di upgrade.") + userId = gamestate_get_user_id(gameState) + userMonsters = inventory_monster_get_user_monsters(gameState, userId) + userMonsters = array_filter(userMonsters, lambda m, *_: m.referenceId == upgradeOption.fromMonsterId) + for userMonster in userMonsters: + targetHp = max(userMonster.healthPoints, afterMonster.healthPoints) + targetAtk = max(userMonster.attackPower, afterMonster.attackPower) + targetDef = max(userMonster.defensePower, afterMonster.defensePower) + hpRatio = (targetHp - userMonster.healthPoints) / userMonster.healthPoints + atkRatio = (targetAtk - userMonster.attackPower) / userMonster.attackPower + defRatio = (targetDef - userMonster.defensePower) / userMonster.defensePower + rowHp = f"{userMonster.healthPoints} {'+' if hpRatio >= 0 else '-'}{hpRatio * 100:.2f}%" + rowAtk = f"{userMonster.attackPower} {'+' if atkRatio >= 0 else '-'}{atkRatio * 100:.2f}%" + rowDef = f"{userMonster.defensePower} {'+' if defRatio >= 0 else '-'}{defRatio * 100:.2f}%" + description = f"HP: {rowHp} ATK: {rowAtk} DEF: {rowDef}" + input(f"{userMonster.name}", description, id=userMonster.id, selectable=True) + selection = meta(action="select") + return "upgradeDialogConfirm", cache, selection + if state == "upgradeDialogConfirm": + cache, monsterId, *_ = args + if monsterId is None: + return "upgradeDialogConfirmChoose", cache, None, "Batal" + gameState = cache.gameState + userMoney = user_get_current(gameState).money + userMonster = inventory_monster_get(gameState, monsterId) + upgradeOption = cache.upgradeOptions[cache.inputPosition] + beforeMonster, afterMonster = __resolve_laboratory_upgrade(gameState, upgradeOption) + print, input, meta = cache.upgradeDialogConsole + meta(action="clear") + if userMoney < upgradeOption.cost: + print(f"Uangmu tidak cukup. ", end="") + input("Lanjut", selectable=True) + selection = meta(action="select") + return "upgradeDialogConfirmChoose", cache, None, "Batal", selection + targetHp = max(userMonster.healthPoints, afterMonster.healthPoints) + targetAtk = max(userMonster.attackPower, afterMonster.attackPower) + targetDef = max(userMonster.defensePower, afterMonster.defensePower) + meta("keySpeed", 120) + print(f"OWCA mu saat ini terdapat {userMoney}. ", end="") + print(f"Kamu akan mengupgrade '{userMonster.name}' dari level {beforeMonster.level} ke level {afterMonster.level} dengan harga {upgradeOption.cost}. ", end="") + print(f"Di akhir transaksi OWCA mu akan tersisa {userMoney - upgradeOption.cost}. Dan '{userMonster.name}' akan memiliki HP: {targetHp} ATK: {targetAtk} DEF: {targetDef}") + input("Konfirmasi", selectable=True) + input("Batal", selectable=True) + selection = meta(action="select") + return "upgradeDialogConfirmChoose", cache, monsterId, selection + if state == "upgradeDialogConfirmChoose": + cache, monsterId, selection, *_ = args + print, input, meta = cache.upgradeDialogConsole + meta("keySpeed", -1) + if selection is None: + return "upgradeDialog", cache + gameState = cache.gameState + mainView = cache.mainView + tableView = cache.tableView + upgradeDialogView = cache.upgradeDialogView + view_remove_child(mainView["contentView"], upgradeDialogView) + tableView["selectable"](True) + if selection == "Batal": + return "updateUpgradeView", cache + userMonster = inventory_monster_get(gameState, monsterId) + upgradeOption = cache.upgradeOptions[cache.inputPosition] + _, afterMonster = __resolve_laboratory_upgrade(gameState, upgradeOption) + currentUser = user_get_current(gameState) + userMoney = currentUser.money - upgradeOption.cost + currentUser = user_set(gameState, currentUser.id, namedtuple_with(currentUser, money=userMoney)) + targetHp = max(userMonster.healthPoints, afterMonster.healthPoints) + targetAtk = max(userMonster.attackPower, afterMonster.attackPower) + targetDef = max(userMonster.defensePower, afterMonster.defensePower) + userMonster = inventory_monster_set(gameState, userMonster.id, namedtuple_with(userMonster, + referenceId=afterMonster.id, + healthPoints=targetHp, + attackPower=targetAtk, + defensePower=targetDef, + )) + return "reloadAll", cache + if state == "reloadAll": # I am too lazy, sorry. + cache, *_ = args + gameState = cache.gameState + visual = gamestate_get_visual(gameState) + if cache.parent is not None: + view_remove_child(cache.parent, cache.mainView) + else: + visual_set_view(visual, None) + return SuspendableInitial, cache.gameState, cache.parent, cache.abortSignal + return SuspendableExhausted + +def __resolve_laboratory_upgrade(gameState: GameState, upgradeOption: LaboratorySchemaType): + beforeMonster = monster_get(gameState, upgradeOption.fromMonsterId) + afterMonster = monster_get(gameState, upgradeOption.toMonsterId) + return (beforeMonster, afterMonster) diff --git a/src/game/menu/ui_shop.py b/src/game/menu/ui_shop.py new file mode 100644 index 0000000..51d74f8 --- /dev/null +++ b/src/game/menu/ui_shop.py @@ -0,0 +1,365 @@ +from utils.primordials import * +from utils.coroutines import * +from utils.console import * +from game.state import * +from game.shop import * +from game.monster import * +from game.potion import * +from game.user import * +from game.inventory import * +from .menu import _menu_show_loading_splash +from typing import NamedTuple + +__MenuShopCache = NamedTuple("MenuShopCache", [ + ("gameState", GameState), # expect not changed + ("parent", View), # expect not changed + ("abortSignal", AbortSignal), # expect not changed + ("shopItems", list[ShopSchemaType]), + ("mainView", View), # expect not changed + ("tableView", View), # expect not changed + ("itemView", View), # expect not changed + ("itemPreviewFrame", View), # expect not changed + ("itemPreviewAnimation", View), + ("itemDescriptionView", View), # expect not changed + ("buyDialogView", View), # expect not changed + ("buyDialogConsole", ConsoleMock), # expect not changed + ("lastInputPosition", int), + ("inputPosition", int), + ("lastTableOffset", int), + ("tableOffset", int), + ("tableLoadIndex", int), +]) + +def _menu_show_shop(state, args): + cache: __MenuShopCache = None + tableMaxShown = 10 + if state is SuspendableInitial: + gameState, parent, abortSignal = args + cache = __MenuShopCache( + gameState=gameState, + parent=parent, + abortSignal=abortSignal, + shopItems=shop_get_all_items(gameState), + mainView=None, + tableView=None, + itemView=None, + itemPreviewFrame=None, + itemPreviewAnimation=None, + itemDescriptionView=None, + buyDialogView=None, + buyDialogConsole=None, + lastInputPosition=None, + inputPosition=-1, + lastTableOffset=None, + tableOffset=0, + tableLoadIndex=None, + ) + return "initInterface", cache, "checkTableLoad" + if state == "initInterface": + cache, intent, *_ = args + gameState = cache.gameState + visual = gamestate_get_visual(gameState) + driver = visual_get_driver(visual) + dumpView = view_new(driver) + + mainView = visual_show_simple_dialog(visual, "SHOP", "", + x=pos_from_center(), y=pos_from_center(), + width=dim_from_factor(0.8), height=dim_from_absolute(3 + 2 * (2 + tableMaxShown) + 2 + 2 + 2 + 2), + parent=cache.parent) + mainView["setContent"](" OWCA: 0") + + tableView = visual_show_table(visual, + [ + ("No.", dim_from_absolute(5), "Center"), + ("Tipe", dim_sub(dim_from_factor(0.2), dim_from_absolute(2)), "Right", (1, 0, 1, 0)), + ("Nama", dim_sub(dim_from_factor(0.6), dim_from_absolute(4)), "Left", (1, 0, 1, 0)), + ("Harga", dim_sub(dim_from_factor(0.2), dim_from_absolute(1)), "Center")], + [], + x=pos_from_absolute(0), y=pos_from_absolute(2), + width=dim_from_factor(0.6), height=dim_from_fill(0), + parent=mainView["contentView"]) + tableView["selectable"](True) + + itemView = visual_show_simple_dialog(visual, None, "", + x=pos_from_view_right(tableView), y=pos_from_absolute(0), + width=dim_from_fill(0), height=dim_from_fill(0), + parent=mainView["contentView"]) + + itemPreviewFrame = visual_show_simple_dialog(visual, None, "", + x=pos_from_absolute(0), y=pos_from_absolute(0), + width=dim_from_fill(0), height=dim_from_factor(0.7), + border=(0, 0, 0, 0), padding=(0, 0, 0, 0), + parent=itemView) + + itemPreviewAnimation = None + + itemDescriptionView = visual_show_simple_dialog(visual, None, "", + x=pos_from_absolute(0), y=pos_from_view_bottom(itemPreviewFrame), + width=dim_from_fill(0), height=dim_from_fill(0), + border=(0, 0, 0, 0), padding=(0, 0, 0, 0), + parent=itemView) + + buyDialogView = visual_show_simple_dialog(visual, "BELI", "", + x=pos_from_center(), y=pos_from_center(), + width=dim_from_factor(0.5), height=dim_from_factor(0.5), + parent=dumpView) + buyDialogConsole = visual_with_mock(visual, buyDialogView, + hasTitle=True, selectableAlignment="Bottom", inputBoxOffset=2) + + cache = namedtuple_with(cache, + mainView=mainView, + tableView=tableView, + itemView=itemView, + itemPreviewFrame=itemPreviewFrame, + itemPreviewAnimation=itemPreviewAnimation, + itemDescriptionView=itemDescriptionView, + buyDialogView=buyDialogView, + buyDialogConsole=buyDialogConsole, + ) + return intent, cache + if state == "checkTableLoad": + cache, *_ = args + gameState = cache.gameState + tableLoadIndex = (cache.tableOffset + tableMaxShown - 1) // tableMaxShown + if cache.tableLoadIndex is not None and tableLoadIndex <= cache.tableLoadIndex: + visual = gamestate_get_visual(gameState) + if cache.parent is not None: + visual_set_view(visual, visual_get_root_view(visual, cache.parent)) + elif cache.mainView is not None: + visual_set_view(visual, cache.mainView) + return "updateTableEntries", cache + startIndex = tableLoadIndex * tableMaxShown + endIndex = startIndex + tableMaxShown + loadItems = array_slice(cache.shopItems, startIndex, endIndex) + loadItemSprites = array_map(loadItems, lambda i, *_: __resolve_shop_item(gameState, i)[2]) + loadItemSprites = array_map(loadItemSprites, lambda s, *_: (f"sprite {s}", s)) + loadItemSprites = dict_set(dict(), loadItemSprites) + promise = promise_from_suspendable(_menu_show_loading_splash, gameState, loadItemSprites) + cache = namedtuple_with(cache, + tableLoadIndex=tableLoadIndex + ) + return "checkTableLoad", cache, promise + if state == "updateTableEntries": + cache, *_ = args + if cache.tableOffset == cache.lastTableOffset: + return "updateItemView", cache + gameState = cache.gameState + tableView = cache.tableView + startIndex = cache.tableOffset + endIndex = startIndex + tableMaxShown + itemRows = array_slice(cache.shopItems, startIndex, endIndex) + itemRows = array_map(itemRows, lambda it, i, *_: [f"{startIndex + i + 1}", it.referenceType, __resolve_shop_item(gameState, it)[0], f"{it.cost}"]) + tableView["updateRows"]([ + ["▲▲▲", "▲▲▲▲▲▲", "▲▲▲▲▲▲", "▲▲▲▲▲▲"], + *itemRows, + ["▼▼▼", "▼▼▼▼▼▼", "▼▼▼▼▼▼", "▼▼▼▼▼▼"], + ]) + cache = namedtuple_with(cache, + lastTableOffset=cache.tableOffset + ) + return "updateItemView", cache + if state == "updateItemView": + cache, *_ = args + gameState = cache.gameState + mainView = cache.mainView + userMoney = user_get_current(gameState).money + mainView["setContent"](f" OWCA: {userMoney}") + if cache.inputPosition == cache.lastInputPosition: + return "waitInput", cache + visual = gamestate_get_visual(gameState) + inputPosition = cache.inputPosition + itemPreviewFrame = cache.itemPreviewFrame + itemPreviewAnimation = cache.itemPreviewAnimation + itemDescriptionView = cache.itemDescriptionView + if itemPreviewAnimation is not None: + itemPreviewAnimation["stop"]() + view_remove_child(itemPreviewFrame, itemPreviewAnimation) + itemPreviewAnimation = None + if inputPosition < -1: + inputPosition = -1 + if inputPosition >= len(cache.shopItems): + inputPosition = len(cache.shopItems) - 1 + if inputPosition != -1: + shopItem = cache.shopItems[inputPosition] + itemName, itemDescription, itemSprite = __resolve_shop_item(gameState, shopItem) + itemPreviewAnimation = visual_show_splash(visual, itemSprite, parent=itemPreviewFrame) + itemPreviewAnimation["play"](60, True) + itemDescriptionView["setContent"](f"ID: {inputPosition}\nNama: {itemName}\nHarga: {shopItem.cost}\n{itemDescription}") + cache = namedtuple_with(cache, + lastInputPosition=inputPosition, + itemPreviewAnimation=itemPreviewAnimation, + ) + return "waitInput", cache + if state == "waitInput": + cache, *_ = args + gameState = cache.gameState + visual = gamestate_get_visual(gameState) + tableView = cache.tableView + def executor(resolve, _): + def cleanup(): + tableView["onHover"](None) + tableView["onEnter"](None) + visual_remove_key_listener(visual, tableView, onKey) + def onHover(index): + cleanup() + resolve((index, "hover")) + def onEnter(index): + cleanup() + resolve((index, "enter")) + def onKey(event: KeyEvent): + if event.key == "ControlESC": + cleanup() + resolve((None, "escape")) + tableView["onHover"](onHover) + tableView["onEnter"](onEnter) + visual_add_key_listener(visual, tableView, onKey) + promise = promise_new(executor) + return "processInput", cache, promise + if state == "processInput": + cache, inputAction, *_ = args + tableView = cache.tableView + position, action = inputAction + if action == "escape": + return SuspendableReturn, None + tableOffset = cache.tableOffset + inputPosition = tableOffset + (position - 1) + if position == 0: + tableView["setSelection"](1) + tableOffset = max(0, tableOffset - 1) + inputPosition = tableOffset + position + cache = namedtuple_with(cache, + tableOffset=tableOffset, + inputPosition=inputPosition, + ) + return "checkTableLoad", cache + if position == min(len(cache.shopItems) + 1, tableMaxShown + 1): + tableView["setSelection"](min(len(cache.shopItems), tableMaxShown)) + tableOffset = min(max(0, len(cache.shopItems) - tableMaxShown), tableOffset + 1) + inputPosition = tableOffset + (min(len(cache.shopItems), tableMaxShown) - 1) + cache = namedtuple_with(cache, + tableOffset=tableOffset, + inputPosition=inputPosition, + ) + return "checkTableLoad", cache + cache = namedtuple_with(cache, + inputPosition=inputPosition + ) + if action == "enter": + return "buyDialog", cache + return "updateItemView", cache + if state == "buyDialog": + cache, *_ = args + gameState = cache.gameState + mainView = cache.mainView + tableView = cache.tableView + buyDialogView = cache.buyDialogView + tableView["selectable"](False) + view_add_child(mainView["contentView"], buyDialogView) + userMoney = user_get_current(gameState).money + print, input, meta = cache.buyDialogConsole + shopItem = cache.shopItems[cache.inputPosition] + itemName = __resolve_shop_item(gameState, shopItem)[0] + meta(action="clear") + print(f"==== Beli '{itemName}' @ {shopItem.cost} ====") + print("Masukkan jumlah yang ingin dibeli.") + def onChange(v): + meta(action="clearPrint") + if v == "": + print("Masukkan jumlah yang ingin dibeli.") + return + quantity = parse_int(v) + if quantity is None or quantity <= 0: + print("Jumlah yang dimasukkan tidak valid.") + return + subtotal = quantity * shopItem.cost + print(f"Total harga: {quantity} * {shopItem.cost} = {subtotal}") + if userMoney < subtotal: + print(f"Uangmu tidak cukup.") + quantity = input("Jumlah: ", f"Uangmu: {userMoney}", onChange=onChange) + return "buyDialogConfirm", cache, quantity + if state == "buyDialogConfirm": + cache, quantity, *_ = args + if quantity is None: + return "buyDialogConfirmChoose", cache, None, "Batal" + quantity = parse_int(quantity) + if quantity is None or quantity <= 0: + return "buyDialog", cache + gameState = cache.gameState + shopItem = cache.shopItems[cache.inputPosition] + itemName = __resolve_shop_item(gameState, shopItem)[0] + print, input, meta = cache.buyDialogConsole + meta(action="clear") + userMoney = user_get_current(gameState).money + subtotal = quantity * shopItem.cost + if userMoney < subtotal: + print(f"Uangmu tidak cukup. ", end="") + input("Lanjut", selectable=True) + selection = meta(action="select") + return "buyDialogConfirmChoose", cache, None, "Batal", selection + meta("keySpeed", 120) + print(f"OWCA mu saat ini terdapat {userMoney}. ", end="") + print(f"Kamu akan membeli '{itemName}' dengan harga satuan {shopItem.cost} sebanyak {quantity} dengan subtotal {subtotal}. ", end="") + print(f"Di akhir transaksi OWCA mu akan tersisa {userMoney - subtotal}.") + input("Konfirmasi", selectable=True) + input("Batal", selectable=True) + selection = meta(action="select") + return "buyDialogConfirmChoose", cache, quantity, selection + if state == "buyDialogConfirmChoose": + cache, quantity, selection, *_ = args + print, input, meta = cache.buyDialogConsole + meta("keySpeed", -1) + if selection is None: + return "buyDialog", cache + gameState = cache.gameState + mainView = cache.mainView + tableView = cache.tableView + buyDialogView = cache.buyDialogView + view_remove_child(mainView["contentView"], buyDialogView) + tableView["selectable"](True) + if selection == "Batal": + return "updateItemView", cache + shopItem = cache.shopItems[cache.inputPosition] + currentUser = user_get_current(gameState) + userMoney = currentUser.money - quantity * shopItem.cost + currentUser = user_set(gameState, currentUser.id, namedtuple_with(currentUser, money=userMoney)) + if shopItem.referenceType == "monster": + monsterType = monster_get(gameState, shopItem.referenceId) + for _ in range(quantity): + newMonster = inventory_monster_new(gameState) + newMonster = inventory_monster_new(gameState, newMonster.id, namedtuple_with(newMonster, + ownerId=currentUser.id, + referenceId=monsterType.id, + name=monsterType.name, + experiencePoints=0, + healthPoints=monsterType.healthPoints, + attackPower=monsterType.attackPower, + defensePower=monsterType.defensePower, + activePotions=[] + )) + if shopItem.referenceType == "item": + itemType = potion_get(gameState, shopItem.referenceId) + userItems = inventory_item_get_user_items(gameState, currentUser.id) + userItem = array_find(userItems, lambda i, *_: i.referenceId == itemType.id) + if userItem is None: + userItem = inventory_item_new(gameState) + userItem = inventory_item_set(gameState, userItem.id, namedtuple_with(userItem, + ownerId=currentUser.id, + referenceId=itemType.id, + quantity=quantity + )) + else: + userItem = inventory_item_set(gameState, userItem.id, namedtuple_with(userItem, + quantity=userItem.quantity + quantity + )) + return "updateItemView", cache + return SuspendableExhausted + +def __resolve_shop_item(gameState: GameState, shopItem: ShopSchemaType): + if shopItem.referenceType == "item": + potion = potion_get(gameState, shopItem.referenceId) + description = f"Deskripsi: {potion.description}" + return (potion.name, description, potion.sprite) + if shopItem.referenceType == "monster": + monster = monster_get(gameState, shopItem.referenceId) + description = f"Family: {monster.family} Level: {monster.level}\nATK: {monster.attackPower} DEF: {monster.defensePower}\nDeskripsi: {monster.description}" + return (monster.name, description, monster.spriteFront) diff --git a/src/game/menu/ui_user.py b/src/game/menu/ui_user.py index 0fe92db..3b861b9 100644 --- a/src/game/menu/ui_user.py +++ b/src/game/menu/ui_user.py @@ -7,13 +7,12 @@ def _menu_show_login(state, args): if state is SuspendableInitial: gameState, console = args - print, input, meta = console - meta(action="clear") - print("=========== LOGIN ===========") return 1, gameState, console if state == 1: gameState, console = args print, input, meta = console + meta(action="clear") + print("=========== LOGIN ===========") print("Silahkan masukkan username dan paswordmu yang telah terdaftar.") username = input(f"{fbg()}Username: {fg('e6dee6')}{bg('734118')}", f"{fbg()}Username hanya boleh berisi alfabet, angka, underscore, dan strip!") password = input(f"{fbg()}Password: {fg('e6dee6')}{bg('734118')}", f"{fbg()}Tekan {fg('e63131')}CTRL+A{fg()} untuk melihat password", renderer=lambda v, *_: array_map(v, lambda r, *_: Rune("•", r.attribute))) @@ -21,12 +20,16 @@ def _menu_show_login(state, args): if state == 2: gameState, console, username, password = args print, input, meta = console - if password is None: + if username is None or password is None: return SuspendableReturn, None + meta(action="clear") with meta(action="foreign"): user_login(gameState, username, password) if user_is_logged_in(gameState): - return SuspendableReturn, None + print("Berhasil login!") + input("Lanjut", selectable=True) + selection = meta(action="select") + return SuspendableReturn, None, selection return promise_from_wait(4000, 3), gameState, console if state == 3: gameState, console = args @@ -36,13 +39,12 @@ def _menu_show_login(state, args): def _menu_show_register(state, args): if state is SuspendableInitial: gameState, console = args - print, input, meta = console - meta(action="clear") - print("=========== REGISTER ===========") return 1, gameState, console if state == 1: gameState, console = args print, input, meta = console + meta(action="clear") + print("=========== REGISTER ===========") print("Silahkan masukkan username dan password untuk register akun.") username = input(f"{fbg()}Username: {fg('e6dee6')}{bg('734118')}", f"{fbg()}Username hanya boleh berisi alfabet, angka, underscore, dan strip!") password = input(f"{fbg()}Password: {fg('e6dee6')}{bg('734118')}", f"{fbg()}Tekan {fg('e63131')}CTRL+A{fg()} untuk melihat password", renderer=lambda v, *_: array_map(v, lambda r, *_: Rune("•", r.attribute))) @@ -50,16 +52,16 @@ def _menu_show_register(state, args): if state == 2: gameState, console, username, password = args print, input, meta = console - if password is None: + if username is None or password is None: return SuspendableReturn, None - with meta(action="foreign"): - user_register(gameState, username, password) + meta(action="clear") + promise = promise_from_suspendable(user_register, gameState, console, username, password) + return 3, gameState, console, promise + if state == 3: + gameState, console, *_ = args if user_is_logged_in(gameState): return SuspendableReturn, None - return promise_from_wait(4000, 3), gameState, console - if state == 3: - gameState, console = args - return SuspendableInitial, gameState, console + return promise_from_wait(4000, 1), gameState, console return SuspendableExhausted def _menu_show_logout(state, args): @@ -80,7 +82,11 @@ def _menu_show_logout(state, args): gameState, console, *_ = args print, input, meta = console meta(action="popFlags") + meta(action="clear") with meta(action="foreign"): user_logout(gameState) - return SuspendableReturn, None + print("Berhasil logout!") + input("Lanjut", selectable=True) + selection = meta(action="select") + return SuspendableReturn, None, selection return SuspendableExhausted diff --git a/src/game/monster/__init__.py b/src/game/monster/__init__.py index 3cb59a9..5310da0 100644 --- a/src/game/monster/__init__.py +++ b/src/game/monster/__init__.py @@ -1,5 +1,6 @@ -from .monster import _monster_get, _monster_set, _monster_new +from .monster import _monster_get, _monster_set, _monster_new, _monster_get_all_monsters monster_get = _monster_get monster_set = _monster_set monster_new = _monster_new +monster_get_all_monsters = _monster_get_all_monsters diff --git a/src/game/monster/monster.py b/src/game/monster/monster.py index 8ebd2a3..f04873c 100644 --- a/src/game/monster/monster.py +++ b/src/game/monster/monster.py @@ -31,3 +31,8 @@ def _monster_new(gameState: GameState) -> MonsterSchemaType: ) database_set_entry_at(monsterDatabase, monsterId, monster) return monster + +def _monster_get_all_monsters(gameState: GameState) -> list[MonsterSchemaType]: + monsterDatabase = gamestate_get_monster_database(gameState) + monsterEntries = database_get_entries(monsterDatabase) + return monsterEntries diff --git a/src/game/shop/__init__.py b/src/game/shop/__init__.py index 8b13789..e617f7c 100644 --- a/src/game/shop/__init__.py +++ b/src/game/shop/__init__.py @@ -1 +1,6 @@ +from .shop import _shop_get, _shop_set, _shop_new, _shop_get_all_items +shop_get = _shop_get +shop_set = _shop_set +shop_new = _shop_new +shop_get_all_items = _shop_get_all_items diff --git a/src/game/shop/shop.py b/src/game/shop/shop.py index e69de29..24ba858 100644 --- a/src/game/shop/shop.py +++ b/src/game/shop/shop.py @@ -0,0 +1,31 @@ +from utils.primordials import * +from game.state import * +from game.database import * +from typing import Callable + +def _shop_get(gameState: GameState, shopId: int) -> ShopSchemaType: + shopDatabase = gamestate_get_shop_database(gameState) + return database_get_entry_at(shopDatabase, shopId) + +def _shop_set(gameState: GameState, shopId: int, modifier: Callable[[ShopSchemaType], ShopSchemaType]) -> ShopSchemaType: + shopDatabase = gamestate_get_shop_database(gameState) + shop = modifier(database_get_entry_at(shopDatabase, shopId)) + database_set_entry_at(shopDatabase, shopId, shop) + return shop + +def _shop_new(gameState: GameState) -> ShopSchemaType: + shopDatabase = gamestate_get_shop_database(gameState) + shopId = database_get_entries_length(shopDatabase) + shop = ShopSchemaType( + id=shopId, + referenceType=None, + referenceId=None, + cost=None + ) + database_set_entry_at(shopDatabase, shopId, shop) + return shop + +def _shop_get_all_items(gameState: GameState) -> list[ShopSchemaType]: + shopDatabase = gamestate_get_shop_database(gameState) + shopEntries = database_get_entries(shopDatabase) + return shopEntries diff --git a/src/game/state/__init__.py b/src/game/state/__init__.py index d746350..979e0cc 100644 --- a/src/game/state/__init__.py +++ b/src/game/state/__init__.py @@ -55,7 +55,7 @@ gamestate_deltatime = _gamestate_deltatime gamestate_rand = _gamestate_rand -from .visual import _Visual, _visual_new, _visual_get_driver, _visual_get_toplevel, _visual_get_view, _visual_get_directory, _visual_set_view, _visual_set_directory, _visual_get_root_view, _visual_add_connect_listener, _visual_remove_connect_listener, _visual_add_disconnect_listener, _visual_remove_disconnect_listener, _visual_is_connected, _visual_add_key_listener, _visual_remove_key_listener, _visual_tick, _visual_draw, _visual_load_splash, _visual_show_frame_sequence, _visual_show_splash, _visual_show_simple_dialog, _ConsoleMock, _visual_with_mock, _fg, _bg, _fbg +from .visual import _Visual, _visual_new, _visual_get_driver, _visual_get_toplevel, _visual_get_view, _visual_get_directory, _visual_set_view, _visual_set_directory, _visual_get_root_view, _visual_add_connect_listener, _visual_remove_connect_listener, _visual_add_disconnect_listener, _visual_remove_disconnect_listener, _visual_is_connected, _visual_add_key_listener, _visual_remove_key_listener, _visual_tick, _visual_draw, _visual_load_splash, _visual_show_frame_sequence, _visual_show_splash, _visual_show_simple_dialog, _visual_show_table, _ConsoleMock, _visual_with_mock, _fg, _bg, _fbg Visual = _Visual visual_new = _visual_new @@ -79,6 +79,7 @@ visual_show_frame_sequence = _visual_show_frame_sequence visual_show_splash = _visual_show_splash visual_show_simple_dialog = _visual_show_simple_dialog +visual_show_table = _visual_show_table ConsoleMock = _ConsoleMock visual_with_mock = _visual_with_mock fg = _fg diff --git a/src/game/state/state.py b/src/game/state/state.py index e4a2e6b..f9e1cc9 100644 --- a/src/game/state/state.py +++ b/src/game/state/state.py @@ -61,6 +61,7 @@ ("duration", float), ("curve", int), # 0 means linear, 1 means ease-out-quint, to be implemented for other curves. # ("flags", list[int]), + ("sprite", str), ("nextId", Optional[int]), # Reference to potionDatabase, this allows a potion to have multiple effects. The referenced potion typically is an internal one. ]) _PotionSchemaProperties = [ @@ -73,6 +74,7 @@ database_property_new("duration", float, lambda x: float(x), lambda x: str(x)), database_property_new("curve", int, lambda x: int(x), lambda x: str(x)), # database_property_new("flags", list[int], lambda x: array_map(string_split(x, "|"), lambda v: int(v)), lambda x: array_join(array_map(x, lambda v: str(v)), "|")), + database_property_new("sprite", str, lambda x: x, lambda x: x), database_property_new("nextId", int, lambda x: int(x) if x != "" else None, lambda x: str(x) if x != None else ""), ] _PotionSchema = database_schema_new("csv", _PotionSchemaType, _PotionSchemaProperties) diff --git a/src/game/state/visual.py b/src/game/state/visual.py index f3c13d4..ca65308 100644 --- a/src/game/state/visual.py +++ b/src/game/state/visual.py @@ -45,8 +45,12 @@ def _visual_get_directory(visual: _Visual) -> str: return visual["directory"] def _visual_set_view(visual: _Visual, view: View) -> None: toplevel = visual["toplevel"] + driver = visual["driver"] + if visual["view"] is view: + return if visual["view"] is not None: view_remove_child(toplevel, visual["view"]) + driver_clear_rect(driver) visual["view"] = view if view is None: return @@ -149,12 +153,12 @@ def _visual_remove_disconnect_listener(visual: _Visual, view: View, callback: Ca def __visual_attach_connect_handler(visual: _Visual, view: View) -> None: connectListeners = None propagateHandler = None - if connectListeners is None: + if "__connect_listeners" not in view: connectListeners = [] view["__connect_listeners"] = connectListeners else: connectListeners = view["__connect_listeners"] - if propagateHandler is None: + if "__connect_propagate_handler" not in view: def propagateToSubviews() -> None: view["__connected"] = True subviews = view["subviews"] @@ -174,12 +178,12 @@ def propagateToSubviews() -> None: def __visual_attach_disconnect_handler(visual: _Visual, view: View) -> None: disconnectListeners = None propagateHandler = None - if disconnectListeners is None: + if "__disconnect_listeners" not in view: disconnectListeners = [] view["__disconnect_listeners"] = disconnectListeners else: disconnectListeners = view["__disconnect_listeners"] - if propagateHandler is None: + if "__disconnect_propagate_handler" not in view: def propagateToSubviews() -> None: view["__connected"] = False subviews = view["subviews"] @@ -216,12 +220,12 @@ def _visual_remove_key_listener(visual: _Visual, view: View, callback: Callable[ def __visual_attach_key_handler(visual: _Visual, view: View) -> None: keyListeners = None propagateHandler = None - if keyListeners is None: + if "__key_listeners" not in view: keyListeners = [] view["__key_listeners"] = keyListeners else: keyListeners = view["__key_listeners"] - if propagateHandler is None: + if "__key_propagate_handle" not in view: def propagateToSubviews(event: KeyEvent) -> None: subviews = view["subviews"] for subview in subviews: @@ -273,19 +277,29 @@ def onResolved(splash: list[list[Rune]]): def _visual_show_frame_sequence( visual: _Visual, - frames: list[__Text], + frames: list[__Text], /, + x: Pos = pos_from_center(), + y: Pos = pos_from_center(), + width: Dim = dim_from_fill(0), + height: Dim = dim_from_fill(0), + border: tuple = (0, 0, 0, 0), + padding: tuple = (0, 0, 0, 0), + horizontalAlignment: TextHorizontalAlignment = "Center", + verticalAlignment: TextVerticalAlignment = "Middle", parent: Optional[View] = None ) -> View: driver = visual["driver"] mainView = view_new(driver) - view_set_x(mainView, pos_from_factor(0)) - view_set_y(mainView, pos_from_factor(0)) - view_set_width(mainView, dim_from_factor(1)) - view_set_height(mainView, dim_from_factor(1)) + view_set_x(mainView, x) + view_set_y(mainView, y) + view_set_width(mainView, width) + view_set_height(mainView, height) + adornment_set_thickness(view_get_border(mainView), Thickness(*border)) + adornment_set_thickness(view_get_padding(mainView), Thickness(*padding)) textFormatter = view_get_text_formatter(mainView) text_formatter_set_wordwrap(textFormatter, False) - text_formatter_set_horizontal_alignment(textFormatter, "Center") - text_formatter_set_vertical_alignment(textFormatter, "Middle") + text_formatter_set_horizontal_alignment(textFormatter, horizontalAlignment) + text_formatter_set_vertical_alignment(textFormatter, verticalAlignment) __visual_attach_mock_view_add_remove_child_method(visual, mainView) __visual_attach_connect_handler(visual, mainView) @@ -327,18 +341,24 @@ def play(fps: float = 60, loopFrame = False) -> None: nonlocal handle if handle is not None: stop() + if len(frames) <= 1: + setFrame(0) + return def loop(): nonlocal handle if nextFrame(): - return + return True if not loopFrame: stop() - return + return False setFrame(0) + return True if not _visual_is_connected(visual, mainView): onConnectCb = lambda: onConnect((fps, loopFrame)) _visual_add_connect_listener(visual, mainView, onConnectCb) return + if not loop(): + return onDisconnectCb = lambda: onDisconnect((fps, loopFrame)) _visual_add_disconnect_listener(visual, mainView, onDisconnectCb) handle = set_interval(loop, 1000 / fps) @@ -369,11 +389,11 @@ def stop() -> None: def _visual_show_splash( visual: _Visual, splash: str, - parent: Optional[View] = None + **kwargs ) -> View: splashes = visual["splashes"] splash = splashes[splash] - return _visual_show_frame_sequence(visual, splash, parent) + return _visual_show_frame_sequence(visual, splash, **kwargs) def _visual_show_simple_dialog( visual: _Visual, @@ -384,10 +404,10 @@ def _visual_show_simple_dialog( y: Pos = pos_from_center(), width: Dim = dim_from_factor(0.5), height: Dim = dim_from_factor(0.5), - horizontalAlignment: TextHorizontalAlignment = "Left", - verticalAlignment: TextVerticalAlignment = "Top", border: tuple = (1, 1, 1, 1), padding: tuple = (2, 1, 2, 1), + horizontalAlignment: TextHorizontalAlignment = "Left", + verticalAlignment: TextVerticalAlignment = "Top", parent: Optional[View] = None ) -> View: driver = visual["driver"] @@ -415,7 +435,7 @@ def _visual_show_simple_dialog( contentView = view_new(driver) view_add_child(mainView, contentView) view_set_x(contentView, pos_from_absolute(0)) - view_set_y(contentView, pos_from_absolute(2 if title is not None else 0)) + view_set_y(contentView, pos_from_view_bottom(titleView) if titleView is not None else pos_from_absolute(0)) view_set_width(contentView, dim_from_fill(0)) view_set_height(contentView, dim_from_fill(0)) textFormatter = view_get_text_formatter(contentView) @@ -438,16 +458,272 @@ def _visual_show_simple_dialog( _visual_set_view(visual, mainView) return mainView +def _visual_show_table( + visual: _Visual, + columns: list[tuple[__Text, float]], + rows: list[list[__Text]], /, + x: Pos = pos_from_absolute(0), + y: Pos = pos_from_absolute(0), + width: Dim = dim_from_fill(0), + height: Dim = dim_from_fill(0), + parent: Optional[View] = None + ) -> View: + driver = visual["driver"] + mainView = view_new(driver) + view_set_x(mainView, x) + view_set_y(mainView, y) + view_set_width(mainView, width) + view_set_height(mainView, height) + + lastColumns: list[tuple[__Text, float]] = None + rowViews: list[View] = [] + patchViews: list[View] = [] + cellValuePadX = [Rune(" ", RuneAttribute_clear) for _ in range(0, 50)] + cellValuePadY = array_flat([[Rune("\n", RuneAttribute_clear), *cellValuePadX, Rune("\n", RuneAttribute_clear)] for _ in range(0, 5)]) + def addPatch(patchCharacter: str, patchPosX: Pos, patchPosY: Pos): + patchView = view_new(driver) + array_push(patchViews, patchView) + view_set_x(patchView, patchPosX) + view_set_y(patchView, patchPosY) + view_set_width(patchView, dim_from_absolute(1)) + view_set_height(patchView, dim_from_absolute(1)) + patchTextFormatter = view_get_text_formatter(patchView) + text_formatter_set_text(patchTextFormatter, [Rune(patchCharacter, RuneAttribute_clear)]) + def updateCellTextView(cellTextView: View, value: __Text, alignment: Optional[str]): + textFormatter = view_get_text_formatter(cellTextView) + text_formatter_set_wordwrap(textFormatter, False) + parsedCellValue = __parse_colored_text(value)[0] + if alignment is not None: + paddedText = parsedCellValue + if string_starts_with(alignment, "Left"): + paddedText = [*paddedText, *cellValuePadX] + text_formatter_set_horizontal_alignment(textFormatter, "Left") + if string_starts_with(alignment, "Center"): + paddedText = [*cellValuePadX, *paddedText, *cellValuePadX] + text_formatter_set_horizontal_alignment(textFormatter, "Center") + if string_starts_with(alignment, "Right"): + paddedText = [*cellValuePadX, *paddedText] + text_formatter_set_horizontal_alignment(textFormatter, "Right") + if string_ends_with(alignment, "Top"): + paddedText = [*paddedText, *cellValuePadY] + text_formatter_set_vertical_alignment(textFormatter, "Top") + if string_ends_with(alignment, "Middle"): + paddedText = [*cellValuePadY, *paddedText] + text_formatter_set_vertical_alignment(textFormatter, "Middle") + if string_ends_with(alignment, "Bottom"): + paddedText = [*cellValuePadY, *paddedText, *cellValuePadY] + text_formatter_set_vertical_alignment(textFormatter, "Bottom") + cellTextView["originalText"] = paddedText + text_formatter_set_text(textFormatter, paddedText) + else: + paddedText = [*parsedCellValue, *cellValuePadX] + cellTextView["originalText"] = paddedText + text_formatter_set_text(textFormatter, paddedText) + def updateTable(columns: list[tuple[__Text, float]], rows: list[list[__Text]]): + nonlocal lastColumns, lastSelectableIndex + for rowView in array_splice(rowViews, 0): + view_remove_child(mainView, rowView) + for patchView in array_splice(patchViews, 0): + view_remove_child(mainView, patchView) + + lastColumns = columns + rows = array_slice(rows) + array_unshift(rows, array_map(columns, lambda c, *_: c[0])) + lastRowView = None + for i in range(0, len(rows)): + rowData = rows[i] + rowView = view_new(driver) + array_push(rowViews, rowView) + view_add_child(mainView, rowView) + view_set_x(rowView, pos_from_absolute(0)) + view_set_y(rowView, pos_from_view_bottom(lastRowView) if lastRowView is not None else pos_from_absolute(0)) + view_set_width(rowView, dim_from_fill(0)) + view_set_height(rowView, dim_from_absolute(3 if i == 0 else 2)) + + cellViews = [] + rowView["cellViews"] = cellViews + lastCellView = None + for j in range(0, len(columns)): + columnSize = columns[j][1] + cellView = view_new(driver) + array_push(cellViews, cellView) + view_add_child(rowView, cellView) + view_set_x(cellView, pos_from_view_right(lastCellView) if lastCellView is not None else pos_from_absolute(0)) + view_set_y(cellView, pos_from_absolute(0)) + view_set_width(cellView, dim_from_factor(columnSize) if type(columnSize) is float else dim_add(columnSize, dim_from_absolute(2 if j == 0 else 1)) if dim_as_absolute(columnSize) else columnSize) + view_set_height(cellView, dim_from_fill(0)) + adornment_set_thickness(view_get_border(cellView), Thickness(1 if j == 0 else 0, 1 if i == 0 else 0, 1, 1)) + if len(columns[j]) > 3: + padding = columns[j][3] + adornment_set_thickness(view_get_padding(cellView), Thickness(*padding)) + + cellTextView = view_new(driver) + cellView["textView"] = cellTextView + view_add_child(cellView, cellTextView) + view_set_x(cellTextView, pos_from_absolute(0)) + view_set_y(cellTextView, pos_from_absolute(0)) + view_set_width(cellTextView, dim_from_fill(0)) + view_set_height(cellTextView, dim_from_fill(0)) + updateCellTextView(cellTextView, rowData[j], columns[j][2] if len(columns[j]) > 2 else None) + + patchCharacter = None + patchPosX = None + patchPosY = None + if i == 0 and j != 0: + addPatch( + "┬", + pos_sub(pos_from_view_left(cellView), pos_from_absolute(1)), + pos_from_view_top(cellView) + ) + if i == len(rows) - 1 and j != 0: + addPatch( + "┴", + pos_sub(pos_from_view_left(cellView), pos_from_absolute(1)), + pos_sub(pos_from_view_bottom(cellView), pos_from_absolute(1)) + ) + if i != 0 and j == 0: + addPatch( + "├", + pos_from_view_left(cellView), + pos_sub(pos_from_view_top(cellView), pos_from_absolute(1)) + ) + if i != 0 and j == len(columns) - 1: + addPatch( + "┤", + pos_sub(pos_from_view_right(cellView), pos_from_absolute(1)), + pos_sub(pos_from_view_top(cellView), pos_from_absolute(1)) + ) + if i != 0 and j != 0: + addPatch( + "┼", + pos_sub(pos_from_view_left(cellView), pos_from_absolute(1)), + pos_sub(pos_from_view_top(cellView), pos_from_absolute(1)) + ) + + lastCellView = cellView + lastRowView = rowView + + for patchView in patchViews: + view_add_child(mainView, patchView) + lastSelectableIndex = -1 + selectableChange() + def updateRows(rows: list[list[__Text]]) -> None: + nonlocal lastColumns, lastSelectableIndex + if len(rows) != len(rowViews) - 1: + updateTable(lastColumns, rows) + return + # Fasttrack + for i in range(0, len(rows)): + rowData = rows[i] + rowView = rowViews[i + 1] + cellViews = rowView["cellViews"] + for j in range(0, len(columns)): + cellView = cellViews[j] + cellTextView = cellView["textView"] + updateCellTextView(cellTextView, rowData[j], columns[j][2] if len(columns[j]) > 2 else None) + lastSelectableIndex = -1 + selectableChange() + + __visual_attach_mock_view_add_remove_child_method(visual, mainView) + __visual_attach_connect_handler(visual, mainView) + __visual_attach_disconnect_handler(visual, mainView) + __visual_attach_key_handler(visual, mainView) + + selectable = False + selectableIndex = -1 + lastSelectableIndex = -1 + hoverListener = None + enterListener = None + def selectable0(value: bool) -> Optional[bool]: + nonlocal selectable + if value is None: + return selectable + selectable = value + selectableChange() + if selectable: + _visual_add_key_listener(visual, mainView, onKey) + else: + _visual_remove_key_listener(visual, mainView, onKey) + def onHover(value: Callable[[int], Any]) -> None: + nonlocal hoverListener + hoverListener = value + def onEnter(value: Callable[[int], Any]) -> None: + nonlocal enterListener + enterListener = value + def setSelection(index: int) -> None: + nonlocal selectableIndex + selectableIndex = index + selectableChange() + def selectableChange(): + nonlocal selectable, selectableIndex, lastSelectableIndex, hoverListener + if selectableIndex < -1: + selectableIndex = -1 + if selectableIndex >= len(rowViews) - 1: + selectableIndex = len(rowViews) - 2 + if selectable and selectableIndex == lastSelectableIndex: + return + if lastSelectableIndex != -1: + rowView = rowViews[lastSelectableIndex + 1] + cellViews = rowView["cellViews"] + for cellView in cellViews: + cellTextView = cellView["textView"] + originalText = cellTextView["originalText"] + textFormatter = view_get_text_formatter(cellTextView) + text_formatter_set_text(textFormatter, originalText) + if not selectable: + lastSelectableIndex = -1 + if hoverListener is not None: + hoverListener(-1) + return + if selectableIndex != -1: + rowView = rowViews[selectableIndex + 1] + cellViews = rowView["cellViews"] + for cellView in cellViews: + cellTextView = cellView["textView"] + originalText = cellTextView["originalText"] + reversedColoredText = __reverse_background_foreground(originalText) + textFormatter = view_get_text_formatter(cellTextView) + text_formatter_set_text(textFormatter, reversedColoredText) + lastSelectableIndex = selectableIndex + if hoverListener is not None: + hoverListener(selectableIndex) + def onKey(event: KeyEvent): + nonlocal selectable, selectableIndex, enterListener + if not selectable or len(rowViews) < 1: + return + if event.key == "ControlCR" or event.key == "ControlLF": + if selectableIndex == -1 or enterListener is None: + return + enterListener(selectableIndex) + return + if event.key == "Up": + selectableIndex = max(0, selectableIndex - 1) if selectableIndex != -1 else len(rowViews) - 2 + selectableChange() + return + if event.key == "Down": + selectableIndex = min(len(rowViews) - 2, selectableIndex + 1) if selectableIndex != -1 else 0 + selectableChange() + return + updateTable(columns, rows) + mainView["rowViews"] = rowViews + mainView["patchViews"] = patchViews + mainView["updateTable"] = updateTable + mainView["updateRows"] = updateRows + mainView["selectable"] = selectable0 + mainView["onHover"] = onHover + mainView["onEnter"] = onEnter + mainView["setSelection"] = setSelection + if parent is not None: + view_add_child(parent, mainView) + else: + _visual_set_view(visual, mainView) + return mainView + _ConsoleMock = tuple[Callable[[__Text], None], Callable[[__Text], Promise[str]], Callable[[str, Any], None]] def _visual_with_mock(visual: _Visual, view: View, **kwargs) -> _ConsoleMock: driver = visual["driver"] setTitle = view["setTitle"] setContent = view["setContent"] - lastDrawing: Optional[float] = None - drawingOnCompletes: list[Callable[[], None]] = [] - contentLastAttribute = RuneAttribute_clear - content: list[Rune] = [] - contentDrawnPosition = 0 flagsStack: list[dict[str, Any]] = [] flags = dict_with( dict( @@ -457,12 +733,20 @@ def _visual_with_mock(visual: _Visual, view: View, **kwargs) -> _ConsoleMock: selectableWaitAfterContent=True, selectableClearAfterSelection=True, selectableAllowEscape=True, + selectableAlignment="CenterMiddle", inputWaitAfterContent=True, inputAllowEscape=True, + inputBoxOffset=1, doNotRaiseSignal=False ), **kwargs ) + + lastDrawing: Optional[float] = None + drawingOnCompletes: list[Callable[[], None]] = [] + contentLastAttribute = RuneAttribute_clear + content: list[Rune] = [] + contentDrawnPosition = 0 printKeyListenerAttached = False def onPrintKey(event: KeyEvent): nonlocal contentDrawnPosition @@ -470,11 +754,21 @@ def onPrintKey(event: KeyEvent): if not flags["keyAnimationAllowSkip"]: return contentDrawnPosition = len(content) - doPrintDraw() + setContent(content) def doPrintDraw(): nonlocal lastDrawing, contentDrawnPosition, printKeyListenerAttached if not _visual_is_connected(visual, view): return + keySpeed = flags["keySpeed"] + if keySpeed == -1: + contentDrawnPosition = len(content) + setContent(content) + if printKeyListenerAttached: + _visual_remove_key_listener(visual, view, onPrintKey) + printKeyListenerAttached = False + for drawingOnComplete in array_splice(drawingOnCompletes, 0): + drawingOnComplete() + return now = __now() deltaTime = now - lastDrawing if lastDrawing is not None else 0 lastDrawing = now @@ -489,7 +783,6 @@ def doPrintDraw(): if not printKeyListenerAttached: _visual_add_key_listener(visual, view, onPrintKey) printKeyListenerAttached = True - keySpeed = flags["keySpeed"] advancePosition = keySpeed * deltaTime / 1000 if keySpeed != -1 else len(content) - contentDrawnPosition contentDrawnPosition += advancePosition contentDrawnPosition = min(contentDrawnPosition, len(content)) @@ -502,6 +795,11 @@ def waitPrintDrawComplete(): def executor(resolve, _): array_push(drawingOnCompletes, lambda: resolve(None)) return promise_new(executor) + def clearPrint(): + array_splice(content, 0) + contentDrawnPosition = 0 + contentLastAttribute = RuneAttribute_clear + doPrintDraw() def recognizeTitle(text: list[Rune]) -> Optional[list[Rune]]: string = array_join(array_map(text, lambda r, *_: r.character), "") indexStart = string_index_of(string, "==== ") @@ -513,13 +811,47 @@ def recognizeTitle(text: list[Rune]) -> Optional[list[Rune]]: return None return array_slice(text, indexStart, indexEnd) _visual_add_connect_listener(visual, view, doPrintDraw) + + def encapsulatePromise(promise: Promise[Any], kwargs: dict, onSignalCb: Callable[[], Any] = None) -> Promise[Any]: + if "signal" not in kwargs or kwargs["signal"] is None: + return promise + doneFirst = False + def onResolve(result): + nonlocal doneFirst + doneFirst = True + return result + promise = promise_then(promise, onResolve) + signal = kwargs["signal"] + signalPromise = promise_from_abortsignal(signal) + if flags["doNotRaiseSignal"]: + def onReject(_): + nonlocal doneFirst + if doneFirst: + return signal + if onSignalCb is not None: + onSignalCb() + return signal + signalPromise = promise_catch(signalPromise, onSignalCb) + elif onSignalCb is not None: + def onReject(reason): + nonlocal doneFirst + if not doneFirst: + onSignalCb() + raise reason + signalPromise = promise_catch(signalPromise, onSignalCb) + return promise_race([promise, signalPromise]) + selectableIndex = -1 selectables: list[View] = [] selectableDescription: View = None selectableListeners: list[Callable[[str], None]] = [] lastSelectableIndex = -1 lastSelectables: list[View] = [] - def newSelectable(message: __Text, description: __Text, /, id: Any = None) -> None: + def newSelectable( + message: __Text, + description: __Text, /, + id: Any = None, + onChange: Callable[[bool], Any] = None): nonlocal contentLastAttribute message, contentLastAttribute = __parse_colored_text(message, contentLastAttribute) if description is not None: @@ -534,6 +866,7 @@ def newSelectable(message: __Text, description: __Text, /, id: Any = None) -> No selectable["selectableId"] = id selectable["selectableMessage"] = message selectable["selectableDescription"] = description + selectable["selectableOnChange"] = onChange array_push(selectables, selectable) layoutSelectable() selectableChange() @@ -568,10 +901,21 @@ def layoutSelectable(): view_set_height(selectableDescription, dim_from_absolute(2)) adornment_set_thickness(view_get_border(selectableDescription), Thickness(0, 1, 0, 0)) _visual_add_key_listener(visual, view, selectableOnKey) + selectableAlignment = flags["selectableAlignment"] for i in range(len(selectables)): selectable = selectables[i] - view_set_x(selectable, pos_from_center()) - view_set_y(selectable, pos_add(pos_from_center(), pos_from_absolute(i - (len(selectables) // 2) + 2))) + if string_starts_with(selectableAlignment, "Left"): + view_set_x(selectable, pos_from_absolute(0)) + if string_starts_with(selectableAlignment, "Center"): + view_set_x(selectable, pos_from_center()) + if string_starts_with(selectableAlignment, "Right"): + view_set_x(selectable, pos_from_end()) + if string_ends_with(selectableAlignment, "Top"): + view_set_y(selectable, pos_from_absolute(i + 2)) + if string_ends_with(selectableAlignment, "Middle"): + view_set_y(selectable, pos_add(pos_from_center(), pos_from_absolute(i - (len(selectables) // 2) + 2))) + if string_ends_with(selectableAlignment, "Bottom"): + view_set_y(selectable, pos_sub(pos_from_end(), pos_from_absolute(len(selectables) - i - 1))) view_set_width(selectable, dim_from_fill(0)) view_set_height(selectable, dim_from_absolute(1)) def selectableChange(): @@ -587,6 +931,9 @@ def selectableChange(): textFormatter = view_get_text_formatter(selectable) message = selectable["selectableMessage"] text_formatter_set_text(textFormatter, message) + onChange = selectable["selectableOnChange"] + if onChange is not None: + onChange(False) if selectableIndex != -1: selectable = selectables[selectableIndex] textFormatter = view_get_text_formatter(selectable) @@ -599,6 +946,9 @@ def selectableChange(): view_add_child(view, selectableDescription) else: view_remove_child(view, selectableDescription) + onChange = selectable["selectableOnChange"] + if onChange is not None: + onChange(True) else: view_remove_child(view, selectableDescription) lastSelectableIndex = selectableIndex @@ -632,28 +982,18 @@ def selectableOnKey(event: KeyEvent): selectableIndex = min(len(selectables) - 1, selectableIndex + 1) if selectableIndex != -1 else 0 selectableChange() return - def print(message: __Text, *args, end: __Text = "\n", **kwargs) -> Any: - nonlocal lastDrawing, contentLastAttribute - additionalContent, contentLastAttribute = __parse_colored_text(message, contentLastAttribute) - if flags["hasTitle"]: - titleText = recognizeTitle(additionalContent) - if titleText is not None: - additionalContent = [] - setTitle(titleText) - return - array_push(content, *additionalContent) - additionalContent, contentLastAttribute = __parse_colored_text(end, contentLastAttribute) - array_push(content, *additionalContent) - if lastDrawing is not None: - return - doPrintDraw() + inputParent: View = None inputDescription: View = None inputIndex = -1 inputViews: list[View] = [] lastInputIndex = -1 lastInputViews: list[View] = [] - def newInput(message: __Text, description: __Text, /, renderer: Callable[[list[Rune]], list[Rune]] = None, signal: AbortSignal = None): + def newInput( + message: __Text, + description: __Text, /, + renderer: Callable[[list[Rune]], list[Rune]] = None, + onChange: Callable[[str], Any] = None, signal: AbortSignal = None): nonlocal inputIndex, contentLastAttribute message, contentLastAttribute = __parse_colored_text(message, contentLastAttribute) if description is not None: @@ -670,6 +1010,7 @@ def newInput(message: __Text, description: __Text, /, renderer: Callable[[list[R inputView["inputOffset"] = 0 inputView["inputCursor"] = -1 inputView["inputValueRenderer"] = renderer + inputView["inputOnChange"] = onChange def update(): message = array_slice(inputView["inputMessage"]) done = inputView["inputDone"] @@ -743,8 +1084,9 @@ def layoutInput(): return if inputParent is None: inputParent = view_new(driver) + inputBoxOffset = flags["inputBoxOffset"] view_set_x(inputParent, pos_from_center()) - view_set_y(inputParent, pos_add(pos_from_center(), pos_from_absolute(1))) + view_set_y(inputParent, pos_add(pos_from_center(), pos_from_absolute(inputBoxOffset))) view_set_width(inputParent, dim_from_factor(0.8)) view_set_height(inputParent, dim_from_absolute(0)) adornment_set_thickness(view_get_border(inputParent), Thickness(1, 1, 1, 1)) @@ -896,19 +1238,6 @@ def restoreRendererIfAvailable(inputCurrent): inputCurrent["inputCursor"] = len(inputCurrent["inputValue"]) inputCurrent["inputUpdate"]() return - if event.key == "ControlBS": - if inputIndex == -1: - return - inputCurrent = inputViews[inputIndex] - value = inputCurrent["inputValue"] - cursor = inputCurrent["inputCursor"] - valueBefore = array_slice(value, 0, cursor - 1) - valueAfter = array_slice(value, cursor) - inputCurrent["inputValue"] = valueBefore + valueAfter - if inputCurrent["inputCursor"] > 0: - inputCurrent["inputCursor"] -= 1 - inputCurrent["inputUpdate"]() - return if event.code == "KeyA" and event.ctrlKey: if inputIndex == -1: return @@ -923,18 +1252,55 @@ def restoreRendererIfAvailable(inputCurrent): inputCurrent["__inputValueRenderer"] = None inputCurrent["inputUpdate"]() return + if event.key == "ControlBS": + if inputIndex == -1: + return + inputCurrent = inputViews[inputIndex] + value = inputCurrent["inputValue"] + cursor = inputCurrent["inputCursor"] + onChange = inputCurrent["inputOnChange"] + if cursor == 0: + return + valueBefore = array_slice(value, 0, cursor - 1) + valueAfter = array_slice(value, cursor) + inputCurrent["inputValue"] = valueBefore + valueAfter + if inputCurrent["inputCursor"] > 0: + inputCurrent["inputCursor"] -= 1 + inputCurrent["inputUpdate"]() + if onChange is not None: + onChange(inputCurrent["inputValue"]) + return if len(event.key) == 1: if inputIndex == -1: return inputCurrent = inputViews[inputIndex] value = inputCurrent["inputValue"] cursor = inputCurrent["inputCursor"] + onChange = inputCurrent["inputOnChange"] valueBefore = array_slice(value, 0, cursor) valueAfter = array_slice(value, cursor) inputCurrent["inputValue"] = valueBefore + event.key + valueAfter inputCurrent["inputCursor"] += 1 inputCurrent["inputUpdate"]() + if onChange is not None: + onChange(inputCurrent["inputValue"]) + return + + def print(message: __Text, *args, end: __Text = "\n", **kwargs) -> Any: + nonlocal lastDrawing, contentLastAttribute + additionalContent, contentLastAttribute = __parse_colored_text(message, contentLastAttribute) + if flags["hasTitle"]: + titleText = recognizeTitle(additionalContent) + if titleText is not None: + additionalContent = [] + setTitle(titleText) + return + array_push(content, *additionalContent) + additionalContent, contentLastAttribute = __parse_colored_text(end, contentLastAttribute) + array_push(content, *additionalContent) + if lastDrawing is not None: return + doPrintDraw() def input(message: __Text, *args, **kwargs) -> Any: if "selectable" in kwargs and kwargs["selectable"]: del kwargs["selectable"] @@ -947,36 +1313,8 @@ def input(message: __Text, *args, **kwargs) -> Any: if flags["inputWaitAfterContent"]: return encapsulatePromise(promise_then(waitPrintDrawComplete(), do), kwargs) return do() - def encapsulatePromise(promise: Promise[Any], kwargs: dict, onSignalCb: Callable[[], Any] = None) -> Promise[Any]: - if "signal" not in kwargs or kwargs["signal"] is None: - return promise - doneFirst = False - def onResolve(result): - nonlocal doneFirst - doneFirst = True - return result - promise = promise_then(promise, onResolve) - signal = kwargs["signal"] - signalPromise = promise_from_abortsignal(signal) - if flags["doNotRaiseSignal"]: - def onReject(_): - nonlocal doneFirst - if doneFirst: - return signal - if onSignalCb is not None: - onSignalCb() - return signal - signalPromise = promise_catch(signalPromise, onSignalCb) - elif onSignalCb is not None: - def onReject(reason): - nonlocal doneFirst - if not doneFirst: - onSignalCb() - raise reason - signalPromise = promise_catch(signalPromise, onSignalCb) - return promise_race([promise, signalPromise]) def metaAction(action: str, kwargs: dict) -> Any: - nonlocal contentLastAttribute + nonlocal contentDrawnPosition, contentLastAttribute if action == "getVisual": return visual if action == "getRootView": @@ -998,13 +1336,15 @@ def metaAction(action: str, kwargs: dict) -> Any: dict_clear(flags) dict_set(flags, lastFlag) return + if action == "foreign": + return __ConsoleMockClosureType((print, input, meta)) if action == "clear": - array_splice(content, 0) - drawnContentPosition = 0 - contentLastAttribute = RuneAttribute_clear + clearPrint() clearSelectables() clearInputs() - doPrintDraw() + return + if action == "clearPrint": + clearPrint() return if action == "waitContent": return encapsulatePromise(waitPrintDrawComplete(), kwargs) @@ -1024,7 +1364,6 @@ def cleanup(): return raise f"Unknown action {action}" def meta(*args, **kwargs) -> Any: - nonlocal contentDrawnPosition, contentLastAttribute if len(args) == 2 and type(args[0]) is str: name = args[0] value = args[1] @@ -1038,6 +1377,33 @@ def meta(*args, **kwargs) -> Any: return metaAction(action, kwargs) return (print, input, meta) +def __make_console_mock_closure_type(): + import builtins + def __init__(self, console: _ConsoleMock): + self.console = console + self.lastConsole = None + def __enter__(self): + lastPrint = builtins.print + lastInput = builtins.input + lastMeta = builtins.meta if hasattr(builtins, "meta") else None + self.lastConsole = (lastPrint, lastInput, lastMeta) + builtins.print = self.console[0] + builtins.input = self.console[1] + builtins.meta = self.console[2] + def __exit__(self, *_): + builtins.print = self.lastConsole[0] + builtins.input = self.lastConsole[1] + builtins.meta = self.lastConsole[2] + self.lastConsole = None + return False + ConsoleMockClosureType = type("ConsoleMockClosure", (object,), dict( + __init__=__init__, + __enter__=__enter__, + __exit__=__exit__ + )) + return ConsoleMockClosureType +__ConsoleMockClosureType = __make_console_mock_closure_type() + def _fg(r: Union[int, str] = None, g: int = None, b: int = None) -> str: if r is None and g is None and b is None: return "§q|" @@ -1074,8 +1440,13 @@ def __now() -> float: def __load_splash_suspendable(state, args): if state == SuspendableInitial: path, progress = args - rawFrames = csv_read_from_file(path) - rawFrames = array_map(rawFrames, lambda f, *_: array_join(f, "\n")) + rawFrames = None + if string_ends_with(path, ".gif.txt"): + rawFrames = csv_read_from_file(path) + rawFrames = array_map(rawFrames, lambda f, *_: array_join(f, "\n")) + if string_ends_with(path, ".png.txt"): + with open(path, encoding="utf-8") as f: + rawFrames = [f.read()] processedFrames = [[] for _ in range(len(rawFrames))] return 1, rawFrames, processedFrames, progress, 0, 0, RuneAttribute_clear if state == 1: @@ -1085,6 +1456,8 @@ def __load_splash_suspendable(state, args): i += 1 offset = 0 if i >= len(rawFrames): + if progress is not None: + progress(1) return SuspendableReturn, processedFrames rawFrame = rawFrames[i] endOffset = min(len(rawFrame), offset + 12259) diff --git a/src/game/user/__init__.py b/src/game/user/__init__.py index 7c2d0d1..61db0b3 100644 --- a/src/game/user/__init__.py +++ b/src/game/user/__init__.py @@ -1,7 +1,8 @@ -from .user import _user_get, _user_set, _user_get_all_npcs, _user_is_logged_in, _user_get_current, _user_register, _user_login, _user_logout +from .user import _user_get, _user_set, _user_new, _user_get_all_npcs, _user_is_logged_in, _user_get_current, _user_register, _user_login, _user_logout user_get = _user_get user_set = _user_set +user_new = _user_new user_get_all_npcs = _user_get_all_npcs user_is_logged_in = _user_is_logged_in user_get_current = _user_get_current diff --git a/src/game/user/user.py b/src/game/user/user.py index cf1c89a..0478256 100644 --- a/src/game/user/user.py +++ b/src/game/user/user.py @@ -1,6 +1,10 @@ from utils.primordials import * +from utils.coroutines import * +from utils.math import * from game.state import * from game.database import * +from game.monster import * +from game.inventory import * from typing import Optional, Union, Callable def _user_get(gameState: GameState, userId: int) -> UserSchemaType: @@ -13,6 +17,19 @@ def _user_set(gameState: GameState, userId: int, modifier: Union[UserSchemaType, database_set_entry_at(userDatabase, userId, user) return user +def _user_new(gameState: GameState) -> UserSchemaType: + userDatabase = gamestate_get_user_database(gameState) + userId = database_get_entries_length(userDatabase) + user = UserSchemaType( + id=userId, + username=None, + password=None, + role=None, + money=None, + ) + database_set_entry_at(userDatabase, userId, user) + return user + def _user_get_all_npcs(gameState: GameState) -> list[UserSchemaType]: userDatabase = gamestate_get_user_database(gameState) userEntries = database_get_entries(userDatabase) @@ -32,87 +49,100 @@ def _user_get_current(gameState: GameState) -> Optional[UserSchemaType]: user_database = gamestate_get_user_database(gameState) return database_get_entry_at(user_database, userId) -def _user_register(gameState: GameState, new_username: str, new_password: str) -> None: - ''' - Prosedur untuk register user baru - ''' - user_database = gamestate_get_user_database(gameState) - user_entries = database_get_entries(user_database) - # mengecek apakah user sedang login atau tidak - if _user_is_logged_in(gameState): - print("Anda sudah login, logout dulu untuk register!") - return - - # input username baru - # new_username: str = input("Masukan username: ") +def _user_register(state, args) -> None: + if state is SuspendableInitial: + gameState, console, new_username, new_password = args + print, input, meta = console + ''' + Prosedur untuk register user baru + ''' + user_database = gamestate_get_user_database(gameState) + user_entries = database_get_entries(user_database) + # mengecek apakah user sedang login atau tidak + if _user_is_logged_in(gameState): + print("Anda sudah login, logout dulu untuk register!") + return SuspendableReturn, None - # mengecek apakah karakter dalam username valid - user_false_input: bool = True - valid_username: str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_1234567890" - - for i in range (len(new_username)): - for j in range (len(valid_username)): - if new_username[i] != valid_username[j]: - user_false_input = True - else: - user_false_input = False + # input username baru + # new_username: str = input("Masukan username: ") + + # mengecek apakah karakter dalam username valid + user_false_input: bool = True + valid_username: str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_1234567890" + + for i in range (len(new_username)): + for j in range (len(valid_username)): + if new_username[i] != valid_username[j]: + user_false_input = True + else: + user_false_input = False + break + if user_false_input == True: break + if user_false_input == True: - break + print("Username hanya boleh berisi alfabet, angka, underscore, dan strip!") + return SuspendableReturn, None + else: + # mengecek apakah username sudah ada di database + for i in range (len(user_entries)): + if new_username == user_entries[i].username: + print(f"Username {new_username} sudah terpakai, silahkan gunakan username lain!") + return SuspendableReturn, None + + # input password + # new_password: str = input("Masukan password: ") - if user_false_input == True: - print("Username hanya boleh berisi alfabet, angka, underscore, dan strip!") - return - else: - # mengecek apakah username sudah ada di database - for i in range (len(user_entries)): - if new_username == user_entries[i].username: - print(f"Username {new_username} sudah terpakai, silahkan gunakan username lain!") - return + # input pilihan monster starter + availableMonsters = monster_get_all_monsters(gameState) + availableMonsters = array_filter(availableMonsters, lambda m, *_: m.level == 1) + availableMonsters = array_map(rand_uniq_int_array(0, len(availableMonsters), min(5, len(availableMonsters))), lambda i, *_: availableMonsters[i]) + print("Silakan pilih salah satu monster sebagai monster awalmu.") + for availableMonster in availableMonsters: + description = f"L: {availableMonster.level} HP: {availableMonster.healthPoints} ATK: {availableMonster.attackPower} DEF: {availableMonster.defensePower}" + input(f"{availableMonster.name}", description, id=availableMonster.id, selectable=True) + meta(action="pushFlags") + meta("selectableAllowEscape", False) + selection = meta(action="select") + return "choose_monster", gameState, console, new_username, new_password, selection + if state == "choose_monster": + gameState, console, new_username, new_password, selection = args + print, input, meta = console + meta(action="popFlags") + + # writing new user to database + # id itu zero-based, jadi langsung pakai length() untuk mendapatkan id selanjutnya. + user = _user_new(gameState) + user = _user_set(gameState, user.id, namedtuple_with(user, + username=new_username, + password=new_password, + role="agent", + money=0 + )) + + monsterId = selection + monster = monster_get(gameState, monsterId) + inventoryMonster = inventory_monster_new(gameState) + inventoryMonster = inventory_monster_set(gameState, inventoryMonster.id, namedtuple_with(inventoryMonster, + ownerId=user.id, + referenceId=monsterId, + name=monster.name, + experiencePoints=0, + healthPoints=monster.healthPoints, + attackPower=monster.attackPower, + defensePower=monster.defensePower, + activePotions=[], + )) - # input password - # new_password: str = input("Masukan password: ") - - # input pilihan monster starter - monster_false: bool = True - input_salah: bool = False - while monster_false == True: - if input_salah == True: - print("Pilihan hanya '1', '2', atau '3'! Pilih salah satu!\n") - print(''' -Silakan pilih salah satu monster sebagai monster awalmu. -1. Monster1 -2. Monster2 -3. Monster3 -Input angka saja, contoh: input '1' untuk memilih Monster1 - ''') - starter_num: str = input("Monster pilihanmu: ") - if starter_num == "1" or starter_num == "2" or starter_num == "3": - monster_false = False - else: - input_salah = True + gamestate_set_user_id(gameState, user.id) - starter_choice: str - if starter_num == '1': - starter_choice = "Monster1" - elif starter_num == '2': - starter_choice = "Monster2" - else: - starter_choice = "Monster3" + meta(action="clear") + print(f"Selamat datang Agent {new_username}. Mari kita mengalahkan Dr. Asep Spakbor dengan {inventoryMonster.name}!") + input("Lanjut", selectable=True) + selection = meta(action="select") - print(f"Selamat datang Agent {new_username}. Mari kita mengalahkan Dr. Asep Spakbor dengan {starter_choice}!") - - # writing new user to database - # id itu zero-based, jadi langsung pakai length() untuk mendapatkan id selanjutnya. - new_id = database_get_entries_length(user_database) - database_set_entry_at(user_database, new_id, - UserSchemaType(id = new_id, - username = new_username, - password = new_password, - role = "agent", - money = 0)) - gamestate_set_user_id(gameState, new_id) - return + return SuspendableReturn, None, selection + return SuspendableExhausted def _user_login(gameState: GameState, username: str, password: str) -> None: ''' @@ -153,7 +183,6 @@ def _user_login(gameState: GameState, username: str, password: str) -> None: else: # benar print(f''' Selamat datang, Agent {username}! -Masukkan command “help” untuk daftar command yang dapat kamu panggil. ''') newly_logged_in_id = user_entries[user_index].id gamestate_set_user_id(gameState, newly_logged_in_id) diff --git a/src/utils/console/__init__.py b/src/utils/console/__init__.py index 667ed59..c19b5bd 100644 --- a/src/utils/console/__init__.py +++ b/src/utils/console/__init__.py @@ -1,4 +1,4 @@ -from .console import _Direction, _PosType, _Pos, _PosAbsolute, _PosFactor, _PosCenter, _PosEnd, _PosCombine, _PosView, _PosFunction, _pos_from_absolute, _pos_from_factor, _pos_from_center, _pos_from_end, _pos_from_combine, _pos_from_view, _pos_from_function, _pos_as_absolute, _pos_as_factor, _pos_as_center, _pos_as_end, _pos_as_combine, _pos_as_view, _pos_as_function, _pos_anchor, _pos_absolute_anchor, _pos_factor_anchor, _pos_center_anchor, _pos_end_anchor, _pos_combine_anchor, _pos_view_anchor, _pos_function_anchor, _pos_calculate, _pos_absolute_calculate, _pos_factor_calculate, _pos_center_calculate, _pos_end_calculate, _pos_combine_calculate, _pos_view_calculate, _pos_function_calculate, _pos_add, _pos_sub, _pos_from_view_left, _pos_from_view_top, _pos_from_view_right, _pos_from_view_bottom, _pos_from_view_x, _pos_from_view_y, _DimType, _Dim, _DimAbsolute, _DimFactor, _DimFill, _DimCombine, _DimView, _DimFunction, _DimAutoMode, _DimAuto, _dim_from_absolute, _dim_from_factor, _dim_from_fill, _dim_from_combine, _dim_from_view, _dim_from_function, _dim_from_auto, _dim_as_absolute, _dim_as_factor, _dim_as_fill, _dim_as_combine, _dim_as_view, _dim_as_function, _dim_as_auto, _dim_anchor, _dim_absolute_anchor, _dim_factor_anchor, _dim_fill_anchor, _dim_combine_anchor, _dim_view_anchor, _dim_function_anchor, _dim_auto_anchor, _dim_calculate, _dim_absolute_calculate, _dim_factor_calculate, _dim_fill_calculate, _dim_combine_calculate, _dim_view_calculate, _dim_function_calculate, _dim_auto_calculate, _dim_add, _dim_sub, _dim_from_view_width, _dim_from_view_height, _RuneAttribute, _Rune, _RuneAttribute_clear, _Rune_clear, _rune_from_string, _DriverAttribute, _Driver, _driver_new, _driver_get_size, _driver_get_attribtue, _driver_get_rune_width, _driver_get_rune_height, _driver_set_size, _driver_set_content, _driver_clear_content, _driver_fill_rect, _driver_clear_rect, _driver_tick, _driver_draw, _TextHorizontalAlignment, _TextVerticalAlignment, _TextDirection, _text_direction_is_horizontal, _text_direction_is_vertical, _text_direction_is_left_to_right, _text_direction_is_top_to_bottom, _TextFormatter, _text_formatter_new, _text_formatter_get_text, _text_formatter_get_auto_size, _text_formatter_get_size, _text_formatter_get_multiline, _text_formatter_get_wordwrap, _text_formatter_get_direction, _text_formatter_get_horizontal_alignment, _text_formatter_get_vertical_alignment, _text_formatter_get_lines, _text_formatter_set_text, _text_formatter_set_auto_size, _text_formatter_set_size, _text_formatter_set_multiline, _text_formatter_set_wordwrap, _text_formatter_set_direction, _text_formatter_set_horizontal_alignment, _text_formatter_set_vertical_alignment, _text_formatter_draw, _text_formatter_get_computed_lines, _text_formatter_get_calculated_auto_size, _Thickness, _EmptyThickness, _thickness_new, _thickness_get_horizontal, _thickness_get_vertical, _thickness_compute_inside, _thickness_compute_outside, _thickness_draw, _thickness_with_horizontal, _thickness_with_vertical, _thickness_eq, _thickness_neq, _thickness_add, _thickness_sub, _Adornment, _adornment_new, _adornment_get_frame, _adornment_get_thickness, _adornment_get_viewport_position, _adornment_get_viewport_size, _adornment_set_frame, _adornment_set_thickness, _adornment_set_viewport_position, _adornment_set_viewport_size, _adornment_mark_recompute_viewport_size, _adornment_recompute_viewport_size, _adornment_mark_recompute_layout, _adornment_recompute_layout, _adornment_mark_recompute_display, _adornment_draw, _Margin, _margin_new, _Border, _border_new, _border_draw, _Padding, _padding_new, _LayoutMode, _View, _view_new, _view_get_x, _view_get_y, _view_get_width, _view_get_height, _view_get_margin, _view_get_border, _view_get_padding, _view_get_frame, _view_get_content_size, _view_get_viewport_position, _view_get_viewport_size, _view_get_text_formatter, _view_get_layout_mode, _view_set_x, _view_set_y, _view_set_width, _view_set_height, _view_set_frame, _view_set_content_size, _view_set_viewport_position, _view_set_viewport_size, _view_has_child, _view_add_child, _view_remove_child, _view_resolve_computed_layout, _view_mark_recompute_viewport_size, _view_recompute_viewport_size, _view_mark_recompute_layout, _view_recompute_layout, _view_mark_recompute_display, _view_transform_frame_to_screen, _view_transform_viewport_to_screen, _view_transform_content_to_screen, _view_draw +from .console import _Direction, _PosType, _Pos, _PosAbsolute, _PosFactor, _PosCenter, _PosEnd, _PosCombine, _PosView, _PosFunction, _pos_from_absolute, _pos_from_factor, _pos_from_center, _pos_from_end, _pos_from_combine, _pos_from_view, _pos_from_function, _pos_as_absolute, _pos_as_factor, _pos_as_center, _pos_as_end, _pos_as_combine, _pos_as_view, _pos_as_function, _pos_anchor, _pos_absolute_anchor, _pos_factor_anchor, _pos_center_anchor, _pos_end_anchor, _pos_combine_anchor, _pos_view_anchor, _pos_function_anchor, _pos_calculate, _pos_absolute_calculate, _pos_factor_calculate, _pos_center_calculate, _pos_end_calculate, _pos_combine_calculate, _pos_view_calculate, _pos_function_calculate, _pos_add, _pos_sub, _pos_from_view_left, _pos_from_view_top, _pos_from_view_right, _pos_from_view_bottom, _pos_from_view_x, _pos_from_view_y, _DimType, _Dim, _DimAbsolute, _DimFactor, _DimFill, _DimCombine, _DimView, _DimFunction, _DimAutoMode, _DimAuto, _dim_from_absolute, _dim_from_factor, _dim_from_fill, _dim_from_combine, _dim_from_view, _dim_from_function, _dim_from_auto, _dim_as_absolute, _dim_as_factor, _dim_as_fill, _dim_as_combine, _dim_as_view, _dim_as_function, _dim_as_auto, _dim_anchor, _dim_absolute_anchor, _dim_factor_anchor, _dim_fill_anchor, _dim_combine_anchor, _dim_view_anchor, _dim_function_anchor, _dim_auto_anchor, _dim_calculate, _dim_absolute_calculate, _dim_factor_calculate, _dim_fill_calculate, _dim_combine_calculate, _dim_view_calculate, _dim_function_calculate, _dim_auto_calculate, _dim_add, _dim_sub, _dim_from_view_width, _dim_from_view_height, _RuneAttribute, _Rune, _RuneAttribute_clear, _Rune_clear, _rune_from_string, _DriverAttribute, _Driver, _driver_new, _driver_get_size, _driver_get_attribtue, _driver_get_rune_width, _driver_get_rune_height, _driver_set_size, _driver_set_content, _driver_clear_content, _driver_fill_rect, _driver_clear_rect, _driver_tick, _driver_draw, _TextHorizontalAlignment, _TextVerticalAlignment, _TextDirection, _text_direction_is_horizontal, _text_direction_is_vertical, _text_direction_is_left_to_right, _text_direction_is_top_to_bottom, _TextFormatter, _text_formatter_new, _text_formatter_get_text, _text_formatter_get_auto_size, _text_formatter_get_size, _text_formatter_get_multiline, _text_formatter_get_wordwrap, _text_formatter_get_direction, _text_formatter_get_horizontal_alignment, _text_formatter_get_vertical_alignment, _text_formatter_get_lines, _text_formatter_set_text, _text_formatter_set_auto_size, _text_formatter_set_size, _text_formatter_set_multiline, _text_formatter_set_wordwrap, _text_formatter_set_direction, _text_formatter_set_horizontal_alignment, _text_formatter_set_vertical_alignment, _text_formatter_draw, _text_formatter_get_calculated_auto_size, _Thickness, _EmptyThickness, _thickness_new, _thickness_get_horizontal, _thickness_get_vertical, _thickness_compute_inside, _thickness_compute_outside, _thickness_draw, _thickness_with_horizontal, _thickness_with_vertical, _thickness_eq, _thickness_neq, _thickness_add, _thickness_sub, _Adornment, _adornment_new, _adornment_get_frame, _adornment_get_thickness, _adornment_get_viewport_position, _adornment_get_viewport_size, _adornment_set_frame, _adornment_set_thickness, _adornment_set_viewport_position, _adornment_set_viewport_size, _adornment_mark_recompute_viewport_size, _adornment_recompute_viewport_size, _adornment_mark_recompute_layout, _adornment_recompute_layout, _adornment_mark_recompute_display, _adornment_draw, _Margin, _margin_new, _Border, _border_new, _border_draw, _Padding, _padding_new, _LayoutMode, _View, _view_new, _view_get_x, _view_get_y, _view_get_width, _view_get_height, _view_get_margin, _view_get_border, _view_get_padding, _view_get_frame, _view_get_content_size, _view_get_viewport_position, _view_get_viewport_size, _view_get_text_formatter, _view_get_layout_mode, _view_set_x, _view_set_y, _view_set_width, _view_set_height, _view_set_frame, _view_set_content_size, _view_set_viewport_position, _view_set_viewport_size, _view_has_child, _view_add_child, _view_remove_child, _view_resolve_computed_layout, _view_mark_recompute_viewport_size, _view_recompute_viewport_size, _view_mark_recompute_layout, _view_recompute_layout, _view_mark_recompute_display, _view_transform_frame_to_screen, _view_transform_frame_to_superview, _view_transform_viewport_to_screen, _view_transform_content_to_screen, _view_draw Direction = _Direction PosType = _PosType @@ -138,7 +138,6 @@ text_formatter_set_horizontal_alignment = _text_formatter_set_horizontal_alignment text_formatter_set_vertical_alignment = _text_formatter_set_vertical_alignment text_formatter_draw = _text_formatter_draw -text_formatter_get_computed_lines = _text_formatter_get_computed_lines text_formatter_get_calculated_auto_size = _text_formatter_get_calculated_auto_size Thickness = _Thickness EmptyThickness = _EmptyThickness @@ -211,6 +210,7 @@ view_recompute_layout = _view_recompute_layout view_mark_recompute_display = _view_mark_recompute_display view_transform_frame_to_screen = _view_transform_frame_to_screen +view_transform_frame_to_superview = _view_transform_frame_to_superview view_transform_viewport_to_screen = _view_transform_viewport_to_screen view_transform_content_to_screen = _view_transform_content_to_screen view_draw = _view_draw diff --git a/src/utils/console/console.py b/src/utils/console/console.py index 5944e50..ed8aee2 100644 --- a/src/utils/console/console.py +++ b/src/utils/console/console.py @@ -180,8 +180,20 @@ def _pos_combine_calculate(pos: _PosCombine, scalar: float, direction: _Directio leftAnchor = _pos_calculate(pos["left"], scalar, direction, dim, view) rightAnchor = _pos_calculate(pos["right"], scalar, direction, dim, view) return leftAnchor + rightAnchor if pos["add"] else leftAnchor - rightAnchor -def _pos_view_calculate(pos: _PosView, scalar: float, direction: _Direction, dim: "_Dim", view: "_View") -> float: - return _pos_view_anchor(pos, scalar) +def _pos_view_calculate(pos: _PosView, scalar: float, direction: _Direction, dim: "_Dim", view0: "_View") -> float: + view = pos["view"] + side = pos["side"] + frame = view["frame"] + offset = _view_transform_frame_to_superview(view, view0["superview"]) + if side == 0: + return offset.x + if side == 1: + return offset.y + if side == 2: + return offset.x + frame.w + if side == 3: + return offset.y + frame.h + return None def _pos_function_calculate(pos: _PosFunction, scalar: float, direction: _Direction, dim: "_Dim", view: "_View") -> float: return _pos_function_anchor(pos, scalar) @@ -377,7 +389,7 @@ def _dim_combine_calculate(dim: _DimCombine, scalar: float, direction: _Directio rightAnchor = _dim_calculate(dim["right"], scalar, direction, position, view) return leftAnchor + rightAnchor if dim["add"] else max(0, leftAnchor - rightAnchor) def _dim_view_calculate(dim: _DimView, scalar: float, direction: _Direction, position: float, view: "_View") -> float: - return max(0, _dim_view_calculate(dim, scalar - position)) + return max(0, _dim_view_anchor(dim, scalar - position)) def _dim_function_calculate(dim: _DimFunction, scalar: float, direction: _Direction, position: float, view: "_View") -> float: return max(0, _dim_function_anchor(dim, scalar - position)) def _dim_auto_calculate(dim: _DimAuto, scalar: float, direction: _Direction, position: float, view: "_View") -> float: @@ -425,9 +437,9 @@ def _dim_sub(left: _Dim, right: _Dim) -> _Dim: return _dim_from_absolute(_dim_anchor(left, 0) - _dim_anchor(right, 0)) return _dim_from_combine(False, left, right) def _dim_from_view_width(view: "_View") -> _Dim: - return _dim_from_view(view, 1) -def _dim_from_view_height(view: "_View") -> _Dim: return _dim_from_view(view, 0) +def _dim_from_view_height(view: "_View") -> _Dim: + return _dim_from_view(view, 1) _RuneAttribute = NamedTuple("RuneAttribute", [ ("font", int), @@ -647,7 +659,7 @@ def _text_formatter_get_vertical_alignment(textFormatter: _TextFormatter) -> _Te def _text_formatter_get_lines(textFormatter: _TextFormatter) -> list[list[_Rune]]: if not textFormatter["recompute"]: return textFormatter["lines"] - lines = _text_formatter_get_computed_lines(textFormatter) + lines = __text_formatter_get_computed_lines(textFormatter) textFormatter["lines"] = lines textFormatter["recompute"] = False return lines @@ -714,7 +726,7 @@ def matchSizeIndexAtPosition(sizes: list[int], offsetIndex: int, position: int) startY = screen.y + screen.h - lineTotalSize lineOffset = 0 if startY < 0: - for i in range(8): + for i in range(32): lineOffset = matchSizeIndexAtPosition(lineSizes, 0, -startY + (i // 2) * (1 if i % 2 == 0 else -1)) if lineOffset is not None: break @@ -736,7 +748,7 @@ def matchSizeIndexAtPosition(sizes: list[int], offsetIndex: int, position: int) startX = screen.x + screen.w - textSize runeOffset = 0 if startX < 0: - for i in range(8): + for i in range(32): runeOffset = matchSizeIndexAtPosition(runeSizes, 0, -startX + (i // 2) * (1 if i % 2 == 0 else -1)) if runeOffset is not None: break @@ -760,7 +772,7 @@ def matchSizeIndexAtPosition(sizes: list[int], offsetIndex: int, position: int) startX = screen.x + screen.w - lineTotalSize lineOffset = 0 if startX < 0: - for i in range(8): + for i in range(32): lineOffset = matchSizeIndexAtPosition(lineSizes, 0, -startX + (i // 2) * (1 if i % 2 == 0 else -1)) if lineOffset is not None: break @@ -782,7 +794,7 @@ def matchSizeIndexAtPosition(sizes: list[int], offsetIndex: int, position: int) startY = screen.y + screen.h - textSize runeOffset = 0 if startY < 0: - for i in range(8): + for i in range(32): runeOffset = matchSizeIndexAtPosition(runeSizes, 0, -startY + (i // 2) * (1 if i % 2 == 0 else -1)) if runeOffset is not None: break @@ -795,7 +807,7 @@ def matchSizeIndexAtPosition(sizes: list[int], offsetIndex: int, position: int) rune = text[runeIndex] _driver_set_content(driver, x, y, rune) pass -def _text_formatter_get_computed_lines(textFormatter: _TextFormatter) -> list[list[_Rune]]: +def __text_formatter_get_computed_lines(textFormatter: _TextFormatter) -> list[list[_Rune]]: driver = textFormatter["driver"] text = textFormatter["text"] multiline = textFormatter["multiline"] @@ -1602,6 +1614,19 @@ def _view_transform_frame_to_screen(view: _View) -> Point: result = Point(result.x + currentViewportOffset.x + currentFrame.x - currentViewportPosition.x, result.y + currentViewportOffset.y + currentFrame.y - currentViewportPosition.y) current = current["superview"] return result +def _view_transform_frame_to_superview(view: _View, superview: _View) -> Point: + result = rectangle_get_point(view["frame"]) + current = view["superview"] + while current is not None and current is not superview: + currentFrame = current["frame"] + currentViewportPosition = current["viewportPosition"] + currentPadding = current["padding"] + currentViewportOffset = _thickness_compute_inside(_adornment_get_thickness(currentPadding), _adornment_get_frame(currentPadding)) + result = Point(result.x + currentViewportOffset.x + currentFrame.x - currentViewportPosition.x, result.y + currentViewportOffset.y + currentFrame.y - currentViewportPosition.y) + current = current["superview"] + if current is not superview: + return None + return result def _view_transform_viewport_to_screen(view: _View, point: Point) -> Point: screen = _view_transform_frame_to_screen(view) padding = view["padding"] diff --git a/src/utils/coroutines/__init__.py b/src/utils/coroutines/__init__.py index 0369f3b..85841ec 100644 --- a/src/utils/coroutines/__init__.py +++ b/src/utils/coroutines/__init__.py @@ -1,18 +1,13 @@ -from .coroutines import _Timer, _Pollable, _Looper, _looper_new, _looper_get_current, _ClosureType, _looper_closure, _looper_needs_tick, _looper_tick, _looper_tick_timers, _looper_tick_pollables, _looper_tick_immediates, _looper_tick_microtasks, _set_timeout, _set_interval, _clear_timeout, _clear_interval, _set_immediate, _next_tick, _PromiseState, _PromiseValue, _PromiseChainValue, Promise, _PromiseResolve, _PromiseReject, _promise_new, _as_promise, _promise_then, _promise_catch, _promise_finally, _promise_resolved, _promise_rejected, _promise_all, _promise_all_settled, _promise_any, _promise_race, _Suspendable, _SuspendableInitial, _SuspendableReturn, _SuspendableExhausted, _promise_from_suspendable, _promise_from_wait, _AbortSignal, _AbortController, _as_abortsignal, _abortsignal_is_aborted, _abortsignal_get_reason, _abortsignal_on_abort, _abortsignal_abort, _abortsignal_any, _abortsignal_timeout, _abortcontroller_new, _abortcontroller_get_signal, _abortcontroller_abort, _promise_from_abortsignal +from .coroutines import _Timer, _Pollable, _Looper, _looper_new, _looper_get_current, _looper_closure, _looper_needs_tick, _looper_tick, _set_timeout, _set_interval, _clear_timeout, _clear_interval, _set_immediate, _next_tick, _PromiseState, _PromiseValue, _PromiseChainValue, Promise, _PromiseResolve, _PromiseReject, _promise_new, _as_promise, _promise_then, _promise_catch, _promise_finally, _promise_resolved, _promise_rejected, _promise_all, _promise_all_settled, _promise_any, _promise_race, _Suspendable, _SuspendableInitial, _SuspendableReturn, _SuspendableExhausted, _promise_from_suspendable, _promise_from_wait, _AbortSignal, _AbortController, _as_abortsignal, _abortsignal_is_aborted, _abortsignal_get_reason, _abortsignal_on_abort, _abortsignal_abort, _abortsignal_any, _abortsignal_timeout, _abortcontroller_new, _abortcontroller_get_signal, _abortcontroller_abort, _promise_from_abortsignal Timer = _Timer Pollable = _Pollable Looper = _Looper looper_new = _looper_new looper_get_current = _looper_get_current -ClosureType = _ClosureType looper_closure = _looper_closure looper_needs_tick = _looper_needs_tick looper_tick = _looper_tick -looper_tick_timers = _looper_tick_timers -looper_tick_pollables = _looper_tick_pollables -looper_tick_immediates = _looper_tick_immediates -looper_tick_microtasks = _looper_tick_microtasks set_timeout = _set_timeout set_interval = _set_interval clear_timeout = _clear_timeout diff --git a/src/utils/coroutines/coroutines.py b/src/utils/coroutines/coroutines.py index 7baa2e9..b4485e0 100644 --- a/src/utils/coroutines/coroutines.py +++ b/src/utils/coroutines/coroutines.py @@ -50,7 +50,7 @@ def _looper_get_current() -> _Looper: global __looper_current_id, __loopers return __loopers[__looper_current_id] -def __looper_make_closure_type(): +def __make_looper_closure_type(): def __init__(self, looperId: int): self.looperId = looperId self.lastLooperId = -1 @@ -63,16 +63,16 @@ def __exit__(self, *_): __looper_current_id = self.lastLooperId self.lastLooperId = -1 return False - ClosureType = type("Closure", (object,), dict( + LooperClosureType = type("LooperClosure", (object,), dict( __init__=__init__, __enter__=__enter__, __exit__=__exit__ )) - return ClosureType -_ClosureType = __looper_make_closure_type() + return LooperClosureType +__LooperClosureType = __make_looper_closure_type() -def _looper_closure(looperId: int) -> _ClosureType: - return _ClosureType(looperId) +def _looper_closure(looperId: int) -> __LooperClosureType: + return __LooperClosureType(looperId) def _looper_needs_tick(looperId: int) -> bool: looper = __loopers[looperId] @@ -88,16 +88,17 @@ def _looper_tick(looperId: int) -> None: __looper_current_id = looperId try: looper = _looper_get_current() - _looper_tick_timers(looper) - _looper_tick_microtasks(looper) - _looper_tick_pollables(looper) - _looper_tick_microtasks(looper) - _looper_tick_immediates(looper) - _looper_tick_microtasks(looper) + __looper_tick_timers(looper) + __looper_tick_microtasks(looper) + __looper_tick_pollables(looper) + __looper_tick_microtasks(looper) + __looper_tick_immediates(looper) + __looper_tick_microtasks(looper) + __looper_wait_next_timers(looper) finally: __looper_current_id = lastLooperId -def _looper_tick_timers(looper: _Looper) -> None: +def __looper_tick_timers(looper: _Looper) -> None: timers = looper["timers"] timersCopy = array_slice(timers) now = __now() @@ -110,7 +111,15 @@ def _looper_tick_timers(looper: _Looper) -> None: if not timer["repeat"]: array_splice(timers, array_index_of(timers, timer), 1) -def _looper_tick_pollables(looper: _Looper) -> None: +def __looper_wait_next_timers(looper: _Looper) -> None: + now = __now() + timers = looper["timers"] + sleepTime = array_reduce(timers, lambda c, t, *_: min(c, t["anchor"] + t["timeout"] - now), 150) + if sleepTime <= 5: + return + time.sleep(sleepTime / 1000) + +def __looper_tick_pollables(looper: _Looper) -> None: pollables = looper["pollables"] nextPollables = array_map(pollables, lambda p, *_: p["callback"]()) for i in range(len(nextPollables) - 1, -1, -1): @@ -118,12 +127,12 @@ def _looper_tick_pollables(looper: _Looper) -> None: continue array_splice(pollables, i, 1) -def _looper_tick_immediates(looper: _Looper) -> None: +def __looper_tick_immediates(looper: _Looper) -> None: immediates = array_splice(looper["immediates"], 0) for immediate in immediates: immediate() -def _looper_tick_microtasks(looper: _Looper) -> None: +def __looper_tick_microtasks(looper: _Looper) -> None: while True: microtasks = array_splice(looper["microtasks"], 0) for microtask in microtasks: @@ -463,7 +472,7 @@ def onResolved(result: list[Any]): result = handle(state, (*args,)) if _as_promise(result) is None: if result is _SuspendableExhausted: - raise "Exhausted state" + raise BaseException(f"Exhausted state {state}") if type(result) is tuple: state, *args = result else: @@ -474,7 +483,7 @@ def onResolved(result: list[Any]): def onResolved(result: tuple): nonlocal state, args if result is _SuspendableExhausted: - raise "Exhausted state" + raise BaseException(f"Exhausted state {state}") if type(result) is tuple: state, *args = result else: diff --git a/src/utils/generateinit.mjs b/src/utils/generateinit.mjs index 663b2c7..3b74982 100644 --- a/src/utils/generateinit.mjs +++ b/src/utils/generateinit.mjs @@ -16,13 +16,15 @@ const __dirname = process.cwd(); for(const file of files) { const moduleName = path.basename(file).split(".")[0]; const contents = await fs.readFile(file); - const regex = /^(?:(_{0,1}[a-zA-Z][^\s]*)\s*=|def\s*(_{0,1}[a-zA-Z][^\s(]*))/gm; + const regex = /^(?:(_{0,1}[a-zA-Z][^\.\s]*)\s*=|def\s*(_{0,1}[a-zA-Z][^\.\s(]*))/gm; const exports = []; let matcher; while((matcher = regex.exec(contents)) != null) exports.push(matcher[1] || matcher[2]); - if(exports.length == 0) + if(exports.length == 0) { + // result += `__import__("${moduleName}", globals(), locals(), [], level=1)`; continue + } result += `from .${moduleName} import ${exports.join(", ")}\n\n`; result += `${exports.map(e => `${e.startsWith("_") ? e.slice(1) : e} = ${e}`).join("\n")}\n\n` } diff --git a/src/utils/primordials/__init__.py b/src/utils/primordials/__init__.py index e2b8349..2bb820b 100644 --- a/src/utils/primordials/__init__.py +++ b/src/utils/primordials/__init__.py @@ -1,4 +1,4 @@ -from .primordials import _string_concat, _string_slice, _string_substring, _string_code_point_at, _string_index_of, _string_last_index_of, _string_includes, _string_starts_with, _string_ends_with, _string_pad_start, _string_pad_end, _string_repeat, _string_to_upper_case, _string_to_lower_case, _string_whitespace_characters, _string_line_terminators_characters, _string_trim_end, _string_trim_start, _string_trim, _string_replace, _string_replace_all, _string_split, _array_push, _array_pop, _array_unshift, _array_shift, _array_concat, _array_slice, _array_splice, _array_copy_within, _array_every, _array_some, _array_index_of, _array_last_index_of, _array_includes, _array_find, _array_find_index, _array_find_last, _array_find_last_index, _array_for_each, _array_filter, _array_map, _array_flat, _array_flat_map, _array_join, _array_reduce, _array_reduce_right, _array_reverse, _array_sort, _array_to_reversed, _array_to_sorted, _array_to_spliced, _array_with, _tuple_to_array, _tuple_with, _namedtuple_to_dict, _namedtuple_with, _dict_clear, _dict_set, _dict_with, _dict_key_of +from .primordials import _string_concat, _string_slice, _string_substring, _string_code_point_at, _string_index_of, _string_last_index_of, _string_includes, _string_starts_with, _string_ends_with, _string_pad_start, _string_pad_end, _string_repeat, _string_to_upper_case, _string_to_lower_case, _string_whitespace_characters, _string_line_terminators_characters, _string_trim_end, _string_trim_start, _string_trim, _string_replace, _string_replace_all, _string_split, _array_push, _array_pop, _array_unshift, _array_shift, _array_concat, _array_slice, _array_splice, _array_copy_within, _array_every, _array_some, _array_index_of, _array_last_index_of, _array_includes, _array_find, _array_find_index, _array_find_last, _array_find_last_index, _array_for_each, _array_filter, _array_map, _array_flat, _array_flat_map, _array_join, _array_reduce, _array_reduce_right, _array_reverse, _array_sort, _array_to_reversed, _array_to_sorted, _array_to_spliced, _array_with, _parse_int, _tuple_to_array, _tuple_with, _namedtuple_to_dict, _namedtuple_with, _dict_clear, _dict_set, _dict_with, _dict_key_of string_concat = _string_concat string_slice = _string_slice @@ -53,6 +53,7 @@ array_to_sorted = _array_to_sorted array_to_spliced = _array_to_spliced array_with = _array_with +parse_int = _parse_int tuple_to_array = _tuple_to_array tuple_with = _tuple_with namedtuple_to_dict = _namedtuple_to_dict diff --git a/src/utils/primordials/primordials.py b/src/utils/primordials/primordials.py index 15ff6a5..727ba80 100644 --- a/src/utils/primordials/primordials.py +++ b/src/utils/primordials/primordials.py @@ -1,4 +1,4 @@ -from typing import Any, TypeVar, Callable, Union, NamedTuple, Type +from typing import Any, TypeVar, Callable, Union, NamedTuple, Type, Optional __T = TypeVar("__T") __U = TypeVar("__U") @@ -41,7 +41,7 @@ def _string_code_point_at(string: str, i: int) -> int: return ord(string[i]) def _string_index_of(string: str, search: str, position: int = 0) -> int: - # return string.index(search) + return string.find(search, position) # Fasttrack stringLength = len(string) searchLength = len(search) if position < 0: @@ -220,6 +220,7 @@ def _array_pop(array: list[__T]) -> __T: return array.pop() def _array_unshift(array: list[__T], *elements: __T) -> int: + elements = list(elements) _array_reverse(elements) for element in elements: array.insert(0, element) @@ -252,6 +253,7 @@ def _array_splice(array: list[__T], start: int, deleteCount: int = None, *elemen break deleted.append(array.pop(start)) deleteCount -= 1 + elements = list(elements) _array_reverse(elements) for element in elements: array.insert(start, element) @@ -279,6 +281,7 @@ def _array_some(array: list[__T], callback: Callable[[__T, int, list[__T]], bool return False def _array_index_of(array: list[__T], element: __T) -> int: + # return array.index(element) if element in array else -1 # Fasttrack for i in range(0, len(array)): if array[i] is not element: continue @@ -293,6 +296,7 @@ def _array_last_index_of(array: list[__T], element: __T) -> int: return -1 def _array_includes(array: list[__T], element: __T) -> bool: + # return element in array # Fasttrack return _array_index_of(array, element) >= 0 def _array_find(array: list[__T], callback: Callable[[__T, int, list[__T]], bool]) -> __T: @@ -341,9 +345,11 @@ def _array_map(array: list[__T], callback: Callable[[__T, int, list[__T]], __U]) result.append(callback(array[i], i, array)) return result -def _array_flat(array: list[Any], depth: int = 1, result: list[Any] = []) -> list[Any]: +def _array_flat(array: list[Any], depth: int = 1, result: list[Any] = None) -> list[Any]: + if result is None: + result = [] for i in range(0, len(array)): - if not hasattr(array[i], "__len__"): + if type(array[i]) is not list: result.append(array[i]) continue _array_flat(array[i], depth - 1, result) @@ -353,6 +359,7 @@ def _array_flat_map(array: list[__T], callback: Callable[[__T, int, list[__T]], return _array_flat(_array_map(array, callback)) def _array_join(array: list[Any], separator: str = ",") -> str: + return separator.join(array) # Fasttrack result = "" arrayLength = len(array) for i in range(0, arrayLength): @@ -374,6 +381,8 @@ def _array_reduce_right(array: list[__T], callback: Callable[[__U, __T, int, lis return accumulator def _array_reverse(array: list[__T]) -> None: + array.reverse() # Fasttrack + return arrayLength = len(array) for i in range(0, arrayLength // 2): temp = array[i] @@ -411,6 +420,27 @@ def _array_with(array: list[__T], index: int, value: __T) -> list[__T]: array[index] = value return array +# This function is not compliant with JS standard. +# isNaN(parseInt("23a")) == true +# _parse_int("23a") is None +__zero_ord = ord("0") +def _parse_int(string: str) -> Optional[int]: + string = _string_trim(string) + if string == "" or string == "-": + return None + negate = False + result = 0 + for i in range(len(string)): + character = string[i] + if i == 0 and character == "-": + negate = True + continue + digit = ord(character) - __zero_ord + if digit < 0 or digit > 9: + return None + result = result * 10 + digit + return -result if negate else result + # Tuple/NamedTuple/Dict primordials __Tuple = TypeVar("__Tuple", covariant=tuple) @@ -449,9 +479,14 @@ def _dict_clear(dictionary: __Dict): for key in list(dictionary.keys()): del dictionary[key] -def _dict_set(dictionary: __Dict, to: __Dict): - for key, value in to.items(): - dictionary[key] = value +def _dict_set(dictionary: __Dict, to: Union[__Dict, list[tuple[str, Any]]]): + if type(to) is dict: + for key, value in to.items(): + dictionary[key] = value + if type(to) is list: + for key, value in to: + dictionary[key] = value + return dictionary def _dict_with(dictionary: __Dict, **elements: Any) -> __Dict: result = dict() diff --git a/test/testabortsignalsuspendable.py b/test/testabortsignalsuspendable.py new file mode 100644 index 0000000..217cb91 --- /dev/null +++ b/test/testabortsignalsuspendable.py @@ -0,0 +1,34 @@ +from utils.primordials import * +from utils.coroutines import * +import traceback + +def main(state, args): + if state is SuspendableInitial: + print("Hello world!") + abortController = abortcontroller_new() + abortSignal = abortcontroller_get_signal(abortController) + promise = promise_from_suspendable(test, 1, abortSignal) + return SuspendableReturn, promise + return SuspendableExhausted + +def test(state, args): + if state is SuspendableInitial: + testArg1, testArg2 = args + print(testArg1) + print(testArg2) + return SuspendableReturn, promise_from_wait(100, "Hah") + return SuspendableExhausted + +if __name__ == "__main__": + looper = looper_new("Main") + with looper_closure(looper): + promise = promise_from_suspendable(main) + promise = promise_then(promise, lambda v: print(v)) + promise = promise_catch(promise, lambda e: traceback.print_exception(e)) + promise = promise_finally(promise, lambda: exit()) + try: + while True: + looper_tick(looper) + except KeyboardInterrupt: + exit() + diff --git a/test/testgetch.py b/test/testgetch.py new file mode 100644 index 0000000..e7c5720 --- /dev/null +++ b/test/testgetch.py @@ -0,0 +1,128 @@ +import msvcrt +import os +from utils.primordials import * +from typing import NamedTuple, Union + +_KeyEvent = NamedTuple("KeyEvent", [ + ("which", Union[int, tuple[int, int]]), + ("key", str), + ("code", str), + ("shiftKey", bool), + ("ctrlKey", bool), + ("altKey", bool), + ("metaKey", bool), + ("repeat", bool), +]) +__keyevent_table = [ + _KeyEvent(27, "ControlESC", "KeyESC", None, None, None, None, None), + _KeyEvent(8, "ControlCR", "Enter", None, False, None, None, None), _KeyEvent(127, "ControlLF", "Enter", None, True, False, None, None), _KeyEvent((0, 28), "ControlNUL", "Enter", None, True, True, None, None), + _KeyEvent(8, "ControlCR", "NumpadEnter", None, False, None, None, None), _KeyEvent(127, "ControlLF", "NumpadEnter", None, True, False, None, None), _KeyEvent((0, 166), "ControlNUL", "NumpadEnter", None, True, True, None, None), + _KeyEvent(49, "1", "Digit1", False, None, None, None, None), _KeyEvent(33, "!", "Digit1", True, None, None, None, None), _KeyEvent((0, 120), "ControlNUL", "Digit1", None, True, True, None, None), + _KeyEvent(50, "2", "Digit2", False, None, None, None, None), _KeyEvent(64, "@", "Digit2", True, None, None, None, None), _KeyEvent(3, "ControlETX", "Digit2", None, True, False, None, None), _KeyEvent((0, 121), "ControlNUL", "Digit2", None, True, True, None, None), + _KeyEvent(51, "3", "Digit3", False, None, None, None, None), _KeyEvent(35, "#", "Digit3", True, None, None, None, None), _KeyEvent((0, 122), "ControlNUL", "Digit3", None, True, True, None, None), + _KeyEvent(52, "4", "Digit4", False, None, None, None, None), _KeyEvent(36, "$", "Digit4", True, None, None, None, None), _KeyEvent((0, 123), "ControlNUL", "Digit4", None, True, True, None, None), + _KeyEvent(53, "5", "Digit5", False, None, None, None, None), _KeyEvent(37, "%", "Digit5", True, None, None, None, None), _KeyEvent((0, 124), "ControlNUL", "Digit5", None, True, True, None, None), + _KeyEvent(54, "6", "Digit6", False, None, None, None, None), _KeyEvent(94, "^", "Digit6", True, None, None, None, None), _KeyEvent(31, "ControlRS", "Digit6", None, True, False, None, None), _KeyEvent((0, 125), "ControlNUL", "Digit6", None, True, True, None, None), + _KeyEvent(55, "7", "Digit7", False, None, None, None, None), _KeyEvent(38, "&", "Digit7", True, None, None, None, None), _KeyEvent((0, 126), "ControlNUL", "Digit7", None, True, True, None, None), + _KeyEvent(56, "8", "Digit8", False, None, None, None, None), _KeyEvent(42, "*", "Digit8", True, None, None, None, None), _KeyEvent((0, 127), "ControlNUL", "Digit8", None, True, True, None, None), + _KeyEvent(57, "9", "Digit9", False, None, None, None, None), _KeyEvent(40, "(", "Digit9", True, None, None, None, None), _KeyEvent((0, 128), "ControlNUL", "Digit9", None, True, True, None, None), + _KeyEvent(48, "0", "Digit0", False, None, None, None, None), _KeyEvent(41, ")", "Digit0", True, None, None, None, None), _KeyEvent((0, 129), "ControlNUL", "Digit0", None, True, True, None, None), + _KeyEvent(45, "-", "Minus", False, None, None, None, None), _KeyEvent(95, "_", "Minus", True, None, None, None, None), _KeyEvent(45, "ControlUS", "Minus", None, True, False, None, None), _KeyEvent((0, 130), "-", "Minus", False, None, None, None, None), + _KeyEvent(61, "=", "Equal", False, None, None, None, None), _KeyEvent(43, "+", "Equal", True, None, None, None, None), _KeyEvent((0, 131), "=", "Equal", False, None, None, None, None), + _KeyEvent(8, "ControlBS", "Backspace", None, False, None, None, None), _KeyEvent(127, "ControlDEL", "Backspace", None, True, False, None, None), _KeyEvent((0, 14), "ControlNUL", "Backspace", None, True, True, None, None), + _KeyEvent(113, "q", "KeyQ", False, False, None, None, None), _KeyEvent(81, "Q", "KeyQ", True, False, None, None, None), _KeyEvent(17, "ControlDC1", "KeyQ", None, True, False, None, None), _KeyEvent((0, 16), "ControlNUL", "KeyQ", None, True, True, None, None), + _KeyEvent(119, "w", "KeyW", False, False, None, None, None), _KeyEvent(87, "W", "KeyW", True, False, None, None, None), _KeyEvent(23, "ControlETB", "KeyW", None, True, False, None, None), _KeyEvent((0, 17), "ControlNUL", "KeyW", None, True, True, None, None), + _KeyEvent(101, "e", "KeyE", False, False, None, None, None), _KeyEvent(69, "E", "KeyE", True, False, None, None, None), _KeyEvent(5, "ControlENQ", "KeyE", None, True, False, None, None), _KeyEvent((0, 18), "ControlNUL", "KeyE", None, True, True, None, None), + _KeyEvent(114, "r", "KeyR", False, False, None, None, None), _KeyEvent(82, "R", "KeyR", True, False, None, None, None), _KeyEvent(18, "ControlDC2", "KeyR", None, True, False, None, None), _KeyEvent((0, 19), "ControlNUL", "KeyR", None, True, True, None, None), + _KeyEvent(116, "t", "KeyT", False, False, None, None, None), _KeyEvent(84, "T", "KeyT", True, False, None, None, None), _KeyEvent(20, "ControlSO", "KeyT", None, True, False, None, None), _KeyEvent((0, 20), "ControlNUL", "KeyT", None, True, True, None, None), + _KeyEvent(121, "y", "KeyY", False, False, None, None, None), _KeyEvent(89, "Y", "KeyY", True, False, None, None, None), _KeyEvent(25, "ControlEM", "KeyY", None, True, False, None, None), _KeyEvent((0, 21), "ControlNUL", "KeyY", None, True, True, None, None), + _KeyEvent(117, "u", "KeyU", False, False, None, None, None), _KeyEvent(85, "U", "KeyU", True, False, None, None, None), _KeyEvent(21, "ControlNAK", "KeyU", None, True, False, None, None), _KeyEvent((0, 22), "ControlNUL", "KeyU", None, True, True, None, None), + _KeyEvent(105, "i", "KeyI", False, False, None, None, None), _KeyEvent(73, "I", "KeyI", True, False, None, None, None), _KeyEvent(9, "ControlIAB", "KeyI", None, True, False, None, None), _KeyEvent((0, 23), "ControlNUL", "KeyI", None, True, True, None, None), + _KeyEvent(111, "o", "KeyO", False, False, None, None, None), _KeyEvent(79, "O", "KeyO", True, False, None, None, None), _KeyEvent(15, "ControlSI", "KeyO", None, True, False, None, None), _KeyEvent((0, 24), "ControlNUL", "KeyO", None, True, True, None, None), + _KeyEvent(112, "p", "KeyP", False, False, None, None, None), _KeyEvent(80, "P", "KeyP", True, False, None, None, None), _KeyEvent(16, "ControlDLE", "KeyP", None, True, False, None, None), _KeyEvent((0, 25), "ControlNUL", "KeyP", None, True, True, None, None), + _KeyEvent(91, "[", "BracketLeft", False, False, None, None, None), _KeyEvent(123, "{", "BracketLeft", True, False, None, None, None), _KeyEvent(27, "ControlESC", "BracketLeft", None, True, False, None, None), _KeyEvent((0, 26), "ControlNUL", "BracketLeft", None, True, True, None, None), + _KeyEvent(93, "]", "BracketRight", False, False, None, None, None), _KeyEvent(125, "}", "BracketRight", True, False, None, None, None), _KeyEvent(29, "ControlGS", "BracketRight", None, True, False, None, None), _KeyEvent((0, 27), "ControlNUL", "BracketRight", None, True, True, None, None), + _KeyEvent(97, "a", "KeyA", False, False, None, None, None), _KeyEvent(65, "A", "KeyA", True, False, None, None, None), _KeyEvent(1, "ControlSOH", "KeyA", None, True, False, None, None), _KeyEvent((0, 30), "ControlNUL", "KeyA", None, True, True, None, None), + _KeyEvent(115, "s", "KeyS", False, False, None, None, None), _KeyEvent(83, "S", "KeyS", True, False, None, None, None), _KeyEvent(19, "ControlDC3", "KeyS", None, True, False, None, None), _KeyEvent((0, 31), "ControlNUL", "KeyS", None, True, True, None, None), + _KeyEvent(100, "d", "KeyD", False, False, None, None, None), _KeyEvent(68, "D", "KeyD", True, False, None, None, None), _KeyEvent(4, "ControlEOT", "KeyD", None, True, False, None, None), _KeyEvent((0, 32), "ControlNUL", "KeyD", None, True, True, None, None), + _KeyEvent(102, "f", "KeyF", False, False, None, None, None), _KeyEvent(70, "F", "KeyF", True, False, None, None, None), _KeyEvent(6, "ControlACK", "KeyF", None, True, False, None, None), _KeyEvent((0, 33), "ControlNUL", "KeyF", None, True, True, None, None), + _KeyEvent(103, "g", "KeyG", False, False, None, None, None), _KeyEvent(71, "G", "KeyG", True, False, None, None, None), _KeyEvent(7, "ControlBEL", "KeyG", None, True, False, None, None), _KeyEvent((0, 34), "ControlNUL", "KeyG", None, True, True, None, None), + _KeyEvent(104, "h", "KeyH", False, False, None, None, None), _KeyEvent(72, "H", "KeyH", True, False, None, None, None), _KeyEvent(8, "ControlBS", "KeyH", None, True, False, None, None), _KeyEvent((0, 35), "ControlNUL", "KeyH", None, True, True, None, None), + _KeyEvent(106, "j", "KeyJ", False, False, None, None, None), _KeyEvent(74, "J", "KeyJ", True, False, None, None, None), _KeyEvent(10, "ControlLF", "KeyJ", None, True, False, None, None), _KeyEvent((0, 36), "ControlNUL", "KeyJ", None, True, True, None, None), + _KeyEvent(107, "k", "KeyK", False, False, None, None, None), _KeyEvent(75, "K", "KeyK", True, False, None, None, None), _KeyEvent(11, "ControlVT", "KeyK", None, True, False, None, None), _KeyEvent((0, 37), "ControlNUL", "KeyK", None, True, True, None, None), + _KeyEvent(108, "l", "KeyL", False, False, None, None, None), _KeyEvent(76, "L", "KeyL", True, False, None, None, None), _KeyEvent(12, "ControlFF", "KeyL", None, True, False, None, None), _KeyEvent((0, 38), "ControlNUL", "KeyL", None, True, True, None, None), + _KeyEvent(91, ";", "Semicolon", False, None, None, None, None), _KeyEvent(123, ":", "Semicolon", True, None, None, None, None), _KeyEvent((0, 39), "ControlNUL", "Semicolon", None, True, True, None, None), + _KeyEvent(39, "'", "Quote", False, None, None, None, None), _KeyEvent(34, "\"", "Quote", True, None, None, None, None), _KeyEvent((0, 40), "ControlNUL", "Quote", None, True, True, None, None), + _KeyEvent(96, "`", "Backquote", False, None, None, None, None), _KeyEvent(126, "~", "Backquote", True, None, None, None, None), _KeyEvent((0, 41), "ControlNUL", "Backquote", None, True, True, None, None), + _KeyEvent(92, "\\", "Backslash", False, False, None, None, None), _KeyEvent(124, "|", "Backslash", True, False, None, None, None), _KeyEvent(28, "ControlFS", "Backslash", None, True, False, None, None), + _KeyEvent(122, "z", "KeyZ", False, False, None, None, None), _KeyEvent(90, "Z", "KeyZ", True, False, None, None, None), _KeyEvent(26, "ControlSUB", "KeyZ", None, True, False, None, None), _KeyEvent((0, 44), "ControlNUL", "KeyZ", None, True, True, None, None), + _KeyEvent(120, "x", "KeyX", False, False, None, None, None), _KeyEvent(88, "X", "KeyX", True, False, None, None, None), _KeyEvent(24, "ControlCAN", "KeyX", None, True, False, None, None), _KeyEvent((0, 45), "ControlNUL", "KeyX", None, True, True, None, None), + _KeyEvent(99, "c", "KeyC", False, False, None, None, None), _KeyEvent(67, "C", "KeyC", True, False, None, None, None), _KeyEvent(3, "ControlETX", "KeyC", None, True, False, None, None), _KeyEvent((0, 46), "ControlNUL", "KeyC", None, True, True, None, None), + _KeyEvent(118, "v", "KeyV", False, False, None, None, None), _KeyEvent(86, "V", "KeyV", True, False, None, None, None), _KeyEvent(22, "ControlSYN", "KeyV", None, True, False, None, None), _KeyEvent((0, 47), "ControlNUL", "KeyV", None, True, True, None, None), + _KeyEvent(98, "b", "KeyB", False, False, None, None, None), _KeyEvent(66, "B", "KeyB", True, False, None, None, None), _KeyEvent(2, "ControlSTX", "KeyB", None, True, False, None, None), _KeyEvent((0, 48), "ControlNUL", "KeyB", None, True, True, None, None), + _KeyEvent(110, "n", "KeyN", False, False, None, None, None), _KeyEvent(78, "N", "KeyN", True, False, None, None, None), _KeyEvent(14, "ControlSO", "KeyN", None, True, False, None, None), _KeyEvent((0, 49), "ControlNUL", "KeyN", None, True, True, None, None), + _KeyEvent(109, "m", "KeyM", False, False, None, None, None), _KeyEvent(77, "M", "KeyM", True, False, None, None, None), _KeyEvent(13, "ControlCR", "KeyM", None, True, False, None, None), _KeyEvent((0, 50), "ControlNUL", "KeyM", None, True, True, None, None), + _KeyEvent(44, ",", "Comma", False, None, None, None, None), _KeyEvent(60, "<", "Comma", True, None, None, None, None), _KeyEvent((0, 51), "ControlNUL", "Comma", None, True, True, None, None), + _KeyEvent(46, ".", "Period", False, None, None, None, None), _KeyEvent(62, ">", "Period", True, None, None, None, None), _KeyEvent((0, 52), "ControlNUL", "Period", None, True, True, None, None), + _KeyEvent(47, "/", "Slash", False, None, None, None, None), _KeyEvent(63, "?", "Slash", True, None, None, None, None), _KeyEvent((0, 53), "ControlNUL", "Slash", None, True, True, None, None), + _KeyEvent(47, "/", "NumpadDivide", False, False, None, None, None), _KeyEvent(63, "?", "NumpadDivide", True, False, None, None, None), _KeyEvent((0, 149), "ControlNUL", "NumpadDivide", None, True, False, None, None), _KeyEvent((0, 164), "ControlNUL", "NumpadDivide", None, True, True, None, None), + _KeyEvent(42, "*", "NumpadMultiply", None, False, None, None, None), _KeyEvent(16, "ControlDLE", "NumpadMultiply", None, True, None, None, None), + _KeyEvent(32, " ", "Space", None, None, None, None, None), + _KeyEvent((0, 59), "F1", "F1", False, False, None, None, None), _KeyEvent((0, 84), "F1", "F1", True, False, None, None, None), _KeyEvent((0, 94), "F1", "F1", None, True, False, None, None), _KeyEvent((0, 104), "F1", "F1", None, True, True, None, None), + _KeyEvent((0, 60), "F2", "F2", False, False, None, None, None), _KeyEvent((0, 85), "F2", "F2", True, False, None, None, None), _KeyEvent((0, 95), "F2", "F2", None, True, False, None, None), _KeyEvent((0, 105), "F2", "F2", None, True, True, None, None), + _KeyEvent((0, 61), "F3", "F3", False, False, None, None, None), _KeyEvent((0, 86), "F3", "F3", True, False, None, None, None), _KeyEvent((0, 96), "F3", "F3", None, True, False, None, None), _KeyEvent((0, 106), "F3", "F3", None, True, True, None, None), + _KeyEvent((0, 62), "F4", "F4", False, False, None, None, None), _KeyEvent((0, 87), "F4", "F4", True, False, None, None, None), _KeyEvent((0, 97), "F4", "F4", None, True, False, None, None), _KeyEvent((0, 107), "F4", "F4", None, True, True, None, None), + _KeyEvent((0, 63), "F5", "F5", False, False, None, None, None), _KeyEvent((0, 88), "F5", "F5", True, False, None, None, None), _KeyEvent((0, 98), "F5", "F5", None, True, False, None, None), _KeyEvent((0, 108), "F5", "F5", None, True, True, None, None), + _KeyEvent((0, 64), "F6", "F6", False, False, None, None, None), _KeyEvent((0, 89), "F6", "F6", True, False, None, None, None), _KeyEvent((0, 99), "F6", "F6", None, True, False, None, None), _KeyEvent((0, 109), "F6", "F6", None, True, True, None, None), + _KeyEvent((0, 65), "F7", "F7", False, False, None, None, None), _KeyEvent((0, 90), "F7", "F7", True, False, None, None, None), _KeyEvent((0, 100), "F7", "F7", None, True, False, None, None), _KeyEvent((0, 110), "F7", "F7", None, True, True, None, None), + _KeyEvent((0, 66), "F8", "F8", False, False, None, None, None), _KeyEvent((0, 91), "F8", "F8", True, False, None, None, None), _KeyEvent((0, 101), "F8", "F8", None, True, False, None, None), _KeyEvent((0, 111), "F8", "F8", None, True, True, None, None), + _KeyEvent((0, 67), "F9", "F9", False, False, None, None, None), _KeyEvent((0, 92), "F9", "F9", True, False, None, None, None), _KeyEvent((0, 102), "F9", "F9", None, True, False, None, None), _KeyEvent((0, 112), "F9", "F9", None, True, True, None, None), + _KeyEvent((0, 68), "F10", "F10", False, False, None, None, None), _KeyEvent((0, 93), "F10", "F10", True, False, None, None, None), _KeyEvent((0, 103), "F10", "F10", None, True, False, None, None), _KeyEvent((0, 113), "F10", "F10", None, True, True, None, None), + _KeyEvent((224, 133), "F11", "F11", False, False, None, None, None), _KeyEvent((224, 135), "F11", "F11", True, False, None, None, None), _KeyEvent((224, 137), "F11", "F11", None, True, False, None, None), _KeyEvent((224, 139), "F11", "F11", None, True, True, None, None), + _KeyEvent((224, 134), "F12", "F12", False, False, None, None, None), _KeyEvent((224, 136), "F12", "F12", True, False, None, None, None), _KeyEvent((224, 138), "F12", "F12", None, True, False, None, None), _KeyEvent((224, 140), "F12", "F12", None, True, True, None, None), + _KeyEvent((0, 71), "Home", "Numpad7", False, False, None, None, None), _KeyEvent(55, "7", "Numpad7", True, False, None, None, None), _KeyEvent((0, 119), "ControlNUL", "Numpad7", None, True, None, None, None), + _KeyEvent((0, 71), "Home", "Home", None, False, False, None, None), _KeyEvent((0, 119), "ControlNUL", "Home", None, True, False, None, None), _KeyEvent((0, 151), "ControlNUL", "Home", None, None, True, None, None), + _KeyEvent((0, 72), "Up", "Numpad8", False, False, None, None, None), _KeyEvent(56, "8", "Numpad8", True, False, None, None, None), _KeyEvent((0, 141), "ControlNUL", "Numpad8", None, True, None, None, None), + _KeyEvent((224, 72), "Up", "Up", None, False, False, None, None), _KeyEvent((224, 141), "ControlNUL", "Up", None, True, False, None, None), _KeyEvent((0, 152), "ControlNUL", "Up", None, None, True, None, None), + _KeyEvent((0, 73), "PageUp", "Numpad9", False, False, None, None, None), _KeyEvent(57, "9", "Numpad9", True, False, None, None, None), _KeyEvent((0, 132), "ControlNUL", "Numpad9", None, True, None, None, None), + _KeyEvent((0, 73), "PageUp", "PageUp", None, False, False, None, None), _KeyEvent((0, 132), "ControlNUL", "PageUp", None, True, False, None, None), _KeyEvent((0, 153), "ControlNUL", "PageUp", None, None, True, None, None), + _KeyEvent(53, "-", "NumpadMinus", True, None, None, None, None), + _KeyEvent((0, 75), "Left", "Numpad4", False, False, None, None, None), _KeyEvent(52, "4", "Numpad4", True, False, None, None, None), _KeyEvent((0, 115), "ControlNUL", "Numpad4", None, True, None, None, None), + _KeyEvent((224, 75), "Left", "Left", None, False, False, None, None), _KeyEvent((224, 115), "ControlNUL", "Left", None, True, False, None, None), _KeyEvent((0, 155), "ControlNUL", "Left", None, None, True, None, None), + _KeyEvent(53, "5", "Numpad5", True, None, None, None, None), + _KeyEvent((0, 77), "Right", "Numpad6", False, False, None, None, None), _KeyEvent(54, "4", "Numpad6", True, False, None, None, None), _KeyEvent((0, 116), "ControlNUL", "Numpad6", None, True, None, None, None), + _KeyEvent((224, 77), "Right", "Right", None, False, False, None, None), _KeyEvent((224, 116), "ControlNUL", "Right", None, True, False, None, None), _KeyEvent((0, 157), "ControlNUL", "Right", None, None, True, None, None), + _KeyEvent(43, "+", "NumpadPlus", True, None, None, None, None), + _KeyEvent((0, 79), "End", "Numpad1", False, False, None, None, None), _KeyEvent(49, "1", "Numpad1", True, False, None, None, None), _KeyEvent((0, 117), "ControlNUL", "Numpad1", None, True, None, None, None), + _KeyEvent((0, 79), "End", "End", None, False, False, None, None), _KeyEvent((0, 117), "ControlNUL", "End", None, True, False, None, None), _KeyEvent((0, 159), "ControlNUL", "End", None, None, True, None, None), + _KeyEvent((0, 80), "Down", "Numpad2", False, False, None, None, None), _KeyEvent(50, "2", "Numpad2", True, False, None, None, None), _KeyEvent((0, 145), "ControlNUL", "Numpad2", None, True, None, None, None), + _KeyEvent((224, 80), "Down", "Down", None, False, False, None, None), _KeyEvent((224, 145), "ControlNUL", "Down", None, True, False, None, None), _KeyEvent((0, 160), "ControlNUL", "Down", None, None, True, None, None), + _KeyEvent((0, 81), "PageDown", "Numpad3", False, False, None, None, None), _KeyEvent(51, "3", "Numpad3", True, False, None, None, None), _KeyEvent((0, 118), "ControlNUL", "Numpad3", None, True, None, None, None), + _KeyEvent((0, 81), "PageDown", "PageDown", None, False, False, None, None), _KeyEvent((0, 118), "ControlNUL", "PageDown", None, True, False, None, None), _KeyEvent((0, 161), "ControlNUL", "PageDown", None, None, True, None, None), + _KeyEvent((0, 82), "Insert", "Numpad0", False, False, None, None, None), _KeyEvent(48, "3", "Numpad0", True, False, None, None, None), _KeyEvent((0, 146), "ControlNUL", "Numpad0", None, True, None, None, None), + _KeyEvent((0, 82), "Insert", "Insert", None, False, False, None, None), _KeyEvent((0, 146), "ControlNUL", "Insert", None, True, False, None, None), _KeyEvent((0, 162), "ControlNUL", "Insert", None, None, True, None, None), + _KeyEvent((0, 83), "Delete", "NumpadDecimal", False, False, None, None, None), _KeyEvent(46, "3", "NumpadDecimal", True, False, None, None, None), _KeyEvent((0, 147), "ControlNUL", "NumpadDecimal", None, True, None, None, None), + _KeyEvent((0, 83), "Delete", "Delete", None, False, False, None, None), _KeyEvent((0, 147), "ControlNUL", "Delete", None, True, False, None, None), _KeyEvent((0, 163), "ControlNUL", "Delete", None, None, True, None, None), +] + +os.system('cls') +print(len(__keyevent_table)) +while True: + print('Press key: ', end='', flush=True) + + # key = msvcrt.getwch() + # num = ord(key) + # if num in (0, 224): + # ext = msvcrt.getwch() + # print(f'prefix: {num} - key: {ext!r} - unicode: {ord(ext)}') + # else: + # print(f'key: {key!r} - unicode: {ord(key)}') + + key = ord(msvcrt.getwch()) + if key == 0 or key == 224: + key = (key, ord(msvcrt.getwch())) + keyevent = array_find(__keyevent_table, lambda e, *_: e[0] == key) + if keyevent == None: + print(f"Unrecognized event: {str(key)}") + continue + print(f"Event: {str(keyevent)}") diff --git a/test/testprimordialstrim.py b/test/testprimordialstrim.py new file mode 100644 index 0000000..bfb884c --- /dev/null +++ b/test/testprimordialstrim.py @@ -0,0 +1,18 @@ +from utils.primordials import * + +def formatString(string): + try: + string = string_trim(string) + string = array_join(array_map(string_split(string, "\n"), lambda l, *_: string_trim(l)), "\n") + return string + except BaseException as e: + print(string) + raise e + +print(formatString(f""" + Nama: Monster 2 + HP: 301.5 + Attack: 75 + Defense: 80 + Potions: 0 +""")) diff --git a/tmp.prof b/tmp.prof new file mode 100644 index 0000000000000000000000000000000000000000..39b4ebc6bb67581fce9a47467ca65e60a85d8279 GIT binary patch literal 109834 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