# HG changeset patch # User Wuzzy # Date 1591479629 -7200 # Node ID ac44dae6425edd6f043409d938ae916160240275 # Parent 583080b0272b0b37edcc69dd6fe0de5e7e83441c Teach AI to use drill strike diff -r 583080b0272b -r ac44dae6425e ChangeLog.txt --- a/ChangeLog.txt Sat Jun 06 20:51:12 2020 +0200 +++ b/ChangeLog.txt Sat Jun 06 23:40:29 2020 +0200 @@ -11,6 +11,7 @@ + Themes: Make Sudden Death flakes in Underwater theme rise + New taunt chat commands: /bubble, /happy + Teach computer players how to drop mines + + Teach computer players how to use drill strike * Racer: Resize waypoints in custom-sized drawn maps * Mutant: Fix impossible to become mutant after mutant is gone * A Classic Fairytale: Mission 1: Fix possibility of getting stuck in “Leap of Faith” section diff -r 583080b0272b -r ac44dae6425e hedgewars/uAI.pas --- a/hedgewars/uAI.pas Sat Jun 06 20:51:12 2020 +0200 +++ b/hedgewars/uAI.pas Sat Jun 06 23:40:29 2020 +0200 @@ -167,11 +167,6 @@ AddAction(BestActions, aia_Weapon, Longword(a), 300 + random(400), 0, 0); - if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then - begin - AddAction(BestActions, aia_Put, 0, 8, ap.AttackPutX, ap.AttackPutY) - end; - if (ap.Angle > 0) then AddAction(BestActions, aia_LookRight, 0, 200, 0, 0) else if (ap.Angle < 0) then @@ -187,6 +182,11 @@ AddAction(BestActions, aia_Precise, aim_release, 10, 0, 0); end; + if (Ammoz[a].Ammo.Propz and ammoprop_NeedTarget) <> 0 then + begin + AddAction(BestActions, aia_Put, 0, 8, ap.AttackPutX, ap.AttackPutY) + end; + if (Ammoz[a].Ammo.Propz and ammoprop_NoCrosshair) = 0 then begin dAngle:= LongInt(Me^.Angle) - Abs(ap.Angle); diff -r 583080b0272b -r ac44dae6425e hedgewars/uAIAmmoTests.pas --- a/hedgewars/uAIAmmoTests.pas Sat Jun 06 20:51:12 2020 +0200 +++ b/hedgewars/uAIAmmoTests.pas Sat Jun 06 23:40:29 2020 +0200 @@ -53,6 +53,7 @@ function TestWhip(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; function TestKamikaze(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; function TestAirAttack(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; +function TestDrillStrike(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; function TestTeleport(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; function TestHammer(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; function TestCake(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; @@ -118,7 +119,7 @@ (proc: @TestGrenade; flags: 0), // amSMine (proc: @TestHammer; flags: amtest_NoTarget), // amHammer (proc: nil; flags: 0), // amResurrector - (proc: nil; flags: 0), // amDrillStrike + (proc: @TestDrillStrike; flags: amtest_Rare), // amDrillStrike (proc: nil; flags: 0), // amSnowball (proc: nil; flags: 0), // amTardis (proc: nil; flags: 0), // amLandGun @@ -1306,6 +1307,142 @@ TestAirAttack:= valueResult; end; +function TestDrillStrike(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; +const cShift = 4; +var bombsSpeed, X, Y, dX, dY, drillX, drillY: real; + t2: real; + b: array[0..9] of boolean; + dmg: array[0..9] of LongInt; + fexit, collided: boolean; + i, t, value, valueResult, attackTime, drillTimer, targetX: LongInt; +begin +Flags:= Flags; // avoid compiler hint +ap.ExplR:= 0; +if (Level > 3) or (cGravityf = 0) then + exit(BadTurn); + +ap.Angle:= 0; +targetX:= Targ.Point.X; +ap.AttackPutY:= Targ.Point.Y; + +bombsSpeed:= hwFloat2Float(cBombsSpeed); +X:= Targ.Point.X - 135 - cShift; // hh center - cShift +X:= X - bombsSpeed * sqrt(((Targ.Point.Y + 128) * 2) / cGravityf); +Y:= -128; +dX:= bombsSpeed; +dY:= 0; + +valueResult:= 0; + +attackTime:= 0; +while attackTime <= 4000 do + begin + inc(attackTime, 1000); + value:= 0; + for i:= 0 to 9 do + begin + b[i]:= true; + dmg[i]:= 0 + end; + + repeat + X:= X + dX; + Y:= Y + dY; + dY:= dY + cGravityf; + fexit:= true; + + for i:= 0 to 9 do + if b[i] then + begin + fexit:= false; + collided:= false; + drillX:= trunc(X) + LongWord(i * 30); + drillY:= trunc(Y); + // Collided with land ... simulate drilling + if TestCollExcludingObjects(trunc(drillX), trunc(drillY), 4) and + (Abs(Targ.Point.X - trunc(X)) + Abs(Targ.Point.Y - trunc(Y)) > 21) then + begin + drillTimer := attackTime; + t2 := 0.5 / sqrt(sqr(dX) + sqr(dY)); + dX := dX * t2; + dY := dY * t2; + repeat + drillX:= drillX + dX; + drillY:= drillY + dY; + dec(drillTimer, 10); + until (Abs(Targ.Point.X - drillX) + Abs(Targ.Point.Y - drillY) < 22) + or (drillX < 0) + or (drillY < 0) + or (trunc(drillX) > LAND_WIDTH) + or (trunc(drillY) > LAND_HEIGHT) + // TODO: Simulate falling again when rocket has left terrain again + or (drillTimer <= 0); + collided:= true; + end + // Collided with something else ... record collision + else if TestColl(trunc(drillX), trunc(drillY), 4) then + collided:= true; + + // Simulate explosion + if collided then + begin + b[i]:= false; + dmg[i]:= RateExplosion(Me, trunc(drillX), trunc(drillY), 58); + // 58 (instead of 60) for better prediction (hh moves after explosion of one of the rockets) + end; + end; + until fexit or (Y > cWaterLine); + + for i:= 0 to 5 do + if dmg[i] <> BadTurn then + inc(value, dmg[i]); + t:= value; + targetX:= Targ.Point.X - 60; + + for i:= 0 to 3 do + if dmg[i] <> BadTurn then + begin + dec(t, dmg[i]); + inc(t, dmg[i + 6]); + if t > value then + begin + value:= t; + targetX:= Targ.Point.X - 30 - cShift + i * 30 + end + end; + + if value > valueResult then + begin + valueResult:= value; + ap.AttackPutX:= targetX; + ap.Time:= attackTime; + end; +end; + +if valueResult <= 0 then + valueResult:= BadTurn +else + begin + // Weaker AI has chance to get the time wrong by 1-3 seconds + if Level = 5 then + // +/- 3 seconds + ap.Time:= ap.Time + (3 - random(7)) * 1000 + else if Level = 4 then + // +/- 2 seconds + ap.Time:= ap.Time + (2 - random(5)) * 1000 + else if Level = 3 then + // +/- 1 second + if (random(2) = 0) then + ap.Time:= ap.Time + (1 - random(3)) * 1000 + else if Level = 2 then + // 50% chance for +/- 1 second + if (random(2) = 0) then + ap.Time:= ap.Time + (1 - random(3)) * 1000; + ap.Time:= Min(5000, Max(1000, ap.Time)); + end; + +TestDrillStrike:= valueResult; +end; function TestTeleport(Me: PGear; Targ: TTarget; Level: LongInt; var ap: TAttackParams; Flags: LongWord): LongInt; var