crimsonland 0.1.0.dev1__py3-none-any.whl → 0.1.0.dev3__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.
- crimson/audio_router.py +3 -1
- crimson/cli.py +5 -2
- crimson/projectiles.py +1 -0
- crimson/render/world_renderer.py +368 -31
- crimson/sim/world_defs.py +16 -5
- crimson/views/arsenal_debug.py +9 -30
- crimson/views/audio_bootstrap.py +47 -0
- crimson/views/projectile_render_debug.py +9 -30
- {crimsonland-0.1.0.dev1.dist-info → crimsonland-0.1.0.dev3.dist-info}/METADATA +1 -1
- {crimsonland-0.1.0.dev1.dist-info → crimsonland-0.1.0.dev3.dist-info}/RECORD +12 -11
- {crimsonland-0.1.0.dev1.dist-info → crimsonland-0.1.0.dev3.dist-info}/WHEEL +0 -0
- {crimsonland-0.1.0.dev1.dist-info → crimsonland-0.1.0.dev3.dist-info}/entry_points.txt +0 -0
crimson/audio_router.py
CHANGED
|
@@ -111,7 +111,9 @@ class AudioRouter:
|
|
|
111
111
|
beam_types: frozenset[int],
|
|
112
112
|
rand: Callable[[], int],
|
|
113
113
|
) -> str | None:
|
|
114
|
-
|
|
114
|
+
weapon = WEAPON_BY_ID.get(int(type_id))
|
|
115
|
+
ammo_class = weapon.ammo_class if weapon is not None else None
|
|
116
|
+
if ammo_class == 4:
|
|
115
117
|
return "sfx_shock_hit_01"
|
|
116
118
|
return self._rand_choice(rand, _BULLET_HIT_SFX)
|
|
117
119
|
|
crimson/cli.py
CHANGED
|
@@ -202,8 +202,9 @@ def cmd_view(
|
|
|
202
202
|
run_view(view, width=width, height=height, title=title, fps=fps)
|
|
203
203
|
|
|
204
204
|
|
|
205
|
-
@app.
|
|
205
|
+
@app.callback(invoke_without_command=True)
|
|
206
206
|
def cmd_game(
|
|
207
|
+
ctx: typer.Context,
|
|
207
208
|
width: int | None = typer.Option(None, help="window width (default: use crimson.cfg)"),
|
|
208
209
|
height: int | None = typer.Option(None, help="window height (default: use crimson.cfg)"),
|
|
209
210
|
fps: int = typer.Option(60, help="target fps"),
|
|
@@ -221,7 +222,9 @@ def cmd_game(
|
|
|
221
222
|
help="assets root (default: base-dir; missing .paq files are downloaded)",
|
|
222
223
|
),
|
|
223
224
|
) -> None:
|
|
224
|
-
"""Run the reimplementation game flow."""
|
|
225
|
+
"""Run the reimplementation game flow (default command)."""
|
|
226
|
+
if ctx.invoked_subcommand:
|
|
227
|
+
return
|
|
225
228
|
from .game import GameConfig, run_game
|
|
226
229
|
|
|
227
230
|
config = GameConfig(
|
crimson/projectiles.py
CHANGED
crimson/render/world_renderer.py
CHANGED
|
@@ -17,7 +17,14 @@ from ..effects_atlas import EFFECT_ID_ATLAS_TABLE_BY_ID, SIZE_CODE_GRID
|
|
|
17
17
|
from ..gameplay import bonus_find_aim_hover_entry, perk_active
|
|
18
18
|
from ..perks import PerkId
|
|
19
19
|
from ..projectiles import ProjectileTypeId
|
|
20
|
-
from ..sim.world_defs import
|
|
20
|
+
from ..sim.world_defs import (
|
|
21
|
+
BEAM_TYPES,
|
|
22
|
+
CREATURE_ANIM,
|
|
23
|
+
CREATURE_ASSET,
|
|
24
|
+
ION_TYPES,
|
|
25
|
+
KNOWN_PROJ_FRAMES,
|
|
26
|
+
PLASMA_PARTICLE_TYPES,
|
|
27
|
+
)
|
|
21
28
|
from ..weapons import WEAPON_BY_ID
|
|
22
29
|
|
|
23
30
|
if TYPE_CHECKING:
|
|
@@ -683,6 +690,366 @@ class WorldRenderer:
|
|
|
683
690
|
if drawn:
|
|
684
691
|
return
|
|
685
692
|
|
|
693
|
+
if type_id in PLASMA_PARTICLE_TYPES and self.particles_texture is not None:
|
|
694
|
+
particles_texture = self.particles_texture
|
|
695
|
+
atlas = EFFECT_ID_ATLAS_TABLE_BY_ID.get(0x0D)
|
|
696
|
+
if atlas is not None:
|
|
697
|
+
grid = SIZE_CODE_GRID.get(int(atlas.size_code))
|
|
698
|
+
if grid:
|
|
699
|
+
cell_w = float(particles_texture.width) / float(grid)
|
|
700
|
+
cell_h = float(particles_texture.height) / float(grid)
|
|
701
|
+
frame = int(atlas.frame)
|
|
702
|
+
col = frame % grid
|
|
703
|
+
row = frame // grid
|
|
704
|
+
src = rl.Rectangle(
|
|
705
|
+
cell_w * float(col),
|
|
706
|
+
cell_h * float(row),
|
|
707
|
+
max(0.0, cell_w - 2.0),
|
|
708
|
+
max(0.0, cell_h - 2.0),
|
|
709
|
+
)
|
|
710
|
+
|
|
711
|
+
speed_scale = float(getattr(proj, "speed_scale", 1.0))
|
|
712
|
+
fx_detail_1 = bool(self.config.data.get("fx_detail_1", 0)) if self.config is not None else True
|
|
713
|
+
|
|
714
|
+
rgb = (1.0, 1.0, 1.0)
|
|
715
|
+
spacing = 2.1
|
|
716
|
+
seg_limit = 3
|
|
717
|
+
tail_size = 12.0
|
|
718
|
+
head_size = 16.0
|
|
719
|
+
head_alpha_mul = 0.45
|
|
720
|
+
aura_rgb = rgb
|
|
721
|
+
aura_size = 120.0
|
|
722
|
+
aura_alpha_mul = 0.15
|
|
723
|
+
|
|
724
|
+
if type_id == int(ProjectileTypeId.PLASMA_RIFLE):
|
|
725
|
+
spacing = 2.5
|
|
726
|
+
seg_limit = 8
|
|
727
|
+
tail_size = 22.0
|
|
728
|
+
head_size = 56.0
|
|
729
|
+
aura_size = 256.0
|
|
730
|
+
aura_alpha_mul = 0.3
|
|
731
|
+
elif type_id == int(ProjectileTypeId.PLASMA_MINIGUN):
|
|
732
|
+
spacing = 2.1
|
|
733
|
+
seg_limit = 3
|
|
734
|
+
tail_size = 12.0
|
|
735
|
+
head_size = 16.0
|
|
736
|
+
aura_size = 120.0
|
|
737
|
+
aura_alpha_mul = 0.15
|
|
738
|
+
elif type_id == int(ProjectileTypeId.PLASMA_CANNON):
|
|
739
|
+
spacing = 2.6
|
|
740
|
+
seg_limit = 18
|
|
741
|
+
tail_size = 44.0
|
|
742
|
+
head_size = 84.0
|
|
743
|
+
aura_size = 256.0
|
|
744
|
+
# In the decompile, cannon reuses the tail alpha for the aura (0.4).
|
|
745
|
+
aura_alpha_mul = 0.4
|
|
746
|
+
elif type_id == int(ProjectileTypeId.SPIDER_PLASMA):
|
|
747
|
+
rgb = (0.3, 1.0, 0.3)
|
|
748
|
+
aura_rgb = rgb
|
|
749
|
+
elif type_id == int(ProjectileTypeId.SHRINKIFIER):
|
|
750
|
+
rgb = (0.3, 0.3, 1.0)
|
|
751
|
+
aura_rgb = rgb
|
|
752
|
+
|
|
753
|
+
if life >= 0.4:
|
|
754
|
+
# Reconstruct the tail length heuristic used by the native render path.
|
|
755
|
+
seg_count = int(float(getattr(proj, "base_damage", 0.0)))
|
|
756
|
+
if seg_count < 0:
|
|
757
|
+
seg_count = 0
|
|
758
|
+
seg_count //= 5
|
|
759
|
+
if seg_count > seg_limit:
|
|
760
|
+
seg_count = seg_limit
|
|
761
|
+
|
|
762
|
+
# The stored projectile angle is rotated by +pi/2 vs travel direction.
|
|
763
|
+
dir_x = math.cos(angle + math.pi / 2.0) * speed_scale
|
|
764
|
+
dir_y = math.sin(angle + math.pi / 2.0) * speed_scale
|
|
765
|
+
|
|
766
|
+
tail_tint = self._color_from_rgba((rgb[0], rgb[1], rgb[2], alpha * 0.4))
|
|
767
|
+
head_tint = self._color_from_rgba((rgb[0], rgb[1], rgb[2], alpha * head_alpha_mul))
|
|
768
|
+
aura_tint = self._color_from_rgba((aura_rgb[0], aura_rgb[1], aura_rgb[2], alpha * aura_alpha_mul))
|
|
769
|
+
|
|
770
|
+
rl.begin_blend_mode(rl.BLEND_ADDITIVE)
|
|
771
|
+
|
|
772
|
+
if seg_count > 0:
|
|
773
|
+
size = tail_size * scale
|
|
774
|
+
origin = rl.Vector2(size * 0.5, size * 0.5)
|
|
775
|
+
step_x = dir_x * spacing
|
|
776
|
+
step_y = dir_y * spacing
|
|
777
|
+
for idx in range(seg_count):
|
|
778
|
+
px = pos_x + float(idx) * step_x
|
|
779
|
+
py = pos_y + float(idx) * step_y
|
|
780
|
+
psx, psy = self.world_to_screen(px, py)
|
|
781
|
+
dst = rl.Rectangle(float(psx), float(psy), float(size), float(size))
|
|
782
|
+
rl.draw_texture_pro(particles_texture, src, dst, origin, 0.0, tail_tint)
|
|
783
|
+
|
|
784
|
+
size = head_size * scale
|
|
785
|
+
origin = rl.Vector2(size * 0.5, size * 0.5)
|
|
786
|
+
dst = rl.Rectangle(float(sx), float(sy), float(size), float(size))
|
|
787
|
+
rl.draw_texture_pro(particles_texture, src, dst, origin, 0.0, head_tint)
|
|
788
|
+
|
|
789
|
+
if fx_detail_1:
|
|
790
|
+
size = aura_size * scale
|
|
791
|
+
origin = rl.Vector2(size * 0.5, size * 0.5)
|
|
792
|
+
dst = rl.Rectangle(float(sx), float(sy), float(size), float(size))
|
|
793
|
+
rl.draw_texture_pro(particles_texture, src, dst, origin, 0.0, aura_tint)
|
|
794
|
+
|
|
795
|
+
rl.end_blend_mode()
|
|
796
|
+
return
|
|
797
|
+
|
|
798
|
+
fade = clamp(life * 2.5, 0.0, 1.0)
|
|
799
|
+
fade_alpha = fade * alpha
|
|
800
|
+
if fade_alpha > 1e-3:
|
|
801
|
+
tint = self._color_from_rgba((1.0, 1.0, 1.0, fade_alpha))
|
|
802
|
+
size = 56.0 * scale
|
|
803
|
+
dst = rl.Rectangle(float(sx), float(sy), float(size), float(size))
|
|
804
|
+
origin = rl.Vector2(size * 0.5, size * 0.5)
|
|
805
|
+
rl.begin_blend_mode(rl.BLEND_ADDITIVE)
|
|
806
|
+
rl.draw_texture_pro(particles_texture, src, dst, origin, 0.0, tint)
|
|
807
|
+
rl.end_blend_mode()
|
|
808
|
+
return
|
|
809
|
+
|
|
810
|
+
if type_id in BEAM_TYPES and texture is not None:
|
|
811
|
+
# Ion weapons and Fire Bullets use the projs.png streak effect (and Ion adds chain arcs on impact).
|
|
812
|
+
grid = 4
|
|
813
|
+
frame = 2
|
|
814
|
+
|
|
815
|
+
is_fire_bullets = type_id == int(ProjectileTypeId.FIRE_BULLETS)
|
|
816
|
+
is_ion = type_id in ION_TYPES
|
|
817
|
+
|
|
818
|
+
ox = float(getattr(proj, "origin_x", pos_x))
|
|
819
|
+
oy = float(getattr(proj, "origin_y", pos_y))
|
|
820
|
+
dx = pos_x - ox
|
|
821
|
+
dy = pos_y - oy
|
|
822
|
+
dist = math.hypot(dx, dy)
|
|
823
|
+
if dist <= 1e-6:
|
|
824
|
+
return
|
|
825
|
+
|
|
826
|
+
dir_x = dx / dist
|
|
827
|
+
dir_y = dy / dist
|
|
828
|
+
|
|
829
|
+
# In the native renderer, Ion Gun Master increases the chain effect thickness and reach.
|
|
830
|
+
perk_scale = 1.0
|
|
831
|
+
if any(perk_active(player, PerkId.ION_GUN_MASTER) for player in self.players):
|
|
832
|
+
perk_scale = 1.2
|
|
833
|
+
|
|
834
|
+
if life >= 0.4:
|
|
835
|
+
base_alpha = alpha
|
|
836
|
+
effect_scale = 1.0
|
|
837
|
+
else:
|
|
838
|
+
fade = clamp(life * 2.5, 0.0, 1.0)
|
|
839
|
+
base_alpha = fade * alpha
|
|
840
|
+
if type_id == int(ProjectileTypeId.ION_MINIGUN):
|
|
841
|
+
effect_scale = 1.05
|
|
842
|
+
elif type_id == int(ProjectileTypeId.ION_RIFLE):
|
|
843
|
+
effect_scale = 2.2
|
|
844
|
+
elif type_id == int(ProjectileTypeId.ION_CANNON):
|
|
845
|
+
effect_scale = 3.5
|
|
846
|
+
else:
|
|
847
|
+
effect_scale = 0.8
|
|
848
|
+
|
|
849
|
+
if base_alpha <= 1e-3:
|
|
850
|
+
return
|
|
851
|
+
|
|
852
|
+
streak_rgb = (1.0, 0.6, 0.1) if is_fire_bullets else (0.5, 0.6, 1.0)
|
|
853
|
+
head_rgb = (1.0, 1.0, 0.7)
|
|
854
|
+
|
|
855
|
+
# Only draw the last 256 units of the path.
|
|
856
|
+
start = 0.0
|
|
857
|
+
span = dist
|
|
858
|
+
if dist > 256.0:
|
|
859
|
+
start = dist - 256.0
|
|
860
|
+
span = 256.0
|
|
861
|
+
|
|
862
|
+
step = min(effect_scale * 3.1, 9.0)
|
|
863
|
+
sprite_scale = effect_scale * scale
|
|
864
|
+
|
|
865
|
+
rl.begin_blend_mode(rl.BLEND_ADDITIVE)
|
|
866
|
+
|
|
867
|
+
s = start
|
|
868
|
+
while s < dist:
|
|
869
|
+
t = (s - start) / span if span > 1e-6 else 1.0
|
|
870
|
+
seg_alpha = t * base_alpha
|
|
871
|
+
if seg_alpha > 1e-3:
|
|
872
|
+
px = ox + dir_x * s
|
|
873
|
+
py = oy + dir_y * s
|
|
874
|
+
psx, psy = self.world_to_screen(px, py)
|
|
875
|
+
tint = self._color_from_rgba((streak_rgb[0], streak_rgb[1], streak_rgb[2], seg_alpha))
|
|
876
|
+
self._draw_atlas_sprite(
|
|
877
|
+
texture,
|
|
878
|
+
grid=grid,
|
|
879
|
+
frame=frame,
|
|
880
|
+
x=psx,
|
|
881
|
+
y=psy,
|
|
882
|
+
scale=sprite_scale,
|
|
883
|
+
rotation_rad=angle,
|
|
884
|
+
tint=tint,
|
|
885
|
+
)
|
|
886
|
+
s += step
|
|
887
|
+
|
|
888
|
+
head_tint = self._color_from_rgba((head_rgb[0], head_rgb[1], head_rgb[2], base_alpha))
|
|
889
|
+
self._draw_atlas_sprite(
|
|
890
|
+
texture,
|
|
891
|
+
grid=grid,
|
|
892
|
+
frame=frame,
|
|
893
|
+
x=sx,
|
|
894
|
+
y=sy,
|
|
895
|
+
scale=sprite_scale,
|
|
896
|
+
rotation_rad=angle,
|
|
897
|
+
tint=head_tint,
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
# Ion-only: impact core + chain arcs. (Fire Bullets renders an extra particles.png overlay in a later pass.)
|
|
901
|
+
if is_fire_bullets and life >= 0.4 and self.particles_texture is not None:
|
|
902
|
+
particles_texture = self.particles_texture
|
|
903
|
+
atlas = EFFECT_ID_ATLAS_TABLE_BY_ID.get(0x0D)
|
|
904
|
+
if atlas is not None:
|
|
905
|
+
grid = SIZE_CODE_GRID.get(int(atlas.size_code))
|
|
906
|
+
if grid:
|
|
907
|
+
cell_w = float(particles_texture.width) / float(grid)
|
|
908
|
+
cell_h = float(particles_texture.height) / float(grid)
|
|
909
|
+
frame = int(atlas.frame)
|
|
910
|
+
col = frame % grid
|
|
911
|
+
row = frame // grid
|
|
912
|
+
src = rl.Rectangle(
|
|
913
|
+
cell_w * float(col),
|
|
914
|
+
cell_h * float(row),
|
|
915
|
+
max(0.0, cell_w - 2.0),
|
|
916
|
+
max(0.0, cell_h - 2.0),
|
|
917
|
+
)
|
|
918
|
+
tint = self._color_from_rgba((1.0, 1.0, 1.0, alpha))
|
|
919
|
+
size = 64.0 * scale
|
|
920
|
+
dst = rl.Rectangle(float(sx), float(sy), float(size), float(size))
|
|
921
|
+
origin = rl.Vector2(size * 0.5, size * 0.5)
|
|
922
|
+
rl.draw_texture_pro(particles_texture, src, dst, origin, float(angle * _RAD_TO_DEG), tint)
|
|
923
|
+
|
|
924
|
+
if is_ion and life < 0.4:
|
|
925
|
+
core_tint = self._color_from_rgba((0.5, 0.6, 1.0, base_alpha))
|
|
926
|
+
self._draw_atlas_sprite(
|
|
927
|
+
texture,
|
|
928
|
+
grid=grid,
|
|
929
|
+
frame=frame,
|
|
930
|
+
x=sx,
|
|
931
|
+
y=sy,
|
|
932
|
+
scale=1.0 * scale,
|
|
933
|
+
rotation_rad=angle,
|
|
934
|
+
tint=core_tint,
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
if type_id == int(ProjectileTypeId.ION_RIFLE):
|
|
938
|
+
radius = 88.0
|
|
939
|
+
elif type_id == int(ProjectileTypeId.ION_MINIGUN):
|
|
940
|
+
radius = 60.0
|
|
941
|
+
else:
|
|
942
|
+
radius = 128.0
|
|
943
|
+
radius *= perk_scale
|
|
944
|
+
|
|
945
|
+
# Pick a stable set of targets so the arc visuals don't flicker.
|
|
946
|
+
candidates: list[tuple[float, object]] = []
|
|
947
|
+
for creature in self.creatures.entries:
|
|
948
|
+
if not creature.active or float(creature.hp) <= 0.0:
|
|
949
|
+
continue
|
|
950
|
+
if float(getattr(creature, "hitbox_size", 0.0)) < 5.0:
|
|
951
|
+
continue
|
|
952
|
+
d = math.hypot(float(creature.x) - pos_x, float(creature.y) - pos_y)
|
|
953
|
+
threshold = float(creature.size) * 0.142857149 + 3.0
|
|
954
|
+
if d > radius + threshold:
|
|
955
|
+
continue
|
|
956
|
+
candidates.append((d, creature))
|
|
957
|
+
|
|
958
|
+
candidates.sort(key=lambda item: item[0])
|
|
959
|
+
targets = [creature for _d, creature in candidates[:8]]
|
|
960
|
+
|
|
961
|
+
inner = 10.0 * perk_scale * scale
|
|
962
|
+
outer = 14.0 * perk_scale * scale
|
|
963
|
+
u = 0.625
|
|
964
|
+
v0 = 0.0
|
|
965
|
+
v1 = 0.25
|
|
966
|
+
|
|
967
|
+
glow_targets: list[object] = []
|
|
968
|
+
rl.rl_set_texture(texture.id)
|
|
969
|
+
rl.rl_begin(rl.RL_QUADS)
|
|
970
|
+
|
|
971
|
+
for creature in targets:
|
|
972
|
+
tx, ty = self.world_to_screen(float(creature.x), float(creature.y))
|
|
973
|
+
ddx = tx - sx
|
|
974
|
+
ddy = ty - sy
|
|
975
|
+
dlen = math.hypot(ddx, ddy)
|
|
976
|
+
if dlen <= 1e-3:
|
|
977
|
+
continue
|
|
978
|
+
glow_targets.append(creature)
|
|
979
|
+
inv = 1.0 / dlen
|
|
980
|
+
nx = ddx * inv
|
|
981
|
+
ny = ddy * inv
|
|
982
|
+
px = -ny
|
|
983
|
+
py = nx
|
|
984
|
+
|
|
985
|
+
# Outer strip (softer).
|
|
986
|
+
half = outer * 0.5
|
|
987
|
+
off_x = px * half
|
|
988
|
+
off_y = py * half
|
|
989
|
+
x0 = sx - off_x
|
|
990
|
+
y0 = sy - off_y
|
|
991
|
+
x1 = sx + off_x
|
|
992
|
+
y1 = sy + off_y
|
|
993
|
+
x2 = tx + off_x
|
|
994
|
+
y2 = ty + off_y
|
|
995
|
+
x3 = tx - off_x
|
|
996
|
+
y3 = ty - off_y
|
|
997
|
+
|
|
998
|
+
outer_tint = self._color_from_rgba((0.5, 0.6, 1.0, base_alpha * 0.5))
|
|
999
|
+
rl.rl_color4ub(outer_tint.r, outer_tint.g, outer_tint.b, outer_tint.a)
|
|
1000
|
+
rl.rl_tex_coord2f(u, v0)
|
|
1001
|
+
rl.rl_vertex2f(x0, y0)
|
|
1002
|
+
rl.rl_tex_coord2f(u, v1)
|
|
1003
|
+
rl.rl_vertex2f(x1, y1)
|
|
1004
|
+
rl.rl_tex_coord2f(u, v1)
|
|
1005
|
+
rl.rl_vertex2f(x2, y2)
|
|
1006
|
+
rl.rl_tex_coord2f(u, v0)
|
|
1007
|
+
rl.rl_vertex2f(x3, y3)
|
|
1008
|
+
|
|
1009
|
+
# Inner strip (brighter).
|
|
1010
|
+
half = inner * 0.5
|
|
1011
|
+
off_x = px * half
|
|
1012
|
+
off_y = py * half
|
|
1013
|
+
x0 = sx - off_x
|
|
1014
|
+
y0 = sy - off_y
|
|
1015
|
+
x1 = sx + off_x
|
|
1016
|
+
y1 = sy + off_y
|
|
1017
|
+
x2 = tx + off_x
|
|
1018
|
+
y2 = ty + off_y
|
|
1019
|
+
x3 = tx - off_x
|
|
1020
|
+
y3 = ty - off_y
|
|
1021
|
+
|
|
1022
|
+
inner_tint = self._color_from_rgba((0.5, 0.6, 1.0, base_alpha))
|
|
1023
|
+
rl.rl_color4ub(inner_tint.r, inner_tint.g, inner_tint.b, inner_tint.a)
|
|
1024
|
+
rl.rl_tex_coord2f(u, v0)
|
|
1025
|
+
rl.rl_vertex2f(x0, y0)
|
|
1026
|
+
rl.rl_tex_coord2f(u, v1)
|
|
1027
|
+
rl.rl_vertex2f(x1, y1)
|
|
1028
|
+
rl.rl_tex_coord2f(u, v1)
|
|
1029
|
+
rl.rl_vertex2f(x2, y2)
|
|
1030
|
+
rl.rl_tex_coord2f(u, v0)
|
|
1031
|
+
rl.rl_vertex2f(x3, y3)
|
|
1032
|
+
|
|
1033
|
+
rl.rl_end()
|
|
1034
|
+
rl.rl_set_texture(0)
|
|
1035
|
+
|
|
1036
|
+
for creature in glow_targets:
|
|
1037
|
+
tx, ty = self.world_to_screen(float(creature.x), float(creature.y))
|
|
1038
|
+
target_tint = self._color_from_rgba((0.5, 0.6, 1.0, base_alpha))
|
|
1039
|
+
self._draw_atlas_sprite(
|
|
1040
|
+
texture,
|
|
1041
|
+
grid=grid,
|
|
1042
|
+
frame=frame,
|
|
1043
|
+
x=tx,
|
|
1044
|
+
y=ty,
|
|
1045
|
+
scale=sprite_scale,
|
|
1046
|
+
rotation_rad=0.0,
|
|
1047
|
+
tint=target_tint,
|
|
1048
|
+
)
|
|
1049
|
+
|
|
1050
|
+
rl.end_blend_mode()
|
|
1051
|
+
return
|
|
1052
|
+
|
|
686
1053
|
mapping = KNOWN_PROJ_FRAMES.get(type_id)
|
|
687
1054
|
if texture is None or mapping is None:
|
|
688
1055
|
rl.draw_circle(int(sx), int(sy), max(1.0, 3.0 * scale), rl.Color(240, 220, 160, int(255 * alpha + 0.5)))
|
|
@@ -699,36 +1066,6 @@ class WorldRenderer:
|
|
|
699
1066
|
elif type_id == ProjectileTypeId.BLADE_GUN:
|
|
700
1067
|
color = rl.Color(240, 120, 255, 255)
|
|
701
1068
|
|
|
702
|
-
if type_id in BEAM_TYPES and life >= 0.4:
|
|
703
|
-
ox = float(getattr(proj, "origin_x", 0.0))
|
|
704
|
-
oy = float(getattr(proj, "origin_y", 0.0))
|
|
705
|
-
dx = float(getattr(proj, "pos_x", 0.0)) - ox
|
|
706
|
-
dy = float(getattr(proj, "pos_y", 0.0)) - oy
|
|
707
|
-
dist = math.hypot(dx, dy)
|
|
708
|
-
if dist > 1e-6:
|
|
709
|
-
step = 14.0
|
|
710
|
-
seg_count = max(1, int(dist // step) + 1)
|
|
711
|
-
dir_x = dx / dist
|
|
712
|
-
dir_y = dy / dist
|
|
713
|
-
for idx in range(seg_count):
|
|
714
|
-
t = float(idx) / float(max(1, seg_count - 1))
|
|
715
|
-
px = ox + dir_x * dist * t
|
|
716
|
-
py = oy + dir_y * dist * t
|
|
717
|
-
seg_alpha = int(clamp(220.0 * (1.0 - t * 0.75) * alpha, 0.0, 255.0) + 0.5)
|
|
718
|
-
tint = rl.Color(color.r, color.g, color.b, seg_alpha)
|
|
719
|
-
psx, psy = self.world_to_screen(px, py)
|
|
720
|
-
self._draw_atlas_sprite(
|
|
721
|
-
texture,
|
|
722
|
-
grid=grid,
|
|
723
|
-
frame=frame,
|
|
724
|
-
x=psx,
|
|
725
|
-
y=psy,
|
|
726
|
-
scale=0.55 * scale,
|
|
727
|
-
rotation_rad=angle,
|
|
728
|
-
tint=tint,
|
|
729
|
-
)
|
|
730
|
-
return
|
|
731
|
-
|
|
732
1069
|
alpha_byte = int(clamp(clamp(life / 0.4, 0.0, 1.0) * 255.0 * alpha, 0.0, 255.0) + 0.5)
|
|
733
1070
|
tint = rl.Color(color.r, color.g, color.b, alpha_byte)
|
|
734
1071
|
self._draw_atlas_sprite(
|
crimson/sim/world_defs.py
CHANGED
|
@@ -43,14 +43,25 @@ KNOWN_PROJ_FRAMES: dict[int, tuple[int, int]] = {
|
|
|
43
43
|
ProjectileTypeId.ION_RIFLE: (4, 2),
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
PLASMA_PARTICLE_TYPES = frozenset(
|
|
47
|
+
{
|
|
48
|
+
ProjectileTypeId.PLASMA_RIFLE,
|
|
49
|
+
ProjectileTypeId.PLASMA_MINIGUN,
|
|
50
|
+
ProjectileTypeId.PLASMA_CANNON,
|
|
51
|
+
ProjectileTypeId.SPIDER_PLASMA,
|
|
52
|
+
ProjectileTypeId.SHRINKIFIER,
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
ION_TYPES = frozenset(
|
|
47
57
|
{
|
|
48
58
|
ProjectileTypeId.ION_RIFLE,
|
|
49
59
|
ProjectileTypeId.ION_MINIGUN,
|
|
50
60
|
ProjectileTypeId.ION_CANNON,
|
|
51
|
-
ProjectileTypeId.SHRINKIFIER,
|
|
52
|
-
ProjectileTypeId.FIRE_BULLETS,
|
|
53
|
-
ProjectileTypeId.BLADE_GUN,
|
|
54
|
-
ProjectileTypeId.SPLITTER_GUN,
|
|
55
61
|
}
|
|
56
62
|
)
|
|
63
|
+
|
|
64
|
+
FIRE_BULLETS_TYPES = frozenset({ProjectileTypeId.FIRE_BULLETS})
|
|
65
|
+
|
|
66
|
+
# "Beam" in the original renderer is really the Ion/Fire streak + chain UV family.
|
|
67
|
+
BEAM_TYPES = ION_TYPES | FIRE_BULLETS_TYPES
|
crimson/views/arsenal_debug.py
CHANGED
|
@@ -5,9 +5,7 @@ import random
|
|
|
5
5
|
|
|
6
6
|
import pyray as rl
|
|
7
7
|
|
|
8
|
-
from grim.audio import AudioState,
|
|
9
|
-
from grim.config import ensure_crimson_cfg
|
|
10
|
-
from grim.console import ConsoleLog, ConsoleState
|
|
8
|
+
from grim.audio import AudioState, shutdown_audio, update_audio
|
|
11
9
|
from grim.fonts.small import SmallFontData, draw_small_text, load_small_font
|
|
12
10
|
from grim.view import View, ViewContext
|
|
13
11
|
|
|
@@ -15,7 +13,6 @@ from ..creatures.spawn import SpawnId
|
|
|
15
13
|
from ..game_modes import GameMode
|
|
16
14
|
from ..game_world import GameWorld
|
|
17
15
|
from ..gameplay import PlayerInput, weapon_assign_player
|
|
18
|
-
from ..paths import default_runtime_dir
|
|
19
16
|
from ..projectiles import ProjectileTypeId
|
|
20
17
|
from ..ui.cursor import draw_aim_cursor
|
|
21
18
|
from ..weapon_sfx import resolve_weapon_sfx_ref
|
|
@@ -25,6 +22,7 @@ from ..weapons import (
|
|
|
25
22
|
Weapon,
|
|
26
23
|
projectile_type_id_from_weapon_id,
|
|
27
24
|
)
|
|
25
|
+
from .audio_bootstrap import init_view_audio
|
|
28
26
|
from .registry import register_view
|
|
29
27
|
|
|
30
28
|
|
|
@@ -284,32 +282,13 @@ class ArsenalDebugView:
|
|
|
284
282
|
except Exception:
|
|
285
283
|
self._small = None
|
|
286
284
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
self._world.config = None
|
|
295
|
-
|
|
296
|
-
if self._world.config is not None:
|
|
297
|
-
try:
|
|
298
|
-
self._console = ConsoleState(
|
|
299
|
-
base_dir=runtime_dir,
|
|
300
|
-
log=ConsoleLog(base_dir=runtime_dir),
|
|
301
|
-
assets_dir=self._assets_root,
|
|
302
|
-
)
|
|
303
|
-
self._audio = init_audio_state(self._world.config, self._assets_root, self._console)
|
|
304
|
-
self._audio_rng = random.Random(0xBEEF)
|
|
305
|
-
self._world.audio = self._audio
|
|
306
|
-
self._world.audio_rng = self._audio_rng
|
|
307
|
-
except Exception:
|
|
308
|
-
self._audio = None
|
|
309
|
-
self._audio_rng = None
|
|
310
|
-
self._console = None
|
|
311
|
-
self._world.audio = None
|
|
312
|
-
self._world.audio_rng = None
|
|
285
|
+
bootstrap = init_view_audio(self._assets_root)
|
|
286
|
+
self._world.config = bootstrap.config
|
|
287
|
+
self._console = bootstrap.console
|
|
288
|
+
self._audio = bootstrap.audio
|
|
289
|
+
self._audio_rng = bootstrap.audio_rng
|
|
290
|
+
self._world.audio = self._audio
|
|
291
|
+
self._world.audio_rng = self._audio_rng
|
|
313
292
|
|
|
314
293
|
self._world.open()
|
|
315
294
|
self._aim_texture = self._world._load_texture(
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import random
|
|
6
|
+
|
|
7
|
+
from grim.audio import AudioState, init_audio_state
|
|
8
|
+
from grim.config import CrimsonConfig, ensure_crimson_cfg
|
|
9
|
+
from grim.console import ConsoleLog, ConsoleState
|
|
10
|
+
|
|
11
|
+
from ..assets_fetch import download_missing_paqs
|
|
12
|
+
from ..paths import default_runtime_dir
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(slots=True)
|
|
16
|
+
class ViewAudioBootstrap:
|
|
17
|
+
config: CrimsonConfig | None
|
|
18
|
+
console: ConsoleState | None
|
|
19
|
+
audio: AudioState | None
|
|
20
|
+
audio_rng: random.Random | None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def init_view_audio(assets_dir: Path, *, seed: int = 0xBEEF) -> ViewAudioBootstrap:
|
|
24
|
+
runtime_dir = default_runtime_dir()
|
|
25
|
+
runtime_dir.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
try:
|
|
27
|
+
config = ensure_crimson_cfg(runtime_dir)
|
|
28
|
+
except Exception:
|
|
29
|
+
return ViewAudioBootstrap(None, None, None, None)
|
|
30
|
+
|
|
31
|
+
console = ConsoleState(
|
|
32
|
+
base_dir=runtime_dir,
|
|
33
|
+
log=ConsoleLog(base_dir=runtime_dir),
|
|
34
|
+
assets_dir=assets_dir,
|
|
35
|
+
)
|
|
36
|
+
try:
|
|
37
|
+
download_missing_paqs(assets_dir, console)
|
|
38
|
+
except Exception as exc:
|
|
39
|
+
console.log.log(f"assets: download failed: {exc}")
|
|
40
|
+
console.log.flush()
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
audio = init_audio_state(config, assets_dir, console)
|
|
44
|
+
except Exception:
|
|
45
|
+
return ViewAudioBootstrap(config, console, None, None)
|
|
46
|
+
|
|
47
|
+
return ViewAudioBootstrap(config, console, audio, random.Random(seed))
|
|
@@ -6,17 +6,15 @@ import random
|
|
|
6
6
|
|
|
7
7
|
import pyray as rl
|
|
8
8
|
|
|
9
|
-
from grim.audio import AudioState,
|
|
10
|
-
from grim.config import ensure_crimson_cfg
|
|
11
|
-
from grim.console import ConsoleLog, ConsoleState
|
|
9
|
+
from grim.audio import AudioState, shutdown_audio
|
|
12
10
|
from grim.fonts.small import SmallFontData, draw_small_text, load_small_font
|
|
13
11
|
from grim.view import View, ViewContext
|
|
14
12
|
|
|
15
13
|
from ..game_world import GameWorld
|
|
16
14
|
from ..gameplay import PlayerInput, player_update, weapon_assign_player
|
|
17
|
-
from ..paths import default_runtime_dir
|
|
18
15
|
from ..ui.cursor import draw_aim_cursor
|
|
19
16
|
from ..weapons import WEAPON_TABLE
|
|
17
|
+
from .audio_bootstrap import init_view_audio
|
|
20
18
|
from .registry import register_view
|
|
21
19
|
|
|
22
20
|
|
|
@@ -203,32 +201,13 @@ class ProjectileRenderDebugView:
|
|
|
203
201
|
except Exception:
|
|
204
202
|
self._small = None
|
|
205
203
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
self._world.config = None
|
|
214
|
-
|
|
215
|
-
if self._world.config is not None:
|
|
216
|
-
try:
|
|
217
|
-
self._console = ConsoleState(
|
|
218
|
-
base_dir=runtime_dir,
|
|
219
|
-
log=ConsoleLog(base_dir=runtime_dir),
|
|
220
|
-
assets_dir=self._assets_root,
|
|
221
|
-
)
|
|
222
|
-
self._audio = init_audio_state(self._world.config, self._assets_root, self._console)
|
|
223
|
-
self._audio_rng = random.Random(0xBEEF)
|
|
224
|
-
self._world.audio = self._audio
|
|
225
|
-
self._world.audio_rng = self._audio_rng
|
|
226
|
-
except Exception:
|
|
227
|
-
self._audio = None
|
|
228
|
-
self._audio_rng = None
|
|
229
|
-
self._console = None
|
|
230
|
-
self._world.audio = None
|
|
231
|
-
self._world.audio_rng = None
|
|
204
|
+
bootstrap = init_view_audio(self._assets_root)
|
|
205
|
+
self._world.config = bootstrap.config
|
|
206
|
+
self._console = bootstrap.console
|
|
207
|
+
self._audio = bootstrap.audio
|
|
208
|
+
self._audio_rng = bootstrap.audio_rng
|
|
209
|
+
self._world.audio = self._audio
|
|
210
|
+
self._world.audio_rng = self._audio_rng
|
|
232
211
|
|
|
233
212
|
self._world.open()
|
|
234
213
|
self._aim_texture = self._world._load_texture(
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
crimson/__init__.py,sha256=dij6OQ6Wctqur9P00ojTTZw6IaUNeZagPX4-2Qqr-Kw,367
|
|
2
2
|
crimson/assets_fetch.py,sha256=z4vFH9h-RIwc2o6uKGzEtUaOdhUDSX6Art-WU6tNjd0,1864
|
|
3
3
|
crimson/atlas.py,sha256=hEcCHhPvguXAI6eH_G9Q8rpiX7M5akZ8fgJjMogmYrA,2401
|
|
4
|
-
crimson/audio_router.py,sha256=
|
|
4
|
+
crimson/audio_router.py,sha256=XauJvjT_qAooPKBo4d8NVe9HRWHzZDD8PCzmsWrASic,4729
|
|
5
5
|
crimson/bonuses.py,sha256=owwYIRHRu1Kymtt4eEvpd62JwWAg8LOe20vDuPFB5SU,5094
|
|
6
6
|
crimson/camera.py,sha256=VxTNXTh0qHh5VR1OpkiXr-QcgEWPrWw0A3PFyQFqDkY,2278
|
|
7
|
-
crimson/cli.py,sha256=
|
|
7
|
+
crimson/cli.py,sha256=Pu4RM_bjGtUgIE_5zZs0uWFY9O8YkhJDseRcVMMPJ8k,14595
|
|
8
8
|
crimson/creatures/__init__.py,sha256=RJwwR_w98zC_kHF9CwkhzSlf0xjpiP0JU87W4awdAAM,233
|
|
9
9
|
crimson/creatures/ai.py,sha256=oCuUkDe6Kxya1dThUthEy8GIuObAUjDv8WPfe17HYV4,6353
|
|
10
10
|
crimson/creatures/anim.py,sha256=E7MAR9vFBrvmIYrOfPr36UohsfROZZL00jgRvZQf6nc,4971
|
|
@@ -46,7 +46,7 @@ crimson/persistence/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVO
|
|
|
46
46
|
crimson/persistence/highscores.py,sha256=i3eU_Vlu-Bn6kZHDWNMzUoJXvzkqOnPp_UFOeZXclYU,13026
|
|
47
47
|
crimson/persistence/save_status.py,sha256=s-FZONO6JVYRULewtlxoIwATTy3P7rLy-vsVBfSKPk4,7748
|
|
48
48
|
crimson/player_damage.py,sha256=cECZO65NJwARoYdtsbvZBM8vQNs_8AVO4xa0omX0pGg,2215
|
|
49
|
-
crimson/projectiles.py,sha256=
|
|
49
|
+
crimson/projectiles.py,sha256=rxgfcJcszSMIHv8Y6pxWqZ2sXogiOk8tAQSruLRM8Gc,40129
|
|
50
50
|
crimson/quests/__init__.py,sha256=pKCkH0o1XIGDN9h6yNNaqvWek2CybEZRpejYRooULj8,375
|
|
51
51
|
crimson/quests/helpers.py,sha256=TeZWhIy2x8Zs9S-vYRBOEZ7pkHNMr5YJ-wi4vYQ2q2M,3625
|
|
52
52
|
crimson/quests/registry.py,sha256=Cees1QBN0VYTB-XyQtvZ1b4SykB-i4tAnWF6WSV-1pg,1478
|
|
@@ -61,9 +61,9 @@ crimson/quests/timeline.py,sha256=leK898fPt3zq52v-O6dK4c-wJBcY-E6v-y57Fk3kJ3Q,40
|
|
|
61
61
|
crimson/quests/types.py,sha256=iSrz8VSiRZh1pUDSyq37ir7B28c0HF8DjdF6OJ3FoBo,1772
|
|
62
62
|
crimson/render/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
|
|
63
63
|
crimson/render/terrain_fx.py,sha256=MpBV6ukGwGqDluIO86BQhHRVWUvcFOi8MV-z_TfLsiw,2705
|
|
64
|
-
crimson/render/world_renderer.py,sha256=
|
|
64
|
+
crimson/render/world_renderer.py,sha256=dp6vNsYiH1td6xoxNCgoSC1He-By2sy6RP6rdAp9gSs,73026
|
|
65
65
|
crimson/sim/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
|
|
66
|
-
crimson/sim/world_defs.py,sha256=
|
|
66
|
+
crimson/sim/world_defs.py,sha256=HiMl--THnII3BTpt6mWAd20Xu-SRzCyHcs5pCR8aMbU,2195
|
|
67
67
|
crimson/sim/world_state.py,sha256=kCaNJ28J5OGd_Pg-0Log5IsLjRSeKHeteSrIB7eHPhY,15607
|
|
68
68
|
crimson/terrain_assets.py,sha256=X19LN3EZSkiz4uRvQfra3NtAXKAA8SMQ-nvOBlrspwE,670
|
|
69
69
|
crimson/tutorial/__init__.py,sha256=SDDZKRpkQBqyVMU9U03DN242t8lo8ThLuWHmbVrP69k,333
|
|
@@ -82,7 +82,8 @@ crimson/ui/perk_menu.py,sha256=FdbwG_B7UyZmPVdvZho0nJNvkAVc0VVHM2TSrY4eTfM,12740
|
|
|
82
82
|
crimson/views/__init__.py,sha256=1UWCzBSsgIeOtcKxqM4Vf12JEfPHKfb9KRZgl2TB84Q,1880
|
|
83
83
|
crimson/views/aim_debug.py,sha256=2ldQ1MJO8AeG0i9x4ZJ3YLyp1X-HY_nJ0E1mY8jFF7Q,10873
|
|
84
84
|
crimson/views/animations.py,sha256=iI6uTpYS-lbjnpqz_ihETm5q09V6RqjO7YHqaGjovPc,10660
|
|
85
|
-
crimson/views/arsenal_debug.py,sha256=
|
|
85
|
+
crimson/views/arsenal_debug.py,sha256=BWxjrrVF2dP5aa6GSaihzbNDIIS0OOXNyxgyvwqm4RU,13713
|
|
86
|
+
crimson/views/audio_bootstrap.py,sha256=a2qM91biyI-LlSiIhEceJ8ptYJy-xQ2-b-YjeyXWx7w,1408
|
|
86
87
|
crimson/views/bonuses.py,sha256=dbGx0IBjMMpqrnuVjBUkscxHq6uErCxAkEAb3uDhFqQ,7454
|
|
87
88
|
crimson/views/camera_debug.py,sha256=Vi6jANGQpXMK0bpL7hX_QQI_6Rl97ykElaoAuXd06YY,14025
|
|
88
89
|
crimson/views/camera_shake.py,sha256=miYxPJcVChv5P2AdbAWTEsrjZR0SAszqClLMbmrgg-0,8148
|
|
@@ -99,7 +100,7 @@ crimson/views/perks.py,sha256=Uac31DVvHZvjDmMrHpfvkg7LVpXw76xKL1BqwaLAsHU,15052
|
|
|
99
100
|
crimson/views/player.py,sha256=pj1_hFZ7VRUDfZYdUI1b-EpA7FdoBhhKM3Jk4x4s-GI,16540
|
|
100
101
|
crimson/views/player_sprite_debug.py,sha256=DDU6kl9sfhZ2nVXzBbsPSUKdIutHf5YNs2DrNQPRb2Q,11158
|
|
101
102
|
crimson/views/projectile_fx.py,sha256=XIp3Zq82Pz6g-B30qa6-nlKF_dseLNoQNSUPhj9Y4i8,23148
|
|
102
|
-
crimson/views/projectile_render_debug.py,sha256=
|
|
103
|
+
crimson/views/projectile_render_debug.py,sha256=CBSkUbflOSdrauyX1WucZ96CdYVBt_2QmGlx7i-KijM,14048
|
|
103
104
|
crimson/views/projectiles.py,sha256=k5zD_9614_-M4qYpnwKUS23fUUrjJS2Wzm7ZalOSKnU,8067
|
|
104
105
|
crimson/views/quest_title_overlay.py,sha256=EbMvXvYqagLLXmhaM7PC0fhxxhHGYGYI2ytgYiWqAd4,3338
|
|
105
106
|
crimson/views/registry.py,sha256=Kxc_aQMWOAjI6Uq_SRkb0AmYQxyVmM6rxT3UF74irzs,930
|
|
@@ -132,7 +133,7 @@ grim/sfx.py,sha256=cpn2Mmeio7BSDgbStSft-eZchO9Ot2MrK6iXJqxlLqU,7836
|
|
|
132
133
|
grim/sfx_map.py,sha256=FM5iBzKkG30Vtu78SRavVNgXMbGK7ZFcQ8i6lgMlzVw,4697
|
|
133
134
|
grim/terrain_render.py,sha256=EZ7ySYJyTZwXcrJx1mKbY3ewZtPi7Y270XnZgGJyZG8,31509
|
|
134
135
|
grim/view.py,sha256=oF4pHZehBqOxPjKMU28TDg3qATh_amMIRJp-vMQnpn4,334
|
|
135
|
-
crimsonland-0.1.0.
|
|
136
|
-
crimsonland-0.1.0.
|
|
137
|
-
crimsonland-0.1.0.
|
|
138
|
-
crimsonland-0.1.0.
|
|
136
|
+
crimsonland-0.1.0.dev3.dist-info/WHEEL,sha256=fAguSjoiATBe7TNBkJwOjyL1Tt4wwiaQGtNtjRPNMQA,80
|
|
137
|
+
crimsonland-0.1.0.dev3.dist-info/entry_points.txt,sha256=jzzcExxiE9xpt4Iw2nbB1lwTv2Zj4H14WJTIPMkAjoE,77
|
|
138
|
+
crimsonland-0.1.0.dev3.dist-info/METADATA,sha256=a41mEtPKIFi7ZlCS1FchQjc5Ii6GeGxJM_rJxJDeBns,243
|
|
139
|
+
crimsonland-0.1.0.dev3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|