• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Better AI system

#Not Important

All hail the wishmaker
910
Posts
4
Years
  • Code:
    #===============================================================================
    # ** Better AI
    # ** By #Not Important
    #===============================================================================
    =begin
    Changes:
      - There is now an AI class for over 200 skill, beast mode.
    	- Mega Evolution will only be used if:
        ~ One of the AI's moves is super effective
        ~ The opponent is on low HP (1/3)
      - The switching out for AI is now *much* more sophisticated, here are a few
        things I did:
        ~ If the user has a priority move, stay in
        ~ If the user is faster than the opponent and has a super effective move,
          stay in
    		~ If the opponent is in the middle of a two-turn move, and cannot attack,
          stay in.
        ~ If the user is in the last turn of perish song, switch
        ~ I did more stuff but cannot be bothered to document it all here
      - Moves are NOT chosen as a possibility if they are not:
    		~ Priority
    		~ Super effective
        ~ Powerful
      - If no moves fit the above conditions, choose a random one
    =end
    MEGAEVOMETHOD = 1 #if its 1, it will start as false and run checks to make sure it needs to, if 2, the opposite
    SPIRIT_POWERS = false
    #-------------------------------------------------------------------------------
    # AI skill levels:
    #     0:     Wild Pokémon
    #     1-31:  Basic trainer (young/inexperienced)
    #     32-47: Some skill
    #     48-99: High skill
    #     100+:  Best trainers (Gym Leaders, Elite Four, Champion)
    # NOTE: A trainer's skill value can range from 0-255, but by default only four
    #       distinct skill levels exist. The skill value is typically the same as
    #       the trainer's base money value.
    module PBTrainerAI
    	# Minimum skill level to be in each AI category.
    	def self.minimumSkill; return 1;   end
    	def self.mediumSkill;  return 32;  end
    	def self.highSkill;    return 48;  end
    	def self.bestSkill;    return 100; end
    	def self.beastMode;    return 200; end
    end
    $nextMove   = nil
    $nextTarget = nil
    $nextQue    = 0
    def superEffective?(battler1,battler2)
    	types = [battler2.type1,battler2.type2]
    	mod = 0
    	battler1.moves do |m|
    		types do |t|
    			if PurifyChamberSet.isSuperEffective(m,t)
    				mod += 1
    			else
    				mod -= 1
    			end
    		end
    	end
    	if mod >= 1
    		return true
    	end
    	return false
    end
    def getSkill(battler,battle)
      begin
        s = @battle.pbGetOwnerFromBattlerIndex(battler).skill
        if !s
          s = 20
        end
        return s
      rescue
        return 20
      end
    end
    class PokeBattle_AI
    	def initialize(battle)
    		@battle = battle
    	end
    	
    	def pbAIRandom(x); return rand(x); end
    	
    	def pbStdDev(choices)
    		sum = 0
    		n   = 0
    		choices.each do |c|
    			sum += c[1]
    			n   += 1
    		end
    		return 0 if n<2
    		mean = sum.to_f/n.to_f
    		varianceTimesN = 0
    		choices.each do |c|
    			next if c[1]<=0
    			deviation = c[1].to_f-mean
    			varianceTimesN += deviation*deviation
    		end
    		# Using population standard deviation 
    		# [(n-1) makes it a sample std dev, would be 0 with only 1 sample]
    		return Math.sqrt(varianceTimesN/n)
    	end
    	
    	#=============================================================================
    	# Decide whether the opponent should Mega Evolve their Pokémon
    	#=============================================================================
    	def pbEnemyShouldMegaEvolve?(idxBattler)
    		battler = @battle.battlers[idxBattler]
    		opposing = []
        for i in @battle.battlers
          if i != battler
            if i
              if not(i.fainted?)
                if i.opposes?
                  opposing.push(i)
                end
              end
            end
          end
        end
    		moves = battler.moves
    		if MEGAEVOMETHOD==1
          should = false
        else
          should = true
        end
    		move   = false
        skill  = getSkill(idxBattler,@battler)
        #if @battle.pbGetOwnerFromBattlerIndex(idxBattler).skill
        #  skill = @battle.pbGetOwnerFromBattlerIndex(idxBattler).skill
        #end
    		battler.moves do |move|
    			opposing do |o|
    				baseDmg = pbMoveBaseDamage(move,battler,o,skill)
    				if pbRoughDamage(move,battler,o,skill,baseDmg) >= o.hp
    					move = false
    					$nextTarget = o
    					$nextMove = move
    					$nextQue = 1 
    				end
    			end
    		end
    		for o in opposing
    			if superEffective?(battler,o)
    				move = true
    			end 
    		end
    		for o in opposing
    			if o.hp <= (o.totalhp/3).floor
    				should = true
    			end
    		end
    		if move
    			should = true
    		end
    		if should && @battle.pbCanMegaEvolve?(idxBattler)
    			PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will Mega Evolve")
    			return true
    		end
    		return false
    	end
    	
    	#=============================================================================
    	# Choose an action
    	#=============================================================================
    	def pbDefaultChooseEnemyCommand(idxBattler)
    		return if pbEnemyShouldUseItem?(idxBattler)
    		return if pbEnemyShouldWithdraw?(idxBattler)
    		return if @battle.pbAutoFightMenu(idxBattler)
    		@battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve?(idxBattler)
    		if SPIRIT_POWERS
    			@battle.pbRegisterSpiritPower(idxBattler) if pbEnemyShouldUseSpiritPower?(idxBattler)
    		end
        if pbChooseMoves(idxBattler)=="switch"
          return
        end
        pbChooseMoves(idxBattler)
    	end
    end
    
    
    #-------------------------------------------------------------------------------
    # Switching pkmn
    class PokeBattle_AI
    	#=============================================================================
    	# Decide whether the opponent should switch Pokémon
    	#=============================================================================
    	def pbEnemyShouldWithdraw?(idxBattler)
    		return pbEnemyShouldWithdrawEx?(idxBattler,false)
    	end
    	
    	def shouldSwitchHandler(idxBattler,battler,opps)
        battler = @battle.battlers[idxBattler]
    		skill = getSkill(idxBattler,@battle)
        #@battle.pbGetOwnerFromBattlerIndex(idxBattler).skill rescue 0
    		moves = battler.moves
    		hp = battler.hp
    		thp = battler.totalhp
    #		opps = battler.eachOpposing
    		move_pri = false
    		move_super = false
    		faster = false
    		opp_move_pri = false
        higherhp = false
    		battler.moves do |m|
    			if m.priority>0
    				move_pri = true
    			end
    			opps do |o|
    				if PurifyChamberSet.isSuperEffective(m.type,o.type1)
    					move_super = true
    				end
    				if o.type2
    					if PurifyChamberSet.isSuperEffective(m.type,o.type2)
    						move_super = true
    					end
    				end
    				if battler.stats[PBStats::SPEED]>o.stats[PBStats::SPEED] && battler.status != PBStatuses::PARALYSIS
    					faster = true
    				end
    				oppmoves = o.moves
    				oppmoves do |om|
    					if om.priority>0
    						opp_move_pri = true
    					end
    				end
    				if hp > o.hp
    					higherhp = true
    				else
    					higherhp = false
    				end
    				if @battle.pbSideSize(battler.index+1)==1 && !(battler.pbDirectOpposing.fainted?) && skill>=PBTrainerAI.highSkill
    					opp = battler.pbDirectOpposing
    					if opp.effects[PBEffects::HyperBeam]>0 ||
    						(opp.hasActiveAbility?(:TRUANT) && opp.effects[PBEffects::Truant])
    						hyper = true
    					end
    				end
    			end
    		end
    		if move_pri && !opp_move_pri
    			return false
    		end
    		if skill >= PBTrainerAI.mediumSkill
    			if move_super && faster
    				return false
    			end
    		end
    		if skill >= PBTrainerAI.highSkill
    			if (higherhp && faster) || (higherhp && move_pri) || (higherhp && faster && move_super)
    				return false
    			end
    		end
    		if skill >= PBTrainerAI.bestSkill
    			if battler.effects[PBEffects::PerishSong]==1
    				return true
    			end
    			if hyper
    				return false
    			end
    		end
    		if skill >= PBTrainerAI.beastMode
    			if battler.effects[PBEffects::Encore]>0
    				idxEncoredMove = battler.pbEncoredMoveIndex
    				if idxEncoredMove>=0
    					scoreSum   = 0
    					scoreCount = 0
    					battler.eachOpposing do |b|
    						scoreSum += pbGetMoveScore(battler.moves[idxEncoredMove],battler,b,skill)
    						scoreCount += 1
    					end
    					if scoreCount>0 && scoreSum/scoreCount<=20
    						return false
    					end
    				end
    			end
    			if battler.status==PBStatuses::POISON && battler.statusCount>0
    				toxicHP = battler.totalhp/16
    				nextToxicHP = toxicHP*(battler.effects[PBEffects::Toxic]+1)
    				if battler.hp<=nextToxicHP && battler.hp>toxicHP*2
    					return true
    				end
    			end
    		end
    		return false
    	end
    	
    	def pbEnemyShouldWithdrawEx?(idxBattler,forceSwitch)
    		return false if @battle.wildBattle?
    		if forceSwitch
    			shouldSwitch = forceSwitch
    		end
    		batonPass = -1
    		moveType = -1
    		skill = getSkill(idxBattler,@battle)
        #@battle.pbGetOwnerFromBattlerIndex(idxBattler).skill rescue 0
    		battler = @battle.battlers[idxBattler]
        opps = []
        for i in @battle.battlers
          if i != battler
            if i
              if not(i.fainted?)
                if i.opposes?
                  opps.push(i)
                end
              end
            end
          end
        end
    		#I removed all this bc it's handled in the shouldSwitchHandler def
    		shouldSwitch = shouldSwitchHandler(idxBattler,battler,opps)
    		if shouldSwitch
    			list = []
    			@battle.pbParty(idxBattler).each_with_index do |pkmn,i|
    				next if [email protected]?(idxBattler,i)
    				# If perish count is 1, it may be worth it to switch
    				# even with Spikes, since Perish Song's effect will end
    				if battler.effects[PBEffects::PerishSong]!=1
    					# Will contain effects that recommend against switching
    					spikes = battler.pbOwnSide.effects[PBEffects::Spikes]
    					# Don't switch to this if too little HP
    					if spikes>0
    						spikesDmg = [8,6,4][spikes-1]
    						if pkmn.hp<=pkmn.totalhp/spikesDmg
    							next if !pkmn.hasType?(:FLYING) && !pkmn.hasActiveAbility?(:LEVITATE)
    						end
    					end
    				end
    				# moveType is the type of the target's last used move
    				if moveType>=0 && PBTypes.ineffective?(pbCalcTypeMod(moveType,battler,battler))
    					weight = 65
    					typeMod = pbCalcTypeModPokemon(pkmn,battler.pbDirectOpposing(true))
    					if PBTypes.superEffective?(typeMod.to_f/PBTypeEffectivenesss::NORMAL_EFFECTIVE)
    						# Greater weight if new Pokemon's type is effective against target
    						weight = 85
    					end
    					list.unshift(i) if pbAIRandom(100)<weight   # Put this Pokemon first
    				elsif moveType>=0 && PBTypes.resistant?(pbCalcTypeMod(moveType,battler,battler))
    					weight = 40
    					typeMod = pbCalcTypeModPokemon(pkmn,battler.pbDirectOpposing(true))
    					if PBTypes.superEffective?(typeMod.to_f/PBTypeEffectivenesss::NORMAL_EFFECTIVE)
    						# Greater weight if new Pokemon's type is effective against target
    						weight = 60
    					end
    					list.unshift(i) if pbAIRandom(100)<weight   # Put this Pokemon first
    				else
    					list.push(i)   # put this Pokemon last
    				end
    			end
    			if list.length>0
    				if batonPass>=0 && @battle.pbRegisterMove(idxBattler,batonPass,false)
    					PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will use Baton Pass to avoid Perish Song")
    					return true
    				end
    				if @battle.pbRegisterSwitch(idxBattler,list[0])
    					PBDebug.log("[AI] #{battler.pbThis} (#{idxBattler}) will switch with " +
    						"#{@battle.pbParty(idxBattler)[list[0]].name}")
    					return 
    				end
    			end
    		end
    		return false
    	end
    	
    	#=============================================================================
    	# Choose a replacement Pokémon
    	#=============================================================================
    	def pbDefaultChooseNewEnemy(idxBattler,party)
    		enemies = []
    		party.each_with_index do |p,i|
    			enemies.push(i) if @battle.pbCanSwitchLax?(idxBattler,i)
    		end
    		return -1 if enemies.length==0
    		return pbChooseBestNewEnemy(idxBattler,party,enemies)
    	end
    	
    	def pbChooseBestNewEnemy(idxBattler,party,enemies)
    		return -1 if !enemies || enemies.length==0
    		best    = -1
    		bestSum = 0
    		movesData = pbLoadMovesData
    		enemies.each do |i|
    			pkmn = party[i]
    			sum  = 0
    			pkmn.moves.each do |m|
    				next if m.id==0
    				moveData = movesData[m.id]
    				next if moveData[MOVE_BASE_DAMAGE]==0
    				@battle.battlers[idxBattler].eachOpposing do |b|
    					bTypes = b.pbTypes(true)
    					sum += PBTypes.getCombinedEffectiveness(moveData[MOVE_TYPE],
    						bTypes[0],bTypes[1],bTypes[2])
    				end
    			end
    			if best==-1 || sum>bestSum
    				best = i
    				bestSum = sum
    			end
    		end
    		return best
    	end
    end
    #===============================================================================
    # * Attacks
    class PokeBattle_AI
    	#=============================================================================
    	# Main move-choosing method
    	#=============================================================================
    	def pbChooseMoves(idxBattler)
    		user        = @battle.battlers[idxBattler]
    		wildBattler = (@battle.wildBattle? && @battle.opposes?(idxBattler))
    		skill       = 0
    		if !wildBattler
    			skill     = getSkill(user.index,@battle)
          #@battle.pbGetOwnerFromBattlerIndex(user.index).skill || 0
    		end
    		# Get scores and targets for each move
    		# NOTE: A move is only added to the choices array if it has a non-zero
    		#       score.
    		choices     = []
    		user.eachMoveWithIndex do |m,i|
    			next if [email protected]?(idxBattler,i,false)
    			if wildBattler
    				pbRegisterMoveWild(user,i,choices)
    			else
    				pbRegisterMoveTrainer(user,i,choices,skill)
    			end
    		end
    		# Figure out useful information about the choices
    		totalScore = 0
    		maxScore   = 0
    		choices.each do |c|
    			totalScore += c[1]
    			maxScore = c[1] if maxScore<c[1]
    		end
    		# Log the available choices
    		if $INTERNAL
    			logMsg = "[AI] Move choices for #{user.pbThis(true)} (#{user.index}): "
    			choices.each_with_index do |c,i|
    				logMsg += "#{user.moves[c[0]].name}=#{c[1]}"
    				logMsg += " (target #{c[2]})" if c[2]>=0
    				logMsg += ", " if i<choices.length-1
    			end
    			PBDebug.log(logMsg)
    		end
    		# Find any preferred moves and just choose from them
    		if !wildBattler && skill>=PBTrainerAI.highSkill && maxScore>100
    			stDev = pbStdDev(choices)
    			if stDev>=40
    				preferredMoves = []
    				choices.each do |c|
    					next if c[1]<200 && c[1]<maxScore*0.8
    					if c.priority != 0
    						preferredMoves.push(c)
    						preferredMoves.push(c) if user.hp <= (user.totalhp/3).floor
    					end
    					user.eachOpposing do |o|
    						superEffective = superEffective?(c,o)
    						if superEffective
    							prefferedMoves.push(c)
    						end
    					end
    					# preferredMoves.push(c) No. Bad moves should not be added to possible moves
    					preferredMoves.push(c) if c[1]==maxScore   # Doubly prefer the best move
    				end
    				if preferredMoves.length == 0
    					choices.each do |move|
    						prefferedMoves.push(move) #choose rand move bc all bad
    					end
    				end
            opposing = []
            for i in @battle.battlers
              if i != battler
                if i
                  if not(i.fainted?)
                    if i.opposes?
                      opposing.push(i)
                    end
                  end
                end
              end
            end
            for move in prefferedMoves
              for opp in opposing
                if ((opp.type1 == move.type) or (opp.type2 == move.type))
                  prefferedMoves.delete(move) 
                end
              end
            end
    				if preferredMoves.length>0
    					m = preferredMoves[pbAIRandom(preferredMoves.length)]
    					PBDebug.log("[AI] #{user.pbThis} (#{user.index}) prefers #{user.moves[m[0]].name}")
    					@battle.pbRegisterMove(idxBattler,m[0],false)
    					@battle.pbRegisterTarget(idxBattler,m[2]) if m[2]>=0
    					return
    				end
    			end
    		end
    		# Decide whether all choices are bad, and if so, try switching instead
    		if !wildBattler && skill>=PBTrainerAI.highSkill
    			badMoves = false
    			if (maxScore<=20 && user.turnCount>2) ||
    				(maxScore<=40 && user.turnCount>5)
    				badMoves = true if pbAIRandom(100)<80
    			end
    			if !badMoves && totalScore<100 && user.turnCount>1
    				badMoves = true
    				choices.each do |c|
    					next if !user.moves[c[0]].damagingMove?
    					badMoves = false
    					break
    				end
    				badMoves = false if badMoves
    			end
    			if badMoves && pbEnemyShouldWithdrawEx?(idxBattler,true)
    				if $INTERNAL
    					PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will switch due to terrible moves lol. you should have better moves tbh")
    				end
    				return "switch"
    			end
    		end
    		battler = @battle.battlers[idxBattler]
    		moves = battler.moves
    		skill = getSkill(idxBattler,@battle)
        #@battle.pbGetOwnerFromBattlerIndex(idxBattler) rescue 0
    		battler.moves do |move|
    			for o in opposing
    				baseDmg = pbMoveBaseDamage(move,battler,o,skill)
    				if pbRoughDamage(move,battler,o,skill,baseDmg) >= o.hp
    					$nextTarget = o
    					$nextMove = move
    					$nextQue = 1 
    				end
    			end
    		end
    		# Randomly choose a move to use
    		if choices.length==0
    			# If there are no calculated choices, use Struggle (or an Encored move)
    			@battle.pbAutoChooseMove(idxBattler)
    		else
    			# Randomly choose a move from the choices and register it
    			if !($nextQue == 1)
    				randNum = pbAIRandom(totalScore)
    				choices.each do |c|
    					randNum -= c[1]
    					next if randNum>=0
    					@battle.pbRegisterMove(idxBattler,c[0],false)
    					@battle.pbRegisterTarget(idxBattler,c[2]) if c[2]>=0
    					break
    				end
    			else
    				@battle.pbRegisterMove(idxBattler,$nextMove,false)
    				@battle.pbRegisterTarget(idxBattler,$nextTarget) if $nextTarget>=0
    				$nextQue = 0
    			end
    		end
    		# Log the result
    		if @battle.choices[idxBattler][2]
    			PBDebug.log("[AI] #{user.pbThis} (#{user.index}) will use #{@battle.choices[user.index][2].name} boom boom you die")
    		end
      end
    	
    	#=============================================================================
    	# Get scores for the given move against each possible target
    	#=============================================================================
    	# Wild Pokémon choose their moves randomly.
    	# If you dont want this and want wild battles to be hard tell me		
    	def pbRegisterMoveWild(user,idxMove,choices)
    		choices.push([idxMove,100,-1])   # Move index, score, target
    	end
    	
    	# Trainer Pokémon calculate how much they want to use each of their moves.
    	def pbRegisterMoveTrainer(user,idxMove,choices,skill)
    		move = user.moves[idxMove]
    		targetType = move.pbTarget(user)
    		if PBTargets.multipleTargets?(targetType)
    			# If move affects multiple battlers and you don't choose a particular one
    			totalScore = 0
    			@battle.eachBattler do |b|
    				next if [email protected]?(user.index,b.index,targetType)
    				score = pbGetMoveScore(move,user,b,skill)
    				totalScore += ((user.opposes?(b)) ? score : -score)
    			end
    			choices.push([idxMove,totalScore,-1]) if totalScore>0
    		elsif PBTargets.noTargets?(targetType)
    			# If move has no targets, affects the user, a side or the whole field
    			score = pbGetMoveScore(move,user,user,skill)
    			choices.push([idxMove,score,-1]) if score>0
    		else
    			# If move affects one battler and you have to choose which one
    			scoresAndTargets = []
    			@battle.eachBattler do |b|
    				next if [email protected]?(user.index,b.index,targetType)
    				next if PBTargets.canChooseFoeTarget?(targetType) && !user.opposes?(b)
    				score = pbGetMoveScore(move,user,b,skill)
    				scoresAndTargets.push([score,b.index]) if score>0
    			end
    			if scoresAndTargets.length>0
    				# Get the one best target for the move
    				scoresAndTargets.sort! { |a,b| b[0]<=>a[0] }
    				choices.push([idxMove,scoresAndTargets[0][0],scoresAndTargets[0][1]])
    			end
    		end
    	end
    	
    	#=============================================================================
    	# Get a score for the given move being used against the given target
    	#=============================================================================
    	def pbGetMoveScore(move,user,target,skill=100)
    		skill = PBTrainerAI.minimumSkill if skill<PBTrainerAI.minimumSkill
    		score = 100
    		score = pbGetMoveScoreFunctionCode(score,move,user,target,skill)
    		# A score of 0 here means it absolutely should not be used
    		return 0 if score<=0
    		if skill>=PBTrainerAI.mediumSkill
    			# Prefer damaging moves if AI has no more Pokémon or AI is less clever
    			if @battle.pbAbleNonActiveCount(user.idxOwnSide)==0
    				if !(skill>=PBTrainerAI.highSkill && @battle.pbAbleNonActiveCount(target.idxOwnSide)>0)
    					if move.statusMove?
    						score /= 1.5
    					elsif target.hp<=target.totalhp/2
    						score *= 1.5
    					end
    				end
    			end
    			# Don't prefer attacking the target if they'd be semi-invulnerable
    			if skill>=PBTrainerAI.highSkill && move.accuracy>0 &&
    				(target.semiInvulnerable? || target.effects[PBEffects::SkyDrop]>=0)
    				miss = true
    				miss = false if user.hasActiveAbility?(:NOGUARD) || target.hasActiveAbility?(:NOGUARD)
    				if miss && pbRoughStat(user,PBStats::SPEED,skill)>pbRoughStat(target,PBStats::SPEED,skill)
    					# Knows what can get past semi-invulnerability
    					if target.effects[PBEffects::SkyDrop]>=0
    						miss = false if move.hitsFlyingTargets?
    					else
    						if target.inTwoTurnAttack?("0C9","0CC","0CE")   # Fly, Bounce, Sky Drop
    							miss = false if move.hitsFlyingTargets?
    						elsif target.inTwoTurnAttack?("0CA")          # Dig
    							miss = false if move.hitsDiggingTargets?
    						elsif target.inTwoTurnAttack?("0CB")          # Dive
    							miss = false if move.hitsDivingTargets?
    						end
    					end
    				end
    				score -= 80 if miss
    			end
    			# Pick a good move for the Choice items
    			if user.hasActiveItem?([:CHOICEBAND,:CHOICESPECS,:CHOICESCARF])
    				if move.baseDamage>=60;     score += 60
    				elsif move.damagingMove?;   score += 30
    				elsif move.function=="0F2"; score += 70   # Trick
    				else;                       score -= 60
    				end
    			end
    			# If user is asleep, prefer moves that are usable while asleep
    			if user.status==PBStatuses::SLEEP && !move.usableWhenAsleep?
    				hasSleepMove = false
    				user.eachMove do |m|
    					next unless m.usableWhenAsleep?
    					score -= 60
    					break
    				end
    			end
    			# If user is frozen, prefer a move that can thaw the user
    			if user.status==PBStatuses::FROZEN
    				if move.thawsUser?
    					score += 40
    				else
    					user.eachMove do |m|
    						next unless m.thawsUser?
    						score -= 60
    						break
    					end
    				end
    			end
    			# If target is frozen, don't prefer moves that could thaw them
    			if target.status==PBStatuses::FROZEN
    				user.eachMove do |m|
    					next if m.thawsUser?
    					score -= 60
    					break
    				end
    			end
    		end
    		# Adjust score based on how much damage it can deal
    		if move.damagingMove?
    			score = pbGetMoveScoreDamage(score,move,user,target,skill)
    		else   # Status moves
    			# Don't prefer attacks which don't deal damage
    			score -= 10
    			# Account for accuracy of move
    			accuracy = pbRoughAccuracy(move,user,target,skill)
    			score *= accuracy/100.0
    			score = 0 if score<=10 && skill>=PBTrainerAI.highSkill
    		end
    		score = score.to_i
    		score = 0 if score<0
    		return score
    	end
    	
    	#=============================================================================
    	# Add to a move's score based on how much damage it will deal (as a percentage
    	# of the target's current HP)
    	#=============================================================================
    	def pbGetMoveScoreDamage(score,move,user,target,skill)
    		# Don't prefer moves that are ineffective because of abilities or effects
    		return 0 if score<=0 || pbCheckMoveImmunity(score,move,user,target,skill)
    		# Calculate how much damage the move will do (roughly)
    		baseDmg = pbMoveBaseDamage(move,user,target,skill)
    		realDamage = pbRoughDamage(move,user,target,skill,baseDmg)
    		# Account for accuracy of move
    		accuracy = pbRoughAccuracy(move,user,target,skill)
    		realDamage *= accuracy/100.0
    		# Two-turn attacks waste 2 turns to deal one lot of damage
    		if move.chargingTurnMove? || move.function=="0C2"   # Hyper Beam
    			realDamage *= 2/3   # Not halved because semi-invulnerable during use or hits first turn
    		end
    		# Prefer flinching external effects (note that move effects which cause
    		# flinching are dealt with in the function code part of score calculation)
    		if skill>=PBTrainerAI.mediumSkill
    			if !target.hasActiveAbility?(:INNERFOCUS) &&
    				!target.hasActiveAbility?(:SHIELDDUST) &&
    				target.effects[PBEffects::Substitute]==0
    				canFlinch = false
    				if move.canKingsRock? && user.hasActiveItem?([:KINGSROCK,:RAZORFANG])
    					canFlinch = true
    				end
    				if user.hasActiveAbility?(:STENCH) && !move.flinchingMove?
    					canFlinch = true
    				end
    				realDamage *= 1.3 if canFlinch
    			end
    		end
    		# Convert damage to percentage of target's remaining HP
    		damagePercentage = realDamage*100.0/target.hp
    		# Don't prefer weak attacks
    		#    damagePercentage /= 2 if damagePercentage<20
    		# Prefer damaging attack if level difference is significantly high
    		damagePercentage *= 1.2 if user.level-10>target.level
    		# Adjust score
    		damagePercentage = 120 if damagePercentage>120   # Treat all lethal moves the same
    		damagePercentage += 40 if damagePercentage>100   # Prefer moves likely to be lethal
    		score += damagePercentage.to_i
    		return score
    	end
    end
    #------------------------------------------------------------------------------#
    #                            Thanks for using!																 #
    #------------------------------------------------------------------------------#	
    PluginManager.register({                                                 
    		:name    => "Better AI",                             
    		:version => "2.0",                                   
    		:link    => "https://www.pokecommunity.com/showthread.php?t=442787",             
    		:credits => ["#Not Important"]
    })
     
    Last edited:

    Boonzeet

    Pokémon Secrets of the Ages Developer
    188
    Posts
    15
    Years
  • This sounds pretty great! I just have one question regarding the first section:

    Code:
      - There is now an AI class for over 200 skill, beast mode.
    	- Mega Evolution will only be used if:
        ~ One of the AI's moves is super effective
        ~ The opponent is on low HP (1/2) max

    The more-skilled AI is less likely to mega-evolve a Pokémon holding a mega-stone? What would be the advantage of not receiving the stat boosts?
     
    465
    Posts
    7
    Years
    • Seen May 9, 2024
    Very interesting script, seen some decent base ai moves at really high levels, but you rarely see them (and its only like regenerator switches) feel its really dumb as i assume its just as a regular addon/plugin, putting it above main, just making sure it doesnt need to replace the existing one?
     

    #Not Important

    All hail the wishmaker
    910
    Posts
    4
    Years
  • This sounds pretty great! I just have one question regarding the first section:



    The more-skilled AI is less likely to mega-evolve a Pokémon holding a mega-stone? What would be the advantage of not receiving the stat boosts?

    If it doesn't need the boosts, I thought it would be better if it didn't use it.

    archythearc said:
    It is kinda basic apart from switches... I'm working on attacking right now. I'll release a v1.1 soon. It is a plug-n-play script.
     

    DarrylBD99

    Content Creator and Game Developer
    321
    Posts
    4
    Years
  • If it doesn't need the boosts, I thought it would be better if it didn't use it.

    I mean tbh, the trainer ai should react as the player can just use moves that give a guaranteed OHKO like Guillotine, Fissure, Horn Drill, and Sheer Cold. Therefore, not adding much of a difficulty there...
     
    Last edited:
    125
    Posts
    4
    Years
    • Seen Nov 20, 2023
    Well,it is a good idea.But still I dont think it's better to set mega rules.
    Firstly,we always put only one mega pokemon in npc's party,evolve it does no harm.
    And if the pokemon is a Venusaur,a Sableye or a Audino,we dont always expect an"super effective" move,cause we may perfer to use a status move.
     

    AskipLudo

    The Masked Guy
    159
    Posts
    7
    Years
  • I'm getting a "idxBattler" error but I can click on the "ok" from the pop up window and the battle continue but keep appearing again and again
     

    Boonzeet

    Pokémon Secrets of the Ages Developer
    188
    Posts
    15
    Years
  • If it doesn't need the boosts, I thought it would be better if it didn't use it.

    The boosts aren't just to offensive stats, they're also to defensive stats and useful abilities. There's no benefit to the AI to not use it, but there are drawbacks, so that would make the AI easier to defeat.

    Difficulty around megas could involve how they're switched in.
     
    4
    Posts
    3
    Years
    • Seen Feb 25, 2021
    Hey dude I am getting this error after fixing multiple other erros in your script. Do you have any suggestion as what to do now?
    Better AI system
     
    Last edited:

    #Not Important

    All hail the wishmaker
    910
    Posts
    4
    Years
  • The boosts aren't just to offensive stats, they're also to defensive stats and useful abilities. There's no benefit to the AI to not use it, but there are drawbacks, so that would make the AI easier to defeat.

    Difficulty around megas could involve how they're switched in.

    I admit that checking abilities would be good, but maybe make the AI too tough... I'm trying to make the AI better, not impossible.

    Does it really need the HP and defence boosts when the AI is almost at full HP?
     
    286
    Posts
    5
    Years
    • Seen May 9, 2024
    Just want to point out some stuff about the Mega Evolution check. What you did instead checks if the Pokemon has a move that is super-effective against at least one of the opponent's types, meaning that it would think it has a super effective move if it has Thunderbolt and the opponent was Swampert (which is obviously immune to the attack). I also agree with Boonzeet that the strategy behind this is flawed. If the opponent is below half HP or can get hit by a super effective move, it's likely to be able to get taken out by the Pokemon's base form, meaning it would NOT need to Mega Evolve (this would be beneficial for Sharpedo's Speed Boost and Heracross' Moxie for reasons I don't think I need to explain).

    I would say it would be better to set the initial decision to mega evolve to be true, and maybe check some conditions to see if the decision should be changed to false. Maybe make a check to see if the Pokemon can knock out the opponent (using pbRoughDamage), and if it's faster than the opponent(s) and can do this, set the decision to false (because it doesn't need to be mega to knock out the opponent). Another condition you could check is if the Pokemon would become slower than the opponent upon Mega Evolution (like for Mega Ampharos), and if this is the case, maybe stay un-mega. I'd say the first is an easy check and is the most important part, but you could get more complicated with potential stats like I was saying with how speed would change, and maybe even stuff with how damage would change upon mega evolution.
     

    #Not Important

    All hail the wishmaker
    910
    Posts
    4
    Years
  • Just want to point out some stuff about the Mega Evolution check. What you did instead checks if the Pokemon has a move that is super-effective against at least one of the opponent's types, meaning that it would think it has a super effective move if it has Thunderbolt and the opponent was Swampert (which is obviously immune to the attack). I also agree with Boonzeet that the strategy behind this is flawed. If the opponent is below half HP or can get hit by a super effective move, it's likely to be able to get taken out by the Pokemon's base form, meaning it would NOT need to Mega Evolve (this would be beneficial for Sharpedo's Speed Boost and Heracross' Moxie for reasons I don't think I need to explain).

    I would say it would be better to set the initial decision to mega evolve to be true, and maybe check some conditions to see if the decision should be changed to false. Maybe make a check to see if the Pokemon can knock out the opponent (using pbRoughDamage), and if it's faster than the opponent(s) and can do this, set the decision to false (because it doesn't need to be mega to knock out the opponent). Another condition you could check is if the Pokemon would become slower than the opponent upon Mega Evolution (like for Mega Ampharos), and if this is the case, maybe stay un-mega. I'd say the first is an easy check and is the most important part, but you could get more complicated with potential stats like I was saying with how speed would change, and maybe even stuff with how damage would change upon mega evolution.

    So how would I fix that in my script then?

    I'm voting to release V2 soon all this will be fixed/changed. GamingOptima what fixes have you already made
     

    #Not Important

    All hail the wishmaker
    910
    Posts
    4
    Years
  • v1.1 is now out there have been a few changes, including spirit powers compatiablility, the thing that silverlime suggested for choosing whether the mega evo starts true or false, and a better Move choosing process.
     
    Back
    Top