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.
- crimson/__init__.py +24 -0
- crimson/assets_fetch.py +60 -0
- crimson/atlas.py +92 -0
- crimson/audio_router.py +153 -0
- crimson/bonuses.py +167 -0
- crimson/camera.py +75 -0
- crimson/cli.py +377 -0
- crimson/creatures/__init__.py +8 -0
- crimson/creatures/ai.py +186 -0
- crimson/creatures/anim.py +173 -0
- crimson/creatures/damage.py +103 -0
- crimson/creatures/runtime.py +1019 -0
- crimson/creatures/spawn.py +2871 -0
- crimson/debug.py +7 -0
- crimson/demo.py +1360 -0
- crimson/demo_trial.py +140 -0
- crimson/effects.py +1086 -0
- crimson/effects_atlas.py +73 -0
- crimson/frontend/__init__.py +1 -0
- crimson/frontend/assets.py +43 -0
- crimson/frontend/boot.py +424 -0
- crimson/frontend/menu.py +700 -0
- crimson/frontend/panels/__init__.py +1 -0
- crimson/frontend/panels/base.py +410 -0
- crimson/frontend/panels/controls.py +132 -0
- crimson/frontend/panels/mods.py +128 -0
- crimson/frontend/panels/options.py +409 -0
- crimson/frontend/panels/play_game.py +627 -0
- crimson/frontend/panels/stats.py +351 -0
- crimson/frontend/transitions.py +31 -0
- crimson/game.py +2533 -0
- crimson/game_modes.py +15 -0
- crimson/game_world.py +663 -0
- crimson/gameplay.py +2450 -0
- crimson/input_codes.py +176 -0
- crimson/modes/__init__.py +1 -0
- crimson/modes/base_gameplay_mode.py +219 -0
- crimson/modes/quest_mode.py +502 -0
- crimson/modes/rush_mode.py +300 -0
- crimson/modes/survival_mode.py +792 -0
- crimson/modes/tutorial_mode.py +648 -0
- crimson/modes/typo_mode.py +472 -0
- crimson/paths.py +23 -0
- crimson/perks.py +828 -0
- crimson/persistence/__init__.py +1 -0
- crimson/persistence/highscores.py +385 -0
- crimson/persistence/save_status.py +245 -0
- crimson/player_damage.py +77 -0
- crimson/projectiles.py +1039 -0
- crimson/quests/__init__.py +18 -0
- crimson/quests/helpers.py +147 -0
- crimson/quests/registry.py +49 -0
- crimson/quests/results.py +164 -0
- crimson/quests/runtime.py +91 -0
- crimson/quests/tier1.py +620 -0
- crimson/quests/tier2.py +652 -0
- crimson/quests/tier3.py +579 -0
- crimson/quests/tier4.py +721 -0
- crimson/quests/tier5.py +886 -0
- crimson/quests/timeline.py +115 -0
- crimson/quests/types.py +70 -0
- crimson/render/__init__.py +1 -0
- crimson/render/terrain_fx.py +88 -0
- crimson/render/world_renderer.py +1338 -0
- crimson/sim/__init__.py +1 -0
- crimson/sim/world_defs.py +56 -0
- crimson/sim/world_state.py +421 -0
- crimson/terrain_assets.py +19 -0
- crimson/tutorial/__init__.py +12 -0
- crimson/tutorial/timeline.py +291 -0
- crimson/typo/__init__.py +2 -0
- crimson/typo/names.py +233 -0
- crimson/typo/player.py +43 -0
- crimson/typo/spawns.py +73 -0
- crimson/typo/typing.py +52 -0
- crimson/ui/__init__.py +3 -0
- crimson/ui/cursor.py +95 -0
- crimson/ui/demo_trial_overlay.py +235 -0
- crimson/ui/game_over.py +660 -0
- crimson/ui/hud.py +601 -0
- crimson/ui/perk_menu.py +388 -0
- crimson/views/__init__.py +40 -0
- crimson/views/aim_debug.py +276 -0
- crimson/views/animations.py +274 -0
- crimson/views/arsenal_debug.py +414 -0
- crimson/views/bonuses.py +201 -0
- crimson/views/camera_debug.py +359 -0
- crimson/views/camera_shake.py +229 -0
- crimson/views/corpse_stamp_debug.py +324 -0
- crimson/views/decals_debug.py +739 -0
- crimson/views/empty.py +19 -0
- crimson/views/fonts.py +114 -0
- crimson/views/game_over.py +117 -0
- crimson/views/ground.py +259 -0
- crimson/views/lighting_debug.py +1166 -0
- crimson/views/particles.py +293 -0
- crimson/views/perk_menu_debug.py +430 -0
- crimson/views/perks.py +398 -0
- crimson/views/player.py +433 -0
- crimson/views/player_sprite_debug.py +314 -0
- crimson/views/projectile_fx.py +608 -0
- crimson/views/projectile_render_debug.py +407 -0
- crimson/views/projectiles.py +221 -0
- crimson/views/quest_title_overlay.py +108 -0
- crimson/views/registry.py +34 -0
- crimson/views/rush.py +16 -0
- crimson/views/small_font_debug.py +204 -0
- crimson/views/spawn_plan.py +363 -0
- crimson/views/sprites.py +214 -0
- crimson/views/survival.py +15 -0
- crimson/views/terrain.py +132 -0
- crimson/views/ui.py +123 -0
- crimson/views/wicons.py +166 -0
- crimson/weapon_sfx.py +63 -0
- crimson/weapons.py +860 -0
- crimsonland-0.1.0.dev1.dist-info/METADATA +9 -0
- crimsonland-0.1.0.dev1.dist-info/RECORD +138 -0
- crimsonland-0.1.0.dev1.dist-info/WHEEL +4 -0
- crimsonland-0.1.0.dev1.dist-info/entry_points.txt +4 -0
- grim/__init__.py +20 -0
- grim/app.py +92 -0
- grim/assets.py +231 -0
- grim/audio.py +106 -0
- grim/config.py +294 -0
- grim/console.py +737 -0
- grim/fonts/__init__.py +7 -0
- grim/fonts/grim_mono.py +111 -0
- grim/fonts/small.py +120 -0
- grim/input.py +44 -0
- grim/jaz.py +103 -0
- grim/math.py +17 -0
- grim/music.py +403 -0
- grim/paq.py +76 -0
- grim/rand.py +37 -0
- grim/sfx.py +276 -0
- grim/sfx_map.py +103 -0
- grim/terrain_render.py +840 -0
- grim/view.py +16 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import struct
|
|
4
|
+
|
|
5
|
+
from .spawn import CreatureFlags
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _f32(value: float) -> float:
|
|
9
|
+
"""Round-trip through float32 to match the game's stored float behavior."""
|
|
10
|
+
return struct.unpack("<f", struct.pack("<f", float(value)))[0]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _u32(value: int) -> int:
|
|
14
|
+
return value & 0xFFFFFFFF
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _i32(value: int) -> int:
|
|
18
|
+
value &= 0xFFFFFFFF
|
|
19
|
+
if value & 0x80000000:
|
|
20
|
+
return value - 0x100000000
|
|
21
|
+
return value
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
_CREATURE_CORPSE_FRAMES: dict[int, int] = {
|
|
25
|
+
0: 0, # zombie
|
|
26
|
+
1: 3, # lizard
|
|
27
|
+
2: 4, # alien
|
|
28
|
+
3: 1, # spider sp1
|
|
29
|
+
4: 2, # spider sp2
|
|
30
|
+
5: 7, # trooper
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def creature_corpse_frame_for_type(type_id: int) -> int:
|
|
35
|
+
"""Resolve the bodyset frame index used for corpse decals (`fx_queue_render`)."""
|
|
36
|
+
|
|
37
|
+
return _CREATURE_CORPSE_FRAMES.get(int(type_id), int(type_id) & 0xF)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def creature_anim_is_long_strip(flags: CreatureFlags) -> bool:
|
|
41
|
+
# From creature_update_all / creature_render_type:
|
|
42
|
+
# long strip when (flags & 4) == 0 OR (flags & 0x40) != 0
|
|
43
|
+
return (flags & CreatureFlags.ANIM_PING_PONG) == 0 or (flags & CreatureFlags.ANIM_LONG_STRIP) != 0
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def creature_anim_phase_step(
|
|
47
|
+
*,
|
|
48
|
+
anim_rate: float,
|
|
49
|
+
move_speed: float,
|
|
50
|
+
dt: float,
|
|
51
|
+
size: float,
|
|
52
|
+
local_scale: float = 1.0,
|
|
53
|
+
flags: CreatureFlags = CreatureFlags(0),
|
|
54
|
+
ai_mode: int = 0,
|
|
55
|
+
quantize_f32: bool = True,
|
|
56
|
+
) -> float:
|
|
57
|
+
"""Compute the per-frame animation phase increment (creature_update_all)."""
|
|
58
|
+
if size == 0.0:
|
|
59
|
+
return 0.0
|
|
60
|
+
|
|
61
|
+
if quantize_f32:
|
|
62
|
+
anim_rate = _f32(anim_rate)
|
|
63
|
+
move_speed = _f32(move_speed)
|
|
64
|
+
dt = _f32(dt)
|
|
65
|
+
size = _f32(size)
|
|
66
|
+
local_scale = _f32(local_scale)
|
|
67
|
+
|
|
68
|
+
speed_scale = (_f32(30.0) if quantize_f32 else 30.0) / size
|
|
69
|
+
strip_mul = _f32(25.0) if quantize_f32 else 25.0
|
|
70
|
+
if not creature_anim_is_long_strip(flags):
|
|
71
|
+
strip_mul = _f32(22.0) if quantize_f32 else 22.0
|
|
72
|
+
elif ai_mode == 7:
|
|
73
|
+
# Long-strip creatures stop advancing animation phase in ai_mode == 7.
|
|
74
|
+
return 0.0
|
|
75
|
+
|
|
76
|
+
step = anim_rate * move_speed * dt * speed_scale * local_scale * strip_mul
|
|
77
|
+
return _f32(step) if quantize_f32 else step
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def creature_anim_advance_phase(
|
|
81
|
+
phase: float,
|
|
82
|
+
*,
|
|
83
|
+
anim_rate: float,
|
|
84
|
+
move_speed: float,
|
|
85
|
+
dt: float,
|
|
86
|
+
size: float,
|
|
87
|
+
local_scale: float = 1.0,
|
|
88
|
+
flags: CreatureFlags = CreatureFlags(0),
|
|
89
|
+
ai_mode: int = 0,
|
|
90
|
+
quantize_f32: bool = True,
|
|
91
|
+
) -> tuple[float, float]:
|
|
92
|
+
"""Advance anim_phase and wrap it the same way as creature_update_all.
|
|
93
|
+
|
|
94
|
+
Returns (new_phase, applied_step).
|
|
95
|
+
"""
|
|
96
|
+
if quantize_f32:
|
|
97
|
+
phase = _f32(phase)
|
|
98
|
+
|
|
99
|
+
step = creature_anim_phase_step(
|
|
100
|
+
anim_rate=anim_rate,
|
|
101
|
+
move_speed=move_speed,
|
|
102
|
+
dt=dt,
|
|
103
|
+
size=size,
|
|
104
|
+
local_scale=local_scale,
|
|
105
|
+
flags=flags,
|
|
106
|
+
ai_mode=ai_mode,
|
|
107
|
+
quantize_f32=quantize_f32,
|
|
108
|
+
)
|
|
109
|
+
if step == 0.0:
|
|
110
|
+
return phase, 0.0
|
|
111
|
+
|
|
112
|
+
phase = phase + step
|
|
113
|
+
if quantize_f32:
|
|
114
|
+
phase = _f32(phase)
|
|
115
|
+
|
|
116
|
+
if creature_anim_is_long_strip(flags):
|
|
117
|
+
limit = _f32(31.0) if quantize_f32 else 31.0
|
|
118
|
+
while phase > limit:
|
|
119
|
+
phase = phase - limit
|
|
120
|
+
if quantize_f32:
|
|
121
|
+
phase = _f32(phase)
|
|
122
|
+
else:
|
|
123
|
+
limit = _f32(15.0) if quantize_f32 else 15.0
|
|
124
|
+
if phase > limit:
|
|
125
|
+
while phase > limit:
|
|
126
|
+
phase = phase - limit
|
|
127
|
+
if quantize_f32:
|
|
128
|
+
phase = _f32(phase)
|
|
129
|
+
|
|
130
|
+
return phase, step
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def creature_anim_select_frame(
|
|
134
|
+
phase: float,
|
|
135
|
+
*,
|
|
136
|
+
base_frame: int,
|
|
137
|
+
mirror_long: bool,
|
|
138
|
+
flags: CreatureFlags = CreatureFlags(0),
|
|
139
|
+
) -> tuple[int, bool, str]:
|
|
140
|
+
"""Select an 8x8 atlas frame index (creature_render_type).
|
|
141
|
+
|
|
142
|
+
Returns (frame_index, mirror_applied, mode).
|
|
143
|
+
|
|
144
|
+
Note: mirror_applied refers to the long-strip ping-pong index mirroring
|
|
145
|
+
(frame = 0x1f - frame) when the per-type mirror flag is set, not a texture flip.
|
|
146
|
+
"""
|
|
147
|
+
if creature_anim_is_long_strip(flags):
|
|
148
|
+
if phase < 0.0:
|
|
149
|
+
# Negative anim_phase is used as a special render state in the game; keep the
|
|
150
|
+
# same fallback frame selection.
|
|
151
|
+
frame = base_frame + 0x0F
|
|
152
|
+
mirrored = False
|
|
153
|
+
else:
|
|
154
|
+
# Matches __ftol(phase + 0.5f) used by the original binary.
|
|
155
|
+
frame = int(phase + 0.5)
|
|
156
|
+
mirrored = False
|
|
157
|
+
if mirror_long and frame > 0x0F:
|
|
158
|
+
frame = 0x1F - frame
|
|
159
|
+
mirrored = True
|
|
160
|
+
if flags & CreatureFlags.RANGED_ATTACK_SHOCK:
|
|
161
|
+
frame += 0x20
|
|
162
|
+
return frame, mirrored, "long"
|
|
163
|
+
|
|
164
|
+
# Ping-pong strip:
|
|
165
|
+
# idx = (__ftol(phase + 0.5f) & 0x8000000f); then normalize negatives; then mirror >7.
|
|
166
|
+
raw = int(phase + 0.5)
|
|
167
|
+
idx = _i32(_u32(raw) & 0x8000000F)
|
|
168
|
+
if idx < 0:
|
|
169
|
+
idx = _i32(_u32(((idx - 1) | 0xFFFFFFF0) + 1))
|
|
170
|
+
if idx > 7:
|
|
171
|
+
idx = 0x0F - idx
|
|
172
|
+
frame = base_frame + 0x10 + idx
|
|
173
|
+
return frame, False, "ping-pong"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from typing import Callable
|
|
5
|
+
|
|
6
|
+
from ..gameplay import PlayerState, perk_active
|
|
7
|
+
from ..perks import PerkId
|
|
8
|
+
from .runtime import CREATURE_HITBOX_ALIVE, CreatureState
|
|
9
|
+
from .spawn import CreatureFlags
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _owner_id_to_player_index(owner_id: int) -> int | None:
|
|
13
|
+
if owner_id == -100:
|
|
14
|
+
return 0
|
|
15
|
+
if owner_id < 0:
|
|
16
|
+
return -1 - owner_id
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def creature_apply_damage(
|
|
21
|
+
creature: CreatureState,
|
|
22
|
+
*,
|
|
23
|
+
damage_amount: float,
|
|
24
|
+
damage_type: int,
|
|
25
|
+
impulse_x: float,
|
|
26
|
+
impulse_y: float,
|
|
27
|
+
owner_id: int,
|
|
28
|
+
dt: float,
|
|
29
|
+
players: list[PlayerState],
|
|
30
|
+
rand: Callable[[], int],
|
|
31
|
+
) -> bool:
|
|
32
|
+
"""Apply damage to a creature, returning True if the hit killed it.
|
|
33
|
+
|
|
34
|
+
This is a partial port of `creature_apply_damage` (FUN_004207c0).
|
|
35
|
+
|
|
36
|
+
Notes:
|
|
37
|
+
- Death side-effects are handled by the caller (see Phase 2 in `plan.md`).
|
|
38
|
+
- `damage_type` is a native integer category; call sites must supply it.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
creature.last_hit_owner_id = int(owner_id)
|
|
42
|
+
creature.hit_flash_timer = 0.2
|
|
43
|
+
|
|
44
|
+
player_index = _owner_id_to_player_index(owner_id)
|
|
45
|
+
attacker = players[player_index] if player_index is not None and 0 <= player_index < len(players) else None
|
|
46
|
+
|
|
47
|
+
damage = float(damage_amount)
|
|
48
|
+
|
|
49
|
+
if int(damage_type) == 1 and attacker is not None:
|
|
50
|
+
if perk_active(attacker, PerkId.URANIUM_FILLED_BULLETS):
|
|
51
|
+
damage *= 2.0
|
|
52
|
+
|
|
53
|
+
if perk_active(attacker, PerkId.LIVING_FORTRESS):
|
|
54
|
+
for player in players:
|
|
55
|
+
if float(player.health) <= 0.0:
|
|
56
|
+
continue
|
|
57
|
+
timer = float(player.living_fortress_timer)
|
|
58
|
+
if timer > 0.0:
|
|
59
|
+
damage *= timer * 0.05 + 1.0
|
|
60
|
+
|
|
61
|
+
if perk_active(attacker, PerkId.BARREL_GREASER):
|
|
62
|
+
damage *= 1.4
|
|
63
|
+
if perk_active(attacker, PerkId.DOCTOR):
|
|
64
|
+
damage *= 1.2
|
|
65
|
+
|
|
66
|
+
if (creature.flags & CreatureFlags.ANIM_PING_PONG) == 0:
|
|
67
|
+
jitter = float((int(rand()) & 0x7F) - 0x40) * 0.002
|
|
68
|
+
size = max(1e-6, float(creature.size))
|
|
69
|
+
turn = jitter / (size * 0.025)
|
|
70
|
+
turn = max(-math.pi / 2.0, min(math.pi / 2.0, turn))
|
|
71
|
+
creature.heading += turn
|
|
72
|
+
|
|
73
|
+
if int(damage_type) == 7 and attacker is not None:
|
|
74
|
+
if perk_active(attacker, PerkId.ION_GUN_MASTER):
|
|
75
|
+
damage *= 1.2
|
|
76
|
+
|
|
77
|
+
if creature.hp <= 0.0:
|
|
78
|
+
if dt > 0.0:
|
|
79
|
+
creature.hitbox_size -= float(dt) * 15.0
|
|
80
|
+
return True
|
|
81
|
+
|
|
82
|
+
if int(damage_type) == 4 and attacker is not None:
|
|
83
|
+
if perk_active(attacker, PerkId.PYROMANIAC):
|
|
84
|
+
damage *= 1.5
|
|
85
|
+
rand()
|
|
86
|
+
|
|
87
|
+
creature.hp -= damage
|
|
88
|
+
creature.vel_x -= float(impulse_x)
|
|
89
|
+
creature.vel_y -= float(impulse_y)
|
|
90
|
+
|
|
91
|
+
if creature.hp <= 0.0:
|
|
92
|
+
if dt > 0.0:
|
|
93
|
+
creature.hitbox_size = float(creature.hitbox_size) - float(dt)
|
|
94
|
+
else:
|
|
95
|
+
creature.hitbox_size = float(creature.hitbox_size) - 0.001
|
|
96
|
+
creature.vel_x -= float(impulse_x) * 2.0
|
|
97
|
+
creature.vel_y -= float(impulse_y) * 2.0
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
if creature.hitbox_size != CREATURE_HITBOX_ALIVE and dt > 0.0:
|
|
101
|
+
creature.hitbox_size = CREATURE_HITBOX_ALIVE
|
|
102
|
+
|
|
103
|
+
return False
|