| 09-15-2003, 02:10 PM | #1 |
Well, do you guys have some workarounds for this nasty bug - When I playtested my A.I vs my A.I, almost all of the game-deciding factor is who attacks who's base first, since then the defending team will send all of their units to help the attacked player, and continue to send units to the attacked base, one by one as they're created. It doesn't matter if the attacked base survives, or gone, they still send their newly created units there :bgrun: and suicides them :nono: . Often if the attack is repelled, a player or two still do that and just clump there doing nothing. This has to be the most :nono: bug in the A.I system. |
| 09-16-2003, 09:58 AM | #2 |
Sounds like the "Kemikaze bug" we sometimes happens to trigger with AMAI. Sadly I have no idea on how we solved it, I guess AIAndy can help you with that. |
| 09-16-2003, 11:39 AM | #3 |
It is similar to the Kamikaze bug. That was when they would send all their units directly after they are created to attack the enemy who attacked them before. It is as if they think that that player still attacks them although he fled. Now in the current version of TFT I think the Kamikaze bug is gone because I have not seen it in this form since then. But who knows, it might not have been properly fixed and something you did triggered it. Or it is something that is inert to the way the hard coded AI is implemented. You mention TownThreatened() . Have you checked if that is true or false when the bug happens? |
| 09-16-2003, 01:05 PM | #4 |
This is what I found so far... There is probably a bug in the standard AI Code (NOT Hardcoded :ggani:) on attacking thread. Since my A.I's attacking 'thread' (and probably yours as well) is based on the original AI, it contains this bug also. Let's see... how to explain this... I'll just copy-paste important codes first. Functions taken from standard, unaltered A.I files - common.ai and elf.ai common.ai's SingleMeleeAttack... the core attacking function. Code:
//============================================================================
// SingleMeleeAttack
//============================================================================
function SingleMeleeAttack takes boolean needs_exp, boolean has_siege, boolean major_ok, boolean air_units returns nothing
local boolean can_siege
local real daytime
local unit hall
local unit mega
local unit creep
local unit common
local integer minimum
local boolean allies
call Trace("===SingleMeleeAttack===\n") //xxx
if TownThreatened() then
call Trace("sleep 2, town threatened\n") //xxx
call Sleep(2)
return
endif
// purchase zeppelins
//
if get_zeppelin and GetGold() > 300 and GetWood() > 100 then
call Trace("purchase zep\n") //xxx
call PurchaseZeppelin()
set get_zeppelin = false
set ready_for_zeppelin = false
return
endif
set ready_for_zeppelin = true
// coordinate with allies
//
set allies = GetAllyCount(ai_player) > 0
if allies and MeleeDifficulty() != MELEE_NEWBIE then
set common = GetAllianceTarget()
if common != null then
call Trace("join ally force\n") //xxx
if GetMegaTarget() != null then
call AddSiege()
endif
call FormGroup(3,true)
call AttackMoveKillA(common)
call SetAllianceTarget(null)
return
endif
endif
// take expansions as needed
//
if needs_exp then
call Trace("needs exp\n") //xxx
set creep = GetExpansionFoe()
if creep != null then
call Trace("attack exp\n") //xxx
call SetAllianceTarget(creep)
call FormGroup(3,true)
call AttackMoveKillA(creep)
call Sleep(20)
set take_exp = false
return
endif
endif
// all-out attack if the player is weak
//
if MeleeDifficulty() != MELEE_NEWBIE then
set mega = GetMegaTarget()
if mega != null then
call Trace("MEGA TARGET!!!\n") //xxx
call AddSiege()
call FormGroup(3,true)
call AttackMoveKillA(mega)
return
endif
endif
// deny player an expansion
//
set hall = GetEnemyExpansion()
set daytime = GetFloatGameState(GAME_STATE_TIME_OF_DAY)
set can_siege = has_siege and (air_units or (daytime>=4 and daytime<=12))
if hall!=null and (can_siege or not IsTowered(hall)) then
call Trace("test player town attack\n") //xxx
if MeleeDifficulty() == MELEE_NEWBIE then
set minimum = 3
elseif allies and MeleeDifficulty() == MELEE_NORMAL then
set minimum = 1
else
set minimum = 0 // HARD, INSANE, and NORMAL with no allies
endif
if exp_seen >= minimum then
call Trace("do player town attack\n") //xxx
set exp_seen = 0
call AddSiege()
call SetAllianceTarget(hall)
call FormGroup(3,true)
call AttackMoveKillA(hall)
return
endif
set exp_seen = exp_seen + 1
endif
// attack player's main base when siege is available
//
if can_siege then
call Trace("attack player's town\n") //xxx
call AddSiege()
call AnyPlayerAttack()
return
endif
// extended, more specific method of determining creep levels
//
if min_creeps != -1 then
call TraceI("custom creep attack %d\n",max_creeps) //xxx
call CreepAttackEx()
return
endif
// nothing better to do, so kill a creep camp
//
if major_ok then
call Trace("major creep attack\n") //xxx
call MajorCreepAttack()
return
endif
call Trace("minor creep attack\n") //xxx
call MinorCreepAttack()
endfunctionelf.ai's attack_sequence Code:
//--------------------------------------------------------------------------------------------------
// attack_sequence
//--------------------------------------------------------------------------------------------------
function attack_sequence takes nothing returns nothing
local boolean needs_exp
local boolean has_siege
local boolean air_units
local integer level
loop
exitwhen c_hero1_done > 0 and c_archer_done >= 2
call Sleep(2)
endloop
if MeleeDifficulty() == MELEE_NEWBIE then
call Sleep(240)
endif
call StaggerSleep(0,2)
loop
loop
exitwhen not CaptainRetreating()
call Sleep(2)
endloop
set wave = wave + 1
if wave == 2 then
loop
exitwhen c_archer_done >= 4
call Sleep(2)
endloop
endif
call setup_force()
set level = force_level()
set max_creeps = level * 4 / 5
set min_creeps = max_creeps - 10
if min_creeps < 0 then
set min_creeps = 0
endif
set needs_exp = take_exp and (level >= 9 or c_gold_owned < 2000)
set has_siege = level >= 40 or c_ballista_done > 0 or c_chimaera_done > 0 or c_mtn_giant_done > 0
set air_units = c_chimaera_done > 0 or c_dragon_done > 0
set allow_air_creeps = air_units or c_archer_done > 3
call SingleMeleeAttack(needs_exp,has_siege,false,air_units)
if MeleeDifficulty() == MELEE_NEWBIE then
call Sleep(60)
endif
endloop
endfunctionelf.ai's setup_force Code:
//--------------------------------------------------------------------------------------------------
// setup_force
//--------------------------------------------------------------------------------------------------
function setup_force takes nothing returns nothing
call AwaitMeleeHeroes()
call InitMeleeGroup()
call SetMeleeGroup( hero_id )
call SetMeleeGroup( hero_id2 )
call SetMeleeGroup( hero_id3 )
call SetMeleeGroup( ARCHER )
call SetMeleeGroup( HUNTRESS )
call SetMeleeGroup( DRUID_TALON )
call SetMeleeGroup( DRUID_CLAW )
call SetMeleeGroup( DRYAD )
call SetMeleeGroup( CHIMAERA )
call SetMeleeGroup( MOUNTAIN_GIANT )
call SetMeleeGroup( FAERIE_DRAGON )
if GetUnitCountDone(HIPPO) > 0 then
call SetMeleeGroup( HIPPO )
endif
if GetUnitCountDone(HIPPO_RIDER) > 0 then
call SetMeleeGroup( HIPPO_RIDER )
endif
call SetInitialWave(10)
endfunctionAt the start of each wave, attack_sequence calls setup_force and adds each military unit into the attacking force. Then after that it calls SingleMeleeAttack. Look at the TownThreatened() code in the SingleMeleeAttack. It returns when Town is threatened or captain is in combat. Now it returns to the attack_sequence and loops once agains, calling setup_force again. When it calls SingleMeleeAttack again, it will probably also return because Town is still threatened or captain is still in combat. Now, if a unit has been created between the first SingleMeleeAttack and the last setup_force, it will call up the unit and send it to the fray because the new unit is also a part of the captain group. My guess is that either TownThreatened or CaptainInCombat has some timeout value to consider it is not threatened/in combat, so the A.I will send the units one by one as they created, then dying (and probably setting CaptainInCombat's timer for timeout to zero again), then sending new units, dying, new units, dying, ........ blah blah blah. My solution for now is changing this inside SingleMeleeAttack... Code:
if TownThreatened() then
call Trace("sleep 2, town threatened\n") //xxx
call Sleep(2)
return
endifwith this... Code:
loop
exitwhen not TownThreatened() and not CaptainInCombat(true)
call Sleep(2)
endloopThis may be true... or not, I'm not too sure about this and still testing. |
| 09-16-2003, 02:17 PM | #5 |
This would be true if setup_force would actually add the units to the attack group but actually it only adds them to an array. Only when FormGroup is called, the units in the array are actually added to the attack group. |
