Contexte
Apres test in-game de la phase 2.C, deux problemes majeurs identifies :
- Tous les aliens se comportent pareil : Red Aliens et Guards aggressent
et chargent de la meme facon, alors que dans le jeu original, les Guards
tirent avec leur arme (Shotgun, Blaster, Mind Zap), tandis que les Red
Aliens chargent en corps-a-corps. - Distance de detection : la limite
LOS_MAX_DIST_JME = 100fetait
arbitraire et beaucoup plus courte que dans l’ASM original (qui n’a
aucune limite distance, juste un test PVS de visibilite).
Cette phase 2.D reprend l’ASM (modules/ai.s) ligne par ligne et corrige
ces 4 ecarts :
| # | Probleme | Avant | ASM-fidele |
|---|---|---|---|
| 1 | Distance LOS | hardcap 100 JME | pas de cap (PVS-based) ; on cap a 200 JME |
| 2 | Modele de damage | hp -= damage chaque coup |
cumul/4 >= HP_init (HP immuable) |
| 3 | Field of view | aucun | ai_CheckInFront : dot(forward, delta) > 0 |
| 4 | Tir alien | non-implemente | attackWithGun (hitscan) / spawnAlienProjectile |
1. Modele de damage : cumul/4 vs HP_init
L’ASM original (modules/ai.s::ai_TakeDamage ligne ~100) ne fait jamais
HP -= damage. Au lieu de cela :
ai_TakeDamage:
add.w d0, AI_Damaged_vw[idx] ; cumul += damageTaken (ce coup)
move.w AI_Damaged_vw[idx], d0
asr.w #2, d0 ; d0 = cumul/4
moveq #0, d1
move.b EntT_HitPoints_b(a0), d1 ; d1 = HP_init (jamais decremente)
move.b #0, EntT_DamageTaken_b(a0)
cmp.w d0, d1
ble ai_JustDied ; si HP_init <= cumul/4 -> mort
Donc EntT_HitPoints_b est fixe = HP_init, c’est juste le seuil de
mort. Le cumul de tous les degats recus est dans AI_Damaged_vw[idx]
(notre totalDamageDone), et on divise par 4 avant de comparer.
Resultats :
– Red Alien (HP=2) meurt quand totalDamage >= 8 (= 8 plasmas a 1 dmg).
– Mantis Boss (HP=125) meurt quand totalDamage >= 500 (= 500 plasmas).
Avant on faisait HP -= 1 par plasma, donc Red Alien mourait apres 2 tirs.
Maintenant il faut 8 tirs. Plus realiste, plus fidele a l’experience
originale.
2. Field of view (ai_CheckInFront)
L’ASM original a une routine ai_CheckInFront qui filtre les detections de
joueur. Sans elle, les aliens detectent le joueur a 360deg (ils « voient
derriere eux »). Avec, ils n’agressent que si le joueur est dans leur
demi-plan frontal :
ai_CheckInFront:
dx = playerX - alienX
dz = playerZ - alienZ
sin = SinCosTable[currentAngle]
cos = SinCosTable[currentAngle + COSINE_OFS]
dot = dx*sin + dz*cos
sgt d0 ; D0 = -1 si dot > 0 (joueur devant), 0 sinon
C’est un produit scalaire entre le vecteur « forward » de l’alien (oriente
par currentAngle) et le vecteur delta (alien -> joueur). Positif = devant.
Ajoute dans 3 endroits : doDefault, doFollowup, doResponse (toutes
les transitions vers RESPONSE/agression sont gatees par isPlayerInFront).
Dans doResponse, l’alien tourne face au joueur a chaque frame, donc une
fois qu’il a aggrer une fois, il garde le contact tant que le joueur est
suffisamment proche.
3. Tir alien (ai_AttackWithGun / ai_AttackWithProjectile)
L’ASM dispatche la routine d’attaque selon AlienT_BulType_w :
ai_AttackCommon:
move.l GLF_DatabasePtr_l, a1
lea GLFT_BulletDefs_l(a1), a1
muls #BulT_SizeOf_l, d0
add.l d0, a1
tst.l BulT_IsHitScan_l(a1)
beq ai_AttackWithProjectile ; bullet projectile
; sinon : ai_AttackWithHitScan ; bullet hitscan
Hitscan (Machine Gun=1, Shotgun=7, MindZap=12) : test de probabilite
base sur la distance :
ai_AttackWithHitScan:
jsr GetRand
and.w #$7fff, d0 ; rand 0..32767
move.w (a6), d1 ; dx (rotated)
muls d1, d1 ; dx^2
move.w 2(a6), d2 ; dz (rotated)
muls d2, d2 ; dz^2
add.l d2, d1 ; dist^2
asr.l #6, d1 ; dist^2 / 64
ext.l d0
asl.l #2, d0 ; rand * 4
cmp.l d1, d0
bgt.s .hit_player ; rand*4 > dist^2/64 -> HIT
Approximations :
– A dist=100 (proche) : dist^2/64 = 156, max rand4 = 131068, ~99.9%
chance de hit
– A dist=1000 : dist^2/64 = 15625, ~88% chance de hit
– A dist=4000 : dist^2/64 = 250000, ~0% chance de hit (rand4 max = 131068)
Projectile (Plasma=0, Rocket=2, Blaster=9, Lazer=14, Grenade=8…) :
spawn une bullet alien qui voyage vers le joueur (delegue a
spawnAlienProjectile dans le port). Pour la 2.D initiale, on simule
avec 50% chance de hit + damage immediat (placeholder, vraie pool en 2.E).
Differenciation par alien :
– Red Alien : bulType=0, responseBehaviour=0 (Charge) -> charge melee
– Guard : bulType=9 (Blaster), respBeh=2 (AttackWithGun) -> tire projectile
– ‘Ard Guard : bulType=7 (Shotgun), respBeh=2 -> tire hitscan
– Mind Priest : bulType=12 (MindZap), respBeh=2 -> tire hitscan
– Mantis Boss : bulType=2 (Rocket), respBeh=2 -> tire projectile
4. Trigger de tir (ai_DoAction_b)
Dans l’ASM, le tir n’a lieu QUE pendant les frames « kick » de l’animation
d’attaque, marquees par un byte ai_DoAction_b non-zero dans la table
AlienAnimPtr_l. Sans porter les tables d’animations completes, on simule
par : tirer une fois quand timer2 traverse FIRE_FRAME=4. Une seule
fois par cycle d’attaque (de 8 frames).
boolean fireTrigger = (prevTimer2 < FIRE_FRAME && a.timer2 >= FIRE_FRAME);
if (fireTrigger && a.seesPlayer && isPlayerInFront(a)) {
performAttack(a); // dispatch melee/hitscan/projectile
}
Fichiers modifies
-
core/ai/AlienRuntimeState.java:hitPointsdocumente comme
IMMUABLE (= HP_init, jamais decremente).isAlive()reecrit en
mode != DIE && (totalDamageDone >> 2) < hitPoints. -
core/ai/AlienAI.java:
–applyDamage()reecrit :totalDamageDone += damageTaken, mort si
totalDamageDone >> 2 >= hitPoints. HP_init n’est plus decremente.
– AjoutisPlayerInFront(a): produit scalaire forward.delta
–doDefault,doFollowup,doResponse: ajout du gate FOV avant
toute transition vers RESPONSE
–doResponse()reecrit avec :FIRE_FRAME=4trigger pour simulerai_DoAction_b- Dispatch
performAttack(a): melee si!attacksWithGun, hitscan
siisHitscanBullet(bulType), sinon projectile - Ajout
attackWithGun(a): formule ASM(rand*256f) > dist^2,
applique damage viaworld.applyDamageToPlayer - Ajout
attackWithProjectile(a): delegate versworld.spawnAlienProjectile - Ajout
attackMelee(a): sidist^2 < 80*80, appliqueMELEE_DAMAGE=2 - Ajout
damageForBulletType(bulType): lookup hardcode des degats GLF
-
core/ai/AiWorld.java: ajout 2 methodes default :
–applyDamageToPlayer(damage, fromX, fromZ)(no-op default)
–spawnAlienProjectile(bulletType, damage, fromX, fromY, fromZ)(no-op) -
world/AiWorldAdapter.java:
–LOS_MAX_DIST_JMEpasse de 100 a 200 (pas de cap dans l’ASM)
– Ajout champplayerHealth+ settersetPlayerHealth(ph)
– ImplementationapplyDamageToPlayer: appelleplayerHealth.takeDamage(damage)
– ImplementationspawnAlienProjectile: 50% chance + takeDamage (placeholder 2.D) -
app/GameAppState.java: apres creation de l’AiWorldAdapter, branche
aiWorld.setPlayerHealth(healthState)pour que les tirs alien fassent
vraiment des degats.
Tests
core/ai/AlienAITest.java mis a jour : 17 -> 22 tests
- 3 nouveaux tests dans
class FieldOfView: playerInFrontTriggersResponseplayerBehindDoesNotTrigger-
playerToTheSide -
5 nouveaux tests dans
class AlienShooting: ardGuardShootsHitscanCloseRange(‘Ard Guard, Shotgun=hitscan, courte distance)ardGuardMissesAtLongRange(dist=10000 -> miss garanti, formulerand*256 > dist^2)guardShootsProjectile(Guard, Blaster=projectile, spawnAlienProjectile appele)noShootBeforeFireFrame(timer2=2 ne declenche pas, timer2 doit traverser 4)-
onlyOneShotPerCycle(timer2=5 deja passe ne re-tire pas) -
Test
lethalDamageKillscorrige : verifie maintenanthitPoints==0
apresdoDie(= ASM-fidele, cf.ai.s::ai_DoDie .still_dying) -
Test
damageReducesHPmodifie : verifie que hitPoints reste FIXE
(= HP_init) en cas de dommages non letaux, et quetotalDamageDone
s’accumule -
Nouveau test
redAlienDiesAfter8Plasmas: valide la formule cumul/4
(Red Alien HP=2 meurt apres 8 plasmas, pas 2) -
FakeWorldetend les capture des appels de combat : applyDamageCount,lastDamageAmountpourapplyDamageToPlayerspawnProjectileCount,lastBulletTypepourspawnAlienProjectile
Resultat in-game attendu
-
Red Aliens : chargent toujours en ligne droite, font des degats melee
quand ils touchent le joueur (= 2 HP par contact). Plus difficile a
tomber : 8 plasmas au lieu de 2. Sans gun, jamais de tir distant. -
Guards (Blaster) : s’arretent a portee, tirent un projectile vers
le joueur. Le projectile a 50% chance de hit (placeholder). Damage = 2
(Blaster Bolt). -
‘Ard Guards (Shotgun) : s’arretent a portee, tirent un hitscan.
Le hit est probabiliste : ~99% a courte distance, ~88% a 1000 unites,
~0% au-dela de 4000 unites. Damage = 1. -
Mind Priests (MindZap) : tirent un hitscan tres puissant (3 dmg)
avec memes proba que Shotgun. -
Field of view : si on est derriere un alien, il ne reagit pas.
Ils faut entrer dans son demi-plan frontal pour declencher l’aggression. -
HP joueur : visible dans l’overlay debug (touche F3) sous forme
HP:N/200. Decremente quand on est touche.
Limitations connues (a faire en phase 2.E ou plus tard)
- Pas de shield logic dans
applyDamageToPlayer: les degats vont
directement a la sante, sans consommer le shield d’abord. A implementer
quand on rebranche le HUD complet. - Animation trigger simplifie : on tire au FIRE_FRAME=4 fixe, alors
que l’ASM utilise des tablesAlienAnimPtr_lavec un byte par frame.
Pour porter ca correctement, il faut extraire les tables de l’ASM
(« draw.s » cote draw + tables alimentees par newaliencontrol). - Projectile 50% hit placeholder : il n’y a pas de vrai pool de
bullets aliens (AlienShotPoolsymetrique aPlayerShotPool).
Placeholder simplifie : 50% chance de hit + damage direct. La 2.E
ajoutera un vrai pool +AlienBulletUpdateSystempour faire voyager
les bullets et tester collision avec le joueur. - Pas de red flash overlay quand le joueur est touche.
- Pas de knockback visuel (ASM utilise
EntT_ImpactX/Zpour pousser
le joueur dans la direction du tir). - Pas de SFX positionnel (cris d’alien, son de tir aliens).
- Boss death spawn non implemente : si
splatType >= NUM_BULLET_DEFS,
l’alien parent doit spawner 2 plus petits aliens. Pour l’instant tous
les aliens font juste un fade out simple. - Movement collision : aliens traversent toujours les murs (deplacement
lineaire vers la cible). La 2.F porteraMoveObject+Obj_DoCollision. - Sprite 4-directionnel : les sprites sont toujours frontaux. La 2.G
porteraViewpointToDraw(TOWARDS=0/RIGHT=1/AWAY=2/LEFT=3) qui choisit
le sprite selon l’angle relatif joueur-alien.
Statistiques cumulees session 113 (4 phases)
- Phases livrees : 2.A (machine d’etats) + 2.B (integration runtime)
- 2.C (tirs joueur -> aliens) + 2.D (ASM-fidelity fixes)
- 8 fichiers nouveaux : 6 dans
core/ai/, 1 dansworld/, 1 danscombat/ - 6 fichiers modifies dont 2 fois pour 2.D :
AlienRuntimeState,
AlienAI,AiWorld,AiWorldAdapter,GameAppState,
BulletUpdateSystem,HitscanTracerSystem,PlayerShootSystem - 3 fichiers test : 22 tests AlienAI + 7 integration AlienDefLoader
- 11 unitaires AlienHitDetector = 40 tests au total
- CHANGELOG : 4 sections (2.A -> 2.B -> 2.C -> 2.D) toutes datees
2026-04-28