crimsonland 0.1.0.dev5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. crimson/__init__.py +24 -0
  2. crimson/assets_fetch.py +60 -0
  3. crimson/atlas.py +92 -0
  4. crimson/audio_router.py +155 -0
  5. crimson/bonuses.py +167 -0
  6. crimson/camera.py +75 -0
  7. crimson/cli.py +380 -0
  8. crimson/creatures/__init__.py +8 -0
  9. crimson/creatures/ai.py +186 -0
  10. crimson/creatures/anim.py +173 -0
  11. crimson/creatures/damage.py +103 -0
  12. crimson/creatures/runtime.py +1019 -0
  13. crimson/creatures/spawn.py +2871 -0
  14. crimson/debug.py +7 -0
  15. crimson/demo.py +1360 -0
  16. crimson/demo_trial.py +140 -0
  17. crimson/effects.py +1086 -0
  18. crimson/effects_atlas.py +73 -0
  19. crimson/frontend/__init__.py +1 -0
  20. crimson/frontend/assets.py +43 -0
  21. crimson/frontend/boot.py +424 -0
  22. crimson/frontend/menu.py +700 -0
  23. crimson/frontend/panels/__init__.py +1 -0
  24. crimson/frontend/panels/base.py +410 -0
  25. crimson/frontend/panels/controls.py +132 -0
  26. crimson/frontend/panels/mods.py +128 -0
  27. crimson/frontend/panels/options.py +409 -0
  28. crimson/frontend/panels/play_game.py +627 -0
  29. crimson/frontend/panels/stats.py +351 -0
  30. crimson/frontend/transitions.py +31 -0
  31. crimson/game.py +2533 -0
  32. crimson/game_modes.py +15 -0
  33. crimson/game_world.py +652 -0
  34. crimson/gameplay.py +2467 -0
  35. crimson/input_codes.py +176 -0
  36. crimson/modes/__init__.py +1 -0
  37. crimson/modes/base_gameplay_mode.py +219 -0
  38. crimson/modes/quest_mode.py +502 -0
  39. crimson/modes/rush_mode.py +300 -0
  40. crimson/modes/survival_mode.py +792 -0
  41. crimson/modes/tutorial_mode.py +648 -0
  42. crimson/modes/typo_mode.py +472 -0
  43. crimson/paths.py +23 -0
  44. crimson/perks.py +828 -0
  45. crimson/persistence/__init__.py +1 -0
  46. crimson/persistence/highscores.py +385 -0
  47. crimson/persistence/save_status.py +245 -0
  48. crimson/player_damage.py +77 -0
  49. crimson/projectiles.py +1133 -0
  50. crimson/quests/__init__.py +18 -0
  51. crimson/quests/helpers.py +147 -0
  52. crimson/quests/registry.py +49 -0
  53. crimson/quests/results.py +164 -0
  54. crimson/quests/runtime.py +91 -0
  55. crimson/quests/tier1.py +620 -0
  56. crimson/quests/tier2.py +652 -0
  57. crimson/quests/tier3.py +579 -0
  58. crimson/quests/tier4.py +721 -0
  59. crimson/quests/tier5.py +886 -0
  60. crimson/quests/timeline.py +115 -0
  61. crimson/quests/types.py +70 -0
  62. crimson/render/__init__.py +1 -0
  63. crimson/render/terrain_fx.py +88 -0
  64. crimson/render/world_renderer.py +1941 -0
  65. crimson/sim/__init__.py +1 -0
  66. crimson/sim/world_defs.py +67 -0
  67. crimson/sim/world_state.py +422 -0
  68. crimson/terrain_assets.py +19 -0
  69. crimson/tutorial/__init__.py +12 -0
  70. crimson/tutorial/timeline.py +291 -0
  71. crimson/typo/__init__.py +2 -0
  72. crimson/typo/names.py +233 -0
  73. crimson/typo/player.py +43 -0
  74. crimson/typo/spawns.py +73 -0
  75. crimson/typo/typing.py +52 -0
  76. crimson/ui/__init__.py +3 -0
  77. crimson/ui/cursor.py +95 -0
  78. crimson/ui/demo_trial_overlay.py +235 -0
  79. crimson/ui/game_over.py +660 -0
  80. crimson/ui/hud.py +601 -0
  81. crimson/ui/perk_menu.py +388 -0
  82. crimson/views/__init__.py +40 -0
  83. crimson/views/aim_debug.py +276 -0
  84. crimson/views/animations.py +274 -0
  85. crimson/views/arsenal_debug.py +404 -0
  86. crimson/views/audio_bootstrap.py +47 -0
  87. crimson/views/bonuses.py +201 -0
  88. crimson/views/camera_debug.py +359 -0
  89. crimson/views/camera_shake.py +229 -0
  90. crimson/views/corpse_stamp_debug.py +324 -0
  91. crimson/views/decals_debug.py +739 -0
  92. crimson/views/empty.py +19 -0
  93. crimson/views/fonts.py +114 -0
  94. crimson/views/game_over.py +117 -0
  95. crimson/views/ground.py +259 -0
  96. crimson/views/lighting_debug.py +1166 -0
  97. crimson/views/particles.py +293 -0
  98. crimson/views/perk_menu_debug.py +430 -0
  99. crimson/views/perks.py +398 -0
  100. crimson/views/player.py +434 -0
  101. crimson/views/player_sprite_debug.py +314 -0
  102. crimson/views/projectile_fx.py +609 -0
  103. crimson/views/projectile_render_debug.py +393 -0
  104. crimson/views/projectiles.py +221 -0
  105. crimson/views/quest_title_overlay.py +108 -0
  106. crimson/views/registry.py +34 -0
  107. crimson/views/rush.py +16 -0
  108. crimson/views/small_font_debug.py +204 -0
  109. crimson/views/spawn_plan.py +363 -0
  110. crimson/views/sprites.py +214 -0
  111. crimson/views/survival.py +15 -0
  112. crimson/views/terrain.py +132 -0
  113. crimson/views/ui.py +123 -0
  114. crimson/views/wicons.py +166 -0
  115. crimson/weapon_sfx.py +63 -0
  116. crimson/weapons.py +860 -0
  117. crimsonland-0.1.0.dev5.dist-info/METADATA +9 -0
  118. crimsonland-0.1.0.dev5.dist-info/RECORD +139 -0
  119. crimsonland-0.1.0.dev5.dist-info/WHEEL +4 -0
  120. crimsonland-0.1.0.dev5.dist-info/entry_points.txt +4 -0
  121. grim/__init__.py +20 -0
  122. grim/app.py +92 -0
  123. grim/assets.py +231 -0
  124. grim/audio.py +106 -0
  125. grim/config.py +294 -0
  126. grim/console.py +737 -0
  127. grim/fonts/__init__.py +7 -0
  128. grim/fonts/grim_mono.py +111 -0
  129. grim/fonts/small.py +120 -0
  130. grim/input.py +44 -0
  131. grim/jaz.py +103 -0
  132. grim/math.py +17 -0
  133. grim/music.py +403 -0
  134. grim/paq.py +76 -0
  135. grim/rand.py +37 -0
  136. grim/sfx.py +276 -0
  137. grim/sfx_map.py +103 -0
  138. grim/terrain_render.py +840 -0
  139. grim/view.py +16 -0
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.3
2
+ Name: crimsonland
3
+ Version: 0.1.0.dev5
4
+ Requires-Dist: construct>=2.10.70
5
+ Requires-Dist: pillow>=12.1.0
6
+ Requires-Dist: platformdirs>=4.5.1
7
+ Requires-Dist: raylib>=5.5.0.4
8
+ Requires-Dist: typer>=0.21.1
9
+ Requires-Python: >=3.13
@@ -0,0 +1,139 @@
1
+ crimson/__init__.py,sha256=dij6OQ6Wctqur9P00ojTTZw6IaUNeZagPX4-2Qqr-Kw,367
2
+ crimson/assets_fetch.py,sha256=z4vFH9h-RIwc2o6uKGzEtUaOdhUDSX6Art-WU6tNjd0,1864
3
+ crimson/atlas.py,sha256=hEcCHhPvguXAI6eH_G9Q8rpiX7M5akZ8fgJjMogmYrA,2401
4
+ crimson/audio_router.py,sha256=XauJvjT_qAooPKBo4d8NVe9HRWHzZDD8PCzmsWrASic,4729
5
+ crimson/bonuses.py,sha256=owwYIRHRu1Kymtt4eEvpd62JwWAg8LOe20vDuPFB5SU,5094
6
+ crimson/camera.py,sha256=VxTNXTh0qHh5VR1OpkiXr-QcgEWPrWw0A3PFyQFqDkY,2278
7
+ crimson/cli.py,sha256=Pu4RM_bjGtUgIE_5zZs0uWFY9O8YkhJDseRcVMMPJ8k,14595
8
+ crimson/creatures/__init__.py,sha256=RJwwR_w98zC_kHF9CwkhzSlf0xjpiP0JU87W4awdAAM,233
9
+ crimson/creatures/ai.py,sha256=oCuUkDe6Kxya1dThUthEy8GIuObAUjDv8WPfe17HYV4,6353
10
+ crimson/creatures/anim.py,sha256=E7MAR9vFBrvmIYrOfPr36UohsfROZZL00jgRvZQf6nc,4971
11
+ crimson/creatures/damage.py,sha256=pjKIX32nGDVPnFaWCce0LNZ-UZXZqJpNvwHwq-DCWbE,3211
12
+ crimson/creatures/runtime.py,sha256=1FagJNwGWJuOXZEv4VaWak2IktNskSof82WCYJqui-U,40733
13
+ crimson/creatures/spawn.py,sha256=ikgtr4sM2KdA2-eyxYdVmobcuZN6aA7xK7ceaIl7RSw,90059
14
+ crimson/debug.py,sha256=vtfr0_HQHpiB5h57jAsl9cWyYxErSbZQ2uazcL1sJhU,127
15
+ crimson/demo.py,sha256=rQG6Wdwy6t8sXZRt52vAmexpmgEyjRXuKs1sNx3wMoI,52500
16
+ crimson/demo_trial.py,sha256=BuoKB1DB-cPZ8jBp4x9gmxWF6tvhXMYRqJ9hMYrhxH4,4651
17
+ crimson/effects.py,sha256=6-v23jb5-Xs_gu3EezjRJLCGAU8obSUR6b2opdTLTvc,33999
18
+ crimson/effects_atlas.py,sha256=Ko1O7z-1jGkH_KSeb8RrR2EtAs4V8vfo70ofhqbFak4,2104
19
+ crimson/frontend/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
20
+ crimson/frontend/assets.py,sha256=JHxOID6Ti6bD2LercdRJ7sSSr0nRX3WWeuRPDQwwbsY,1343
21
+ crimson/frontend/boot.py,sha256=BUT8O9DAsAKOARBpoc7VhUQsV7IzpKVmbYtaEwkxu7M,15288
22
+ crimson/frontend/menu.py,sha256=2qqUdQ4UhjFViHUBVcsmitRXE2MU2UYXZuh4acY6aRA,26759
23
+ crimson/frontend/panels/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
24
+ crimson/frontend/panels/base.py,sha256=bHmNUGeHm9Y69x75VKFfgK88Y0PK937ta2CPpkSkGaA,14697
25
+ crimson/frontend/panels/controls.py,sha256=JhyylkqUIcq073VtxsrqhEaQYLU4Yf0yQ79ep5PLwpA,4763
26
+ crimson/frontend/panels/mods.py,sha256=GI6KZID2UnWHVm6VcxH6-p3tUx46mJtRPpzV8DK0PU0,4144
27
+ crimson/frontend/panels/options.py,sha256=0oBAGs9_nnP_H_JRa2hHZ6o-NaaavnuyzTSmeQyGUBA,15905
28
+ crimson/frontend/panels/play_game.py,sha256=_Tqw-vFVzgDUd-H1Q8txyw-sTSZKCEDf3aU0Iw9B26I,24299
29
+ crimson/frontend/panels/stats.py,sha256=jtIRRfffR4u_q6ayLvwCZ6u2YDdFC6Ei_ytQbxY-Bpo,12713
30
+ crimson/frontend/transitions.py,sha256=-sAJUDqNZ943zXlqtvJ6jCg2YH8dSi8k7qK8caAfOKI,883
31
+ crimson/game.py,sha256=_nPXvZDgu2sqEPk00ww9qx7nD8eQIXlIvU-yKfBuUA8,97537
32
+ crimson/game_modes.py,sha256=qW7Tt97lSBmGrt0F17Ni5h8vRyngBzyS9XwWM1TFIEI,255
33
+ crimson/game_world.py,sha256=nfKGcm3LHChPGLHJsurDFAATrHmhRvTmgxcLzUN9m5I,25440
34
+ crimson/gameplay.py,sha256=QsxHKgGTp79jAOIRoUn_L1jbN9EK2MIdCksLQ-KCqY4,87575
35
+ crimson/input_codes.py,sha256=PmSWFZIit8unTBWk3uwifpHWMuk0qMg1ueKX3MGC7D0,5379
36
+ crimson/modes/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
37
+ crimson/modes/base_gameplay_mode.py,sha256=kmXJxkGnCKV5QZL_y5ML7r-p4bw7hMswKseG9XUn2jo,7555
38
+ crimson/modes/quest_mode.py,sha256=hcq_n3Vi9CtED899ELi5JZKZWXWvzmDcIBBerVExz7w,19153
39
+ crimson/modes/rush_mode.py,sha256=ZspTGjLIyjxXtP9lbEFXQJv_PQEyj52xTNicNEfRSBk,10932
40
+ crimson/modes/survival_mode.py,sha256=kuY8s1wDWOII2wE4JfMmSB4_4kbkRxzOYeMbcJebELM,32691
41
+ crimson/modes/tutorial_mode.py,sha256=fVYPSZbAsUARI95QP3GamPRMf-v6ix5SKfCoa_0nfAY,26422
42
+ crimson/modes/typo_mode.py,sha256=JoiOqGjft922ZtjOzFpLeIy1hv35N7eHO-nsWVlSooY,17396
43
+ crimson/paths.py,sha256=JCKauCxHJnP4IIy5ocgs6tvbaSMkMUByQ76Zoz2UGZM,653
44
+ crimson/perks.py,sha256=JHNhKPOUq2w8h23gU7jpkx-erFKTO4Z8l8aHU8vrkO0,37068
45
+ crimson/persistence/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
46
+ crimson/persistence/highscores.py,sha256=i3eU_Vlu-Bn6kZHDWNMzUoJXvzkqOnPp_UFOeZXclYU,13026
47
+ crimson/persistence/save_status.py,sha256=s-FZONO6JVYRULewtlxoIwATTy3P7rLy-vsVBfSKPk4,7748
48
+ crimson/player_damage.py,sha256=cECZO65NJwARoYdtsbvZBM8vQNs_8AVO4xa0omX0pGg,2215
49
+ crimson/projectiles.py,sha256=44sL_2ElYAtNHHo8bzcx3t2NDq-yix56Pfui8zNGOeo,43644
50
+ crimson/quests/__init__.py,sha256=pKCkH0o1XIGDN9h6yNNaqvWek2CybEZRpejYRooULj8,375
51
+ crimson/quests/helpers.py,sha256=TeZWhIy2x8Zs9S-vYRBOEZ7pkHNMr5YJ-wi4vYQ2q2M,3625
52
+ crimson/quests/registry.py,sha256=Cees1QBN0VYTB-XyQtvZ1b4SykB-i4tAnWF6WSV-1pg,1478
53
+ crimson/quests/results.py,sha256=nwVk4CafEIqLvI2s8i4RueGiW48pnbwNmsoaoWPNebU,5279
54
+ crimson/quests/runtime.py,sha256=r0ANBiaCe1sY3_OUesd9gbPqRF4yeTeR-nududZgF-o,2668
55
+ crimson/quests/tier1.py,sha256=D4VQ0KqkTlp3j627a7hB13nB3WUJcviINCva9U6jrWc,17459
56
+ crimson/quests/tier2.py,sha256=zNF1lVnO1C3Yzq3EdVtsmeTqUZwqN7DWZuMTwmcp5pA,18249
57
+ crimson/quests/tier3.py,sha256=LzaQaxv3pb4JSwAS7BONtY8mBhrQBbcHuHhAFEt1r3g,16635
58
+ crimson/quests/tier4.py,sha256=wInwikrYb02KHf3-eK17vTHDHj9dvZMBJptj0x5vBzA,19644
59
+ crimson/quests/tier5.py,sha256=sZSz7VBxOpkwO-wQAMOvYgwstkvQ361_vQzux271bWs,23510
60
+ crimson/quests/timeline.py,sha256=leK898fPt3zq52v-O6dK4c-wJBcY-E6v-y57Fk3kJ3Q,4021
61
+ crimson/quests/types.py,sha256=iSrz8VSiRZh1pUDSyq37ir7B28c0HF8DjdF6OJ3FoBo,1772
62
+ crimson/render/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
63
+ crimson/render/terrain_fx.py,sha256=MpBV6ukGwGqDluIO86BQhHRVWUvcFOi8MV-z_TfLsiw,2705
64
+ crimson/render/world_renderer.py,sha256=wSipGqm900eJaBNctXO9WDRSdM6ruOBXsG-TDXWSyCE,83776
65
+ crimson/sim/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
66
+ crimson/sim/world_defs.py,sha256=HiMl--THnII3BTpt6mWAd20Xu-SRzCyHcs5pCR8aMbU,2195
67
+ crimson/sim/world_state.py,sha256=h_PPaomj-1KPscPsW3zZ07tVFb3DYPM-FQq3pepMGng,15653
68
+ crimson/terrain_assets.py,sha256=X19LN3EZSkiz4uRvQfra3NtAXKAA8SMQ-nvOBlrspwE,670
69
+ crimson/tutorial/__init__.py,sha256=SDDZKRpkQBqyVMU9U03DN242t8lo8ThLuWHmbVrP69k,333
70
+ crimson/tutorial/timeline.py,sha256=aJDtASfkCSCL1RH4AZX-7xOZml9E4AORTAX6lI6A578,11307
71
+ crimson/typo/__init__.py,sha256=PHh4jqD7i0aL8W8hXThczKcT4xsNZiXanBxOH5IOW5I,36
72
+ crimson/typo/names.py,sha256=SslKyngsp81iHJPEdMJakvaenIkTskZIjLKuEmrt7d0,6320
73
+ crimson/typo/player.py,sha256=2cnyiOY9WzJadxvvYdnc9BQhmUifFfdRCUjq5LLiby0,1119
74
+ crimson/typo/spawns.py,sha256=63lYhgR8iFcE80YBYP_Z2Xvg7xKo1G1swRPLqM0JBS0,1852
75
+ crimson/typo/typing.py,sha256=-h9udu7s-zGKlXxd6Xi7vbaiHj3pNuHqbL_0jqoTR9s,1284
76
+ crimson/ui/__init__.py,sha256=0-of-3kMbUcm08RgaXNBkTaWHsqYtG0bk1b0ztpLyww,77
77
+ crimson/ui/cursor.py,sha256=_6EJ7tnsW_Q4kPUrqBO5FInxhpHKUGGyVpDET5EG38I,2842
78
+ crimson/ui/demo_trial_overlay.py,sha256=DugDImlPKx6DGe6gaXmA2s7dssjS6E9PMTi_3mOshx8,8724
79
+ crimson/ui/game_over.py,sha256=hXgV0v4L9peohj2xUIipOSlY1gMRsmAPCxlToo8dj08,29610
80
+ crimson/ui/hud.py,sha256=MnCBZGdI2nLMp9JOxaJBA0inErW2X7W95LtNb80vVIo,22283
81
+ crimson/ui/perk_menu.py,sha256=FdbwG_B7UyZmPVdvZho0nJNvkAVc0VVHM2TSrY4eTfM,12740
82
+ crimson/views/__init__.py,sha256=1UWCzBSsgIeOtcKxqM4Vf12JEfPHKfb9KRZgl2TB84Q,1880
83
+ crimson/views/aim_debug.py,sha256=2ldQ1MJO8AeG0i9x4ZJ3YLyp1X-HY_nJ0E1mY8jFF7Q,10873
84
+ crimson/views/animations.py,sha256=iI6uTpYS-lbjnpqz_ihETm5q09V6RqjO7YHqaGjovPc,10660
85
+ crimson/views/arsenal_debug.py,sha256=bS_FV6ffknOgSPBcAEZFDJ8CBjWitBziWVanjwXvMfs,14130
86
+ crimson/views/audio_bootstrap.py,sha256=a2qM91biyI-LlSiIhEceJ8ptYJy-xQ2-b-YjeyXWx7w,1408
87
+ crimson/views/bonuses.py,sha256=dbGx0IBjMMpqrnuVjBUkscxHq6uErCxAkEAb3uDhFqQ,7454
88
+ crimson/views/camera_debug.py,sha256=Vi6jANGQpXMK0bpL7hX_QQI_6Rl97ykElaoAuXd06YY,14025
89
+ crimson/views/camera_shake.py,sha256=miYxPJcVChv5P2AdbAWTEsrjZR0SAszqClLMbmrgg-0,8148
90
+ crimson/views/corpse_stamp_debug.py,sha256=1iLqiCQZpl8kbqfadgxbtVUiRFoGTnZfUwUXt3rVNq0,11716
91
+ crimson/views/decals_debug.py,sha256=tE-WUZIJ-skPiDB0kByQfyWS3XP371y4u-s7DT2fl08,27841
92
+ crimson/views/empty.py,sha256=ieyD6jk5KKqVdo90HXDm-ytewlIR087TJPj_7PmnaDI,358
93
+ crimson/views/fonts.py,sha256=MkAQz4boPsYa0vUBV5t2ddLVT5pRa-JKiAd0VHYglAo,3890
94
+ crimson/views/game_over.py,sha256=R7K7ENM7-ObcsZ5DqPHkrC8fuzAKLOFLZf3j7K5cvDE,4080
95
+ crimson/views/ground.py,sha256=_QzsoFSPKA2qIj8OTvJFr2mqT_sM5_w05ucL4a9cCZQ,10226
96
+ crimson/views/lighting_debug.py,sha256=T9ce224ZrJtztKTSFUn1G29MY0KYuHVhNdcDU7hlrFc,45502
97
+ crimson/views/particles.py,sha256=z3nFPEyItv9aAY_RbByl-jDlwvH22fG30sIMWiaR1aM,10109
98
+ crimson/views/perk_menu_debug.py,sha256=q4rn3R4tJASlep5jeAXwrBffj5_akJ4a4veq7FIpRiU,17831
99
+ crimson/views/perks.py,sha256=Uac31DVvHZvjDmMrHpfvkg7LVpXw76xKL1BqwaLAsHU,15052
100
+ crimson/views/player.py,sha256=cMZiFbJK_um8yAFR1pFW7Hdq4_Tl6RZmGmZk1FwAiZE,16569
101
+ crimson/views/player_sprite_debug.py,sha256=DDU6kl9sfhZ2nVXzBbsPSUKdIutHf5YNs2DrNQPRb2Q,11158
102
+ crimson/views/projectile_fx.py,sha256=pmMiXm2hJ1XeGf40VtdPk2TbVJX56aATBYTCRekYOdU,23177
103
+ crimson/views/projectile_render_debug.py,sha256=gogOiEGT1P_5qD5rMcuxqPac3-WrPJmbJGvg6USvoxE,14347
104
+ crimson/views/projectiles.py,sha256=k5zD_9614_-M4qYpnwKUS23fUUrjJS2Wzm7ZalOSKnU,8067
105
+ crimson/views/quest_title_overlay.py,sha256=EbMvXvYqagLLXmhaM7PC0fhxxhHGYGYI2ytgYiWqAd4,3338
106
+ crimson/views/registry.py,sha256=Kxc_aQMWOAjI6Uq_SRkb0AmYQxyVmM6rxT3UF74irzs,930
107
+ crimson/views/rush.py,sha256=61XavUpHgwc6Kw9e9Y-OkDt_XfY6iHoDrcpxjMbe1ms,298
108
+ crimson/views/small_font_debug.py,sha256=c-9qw7QL-nY-ajVOgDNjMGOnGqmVx_j-Iiy5DQhOlz0,7712
109
+ crimson/views/spawn_plan.py,sha256=Xo2vw3VwNYqdbNJ6K8d3PJ81sJjLdrXhYEQLTwrxAdM,13378
110
+ crimson/views/sprites.py,sha256=_DMjuXaRKvzgGRkv9gieaSubJFJraKNZ01C7iNg1niE,7372
111
+ crimson/views/survival.py,sha256=6yuhPe6-bC4IDaAbv74PStiAX9cBbg7HFbSr12ytQAw,333
112
+ crimson/views/terrain.py,sha256=ku3R7igsvtfLdeh_uBbY41Tumi9BaNa1C6IAe6vKUOY,4763
113
+ crimson/views/ui.py,sha256=2gGyCiI_JnlHzG9CWH8nomQIgCthfLwP7bTsDyqAyx4,4294
114
+ crimson/views/wicons.py,sha256=bGbmE25q_pYJZTiPt6Q5Sy1Ub667KvUIFp1a5rVxhi0,5783
115
+ crimson/weapon_sfx.py,sha256=_JAFpXi-c7jFNz3hxKygAYwqAR6zC94QnNHQYDCHnOY,1717
116
+ crimson/weapons.py,sha256=FSyOmOQbmGBS5KTZuyBQ5hd5prFp6V1LtIQRoGc8pqQ,23666
117
+ grim/__init__.py,sha256=Cn4f7JY6bscS1gwjDPaWvIvj_vneuUXaiu7wdJB6EVg,262
118
+ grim/app.py,sha256=Wae-tQcpDA0mI99GRupHXoxKnC5F6xfLGxVV8KOxcx4,2702
119
+ grim/assets.py,sha256=k2rFqNYfcUZfRjFL7r_UBGf1RlcP-urChXCUJ6GzEsI,7661
120
+ grim/audio.py,sha256=V_TrnfrCb_0e2jeOhN7_kGBu8uMdMsFbk-REoUeWY7Q,3189
121
+ grim/config.py,sha256=eQk_3ls_na9AiQr1yqbdyNRc2venM3_NS7Cqz75V08g,9644
122
+ grim/console.py,sha256=bdhAedOwMpk9yKqwMfMoUsoLG5RQqOET6Ya0Zu2eWro,26598
123
+ grim/fonts/__init__.py,sha256=yd2yLa0C-LTfjzZHpBl_AglynL3-PmPHePkulMiqLh0,81
124
+ grim/fonts/grim_mono.py,sha256=DdTAuzGyliJF9ufI5YP039yfYYMWnJp3b6j7u9yvd8Q,3587
125
+ grim/fonts/small.py,sha256=8Fgqhs-iejqXLyPxo2if2jqfKy9YvM2AZRTBj91M4Rs,4495
126
+ grim/input.py,sha256=kiJ0AcOIT7slGA8snkfiKeAP7GyGDMF0dvZz2ePcG4I,1156
127
+ grim/jaz.py,sha256=HcuJR9cj0RLhANlv9fzDuyLh8U6ktYoCiSq8hqg6Sx8,2870
128
+ grim/math.py,sha256=xf6K1NKduWNHpKV1KE3Oet_uZW4pF4w0HhaXk7eGMm0,343
129
+ grim/music.py,sha256=UGZ8W3a3Hus78O2N6WRf24_BYn9Eg9mj7RLi6jB5feo,12760
130
+ grim/paq.py,sha256=KKQRueYP4HiqF9hNEk7dSem-4bn0J4OH2TJDg2CjbnA,1952
131
+ grim/rand.py,sha256=TTxAe2o92XLloLOdMwLbE0lwdHXd8LPgWsrZCX2-nVU,862
132
+ grim/sfx.py,sha256=cpn2Mmeio7BSDgbStSft-eZchO9Ot2MrK6iXJqxlLqU,7836
133
+ grim/sfx_map.py,sha256=FM5iBzKkG30Vtu78SRavVNgXMbGK7ZFcQ8i6lgMlzVw,4697
134
+ grim/terrain_render.py,sha256=EZ7ySYJyTZwXcrJx1mKbY3ewZtPi7Y270XnZgGJyZG8,31509
135
+ grim/view.py,sha256=oF4pHZehBqOxPjKMU28TDg3qATh_amMIRJp-vMQnpn4,334
136
+ crimsonland-0.1.0.dev5.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
137
+ crimsonland-0.1.0.dev5.dist-info/entry_points.txt,sha256=jzzcExxiE9xpt4Iw2nbB1lwTv2Zj4H14WJTIPMkAjoE,77
138
+ crimsonland-0.1.0.dev5.dist-info/METADATA,sha256=kBl208VLbh6OZaSSjTWXJ3pMc2q9tFXkDEJYhjg1BZc,243
139
+ crimsonland-0.1.0.dev5.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.28
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,4 @@
1
+ [console_scripts]
2
+ crimson = crimson.cli:main
3
+ crimsonland = crimson.cli:main
4
+
grim/__init__.py ADDED
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ __all__ = [
4
+ "app",
5
+ "assets",
6
+ "audio",
7
+ "config",
8
+ "console",
9
+ "fonts",
10
+ "input",
11
+ "jaz",
12
+ "math",
13
+ "music",
14
+ "paq",
15
+ "rand",
16
+ "sfx",
17
+ "sfx_map",
18
+ "terrain_render",
19
+ "view",
20
+ ]
grim/app.py ADDED
@@ -0,0 +1,92 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ import shutil
5
+
6
+ import pyray as rl
7
+
8
+ from .view import View
9
+
10
+ SCREENSHOT_DIR = Path("screenshots")
11
+ SCREENSHOT_KEY = rl.KeyboardKey.KEY_F12
12
+
13
+
14
+ def _next_screenshot_index(directory: Path) -> int:
15
+ if not directory.exists():
16
+ return 1
17
+ max_index = 0
18
+ for entry in directory.glob("*.png"):
19
+ stem = entry.stem
20
+ if stem.isdigit():
21
+ max_index = max(max_index, int(stem))
22
+ return max_index + 1
23
+
24
+
25
+ def _view_should_close(view: View) -> bool:
26
+ should_close = getattr(view, "should_close", None)
27
+ if callable(should_close):
28
+ return bool(should_close())
29
+ return bool(getattr(view, "close_requested", False))
30
+
31
+
32
+ def run_view(
33
+ view: View,
34
+ *,
35
+ width: int = 1280,
36
+ height: int = 720,
37
+ title: str = "Crimsonland",
38
+ fps: int = 60,
39
+ config_flags: int = 0,
40
+ ) -> None:
41
+ """Run a Raylib window with a pluggable debug view."""
42
+ if config_flags:
43
+ rl.set_config_flags(config_flags)
44
+ rl.init_window(width, height, title)
45
+ rl.set_target_fps(fps)
46
+ open_fn = getattr(view, "open", None)
47
+ if callable(open_fn):
48
+ open_fn()
49
+ screenshot_dir = SCREENSHOT_DIR if SCREENSHOT_DIR.is_absolute() else Path.cwd() / SCREENSHOT_DIR
50
+ screenshot_index = _next_screenshot_index(screenshot_dir)
51
+ while not rl.window_should_close():
52
+ dt = rl.get_frame_time()
53
+ view.update(dt)
54
+ take_screenshot = rl.is_key_pressed(SCREENSHOT_KEY)
55
+ consume_screenshot = getattr(view, "consume_screenshot_request", None)
56
+ if callable(consume_screenshot) and consume_screenshot():
57
+ take_screenshot = True
58
+ rl.begin_drawing()
59
+ view.draw()
60
+ rl.end_drawing()
61
+ if _view_should_close(view):
62
+ break
63
+ if take_screenshot:
64
+ screenshot_dir.mkdir(parents=True, exist_ok=True)
65
+ filename = f"{screenshot_index:05d}.png"
66
+ rl.take_screenshot(filename)
67
+ src = Path.cwd() / filename
68
+ if src.exists():
69
+ shutil.move(str(src), str(screenshot_dir / filename))
70
+ screenshot_index += 1
71
+ close_fn = getattr(view, "close", None)
72
+ if callable(close_fn):
73
+ close_fn()
74
+ rl.close_window()
75
+
76
+
77
+ def run_window(
78
+ width: int = 1280,
79
+ height: int = 720,
80
+ title: str = "Crimsonland",
81
+ fps: int = 60,
82
+ ) -> None:
83
+ """Open a minimal Raylib window for the reference implementation."""
84
+
85
+ class _EmptyView:
86
+ def update(self, dt: float) -> None:
87
+ return None
88
+
89
+ def draw(self) -> None:
90
+ rl.clear_background(rl.BLACK)
91
+
92
+ run_view(_EmptyView(), width=width, height=height, title=title, fps=fps)
grim/assets.py ADDED
@@ -0,0 +1,231 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ import io
5
+ from pathlib import Path
6
+
7
+ import pyray as rl
8
+ from PIL import Image
9
+
10
+ from . import jaz, paq
11
+
12
+
13
+ PAQ_NAME = "crimson.paq"
14
+
15
+
16
+ def find_paq_path(assets_root: Path, *, paq_name: str = PAQ_NAME) -> Path | None:
17
+ """Return the first matching PAQ path for the given assets root.
18
+
19
+ The repo layout often keeps extracted assets under `artifacts/assets/` while
20
+ the runtime PAQ lives under `artifacts/runtime/`. Views typically point at
21
+ the extracted root, so we look for common sibling/parent layouts too.
22
+ """
23
+
24
+ roots = (assets_root, assets_root.parent, assets_root.parent.parent)
25
+ for root in roots:
26
+ direct = root / paq_name
27
+ if direct.is_file():
28
+ return direct
29
+ runtime = root / "runtime" / paq_name
30
+ if runtime.is_file():
31
+ return runtime
32
+ return None
33
+
34
+
35
+ def resolve_asset_path(assets_root: Path, rel_path: str) -> Path | None:
36
+ direct = assets_root / rel_path
37
+ if direct.is_file():
38
+ return direct
39
+ legacy = assets_root / "crimson" / rel_path
40
+ if legacy.is_file():
41
+ return legacy
42
+ return None
43
+
44
+
45
+ @dataclass(slots=True)
46
+ class TextureLoader:
47
+ assets_root: Path
48
+ cache: PaqTextureCache | None = None
49
+ missing: list[str] = field(default_factory=list)
50
+ _fs_textures: dict[str, rl.Texture] = field(default_factory=dict)
51
+
52
+ @classmethod
53
+ def from_assets_root(cls, assets_root: Path) -> TextureLoader:
54
+ paq_path = find_paq_path(assets_root)
55
+ if paq_path is not None:
56
+ try:
57
+ entries = load_paq_entries_from_path(paq_path)
58
+ cache = PaqTextureCache(entries=entries, textures={})
59
+ return cls(assets_root=assets_root, cache=cache)
60
+ except Exception:
61
+ pass
62
+ return cls(assets_root=assets_root)
63
+
64
+ def resolve_path(self, rel_path: str) -> Path | None:
65
+ return resolve_asset_path(self.assets_root, rel_path)
66
+
67
+ def _record_missing(self, rel_path: str) -> None:
68
+ raise FileNotFoundError(f"Missing asset: {rel_path}")
69
+
70
+ def load_from_cache(self, name: str, rel_path: str) -> rl.Texture | None:
71
+ if self.cache is None:
72
+ return None
73
+ try:
74
+ asset = self.cache.get_or_load(name, rel_path)
75
+ except FileNotFoundError:
76
+ return None
77
+ if asset.texture is None:
78
+ return None
79
+ return asset.texture
80
+
81
+ def load_from_path(self, name: str, rel_path: str) -> rl.Texture | None:
82
+ if name in self._fs_textures:
83
+ return self._fs_textures[name]
84
+ path = resolve_asset_path(self.assets_root, rel_path)
85
+ if path is None:
86
+ self._record_missing(rel_path)
87
+ return None
88
+ texture = rl.load_texture(str(path))
89
+ self._fs_textures[name] = texture
90
+ return texture
91
+
92
+ def get(self, *, name: str, paq_rel: str, fs_rel: str | None = None) -> rl.Texture | None:
93
+ if self.cache is not None:
94
+ texture = self.load_from_cache(name, paq_rel)
95
+ if texture is not None:
96
+ return texture
97
+ if fs_rel is None:
98
+ fs_rel = paq_rel
99
+ return self.load_from_path(name, fs_rel)
100
+
101
+
102
+ @dataclass(slots=True)
103
+ class TextureAsset:
104
+ name: str
105
+ rel_path: str
106
+ texture: rl.Texture2D | None
107
+
108
+ def unload(self) -> None:
109
+ texture = self.texture
110
+ if texture is None:
111
+ return
112
+ rl.unload_texture(texture)
113
+ self.texture = None
114
+
115
+
116
+ @dataclass(slots=True)
117
+ class LogoAssets:
118
+ backplasma: TextureAsset
119
+ mockup: TextureAsset
120
+ logo_esrb: TextureAsset
121
+ loading: TextureAsset
122
+ cl_logo: TextureAsset
123
+
124
+ def all(self) -> tuple[TextureAsset, ...]:
125
+ return (
126
+ self.backplasma,
127
+ self.mockup,
128
+ self.logo_esrb,
129
+ self.loading,
130
+ self.cl_logo,
131
+ )
132
+
133
+ def loaded_count(self) -> int:
134
+ return sum(1 for asset in self.all() if asset.texture is not None)
135
+
136
+
137
+ @dataclass(slots=True)
138
+ class PaqTextureCache:
139
+ entries: dict[str, bytes]
140
+ textures: dict[str, TextureAsset]
141
+
142
+ def get(self, name: str) -> TextureAsset | None:
143
+ return self.textures.get(name)
144
+
145
+ def texture(self, name: str) -> rl.Texture2D | None:
146
+ asset = self.textures.get(name)
147
+ return asset.texture if asset is not None else None
148
+
149
+ def get_or_load(self, name: str, rel_path: str) -> TextureAsset:
150
+ if name in self.textures:
151
+ return self.textures[name]
152
+ asset = _load_texture_asset_from_bytes(name, rel_path, self.entries.get(rel_path))
153
+ self.textures[name] = asset
154
+ return asset
155
+
156
+ def loaded_count(self) -> int:
157
+ return sum(1 for asset in self.textures.values() if asset.texture is not None)
158
+
159
+
160
+ def load_paq_entries_from_path(paq_path: Path) -> dict[str, bytes]:
161
+ entries: dict[str, bytes] = {}
162
+ if not paq_path.exists():
163
+ raise FileNotFoundError(f"Missing PAQ archive: {paq_path}")
164
+ for name, data in paq.iter_entries(paq_path):
165
+ entries[name.replace("\\", "/")] = data
166
+ return entries
167
+
168
+
169
+ def load_paq_entries(assets_dir: Path) -> dict[str, bytes]:
170
+ return load_paq_entries_from_path(assets_dir / PAQ_NAME)
171
+
172
+
173
+ def _load_texture_from_bytes(data: bytes, fmt: str) -> rl.Texture2D:
174
+ image = rl.load_image_from_memory(fmt, data, len(data))
175
+ texture = rl.load_texture_from_image(image)
176
+ rl.unload_image(image)
177
+ rl.set_texture_filter(texture, rl.TEXTURE_FILTER_BILINEAR)
178
+ return texture
179
+
180
+
181
+ def _load_texture_asset_from_bytes(name: str, rel_path: str, data: bytes | None) -> TextureAsset:
182
+ if data is None:
183
+ raise FileNotFoundError(f"Missing asset data: {rel_path}")
184
+ if rel_path.lower().endswith(".jaz"):
185
+ jaz_image = jaz.decode_jaz_bytes(data)
186
+ buf = io.BytesIO()
187
+ jaz_image.composite_image().save(buf, format="PNG")
188
+ return TextureAsset(
189
+ name=name,
190
+ rel_path=rel_path,
191
+ texture=_load_texture_from_bytes(buf.getvalue(), ".png"),
192
+ )
193
+ if rel_path.lower().endswith(".tga"):
194
+ img = Image.open(io.BytesIO(data))
195
+ buf = io.BytesIO()
196
+ img.save(buf, format="PNG")
197
+ return TextureAsset(
198
+ name=name,
199
+ rel_path=rel_path,
200
+ texture=_load_texture_from_bytes(buf.getvalue(), ".png"),
201
+ )
202
+ if rel_path.lower().endswith((".jpg", ".jpeg")):
203
+ img = Image.open(io.BytesIO(data))
204
+ buf = io.BytesIO()
205
+ img.save(buf, format="PNG")
206
+ return TextureAsset(
207
+ name=name,
208
+ rel_path=rel_path,
209
+ texture=_load_texture_from_bytes(buf.getvalue(), ".png"),
210
+ )
211
+ return TextureAsset(name=name, rel_path=rel_path, texture=None)
212
+
213
+
214
+ def load_logo_assets(assets_dir: Path, *, entries: dict[str, bytes] | None = None) -> LogoAssets:
215
+ if entries is None:
216
+ entries = load_paq_entries(assets_dir)
217
+ return LogoAssets(
218
+ backplasma=_load_texture_asset_from_bytes(
219
+ "backplasma", "load/backplasma.jaz", entries.get("load/backplasma.jaz")
220
+ ),
221
+ mockup=_load_texture_asset_from_bytes("mockup", "load/mockup.jaz", entries.get("load/mockup.jaz")),
222
+ logo_esrb=_load_texture_asset_from_bytes(
223
+ "logo_esrb", "load/esrb_mature.jaz", entries.get("load/esrb_mature.jaz")
224
+ ),
225
+ loading=_load_texture_asset_from_bytes("loading", "load/loading.jaz", entries.get("load/loading.jaz")),
226
+ cl_logo=_load_texture_asset_from_bytes(
227
+ "cl_logo",
228
+ "load/logo_crimsonland.tga",
229
+ entries.get("load/logo_crimsonland.tga"),
230
+ ),
231
+ )
grim/audio.py ADDED
@@ -0,0 +1,106 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from collections.abc import Callable
5
+ from pathlib import Path
6
+ import random
7
+
8
+ import pyray as rl
9
+
10
+ from .config import CrimsonConfig
11
+ from .console import ConsoleState
12
+ from . import music, sfx
13
+
14
+
15
+ @dataclass(slots=True)
16
+ class AudioState:
17
+ ready: bool
18
+ music: music.MusicState
19
+ sfx: sfx.SfxState
20
+
21
+
22
+ def init_audio_state(config: CrimsonConfig, assets_dir: Path, console: ConsoleState) -> AudioState:
23
+ music_disabled = int(config.data.get("music_disable", 0)) != 0
24
+ sound_disabled = int(config.data.get("sound_disable", 0)) != 0
25
+ music_volume = float(config.data.get("music_volume", 1.0))
26
+ sfx_volume = float(config.data.get("sfx_volume", 1.0))
27
+
28
+ music_enabled = not music_disabled
29
+ sfx_enabled = not sound_disabled
30
+ if not music_enabled and not sfx_enabled:
31
+ console.log.log("audio: disabled (music + sfx)")
32
+ console.log.flush()
33
+ return AudioState(
34
+ ready=False,
35
+ music=music.init_music_state(ready=False, enabled=False, volume=music_volume),
36
+ sfx=sfx.init_sfx_state(ready=False, enabled=False, volume=sfx_volume),
37
+ )
38
+
39
+ if not rl.is_audio_device_ready():
40
+ rl.init_audio_device()
41
+ ready = bool(rl.is_audio_device_ready())
42
+ if not ready:
43
+ console.log.log("audio: device init failed")
44
+ console.log.flush()
45
+ return AudioState(
46
+ ready=False,
47
+ music=music.init_music_state(ready=False, enabled=False, volume=music_volume),
48
+ sfx=sfx.init_sfx_state(ready=False, enabled=False, volume=sfx_volume),
49
+ )
50
+
51
+ state = AudioState(
52
+ ready=True,
53
+ music=music.init_music_state(ready=True, enabled=music_enabled, volume=music_volume),
54
+ sfx=sfx.init_sfx_state(ready=True, enabled=sfx_enabled, volume=sfx_volume),
55
+ )
56
+ sfx.load_sfx_index(state.sfx, assets_dir, console)
57
+ music.load_music_tracks(state.music, assets_dir, console)
58
+ return state
59
+
60
+
61
+ def play_music(state: AudioState, track_name: str) -> None:
62
+ music.play_music(state.music, track_name)
63
+
64
+
65
+ def stop_music(state: AudioState) -> None:
66
+ music.stop_music(state.music)
67
+
68
+
69
+ def trigger_game_tune(state: AudioState, *, rand: Callable[[], int] | None = None) -> str | None:
70
+ return music.trigger_game_tune(state.music, rand=rand)
71
+
72
+
73
+ def play_sfx(
74
+ state: AudioState | None,
75
+ key: str | None,
76
+ *,
77
+ rng: random.Random | None = None,
78
+ allow_variants: bool = True,
79
+ ) -> None:
80
+ if state is None:
81
+ return
82
+ sfx.play_sfx(state.sfx, key, rng=rng, allow_variants=allow_variants)
83
+
84
+
85
+ def set_sfx_volume(state: AudioState | None, volume: float) -> None:
86
+ if state is None:
87
+ return
88
+ sfx.set_sfx_volume(state.sfx, volume)
89
+
90
+
91
+ def set_music_volume(state: AudioState | None, volume: float) -> None:
92
+ if state is None:
93
+ return
94
+ music.set_music_volume(state.music, volume)
95
+
96
+
97
+ def update_audio(state: AudioState, dt: float) -> None:
98
+ music.update_music(state.music, dt)
99
+
100
+
101
+ def shutdown_audio(state: AudioState) -> None:
102
+ if not state.ready:
103
+ return
104
+ sfx.shutdown_sfx(state.sfx)
105
+ music.shutdown_music(state.music)
106
+ rl.close_audio_device()