crimsonland 0.1.0.dev1__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 (138) 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 +153 -0
  5. crimson/bonuses.py +167 -0
  6. crimson/camera.py +75 -0
  7. crimson/cli.py +377 -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 +663 -0
  34. crimson/gameplay.py +2450 -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 +1039 -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 +1338 -0
  65. crimson/sim/__init__.py +1 -0
  66. crimson/sim/world_defs.py +56 -0
  67. crimson/sim/world_state.py +421 -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 +414 -0
  86. crimson/views/bonuses.py +201 -0
  87. crimson/views/camera_debug.py +359 -0
  88. crimson/views/camera_shake.py +229 -0
  89. crimson/views/corpse_stamp_debug.py +324 -0
  90. crimson/views/decals_debug.py +739 -0
  91. crimson/views/empty.py +19 -0
  92. crimson/views/fonts.py +114 -0
  93. crimson/views/game_over.py +117 -0
  94. crimson/views/ground.py +259 -0
  95. crimson/views/lighting_debug.py +1166 -0
  96. crimson/views/particles.py +293 -0
  97. crimson/views/perk_menu_debug.py +430 -0
  98. crimson/views/perks.py +398 -0
  99. crimson/views/player.py +433 -0
  100. crimson/views/player_sprite_debug.py +314 -0
  101. crimson/views/projectile_fx.py +608 -0
  102. crimson/views/projectile_render_debug.py +407 -0
  103. crimson/views/projectiles.py +221 -0
  104. crimson/views/quest_title_overlay.py +108 -0
  105. crimson/views/registry.py +34 -0
  106. crimson/views/rush.py +16 -0
  107. crimson/views/small_font_debug.py +204 -0
  108. crimson/views/spawn_plan.py +363 -0
  109. crimson/views/sprites.py +214 -0
  110. crimson/views/survival.py +15 -0
  111. crimson/views/terrain.py +132 -0
  112. crimson/views/ui.py +123 -0
  113. crimson/views/wicons.py +166 -0
  114. crimson/weapon_sfx.py +63 -0
  115. crimson/weapons.py +860 -0
  116. crimsonland-0.1.0.dev1.dist-info/METADATA +9 -0
  117. crimsonland-0.1.0.dev1.dist-info/RECORD +138 -0
  118. crimsonland-0.1.0.dev1.dist-info/WHEEL +4 -0
  119. crimsonland-0.1.0.dev1.dist-info/entry_points.txt +4 -0
  120. grim/__init__.py +20 -0
  121. grim/app.py +92 -0
  122. grim/assets.py +231 -0
  123. grim/audio.py +106 -0
  124. grim/config.py +294 -0
  125. grim/console.py +737 -0
  126. grim/fonts/__init__.py +7 -0
  127. grim/fonts/grim_mono.py +111 -0
  128. grim/fonts/small.py +120 -0
  129. grim/input.py +44 -0
  130. grim/jaz.py +103 -0
  131. grim/math.py +17 -0
  132. grim/music.py +403 -0
  133. grim/paq.py +76 -0
  134. grim/rand.py +37 -0
  135. grim/sfx.py +276 -0
  136. grim/sfx_map.py +103 -0
  137. grim/terrain_render.py +840 -0
  138. grim/view.py +16 -0
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.3
2
+ Name: crimsonland
3
+ Version: 0.1.0.dev1
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,138 @@
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=LLCYI9oUoPHbmh_pjW4Ey1Bk33_nNnYK5qwroCbnEPQ,4616
5
+ crimson/bonuses.py,sha256=owwYIRHRu1Kymtt4eEvpd62JwWAg8LOe20vDuPFB5SU,5094
6
+ crimson/camera.py,sha256=VxTNXTh0qHh5VR1OpkiXr-QcgEWPrWw0A3PFyQFqDkY,2278
7
+ crimson/cli.py,sha256=UbDqUVPoOvh2udY_uxJ6yvQLpVEBkG-1YhBudKcoKDM,14485
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=088ZGsXdakFz_IDYc9hjQTNHCEy0niuPuCVcspdc0Yw,25961
34
+ crimson/gameplay.py,sha256=Wvq5aeEZ-WHxOix7Bt_1V7pPn11mCn7pp9aDbZvmgl8,86812
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=HrT7peHvOAvXAmFyTDGZnn7gm7ZbDGz58WG0tDLCKU8,40104
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=T3aQFa9gonzypiN-jZk-aZtG-pU6I6GazuRuNSx_yLk,58589
65
+ crimson/sim/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
66
+ crimson/sim/world_defs.py,sha256=xkf4NpgSCIR5zmK_sLnoej28XYvDVYSSLcbvxsB2FWQ,1907
67
+ crimson/sim/world_state.py,sha256=kCaNJ28J5OGd_Pg-0Log5IsLjRSeKHeteSrIB7eHPhY,15607
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=MOpQObtuXN43W53GzFBr5l_BC0l7hfkee4237AGvXAQ,14554
86
+ crimson/views/bonuses.py,sha256=dbGx0IBjMMpqrnuVjBUkscxHq6uErCxAkEAb3uDhFqQ,7454
87
+ crimson/views/camera_debug.py,sha256=Vi6jANGQpXMK0bpL7hX_QQI_6Rl97ykElaoAuXd06YY,14025
88
+ crimson/views/camera_shake.py,sha256=miYxPJcVChv5P2AdbAWTEsrjZR0SAszqClLMbmrgg-0,8148
89
+ crimson/views/corpse_stamp_debug.py,sha256=1iLqiCQZpl8kbqfadgxbtVUiRFoGTnZfUwUXt3rVNq0,11716
90
+ crimson/views/decals_debug.py,sha256=tE-WUZIJ-skPiDB0kByQfyWS3XP371y4u-s7DT2fl08,27841
91
+ crimson/views/empty.py,sha256=ieyD6jk5KKqVdo90HXDm-ytewlIR087TJPj_7PmnaDI,358
92
+ crimson/views/fonts.py,sha256=MkAQz4boPsYa0vUBV5t2ddLVT5pRa-JKiAd0VHYglAo,3890
93
+ crimson/views/game_over.py,sha256=R7K7ENM7-ObcsZ5DqPHkrC8fuzAKLOFLZf3j7K5cvDE,4080
94
+ crimson/views/ground.py,sha256=_QzsoFSPKA2qIj8OTvJFr2mqT_sM5_w05ucL4a9cCZQ,10226
95
+ crimson/views/lighting_debug.py,sha256=T9ce224ZrJtztKTSFUn1G29MY0KYuHVhNdcDU7hlrFc,45502
96
+ crimson/views/particles.py,sha256=z3nFPEyItv9aAY_RbByl-jDlwvH22fG30sIMWiaR1aM,10109
97
+ crimson/views/perk_menu_debug.py,sha256=q4rn3R4tJASlep5jeAXwrBffj5_akJ4a4veq7FIpRiU,17831
98
+ crimson/views/perks.py,sha256=Uac31DVvHZvjDmMrHpfvkg7LVpXw76xKL1BqwaLAsHU,15052
99
+ crimson/views/player.py,sha256=pj1_hFZ7VRUDfZYdUI1b-EpA7FdoBhhKM3Jk4x4s-GI,16540
100
+ crimson/views/player_sprite_debug.py,sha256=DDU6kl9sfhZ2nVXzBbsPSUKdIutHf5YNs2DrNQPRb2Q,11158
101
+ crimson/views/projectile_fx.py,sha256=XIp3Zq82Pz6g-B30qa6-nlKF_dseLNoQNSUPhj9Y4i8,23148
102
+ crimson/views/projectile_render_debug.py,sha256=kgfNXwd-p8htPXQRKwFOky9wSUnXeoQHoPUmgOFpm3Y,14889
103
+ crimson/views/projectiles.py,sha256=k5zD_9614_-M4qYpnwKUS23fUUrjJS2Wzm7ZalOSKnU,8067
104
+ crimson/views/quest_title_overlay.py,sha256=EbMvXvYqagLLXmhaM7PC0fhxxhHGYGYI2ytgYiWqAd4,3338
105
+ crimson/views/registry.py,sha256=Kxc_aQMWOAjI6Uq_SRkb0AmYQxyVmM6rxT3UF74irzs,930
106
+ crimson/views/rush.py,sha256=61XavUpHgwc6Kw9e9Y-OkDt_XfY6iHoDrcpxjMbe1ms,298
107
+ crimson/views/small_font_debug.py,sha256=c-9qw7QL-nY-ajVOgDNjMGOnGqmVx_j-Iiy5DQhOlz0,7712
108
+ crimson/views/spawn_plan.py,sha256=Xo2vw3VwNYqdbNJ6K8d3PJ81sJjLdrXhYEQLTwrxAdM,13378
109
+ crimson/views/sprites.py,sha256=_DMjuXaRKvzgGRkv9gieaSubJFJraKNZ01C7iNg1niE,7372
110
+ crimson/views/survival.py,sha256=6yuhPe6-bC4IDaAbv74PStiAX9cBbg7HFbSr12ytQAw,333
111
+ crimson/views/terrain.py,sha256=ku3R7igsvtfLdeh_uBbY41Tumi9BaNa1C6IAe6vKUOY,4763
112
+ crimson/views/ui.py,sha256=2gGyCiI_JnlHzG9CWH8nomQIgCthfLwP7bTsDyqAyx4,4294
113
+ crimson/views/wicons.py,sha256=bGbmE25q_pYJZTiPt6Q5Sy1Ub667KvUIFp1a5rVxhi0,5783
114
+ crimson/weapon_sfx.py,sha256=_JAFpXi-c7jFNz3hxKygAYwqAR6zC94QnNHQYDCHnOY,1717
115
+ crimson/weapons.py,sha256=FSyOmOQbmGBS5KTZuyBQ5hd5prFp6V1LtIQRoGc8pqQ,23666
116
+ grim/__init__.py,sha256=Cn4f7JY6bscS1gwjDPaWvIvj_vneuUXaiu7wdJB6EVg,262
117
+ grim/app.py,sha256=Wae-tQcpDA0mI99GRupHXoxKnC5F6xfLGxVV8KOxcx4,2702
118
+ grim/assets.py,sha256=k2rFqNYfcUZfRjFL7r_UBGf1RlcP-urChXCUJ6GzEsI,7661
119
+ grim/audio.py,sha256=V_TrnfrCb_0e2jeOhN7_kGBu8uMdMsFbk-REoUeWY7Q,3189
120
+ grim/config.py,sha256=eQk_3ls_na9AiQr1yqbdyNRc2venM3_NS7Cqz75V08g,9644
121
+ grim/console.py,sha256=bdhAedOwMpk9yKqwMfMoUsoLG5RQqOET6Ya0Zu2eWro,26598
122
+ grim/fonts/__init__.py,sha256=yd2yLa0C-LTfjzZHpBl_AglynL3-PmPHePkulMiqLh0,81
123
+ grim/fonts/grim_mono.py,sha256=DdTAuzGyliJF9ufI5YP039yfYYMWnJp3b6j7u9yvd8Q,3587
124
+ grim/fonts/small.py,sha256=8Fgqhs-iejqXLyPxo2if2jqfKy9YvM2AZRTBj91M4Rs,4495
125
+ grim/input.py,sha256=kiJ0AcOIT7slGA8snkfiKeAP7GyGDMF0dvZz2ePcG4I,1156
126
+ grim/jaz.py,sha256=HcuJR9cj0RLhANlv9fzDuyLh8U6ktYoCiSq8hqg6Sx8,2870
127
+ grim/math.py,sha256=xf6K1NKduWNHpKV1KE3Oet_uZW4pF4w0HhaXk7eGMm0,343
128
+ grim/music.py,sha256=UGZ8W3a3Hus78O2N6WRf24_BYn9Eg9mj7RLi6jB5feo,12760
129
+ grim/paq.py,sha256=KKQRueYP4HiqF9hNEk7dSem-4bn0J4OH2TJDg2CjbnA,1952
130
+ grim/rand.py,sha256=TTxAe2o92XLloLOdMwLbE0lwdHXd8LPgWsrZCX2-nVU,862
131
+ grim/sfx.py,sha256=cpn2Mmeio7BSDgbStSft-eZchO9Ot2MrK6iXJqxlLqU,7836
132
+ grim/sfx_map.py,sha256=FM5iBzKkG30Vtu78SRavVNgXMbGK7ZFcQ8i6lgMlzVw,4697
133
+ grim/terrain_render.py,sha256=EZ7ySYJyTZwXcrJx1mKbY3ewZtPi7Y270XnZgGJyZG8,31509
134
+ grim/view.py,sha256=oF4pHZehBqOxPjKMU28TDg3qATh_amMIRJp-vMQnpn4,334
135
+ crimsonland-0.1.0.dev1.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
136
+ crimsonland-0.1.0.dev1.dist-info/entry_points.txt,sha256=jzzcExxiE9xpt4Iw2nbB1lwTv2Zj4H14WJTIPMkAjoE,77
137
+ crimsonland-0.1.0.dev1.dist-info/METADATA,sha256=haZKto26DpNaPBn-jCWBUGh1XcAECPOoe5q_rsNHOMs,243
138
+ crimsonland-0.1.0.dev1.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()