P3D-Legacy/P3D/Battle/BattleSystemV2/TrainerAI.vb

966 lines
48 KiB
VB.net

Namespace BattleSystem
Public Class TrainerAI
'Trainer AI Levels:---------------------------------------------------------------------------------------------------------------------
'Normal Trainers: 0-20 (0 being not very smart trainers like Bugcatchers, 20 being Cooltrainers, get higher levels when rematches occur)
'(Johto) Gym Leaders: 10-40 (10 for Falkner, 40 for Clair)
'(Kanto) Gym Leaders: All 60
'Elite 4: 60
'Lance: 100
'Elder Li: 10
'Rival: 40
'Team Rocket Grunts: 5
'Team Rocket Admins: 20
Public Shared Function GetAIMove(ByVal BattleScreen As BattleScreen, ByVal OwnStep As Battle.RoundConst) As Battle.RoundConst
Dim p As Pokemon = BattleScreen.OppPokemon
Dim op As Pokemon = BattleScreen.OwnPokemon
Dim m As List(Of Attack) = p.Attacks.ToList()
For i = m.Count - 1 To 0 Step -1
If m(i).CurrentPP <= 0 Then
m.RemoveAt(i)
End If
Next
If m.Count = 0 Then
Return New Battle.RoundConst With {.StepType = Battle.RoundConst.StepTypes.Move, .Argument = Attack.GetAttackByID(165)}
End If
'Switching:
' - check for opponent moves: if super effective: 1.5x: 25%, 2x: 50%, 4x: 75%: check for pokemon in party that reacts to every move with less the detected effectiveness k
' - check for own moves: if only 0x: check for other pokemon in party (best fitting) and switch in k
' - check for own moves: if all moves below 1x: 50%: check for other pokemon in party that has at least one move above or equal 1x
' - own pokemon got cursed: 75% k
' - own pokemon got confused: 50% k
' - if has pokemon in team that can stand the attack coming in better than the current one: 25%: switch into that pokemon reflecting that move
'Use Item:
'(Useable items: Potion,Super Point,Hyper Potion,Full Heal,Full Restore,Burn Heal,Antidote,Paralyze heal,Awakening,Ice Heal,Revive,Max Revive,Max Potion)
' - Use potions if fills to at least half + own hp is below 40% (chance 60% + (40 - ownhp%)), always use best potion (normal->super->hyper->max) k
' - Use Full Heal when: Confused, Burned, Frozen, Asleep, Paralyzed, Poisoned and HP is above 25% (100%), use above status restore items. k
' - ^ same with Full Restore but without HP check, use above everything k
' - Use Status restore items if own HP is above 25%, chance: 60% k
' - Use revive when at least 50% of team is beaten, use on highest leveled pokemon (max over normal) (50%) k
'Definite move:
' - If own pokemon is asleep, try to use Snore (100%) k
' - If own pokemon is frozen and has a move to thraw out -> use that move (100%) k
' - Fake Out if first turn -> try to inflict flinch (100%) k
' - use high priority move if opponent is nearly defeated (<= 15%) (100%) k
' - use attacking move if speed is higher than opp speed and opponent has low health (<= 30%) (75%) k
' - use recover moves when HP lower than 50% (75%) k
' - try to paralyse (75%) k
' - try to confuse (75%) k
' - try to burn (75%) k
' - try to sleep (75%) k
' - try to freeze (75%) k
' - try to poison (50%) k
' - try to set up leech seed (75%) k
' - try to use FocusEnergy (50%) k
' - Set up spikes (50%)
' - setup safeguard (20%)
' - setup sandstorm, when at least half of the team is immune (50%)
' - setup hailstorm, when at least half of the team is immune (50%)
' - higher sp atk than atk, not boosted stat, higher spatk than spdef: use sp atk boost (50%) k
' - higher atk than sp atk, not boosted stat, higher atk than def: use atk boost (50%) k
' - not lowered opp def stat, higher atk than spatk, higher def than atk: use def lowering move (50%) k
' - not lowered opp sp def stat, higher sp atk than spdef, higher sp def than sp atk: use sp def lowering move (50%) k
' - when opp has lower atk than def/lower spatk than spdef and can heal stat: use stat healing move (50%) k
' - if entry hazards are on the field and more than one pokemon left: use Rapid Spin (100%)
' - if not done before, boost own evasion (50%) k
' - if not done before, lower opp accuracy (75%) k
' - use Psych up when the opponent total stat change compared to own is at least +2 (50%)
' - has Reflect/Light Screen: Uses the one that fits better for the own stat (75%) k
' - when having a move with accuracy lower than 60 and Mind Reader/Lock On -> Use Mind Reader/Lock On (50%)
' - Use fascade, if burned, paralysed or poisoned (50%)
' - use smelling salt if opponent is paralysed (100%)
'Special Moveset combos:
' - Defense Curl + Rollout k
' - Sunny Day + Solar Beam / Sunny weather + Solar Beam
' - Raindance + Thunder / Rain weather + Thunder / Sunny weather + Thunder
' - Sleep inducting move + Dream Eater / Use Dream Eater if target is asleep
' - Flash + Ride
' - Ghost Pokemon and Curse
' - Use Flail/Reversal when HP is really low
' - Conversion2/Conversion
' - Protect/Detect: when opp used a strong move
' - use Uproar if not set up and detect opponent with sleep moves
' - Stockpile + Swallow/SpitUp
' - Thunderwave + Smelling Salt
'After choosing attacking moves:
' - not use belly drum/substitute with too low HP
' - not use any move that doesnt work due to type disadventage (ghost-normal, normal-ghost, fighting-ghost, dragon-fairy)
'Choose attack move:
' - look for abilities that absorb move types
' - base power, accuracy, effectiveness, STAB, PP Left, attack stats (atk/spatk), EV, IV, defense stat (def/spdef)
' - never use splash
' - use never-miss attack when own accuracy is low/opp evasion is high
'-------------------------------------AI BEGIN------------------------------------------------------------------------------------'
'-------------------------------------Random move depending on difficulty---------------------------------------------------------'
'Only applies if trainer has an AI level below 20:
If BattleScreen.Trainer.AILevel < 20 Then
If Core.Player.DifficultyMode = 0 Then
'Chance of 35% that the trainer is using a random move:
If Core.Random.Next(0, 100) < 35 Then
Return ProduceOppStep(m, Core.Random.Next(0, m.Count))
End If
ElseIf Core.Player.DifficultyMode = 1 Then
'Chance of 18% that the trainer is using a random move:
If Core.Random.Next(0, 100) < 18 Then
Return ProduceOppStep(m, Core.Random.Next(0, m.Count))
End If
End If
End If
'-------------------------------------Switching-----------------------------------------------------------------------------------'
'Only applies if trainer has an AI level above or equal 40:
If BattleScreen.Trainer.AILevel >= 40 Then
If BattleCalculation.CanSwitch(BattleScreen, False) = True Then
If BattleScreen.Trainer.Pokemons.Count > 0 Then
Dim living As Integer = 0
For Each cP As Pokemon In BattleScreen.Trainer.Pokemons
If cP.HP > 0 And cP.Status <> Pokemon.StatusProblems.Fainted Then
living += 1
End If
Next
If living > 1 Then
'check for opponent moves: if super effective: 1.5x: 25%, 2x: 50%, 4x: 75%: check for pokemon in party that reacts to every move with less the detected effectiveness
Dim maxOpponentEff As Single = 0.0F
For Each Attack As BattleSystem.Attack In op.Attacks
Dim effectiveness As Single = BattleCalculation.CalculateEffectiveness(Attack.GetAttackByID(Attack.ID), BattleScreen, op, p, True)
If effectiveness > maxOpponentEff Then
maxOpponentEff = effectiveness
End If
Next
If maxOpponentEff > 1.0F Then
Dim chance As Integer = 0
Select Case maxOpponentEff
Case 1.25F
chance = 10
Case 1.5F
chance = 25
Case 2.0F
chance = 35
Case 4.0F
chance = 50
End Select
If RPercent(chance) = True Then
Dim lessTeamPs As New List(Of Integer)
For i = 0 To BattleScreen.Trainer.Pokemons.Count - 1
If i <> BattleScreen.OppPokemonIndex Then
Dim TeamP As Pokemon = BattleScreen.Trainer.Pokemons(i)
If TeamP.HP > 0 And TeamP.Status <> Pokemon.StatusProblems.Fainted Then
Dim alwaysLess As Boolean = True
For Each Attack As BattleSystem.Attack In op.Attacks
Dim effectiveness As Single = BattleCalculation.CalculateEffectiveness(Attack.GetAttackByID(Attack.ID), BattleScreen, op, TeamP, True)
If effectiveness >= maxOpponentEff Then
alwaysLess = False
Exit For
End If
Next
If alwaysLess = True Then
lessTeamPs.Add(i)
End If
End If
End If
Next
If lessTeamPs.Count > 0 Then
Return ProduceOppStep(lessTeamPs(Core.Random.Next(0, lessTeamPs.Count)))
End If
End If
End If
'check for own moves: if only 0x: check for other pokemon in party (best fitting) and switch in
Dim only0 As Boolean = True
For Each Attack As BattleSystem.Attack In p.Attacks
Dim effectiveness As Single = BattleCalculation.CalculateEffectiveness(Attack.GetAttackByID(Attack.ID), BattleScreen, p, op, False)
If effectiveness <> 0.0F Then
only0 = False
Exit For
End If
Next
If only0 = True Then
Dim switchList As New List(Of Integer)
For i = 0 To BattleScreen.Trainer.Pokemons.Count - 1
If i <> BattleScreen.OppPokemonIndex Then
Dim TeamP As Pokemon = BattleScreen.Trainer.Pokemons(i)
If TeamP.HP > 0 And TeamP.Status <> Pokemon.StatusProblems.Fainted Then
switchList.Add(i)
End If
End If
Next
If switchList.Count > 0 Then
Return ProduceOppStep(switchList(Core.Random.Next(0, switchList.Count)))
End If
End If
'own pokemon got cursed: 75%
If BattleScreen.FieldEffects.OppCurse > 0 Then
If RPercent(75) = True Then
Dim newSwitchIndex As Integer = 0
Dim canSwitchTo As New List(Of Integer)
For i = 0 To BattleScreen.Trainer.Pokemons.Count - 1
Dim TeamP As Pokemon = BattleScreen.Trainer.Pokemons(i)
If TeamP.HP > 0 And TeamP.Status <> Pokemon.StatusProblems.Fainted And i <> BattleScreen.OppPokemonIndex Then
canSwitchTo.Add(i)
End If
Next
If canSwitchTo.Count > 0 Then
newSwitchIndex = canSwitchTo(Core.Random.Next(0, canSwitchTo.Count))
Return ProduceOppStep(newSwitchIndex)
End If
End If
End If
'own pokemon got confused: 50%
If p.HasVolatileStatus(Pokemon.VolatileStatus.Confusion) = True Then
If RPercent(50) = True Then
Dim newSwitchIndex As Integer = 0
Dim canSwitchTo As New List(Of Integer)
For i = 0 To BattleScreen.Trainer.Pokemons.Count - 1
Dim TeamP As Pokemon = BattleScreen.Trainer.Pokemons(i)
If TeamP.HP > 0 And TeamP.Status <> Pokemon.StatusProblems.Fainted And i <> BattleScreen.OppPokemonIndex Then
canSwitchTo.Add(i)
End If
Next
If canSwitchTo.Count > 0 Then
newSwitchIndex = canSwitchTo(Core.Random.Next(0, canSwitchTo.Count))
Return ProduceOppStep(newSwitchIndex)
End If
End If
End If
End If
End If
End If
End If
'-------------------------------------Items---------------------------------------------------------------------------------------'
'same with Full Restore but without HP check, use above everything
If p.HasVolatileStatus(Pokemon.VolatileStatus.Confusion) = True Or p.Status <> Pokemon.StatusProblems.None And p.Status <> Pokemon.StatusProblems.Fainted Then
If TrainerHasItem(14, BattleScreen) = True Then
If Not (TrainerHasItem(38, BattleScreen) = True And p.HP = p.MaxHP) = True Then
Return ProduceOppStep(14, -1)
End If
End If
End If
'Use potions if fills to at least half + own hp is below 40% (chance 60% + (40 - ownhp%)), always use best potion (normal->super->hyper->max)
If p.HP <= CInt((p.MaxHP / 100) * 40) Then
Dim potion As Integer = GetBestPotion(BattleScreen)
If potion > -1 Then
If TrainerHasItem(potion, BattleScreen) = True Then
Dim chance As Integer = GetPokemonValue(BattleScreen, p) + (40 - CInt((p.HP / p.MaxHP) * 100))
If RPercent(chance) = True Then
Dim HP As Integer = GetPotionHealHP(p, potion)
If HP >= CInt(Math.Ceiling(p.MaxHP / 2)) Then
Return ProduceOppStep(potion, -1)
End If
End If
End If
End If
End If
'Use Full Heal when: Confused, Burned, Frozen, Asleep, Paralyzed, Poisoned and HP is above 25% (100%), use above status restore items.
If p.HasVolatileStatus(Pokemon.VolatileStatus.Confusion) = True Or p.Status <> Pokemon.StatusProblems.None And p.Status <> Pokemon.StatusProblems.Fainted Then
If p.HP >= CInt((p.MaxHP / 100) * 25) Then
If TrainerHasItem(38, BattleScreen) = True Then
Return ProduceOppStep(38, -1)
End If
End If
End If
'Use Status restore items if own HP is above 25%, chance: 60%
If p.HP >= CInt((p.MaxHP / 100) * 25) Then
If p.Status <> Pokemon.StatusProblems.None And p.Status <> Pokemon.StatusProblems.Fainted Then
If RPercent(60) = True Then
Select Case p.Status
Case Pokemon.StatusProblems.Poison, Pokemon.StatusProblems.BadPoison
If TrainerHasItem(9, BattleScreen) = True Then
Return ProduceOppStep(9, -1)
End If
Case Pokemon.StatusProblems.Burn
If TrainerHasItem(10, BattleScreen) = True Then
Return ProduceOppStep(10, -1)
End If
Case Pokemon.StatusProblems.Freeze
If TrainerHasItem(11, BattleScreen) = True Then
Return ProduceOppStep(11, -1)
End If
Case Pokemon.StatusProblems.Sleep
If TrainerHasItem(12, BattleScreen) = True Then
Return ProduceOppStep(12, -1)
End If
Case Pokemon.StatusProblems.Paralyzed
If TrainerHasItem(13, BattleScreen) = True Then
Return ProduceOppStep(13, -1)
End If
End Select
End If
End If
End If
'Use revive when at least 50% of team is beaten, use on highest leveled pokemon (max over normal) (50%)
If RPercent(50) = True And (TrainerHasItem(39, BattleScreen) = True Or TrainerHasItem(40, BattleScreen) = True) = True Then
Dim beaten As Integer = 0
For Each TeamP As Pokemon In BattleScreen.Trainer.Pokemons
If TeamP.HP <= 0 Or TeamP.Status = Pokemon.StatusProblems.Fainted Or TeamP.IsEgg() = True Then
beaten += 1
End If
Next
If beaten >= CInt(Math.Floor(BattleScreen.Trainer.Pokemons.Count / 2)) Then
Dim highestLevel As Integer = -1
For i = 0 To BattleScreen.Trainer.Pokemons.Count - 1
Dim TeamP As Pokemon = BattleScreen.Trainer.Pokemons(i)
If TeamP.IsEgg() = False Then
If TeamP.HP <= 0 Or TeamP.Status = Pokemon.StatusProblems.Fainted Then
If highestLevel = -1 OrElse BattleScreen.Trainer.Pokemons(highestLevel).Level < TeamP.Level Then
highestLevel = i
End If
End If
End If
Next
If highestLevel > -1 Then
Dim bestRevive As Integer = GetBestRevive(BattleScreen)
Return ProduceOppStep(bestRevive, highestLevel)
End If
End If
End If
'-------------------------------------Moves---------------------------------------------------------------------------------------'
'If own pokemon is asleep, try to use Sleep Talk (100%)
If p.Status = Pokemon.StatusProblems.Sleep And BattleScreen.FieldEffects.OppSleepTurns > 1 And HasMove(m, 214) = True Then
Return ProduceOppStep(m, IDtoMoveIndex(m, 214))
End If
'If own pokemon is asleep, try to use Snore (100%)
If p.Status = Pokemon.StatusProblems.Sleep And BattleScreen.FieldEffects.OppSleepTurns > 1 And HasMove(m, 173) = True Then
Return ProduceOppStep(m, IDtoMoveIndex(m, 173))
End If
'If own pokemon is frozen and has a move to thraw out -> use that move
If p.Status = Pokemon.StatusProblems.Freeze Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.ThrawOut)
If chosenMove > -1 Then
Return ProduceOppStep(m, chosenMove)
End If
End If
'Fake Out if first turn -> try to inflict flinch (100%)
If HasMove(m, 252) = True Then
If op.Ability.Name.ToLower() <> "inner focus" Then
Dim turns As Integer = BattleScreen.FieldEffects.OppPokemonTurns
If turns = 0 Then
Return ProduceOppStep(m, IDtoMoveIndex(m, 252))
End If
End If
End If
'use high priority move if opponent is nearly defeated (<= 15%) (100%)
If op.HP <= CInt((op.MaxHP / 100) * 15) Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.HighPriority)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
'use attacking move if speed is higher than opp speed and opponent has low health (<= 30%) (75%)
If p.Speed > op.Speed And op.HP <= CInt((op.MaxHP / 100) * 30) Then
Dim chosenMove As Integer = GetAttackingMove(BattleScreen, m)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
'use recover moves when HP lower than 50% (50%):
If p.HP <= CInt(p.MaxHP / 2) Then
Dim chance As Integer = 50
'make chance higher when opp has a status condition:
If op.Status = Pokemon.StatusProblems.Freeze Or op.Status = Pokemon.StatusProblems.Paralyzed Or op.HasVolatileStatus(Pokemon.VolatileStatus.Confusion) = True Then
chance = 75
End If
If op.Status = Pokemon.StatusProblems.Sleep Then
chance = 100
End If
If RPercent(chance) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.Healing)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'Try to use Brick Break move if opponent has LightScreen/Reflect up:
If op.IsType(Element.Types.Ghost) = False Then
If BattleScreen.FieldEffects.OwnReflect > 0 Or BattleScreen.FieldEffects.OwnLightScreen > 0 Then
If RPercent(80) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.RemoveReflectLightscreen)
If chosenMove > -1 Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'try to inflict status:
'check if substitute is up:
If BattleScreen.FieldEffects.OwnSubstitute = 0 Then
'check no status already applies:
If op.Status = Pokemon.StatusProblems.None Then
'try to paralyse (75%)
If op.Status <> Pokemon.StatusProblems.Paralyzed Then
If op.Type1.Type <> Element.Types.Electric And op.Type2.Type <> Element.Types.Electric Then
If op.Ability.Name.ToLower() <> "limber" Then
If RPercent(75) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.Paralysis)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
End If
End If
'try to burn (75%)
If op.Status <> Pokemon.StatusProblems.Burn Then
If op.Type1.Type <> Element.Types.Fire And op.Type2.Type <> Element.Types.Fire Then
If op.Ability.Name.ToLower() <> "water veil" Then
If RPercent(75) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.Burn)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
End If
End If
'try to sleep (75%)
If op.Status <> Pokemon.StatusProblems.Sleep Then
If op.Ability.Name.ToLower() <> "vital spirit" Then
If op.Ability.Name.ToLower() <> "insomnia" Then
If RPercent(75) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.Sleep)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
End If
End If
'try to poison (50%)
If op.Status <> Pokemon.StatusProblems.BadPoison And op.Status <> Pokemon.StatusProblems.Poison Then
If op.Type1.Type <> Element.Types.Steel And op.Type1.Type <> Element.Types.Poison And op.Type2.Type <> Element.Types.Steel And op.Type2.Type <> Element.Types.Poison Then
If op.Ability.Name.ToLower() <> "immunity" Then
If RPercent(50) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.Poison)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
End If
End If
End If
End If
'try to confuse (75%)
'check if opp isnt confused already:
If op.HasVolatileStatus(Pokemon.VolatileStatus.Confusion) = False Then
If op.Ability.Name.ToLower() <> "own tempo" Then
If RPercent(75) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.Confusion)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
End If
'try to set up leech seed (75%)
If op.IsType(Element.Types.Grass) = False Then
If BattleScreen.FieldEffects.OwnLeechSeed = 0 Then
If HasMove(m, 73) = True Then
If RPercent(75) = True Then
Return ProduceOppStep(m, IDtoMoveIndex(m, 73))
End If
End If
End If
End If
'try to use FocusEnergy (50%)
If BattleScreen.FieldEffects.OppFocusEnergy = 0 Then
If HasMove(m, 116) = True Then
If RPercent(50) = True Then
Return ProduceOppStep(m, IDtoMoveIndex(m, 116))
End If
End If
End If
'higher sp atk than atk, not boosted stat, higher spatk than spdef: use sp atk boost (50%)
If p.SpAttack > p.Attack And p.StatSpAttack <= 0 And p.SpAttack > p.SpDefense Then
If RPercent(50) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.RaiseSpAttack)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'higher atk than sp atk, not boosted stat, higher atk than def: use atk boost (50%)
If p.Attack > p.SpAttack And p.StatAttack <= 0 And p.Attack > p.Defense Then
If RPercent(50) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.RaiseAttack)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'not lowered opp def stat, higher atk than spatk, higher def than atk: use def lowering move (50%)
If p.Attack > p.SpAttack And p.Defense > p.Attack And op.StatDefense >= 0 Then
If RPercent(50) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.LowerDefense)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'not lowered opp spdef stat, higher spatk than atk, higher spdef than spatk: use spdef lowering move (50%)
If p.SpAttack > p.Attack And p.SpDefense > p.SpAttack And op.StatSpDefense >= 0 Then
If RPercent(50) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.LowerSpDefense)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'when opp has lower atk than def/lower spatk than spdef and can heal stat: use stat healing move (50%)
If op.SpAttack > op.Attack And op.SpAttack < op.SpDefense Or op.Attack > op.SpAttack And op.Attack < op.Defense Then
If p.Status = Pokemon.StatusProblems.BadPoison Or p.Status = Pokemon.StatusProblems.Burn Or p.Status = Pokemon.StatusProblems.Freeze Or p.Status = Pokemon.StatusProblems.Paralyzed Or p.Status = Pokemon.StatusProblems.Poison Or p.Status = Pokemon.StatusProblems.Sleep Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.CureStatus)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'if not done before, boost own evasion (50%)
If p.Evasion <= 0 Then
If RPercent(50) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.RaiseEvasion)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'if not done before, boost own accuracy (75%)
If p.Evasion <= 0 Then
If RPercent(75) = True Then
Dim chosenMove As Integer = MoveAI(m, Attack.AIField.RaiseEvasion)
If chosenMove > -1 Then
If CheckForTypeIneffectiveness(BattleScreen, m, chosenMove) = True Then
Return ProduceOppStep(m, chosenMove)
End If
End If
End If
End If
'Use LightScreen/Reflect (75%):
If RPercent(75) = True Then
If HasMove(m, 113) = True And op.SpAttack > op.Attack And BattleScreen.FieldEffects.OppLightScreen = 0 Then
Return ProduceOppStep(m, IDtoMoveIndex(m, 113))
End If
If HasMove(m, 115) = True And op.Attack > op.SpAttack And BattleScreen.FieldEffects.OppReflect = 0 Then
Return ProduceOppStep(m, IDtoMoveIndex(m, 115))
End If
End If
'Special Moveset combos:
' - Defense Curl + Rollout
If HasMove(m, 205) = True And HasMove(m, 111) = True Then
If BattleScreen.FieldEffects.OppDefenseCurl = 0 Then
Return ProduceOppStep(m, IDtoMoveIndex(m, 111))
Else
Return ProduceOppStep(m, IDtoMoveIndex(m, 205))
End If
End If
'Determine best attacking move:
Dim attackDic As New Dictionary(Of Integer, Integer)
For i = 0 To m.Count - 1
If MoveHasAIField(m(i), Attack.AIField.Damage) = True Then
attackDic.Add(i, 0)
End If
Next
'If has more than 0 attacking moves:
If attackDic.Count > 0 Then
For i = 0 To attackDic.Count - 1
Dim key As Integer = attackDic.Keys(i)
Dim cMove As Attack = m(key)
Dim value As Integer = 0
Dim effectiveness As Single = BattleCalculation.CalculateEffectiveness(False, cMove, BattleScreen)
'Base power
If effectiveness <> 0.0F Then
value += CInt(cMove.GetBasePower(False, BattleScreen) * effectiveness)
End If
'Accuracy
value += cMove.GetAccuracy(False, BattleScreen)
'STAB
If p.IsType(cMove.Type.Type) = True Then
value += 35
End If
'Ignore PP left for now...
'Attack stats:
If cMove.Category = Attack.Categories.Physical Then
value += p.StatAttack * 15
Else 'Special
value += p.StatSpAttack * 15
End If
If cMove.UseOppDefense = True Then
If cMove.Category = Attack.Categories.Physical Then
value -= op.StatDefense * 15
Else 'Special
value -= p.StatSpDefense * 15
End If
End If
'Dry skin: +fire
If cMove.GetAttackType(False, BattleScreen).Type = Element.Types.Fire And op.Ability.Name.ToLower() = "dry skin" Then
value += 25
End If
'Use never-miss attack when own accuracy is low/opp evasion is high
If cMove.Accuracy = 0 Or cMove.GetUseAccEvasion(False, BattleScreen) = False Then
If p.Accuracy < 0 Then
value += CInt(Math.Abs(p.Accuracy * 15))
End If
If op.Evasion > 0 Then
value += op.Evasion * 15
End If
End If
'If the pokemon has other options, dont use selfdestruct or explosion too often:
If cMove.ID = 120 Or cMove.ID = 153 Then
If Core.Random.Next(0, 8) <> 0 Then
value = Core.Random.Next(50, 100)
End If
End If
'Add randomness value:
value += Core.Random.Next(-35, 35)
If value < 0 Then
value = 0
End If
'Effectiveness:
If effectiveness = 0.0F Then
value = 0
End If
'Don't use moves that get absorbed by abilities:
If cMove.GetAttackType(False, BattleScreen).Type = Element.Types.Water And op.Ability.Name.ToLower() = "water absorb" Then
value = 0
End If
If cMove.GetAttackType(False, BattleScreen).Type = Element.Types.Electric And op.Ability.Name.ToLower() = "volt absorb" Then
value = 0
End If
If cMove.GetAttackType(False, BattleScreen).Type = Element.Types.Electric And op.Ability.Name.ToLower() = "motor drive" Then
value = 0
End If
If cMove.GetAttackType(False, BattleScreen).Type = Element.Types.Grass And op.Ability.Name.ToLower() = "sap sipper" Then
value = 0
End If
If cMove.GetAttackType(False, BattleScreen).Type = Element.Types.Fire And op.Ability.Name.ToLower() = "flash fire" Then
value = 0
End If
If cMove.GetAttackType(False, BattleScreen).Type = Element.Types.Water And op.Ability.Name.ToLower() = "dry skin" Then
value = 0
End If
'Special moves to not use:
If cMove.ID = 150 Then 'Never use splash
value = 0
End If
If cMove.ID = 214 And p.Status <> Pokemon.StatusProblems.Sleep Then 'Never use Sleep Talk when user is not sleeping.
value = 0
End If
If cMove.ID = 171 And op.Status <> Pokemon.StatusProblems.Sleep Then 'Never use nightmare when opponent is not sleeping.
value = 0
End If
If cMove.ID = 138 And op.Status <> Pokemon.StatusProblems.Sleep Then 'Never use Dream eater when opponent is not sleeping
value = 0
End If
attackDic(key) = value
Next
'Debug feature: Enable if not sure
'Logger.Debug("RESULTS:")
'For t = 0 To attackDic.Count - 1
' Logger.Debug(t.ToString() & ": " & m(attackDic.Keys(t)).Name & "; VALUE: " & attackDic.Values(t))
'Next
Dim index As Integer = 0
If attackDic.Count > 1 Then
If attackDic.Values(1) > attackDic.Values(index) Then
index = 1
End If
End If
If attackDic.Count > 2 Then
If attackDic.Values(2) > attackDic.Values(index) Then
index = 2
End If
End If
If attackDic.Count > 3 Then
If attackDic.Values(3) > attackDic.Values(index) Then
index = 3
End If
End If
'Dis too:
'Logger.Debug("CHOSEN: " & m(attackDic.Keys(index)).Name & "; VALUE: " & attackDic.Values(index))
Return ProduceOppStep(m, attackDic.Keys(index))
End If
'once everything else failed, choose one of the attacking moves:
Dim chosenAttackMove As Integer = MoveAI(m, Attack.AIField.Damage)
If chosenAttackMove > -1 Then
Return ProduceOppStep(m, chosenAttackMove)
End If
'catch crash: return random move:
Return ProduceOppStep(m, Core.Random.Next(0, m.Count))
End Function
Private Shared Function HasOtherAttackingMoveThanExplosion(ByVal m As List(Of Attack), ByVal leaveOutIndex As Integer) As Boolean
For i = 0 To m.Count - 1
If i <> leaveOutIndex Then
If m(i).ID <> 121 And m(i).ID <> 153 Then
Return True
End If
End If
Next
Return False
End Function
Private Shared Function GetAttackingMove(ByVal BattleScreen As BattleScreen, ByVal m As List(Of Attack)) As Integer
Return 0
End Function
Private Shared Function CheckForTypeIneffectiveness(ByVal BattleScreen As BattleScreen, ByVal m As List(Of Attack), ByVal i As Integer) As Boolean
Dim move As Attack = m(i)
If move.ImmunityAffected = True Then
Dim effectiveness As Single = BattleCalculation.CalculateEffectiveness(False, move, BattleScreen)
If effectiveness = 0.0F Then
Return False
End If
End If
Return True
End Function
Private Shared Function RPercent(ByVal p As Integer) As Boolean
If p >= 100 Then
Return True
End If
If Core.Random.Next(0, 100) < p Then
Return True
End If
Return False
End Function
Private Shared Function MoveAI(ByVal m As List(Of Attack), ByVal AIType As Attack.AIField) As Integer
Dim validMoves As New List(Of Integer)
For i = 0 To m.Count - 1
If m(i).AIField1 = AIType Or m(i).AIField2 = AIType Or m(i).AIField3 = AIType Then
validMoves.Add(i)
End If
Next
If validMoves.Count > 0 Then
Return validMoves(Core.Random.Next(0, validMoves.Count))
End If
Return -1
End Function
Private Shared Function MoveHasAIField(ByVal m As Attack, ByVal AIType As Attack.AIField) As Boolean
Return m.AIField1 = AIType Or m.AIField2 = AIType Or m.AIField3 = AIType
End Function
Private Shared Function HasMove(ByVal m As List(Of Attack), ByVal ID As Integer) As Boolean
For Each move As Attack In m
If move.ID = ID Then
Return True
End If
Next
Return False
End Function
Private Shared Function IDtoMoveIndex(ByVal m As List(Of Attack), ByVal ID As Integer) As Integer
For i = 0 To m.Count - 1
If m(i).ID = ID Then
Return i
End If
Next
Return -1
End Function
#Region "ProduceSteps"
Private Shared Function ProduceOppStep(ByVal m As List(Of Attack), ByVal i As Integer) As Battle.RoundConst
While i > m.Count - 1
i -= 1
End While
If m.Count = 0 Then
Throw New Exception("An empty move array got passed in!")
End If
Return New Battle.RoundConst With {.StepType = Battle.RoundConst.StepTypes.Move, .Argument = m(i)}
End Function
Private Shared Function ProduceOppStep(ByVal ItemID As Integer, ByVal target As Integer) As Battle.RoundConst
Return New Battle.RoundConst With {.StepType = Battle.RoundConst.StepTypes.Item, .Argument = ItemID.ToString() & "," & target.ToString()}
End Function
Private Shared Function ProduceOppStep(ByVal SwitchID As Integer) As Battle.RoundConst
Return New Battle.RoundConst With {.StepType = Battle.RoundConst.StepTypes.Switch, .Argument = SwitchID.ToString()}
End Function
#End Region
#Region "Item"
Private Shared Function TrainerHasItem(ByVal ItemID As Integer, ByVal BattleScreen As BattleScreen) As Boolean
For Each Item As Item In BattleScreen.Trainer.Items
If Item.ID = ItemID Then
Return True
End If
Next
Return False
End Function
Private Shared Function GetBestPotion(ByVal BattleScreen As BattleScreen) As Integer
Dim potionRange As List(Of Integer) = {18, 17, 16, 15, 14}.ToList()
Dim bestPotion As Integer = -1
For Each I As Item In BattleScreen.Trainer.Items
If potionRange.Contains(I.ID) = True Then
If potionRange.IndexOf(I.ID) > bestPotion Then
bestPotion = potionRange.IndexOf(I.ID)
End If
End If
Next
If bestPotion = -1 Then
Return -1
Else
Return potionRange(bestPotion)
End If
End Function
Private Shared Function GetBestRevive(ByVal BattleScreen As BattleScreen) As Integer
For Each Item As Item In BattleScreen.Trainer.Items
If Item.ID = 40 Then
Return 40
End If
Next
Return 39
End Function
Private Shared Function GetPotionHealHP(ByVal p As Pokemon, ByVal ItemID As Integer) As Integer
Select Case ItemID
Case 18
Return 20
Case 17
Return 50
Case 16
Return 200
Case 15
Return p.MaxHP
Case 14
Return p.MaxHP
End Select
Return 0
End Function
#End Region
Private Shared Function GetPokemonValue(ByVal BattleScreen As BattleScreen, ByVal p As Pokemon) As Integer
Dim total As Integer = 0
For Each TeamP As Pokemon In BattleScreen.Trainer.Pokemons
total += TeamP.BaseHP + TeamP.BaseAttack + TeamP.BaseDefense + TeamP.BaseSpAttack + TeamP.BaseSpDefense + TeamP.BaseSpeed
Next
Dim pTotal As Integer = p.BaseHP + p.BaseAttack + p.BaseDefense + p.BaseSpAttack + p.BaseSpDefense + p.BaseSpeed
Dim percent As Integer = CInt((pTotal / total) * 100)
Return percent
End Function
End Class
End Namespace