crimsonland 0.1.0.dev4__py3-none-any.whl → 0.1.0.dev6__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/demo.py +2 -2
- crimson/effects.py +1 -1
- crimson/game_world.py +3 -14
- crimson/gameplay.py +56 -41
- crimson/projectiles.py +317 -61
- crimson/render/world_renderer.py +572 -166
- crimson/sim/world_state.py +1 -0
- crimson/views/arsenal_debug.py +11 -0
- crimson/views/player.py +1 -0
- crimson/views/projectile_fx.py +1 -0
- crimson/views/projectile_render_debug.py +9 -2
- {crimsonland-0.1.0.dev4.dist-info → crimsonland-0.1.0.dev6.dist-info}/METADATA +1 -1
- {crimsonland-0.1.0.dev4.dist-info → crimsonland-0.1.0.dev6.dist-info}/RECORD +15 -15
- {crimsonland-0.1.0.dev4.dist-info → crimsonland-0.1.0.dev6.dist-info}/WHEEL +0 -0
- {crimsonland-0.1.0.dev4.dist-info → crimsonland-0.1.0.dev6.dist-info}/entry_points.txt +0 -0
crimson/projectiles.py
CHANGED
|
@@ -112,7 +112,7 @@ class SecondaryProjectile:
|
|
|
112
112
|
vel_y: float = 0.0
|
|
113
113
|
type_id: int = 0
|
|
114
114
|
owner_id: int = -100
|
|
115
|
-
|
|
115
|
+
trail_timer: float = 0.0
|
|
116
116
|
target_id: int = -1
|
|
117
117
|
|
|
118
118
|
|
|
@@ -220,6 +220,7 @@ class ProjectilePool:
|
|
|
220
220
|
damage_scale_by_type: dict[int, float] | None = None,
|
|
221
221
|
damage_scale_default: float = 1.0,
|
|
222
222
|
ion_aoe_scale: float = 1.0,
|
|
223
|
+
detail_preset: int = 5,
|
|
223
224
|
rng: Callable[[], int] | None = None,
|
|
224
225
|
runtime_state: object | None = None,
|
|
225
226
|
players: list[PlayerDamageable] | None = None,
|
|
@@ -280,6 +281,12 @@ class ProjectilePool:
|
|
|
280
281
|
if rng is None:
|
|
281
282
|
rng = _rng_zero
|
|
282
283
|
|
|
284
|
+
effects = None
|
|
285
|
+
sfx_queue = None
|
|
286
|
+
if runtime_state is not None:
|
|
287
|
+
effects = getattr(runtime_state, "effects", None)
|
|
288
|
+
sfx_queue = getattr(runtime_state, "sfx_queue", None)
|
|
289
|
+
|
|
283
290
|
hits: list[tuple[int, float, float, float, float, float, float]] = []
|
|
284
291
|
margin = 64.0
|
|
285
292
|
|
|
@@ -292,6 +299,170 @@ class ProjectilePool:
|
|
|
292
299
|
def _damage_type_for() -> int:
|
|
293
300
|
return 1
|
|
294
301
|
|
|
302
|
+
def _spawn_ion_hit_effects(type_id: int, pos_x: float, pos_y: float) -> None:
|
|
303
|
+
if effects is None or not hasattr(effects, "spawn"):
|
|
304
|
+
return
|
|
305
|
+
|
|
306
|
+
ring_scale = 0.0
|
|
307
|
+
ring_strength = 0.0
|
|
308
|
+
burst_scale = 0.0
|
|
309
|
+
if type_id == int(ProjectileTypeId.ION_MINIGUN):
|
|
310
|
+
ring_scale = 1.5
|
|
311
|
+
ring_strength = 0.1
|
|
312
|
+
burst_scale = 0.8
|
|
313
|
+
elif type_id == int(ProjectileTypeId.ION_RIFLE):
|
|
314
|
+
ring_scale = 1.2
|
|
315
|
+
ring_strength = 0.4
|
|
316
|
+
burst_scale = 1.2
|
|
317
|
+
elif type_id == int(ProjectileTypeId.ION_CANNON):
|
|
318
|
+
ring_scale = 1.0
|
|
319
|
+
ring_strength = 1.0
|
|
320
|
+
burst_scale = 2.2
|
|
321
|
+
if isinstance(sfx_queue, list):
|
|
322
|
+
sfx_queue.append("sfx_shockwave")
|
|
323
|
+
else:
|
|
324
|
+
return
|
|
325
|
+
|
|
326
|
+
detail = int(detail_preset)
|
|
327
|
+
|
|
328
|
+
# Port of `FUN_0042f270(pos, ring_scale, ring_strength)`: ring burst (effect_id=1).
|
|
329
|
+
effects.spawn(
|
|
330
|
+
effect_id=1,
|
|
331
|
+
pos_x=float(pos_x),
|
|
332
|
+
pos_y=float(pos_y),
|
|
333
|
+
vel_x=0.0,
|
|
334
|
+
vel_y=0.0,
|
|
335
|
+
rotation=0.0,
|
|
336
|
+
scale=1.0,
|
|
337
|
+
half_width=4.0,
|
|
338
|
+
half_height=4.0,
|
|
339
|
+
age=0.0,
|
|
340
|
+
lifetime=float(ring_strength) * 0.8,
|
|
341
|
+
flags=0x19,
|
|
342
|
+
color_r=0.6,
|
|
343
|
+
color_g=0.6,
|
|
344
|
+
color_b=0.9,
|
|
345
|
+
color_a=1.0,
|
|
346
|
+
rotation_step=0.0,
|
|
347
|
+
scale_step=float(ring_scale) * 45.0,
|
|
348
|
+
detail_preset=detail,
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# Port of `FUN_0042f540(pos, burst_scale)`: burst cloud (effect_id=0).
|
|
352
|
+
burst = float(burst_scale) * 0.8
|
|
353
|
+
lifetime = min(burst * 0.7, 1.1)
|
|
354
|
+
half = burst * 32.0
|
|
355
|
+
count = int(half)
|
|
356
|
+
if detail < 3:
|
|
357
|
+
count //= 2
|
|
358
|
+
|
|
359
|
+
for _ in range(max(0, count)):
|
|
360
|
+
rotation = float(int(rng()) & 0x7F) * 0.049087387
|
|
361
|
+
vel_x = float((int(rng()) & 0x7F) - 0x40) * burst * 1.4
|
|
362
|
+
vel_y = float((int(rng()) & 0x7F) - 0x40) * burst * 1.4
|
|
363
|
+
scale_step = (float(int(rng()) % 100) * 0.01 + 0.1) * burst
|
|
364
|
+
effects.spawn(
|
|
365
|
+
effect_id=0,
|
|
366
|
+
pos_x=float(pos_x),
|
|
367
|
+
pos_y=float(pos_y),
|
|
368
|
+
vel_x=vel_x,
|
|
369
|
+
vel_y=vel_y,
|
|
370
|
+
rotation=rotation,
|
|
371
|
+
scale=1.0,
|
|
372
|
+
half_width=half,
|
|
373
|
+
half_height=half,
|
|
374
|
+
age=0.0,
|
|
375
|
+
lifetime=float(lifetime),
|
|
376
|
+
flags=0x1D,
|
|
377
|
+
color_r=0.4,
|
|
378
|
+
color_g=0.5,
|
|
379
|
+
color_b=1.0,
|
|
380
|
+
color_a=0.5,
|
|
381
|
+
rotation_step=0.0,
|
|
382
|
+
scale_step=scale_step,
|
|
383
|
+
detail_preset=detail,
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
def _spawn_plasma_cannon_hit_effects(pos_x: float, pos_y: float) -> None:
|
|
387
|
+
"""Port of `projectile_update` Plasma Cannon hit extras.
|
|
388
|
+
|
|
389
|
+
Native does:
|
|
390
|
+
- `sfx_play_panned(sfx_explosion_medium)`
|
|
391
|
+
- `sfx_play_panned(sfx_shockwave)`
|
|
392
|
+
- `FUN_0042f330(pos, 1.5, 1.0)`
|
|
393
|
+
- `FUN_0042f330(pos, 1.0, 1.0)`
|
|
394
|
+
"""
|
|
395
|
+
|
|
396
|
+
if effects is None or not hasattr(effects, "spawn"):
|
|
397
|
+
return
|
|
398
|
+
|
|
399
|
+
if isinstance(sfx_queue, list):
|
|
400
|
+
sfx_queue.append("sfx_explosion_medium")
|
|
401
|
+
sfx_queue.append("sfx_shockwave")
|
|
402
|
+
|
|
403
|
+
detail = int(detail_preset)
|
|
404
|
+
|
|
405
|
+
def _spawn_ring(*, scale: float) -> None:
|
|
406
|
+
effects.spawn(
|
|
407
|
+
effect_id=1,
|
|
408
|
+
pos_x=float(pos_x),
|
|
409
|
+
pos_y=float(pos_y),
|
|
410
|
+
vel_x=0.0,
|
|
411
|
+
vel_y=0.0,
|
|
412
|
+
rotation=0.0,
|
|
413
|
+
scale=1.0,
|
|
414
|
+
half_width=4.0,
|
|
415
|
+
half_height=4.0,
|
|
416
|
+
age=0.1,
|
|
417
|
+
lifetime=1.0,
|
|
418
|
+
flags=0x19,
|
|
419
|
+
color_r=0.9,
|
|
420
|
+
color_g=0.6,
|
|
421
|
+
color_b=0.3,
|
|
422
|
+
color_a=1.0,
|
|
423
|
+
rotation_step=0.0,
|
|
424
|
+
scale_step=float(scale) * 45.0,
|
|
425
|
+
detail_preset=detail,
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
_spawn_ring(scale=1.5)
|
|
429
|
+
_spawn_ring(scale=1.0)
|
|
430
|
+
|
|
431
|
+
def _spawn_splitter_hit_effects(pos_x: float, pos_y: float) -> None:
|
|
432
|
+
"""Port of `FUN_0042f3f0(pos, 26.0, 3)` from the Splitter Gun hit branch."""
|
|
433
|
+
|
|
434
|
+
if effects is None or not hasattr(effects, "spawn"):
|
|
435
|
+
return
|
|
436
|
+
|
|
437
|
+
detail = int(detail_preset)
|
|
438
|
+
for _ in range(3):
|
|
439
|
+
angle = float(int(rng()) & 0x1FF) * (math.tau / 512.0)
|
|
440
|
+
radius = float(int(rng()) % 26)
|
|
441
|
+
jitter_age = -float(int(rng()) & 0xFF) * 0.0012
|
|
442
|
+
lifetime = 0.1 - jitter_age
|
|
443
|
+
|
|
444
|
+
effects.spawn(
|
|
445
|
+
effect_id=0,
|
|
446
|
+
pos_x=float(pos_x) + math.cos(angle) * radius,
|
|
447
|
+
pos_y=float(pos_y) + math.sin(angle) * radius,
|
|
448
|
+
vel_x=0.0,
|
|
449
|
+
vel_y=0.0,
|
|
450
|
+
rotation=0.0,
|
|
451
|
+
scale=1.0,
|
|
452
|
+
half_width=4.0,
|
|
453
|
+
half_height=4.0,
|
|
454
|
+
age=jitter_age,
|
|
455
|
+
lifetime=lifetime,
|
|
456
|
+
flags=0x19,
|
|
457
|
+
color_r=1.0,
|
|
458
|
+
color_g=0.9,
|
|
459
|
+
color_b=0.1,
|
|
460
|
+
color_a=1.0,
|
|
461
|
+
rotation_step=0.0,
|
|
462
|
+
scale_step=55.0,
|
|
463
|
+
detail_preset=detail,
|
|
464
|
+
)
|
|
465
|
+
|
|
295
466
|
def _apply_damage_to_creature(
|
|
296
467
|
creature_index: int,
|
|
297
468
|
damage: float,
|
|
@@ -476,54 +647,6 @@ class ProjectilePool:
|
|
|
476
647
|
acc_x = 0.0
|
|
477
648
|
acc_y = 0.0
|
|
478
649
|
|
|
479
|
-
if proj.hits_players:
|
|
480
|
-
hit_player_idx = None
|
|
481
|
-
if players is not None:
|
|
482
|
-
for idx, player in enumerate(players):
|
|
483
|
-
if float(player.health) <= 0.0:
|
|
484
|
-
continue
|
|
485
|
-
player_radius = _hit_radius_for(player)
|
|
486
|
-
hit_r = proj.hit_radius + player_radius
|
|
487
|
-
if _distance_sq(proj.pos_x, proj.pos_y, player.pos_x, player.pos_y) <= hit_r * hit_r:
|
|
488
|
-
hit_player_idx = idx
|
|
489
|
-
break
|
|
490
|
-
|
|
491
|
-
if hit_player_idx is None:
|
|
492
|
-
step += 3
|
|
493
|
-
continue
|
|
494
|
-
|
|
495
|
-
type_id = proj.type_id
|
|
496
|
-
hit_x = float(proj.pos_x)
|
|
497
|
-
hit_y = float(proj.pos_y)
|
|
498
|
-
player = players[int(hit_player_idx)] if players is not None else None
|
|
499
|
-
target_x = float(getattr(player, "pos_x", hit_x) if player is not None else hit_x)
|
|
500
|
-
target_y = float(getattr(player, "pos_y", hit_y) if player is not None else hit_y)
|
|
501
|
-
hits.append((type_id, proj.origin_x, proj.origin_y, hit_x, hit_y, target_x, target_y))
|
|
502
|
-
|
|
503
|
-
if proj.life_timer != 0.25 and type_id not in (
|
|
504
|
-
ProjectileTypeId.FIRE_BULLETS,
|
|
505
|
-
ProjectileTypeId.GAUSS_GUN,
|
|
506
|
-
ProjectileTypeId.BLADE_GUN,
|
|
507
|
-
):
|
|
508
|
-
proj.life_timer = 0.25
|
|
509
|
-
jitter = rng() & 3
|
|
510
|
-
proj.pos_x += dir_x * float(jitter)
|
|
511
|
-
proj.pos_y += dir_y * float(jitter)
|
|
512
|
-
|
|
513
|
-
dist = math.hypot(proj.origin_x - proj.pos_x, proj.origin_y - proj.pos_y)
|
|
514
|
-
if dist < 50.0:
|
|
515
|
-
dist = 50.0
|
|
516
|
-
|
|
517
|
-
damage_scale = _damage_scale(type_id)
|
|
518
|
-
damage_amount = ((100.0 / dist) * damage_scale * 30.0 + 10.0) * 0.95
|
|
519
|
-
if damage_amount > 0.0:
|
|
520
|
-
if apply_player_damage is not None:
|
|
521
|
-
apply_player_damage(int(hit_player_idx), float(damage_amount))
|
|
522
|
-
elif players is not None:
|
|
523
|
-
players[int(hit_player_idx)].health -= float(damage_amount)
|
|
524
|
-
|
|
525
|
-
break
|
|
526
|
-
|
|
527
650
|
hit_idx = None
|
|
528
651
|
for idx, creature in enumerate(creatures):
|
|
529
652
|
if creature.hp <= 0.0:
|
|
@@ -537,6 +660,61 @@ class ProjectilePool:
|
|
|
537
660
|
break
|
|
538
661
|
|
|
539
662
|
if hit_idx is None:
|
|
663
|
+
if proj.hits_players:
|
|
664
|
+
hit_player_idx = None
|
|
665
|
+
owner_id = int(proj.owner_id)
|
|
666
|
+
owner_player_index = -1 - owner_id if owner_id < 0 and owner_id != -100 else None
|
|
667
|
+
if players is not None:
|
|
668
|
+
for idx, player in enumerate(players):
|
|
669
|
+
if owner_player_index is not None and idx == owner_player_index:
|
|
670
|
+
continue
|
|
671
|
+
if float(player.health) <= 0.0:
|
|
672
|
+
continue
|
|
673
|
+
player_radius = _hit_radius_for(player)
|
|
674
|
+
hit_r = proj.hit_radius + player_radius
|
|
675
|
+
if (
|
|
676
|
+
_distance_sq(proj.pos_x, proj.pos_y, player.pos_x, player.pos_y)
|
|
677
|
+
<= hit_r * hit_r
|
|
678
|
+
):
|
|
679
|
+
hit_player_idx = idx
|
|
680
|
+
break
|
|
681
|
+
|
|
682
|
+
if hit_player_idx is None:
|
|
683
|
+
step += 3
|
|
684
|
+
continue
|
|
685
|
+
|
|
686
|
+
type_id = proj.type_id
|
|
687
|
+
hit_x = float(proj.pos_x)
|
|
688
|
+
hit_y = float(proj.pos_y)
|
|
689
|
+
player = players[int(hit_player_idx)] if players is not None else None
|
|
690
|
+
target_x = float(getattr(player, "pos_x", hit_x) if player is not None else hit_x)
|
|
691
|
+
target_y = float(getattr(player, "pos_y", hit_y) if player is not None else hit_y)
|
|
692
|
+
hits.append((type_id, proj.origin_x, proj.origin_y, hit_x, hit_y, target_x, target_y))
|
|
693
|
+
|
|
694
|
+
if proj.life_timer != 0.25 and type_id not in (
|
|
695
|
+
ProjectileTypeId.FIRE_BULLETS,
|
|
696
|
+
ProjectileTypeId.GAUSS_GUN,
|
|
697
|
+
ProjectileTypeId.BLADE_GUN,
|
|
698
|
+
):
|
|
699
|
+
proj.life_timer = 0.25
|
|
700
|
+
jitter = rng() & 3
|
|
701
|
+
proj.pos_x += dir_x * float(jitter)
|
|
702
|
+
proj.pos_y += dir_y * float(jitter)
|
|
703
|
+
|
|
704
|
+
dist = math.hypot(proj.origin_x - proj.pos_x, proj.origin_y - proj.pos_y)
|
|
705
|
+
if dist < 50.0:
|
|
706
|
+
dist = 50.0
|
|
707
|
+
|
|
708
|
+
damage_scale = _damage_scale(type_id)
|
|
709
|
+
damage_amount = ((100.0 / dist) * damage_scale * 30.0 + 10.0) * 0.95
|
|
710
|
+
if damage_amount > 0.0:
|
|
711
|
+
if apply_player_damage is not None:
|
|
712
|
+
apply_player_damage(int(hit_player_idx), float(damage_amount))
|
|
713
|
+
elif players is not None:
|
|
714
|
+
players[int(hit_player_idx)].health -= float(damage_amount)
|
|
715
|
+
|
|
716
|
+
break
|
|
717
|
+
|
|
540
718
|
step += 3
|
|
541
719
|
continue
|
|
542
720
|
|
|
@@ -548,6 +726,7 @@ class ProjectilePool:
|
|
|
548
726
|
creature.flags |= CreatureFlags.SELF_DAMAGE_TICK
|
|
549
727
|
|
|
550
728
|
if type_id == ProjectileTypeId.SPLITTER_GUN:
|
|
729
|
+
_spawn_splitter_hit_effects(proj.pos_x, proj.pos_y)
|
|
551
730
|
self.spawn(
|
|
552
731
|
pos_x=proj.pos_x,
|
|
553
732
|
pos_y=proj.pos_y,
|
|
@@ -598,6 +777,8 @@ class ProjectilePool:
|
|
|
598
777
|
if type_id == ProjectileTypeId.ION_RIFLE:
|
|
599
778
|
if runtime_state is not None and getattr(runtime_state, "shock_chain_projectile_id", -1) == proj_index:
|
|
600
779
|
proj.reserved = float(int(hit_idx) + 1)
|
|
780
|
+
if type_id in (ProjectileTypeId.ION_MINIGUN, ProjectileTypeId.ION_RIFLE, ProjectileTypeId.ION_CANNON):
|
|
781
|
+
_spawn_ion_hit_effects(int(type_id), target_x, target_y)
|
|
601
782
|
elif type_id == ProjectileTypeId.PLASMA_CANNON:
|
|
602
783
|
size = float(getattr(creature, "size", 50.0) or 50.0)
|
|
603
784
|
ring_radius = size * 0.5 + 1.0
|
|
@@ -617,6 +798,7 @@ class ProjectilePool:
|
|
|
617
798
|
owner_id=-100,
|
|
618
799
|
base_damage=plasma_meta,
|
|
619
800
|
)
|
|
801
|
+
_spawn_plasma_cannon_hit_effects(proj.pos_x, proj.pos_y)
|
|
620
802
|
elif type_id == ProjectileTypeId.SHRINKIFIER:
|
|
621
803
|
if hasattr(creature, "size"):
|
|
622
804
|
new_size = float(getattr(creature, "size", 50.0) or 50.0) * 0.65
|
|
@@ -821,12 +1003,13 @@ class SecondaryProjectilePool:
|
|
|
821
1003
|
entry.pos_y = float(pos_y)
|
|
822
1004
|
entry.owner_id = int(owner_id)
|
|
823
1005
|
entry.target_id = -1
|
|
1006
|
+
entry.trail_timer = 0.0
|
|
824
1007
|
|
|
825
1008
|
if entry.type_id == 3:
|
|
1009
|
+
# Detonation state: `vel_x` becomes the expansion timer and `vel_y` the scale.
|
|
826
1010
|
entry.vel_x = 0.0
|
|
827
|
-
entry.vel_y =
|
|
1011
|
+
entry.vel_y = float(time_to_live)
|
|
828
1012
|
entry.speed = float(time_to_live)
|
|
829
|
-
entry.lifetime = 0.0
|
|
830
1013
|
return index
|
|
831
1014
|
|
|
832
1015
|
# Effects.md: vel = cos/sin(angle - PI/2) * 90 (190 for type 2).
|
|
@@ -838,7 +1021,6 @@ class SecondaryProjectilePool:
|
|
|
838
1021
|
entry.vel_x = vx
|
|
839
1022
|
entry.vel_y = vy
|
|
840
1023
|
entry.speed = float(time_to_live)
|
|
841
|
-
entry.lifetime = 0.0
|
|
842
1024
|
return index
|
|
843
1025
|
|
|
844
1026
|
def iter_active(self) -> list[SecondaryProjectile]:
|
|
@@ -873,6 +1055,7 @@ class SecondaryProjectilePool:
|
|
|
873
1055
|
rand = _rng_zero
|
|
874
1056
|
freeze_active = False
|
|
875
1057
|
effects = None
|
|
1058
|
+
sprite_effects = None
|
|
876
1059
|
sfx_queue = None
|
|
877
1060
|
if runtime_state is not None:
|
|
878
1061
|
rng = getattr(runtime_state, "rng", None)
|
|
@@ -884,6 +1067,7 @@ class SecondaryProjectilePool:
|
|
|
884
1067
|
freeze_active = True
|
|
885
1068
|
|
|
886
1069
|
effects = getattr(runtime_state, "effects", None)
|
|
1070
|
+
sprite_effects = getattr(runtime_state, "sprite_effects", None)
|
|
887
1071
|
sfx_queue = getattr(runtime_state, "sfx_queue", None)
|
|
888
1072
|
|
|
889
1073
|
for entry in self._entries:
|
|
@@ -891,9 +1075,10 @@ class SecondaryProjectilePool:
|
|
|
891
1075
|
continue
|
|
892
1076
|
|
|
893
1077
|
if entry.type_id == 3:
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1078
|
+
# Detonation: `vel_x` becomes the expansion timer (0..1) and `vel_y` the scale.
|
|
1079
|
+
entry.vel_x += dt * 3.0
|
|
1080
|
+
t = float(entry.vel_x)
|
|
1081
|
+
scale = float(entry.vel_y)
|
|
897
1082
|
if t > 1.0:
|
|
898
1083
|
if fx_queue is not None:
|
|
899
1084
|
fx_queue.add(
|
|
@@ -973,6 +1158,23 @@ class SecondaryProjectilePool:
|
|
|
973
1158
|
|
|
974
1159
|
entry.speed -= dt * 0.5
|
|
975
1160
|
|
|
1161
|
+
# Rocket smoke trail (`trail_timer` in crimsonland.exe).
|
|
1162
|
+
entry.trail_timer -= (abs(float(entry.vel_x)) + abs(float(entry.vel_y))) * dt * 0.01
|
|
1163
|
+
if entry.trail_timer < 0.0:
|
|
1164
|
+
dir_x = math.cos(float(entry.angle) - math.pi / 2.0)
|
|
1165
|
+
dir_y = math.sin(float(entry.angle) - math.pi / 2.0)
|
|
1166
|
+
spawn_x = float(entry.pos_x) - dir_x * 9.0
|
|
1167
|
+
spawn_y = float(entry.pos_y) - dir_y * 9.0
|
|
1168
|
+
vel_x = math.cos(float(entry.angle) + math.pi / 2.0) * 90.0
|
|
1169
|
+
vel_y = math.sin(float(entry.angle) + math.pi / 2.0) * 90.0
|
|
1170
|
+
if sprite_effects is not None and hasattr(sprite_effects, "spawn"):
|
|
1171
|
+
sprite_id = sprite_effects.spawn(pos_x=spawn_x, pos_y=spawn_y, vel_x=vel_x, vel_y=vel_y, scale=14.0)
|
|
1172
|
+
try:
|
|
1173
|
+
sprite_effects.entries[int(sprite_id)].color_a = 0.25
|
|
1174
|
+
except Exception:
|
|
1175
|
+
pass
|
|
1176
|
+
entry.trail_timer = 0.06
|
|
1177
|
+
|
|
976
1178
|
# projectile_update uses creature_find_in_radius(..., 8.0, ...)
|
|
977
1179
|
hit_idx: int | None = None
|
|
978
1180
|
for idx, creature in enumerate(creatures):
|
|
@@ -1025,16 +1227,70 @@ class SecondaryProjectilePool:
|
|
|
1025
1227
|
rand=rand,
|
|
1026
1228
|
)
|
|
1027
1229
|
|
|
1230
|
+
if entry.type_id == 1 and effects is not None and hasattr(effects, "spawn_explosion_burst") and int(detail_preset) > 2:
|
|
1231
|
+
effects.spawn_explosion_burst(
|
|
1232
|
+
pos_x=float(entry.pos_x),
|
|
1233
|
+
pos_y=float(entry.pos_y),
|
|
1234
|
+
scale=0.4,
|
|
1235
|
+
rand=rand,
|
|
1236
|
+
detail_preset=int(detail_preset),
|
|
1237
|
+
)
|
|
1238
|
+
|
|
1028
1239
|
entry.type_id = 3
|
|
1029
1240
|
entry.vel_x = 0.0
|
|
1030
|
-
entry.vel_y =
|
|
1031
|
-
entry.
|
|
1032
|
-
|
|
1241
|
+
entry.vel_y = float(det_scale)
|
|
1242
|
+
entry.trail_timer = 0.0
|
|
1243
|
+
|
|
1244
|
+
# Extra debris and scorch decals on detonation.
|
|
1245
|
+
if not freeze_active:
|
|
1246
|
+
extra_decals = 0
|
|
1247
|
+
extra_radius = 0.0
|
|
1248
|
+
if entry.type_id == 3:
|
|
1249
|
+
# NOTE: entry.type_id is already 3 here; use det_scale based on prior type.
|
|
1250
|
+
if det_scale == 1.0:
|
|
1251
|
+
extra_decals = 0x14
|
|
1252
|
+
extra_radius = 90.0
|
|
1253
|
+
elif det_scale == 0.35:
|
|
1254
|
+
extra_decals = 10
|
|
1255
|
+
extra_radius = 63.0
|
|
1256
|
+
elif det_scale == 0.25:
|
|
1257
|
+
extra_decals = 3
|
|
1258
|
+
extra_radius = 44.0
|
|
1259
|
+
if fx_queue is not None and extra_decals > 0:
|
|
1260
|
+
cx = float(creatures[hit_idx].x)
|
|
1261
|
+
cy = float(creatures[hit_idx].y)
|
|
1262
|
+
for _ in range(int(extra_decals)):
|
|
1263
|
+
angle = float(int(rand()) % 0x274) * 0.01
|
|
1264
|
+
radius = float(int(rand()) % max(1, int(extra_radius)))
|
|
1265
|
+
fx_queue.add_random(
|
|
1266
|
+
pos_x=cx + math.cos(angle) * radius,
|
|
1267
|
+
pos_y=cy + math.sin(angle) * radius,
|
|
1268
|
+
rand=rand,
|
|
1269
|
+
)
|
|
1270
|
+
|
|
1271
|
+
if sprite_effects is not None and hasattr(sprite_effects, "spawn"):
|
|
1272
|
+
step = math.tau / 10.0
|
|
1273
|
+
for idx in range(10):
|
|
1274
|
+
mag = float(int(rand()) % 800) * 0.1
|
|
1275
|
+
ang = float(idx) * step
|
|
1276
|
+
vel_x = math.cos(ang) * mag
|
|
1277
|
+
vel_y = math.sin(ang) * mag
|
|
1278
|
+
sprite_id = sprite_effects.spawn(
|
|
1279
|
+
pos_x=float(entry.pos_x),
|
|
1280
|
+
pos_y=float(entry.pos_y),
|
|
1281
|
+
vel_x=vel_x,
|
|
1282
|
+
vel_y=vel_y,
|
|
1283
|
+
scale=14.0,
|
|
1284
|
+
)
|
|
1285
|
+
try:
|
|
1286
|
+
sprite_effects.entries[int(sprite_id)].color_a = 0.37
|
|
1287
|
+
except Exception:
|
|
1288
|
+
pass
|
|
1289
|
+
|
|
1033
1290
|
continue
|
|
1034
1291
|
|
|
1035
1292
|
if entry.speed <= 0.0:
|
|
1036
1293
|
entry.type_id = 3
|
|
1037
1294
|
entry.vel_x = 0.0
|
|
1038
|
-
entry.vel_y = 0.
|
|
1039
|
-
entry.
|
|
1040
|
-
entry.lifetime = 0.0
|
|
1295
|
+
entry.vel_y = 0.5
|
|
1296
|
+
entry.trail_timer = 0.0
|