• Just a reminder that providing specifics on, sharing links to, or naming websites where ROMs can be accessed is against the rules. If your post has any of this information it will be removed.
  • Our friends from the Johto Times are hosting a favorite Pokémon poll - and we'd love for you to participate! Click here for information on how to vote for your favorites!
  • 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.

[Battle] Help with new Switching AI

  • 118
    Posts
    5
    Years
    I recently saw Petuh 2.0's post on a new type of switching AI, but I found that it was far too manual for my tastes. Therefore, I tried to implement it myself.

    This script is copied off of the already existing Wonder Guard check script:
    Code:
    u8 opposingPosition;
        u8 opposingBattler;
        s32 i, j;
        s32 firstId;
        s32 lastId; // + 1
        struct Pokemon *party = NULL;
        u16 move;
    
        if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
            return FALSE;
    
        opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
    
        // Check if Pokemon has a super effective move.
        for (opposingBattler = GetBattlerAtPosition(opposingPosition), i = 0; i < MAX_MON_MOVES; i++)
        {
            move = gBattleMons[gActiveBattler].moves[i];
            if (move != MOVE_NONE)
            {
                // If Attacker has super effective move, don't switch.
                if (AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0))
                    return FALSE;
            }
        }
    ...
    It works perfectly (After the ellipses is just the code for selecting which new Pokemon to send out). Essentially, if you send out a Pokemon, and if the opponent can't do Super Effective damage, they switch. All I need to do now is make changes so that if YOU are the one using a super effective move (and you have to use the move; you can't just have it available), then they switch to their best counter.

    This code snippet works as follows: If you have a move which is super effective against the opponent, they switch.
    Code:
    static bool8 EnemyMonHasSpecificSuperEffectiveRevealedMove(void)
    {
        u8 opposingPosition;
        u8 opposingBattler;
        s32 i, j;
        s32 firstId;
        s32 lastId; // + 1
        struct Pokemon *party = NULL;
        u16 move;
    
        if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
            return FALSE;
    
        opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
    
        // Check if Pokemon has a super effective move.
        for (opposingBattler = GetBattlerAtPosition(opposingPosition), i = 0; i < MAX_MON_MOVES; i++)
        {
            move = gBattleMons[opposingBattler].moves[i];
            if (move != MOVE_NONE)
            {
                if (AI_GetTypeEffectiveness(move, opposingBattler, gActiveBattler) >= UQ_4_12(2.0))
    			{
                    GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
    
    				if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
    					party = gPlayerParty;
    				else
    					party = gEnemyParty;
    
    				// Find a Pokemon in the party that has a super effective move.
    				for (i = firstId; i < lastId; i++)
    				{
    					if (GetMonData(&party[i], MON_DATA_HP) == 0)
    						continue;
    					if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE)
    						continue;
    					if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_EGG)
    						continue;
    					if (i == gBattlerPartyIndexes[gActiveBattler])
    						continue;
    
    					for (opposingBattler = GetBattlerAtPosition(opposingPosition), j = 0; j < MAX_MON_MOVES; j++)
    					{
    						move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
    						if (move != MOVE_NONE)
    						{
    							if (AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0) && Random() % 3 < 2)
    							{
    								// We found a mon.
    								*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
    								BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
    								return TRUE;
    							}
    						}
    					}
    				}
    			}
    		}
    	}
        return FALSE; 
    }

    This is where I have hit a wall. This code, I feel, should work. What's supposed to happen is the opponent switches if you use a move that was super effective, the opponent will switch next turn. However, the opponent never switches out. Still a little unsure as to how gLastLandedMoves works, tbh.
    Code:
    static bool8 EnemyMonHasSpecificSuperEffectiveRevealedMove(void)
    {
        u8 opposingPosition;
        u8 opposingBattler;
        s32 i, j;
        s32 firstId;
        s32 lastId; // + 1
        struct Pokemon *party = NULL;
        u16 move;
    	opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
    	opposingBattler = GetBattlerAtPosition(opposingPosition);
    
        if (gLastLandedMoves[opposingBattler] == 0)
            return FALSE;
        if (gLastLandedMoves[opposingBattler] == 0xFFFF)
            return FALSE;
        if (gBattleMoves[gLastLandedMoves[opposingBattler]].power == 0)
            return FALSE;
    
    	move = gLastLandedMoves[opposingBattler];
            if (move != MOVE_NONE)
    		{
    			*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
    			BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
    			return TRUE;
    		}
    		return FALSE;
    }
    NOTE: I simplified the "deciding who to switch to" code to something simpler because the game is pretty good at deciding who to switch to. The old code was specifically about finding a Pokemon with a super-effective move. It didn't take the Pokemon's type into account.

    EDIT: I've found something weird. If you implement this script:
    Code:
    static bool8 EnemyMonHasSpecificSuperEffectiveRevealedMove(void)
    {
        u8 opposingPosition;
        u8 opposingBattler;
        s32 i, j;
        s32 firstId;
        s32 lastId; // + 1
        struct Pokemon *party = NULL;
        u16 move;
    	opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
    	opposingBattler = GetBattlerAtPosition(opposingPosition);
    	move = gLastLandedMoves[opposingBattler];
            if (move!=MOVE_NONE)
    		{
    			*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
    			BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
    			return TRUE;
    		}
    		return FALSE;
    }
    Then the opponent will switch whenever their last move is not Move_None. However, if you replace
    Code:
    move = gLastLandedMoves[opposingBattler];
    with
    Code:
    move = gLastLandedMoves[gActiveBattler];
    nothing happens. I would assume that the opponent would switch when you use a move that isn't Move_None. However, they just don't. So weird!

    EDIT 2: Ok, so I took the original code Petuh 2.0 made and upgraded it for Battle Engine Upgrade usage and it looks like this:
    Code:
    static bool8 EnemyMonHasSpecificSuperEffectiveRevealedMove(void)
    {
        u8 opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(gActiveBattler));
    	u8 opposingBattler = GetBattlerAtPosition(opposingPosition);
    	CalcPartyMonTypeEffectivenessMultiplier(gLastLandedMoves[gActiveBattler], gBattleMons[gActiveBattler].species, gBattleMons[gActiveBattler].ability);
    		if (gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) {
    			*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
    			BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
    			return TRUE;
    		}
    	return FALSE;
    }
    How it works is exactly correct. But in reverse. If the opponent uses a move that does super effective damage to YOU, then THEY switch. I tried switching gActiveBattler to opposingBattler, but the effect stays the same.
    If anyone's familiar with this type of hacking and has any ideas, please let me know. Thanks!
     
    Last edited:
    Back
    Top