mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 19:54:21 +01:00
Merge branch 'RHH/master' into RHH/upcoming
# Conflicts: # src/battle_script_commands.c # src/battle_util.c
This commit is contained in:
commit
ad78dfcf68
@ -2,6 +2,7 @@
|
||||
#define GUARD_BATTLE_ANIM_H
|
||||
|
||||
#include "battle.h"
|
||||
#include "constants/battle.h"
|
||||
#include "constants/battle_anim.h"
|
||||
#include "task.h"
|
||||
|
||||
@ -204,10 +205,10 @@ u8 GetBattlerSpriteDefault_Y(u8 battlerId);
|
||||
u8 GetSubstituteSpriteDefault_Y(u8 battlerId);
|
||||
|
||||
// battle_anim_status_effects.c
|
||||
#define STAT_ANIM_PLUS1 15
|
||||
#define STAT_ANIM_PLUS2 39
|
||||
#define STAT_ANIM_MINUS1 22
|
||||
#define STAT_ANIM_MINUS2 46
|
||||
#define STAT_ANIM_PLUS1 MOVE_EFFECT_ATK_PLUS_1
|
||||
#define STAT_ANIM_PLUS2 MOVE_EFFECT_ATK_PLUS_2
|
||||
#define STAT_ANIM_MINUS1 MOVE_EFFECT_ATK_MINUS_1
|
||||
#define STAT_ANIM_MINUS2 MOVE_EFFECT_ATK_MINUS_2
|
||||
#define STAT_ANIM_MULTIPLE_PLUS1 55
|
||||
#define STAT_ANIM_MULTIPLE_PLUS2 56
|
||||
#define STAT_ANIM_MULTIPLE_MINUS1 57
|
||||
|
@ -72,6 +72,7 @@ bool32 IsWildMonSmart(void);
|
||||
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags);
|
||||
void ModifyPersonalityForNature(u32 *personality, u32 newNature);
|
||||
u32 GeneratePersonalityForGender(u32 gender, u32 species);
|
||||
void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMonCustomized *partyEntry);
|
||||
|
||||
extern struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE];
|
||||
|
||||
|
@ -321,21 +321,21 @@
|
||||
#define MOVE_EFFECT_PAYDAY 12
|
||||
#define MOVE_EFFECT_CHARGING 13
|
||||
#define MOVE_EFFECT_WRAP 14
|
||||
#define MOVE_EFFECT_BURN_UP 15 // MOVE_EFFECT_BURN_UP replaces unused MOVE_EFFECT_RECOIL_25 so that stat change animations don't break
|
||||
#define MOVE_EFFECT_ATK_PLUS_1 16
|
||||
#define MOVE_EFFECT_DEF_PLUS_1 17
|
||||
#define MOVE_EFFECT_SPD_PLUS_1 18
|
||||
#define MOVE_EFFECT_SP_ATK_PLUS_1 19
|
||||
#define MOVE_EFFECT_SP_DEF_PLUS_1 20
|
||||
#define MOVE_EFFECT_ACC_PLUS_1 21
|
||||
#define MOVE_EFFECT_EVS_PLUS_1 22
|
||||
#define MOVE_EFFECT_ATK_MINUS_1 23
|
||||
#define MOVE_EFFECT_DEF_MINUS_1 24
|
||||
#define MOVE_EFFECT_SPD_MINUS_1 25
|
||||
#define MOVE_EFFECT_SP_ATK_MINUS_1 26
|
||||
#define MOVE_EFFECT_SP_DEF_MINUS_1 27
|
||||
#define MOVE_EFFECT_ACC_MINUS_1 28
|
||||
#define MOVE_EFFECT_EVS_MINUS_1 29
|
||||
#define MOVE_EFFECT_ATK_PLUS_1 15
|
||||
#define MOVE_EFFECT_DEF_PLUS_1 16
|
||||
#define MOVE_EFFECT_SPD_PLUS_1 17
|
||||
#define MOVE_EFFECT_SP_ATK_PLUS_1 18
|
||||
#define MOVE_EFFECT_SP_DEF_PLUS_1 19
|
||||
#define MOVE_EFFECT_ACC_PLUS_1 20
|
||||
#define MOVE_EFFECT_EVS_PLUS_1 21
|
||||
#define MOVE_EFFECT_ATK_MINUS_1 22
|
||||
#define MOVE_EFFECT_DEF_MINUS_1 23
|
||||
#define MOVE_EFFECT_SPD_MINUS_1 24
|
||||
#define MOVE_EFFECT_SP_ATK_MINUS_1 25
|
||||
#define MOVE_EFFECT_SP_DEF_MINUS_1 26
|
||||
#define MOVE_EFFECT_ACC_MINUS_1 27
|
||||
#define MOVE_EFFECT_EVS_MINUS_1 28
|
||||
#define MOVE_EFFECT_BURN_UP 29
|
||||
#define MOVE_EFFECT_RECHARGE 30
|
||||
#define MOVE_EFFECT_RAGE 31
|
||||
#define MOVE_EFFECT_STEAL_ITEM 32
|
||||
@ -345,21 +345,21 @@
|
||||
#define MOVE_EFFECT_RAPIDSPIN 36
|
||||
#define MOVE_EFFECT_REMOVE_STATUS 37
|
||||
#define MOVE_EFFECT_ATK_DEF_DOWN 38
|
||||
#define MOVE_EFFECT_SCALE_SHOT 39 // MOVE_EFFECT_SCALE_SHOT replaces unused MOVE_EFFECT_RECOIL_33 so that stat change animations don't break
|
||||
#define MOVE_EFFECT_ATK_PLUS_2 40
|
||||
#define MOVE_EFFECT_DEF_PLUS_2 41
|
||||
#define MOVE_EFFECT_SPD_PLUS_2 42
|
||||
#define MOVE_EFFECT_SP_ATK_PLUS_2 43
|
||||
#define MOVE_EFFECT_SP_DEF_PLUS_2 44
|
||||
#define MOVE_EFFECT_ACC_PLUS_2 45
|
||||
#define MOVE_EFFECT_EVS_PLUS_2 46
|
||||
#define MOVE_EFFECT_ATK_MINUS_2 47
|
||||
#define MOVE_EFFECT_DEF_MINUS_2 48
|
||||
#define MOVE_EFFECT_SPD_MINUS_2 49
|
||||
#define MOVE_EFFECT_SP_ATK_MINUS_2 50
|
||||
#define MOVE_EFFECT_SP_DEF_MINUS_2 51
|
||||
#define MOVE_EFFECT_ACC_MINUS_2 52
|
||||
#define MOVE_EFFECT_EVS_MINUS_2 53
|
||||
#define MOVE_EFFECT_ATK_PLUS_2 39
|
||||
#define MOVE_EFFECT_DEF_PLUS_2 40
|
||||
#define MOVE_EFFECT_SPD_PLUS_2 41
|
||||
#define MOVE_EFFECT_SP_ATK_PLUS_2 42
|
||||
#define MOVE_EFFECT_SP_DEF_PLUS_2 43
|
||||
#define MOVE_EFFECT_ACC_PLUS_2 44
|
||||
#define MOVE_EFFECT_EVS_PLUS_2 45
|
||||
#define MOVE_EFFECT_ATK_MINUS_2 46
|
||||
#define MOVE_EFFECT_DEF_MINUS_2 47
|
||||
#define MOVE_EFFECT_SPD_MINUS_2 48
|
||||
#define MOVE_EFFECT_SP_ATK_MINUS_2 49
|
||||
#define MOVE_EFFECT_SP_DEF_MINUS_2 50
|
||||
#define MOVE_EFFECT_ACC_MINUS_2 51
|
||||
#define MOVE_EFFECT_EVS_MINUS_2 52
|
||||
#define MOVE_EFFECT_SCALE_SHOT 53
|
||||
#define MOVE_EFFECT_THRASH 54
|
||||
#define MOVE_EFFECT_KNOCK_OFF 55
|
||||
#define MOVE_EFFECT_DEF_SPDEF_DOWN 56
|
||||
|
@ -48,6 +48,10 @@ static inline void Shuffle(void *data, size_t n, size_t size)
|
||||
* RandomUniform(tag, lo, hi) returns a number from lo to hi inclusive
|
||||
* with uniform probability.
|
||||
*
|
||||
* RandomUniformExcept(tag, lo, hi, reject) returns a number from lo to
|
||||
* hi inclusive with uniform probability, excluding those for which
|
||||
* reject returns TRUE.
|
||||
*
|
||||
* RandomElement(tag, array) returns an element in array with uniform
|
||||
* probability. The array must be known at compile-time (e.g. a global
|
||||
* const array).
|
||||
@ -72,8 +76,11 @@ enum RandomTag
|
||||
RNG_FLAME_BODY,
|
||||
RNG_FORCE_RANDOM_SWITCH,
|
||||
RNG_FROZEN,
|
||||
RNG_HITS,
|
||||
RNG_HOLD_EFFECT_FLINCH,
|
||||
RNG_INFATUATION,
|
||||
RNG_LOADED_DICE,
|
||||
RNG_METRONOME,
|
||||
RNG_PARALYSIS,
|
||||
RNG_POISON_POINT,
|
||||
RNG_RAMPAGE_TURNS,
|
||||
@ -121,10 +128,12 @@ enum RandomTag
|
||||
})
|
||||
|
||||
u32 RandomUniform(enum RandomTag, u32 lo, u32 hi);
|
||||
u32 RandomUniformExcept(enum RandomTag, u32 lo, u32 hi, bool32 (*reject)(u32));
|
||||
u32 RandomWeightedArray(enum RandomTag, u32 sum, u32 n, const u8 *weights);
|
||||
const void *RandomElementArray(enum RandomTag, const void *array, size_t size, size_t count);
|
||||
|
||||
u32 RandomUniformDefault(enum RandomTag, u32 lo, u32 hi);
|
||||
u32 RandomUniformExceptDefault(enum RandomTag, u32 lo, u32 hi, bool32 (*reject)(u32));
|
||||
u32 RandomWeightedArrayDefault(enum RandomTag, u32 sum, u32 n, const u8 *weights);
|
||||
const void *RandomElementArrayDefault(enum RandomTag, const void *array, size_t size, size_t count);
|
||||
|
||||
|
@ -121,7 +121,6 @@ static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite);
|
||||
static void TrySpecialEvolution(void);
|
||||
static u32 Crc32B (const u8 *data, u32 size);
|
||||
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i);
|
||||
static void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMonCustomized *partyEntry);
|
||||
|
||||
EWRAM_DATA u16 gBattle_BG0_X = 0;
|
||||
EWRAM_DATA u16 gBattle_BG0_Y = 0;
|
||||
@ -1955,7 +1954,7 @@ u32 GeneratePersonalityForGender(u32 gender, u32 species)
|
||||
return speciesInfo->genderRatio / 2;
|
||||
}
|
||||
|
||||
static void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMonCustomized *partyEntry)
|
||||
void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMonCustomized *partyEntry)
|
||||
{
|
||||
bool32 noMoveSet = TRUE;
|
||||
u32 j;
|
||||
|
@ -3935,6 +3935,7 @@ u8 GetCurrentPpToMaxPpState(u8 currentPp, u8 maxPp)
|
||||
struct TrainerSlide
|
||||
{
|
||||
u16 trainerId;
|
||||
bool8 isFrontierTrainer;
|
||||
const u8 *msgLastSwitchIn;
|
||||
const u8 *msgLastLowHp;
|
||||
const u8 *msgFirstDown;
|
||||
@ -3954,6 +3955,7 @@ static const struct TrainerSlide sTrainerSlides[] =
|
||||
Example:
|
||||
{
|
||||
.trainerId = TRAINER_WALLY_VR_2,
|
||||
.isFrontierTrainer = FALSE,
|
||||
.msgLastSwitchIn = sText_AarghAlmostHadIt,
|
||||
.msgLastLowHp = sText_BoxIsFull,
|
||||
.msgFirstDown = sText_123Poof,
|
||||
@ -4046,7 +4048,9 @@ u32 ShouldDoTrainerSlide(u32 battlerId, u32 which)
|
||||
|
||||
for (i = 0; i < ARRAY_COUNT(sTrainerSlides); i++)
|
||||
{
|
||||
if (trainerId == sTrainerSlides[i].trainerId)
|
||||
if (trainerId == sTrainerSlides[i].trainerId
|
||||
&& (((gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && sTrainerSlides[i].isFrontierTrainer)
|
||||
|| (!(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && !sTrainerSlides[i].isFrontierTrainer)))
|
||||
{
|
||||
gBattleScripting.battler = battlerId;
|
||||
switch (which)
|
||||
|
@ -11972,30 +11972,14 @@ static void Cmd_setmultihitcounter(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
#if B_MULTI_HIT_CHANCE >= GEN_5
|
||||
// Based on Gen 5's odds
|
||||
// 35% for 2 hits
|
||||
// 35% for 3 hits
|
||||
// 15% for 4 hits
|
||||
// 15% for 5 hits
|
||||
gMultiHitCounter = Random() % 100;
|
||||
if (gMultiHitCounter < 35)
|
||||
gMultiHitCounter = 2;
|
||||
else if (gMultiHitCounter < 35 + 35)
|
||||
gMultiHitCounter = 3;
|
||||
else if (gMultiHitCounter < 35 + 35 + 15)
|
||||
gMultiHitCounter = 4;
|
||||
else
|
||||
gMultiHitCounter = 5;
|
||||
#else
|
||||
// 2 and 3 hits: 37.5%
|
||||
// 4 and 5 hits: 12.5%
|
||||
gMultiHitCounter = Random() % 4;
|
||||
if (gMultiHitCounter > 1)
|
||||
gMultiHitCounter = (Random() % 4) + 2;
|
||||
else
|
||||
gMultiHitCounter += 2;
|
||||
#endif
|
||||
// WARNING: These seem to be unused, see SetRandomMultiHitCounter.
|
||||
#if B_MULTI_HIT_CHANCE >= GEN_5
|
||||
// 35%: 2 hits, 35%: 3 hits, 15% 4 hits, 15% 5 hits.
|
||||
gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 7, 7, 3, 3);
|
||||
#else
|
||||
// 37.5%: 2 hits, 37.5%: 3 hits, 12.5% 4 hits, 12.5% 5 hits.
|
||||
gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 3, 3, 1, 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -12743,41 +12727,37 @@ static void Cmd_mimicattackcopy(void)
|
||||
}
|
||||
}
|
||||
|
||||
static bool32 InvalidMetronomeMove(u32 move)
|
||||
{
|
||||
return gBattleMoves[move].effect == EFFECT_PLACEHOLDER
|
||||
|| gBattleMoves[move].metronomeBanned;
|
||||
}
|
||||
|
||||
static void Cmd_metronome(void)
|
||||
{
|
||||
CMD_ARGS();
|
||||
|
||||
#if B_METRONOME_MOVES >= GEN_9
|
||||
u16 moveCount = MOVES_COUNT_GEN9;
|
||||
u32 moveCount = MOVES_COUNT_GEN9;
|
||||
#elif B_METRONOME_MOVES >= GEN_8
|
||||
u16 moveCount = MOVES_COUNT_GEN8;
|
||||
u32 moveCount = MOVES_COUNT_GEN8;
|
||||
#elif B_METRONOME_MOVES >= GEN_7
|
||||
u16 moveCount = MOVES_COUNT_GEN7;
|
||||
u32 moveCount = MOVES_COUNT_GEN7;
|
||||
#elif B_METRONOME_MOVES >= GEN_6
|
||||
u16 moveCount = MOVES_COUNT_GEN6;
|
||||
u32 moveCount = MOVES_COUNT_GEN6;
|
||||
#elif B_METRONOME_MOVES >= GEN_5
|
||||
u16 moveCount = MOVES_COUNT_GEN5;
|
||||
u32 moveCount = MOVES_COUNT_GEN5;
|
||||
#elif B_METRONOME_MOVES >= GEN_4
|
||||
u16 moveCount = MOVES_COUNT_GEN4;
|
||||
u32 moveCount = MOVES_COUNT_GEN4;
|
||||
#elif B_METRONOME_MOVES >= GEN_3
|
||||
u16 moveCount = MOVES_COUNT_GEN3;
|
||||
u32 moveCount = MOVES_COUNT_GEN3;
|
||||
#endif
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
gCurrentMove = (Random() % (moveCount - 1)) + 1;
|
||||
if (gBattleMoves[gCurrentMove].effect == EFFECT_PLACEHOLDER)
|
||||
continue;
|
||||
|
||||
if (!gBattleMoves[gCurrentMove].metronomeBanned)
|
||||
{
|
||||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||||
SetAtkCancellerForCalledMove();
|
||||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
||||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
gCurrentMove = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove);
|
||||
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
|
||||
SetAtkCancellerForCalledMove();
|
||||
gBattlescriptCurrInstr = gBattleScriptsForMoveEffects[gBattleMoves[gCurrentMove].effect];
|
||||
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
|
||||
}
|
||||
|
||||
static void Cmd_dmgtolevel(void)
|
||||
|
@ -3119,13 +3119,8 @@ static void FillPartnerParty(u16 trainerId)
|
||||
|
||||
CreateMon(&gPlayerParty[i + 3], partyData[i].species, partyData[i].lvl, 0, TRUE, j, otIdType, otID);
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
|
||||
CustomTrainerPartyAssignMoves(&gPlayerParty[i+3], &partyData[i]);
|
||||
|
||||
// TODO: Figure out a default strategy when moves are not set, to generate a good moveset
|
||||
for (j = 0; j < MAX_MON_MOVES; ++j)
|
||||
{
|
||||
SetMonData(&gPlayerParty[i+3], MON_DATA_MOVE1 + j, &partyData[i].moves[j]);
|
||||
SetMonData(&gPlayerParty[i+3], MON_DATA_PP1 + j, &gBattleMoves[partyData[i].moves[j]].pp);
|
||||
}
|
||||
SetMonData(&gPlayerParty[i+3], MON_DATA_IVS, &(partyData[i].iv));
|
||||
if (partyData[i].ev != NULL)
|
||||
{
|
||||
@ -3164,6 +3159,8 @@ static void FillPartnerParty(u16 trainerId)
|
||||
|
||||
StringCopy(trainerName, gTrainers[trainerId - TRAINER_CUSTOM_PARTNER].trainerName);
|
||||
SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_NAME, trainerName);
|
||||
j = gTrainers[trainerId - TRAINER_CUSTOM_PARTNER].encounterMusic_gender >> 7;
|
||||
SetMonData(&gPlayerParty[i+3], MON_DATA_OT_GENDER, &j);
|
||||
}
|
||||
}
|
||||
else if (trainerId == TRAINER_EREADER)
|
||||
|
@ -8846,16 +8846,16 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe
|
||||
}
|
||||
|
||||
if (IsAbilityOnField(ABILITY_VESSEL_OF_RUIN) && atkAbility != ABILITY_VESSEL_OF_RUIN && IS_MOVE_SPECIAL(gCurrentMove))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(0.25));
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(0.75));
|
||||
|
||||
if (IsAbilityOnField(ABILITY_SWORD_OF_RUIN) && defAbility != ABILITY_SWORD_OF_RUIN && IS_MOVE_PHYSICAL(gCurrentMove))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(0.25));
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.25));
|
||||
|
||||
if (IsAbilityOnField(ABILITY_TABLETS_OF_RUIN) && atkAbility != ABILITY_TABLETS_OF_RUIN && IS_MOVE_PHYSICAL(gCurrentMove))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(0.25));
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(0.75));
|
||||
|
||||
if (IsAbilityOnField(ABILITY_BEADS_OF_RUIN) && defAbility != ABILITY_BEADS_OF_RUIN && IS_MOVE_SPECIAL(gCurrentMove))
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(0.25));
|
||||
modifier = uq4_12_multiply(modifier, UQ_4_12(1.25));
|
||||
|
||||
// attacker partner's abilities
|
||||
if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)))
|
||||
@ -10829,35 +10829,19 @@ bool32 CanTargetBattler(u8 battlerAtk, u8 battlerDef, u16 move)
|
||||
|
||||
static void SetRandomMultiHitCounter()
|
||||
{
|
||||
#if (B_MULTI_HIT_CHANCE >= GEN_5)
|
||||
// Based on Gen 5's odds
|
||||
// 35% for 2 hits
|
||||
// 35% for 3 hits
|
||||
// 15% for 4 hits
|
||||
// 15% for 5 hits
|
||||
gMultiHitCounter = Random() % 100;
|
||||
if (gMultiHitCounter < 35)
|
||||
gMultiHitCounter = 2;
|
||||
else if (gMultiHitCounter < 35 + 35)
|
||||
gMultiHitCounter = 3;
|
||||
else if (gMultiHitCounter < 35 + 35 + 15)
|
||||
gMultiHitCounter = 4;
|
||||
else
|
||||
gMultiHitCounter = 5;
|
||||
#else
|
||||
// 2 and 3 hits: 37.5%
|
||||
// 4 and 5 hits: 12.5%
|
||||
gMultiHitCounter = Random() % 4;
|
||||
if (gMultiHitCounter > 1)
|
||||
gMultiHitCounter = (Random() % 4) + 2;
|
||||
else
|
||||
gMultiHitCounter += 2;
|
||||
#endif
|
||||
|
||||
if (gMultiHitCounter < 4 && GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LOADED_DICE)
|
||||
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_LOADED_DICE)
|
||||
{
|
||||
// If roll 4 or 5 Loaded Dice doesn't do anything. Otherwise it rolls the number of hits as 5 minus a random integer from 0 to 1 inclusive.
|
||||
gMultiHitCounter = 5 - (Random() & 1);
|
||||
gMultiHitCounter = RandomUniform(RNG_LOADED_DICE, 4, 5);
|
||||
}
|
||||
else
|
||||
{
|
||||
#if B_MULTI_HIT_CHANCE >= GEN_5
|
||||
// 35%: 2 hits, 35%: 3 hits, 15% 4 hits, 15% 5 hits.
|
||||
gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 7, 7, 3, 3);
|
||||
#else
|
||||
// 37.5%: 2 hits, 37.5%: 3 hits, 12.5% 4 hits, 12.5% 5 hits.
|
||||
gMultiHitCounter = RandomWeighted(RNG_HITS, 0, 0, 3, 3, 1, 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
13
src/random.c
13
src/random.c
@ -80,6 +80,9 @@ void ShuffleN(void *data, size_t n, size_t size)
|
||||
__attribute__((weak, alias("RandomUniformDefault")))
|
||||
u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi);
|
||||
|
||||
__attribute__((weak, alias("RandomUniformExceptDefault")))
|
||||
u32 RandomUniformExcept(enum RandomTag, u32 lo, u32 hi, bool32 (*reject)(u32));
|
||||
|
||||
__attribute__((weak, alias("RandomWeightedArrayDefault")))
|
||||
u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights);
|
||||
|
||||
@ -91,6 +94,16 @@ u32 RandomUniformDefault(enum RandomTag tag, u32 lo, u32 hi)
|
||||
return lo + (((hi - lo + 1) * Random()) >> 16);
|
||||
}
|
||||
|
||||
u32 RandomUniformExceptDefault(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32))
|
||||
{
|
||||
while (TRUE)
|
||||
{
|
||||
u32 n = RandomUniformDefault(tag, lo, hi);
|
||||
if (!reject(n))
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
|
||||
{
|
||||
s32 i, targetSum;
|
||||
|
53
test/ability_beads_of_ruin.c
Normal file
53
test/ability_beads_of_ruin.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_WATER_GUN].split == SPLIT_SPECIAL);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beads of Ruin reduces Sp. Def", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
|
||||
PARAMETRIZE { ability = ABILITY_BEADS_OF_RUIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_BEADS_OF_RUIN) {
|
||||
ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN);
|
||||
MESSAGE("Wobbuffet's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!");
|
||||
}
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.25), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beads of Ruin does not reduce Sp. Def if opposing mon has the same ability", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
|
||||
PARAMETRIZE { ability = ABILITY_BEADS_OF_RUIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_BEADS_OF_RUIN); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_BEADS_OF_RUIN) {
|
||||
ABILITY_POPUP(player, ABILITY_BEADS_OF_RUIN);
|
||||
MESSAGE("Wobbuffet's Beads of Ruin weakened the Sp. Def of all surrounding Pokémon!");
|
||||
}
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage, results[1].damage);
|
||||
}
|
||||
}
|
53
test/ability_sword_of_ruin.c
Normal file
53
test/ability_sword_of_ruin.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sword of Ruin reduces Defense", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
|
||||
PARAMETRIZE { ability = ABILITY_SWORD_OF_RUIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_SWORD_OF_RUIN) {
|
||||
ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN);
|
||||
MESSAGE("Wobbuffet's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
|
||||
}
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.25), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Sword of Ruin does not reduce Defense if opposing mon has the same ability", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
|
||||
PARAMETRIZE { ability = ABILITY_SWORD_OF_RUIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_SWORD_OF_RUIN); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_SWORD_OF_RUIN) {
|
||||
ABILITY_POPUP(player, ABILITY_SWORD_OF_RUIN);
|
||||
MESSAGE("Wobbuffet's Sword of Ruin weakened the Defense of all surrounding Pokémon!");
|
||||
}
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage, results[1].damage);
|
||||
}
|
||||
}
|
53
test/ability_tablets_of_ruin.c
Normal file
53
test/ability_tablets_of_ruin.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_TACKLE].split == SPLIT_PHYSICAL);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Tablets of Ruin reduces Attack", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
|
||||
PARAMETRIZE { ability = ABILITY_TABLETS_OF_RUIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_TABLETS_OF_RUIN) {
|
||||
ABILITY_POPUP(player, ABILITY_TABLETS_OF_RUIN);
|
||||
MESSAGE("Wobbuffet's Tablets of Ruin weakened the Attack of all surrounding Pokémon!");
|
||||
}
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.75), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Tablets of Ruin does not reduce Attack if an opposing mon has the same ability", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
|
||||
PARAMETRIZE { ability = ABILITY_TABLETS_OF_RUIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_TABLETS_OF_RUIN); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_TABLETS_OF_RUIN) {
|
||||
ABILITY_POPUP(player, ABILITY_TABLETS_OF_RUIN);
|
||||
MESSAGE("Wobbuffet's Tablets of Ruin weakened the Attack of all surrounding Pokémon!");
|
||||
}
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage, results[1].damage);
|
||||
}
|
||||
}
|
53
test/ability_vessel_of_ruin.c
Normal file
53
test/ability_vessel_of_ruin.c
Normal file
@ -0,0 +1,53 @@
|
||||
#include "global.h"
|
||||
#include "test_battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gBattleMoves[MOVE_WATER_GUN].split == SPLIT_SPECIAL);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Vessel of Ruin reduces Sp. Atk", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
|
||||
PARAMETRIZE { ability = ABILITY_VESSEL_OF_RUIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_VESSEL_OF_RUIN) {
|
||||
ABILITY_POPUP(player, ABILITY_VESSEL_OF_RUIN);
|
||||
MESSAGE("Wobbuffet's Vessel of Ruin weakened the Sp. Atk of all surrounding Pokémon!");
|
||||
}
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(0.75), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Vessel of Ruin does not reduce Sp. Atk if opposing mon has the same ability", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SHADOW_TAG; }
|
||||
PARAMETRIZE { ability = ABILITY_VESSEL_OF_RUIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Ability(ABILITY_VESSEL_OF_RUIN); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_VESSEL_OF_RUIN) {
|
||||
ABILITY_POPUP(player, ABILITY_VESSEL_OF_RUIN);
|
||||
MESSAGE("Wobbuffet's Vessel of Ruin weakened the Sp. Atk of all surrounding Pokémon!");
|
||||
}
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_EQ(results[0].damage, results[1].damage);
|
||||
}
|
||||
}
|
@ -6,19 +6,13 @@ ASSUMPTIONS
|
||||
ASSUME(gBattleMoves[MOVE_METRONOME].effect == EFFECT_METRONOME);
|
||||
}
|
||||
|
||||
// To do: Turn the seeds to work with WITH_RNG for Metronome.
|
||||
#define RNG_METRONOME_SCRATCH 0x118
|
||||
#define RNG_METRONOME_PSN_POWDER 0x119
|
||||
#define RNG_METRONOME_ROCK_BLAST 0x1F5
|
||||
|
||||
SINGLE_BATTLE_TEST("Metronome picks a random move")
|
||||
{
|
||||
GIVEN {
|
||||
RNGSeed(RNG_METRONOME_SCRATCH);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_METRONOME); }
|
||||
TURN { MOVE(player, MOVE_METRONOME, WITH_RNG(RNG_METRONOME, MOVE_SCRATCH)); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Metronome!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||
@ -34,11 +28,10 @@ SINGLE_BATTLE_TEST("Metronome's called powder move fails against Grass Types")
|
||||
ASSUME(gBattleMoves[MOVE_POISON_POWDER].powderMove);
|
||||
ASSUME(gSpeciesInfo[SPECIES_TANGELA].types[0] == TYPE_GRASS);
|
||||
ASSUME(gBattleMoves[MOVE_POISON_POWDER].effect == EFFECT_POISON);
|
||||
RNGSeed(RNG_METRONOME_PSN_POWDER);
|
||||
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
||||
OPPONENT(SPECIES_TANGELA) {Speed(2);}
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_TANGELA);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_METRONOME); }
|
||||
TURN { MOVE(player, MOVE_METRONOME, WITH_RNG(RNG_METRONOME, MOVE_POISON_POWDER)); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Metronome!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||
@ -53,17 +46,16 @@ SINGLE_BATTLE_TEST("Metronome's called multi-hit move hits multiple times")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_ROCK_BLAST].effect == EFFECT_MULTI_HIT);
|
||||
RNGSeed(RNG_METRONOME_ROCK_BLAST);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_METRONOME); }
|
||||
TURN { MOVE(player, MOVE_METRONOME, WITH_RNG(RNG_METRONOME, MOVE_ROCK_BLAST)); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Metronome!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_METRONOME, player);
|
||||
MESSAGE("Wobbuffet used Rock Blast!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROCK_BLAST, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("Hit 4 time(s)!");
|
||||
MESSAGE("Hit 5 time(s)!");
|
||||
}
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ ASSUMPTIONS
|
||||
SINGLE_BATTLE_TEST("Mirror Move copies the last used move by the target")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) {Speed(2);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(5);}
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_MIRROR_MOVE); }
|
||||
} SCENE {
|
||||
@ -26,10 +26,10 @@ SINGLE_BATTLE_TEST("Mirror Move copies the last used move by the target")
|
||||
SINGLE_BATTLE_TEST("Mirror Move fails if no move was used before")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(2);}
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_MIRROR_MOVE); }
|
||||
TURN { MOVE(player, MOVE_MIRROR_MOVE); MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Mirror Move!");
|
||||
MESSAGE("The Mirror Move failed!");
|
||||
@ -44,8 +44,8 @@ SINGLE_BATTLE_TEST("Mirror Move's called powder move fails against Grass Types")
|
||||
ASSUME(gBattleMoves[MOVE_STUN_SPORE].powderMove);
|
||||
ASSUME(gSpeciesInfo[SPECIES_ODDISH].types[0] == TYPE_GRASS);
|
||||
ASSUME(gBattleMoves[MOVE_STUN_SPORE].effect == EFFECT_PARALYZE);
|
||||
PLAYER(SPECIES_ODDISH) {Speed(5);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(2);}
|
||||
PLAYER(SPECIES_ODDISH);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_STUN_SPORE); MOVE(opponent, MOVE_MIRROR_MOVE); }
|
||||
} SCENE {
|
||||
@ -59,19 +59,18 @@ SINGLE_BATTLE_TEST("Mirror Move's called powder move fails against Grass Types")
|
||||
}
|
||||
}
|
||||
|
||||
// It hits first 2 times, then 5 times with the default rng seed.
|
||||
SINGLE_BATTLE_TEST("Mirror Move's called multi-hit move hits multiple times")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gBattleMoves[MOVE_BULLET_SEED].effect == EFFECT_MULTI_HIT);
|
||||
PLAYER(SPECIES_WOBBUFFET) {Speed(5);}
|
||||
OPPONENT(SPECIES_WOBBUFFET) {Speed(2);}
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BULLET_SEED); MOVE(opponent, MOVE_MIRROR_MOVE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, player);
|
||||
HP_BAR(opponent);
|
||||
MESSAGE("Hit 2 time(s)!");
|
||||
MESSAGE("Hit 5 time(s)!");
|
||||
MESSAGE("Foe Wobbuffet used Mirror Move!");
|
||||
MESSAGE("Foe Wobbuffet used Bullet Seed!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, opponent);
|
||||
|
@ -57,6 +57,25 @@ TEST("RandomUniform generates lo..hi")
|
||||
}
|
||||
}
|
||||
|
||||
static bool32 InvalidEven(u32 n)
|
||||
{
|
||||
return n % 2 == 0;
|
||||
}
|
||||
|
||||
TEST("RandomUniformExcept generates lo..hi")
|
||||
{
|
||||
u32 lo, hi, i;
|
||||
PARAMETRIZE { lo = 0; hi = 1; }
|
||||
PARAMETRIZE { lo = 0; hi = 2; }
|
||||
PARAMETRIZE { lo = 0; hi = 3; }
|
||||
PARAMETRIZE { lo = 2; hi = 4; }
|
||||
for (i = 0; i < 1024; i++)
|
||||
{
|
||||
u32 r = RandomUniformExceptDefault(RNG_NONE, lo, hi, InvalidEven);
|
||||
EXPECT(lo <= r && r <= hi && r % 2 != 0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST("RandomWeighted generates 0..n-1")
|
||||
{
|
||||
u32 n, sum, i;
|
||||
@ -106,6 +125,29 @@ TEST("RandomUniform generates uniform distribution")
|
||||
EXPECT_LT(error, UQ_4_12(0.025));
|
||||
}
|
||||
|
||||
TEST("RandomUniformExcept generates uniform distribution")
|
||||
{
|
||||
u32 i, error;
|
||||
u16 distribution[4];
|
||||
|
||||
memset(distribution, 0, sizeof(distribution));
|
||||
for (i = 0; i < 4096; i++)
|
||||
{
|
||||
u32 r = RandomUniformExceptDefault(RNG_NONE, 0, ARRAY_COUNT(distribution) - 1, InvalidEven);
|
||||
EXPECT(0 <= r && r < ARRAY_COUNT(distribution));
|
||||
distribution[r]++;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
for (i = 0; i < ARRAY_COUNT(distribution); i++)
|
||||
{
|
||||
if (i % 2 != 0)
|
||||
error += abs(UQ_4_12(0.5) - distribution[i]);
|
||||
}
|
||||
|
||||
EXPECT_LT(error, UQ_4_12(0.05));
|
||||
}
|
||||
|
||||
TEST("RandomWeighted generates distribution in proportion to the weights")
|
||||
{
|
||||
u32 i, sum, error;
|
||||
|
@ -603,8 +603,9 @@ struct BattleTestRunnerState
|
||||
u8 parameters;
|
||||
u8 runParameter;
|
||||
u16 rngTag;
|
||||
u8 trials;
|
||||
u8 runTrial;
|
||||
u16 rngTrialOffset;
|
||||
u16 trials;
|
||||
u16 runTrial;
|
||||
u16 expectedRatio;
|
||||
u16 observedRatio;
|
||||
u16 trialRatio;
|
||||
|
@ -307,17 +307,13 @@ static void BattleTest_Run(void *data)
|
||||
u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi)
|
||||
{
|
||||
const struct BattlerTurn *turn = NULL;
|
||||
u32 default_ = hi;
|
||||
|
||||
if (gCurrentTurnActionNumber < gBattlersCount)
|
||||
{
|
||||
u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber];
|
||||
turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId];
|
||||
}
|
||||
|
||||
if (turn && turn->rng.tag == tag)
|
||||
{
|
||||
default_ = turn->rng.value;
|
||||
if (turn && turn->rng.tag == tag)
|
||||
return turn->rng.value;
|
||||
}
|
||||
|
||||
if (tag == STATE->rngTag)
|
||||
@ -332,53 +328,76 @@ u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi)
|
||||
{
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomUniform called with inconsistent trials %d and %d", STATE->trials, n);
|
||||
}
|
||||
STATE->trialRatio = Q_4_12(1) / n;
|
||||
STATE->trialRatio = Q_4_12(1) / STATE->trials;
|
||||
return STATE->runTrial + lo;
|
||||
}
|
||||
|
||||
return hi;
|
||||
}
|
||||
|
||||
u32 RandomUniformExcept(enum RandomTag tag, u32 lo, u32 hi, bool32 (*reject)(u32))
|
||||
{
|
||||
const struct BattlerTurn *turn = NULL;
|
||||
u32 default_;
|
||||
|
||||
if (gCurrentTurnActionNumber < gBattlersCount)
|
||||
{
|
||||
u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber];
|
||||
turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId];
|
||||
if (turn && turn->rng.tag == tag)
|
||||
{
|
||||
if (reject(turn->rng.value))
|
||||
Test_ExitWithResult(TEST_RESULT_INVALID, "WITH_RNG specified a rejected value (%d)", turn->rng.value);
|
||||
return turn->rng.value;
|
||||
}
|
||||
}
|
||||
|
||||
if (tag == STATE->rngTag)
|
||||
{
|
||||
if (STATE->trials == 1)
|
||||
{
|
||||
u32 n = 0, i;
|
||||
for (i = lo; i < hi; i++)
|
||||
if (!reject(i))
|
||||
n++;
|
||||
STATE->trials = n;
|
||||
PrintTestName();
|
||||
}
|
||||
STATE->trialRatio = Q_4_12(1) / STATE->trials;
|
||||
|
||||
while (reject(STATE->runTrial + lo + STATE->rngTrialOffset))
|
||||
{
|
||||
if (STATE->runTrial + lo + STATE->rngTrialOffset > hi)
|
||||
Test_ExitWithResult(TEST_RESULT_INVALID, "RandomUniformExcept called with inconsistent reject");
|
||||
STATE->rngTrialOffset++;
|
||||
}
|
||||
|
||||
return STATE->runTrial + lo + STATE->rngTrialOffset;
|
||||
}
|
||||
|
||||
default_ = hi;
|
||||
while (reject(default_))
|
||||
{
|
||||
if (default_ == lo)
|
||||
Test_ExitWithResult(TEST_RESULT_INVALID, "RandomUniformExcept rejected all values");
|
||||
default_--;
|
||||
}
|
||||
return default_;
|
||||
}
|
||||
|
||||
u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
|
||||
{
|
||||
const struct BattlerTurn *turn = NULL;
|
||||
u32 default_ = n-1;
|
||||
|
||||
if (sum == 0)
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeightedArray called with zero sum");
|
||||
|
||||
if (gCurrentTurnActionNumber < gBattlersCount)
|
||||
{
|
||||
u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber];
|
||||
turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId];
|
||||
}
|
||||
|
||||
if (turn && turn->rng.tag == tag)
|
||||
{
|
||||
default_ = turn->rng.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (tag)
|
||||
{
|
||||
case RNG_ACCURACY:
|
||||
ASSUME(n == 2);
|
||||
if (turn && turn->hit)
|
||||
return turn->hit - 1;
|
||||
default_ = TRUE;
|
||||
break;
|
||||
|
||||
case RNG_CRITICAL_HIT:
|
||||
ASSUME(n == 2);
|
||||
if (turn && turn->criticalHit)
|
||||
return turn->criticalHit - 1;
|
||||
default_ = FALSE;
|
||||
break;
|
||||
|
||||
case RNG_SECONDARY_EFFECT:
|
||||
ASSUME(n == 2);
|
||||
if (turn && turn->secondaryEffect)
|
||||
return turn->secondaryEffect - 1;
|
||||
default_ = TRUE;
|
||||
break;
|
||||
}
|
||||
if (turn && turn->rng.tag == tag)
|
||||
return turn->rng.value;
|
||||
}
|
||||
|
||||
if (tag == STATE->rngTag)
|
||||
@ -397,7 +416,38 @@ u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
|
||||
return STATE->runTrial;
|
||||
}
|
||||
|
||||
return default_;
|
||||
switch (tag)
|
||||
{
|
||||
case RNG_ACCURACY:
|
||||
ASSUME(n == 2);
|
||||
if (turn && turn->hit)
|
||||
return turn->hit - 1;
|
||||
else
|
||||
return TRUE;
|
||||
|
||||
case RNG_CRITICAL_HIT:
|
||||
ASSUME(n == 2);
|
||||
if (turn && turn->criticalHit)
|
||||
return turn->criticalHit - 1;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
case RNG_SECONDARY_EFFECT:
|
||||
ASSUME(n == 2);
|
||||
if (turn && turn->secondaryEffect)
|
||||
return turn->secondaryEffect - 1;
|
||||
else
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
while (weights[n-1] == 0)
|
||||
{
|
||||
if (n == 1)
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomWeightedArray called with all zero weights");
|
||||
n--;
|
||||
}
|
||||
return n-1;
|
||||
}
|
||||
}
|
||||
|
||||
const void *RandomElementArray(enum RandomTag tag, const void *array, size_t size, size_t count)
|
||||
@ -409,22 +459,17 @@ const void *RandomElementArray(enum RandomTag tag, const void *array, size_t siz
|
||||
{
|
||||
u32 battlerId = gBattlerByTurnOrder[gCurrentTurnActionNumber];
|
||||
turn = &DATA.battleRecordTurns[gBattleResults.battleTurnCounter][battlerId];
|
||||
}
|
||||
|
||||
if (turn && turn->rng.tag == tag)
|
||||
{
|
||||
u32 element = 0;
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
memcpy(&element, (const u8 *)array + size * index, size);
|
||||
if (element == turn->rng.value)
|
||||
break;
|
||||
}
|
||||
if (index == count)
|
||||
if (turn && turn->rng.tag == tag)
|
||||
{
|
||||
u32 element = 0;
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
memcpy(&element, (const u8 *)array + size * index, size);
|
||||
if (element == turn->rng.value)
|
||||
return (const u8 *)array + size * index;
|
||||
}
|
||||
// TODO: Incorporate the line number.
|
||||
const char *filename = gTestRunnerState.test->filename;
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, "%s: RandomElement illegal value requested: %d", filename, turn->rng.value);
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, "%s: RandomElement illegal value requested: %d", gTestRunnerState.test->filename, turn->rng.value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,10 +485,8 @@ const void *RandomElementArray(enum RandomTag tag, const void *array, size_t siz
|
||||
Test_ExitWithResult(TEST_RESULT_ERROR, "RandomElement called with inconsistent trials %d and %d", STATE->trials, count);
|
||||
}
|
||||
STATE->trialRatio = Q_4_12(1) / count;
|
||||
index = STATE->runTrial;
|
||||
return (const u8 *)array + size * STATE->runTrial;
|
||||
}
|
||||
|
||||
return (const u8 *)array + size * index;
|
||||
}
|
||||
|
||||
static s32 TryAbilityPopUp(s32 i, s32 n, u32 battlerId, u32 ability)
|
||||
@ -962,6 +1005,7 @@ void Randomly(u32 sourceLine, u32 passes, u32 trials, struct RandomlyContext ctx
|
||||
INVALID_IF(test->resultsSize > 0, "PASSES_RANDOMLY is incompatible with results");
|
||||
INVALID_IF(passes > trials, "%d passes specified, but only %d trials", passes, trials);
|
||||
STATE->rngTag = ctx.tag;
|
||||
STATE->rngTrialOffset = 0;
|
||||
STATE->runTrial = 0;
|
||||
STATE->expectedRatio = Q_4_12(passes) / trials;
|
||||
STATE->observedRatio = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user