Merge branch 'RHH/upcoming' into RHH/pr/feature/formBattleChange

# Conflicts:
#	src/data/pokemon/form_change_table_pointers.h
#	src/data/pokemon/form_change_tables.h
This commit is contained in:
Eduardo Quezada 2023-03-27 10:55:38 -03:00
commit 3888b10769
105 changed files with 2852 additions and 484 deletions

View File

@ -420,6 +420,29 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectShellTrap @ EFFECT_SHELL_TRAP
.4byte BattleScript_EffectHit @ EFFECT_PSYBLADE
.4byte BattleScript_EffectHit @ EFFECT_HYDRO_STEAM
.4byte BattleScript_EffectHitSetEntryHazard @ EFFECT_HIT_SET_ENTRY_HAZARD
.4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW
.4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE
BattleScript_StealthRockActivates::
setstealthrock BattleScript_MoveEnd
printfromtable gDmgHazardsStringIds
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectDireClaw::
setmoveeffect MOVE_EFFECT_DIRE_CLAW
goto BattleScript_EffectHit
BattleScript_EffectHitSetEntryHazard::
argumenttomoveeffect
goto BattleScript_EffectHit
BattleScript_SpikesActivates::
trysetspikes BattleScript_MoveEnd
printfromtable gDmgHazardsStringIds
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectAttackUpUserAlly:
jumpifnoally BS_ATTACKER, BattleScript_EffectAttackUp
@ -3372,6 +3395,7 @@ BattleScript_CantMakeAsleep::
orhalfword gMoveResultFlags, MOVE_RESULT_FAILED
goto BattleScript_MoveEnd
BattleScript_EffectBarbBarrage:
BattleScript_EffectPoisonHit:
setmoveeffect MOVE_EFFECT_POISON
goto BattleScript_EffectHit
@ -8627,6 +8651,7 @@ BattleScript_IntimidateActivates::
BattleScript_IntimidateLoop:
jumpifbyteequal gBattlerTarget, gBattlerAttacker, BattleScript_IntimidateLoopIncrement
jumpiftargetally BattleScript_IntimidateLoopIncrement
jumpifabsent BS_TARGET, BattleScript_IntimidateLoopIncrement
jumpifstatus2 BS_TARGET, STATUS2_SUBSTITUTE, BattleScript_IntimidateLoopIncrement
jumpifholdeffect BS_TARGET, HOLD_EFFECT_CLEAR_AMULET, BattleScript_IntimidatePrevented_Item
jumpifability BS_TARGET, ABILITY_CLEAR_BODY, BattleScript_IntimidatePrevented
@ -8654,6 +8679,7 @@ BattleScript_IntimidateLoopIncrement:
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_IntimidateLoop
BattleScript_IntimidateEnd:
copybyte sBATTLER, gBattlerAttacker
destroyabilitypopup
pause B_WAIT_TIME_MED
end3

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
255 255 255
248 200 240
224 176 232
200 144 224
240 224 248
176 120 216
77 146 186
105 179 221
238 246 246
222 222 222
197 197 197
161 161 161
48 48 48
255 255 255
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
255 255 255
184 208 248
168 200 248
81 123 173
128 168 216
200 232 248
224 248 248
248 248 248
104 104 104
48 48 48
255 255 255
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
255 255 255
160 144 32
192 176 56
224 208 88
128 112 32
72 56 24
224 80 80
176 88 88
120 72 72
48 48 48
255 255 255
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
0 0 0
189 202 71
121 168 43
120 167 42
163 126 74
116 101 78
79 66 46
115 77 43
180 133 94
233 176 96
241 241 193
183 170 147
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
111 45 22
0 0 0
190 91 13
255 139 1
255 255 255
255 231 10
251 42 6
154 157 151
193 161 19
234 238 234
122 111 115
74 67 68
90 185 248
78 86 255
111 52 255

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
0 0 0
54 52 72
101 120 143
103 139 163
84 105 130
74 75 101
90 108 136
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
180 124 35
115 66 13
255 249 234
224 159 27
255 226 121
255 200 59
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
255 255 255
206 181 41
222 198 57
247 231 140
156 132 33
189 156 41
123 99 33
49 49 49
255 255 255
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
0 0 0
188 143 70
131 85 43
186 181 176
80 74 71
239 185 78
138 128 128
220 217 215
250 225 159
246 205 93
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
48 52 78
127 216 17
0 0 0
192 240 140
128 217 18
90 137 46
129 218 19
88 135 48
107 182 17
128 217 18
93 144 44
89 136 47
255 255 255
127 216 19
48 38 64

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
255 255 255
248 216 232
198 159 217
210 171 229
224 192 240
210 192 240
160 192 232
144 200 232
160 216 240
128 216 224
208 240 240
248 240 240
248 248 248
80 80 80
48 48 48

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
255 255 255
66 66 99
90 90 132
115 115 156
90 115 230
132 140 181
125 147 246
48 48 48
255 255 255
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
0 0 0
39 194 168
124 228 211
19 101 84
187 228 77
238 250 169
234 244 124
24 152 108
126 106 28
173 171 15
43 138 128
127 175 55
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
255 255 255
255 212 0
200 56 32
248 64 48
232 232 232
48 48 48
255 255 255
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
0 0 0
130 116 116
86 86 86
54 41 49
104 78 86
56 56 56
141 105 41
223 191 65
242 241 242
184 172 179
186 140 57
0 0 0
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
0 0 0
118 109 109
143 130 127
101 77 83
73 68 68
122 108 104
141 105 41
223 191 65
242 241 242
184 172 179
186 140 57
102 78 84
0 0 0
0 0 0
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
0 0 0
81 41 75
98 61 92
66 72 65
51 4 43
201 185 199
110 86 106
84 73 92
32 41 34
255 255 255
71 50 92
64 71 94
139 165 201
183 191 209
0 0 0

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
0 0 0
0 0 0
111 167 43
179 192 68
107 144 66
84 116 48
68 93 37
124 137 52
168 87 80
168 116 111
241 241 193
168 87 80
183 170 147
121 52 57
0 0 0
0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 735 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 710 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 750 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

View File

@ -529,7 +529,6 @@ struct BattleStruct
u8 wildVictorySong;
u8 dynamicMoveType;
u8 wrappedBy[MAX_BATTLERS_COUNT];
u16 assistPossibleMoves[PARTY_SIZE * MAX_MON_MOVES]; // Each of mons can know max 4 moves.
u8 focusPunchBattlerId;
u8 battlerPreventingSwitchout;
u8 moneyMultiplier:6;

View File

@ -1,6 +1,9 @@
#ifndef GUARD_BATTLE_MAIN_H
#define GUARD_BATTLE_MAIN_H
#include "pokemon.h"
#include "data.h"
struct TrainerMoney
{
u8 classId;
@ -66,6 +69,7 @@ bool8 TryRunFromBattle(u8 battlerId);
void SpecialStatusesClear(void);
void SetTypeBeforeUsingMove(u16 move, u8 battlerAtk);
bool32 IsWildMonSmart(void);
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags);
extern struct MultiPartnerMenuPokemon gMultiPartnerParty[MULTI_PARTY_SIZE];

View File

@ -459,6 +459,8 @@ extern const u8 BattleScript_LunarDanceActivates[];
extern const u8 BattleScript_ShellTrapSetUp[];
extern const u8 BattleScript_CouldntFullyProtect[];
extern const u8 BattleScript_MoveEffectStockpileWoreOff[];
extern const u8 BattleScript_StealthRockActivates[];
extern const u8 BattleScript_SpikesActivates[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View File

@ -2,12 +2,13 @@
#define GUARD_CONFIG_ITEM_H
// Item config
#define I_SHINY_CHARM_REROLLS 3 // Amount of re-rolls if the player has the Shiny Charm. Set to 0 to disable Shiny Charm's effects.
#define I_KEY_FOSSILS GEN_LATEST // In Gen4+, all Gen 3 fossils became regular items.
#define I_KEY_ESCAPE_ROPE GEN_LATEST // In Gen8, Escape Rope became a Key Item. Keep in mind, this will make it free to buy in marts.
#define I_HEALTH_RECOVERY GEN_LATEST // In Gen7+, certain healing items recover a different amount of HP than they used to.
#define I_SITRUS_BERRY_HEAL GEN_LATEST // In Gen4+, Sitrus Berry was changed from healing 30 HP to healing 25% of Max HP.
#define I_VITAMIN_EV_CAP GEN_LATEST // In Gen8, the Vitamins no longer have a cap of 100 EV per stat.
#define I_SHINY_CHARM_REROLLS 3 // Amount of re-rolls if the player has the Shiny Charm. Set to 0 to disable Shiny Charm's effects.
#define I_KEY_FOSSILS GEN_LATEST // In Gen4+, all Gen 3 fossils became regular items.
#define I_KEY_ESCAPE_ROPE GEN_LATEST // In Gen8, Escape Rope became a Key Item. Keep in mind, this will make it free to buy in marts.
#define I_HEALTH_RECOVERY GEN_LATEST // In Gen7+, certain healing items recover a different amount of HP than they used to.
#define I_SITRUS_BERRY_HEAL GEN_LATEST // In Gen4+, Sitrus Berry was changed from healing 30 HP to healing 25% of Max HP.
#define I_VITAMIN_EV_CAP GEN_LATEST // In Gen8+, the Vitamins no longer have a cap of 100 EV per stat.
#define I_GRISEOUS_ORB_FORM_CHANGE GEN_LATEST // In Gen9+, the Griseous Orb no longer changes Giratina's form when held.
// Repel/Lure config
// These two settings are both independent and complementary.

View File

@ -372,9 +372,12 @@
#define MOVE_EFFECT_TRAP_BOTH 70
#define MOVE_EFFECT_DOUBLE_SHOCK 71
#define MOVE_EFFECT_ROUND 72
#define MOVE_EFFECT_STOCKPILE_WORE_OFF 74
#define MOVE_EFFECT_STOCKPILE_WORE_OFF 73
#define MOVE_EFFECT_DIRE_CLAW 74
#define MOVE_EFFECT_STEALTH_ROCK 75
#define MOVE_EFFECT_SPIKES 76
#define NUM_MOVE_EFFECTS 75
#define NUM_MOVE_EFFECTS 77
#define MOVE_EFFECT_AFFECTS_USER 0x4000
#define MOVE_EFFECT_CERTAIN 0x8000

View File

@ -401,7 +401,10 @@
#define EFFECT_SHELL_TRAP 395
#define EFFECT_PSYBLADE 396
#define EFFECT_HYDRO_STEAM 397
#define EFFECT_HIT_SET_ENTRY_HAZARD 398
#define EFFECT_DIRE_CLAW 399
#define EFFECT_BARB_BARRAGE 400
#define NUM_BATTLE_MOVE_EFFECTS 398
#define NUM_BATTLE_MOVE_EFFECTS 401
#endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H

View File

@ -933,4 +933,10 @@
#define B_MSG_Z_STAT_UP 5
#define B_MSG_Z_HP_TRAP 6
// gDmgHazardsStringIds
#define B_MSG_PKMNHURTBYSPIKES 0
#define B_MSG_STEALTHROCKDMG 1
#define B_MSG_POINTEDSTONESFLOAT 2
#define B_MSG_SPIKESSCATTERED 3
#endif // GUARD_CONSTANTS_BATTLE_STRING_IDS_H

View File

@ -159,6 +159,7 @@
#define HOLD_EFFECT_PUNCHING_GLOVE 178
#define HOLD_EFFECT_COVERT_CLOAK 179
#define HOLD_EFFECT_LOADED_DICE 180
#define HOLD_EFFECT_BOOSTER_ENERGY 181 // Not implemented.
#define HOLD_EFFECT_CHOICE(holdEffect)((holdEffect == HOLD_EFFECT_CHOICE_BAND || holdEffect == HOLD_EFFECT_CHOICE_SCARF || holdEffect == HOLD_EFFECT_CHOICE_SPECS))

View File

@ -928,13 +928,48 @@
#define ITEM_RUBY 756
#define ITEM_SAPPHIRE 757
// GEN IX ITEMS
#define ITEM_ABILITY_SHIELD 758
#define ITEM_CLEAR_AMULET 759
#define ITEM_PUNCHING_GLOVE 760
#define ITEM_COVERT_CLOAK 761
#define ITEM_LOADED_DICE 762
#define ITEM_AUSPICIOUS_ARMOR 763
#define ITEM_BOOSTER_ENERGY 764
#define ITEM_BIG_BAMBOO_SHOOT 765
#define ITEM_GIMMIGHOUL_COIN 766
#define ITEM_LEADERS_CREST 767
#define ITEM_MALICIOUS_ARMOR 768
#define ITEM_MIRROR_HERB 769
#define ITEM_SCROLL_OF_DARKNESS 770
#define ITEM_SCROLL_OF_WATERS 771
#define ITEM_TERA_ORB 772
#define ITEM_TINY_BAMBOO_SHOOT 773
#define ITEMS_COUNT 763
#define ITEM_BUG_TERA_SHARD 774
#define ITEM_DARK_TERA_SHARD 775
#define ITEM_DRAGON_TERA_SHARD 776
#define ITEM_ELECTRIC_TERA_SHARD 777
#define ITEM_FAIRY_TERA_SHARD 778
#define ITEM_FIGHTING_TERA_SHARD 779
#define ITEM_FIRE_TERA_SHARD 780
#define ITEM_FLYING_TERA_SHARD 781
#define ITEM_GHOST_TERA_SHARD 782
#define ITEM_GRASS_TERA_SHARD 783
#define ITEM_GROUND_TERA_SHARD 784
#define ITEM_ICE_TERA_SHARD 785
#define ITEM_NORMAL_TERA_SHARD 786
#define ITEM_POISON_TERA_SHARD 787
#define ITEM_PSYCHIC_TERA_SHARD 788
#define ITEM_ROCK_TERA_SHARD 789
#define ITEM_STEEL_TERA_SHARD 790
#define ITEM_WATER_TERA_SHARD 791
#define ITEM_ADAMANT_CRYSTAL 792
#define ITEM_GRISEOUS_CORE 793
#define ITEM_LUSTROUS_GLOBE 794
#define ITEMS_COUNT 795
#define ITEM_FIELD_ARROW ITEMS_COUNT
// A special item id associated with "Cancel"/"Exit" etc. in a list of items or decorations
@ -1001,7 +1036,4 @@
#define ITEM_B_USE_MEDICINE 1
#define ITEM_B_USE_OTHER 2
// Check if the item is one that can be used on a Pokemon.
#define ITEM_HAS_EFFECT(item) ((item) >= ITEM_POTION && (item) <= LAST_BERRY_INDEX)
#endif // GUARD_CONSTANTS_ITEMS_H

View File

@ -374,7 +374,12 @@
// All trainer parties specify the IV, level, and species for each Pokémon in the
// party. Some trainer parties also specify held items and custom moves for each
// Pokémon.
#define F_TRAINER_PARTY_CUSTOM_MOVESET (1 << 0)
#define F_TRAINER_PARTY_HELD_ITEM (1 << 1)
#define F_TRAINER_PARTY_CUSTOM_MOVESET (1 << 0)
#define F_TRAINER_PARTY_HELD_ITEM (1 << 1)
#define F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED (1 << 3)
// Trainer party defines
#define TRAINER_MON_MALE 1
#define TRAINER_MON_FEMALE 2
#endif // GUARD_TRAINERS_H

View File

@ -31,6 +31,26 @@ struct MonCoords
#define MON_COORDS_SIZE(width, height)(DIV_ROUND_UP(width, 8) << 4 | DIV_ROUND_UP(height, 8))
#define GET_MON_COORDS_WIDTH(size)((size >> 4) * 8)
#define GET_MON_COORDS_HEIGHT(size)((size & 0xF) * 8)
#define TRAINER_PARTY_IVS(hp, atk, def, speed, spatk, spdef) (hp | (atk << 5) | (def << 10) | (speed << 15) | (spatk << 20) | (spdef << 25))
#define TRAINER_PARTY_EVS(hp, atk, def, speed, spatk, spdef) ((const u8[6]){hp,atk,def,spatk,spdef,speed})
#define TRAINER_PARTY_NATURE(nature) (nature+1)
struct TrainerMonCustomized
{
const u8 *nickname;
const u8 *ev;
u32 iv;
u16 moves[4];
u16 species;
u16 heldItem;
u16 ability;
u8 lvl;
u8 ball;
u8 friendship;
u8 nature : 5;
bool8 gender : 2;
bool8 isShiny : 1;
};
struct TrainerMonNoItemDefaultMoves
{
@ -68,6 +88,7 @@ struct TrainerMonItemCustomMoves
#define NO_ITEM_CUSTOM_MOVES(party) { .NoItemCustomMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_CUSTOM_MOVESET
#define ITEM_DEFAULT_MOVES(party) { .ItemDefaultMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_HELD_ITEM
#define ITEM_CUSTOM_MOVES(party) { .ItemCustomMoves = party }, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM
#define EVERYTHING_CUSTOMIZED(party) { .EverythingCustomized = party}, .partySize = ARRAY_COUNT(party), .partyFlags = F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED
union TrainerMonPtr
{
@ -75,20 +96,21 @@ union TrainerMonPtr
const struct TrainerMonNoItemCustomMoves *NoItemCustomMoves;
const struct TrainerMonItemDefaultMoves *ItemDefaultMoves;
const struct TrainerMonItemCustomMoves *ItemCustomMoves;
const struct TrainerMonCustomized *EverythingCustomized;
};
struct Trainer
{
/*0x00*/ u8 partyFlags;
/*0x01*/ u8 trainerClass;
/*0x02*/ u8 encounterMusic_gender; // last bit is gender
/*0x03*/ u8 trainerPic;
/*0x04*/ u8 trainerName[TRAINER_NAME_LENGTH + 1];
/*0x10*/ u16 items[MAX_TRAINER_ITEMS];
/*0x18*/ bool8 doubleBattle;
/*0x1C*/ u32 aiFlags;
/*0x20*/ u8 partySize;
/*0x24*/ union TrainerMonPtr party;
/*0x00*/ u32 aiFlags;
/*0x04*/ union TrainerMonPtr party;
/*0x08*/ u16 items[MAX_TRAINER_ITEMS];
/*0x10*/ u8 trainerClass;
/*0x11*/ u8 encounterMusic_gender; // last bit is gender
/*0x12*/ u8 trainerPic;
/*0x13*/ u8 trainerName[TRAINER_NAME_LENGTH + 1];
/*0x1E*/ bool8 doubleBattle:1;
u8 partyFlags:7;
/*0x1F*/ u8 partySize;
};
#define TRAINER_ENCOUNTER_MUSIC(trainer)((gTrainers[trainer].encounterMusic_gender & 0x7F))

View File

@ -147,6 +147,9 @@
#define CAT(a, b) CAT_(a, b)
#define CAT_(a, b) a ## b
// Converts a string to a compound literal, essentially making it a pointer to const u8
#define COMPOUND_STRING(str) (const u8[]) _(str)
// This produces an error at compile-time if expr is zero.
// It looks like file.c:line: size of array `id' is negative
#define STATIC_ASSERT(expr, id) typedef char id[(expr) ? 1 : -1];

View File

@ -8834,6 +8834,45 @@ extern const u32 gItemIcon_Gem[];
extern const u32 gItemIconPalette_Ruby[];
extern const u32 gItemIconPalette_Sapphire[];
// GEN IX ITEMS
extern const u32 gItemIcon_AbilityShield[];
extern const u32 gItemIconPalette_AbilityShield[];
extern const u32 gItemIcon_AuspiciousArmor[];
extern const u32 gItemIconPalette_AuspiciousArmor[];
extern const u32 gItemIcon_BigBambooShoot[];
extern const u32 gItemIconPalette_BigBambooShoot[];
extern const u32 gItemIcon_BoosterEnergy[];
extern const u32 gItemIconPalette_BoosterEnergy[];
extern const u32 gItemIcon_CovertCloak[];
extern const u32 gItemIconPalette_CovertCloak[];
extern const u32 gItemIcon_GimmighoulCoin[];
extern const u32 gItemIconPalette_GimmighoulCoin[];
extern const u32 gItemIcon_LeadersCrest[];
extern const u32 gItemIconPalette_LeadersCrest[];
extern const u32 gItemIcon_LoadedDice[];
extern const u32 gItemIconPalette_LoadedDice[];
extern const u32 gItemIcon_MaliciousArmor[];
extern const u32 gItemIconPalette_MaliciousArmor[];
extern const u32 gItemIcon_MirrorHerb[];
extern const u32 gItemIconPalette_MirrorHerb[];
extern const u32 gItemIcon_PunchingGlove[];
extern const u32 gItemIconPalette_PunchingGlove[];
extern const u32 gItemIcon_ScrollOfDarkness[];
extern const u32 gItemIconPalette_ScrollOfDarkness[];
extern const u32 gItemIcon_ScrollOfWaters[];
extern const u32 gItemIconPalette_ScrollOfWaters[];
extern const u32 gItemIcon_TeraOrb[];
extern const u32 gItemIconPalette_TeraOrb[];
extern const u32 gItemIcon_TinyBambooShoot[];
extern const u32 gItemIconPalette_TinyBambooShoot[];
extern const u32 gItemIcon_AdamantCrystal[];
extern const u32 gItemIconPalette_AdamantCrystal[];
extern const u32 gItemIcon_GriseousCore[];
extern const u32 gItemIconPalette_GriseousCore[];
extern const u32 gItemIcon_LustrousGlobe[];
extern const u32 gItemIconPalette_LustrousGlobe[];
extern const u32 gItemIcon_ReturnToFieldArrow[];
extern const u32 gItemIconPalette_ReturnToFieldArrow[];

View File

@ -2,6 +2,7 @@
#define GUARD_POKEMON_H
#include "sprite.h"
#include "constants/items.h"
#include "constants/region_map_sections.h"
#include "constants/map_groups.h"
@ -401,7 +402,7 @@ extern const struct BattleMove gBattleMoves[];
extern const u8 gFacilityClassToPicIndex[];
extern const u8 gFacilityClassToTrainerClass[];
extern const struct SpeciesInfo gSpeciesInfo[];
extern const u8 *const gItemEffectTable[];
extern const u8 *const gItemEffectTable[ITEMS_COUNT];
extern const u32 gExperienceTables[][MAX_LEVEL + 1];
extern const struct LevelUpMove *const gLevelUpLearnsets[];
extern const u16 *const gTeachableLearnsets[];

View File

@ -20,4 +20,67 @@ u16 Random2(void);
void SeedRng(u16 seed);
void SeedRng2(u16 seed);
/* Structured random number generator.
* Instead of the caller converting bits from Random() to a meaningful
* value, the caller provides metadata that is used to return the
* meaningful value directly. This allows code to interpret the random
* call, for example, battle tests know what the domain of a random call
* is, and can exhaustively test it.
*
* RandomTag identifies the purpose of the value.
*
* RandomUniform(tag, lo, hi) returns a number from lo to hi inclusive.
*
* RandomPercentage(tag, t) returns FALSE with probability (1-t)/100,
* and TRUE with probability t/100.
*
* RandomWeighted(tag, w0, w1, ... wN) returns a number from 0 to N
* inclusive. The return value is proportional to the weights, e.g.
* RandomWeighted(..., 1, 1) returns 50% 0s and 50% 1s.
* RandomWeighted(..., 2, 1) returns 2/3 0s and 1/3 1s. */
enum RandomTag
{
RNG_NONE,
RNG_ACCURACY,
RNG_CONFUSION,
RNG_CRITICAL_HIT,
RNG_CUTE_CHARM,
RNG_DAMAGE_MODIFIER,
RNG_FLAME_BODY,
RNG_FORCE_RANDOM_SWITCH,
RNG_FROZEN,
RNG_HOLD_EFFECT_FLINCH,
RNG_INFATUATION,
RNG_PARALYSIS,
RNG_POISON_POINT,
RNG_RAMPAGE_TURNS,
RNG_SECONDARY_EFFECT,
RNG_SLEEP_TURNS,
RNG_SPEED_TIE,
RNG_STATIC,
RNG_STENCH,
};
#define RandomWeighted(tag, ...) \
({ \
const u8 weights[] = { __VA_ARGS__ }; \
u32 sum, i; \
for (i = 0, sum = 0; i < ARRAY_COUNT(weights); i++) \
sum += weights[i]; \
RandomWeightedArray(tag, sum, ARRAY_COUNT(weights), weights); \
})
#define RandomPercentage(tag, t) \
({ \
const u8 weights[] = { 100 - t, t }; \
RandomWeightedArray(tag, 100, ARRAY_COUNT(weights), weights); \
})
u32 RandomUniform(enum RandomTag, u32 lo, u32 hi);
u32 RandomWeightedArray(enum RandomTag, u32 sum, u32 n, const u8 *weights);
u32 RandomUniformDefault(enum RandomTag, u32 lo, u32 hi);
u32 RandomWeightedArrayDefault(enum RandomTag, u32 sum, u32 n, const u8 *weights);
#endif // GUARD_RANDOM_H

View File

@ -3473,6 +3473,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
break;
case EFFECT_TOXIC:
case EFFECT_POISON:
case EFFECT_BARB_BARRAGE:
IncreasePoisonScore(battlerAtk, battlerDef, move, &score);
break;
case EFFECT_LIGHT_SCREEN:
@ -3864,6 +3865,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
break;
case EFFECT_SPIKES:
case EFFECT_HIT_SET_ENTRY_HAZARD:
case EFFECT_STEALTH_ROCK:
case EFFECT_STICKY_WEB:
case EFFECT_TOXIC_SPIKES:
@ -4898,6 +4900,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
case EFFECT_HAIL:
case EFFECT_GEOMANCY:
case EFFECT_VICTORY_DANCE:
case EFFECT_HIT_SET_ENTRY_HAZARD:
score += 2;
break;
default:

View File

@ -1085,13 +1085,13 @@ static bool8 ShouldUseItem(void)
item = gBattleResources->battleHistory->trainerItems[i];
if (item == ITEM_NONE)
continue;
if (gItemEffectTable[item - ITEM_POTION] == NULL)
if (gItemEffectTable[item] == NULL)
continue;
if (item == ITEM_ENIGMA_BERRY_E_READER)
itemEffects = gSaveBlock1Ptr->enigmaBerry.itemEffect;
else
itemEffects = gItemEffectTable[item - ITEM_POTION];
itemEffects = gItemEffectTable[item];
*(gBattleStruct->AI_itemType + gActiveBattler / 2) = GetAI_ItemType(item, itemEffects);

View File

@ -118,6 +118,10 @@ static void HandleEndTurn_FinishBattle(void);
static void SpriteCB_UnusedBattleInit(struct Sprite *sprite);
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 ModifyPersonalityForNature(u32 *personality, u32 newNature);
static u32 GeneratePersonalityForGender(u32 gender, u32 species);
EWRAM_DATA u16 gBattle_BG0_X = 0;
EWRAM_DATA u16 gBattle_BG0_Y = 0;
@ -1857,72 +1861,129 @@ static void SpriteCB_UnusedBattleInit_Main(struct Sprite *sprite)
}
}
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer)
static u32 Crc32B (const u8 *data, u32 size)
{
s32 i, j;
u32 byte, crc, mask;
i = 0;
crc = 0xFFFFFFFF;
for (i = 0; i < size; ++i)
{
byte = data[i];
crc = crc ^ byte;
for (j = 7; j >= 0; --j)
{
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
}
return ~crc;
}
static u32 GeneratePartyHash(const struct Trainer *trainer, u32 i)
{
const u8 *buffer;
u32 n;
if (trainer->partyFlags == 0)
{
buffer = (const u8 *) &trainer->party.NoItemDefaultMoves[i];
n = sizeof(*trainer->party.NoItemDefaultMoves);
}
else if (trainer->partyFlags == F_TRAINER_PARTY_CUSTOM_MOVESET)
{
buffer = (const u8 *) &trainer->party.NoItemCustomMoves[i];
n = sizeof(*trainer->party.NoItemCustomMoves);
}
else if (trainer->partyFlags == F_TRAINER_PARTY_HELD_ITEM)
{
buffer = (const u8 *) &trainer->party.ItemDefaultMoves[i];
n = sizeof(*trainer->party.ItemDefaultMoves);
}
else if (trainer->partyFlags == (F_TRAINER_PARTY_HELD_ITEM | F_TRAINER_PARTY_CUSTOM_MOVESET))
{
buffer = (const u8 *) &trainer->party.ItemCustomMoves[i];
n = sizeof(*trainer->party.ItemCustomMoves);
}
else if (trainer->partyFlags == F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED)
{
buffer = (const u8 *) &trainer->party.EverythingCustomized[i];
n = sizeof(*trainer->party.EverythingCustomized);
}
return Crc32B(buffer, n);
}
static void ModifyPersonalityForNature(u32 *personality, u32 newNature)
{
u32 nature = GetNatureFromPersonality(*personality);
s32 diff = abs(nature - newNature);
s32 sign = (nature > newNature) ? 1 : -1;
if (diff > NUM_NATURES / 2)
{
diff = NUM_NATURES - diff;
sign *= -1;
}
*personality -= (diff * sign);
}
static u32 GeneratePersonalityForGender(u32 gender, u32 species)
{
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[species];
if (gender == MON_MALE)
return ((255 - speciesInfo->genderRatio) / 2) + speciesInfo->genderRatio;
else
return speciesInfo->genderRatio / 2;
}
u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags)
{
u32 nameHash = 0;
u32 personalityValue;
u8 fixedIV;
s32 i, j;
u8 monsCount;
u16 ball;
if (trainerNum == TRAINER_SECRET_BASE)
return 0;
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER && !(gBattleTypeFlags & (BATTLE_TYPE_FRONTIER
s32 ball = -1;
if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER
| BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_TRAINER_HILL)))
{
if (firstTrainer == TRUE)
ZeroEnemyPartyMons();
if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
if (battleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
{
if (gTrainers[trainerNum].partySize > PARTY_SIZE / 2)
if (trainer->partySize > PARTY_SIZE / 2)
monsCount = PARTY_SIZE / 2;
else
monsCount = gTrainers[trainerNum].partySize;
monsCount = trainer->partySize;
}
else
{
monsCount = gTrainers[trainerNum].partySize;
monsCount = trainer->partySize;
}
for (i = 0; i < monsCount; i++)
{
if (gTrainers[trainerNum].doubleBattle == TRUE)
u32 personalityHash = GeneratePartyHash(trainer, i);
if (trainer->doubleBattle == TRUE)
personalityValue = 0x80;
else if (gTrainers[trainerNum].encounterMusic_gender & F_TRAINER_FEMALE)
else if (trainer->encounterMusic_gender & F_TRAINER_FEMALE)
personalityValue = 0x78; // Use personality more likely to result in a female Pokémon
else
personalityValue = 0x88; // Use personality more likely to result in a male Pokémon
for (j = 0; gTrainers[trainerNum].trainerName[j] != EOS; j++)
nameHash += gTrainers[trainerNum].trainerName[j];
switch (gTrainers[trainerNum].partyFlags)
personalityValue += personalityHash << 8;
switch (trainer->partyFlags)
{
case 0:
{
const struct TrainerMonNoItemDefaultMoves *partyData = gTrainers[trainerNum].party.NoItemDefaultMoves;
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
nameHash += gSpeciesNames[partyData[i].species][j];
personalityValue += nameHash << 8;
const struct TrainerMonNoItemDefaultMoves *partyData = trainer->party.NoItemDefaultMoves;
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
break;
}
case F_TRAINER_PARTY_CUSTOM_MOVESET:
{
const struct TrainerMonNoItemCustomMoves *partyData = gTrainers[trainerNum].party.NoItemCustomMoves;
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
nameHash += gSpeciesNames[partyData[i].species][j];
personalityValue += nameHash << 8;
const struct TrainerMonNoItemCustomMoves *partyData = trainer->party.NoItemCustomMoves;
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
@ -1935,12 +1996,7 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
}
case F_TRAINER_PARTY_HELD_ITEM:
{
const struct TrainerMonItemDefaultMoves *partyData = gTrainers[trainerNum].party.ItemDefaultMoves;
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
nameHash += gSpeciesNames[partyData[i].species][j];
personalityValue += nameHash << 8;
const struct TrainerMonItemDefaultMoves *partyData = trainer->party.ItemDefaultMoves;
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
@ -1949,12 +2005,7 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
}
case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
{
const struct TrainerMonItemCustomMoves *partyData = gTrainers[trainerNum].party.ItemCustomMoves;
for (j = 0; gSpeciesNames[partyData[i].species][j] != EOS; j++)
nameHash += gSpeciesNames[partyData[i].species][j];
personalityValue += nameHash << 8;
const struct TrainerMonItemCustomMoves *partyData = trainer->party.ItemCustomMoves;
fixedIV = partyData[i].iv * MAX_PER_STAT_IVS / 255;
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, fixedIV, TRUE, personalityValue, OT_ID_RANDOM_NO_SHINY, 0);
@ -1967,18 +2018,88 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir
}
break;
}
case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED:
{
const struct TrainerMonCustomized *partyData = trainer->party.EverythingCustomized;
u32 otIdType = OT_ID_RANDOM_NO_SHINY;
u32 fixedOtId = 0;
if (partyData[i].gender == TRAINER_MON_MALE)
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species);
else if (partyData[i].gender == TRAINER_MON_FEMALE)
personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species);
if (partyData[i].nature != 0)
ModifyPersonalityForNature(&personalityValue, partyData[i].nature - 1);
if (partyData[i].isShiny)
{
otIdType = OT_ID_PRESET;
fixedOtId = HIHALF(personalityValue) ^ LOHALF(personalityValue);
}
CreateMon(&party[i], partyData[i].species, partyData[i].lvl, 0, TRUE, personalityValue, otIdType, fixedOtId);
SetMonData(&party[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
// 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(&party[i], MON_DATA_MOVE1 + j, &partyData[i].moves[j]);
SetMonData(&party[i], MON_DATA_PP1 + j, &gBattleMoves[partyData[i].moves[j]].pp);
}
SetMonData(&party[i], MON_DATA_IVS, &(partyData[i].iv));
if (partyData[i].ev != NULL)
{
SetMonData(&party[i], MON_DATA_HP_EV, &(partyData[i].ev[0]));
SetMonData(&party[i], MON_DATA_ATK_EV, &(partyData[i].ev[1]));
SetMonData(&party[i], MON_DATA_DEF_EV, &(partyData[i].ev[2]));
SetMonData(&party[i], MON_DATA_SPATK_EV, &(partyData[i].ev[3]));
SetMonData(&party[i], MON_DATA_SPDEF_EV, &(partyData[i].ev[4]));
SetMonData(&party[i], MON_DATA_SPEED_EV, &(partyData[i].ev[5]));
}
if (partyData[i].ability != ABILITY_NONE)
{
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species];
u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities);
for (j = 0; j < maxAbilities; ++j)
{
if (speciesInfo->abilities[j] == partyData[i].ability)
break;
}
if (j < maxAbilities)
SetMonData(&party[i], MON_DATA_ABILITY_NUM, &j);
}
SetMonData(&party[i], MON_DATA_FRIENDSHIP, &(partyData[i].friendship));
if (partyData[i].ball != ITEM_NONE)
{
ball = partyData[i].ball;
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
}
if (partyData[i].nickname != NULL)
{
SetMonData(&party[i], MON_DATA_NICKNAME, partyData[i].nickname);
}
CalculateMonStats(&party[i]);
}
}
#if B_TRAINER_CLASS_POKE_BALLS >= GEN_7
ball = (sTrainerBallTable[gTrainers[trainerNum].trainerClass]) ? sTrainerBallTable[gTrainers[trainerNum].trainerClass] : ITEM_POKE_BALL;
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
if (ball == -1)
{
ball = (sTrainerBallTable[trainer->trainerClass]) ? sTrainerBallTable[trainer->trainerClass] : ITEM_POKE_BALL;
SetMonData(&party[i], MON_DATA_POKEBALL, &ball);
}
#endif
}
gBattleTypeFlags |= gTrainers[trainerNum].doubleBattle;
}
return gTrainers[trainerNum].partySize;
return trainer->partySize;
}
static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 firstTrainer)
{
u8 retVal;
if (trainerNum == TRAINER_SECRET_BASE)
return 0;
retVal = CreateNPCTrainerPartyFromTrainer(party, &gTrainers[trainerNum], firstTrainer, gBattleTypeFlags);
gBattleTypeFlags |= gTrainers[trainerNum].doubleBattle;
}
void VBlankCB_Battle(void)

View File

@ -209,7 +209,7 @@ static const u8 sText_PkmnFellIntoNightmare[] = _("{B_DEF_NAME_WITH_PREFIX} fell
static const u8 sText_PkmnLockedInNightmare[] = _("{B_ATK_NAME_WITH_PREFIX} is locked\nin a NIGHTMARE!");
static const u8 sText_PkmnLaidCurse[] = _("{B_ATK_NAME_WITH_PREFIX} cut its own HP and\nlaid a CURSE on {B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_PkmnAfflictedByCurse[] = _("{B_ATK_NAME_WITH_PREFIX} is afflicted\nby the CURSE!");
static const u8 sText_SpikesScattered[] = _("Spikes were scattered all around\nthe opponent's side!");
static const u8 sText_SpikesScattered[] = _("Spikes were scattered all around\n{B_DEF_TEAM2} team!");
static const u8 sText_PkmnHurtBySpikes[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} is hurt\nby spikes!");
static const u8 sText_PkmnIdentified[] = _("{B_ATK_NAME_WITH_PREFIX} identified\n{B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_PkmnPerishCountFell[] = _("{B_ATK_NAME_WITH_PREFIX}'s PERISH count\nfell to {B_BUFF1}!");
@ -1475,7 +1475,10 @@ const u16 gHealingWishStringIds[] =
const u16 gDmgHazardsStringIds[] =
{
STRINGID_PKMNHURTBYSPIKES, STRINGID_STEALTHROCKDMG
[B_MSG_PKMNHURTBYSPIKES] = STRINGID_PKMNHURTBYSPIKES,
[B_MSG_STEALTHROCKDMG] = STRINGID_STEALTHROCKDMG,
[B_MSG_POINTEDSTONESFLOAT] = STRINGID_POINTEDSTONESFLOAT,
[B_MSG_SPIKESSCATTERED] = STRINGID_SPIKESSCATTERED
};
const u16 gSwitchInAbilityStringIds[] =

View File

@ -23,6 +23,7 @@
#include "main.h"
#include "palette.h"
#include "money.h"
#include "malloc.h"
#include "bg.h"
#include "string_util.h"
#include "pokemon_icon.h"
@ -1917,15 +1918,25 @@ static void Cmd_accuracycheck(void)
}
else
{
u32 accuracy;
GET_MOVE_TYPE(move, type);
if (JumpIfMoveAffectedByProtect(move))
return;
if (AccuracyCalcHelper(move))
return;
// final calculation
if ((Random() % 100 + 1) > GetTotalAccuracy(gBattlerAttacker, gBattlerTarget, move, GetBattlerAbility(gBattlerAttacker), GetBattlerAbility(gBattlerTarget),
GetBattlerHoldEffect(gBattlerAttacker, TRUE), GetBattlerHoldEffect(gBattlerTarget, TRUE)))
accuracy = GetTotalAccuracy(
gBattlerAttacker,
gBattlerTarget,
move,
GetBattlerAbility(gBattlerAttacker),
GetBattlerAbility(gBattlerTarget),
GetBattlerHoldEffect(gBattlerAttacker, TRUE),
GetBattlerHoldEffect(gBattlerTarget, TRUE)
);
if (!RandomPercentage(RNG_ACCURACY, accuracy))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
if (GetBattlerHoldEffect(gBattlerAttacker, TRUE) == HOLD_EFFECT_BLUNDER_POLICY)
@ -2104,10 +2115,8 @@ static void Cmd_critcalc(void)
gIsCriticalHit = FALSE;
else if (critChance == -2)
gIsCriticalHit = TRUE;
else if (Random() % sCriticalHitChance[critChance] == 0)
gIsCriticalHit = TRUE;
else
gIsCriticalHit = FALSE;
gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitChance[critChance] - 1, 1);
// Counter for EVO_CRITICAL_HITS.
partySlot = gBattlerPartyIndexes[gBattlerAttacker];
@ -2885,6 +2894,7 @@ void SetMoveEffect(bool32 primary, u32 certain)
bool32 mirrorArmorReflected = (GetBattlerAbility(gBattlerTarget) == ABILITY_MIRROR_ARMOR);
u32 flags = 0;
u16 battlerAbility;
bool8 activateAfterFaint = FALSE;
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT
&& gBattleMons[gBattlerTarget].hp != 0
@ -2903,6 +2913,13 @@ void SetMoveEffect(bool32 primary, u32 certain)
gBattleStruct->moveEffect2 = gBattleScripting.moveEffect;
gBattlescriptCurrInstr++;
return;
case MOVE_EFFECT_STEALTH_ROCK:
case MOVE_EFFECT_SPIKES:
case MOVE_EFFECT_PAYDAY:
case MOVE_EFFECT_STEAL_ITEM:
case MOVE_EFFECT_BUG_BITE:
activateAfterFaint = TRUE;
break;
}
if (gBattleScripting.moveEffect & MOVE_EFFECT_AFFECTS_USER)
@ -2943,10 +2960,7 @@ void SetMoveEffect(bool32 primary, u32 certain)
if (TestSheerForceFlag(gBattlerAttacker, gCurrentMove) && affectsUser != MOVE_EFFECT_AFFECTS_USER)
INCREMENT_RESET_RETURN
if (gBattleMons[gEffectBattler].hp == 0
&& gBattleScripting.moveEffect != MOVE_EFFECT_PAYDAY
&& gBattleScripting.moveEffect != MOVE_EFFECT_STEAL_ITEM
&& gBattleScripting.moveEffect != MOVE_EFFECT_BUG_BITE)
if (gBattleMons[gEffectBattler].hp == 0 && !activateAfterFaint)
INCREMENT_RESET_RETURN
if (DoesSubstituteBlockMove(gBattlerAttacker, gEffectBattler, gCurrentMove) && affectsUser != MOVE_EFFECT_AFFECTS_USER)
@ -3153,9 +3167,9 @@ void SetMoveEffect(bool32 primary, u32 certain)
if (sStatusFlagsForMoveEffects[gBattleScripting.moveEffect] == STATUS1_SLEEP)
#if B_SLEEP_TURNS >= GEN_5
gBattleMons[gEffectBattler].status1 |= ((Random() % 3) + 2);
gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 1, 3));
#else
gBattleMons[gEffectBattler].status1 |= ((Random() % 4) + 3);
gBattleMons[gEffectBattler].status1 |= STATUS1_SLEEP_TURN(1 + RandomUniform(RNG_SLEEP_TURNS, 2, 5));
#endif
else
gBattleMons[gEffectBattler].status1 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect];
@ -3558,7 +3572,7 @@ void SetMoveEffect(bool32 primary, u32 certain)
{
gBattleMons[gEffectBattler].status2 |= STATUS2_MULTIPLETURNS;
gLockedMoves[gEffectBattler] = gCurrentMove;
gBattleMons[gEffectBattler].status2 |= STATUS2_LOCK_CONFUSE_TURN((Random() & 1) + 2); // thrash for 2-3 turns
gBattleMons[gEffectBattler].status2 |= STATUS2_LOCK_CONFUSE_TURN(RandomUniform(RNG_RAMPAGE_TURNS, 2, 3));
}
break;
case MOVE_EFFECT_SP_ATK_TWO_DOWN: // Overheat
@ -3761,6 +3775,30 @@ void SetMoveEffect(bool32 primary, u32 certain)
TryUpdateRoundTurnOrder(); // If another Pokémon uses Round before the user this turn, the user will use Round directly after it
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_DIRE_CLAW:
if (!gBattleMons[gEffectBattler].status1)
{
static const u8 sDireClawEffects[] = { MOVE_EFFECT_POISON, MOVE_EFFECT_PARALYSIS, MOVE_EFFECT_SLEEP };
gBattleScripting.moveEffect = sDireClawEffects[Random() % ARRAY_COUNT(sDireClawEffects)];
SetMoveEffect(TRUE, 0);
}
break;
case MOVE_EFFECT_STEALTH_ROCK:
if (!(gSideStatuses[GetBattlerSide(gEffectBattler)] & SIDE_STATUS_STEALTH_ROCK))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_POINTEDSTONESFLOAT;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_StealthRockActivates;
}
break;
case MOVE_EFFECT_SPIKES:
if (gSideTimers[GetBattlerSide(gEffectBattler)].spikesAmount < 3)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SPIKESSCATTERED;
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_SpikesActivates;
}
break;
}
}
}
@ -3779,20 +3817,23 @@ static void Cmd_seteffectwithchance(void)
else
percentChance = gBattleMoves[gCurrentMove].secondaryEffectChance;
if (gBattleScripting.moveEffect & MOVE_EFFECT_CERTAIN
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
&& gBattleScripting.moveEffect)
{
gBattleScripting.moveEffect &= ~MOVE_EFFECT_CERTAIN;
SetMoveEffect(FALSE, MOVE_EFFECT_CERTAIN);
}
else if (Random() % 100 < percentChance
&& gBattleScripting.moveEffect
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{
if (percentChance >= 100)
if (gBattleScripting.moveEffect & MOVE_EFFECT_CERTAIN
|| percentChance >= 100)
{
gBattleScripting.moveEffect &= ~MOVE_EFFECT_CERTAIN;
SetMoveEffect(FALSE, MOVE_EFFECT_CERTAIN);
else
}
else if (RandomPercentage(RNG_SECONDARY_EFFECT, percentChance))
{
SetMoveEffect(FALSE, 0);
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
else
{
@ -5322,7 +5363,8 @@ static bool32 TryKnockOffBattleScript(u32 battlerDef)
gLastUsedItem = gBattleMons[battlerDef].item;
gBattleMons[battlerDef].item = 0;
gBattleStruct->choicedMove[battlerDef] = 0;
if (gBattleMons[battlerDef].ability != ABILITY_GORILLA_TACTICS)
gBattleStruct->choicedMove[battlerDef] = 0;
gWishFutureKnock.knockedOffMons[side] |= gBitTable[gBattlerPartyIndexes[battlerDef]];
CheckSetUnburden(battlerDef);
@ -6965,9 +7007,9 @@ bool32 ShouldPostponeSwitchInAbilities(u32 battlerId)
// Checks for double battle, so abilities like Intimidate wait until all battlers are switched-in before activating.
if (IsDoubleBattle())
{
if (aliveOpposing1 && !aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_OPPOSITE(battlerId), PARTY_SIZE, PARTY_SIZE))
if (aliveOpposing1 && !aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerId)), PARTY_SIZE, PARTY_SIZE))
return TRUE;
if (!aliveOpposing1 && aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_PARTNER(BATTLE_OPPOSITE(battlerId)), PARTY_SIZE, PARTY_SIZE))
if (!aliveOpposing1 && aliveOpposing2 && !HasNoMonsToSwitch(BATTLE_OPPOSITE(battlerId), PARTY_SIZE, PARTY_SIZE))
return TRUE;
}
@ -7028,7 +7070,7 @@ static void Cmd_switchineffects(void)
gBattleMoveDamage = 1;
gSideStatuses[GetBattlerSide(gActiveBattler)] |= SIDE_STATUS_SPIKES_DAMAGED;
SetDmgHazardsBattlescript(gActiveBattler, 0);
SetDmgHazardsBattlescript(gActiveBattler, B_MSG_PKMNHURTBYSPIKES);
}
else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STEALTH_ROCK_DAMAGED)
&& (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_STEALTH_ROCK)
@ -7039,7 +7081,7 @@ static void Cmd_switchineffects(void)
gBattleMoveDamage = GetStealthHazardDamage(gBattleMoves[MOVE_STEALTH_ROCK].type, gActiveBattler);
if (gBattleMoveDamage != 0)
SetDmgHazardsBattlescript(gActiveBattler, 1);
SetDmgHazardsBattlescript(gActiveBattler, B_MSG_STEALTHROCKDMG);
}
else if (!(gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_TOXIC_SPIKES_DAMAGED)
&& (gSideStatuses[GetBattlerSide(gActiveBattler)] & SIDE_STATUS_TOXIC_SPIKES)
@ -7484,6 +7526,12 @@ static u32 GetTrainerMoneyToGive(u16 trainerId)
lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl;
}
break;
case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED:
{
const struct TrainerMonCustomized *party = gTrainers[trainerId].party.EverythingCustomized;
lastMonLevel = party[gTrainers[trainerId].partySize - 1].lvl;
}
break;
}
for (; gTrainerMoneyTable[i].classId != 0xFF; i++)
@ -12276,7 +12324,7 @@ static void Cmd_forcerandomswitch(void)
*(gBattleStruct->battlerPartyIndexes + gBattlerTarget) = gBattlerPartyIndexes[gBattlerTarget];
gBattlescriptCurrInstr = BattleScript_RoarSuccessSwitch;
gBattleStruct->forcedSwitch |= gBitTable[gBattlerTarget];
*(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = validMons[Random() % validMonsCount];
*(gBattleStruct->monToSwitchIntoId + gBattlerTarget) = validMons[RandomUniform(RNG_FORCE_RANDOM_SWITCH, 0, validMonsCount - 1)];
if (!IsMultiBattle())
SwitchPartyOrder(gBattlerTarget);
@ -12483,7 +12531,7 @@ static void Cmd_tryKO(void)
if (gCurrentMove == MOVE_SHEER_COLD && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_ICE))
odds -= 10;
#endif
if (Random() % 100 + 1 < odds && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
if (RandomPercentage(RNG_ACCURACY, odds) && gBattleMons[gBattlerAttacker].level >= gBattleMons[gBattlerTarget].level)
lands = TRUE;
}
@ -14699,38 +14747,40 @@ static void Cmd_assistattackselect(void)
s32 chooseableMovesNo = 0;
struct Pokemon *party;
s32 monId, moveId;
u16 *validMoves = gBattleStruct->assistPossibleMoves;
u16 *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES);
if (GET_BATTLER_SIDE(gBattlerAttacker) != B_SIDE_PLAYER)
party = gEnemyParty;
else
party = gPlayerParty;
for (monId = 0; monId < PARTY_SIZE; monId++)
if (validMoves != NULL)
{
if (monId == gBattlerPartyIndexes[gBattlerAttacker])
continue;
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE)
continue;
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
continue;
if (GET_BATTLER_SIDE(gBattlerAttacker) != B_SIDE_PLAYER)
party = gEnemyParty;
else
party = gPlayerParty;
for (moveId = 0; moveId < MAX_MON_MOVES; moveId++)
for (monId = 0; monId < PARTY_SIZE; monId++)
{
s32 i = 0;
u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
if (sForbiddenMoves[move] & FORBIDDEN_ASSIST)
if (monId == gBattlerPartyIndexes[gBattlerAttacker])
continue;
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE)
continue;
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
continue;
validMoves[chooseableMovesNo] = move;
chooseableMovesNo++;
for (moveId = 0; moveId < MAX_MON_MOVES; moveId++)
{
u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
if (sForbiddenMoves[move] & FORBIDDEN_ASSIST)
continue;
validMoves[chooseableMovesNo++] = move;
}
}
}
if (chooseableMovesNo)
{
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gCalledMove = validMoves[((Random() & 0xFF) * chooseableMovesNo) >> 8];
gCalledMove = validMoves[Random() % chooseableMovesNo];
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -14738,6 +14788,8 @@ static void Cmd_assistattackselect(void)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
TRY_FREE_AND_SET_NULL(validMoves);
}
static void Cmd_trysetmagiccoat(void)

View File

@ -798,6 +798,14 @@ static u8 GetSumOfEnemyPartyLevel(u16 opponentId, u8 numMons)
sum += party[i].lvl;
}
break;
case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED:
{
const struct TrainerMonCustomized *party;
party = gTrainers[opponentId].party.EverythingCustomized;
for (i = 0; i < count; i++)
sum += party[i].lvl;
}
break;
}
return sum;

View File

@ -23,6 +23,7 @@
#include "field_message_box.h"
#include "tv.h"
#include "battle_factory.h"
#include "constants/abilities.h"
#include "constants/apprentice.h"
#include "constants/battle_dome.h"
#include "constants/battle_frontier.h"
@ -3007,6 +3008,7 @@ static void FillPartnerParty(u16 trainerId)
u16 monId;
u32 otID;
u8 trainerName[(PLAYER_NAME_LENGTH * 3) + 1];
s32 ball = -1;
SetFacilityPtrsGetLevel();
if (trainerId == TRAINER_STEVEN_PARTNER)
@ -3098,6 +3100,53 @@ static void FillPartnerParty(u16 trainerId)
}
break;
}
case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED:
{
const struct TrainerMonCustomized *partyData = gTrainers[trainerId - TRAINER_CUSTOM_PARTNER].party.EverythingCustomized;
CreateMon(&gPlayerParty[i], partyData[i].species, partyData[i].lvl, 0, TRUE, j, TRUE, otID);
SetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM, &partyData[i].heldItem);
// 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], MON_DATA_MOVE1 + j, &partyData[i].moves[j]);
SetMonData(&gPlayerParty[i], MON_DATA_PP1 + j, &gBattleMoves[partyData[i].moves[j]].pp);
}
SetMonData(&gPlayerParty[i], MON_DATA_IVS, &(partyData[i].iv));
if (partyData[i].ev != NULL)
{
SetMonData(&gPlayerParty[i], MON_DATA_HP_EV, &(partyData[i].ev[0]));
SetMonData(&gPlayerParty[i], MON_DATA_ATK_EV, &(partyData[i].ev[1]));
SetMonData(&gPlayerParty[i], MON_DATA_DEF_EV, &(partyData[i].ev[2]));
SetMonData(&gPlayerParty[i], MON_DATA_SPATK_EV, &(partyData[i].ev[3]));
SetMonData(&gPlayerParty[i], MON_DATA_SPDEF_EV, &(partyData[i].ev[4]));
SetMonData(&gPlayerParty[i], MON_DATA_SPEED_EV, &(partyData[i].ev[5]));
}
if (partyData[i].ability != ABILITY_NONE)
{
const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species];
u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities);
for (j = 0; j < maxAbilities; ++j)
{
if (speciesInfo->abilities[j] == partyData[i].ability)
break;
}
if (j < maxAbilities)
SetMonData(&gPlayerParty[i], MON_DATA_ABILITY_NUM, &j);
}
SetMonData(&gPlayerParty[i], MON_DATA_FRIENDSHIP, &(partyData[i].friendship));
if (partyData[i].ball != ITEM_NONE)
{
ball = partyData[i].ball;
SetMonData(&gPlayerParty[i], MON_DATA_POKEBALL, &ball);
}
if (partyData[i].nickname != NULL)
{
SetMonData(&gPlayerParty[i], MON_DATA_NICKNAME, partyData[i].nickname);
}
CalculateMonStats(&gPlayerParty[i]);
}
}
StringCopy(trainerName, gTrainers[trainerId - TRAINER_CUSTOM_PARTNER].trainerName);

View File

@ -2127,15 +2127,22 @@ enum
ENDTURN_FIELD_COUNT,
};
static bool32 TryEndTerrain(u32 terrainFlag, u32 stringTableId)
static bool32 EndTurnTerrain(u32 terrainFlag, u32 stringTableId)
{
if (gFieldStatuses & terrainFlag
&& (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0))
if (gFieldStatuses & terrainFlag)
{
gFieldStatuses &= ~terrainFlag;
TryToRevertMimicry();
gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId;
BattleScriptExecute(BattleScript_TerrainEnds);
if (!(gFieldStatuses & STATUS_FIELD_TERRAIN_PERMANENT) && --gFieldTimers.terrainTimer == 0)
{
gFieldStatuses &= ~terrainFlag;
TryToRevertMimicry();
if (!(terrainFlag & STATUS_FIELD_GRASSY_TERRAIN))
{
gBattleCommunication[MULTISTRING_CHOOSER] = stringTableId;
BattleScriptExecute(BattleScript_TerrainEnds);
}
}
if (terrainFlag & STATUS_FIELD_GRASSY_TERRAIN)
BattleScriptExecute(BattleScript_GrassyTerrainHeals);
return TRUE;
}
return FALSE;
@ -2492,19 +2499,19 @@ u8 DoFieldEndTurnEffects(void)
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_ELECTRIC_TERRAIN:
effect = TryEndTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAINENDS_ELECTRIC);
effect = EndTurnTerrain(STATUS_FIELD_ELECTRIC_TERRAIN, B_MSG_TERRAINENDS_ELECTRIC);
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_MISTY_TERRAIN:
effect = TryEndTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAINENDS_MISTY);
effect = EndTurnTerrain(STATUS_FIELD_MISTY_TERRAIN, B_MSG_TERRAINENDS_MISTY);
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_GRASSY_TERRAIN:
effect = TryEndTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAINENDS_GRASS);
effect = EndTurnTerrain(STATUS_FIELD_GRASSY_TERRAIN, B_MSG_TERRAINENDS_GRASS);
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_PSYCHIC_TERRAIN:
effect = TryEndTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAINENDS_PSYCHIC);
effect = EndTurnTerrain(STATUS_FIELD_PSYCHIC_TERRAIN, B_MSG_TERRAINENDS_PSYCHIC);
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_WATER_SPORT:
@ -3479,7 +3486,7 @@ u8 AtkCanceller_UnableToUseMove(void)
case CANCELLER_FROZEN: // check being frozen
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE && !(gBattleMoves[gCurrentMove].flags & FLAG_THAW_USER))
{
if (Random() % 5)
if (!RandomPercentage(RNG_FROZEN, 20))
{
gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen;
gHitMarker |= HITMARKER_NO_ATTACKSTRING;
@ -3597,9 +3604,9 @@ u8 AtkCanceller_UnableToUseMove(void)
{
// confusion dmg
#if B_CONFUSION_SELF_DMG_CHANCE >= GEN_7
if (Random() % 3 == 0)
if (RandomWeighted(RNG_CONFUSION, 2, 1))
#else
if (Random() % 2 == 0)
if (RandomWeighted(RNG_CONFUSION, 1, 1))
#endif
{
gBattleCommunication[MULTISTRING_CHOOSER] = TRUE;
@ -3625,7 +3632,7 @@ u8 AtkCanceller_UnableToUseMove(void)
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_PARALYSED: // paralysis
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && (Random() % 4) == 0)
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && !RandomPercentage(RNG_PARALYSIS, 75))
{
gProtectStructs[gBattlerAttacker].prlzImmobility = TRUE;
// This is removed in FRLG and Emerald for some reason
@ -3640,7 +3647,7 @@ u8 AtkCanceller_UnableToUseMove(void)
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION)
{
gBattleScripting.battler = CountTrailingZeroBits((gBattleMons[gBattlerAttacker].status2 & STATUS2_INFATUATION) >> 0x10);
if (Random() & 1)
if (!RandomPercentage(RNG_INFATUATION, 50))
{
BattleScriptPushCursor();
}
@ -5588,7 +5595,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
&& TARGET_TURN_DAMAGED
&& CanBePoisoned(gBattlerTarget, gBattlerAttacker)
&& IsMoveMakingContact(move, gBattlerAttacker)
&& (Random() % 3) == 0)
&& RandomWeighted(RNG_POISON_POINT, 2, 1))
{
gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_POISON;
PREPARE_ABILITY_BUFFER(gBattleTextBuff1, gLastUsedAbility);
@ -5606,7 +5613,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
&& TARGET_TURN_DAMAGED
&& CanBeParalyzed(gBattlerAttacker)
&& IsMoveMakingContact(move, gBattlerAttacker)
&& (Random() % 3) == 0)
&& RandomWeighted(RNG_STATIC, 2, 1))
{
gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_PARALYSIS;
BattleScriptPushCursor();
@ -5622,7 +5629,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
&& (IsMoveMakingContact(move, gBattlerAttacker))
&& TARGET_TURN_DAMAGED
&& CanBeBurned(gBattlerAttacker)
&& (Random() % 3) == 0)
&& RandomWeighted(RNG_FLAME_BODY, 2, 1))
{
gBattleScripting.moveEffect = MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_BURN;
BattleScriptPushCursor();
@ -5638,7 +5645,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
&& (IsMoveMakingContact(move, gBattlerAttacker))
&& TARGET_TURN_DAMAGED
&& gBattleMons[gBattlerTarget].hp != 0
&& (Random() % 3) == 0
&& RandomWeighted(RNG_CUTE_CHARM, 2, 1)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_OBLIVIOUS
&& !IsAbilityOnSide(gBattlerAttacker, ABILITY_AROMA_VEIL)
&& GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != GetGenderFromSpeciesAndPersonality(speciesDef, pidDef)
@ -5856,7 +5863,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
&& gBattleMons[gBattlerTarget].hp != 0
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
&& (Random() % 10) == 0
&& RandomWeighted(RNG_STENCH, 9, 1)
&& !IS_MOVE_STATUS(move)
&& !sMovesNotAffectedByStench[gCurrentMove])
{
@ -6487,8 +6494,8 @@ bool32 CanBeFrozen(u8 battlerId)
bool32 CanBeConfused(u8 battlerId)
{
if (GetBattlerAbility(gEffectBattler) == ABILITY_OWN_TEMPO
|| gBattleMons[gEffectBattler].status2 & STATUS2_CONFUSION
if (GetBattlerAbility(battlerId) == ABILITY_OWN_TEMPO
|| gBattleMons[battlerId].status2 & STATUS2_CONFUSION
|| IsBattlerTerrainAffected(battlerId, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
@ -7576,7 +7583,7 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn)
if (gBattleMoveDamage != 0 // Need to have done damage
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
&& TARGET_TURN_DAMAGED
&& (Random() % 100) < atkHoldEffectParam
&& RandomPercentage(RNG_HOLD_EFFECT_FLINCH, atkHoldEffectParam)
&& gBattleMoves[gCurrentMove].flags & FLAG_KINGS_ROCK_AFFECTED
&& gBattleMons[gBattlerTarget].hp)
{
@ -8135,7 +8142,7 @@ u32 GetBattlerHoldEffect(u8 battlerId, bool32 checkNegating)
return gBattleStruct->debugHoldEffects[battlerId];
else
#endif
if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY)
if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY_E_READER)
return gEnigmaBerries[battlerId].holdEffect;
else
return ItemId_GetHoldEffect(gBattleMons[battlerId].item);
@ -8144,7 +8151,7 @@ u32 GetBattlerHoldEffect(u8 battlerId, bool32 checkNegating)
//
static u32 GetBattlerItemHoldEffectParam(u8 battlerId, u16 item)
{
if (item == ITEM_ENIGMA_BERRY)
if (item == ITEM_ENIGMA_BERRY_E_READER)
return gEnigmaBerries[battlerId].holdEffectParam;
else
return ItemId_GetHoldEffectParam(item);
@ -8152,7 +8159,7 @@ static u32 GetBattlerItemHoldEffectParam(u8 battlerId, u16 item)
u32 GetBattlerHoldEffectParam(u8 battlerId)
{
if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY)
if (gBattleMons[battlerId].item == ITEM_ENIGMA_BERRY_E_READER)
return gEnigmaBerries[battlerId].holdEffectParam;
else
return ItemId_GetHoldEffectParam(gBattleMons[battlerId].item);
@ -9057,6 +9064,7 @@ static u32 CalcMoveBasePowerAfterModifiers(u16 move, u8 battlerAtk, u8 battlerDe
if (gBattleMons[battlerDef].hp <= (gBattleMons[battlerDef].maxHP / 2))
MulModifier(&modifier, UQ_4_12(2.0));
break;
case EFFECT_BARB_BARRAGE:
case EFFECT_VENOSHOCK:
if (gBattleMons[battlerDef].status1 & STATUS1_PSN_ANY)
MulModifier(&modifier, UQ_4_12(2.0));
@ -9694,7 +9702,7 @@ static s32 DoMoveDamageCalc(u16 move, u8 battlerAtk, u8 battlerDef, u8 moveType,
// Add a random factor.
if (randomFactor)
{
dmg *= 100 - (Random() % 16);
dmg *= 100 - RandomUniform(RNG_DAMAGE_MODIFIER, 0, 15);
dmg /= 100;
}

View File

@ -199,7 +199,7 @@ bool32 IsViableZMove(u8 battlerId, u16 move)
holdEffect = gBattleStruct->debugHoldEffects[battlerId];
else
#endif
if (item == ITEM_ENIGMA_BERRY)
if (item == ITEM_ENIGMA_BERRY_E_READER)
return FALSE; // HoldEffect = gEnigmaBerries[battlerId].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(item);

View File

@ -3,6 +3,7 @@
#include "battle.h"
#include "data.h"
#include "graphics.h"
#include "constants/abilities.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/trainers.h"

View File

@ -12452,11 +12452,11 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
#else
.power = 60,
#endif
.effect = EFFECT_PLACEHOLDER, // EFFECT_DIRE_CLAW,
.effect = EFFECT_DIRE_CLAW,
.type = TYPE_POISON,
.accuracy = 100,
.pp = 15,
.secondaryEffectChance = 0,
.secondaryEffectChance = 50,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST,
@ -12496,17 +12496,18 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_STONE_AXE] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_STONE_AXE,
.effect = EFFECT_HIT_SET_ENTRY_HAZARD,
.power = 65,
.type = TYPE_ROCK,
.accuracy = 90,
.pp = 15,
.secondaryEffectChance = 0,
.target = MOVE_TARGET_USER,
.secondaryEffectChance = 100,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_SLICING_MOVE,
.split = SPLIT_PHYSICAL,
.zMoveEffect = Z_EFFECT_NONE,
.argument = MOVE_EFFECT_STEALTH_ROCK,
},
[MOVE_SPRINGTIDE_STORM] =
@ -12651,12 +12652,16 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_BARB_BARRAGE] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_BARB_BARRAGE,
.effect = EFFECT_BARB_BARRAGE,
.power = 60,
.type = TYPE_POISON,
.accuracy = 100,
.pp = 15,
.secondaryEffectChance = 0,
#if B_UPDATED_MOVE_DATA >= GEN_9
.pp = 10,
#else
.pp = 15,
#endif
.secondaryEffectChance = 50,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST,
@ -12753,17 +12758,18 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_CEASELESS_EDGE] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_CEASELESS_EDGE,
.effect = EFFECT_HIT_SET_ENTRY_HAZARD,
.power = 65,
.type = TYPE_DARK,
.accuracy = 90,
.pp = 15,
.secondaryEffectChance = 0,
.secondaryEffectChance = 100,
.target = MOVE_TARGET_SELECTED,
.priority = 0,
.flags = FLAG_MAKES_CONTACT | FLAG_PROTECT_AFFECTED | FLAG_MIRROR_MOVE_AFFECTED | FLAG_KINGS_ROCK_AFFECTED | FLAG_SHEER_FORCE_BOOST | FLAG_SLICING_MOVE,
.split = SPLIT_PHYSICAL,
.zMoveEffect = Z_EFFECT_NONE,
.argument = MOVE_EFFECT_SPIKES,
},
[MOVE_BLEAKWIND_STORM] =

View File

@ -1931,17 +1931,61 @@ const u32 gItemIconPalette_Ruby[] = INCBIN_U32("graphics/items/icon_palettes/rub
const u32 gItemIconPalette_Sapphire[] = INCBIN_U32("graphics/items/icon_palettes/sapphire.gbapal.lz");
//const u32 gItemIcon_AbilityShield[] = INCBIN_U32("graphics/items/icons/ability_shield.4bpp.lz");
//const u32 gItemIconPalette_AbilityShield[] = INCBIN_U32("graphics/items/icon_palettes/ability_shield.gbapal.lz");
const u32 gItemIcon_AbilityShield[] = INCBIN_U32("graphics/items/icons/ability_shield.4bpp.lz");
const u32 gItemIconPalette_AbilityShield[] = INCBIN_U32("graphics/items/icon_palettes/ability_shield.gbapal.lz");
//const u32 gItemIcon_ClearAmulet[] = INCBIN_U32("graphics/items/icons/clear_amulet.4bpp.lz");
//const u32 gItemIconPalette_ClearAmulet[] = INCBIN_U32("graphics/items/icon_palettes/clear_amulet.gbapal.lz");
//const u32 gItemIcon_PunchingGlove[] = INCBIN_U32("graphics/items/icons/punching_glove.4bpp.lz");
//const u32 gItemIconPalette_PunchingGlove[] = INCBIN_U32("graphics/items/icon_palettes/punching_glove.gbapal.lz");
const u32 gItemIcon_PunchingGlove[] = INCBIN_U32("graphics/items/icons/punching_glove.4bpp.lz");
const u32 gItemIconPalette_PunchingGlove[] = INCBIN_U32("graphics/items/icon_palettes/punching_glove.gbapal.lz");
//const u32 gItemIcon_CovertCloak[] = INCBIN_U32("graphics/items/icons/covert_cloak.4bpp.lz");
//const u32 gItemIconPalette_CovertCloak[] = INCBIN_U32("graphics/items/icon_palettes/covert_cloak.gbapal.lz");
const u32 gItemIcon_CovertCloak[] = INCBIN_U32("graphics/items/icons/covert_cloak.4bpp.lz");
const u32 gItemIconPalette_CovertCloak[] = INCBIN_U32("graphics/items/icon_palettes/covert_cloak.gbapal.lz");
//const u32 gItemIcon_LoadedDice[] = INCBIN_U32("graphics/items/icons/loaded_dice.4bpp.lz");
//const u32 gItemIconPalette_LoadedDice[] = INCBIN_U32("graphics/items/icon_palettes/loaded_dice.gbapal.lz");
const u32 gItemIcon_LoadedDice[] = INCBIN_U32("graphics/items/icons/loaded_dice.4bpp.lz");
const u32 gItemIconPalette_LoadedDice[] = INCBIN_U32("graphics/items/icon_palettes/loaded_dice.gbapal.lz");
const u32 gItemIcon_AuspiciousArmor[] = INCBIN_U32("graphics/items/icons/auspicious_armor.4bpp.lz");
const u32 gItemIconPalette_AuspiciousArmor[] = INCBIN_U32("graphics/items/icon_palettes/auspicious_armor.gbapal.lz");
const u32 gItemIcon_BigBambooShoot[] = INCBIN_U32("graphics/items/icons/big_bamboo_shoot.4bpp.lz");
const u32 gItemIconPalette_BigBambooShoot[] = INCBIN_U32("graphics/items/icon_palettes/big_bamboo_shoot.gbapal.lz");
const u32 gItemIcon_BoosterEnergy[] = INCBIN_U32("graphics/items/icons/booster_energy.4bpp.lz");
const u32 gItemIconPalette_BoosterEnergy[] = INCBIN_U32("graphics/items/icon_palettes/booster_energy.gbapal.lz");
const u32 gItemIcon_GimmighoulCoin[] = INCBIN_U32("graphics/items/icons/gimmighoul_coin.4bpp.lz");
const u32 gItemIconPalette_GimmighoulCoin[] = INCBIN_U32("graphics/items/icon_palettes/gimmighoul_coin.gbapal.lz");
const u32 gItemIcon_LeadersCrest[] = INCBIN_U32("graphics/items/icons/leaders_crest.4bpp.lz");
const u32 gItemIconPalette_LeadersCrest[] = INCBIN_U32("graphics/items/icon_palettes/leaders_crest.gbapal.lz");
const u32 gItemIcon_MaliciousArmor[] = INCBIN_U32("graphics/items/icons/malicious_armor.4bpp.lz");
const u32 gItemIconPalette_MaliciousArmor[] = INCBIN_U32("graphics/items/icon_palettes/malicious_armor.gbapal.lz");
const u32 gItemIcon_MirrorHerb[] = INCBIN_U32("graphics/items/icons/mirror_herb.4bpp.lz");
const u32 gItemIconPalette_MirrorHerb[] = INCBIN_U32("graphics/items/icon_palettes/mirror_herb.gbapal.lz");
const u32 gItemIcon_ScrollOfDarkness[] = INCBIN_U32("graphics/items/icons/scroll_of_darkness.4bpp.lz");
const u32 gItemIconPalette_ScrollOfDarkness[] = INCBIN_U32("graphics/items/icon_palettes/scroll_of_darkness.gbapal.lz");
const u32 gItemIcon_ScrollOfWaters[] = INCBIN_U32("graphics/items/icons/scroll_of_waters.4bpp.lz");
const u32 gItemIconPalette_ScrollOfWaters[] = INCBIN_U32("graphics/items/icon_palettes/scroll_of_waters.gbapal.lz");
const u32 gItemIcon_TeraOrb[] = INCBIN_U32("graphics/items/icons/tera_orb.4bpp.lz");
const u32 gItemIconPalette_TeraOrb[] = INCBIN_U32("graphics/items/icon_palettes/tera_orb.gbapal.lz");
const u32 gItemIcon_TinyBambooShoot[] = INCBIN_U32("graphics/items/icons/tiny_bamboo_shoot.4bpp.lz");
const u32 gItemIconPalette_TinyBambooShoot[] = INCBIN_U32("graphics/items/icon_palettes/tiny_bamboo_shoot.gbapal.lz");
// Tera Shards here
const u32 gItemIcon_AdamantCrystal[] = INCBIN_U32("graphics/items/icons/adamant_crystal.4bpp.lz");
const u32 gItemIconPalette_AdamantCrystal[] = INCBIN_U32("graphics/items/icon_palettes/adamant_crystal.gbapal.lz");
const u32 gItemIcon_GriseousCore[] = INCBIN_U32("graphics/items/icons/griseous_core.4bpp.lz");
const u32 gItemIconPalette_GriseousCore[] = INCBIN_U32("graphics/items/icon_palettes/griseous_core.gbapal.lz");
const u32 gItemIcon_LustrousGlobe[] = INCBIN_U32("graphics/items/icons/lustrous_globe.4bpp.lz");
const u32 gItemIconPalette_LustrousGlobe[] = INCBIN_U32("graphics/items/icon_palettes/lustrous_globe.gbapal.lz");

View File

@ -803,11 +803,43 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] =
[ITEM_TEA] = {gItemIcon_Tea, gItemIconPalette_Tea},
[ITEM_RUBY] = {gItemIcon_Gem, gItemIconPalette_Ruby},
[ITEM_SAPPHIRE] = {gItemIcon_Gem, gItemIconPalette_Sapphire},
[ITEM_ABILITY_SHIELD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_AbilityShield, gItemIconPalette_AbilityShield},
[ITEM_ABILITY_SHIELD] = {gItemIcon_AbilityShield, gItemIconPalette_AbilityShield},
[ITEM_CLEAR_AMULET] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ClearAmulet, gItemIconPalette_ClearAmulet},
[ITEM_PUNCHING_GLOVE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PunchingGlove, gItemIconPalette_PunchingGlove},
[ITEM_COVERT_CLOAK] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_CovertCloak, gItemIconPalette_CovertCloak},
[ITEM_LOADED_DICE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_LoadedDice, gItemIconPalette_LoadedDice},
[ITEM_PUNCHING_GLOVE] = {gItemIcon_PunchingGlove, gItemIconPalette_PunchingGlove},
[ITEM_COVERT_CLOAK] = {gItemIcon_CovertCloak, gItemIconPalette_CovertCloak},
[ITEM_LOADED_DICE] = {gItemIcon_LoadedDice, gItemIconPalette_LoadedDice},
[ITEM_AUSPICIOUS_ARMOR] = {gItemIcon_AuspiciousArmor, gItemIconPalette_AuspiciousArmor},
[ITEM_BOOSTER_ENERGY] = {gItemIcon_BoosterEnergy, gItemIconPalette_BoosterEnergy},
[ITEM_BIG_BAMBOO_SHOOT] = {gItemIcon_BigBambooShoot, gItemIconPalette_BigBambooShoot},
[ITEM_GIMMIGHOUL_COIN] = {gItemIcon_GimmighoulCoin, gItemIconPalette_GimmighoulCoin},
[ITEM_LEADERS_CREST] = {gItemIcon_LeadersCrest, gItemIconPalette_LeadersCrest},
[ITEM_MALICIOUS_ARMOR] = {gItemIcon_MaliciousArmor, gItemIconPalette_MaliciousArmor},
[ITEM_MIRROR_HERB] = {gItemIcon_MirrorHerb, gItemIconPalette_MirrorHerb},
[ITEM_SCROLL_OF_DARKNESS] = {gItemIcon_ScrollOfDarkness, gItemIconPalette_ScrollOfDarkness},
[ITEM_SCROLL_OF_WATERS] = {gItemIcon_ScrollOfWaters, gItemIconPalette_ScrollOfWaters},
[ITEM_TERA_ORB] = {gItemIcon_TeraOrb, gItemIconPalette_TeraOrb},
[ITEM_TINY_BAMBOO_SHOOT] = {gItemIcon_TinyBambooShoot, gItemIconPalette_TinyBambooShoot},
[ITEM_BUG_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_BugTeraShard, gItemIconPalette_BugTeraShard},
[ITEM_DARK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_DarkTeraShard, gItemIconPalette_DarkTeraShard},
[ITEM_DRAGON_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_DragonTeraShard, gItemIconPalette_DragonTeraShard},
[ITEM_ELECTRIC_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ElectricTeraShard, gItemIconPalette_ElectricTeraShard},
[ITEM_FAIRY_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FairyTeraShard, gItemIconPalette_FairyTeraShard},
[ITEM_FIGHTING_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FightingTeraShard, gItemIconPalette_FightingTeraShard},
[ITEM_FIRE_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FireTeraShard, gItemIconPalette_FireTeraShard},
[ITEM_FLYING_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FlyingTeraShard, gItemIconPalette_FlyingTeraShard},
[ITEM_GHOST_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GhostTeraShard, gItemIconPalette_GhostTeraShard},
[ITEM_GRASS_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GrassTeraShard, gItemIconPalette_GrassTeraShard},
[ITEM_GROUND_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GroundTeraShard, gItemIconPalette_GroundTeraShard},
[ITEM_ICE_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_IceTeraShard, gItemIconPalette_IceTeraShard},
[ITEM_NORMAL_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_NormalTeraShard, gItemIconPalette_NormalTeraShard},
[ITEM_POISON_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PoisonTeraShard, gItemIconPalette_PoisonTeraShard},
[ITEM_PSYCHIC_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PsychicTeraShard, gItemIconPalette_PsychicTeraShard},
[ITEM_ROCK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_RockTeraShard, gItemIconPalette_RockTeraShard},
[ITEM_STEEL_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_SteelTeraShard, gItemIconPalette_SteelTeraShard},
[ITEM_WATER_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_WaterTeraShard, gItemIconPalette_WaterTeraShard},
[ITEM_ADAMANT_CRYSTAL] = {gItemIcon_AdamantCrystal, gItemIconPalette_AdamantCrystal},
[ITEM_GRISEOUS_CORE] = {gItemIcon_GriseousCore, gItemIconPalette_GriseousCore},
[ITEM_LUSTROUS_GLOBE] = {gItemIcon_LustrousGlobe, gItemIconPalette_LustrousGlobe},
// Return to field arrow
[ITEMS_COUNT] = {gItemIcon_ReturnToFieldArrow, gItemIconPalette_ReturnToFieldArrow},
};

View File

@ -9896,7 +9896,6 @@ const struct Item gItems[] =
[ITEM_LOADED_DICE] =
{
//YellwApricorn
.name = _("Loaded Dice"),
.itemId = ITEM_LOADED_DICE,
.price = 20000,
@ -9907,4 +9906,370 @@ const struct Item gItems[] =
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.flingPower = 30,
},
[ITEM_AUSPICIOUS_ARMOR] =
{
.name = _("AuspciousArmr"),
.itemId = ITEM_AUSPICIOUS_ARMOR,
.price = 3000,
.description = sAuspiciousArmorDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_PARTY_MENU,
.fieldUseFunc = ItemUseOutOfBattle_EvolutionStone,
.flingPower = 30,
},
[ITEM_BOOSTER_ENERGY] =
{
.name = _("BoosterEnergy"),
.itemId = ITEM_BOOSTER_ENERGY,
.price = 0,
.holdEffect = HOLD_EFFECT_BOOSTER_ENERGY,
.description = sBoosterEnergyDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.flingPower = 30,
},
[ITEM_BIG_BAMBOO_SHOOT] =
{
.name = _("BigBmbooShoot"),
.itemId = ITEM_BIG_BAMBOO_SHOOT,
.price = 3000,
.description = sBigBambooShootDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.flingPower = 30,
},
[ITEM_GIMMIGHOUL_COIN] =
{
.name = _("GimighoulCoin"),
.itemId = ITEM_GIMMIGHOUL_COIN,
.price = 400,
.description = sGimmighoulCoinDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_LEADERS_CREST] =
{
.name = _("Leader'sCrest"),
.itemId = ITEM_LEADERS_CREST,
.price = 3000,
.description = sLeadersCrestDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_MALICIOUS_ARMOR] =
{
.name = _("MaliciousArmr"),
.itemId = ITEM_MALICIOUS_ARMOR,
.price = 3000,
.description = sMaliciousArmorDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_PARTY_MENU,
.fieldUseFunc = ItemUseOutOfBattle_EvolutionStone,
.flingPower = 30,
},
[ITEM_MIRROR_HERB] =
{
.name = _("Mirror Herb"),
.itemId = ITEM_MIRROR_HERB,
.price = 30000,
.holdEffect = HOLD_EFFECT_MIRROR_HERB,
.description = sMirrorHerbDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.flingPower = 30,
},
[ITEM_SCROLL_OF_DARKNESS] =
{
.name = _("ScrllOfDrknss"),
.itemId = ITEM_SCROLL_OF_DARKNESS,
.price = 0,
.description = sScrollOfDarknessDesc,
.importance = 1,
.pocket = POCKET_KEY_ITEMS,
.type = ITEM_USE_PARTY_MENU,
.fieldUseFunc = ItemUseOutOfBattle_EvolutionStone,
},
[ITEM_SCROLL_OF_WATERS] =
{
.name = _("ScrollOfWatrs"),
.itemId = ITEM_SCROLL_OF_WATERS,
.price = 0,
.description = sScrollOfWatersDesc,
.importance = 1,
.pocket = POCKET_KEY_ITEMS,
.type = ITEM_USE_PARTY_MENU,
.fieldUseFunc = ItemUseOutOfBattle_EvolutionStone,
},
[ITEM_TERA_ORB] =
{
.name = _("Tera Orb"),
.itemId = ITEM_TERA_ORB,
.price = 0,
.description = sTeraOrbDesc,
.importance = 1,
.pocket = POCKET_KEY_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_TINY_BAMBOO_SHOOT] =
{
.name = _("TinyBmbooShot"),
.itemId = ITEM_TINY_BAMBOO_SHOOT,
.price = 750,
.description = sTinyBambooShootDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.flingPower = 30,
},
[ITEM_BUG_TERA_SHARD] =
{
.name = _("Bug TeraShard"),
.itemId = ITEM_BUG_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_DARK_TERA_SHARD] =
{
.name = _("DarkTeraShard"),
.itemId = ITEM_DARK_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_DRAGON_TERA_SHARD] =
{
.name = _("DragnTeraShrd"),
.itemId = ITEM_DRAGON_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_ELECTRIC_TERA_SHARD] =
{
.name = _("EltrcTeraShrd"),
.itemId = ITEM_ELECTRIC_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_FAIRY_TERA_SHARD] =
{
.name = _("FairyTeraShrd"),
.itemId = ITEM_FAIRY_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_FIGHTING_TERA_SHARD] =
{
.name = _("FghtngTerShrd"),
.itemId = ITEM_FIGHTING_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_FIRE_TERA_SHARD] =
{
.name = _("FireTeraShard"),
.itemId = ITEM_FIRE_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_FLYING_TERA_SHARD] =
{
.name = _("FlyngTeraShrd"),
.itemId = ITEM_FLYING_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_GHOST_TERA_SHARD] =
{
.name = _("GhostTeraShrd"),
.itemId = ITEM_GHOST_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_GRASS_TERA_SHARD] =
{
.name = _("GrassTeraShrd"),
.itemId = ITEM_GRASS_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_GROUND_TERA_SHARD] =
{
.name = _("GrondTeraShrd"),
.itemId = ITEM_GROUND_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_ICE_TERA_SHARD] =
{
.name = _("Ice TeraShard"),
.itemId = ITEM_ICE_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_NORMAL_TERA_SHARD] =
{
.name = _("NormlTeraShrd"),
.itemId = ITEM_NORMAL_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_POISON_TERA_SHARD] =
{
.name = _("PoisnTeraShrd"),
.itemId = ITEM_POISON_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_PSYCHIC_TERA_SHARD] =
{
.name = _("PschcTeraShrd"),
.itemId = ITEM_PSYCHIC_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_ROCK_TERA_SHARD] =
{
.name = _("RockTeraShard"),
.itemId = ITEM_ROCK_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_STEEL_TERA_SHARD] =
{
.name = _("SteelTeraShrd"),
.itemId = ITEM_STEEL_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_WATER_TERA_SHARD] =
{
.name = _("WaterTeraShrd"),
.itemId = ITEM_WATER_TERA_SHARD,
.price = 0,
.description = sTeraShardDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
},
[ITEM_ADAMANT_CRYSTAL] =
{
.name = _("AdamantCrystl"),
.itemId = ITEM_ADAMANT_CRYSTAL,
.price = 0,
.description = sAdamantCrystalDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.flingPower = 60,
},
[ITEM_GRISEOUS_CORE] =
{
.name = _("Griseous Core"),
.itemId = ITEM_GRISEOUS_CORE,
.price = 0,
.description = sGriseousCoreDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.flingPower = 60,
},
[ITEM_LUSTROUS_GLOBE] =
{
.name = _("LustrousGlobe"),
.itemId = ITEM_LUSTROUS_GLOBE,
.price = 0,
.description = sLustrousGlobeDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.flingPower = 60,
},
};

View File

@ -491,7 +491,9 @@ const struct Evolution gEvolutionTable[NUM_SPECIES][EVOS_PER_MON] =
[SPECIES_DREEPY] = {{EVO_LEVEL, 50, SPECIES_DRAKLOAK}},
[SPECIES_DRAKLOAK] = {{EVO_LEVEL, 60, SPECIES_DRAGAPULT}},
[SPECIES_KUBFU] = {{EVO_DARK_SCROLL, 0, SPECIES_URSHIFU},
{EVO_WATER_SCROLL, 0, SPECIES_URSHIFU_RAPID_STRIKE_STYLE}},
{EVO_ITEM, ITEM_SCROLL_OF_DARKNESS, SPECIES_URSHIFU},
{EVO_WATER_SCROLL, 0, SPECIES_URSHIFU_RAPID_STRIKE_STYLE},
{EVO_ITEM, ITEM_SCROLL_OF_WATERS, SPECIES_URSHIFU_RAPID_STRIKE_STYLE}},
#endif
[SPECIES_RATTATA_ALOLAN] = {{EVO_LEVEL_NIGHT, 20, SPECIES_RATICATE_ALOLAN}},
[SPECIES_SANDSHREW_ALOLAN] = {{EVO_ITEM, ITEM_ICE_STONE, SPECIES_SANDSLASH_ALOLAN}},

View File

@ -98,6 +98,10 @@ const struct FormChange *const gFormChangeTablePointers[NUM_SPECIES] =
[SPECIES_ABOMASNOW_MEGA] = sAbomasnowFormChangeTable,
[SPECIES_GALLADE] = sGalladeFormChangeTable,
[SPECIES_GALLADE_MEGA] = sGalladeFormChangeTable,
[SPECIES_DIALGA] = sDialgaFormChangeTable,
[SPECIES_DIALGA_ORIGIN] = sDialgaFormChangeTable,
[SPECIES_PALKIA] = sPalkiaFormChangeTable,
[SPECIES_PALKIA_ORIGIN] = sPalkiaFormChangeTable,
[SPECIES_GIRATINA] = sGiratinaFormChangeTable,
[SPECIES_GIRATINA_ORIGIN] = sGiratinaFormChangeTable,
[SPECIES_SHAYMIN] = sShayminFormChangeTable,

View File

@ -243,9 +243,24 @@ static const struct FormChange sGalladeFormChangeTable[] = {
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sDialgaFormChangeTable[] = {
{FORM_CHANGE_ITEM_HOLD, SPECIES_DIALGA, ITEM_NONE},
{FORM_CHANGE_ITEM_HOLD, SPECIES_DIALGA_ORIGIN, ITEM_ADAMANT_CRYSTAL},
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sPalkiaFormChangeTable[] = {
{FORM_CHANGE_ITEM_HOLD, SPECIES_PALKIA, ITEM_NONE},
{FORM_CHANGE_ITEM_HOLD, SPECIES_PALKIA_ORIGIN, ITEM_LUSTROUS_GLOBE},
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sGiratinaFormChangeTable[] = {
{FORM_CHANGE_ITEM_HOLD, SPECIES_GIRATINA, ITEM_NONE},
#if I_GRISEOUS_ORB_FORM_CHANGE < GEN_9
{FORM_CHANGE_ITEM_HOLD, SPECIES_GIRATINA_ORIGIN, ITEM_GRISEOUS_ORB},
#endif
{FORM_CHANGE_ITEM_HOLD, SPECIES_GIRATINA_ORIGIN, ITEM_GRISEOUS_CORE},
{FORM_CHANGE_TERMINATOR},
};

View File

@ -447,128 +447,132 @@ const u8 gItemEffect_TamatoBerry[10] = {
EV_BERRY_FRIENDSHIP_CHANGE,
};
const u8 *const gItemEffectTable[] =
const u8 *const gItemEffectTable[ITEMS_COUNT] =
{
// Medicine
[ITEM_POTION - ITEM_POTION] = gItemEffect_Potion,
[ITEM_SUPER_POTION - ITEM_POTION] = gItemEffect_SuperPotion,
[ITEM_HYPER_POTION - ITEM_POTION] = gItemEffect_HyperPotion,
[ITEM_MAX_POTION - ITEM_POTION] = gItemEffect_MaxPotion,
[ITEM_FULL_RESTORE - ITEM_POTION] = gItemEffect_FullRestore,
[ITEM_REVIVE - ITEM_POTION] = gItemEffect_Revive,
[ITEM_MAX_REVIVE - ITEM_POTION] = gItemEffect_MaxRevive,
[ITEM_FRESH_WATER - ITEM_POTION] = gItemEffect_FreshWater,
[ITEM_SODA_POP - ITEM_POTION] = gItemEffect_SodaPop,
[ITEM_LEMONADE - ITEM_POTION] = gItemEffect_Lemonade,
[ITEM_MOOMOO_MILK - ITEM_POTION] = gItemEffect_MoomooMilk,
[ITEM_ENERGY_POWDER - ITEM_POTION] = gItemEffect_EnergyPowder,
[ITEM_ENERGY_ROOT - ITEM_POTION] = gItemEffect_EnergyRoot,
[ITEM_HEAL_POWDER - ITEM_POTION] = gItemEffect_HealPowder,
[ITEM_REVIVAL_HERB - ITEM_POTION] = gItemEffect_RevivalHerb,
[ITEM_ANTIDOTE - ITEM_POTION] = gItemEffect_Antidote,
[ITEM_PARALYZE_HEAL - ITEM_POTION] = gItemEffect_ParalyzeHeal,
[ITEM_BURN_HEAL - ITEM_POTION] = gItemEffect_BurnHeal,
[ITEM_ICE_HEAL - ITEM_POTION] = gItemEffect_IceHeal,
[ITEM_AWAKENING - ITEM_POTION] = gItemEffect_Awakening,
[ITEM_FULL_HEAL - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_ETHER - ITEM_POTION] = gItemEffect_Ether,
[ITEM_MAX_ETHER - ITEM_POTION] = gItemEffect_MaxEther,
[ITEM_ELIXIR - ITEM_POTION] = gItemEffect_Elixir,
[ITEM_MAX_ELIXIR - ITEM_POTION] = gItemEffect_MaxElixir,
[ITEM_BERRY_JUICE - ITEM_POTION] = gItemEffect_BerryJuice,
[ITEM_SACRED_ASH - ITEM_POTION] = gItemEffect_SacredAsh,
[ITEM_SWEET_HEART - ITEM_POTION] = gItemEffect_Potion,
[ITEM_MAX_HONEY - ITEM_POTION] = gItemEffect_MaxRevive,
[ITEM_POTION] = gItemEffect_Potion,
[ITEM_SUPER_POTION] = gItemEffect_SuperPotion,
[ITEM_HYPER_POTION] = gItemEffect_HyperPotion,
[ITEM_MAX_POTION] = gItemEffect_MaxPotion,
[ITEM_FULL_RESTORE] = gItemEffect_FullRestore,
[ITEM_REVIVE] = gItemEffect_Revive,
[ITEM_MAX_REVIVE] = gItemEffect_MaxRevive,
[ITEM_FRESH_WATER] = gItemEffect_FreshWater,
[ITEM_SODA_POP] = gItemEffect_SodaPop,
[ITEM_LEMONADE] = gItemEffect_Lemonade,
[ITEM_MOOMOO_MILK] = gItemEffect_MoomooMilk,
[ITEM_ENERGY_POWDER] = gItemEffect_EnergyPowder,
[ITEM_ENERGY_ROOT] = gItemEffect_EnergyRoot,
[ITEM_HEAL_POWDER] = gItemEffect_HealPowder,
[ITEM_REVIVAL_HERB] = gItemEffect_RevivalHerb,
[ITEM_ANTIDOTE] = gItemEffect_Antidote,
[ITEM_PARALYZE_HEAL] = gItemEffect_ParalyzeHeal,
[ITEM_BURN_HEAL] = gItemEffect_BurnHeal,
[ITEM_ICE_HEAL] = gItemEffect_IceHeal,
[ITEM_AWAKENING] = gItemEffect_Awakening,
[ITEM_FULL_HEAL] = gItemEffect_FullHeal,
[ITEM_ETHER] = gItemEffect_Ether,
[ITEM_MAX_ETHER] = gItemEffect_MaxEther,
[ITEM_ELIXIR] = gItemEffect_Elixir,
[ITEM_MAX_ELIXIR] = gItemEffect_MaxElixir,
[ITEM_BERRY_JUICE] = gItemEffect_BerryJuice,
[ITEM_SACRED_ASH] = gItemEffect_SacredAsh,
[ITEM_SWEET_HEART] = gItemEffect_Potion,
[ITEM_MAX_HONEY] = gItemEffect_MaxRevive,
// Regional Specialties
[ITEM_PEWTER_CRUNCHIES - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_RAGE_CANDY_BAR - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_LAVA_COOKIE - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_OLD_GATEAU - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_CASTELIACONE - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_LUMIOSE_GALETTE - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_SHALOUR_SABLE - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_BIG_MALASADA - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_PEWTER_CRUNCHIES] = gItemEffect_FullHeal,
[ITEM_RAGE_CANDY_BAR] = gItemEffect_FullHeal,
[ITEM_LAVA_COOKIE] = gItemEffect_FullHeal,
[ITEM_OLD_GATEAU] = gItemEffect_FullHeal,
[ITEM_CASTELIACONE] = gItemEffect_FullHeal,
[ITEM_LUMIOSE_GALETTE] = gItemEffect_FullHeal,
[ITEM_SHALOUR_SABLE] = gItemEffect_FullHeal,
[ITEM_BIG_MALASADA] = gItemEffect_FullHeal,
// Vitamins
[ITEM_HP_UP - ITEM_POTION] = gItemEffect_HPUp,
[ITEM_PROTEIN - ITEM_POTION] = gItemEffect_Protein,
[ITEM_IRON - ITEM_POTION] = gItemEffect_Iron,
[ITEM_CALCIUM - ITEM_POTION] = gItemEffect_Calcium,
[ITEM_ZINC - ITEM_POTION] = gItemEffect_Zinc,
[ITEM_CARBOS - ITEM_POTION] = gItemEffect_Carbos,
[ITEM_PP_UP - ITEM_POTION] = gItemEffect_PPUp,
[ITEM_PP_MAX - ITEM_POTION] = gItemEffect_PPMax,
[ITEM_HP_UP] = gItemEffect_HPUp,
[ITEM_PROTEIN] = gItemEffect_Protein,
[ITEM_IRON] = gItemEffect_Iron,
[ITEM_CALCIUM] = gItemEffect_Calcium,
[ITEM_ZINC] = gItemEffect_Zinc,
[ITEM_CARBOS] = gItemEffect_Carbos,
[ITEM_PP_UP] = gItemEffect_PPUp,
[ITEM_PP_MAX] = gItemEffect_PPMax,
// EV Feathers
[ITEM_HEALTH_FEATHER - ITEM_POTION] = gItemEffect_HpFeather,
[ITEM_MUSCLE_FEATHER - ITEM_POTION] = gItemEffect_AtkFeather,
[ITEM_RESIST_FEATHER - ITEM_POTION] = gItemEffect_DefFeather,
[ITEM_GENIUS_FEATHER - ITEM_POTION] = gItemEffect_SpatkFeather,
[ITEM_CLEVER_FEATHER - ITEM_POTION] = gItemEffect_SpdefFeather,
[ITEM_SWIFT_FEATHER - ITEM_POTION] = gItemEffect_SpeedFeather,
[ITEM_HEALTH_FEATHER] = gItemEffect_HpFeather,
[ITEM_MUSCLE_FEATHER] = gItemEffect_AtkFeather,
[ITEM_RESIST_FEATHER] = gItemEffect_DefFeather,
[ITEM_GENIUS_FEATHER] = gItemEffect_SpatkFeather,
[ITEM_CLEVER_FEATHER] = gItemEffect_SpdefFeather,
[ITEM_SWIFT_FEATHER] = gItemEffect_SpeedFeather,
// Candy
[ITEM_RARE_CANDY - ITEM_POTION] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_XS - ITEM_POTION] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_S - ITEM_POTION] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_M - ITEM_POTION] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_L - ITEM_POTION] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_XL - ITEM_POTION] = gItemEffect_RareCandy,
//[ITEM_DYNAMAX_CANDY - ITEM_POTION] = gItemEffect_DynamaxCandy, // Todo
[ITEM_RARE_CANDY] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_XS] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_S] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_M] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_L] = gItemEffect_RareCandy,
[ITEM_EXP_CANDY_XL] = gItemEffect_RareCandy,
//[ITEM_DYNAMAX_CANDY] = gItemEffect_DynamaxCandy, // Todo
// Medicinal Flutes
[ITEM_BLUE_FLUTE - ITEM_POTION] = gItemEffect_BlueFlute,
[ITEM_YELLOW_FLUTE - ITEM_POTION] = gItemEffect_YellowFlute,
[ITEM_RED_FLUTE - ITEM_POTION] = gItemEffect_RedFlute,
[ITEM_BLUE_FLUTE] = gItemEffect_BlueFlute,
[ITEM_YELLOW_FLUTE] = gItemEffect_YellowFlute,
[ITEM_RED_FLUTE] = gItemEffect_RedFlute,
// X Items
[ITEM_X_ATTACK - ITEM_POTION] = gItemEffect_XAttack,
[ITEM_X_DEFENSE - ITEM_POTION] = gItemEffect_XDefense,
[ITEM_X_SPEED - ITEM_POTION] = gItemEffect_XSpeed,
[ITEM_X_ACCURACY - ITEM_POTION] = gItemEffect_XAccuracy,
[ITEM_X_SP_ATK - ITEM_POTION] = gItemEffect_XSpecialAttack,
[ITEM_X_SP_DEF - ITEM_POTION] = gItemEffect_XSpecialDefense,
[ITEM_X_ATTACK] = gItemEffect_XAttack,
[ITEM_X_DEFENSE] = gItemEffect_XDefense,
[ITEM_X_SPEED] = gItemEffect_XSpeed,
[ITEM_X_ACCURACY] = gItemEffect_XAccuracy,
[ITEM_X_SP_ATK] = gItemEffect_XSpecialAttack,
[ITEM_X_SP_DEF] = gItemEffect_XSpecialDefense,
[ITEM_DIRE_HIT - ITEM_POTION] = gItemEffect_DireHit,
[ITEM_GUARD_SPEC - ITEM_POTION] = gItemEffect_GuardSpec,
[ITEM_DIRE_HIT] = gItemEffect_DireHit,
[ITEM_GUARD_SPEC] = gItemEffect_GuardSpec,
//[ITEM_MAX_MUSHROOMS - ITEM_POTION] = gItemEffect_MaxMushrooms, // Todo
//[ITEM_MAX_MUSHROOMS] = gItemEffect_MaxMushrooms, // Todo
// Evolution Items
[ITEM_FIRE_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_WATER_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_THUNDER_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_LEAF_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_ICE_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_SUN_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_MOON_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_SHINY_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_DUSK_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_DAWN_STONE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_SWEET_APPLE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_TART_APPLE - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_CRACKED_POT - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_CHIPPED_POT - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_GALARICA_CUFF - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_GALARICA_WREATH - ITEM_POTION] = gItemEffect_EvoItem,
[ITEM_FIRE_STONE] = gItemEffect_EvoItem,
[ITEM_WATER_STONE] = gItemEffect_EvoItem,
[ITEM_THUNDER_STONE] = gItemEffect_EvoItem,
[ITEM_LEAF_STONE] = gItemEffect_EvoItem,
[ITEM_ICE_STONE] = gItemEffect_EvoItem,
[ITEM_SUN_STONE] = gItemEffect_EvoItem,
[ITEM_MOON_STONE] = gItemEffect_EvoItem,
[ITEM_SHINY_STONE] = gItemEffect_EvoItem,
[ITEM_DUSK_STONE] = gItemEffect_EvoItem,
[ITEM_DAWN_STONE] = gItemEffect_EvoItem,
[ITEM_SWEET_APPLE] = gItemEffect_EvoItem,
[ITEM_TART_APPLE] = gItemEffect_EvoItem,
[ITEM_CRACKED_POT] = gItemEffect_EvoItem,
[ITEM_CHIPPED_POT] = gItemEffect_EvoItem,
[ITEM_GALARICA_CUFF] = gItemEffect_EvoItem,
[ITEM_GALARICA_WREATH] = gItemEffect_EvoItem,
[ITEM_AUSPICIOUS_ARMOR] = gItemEffect_EvoItem,
[ITEM_MALICIOUS_ARMOR] = gItemEffect_EvoItem,
[ITEM_SCROLL_OF_DARKNESS] = gItemEffect_EvoItem,
[ITEM_SCROLL_OF_WATERS] = gItemEffect_EvoItem,
// Berries
[ITEM_CHERI_BERRY - ITEM_POTION] = gItemEffect_CheriBerry,
[ITEM_CHESTO_BERRY - ITEM_POTION] = gItemEffect_ChestoBerry,
[ITEM_PECHA_BERRY - ITEM_POTION] = gItemEffect_PechaBerry,
[ITEM_RAWST_BERRY - ITEM_POTION] = gItemEffect_RawstBerry,
[ITEM_ASPEAR_BERRY - ITEM_POTION] = gItemEffect_AspearBerry,
[ITEM_LEPPA_BERRY - ITEM_POTION] = gItemEffect_LeppaBerry,
[ITEM_ORAN_BERRY - ITEM_POTION] = gItemEffect_OranBerry,
[ITEM_PERSIM_BERRY - ITEM_POTION] = gItemEffect_PersimBerry,
[ITEM_LUM_BERRY - ITEM_POTION] = gItemEffect_FullHeal,
[ITEM_SITRUS_BERRY - ITEM_POTION] = gItemEffect_SitrusBerry,
[ITEM_POMEG_BERRY - ITEM_POTION] = gItemEffect_PomegBerry,
[ITEM_KELPSY_BERRY - ITEM_POTION] = gItemEffect_KelpsyBerry,
[ITEM_QUALOT_BERRY - ITEM_POTION] = gItemEffect_QualotBerry,
[ITEM_HONDEW_BERRY - ITEM_POTION] = gItemEffect_HondewBerry,
[ITEM_GREPA_BERRY - ITEM_POTION] = gItemEffect_GrepaBerry,
[ITEM_TAMATO_BERRY - ITEM_POTION] = gItemEffect_TamatoBerry,
[LAST_BERRY_INDEX - ITEM_POTION] = NULL,
[ITEM_CHERI_BERRY] = gItemEffect_CheriBerry,
[ITEM_CHESTO_BERRY] = gItemEffect_ChestoBerry,
[ITEM_PECHA_BERRY] = gItemEffect_PechaBerry,
[ITEM_RAWST_BERRY] = gItemEffect_RawstBerry,
[ITEM_ASPEAR_BERRY] = gItemEffect_AspearBerry,
[ITEM_LEPPA_BERRY] = gItemEffect_LeppaBerry,
[ITEM_ORAN_BERRY] = gItemEffect_OranBerry,
[ITEM_PERSIM_BERRY] = gItemEffect_PersimBerry,
[ITEM_LUM_BERRY] = gItemEffect_FullHeal,
[ITEM_SITRUS_BERRY] = gItemEffect_SitrusBerry,
[ITEM_POMEG_BERRY] = gItemEffect_PomegBerry,
[ITEM_KELPSY_BERRY] = gItemEffect_KelpsyBerry,
[ITEM_QUALOT_BERRY] = gItemEffect_QualotBerry,
[ITEM_HONDEW_BERRY] = gItemEffect_HondewBerry,
[ITEM_GREPA_BERRY] = gItemEffect_GrepaBerry,
[ITEM_TAMATO_BERRY] = gItemEffect_TamatoBerry,
[LAST_BERRY_INDEX] = NULL,
};

View File

@ -3817,3 +3817,78 @@ static const u8 sLoadedDiceDesc[] = _(
"Rolls high numbers.\n"
"Multihit strikes\n"
"hit more times.");
static const u8 sAuspiciousArmorDesc[] = _(
"Armor inhabited by\n"
"auspicious wishes.\n"
"Causes evolution.");
static const u8 sBoosterEnergyDesc[] = _(
"Encapsuled energy\n"
"ups Pokémon with\n"
"certain Abilities.");
static const u8 sBigBambooShootDesc[] = _(
"A large and rare\n"
"bamboo shoot. Best\n"
"sold to gourmands.");
static const u8 sGimmighoulCoinDesc[] = _(
"Gimmighoul hoard\n"
"and treasure these\n"
"curious coins.");
static const u8 sLeadersCrestDesc[] = _(
"A shard of an old\n"
"blade of some sort.\n"
"Held by Bisharp.");
static const u8 sMaliciousArmorDesc[] = _(
"Armor inhabited by\n"
"malicious will.\n"
"Causes evolution.");
static const u8 sMirrorHerbDesc[] = _(
"Mirrors an enemy's\n"
"stat increases\n"
"but only once.");
static const u8 sScrollOfDarknessDesc[] = _(
"A peculiar scroll\n"
"with secrets of\n"
"the dark path.");
static const u8 sScrollOfWatersDesc[] = _(
"A peculiar scroll\n"
"with secrets of\n"
"the water path.");
static const u8 sTeraOrbDesc[] = _(
"Energy charges can\n"
"be used to cause\n"
"Terastallization.");
static const u8 sTinyBambooShootDesc[] = _(
"A small and rare\n"
"bamboo shoot. Best\n"
"sold to gourmands.");
static const u8 sTeraShardDesc[] = _(
"These shards may\n"
"form when a Tera\n"
"Pokémon faints.");
static const u8 sAdamantCrystalDesc[] = _(
"A large, glowing gem\n"
"that lets Dialga\n"
"change form.");
static const u8 sGriseousCoreDesc[] = _(
"A large, glowing gem\n"
"that lets Giratina\n"
"change form.");
static const u8 sLustrousGlobeDesc[] = _(
"A large, glowing gem\n"
"that lets Palkia\n"
"change form.");

View File

@ -1812,6 +1812,9 @@ static void PopulateSpeciesFromTrainerParty(int matchCallId, u8 *destStr)
case F_TRAINER_PARTY_CUSTOM_MOVESET | F_TRAINER_PARTY_HELD_ITEM:
speciesName = gSpeciesNames[party.ItemCustomMoves[monId].species];
break;
case F_TRAINER_PARTY_EVERYTHING_CUSTOMIZED:
speciesName = gSpeciesNames[party.EverythingCustomized[monId].species];
break;
}
StringCopy(destStr, speciesName);

View File

@ -4288,7 +4288,10 @@ static bool8 IsHPRecoveryItem(u16 item)
if (item == ITEM_ENIGMA_BERRY_E_READER)
effect = gSaveBlock1Ptr->enigmaBerry.itemEffect;
else
effect = gItemEffectTable[item - ITEM_POTION];
effect = gItemEffectTable[item];
if (effect == NULL)
return FALSE;
if (effect[4] & ITEM4_HEAL_HP)
return TRUE;
@ -4799,9 +4802,9 @@ void ItemUseCB_PPRecovery(u8 taskId, TaskFunc task)
if (item == ITEM_ENIGMA_BERRY_E_READER)
effect = gSaveBlock1Ptr->enigmaBerry.itemEffect;
else
effect = gItemEffectTable[item - ITEM_POTION];
effect = gItemEffectTable[item];
if (!(effect[4] & ITEM4_HEAL_PP_ONE))
if (effect == NULL || !(effect[4] & ITEM4_HEAL_PP_ONE))
{
gPartyMenu.data1 = 0;
TryUsePPItem(taskId);
@ -5514,7 +5517,8 @@ void ItemUseCB_EvolutionStone(u8 taskId, TaskFunc task)
}
else
{
RemoveBagItem(gSpecialVar_ItemId, 1);
if (ItemId_GetPocket(gSpecialVar_ItemId) != POCKET_KEY_ITEMS)
RemoveBagItem(gSpecialVar_ItemId, 1);
FreePartyPointers();
}
}
@ -5674,14 +5678,14 @@ u8 GetItemEffectType(u16 item)
const u8 *itemEffect;
u32 statusCure;
if (!ITEM_HAS_EFFECT(item))
return ITEM_EFFECT_NONE;
// Read the item's effect properties.
if (item == ITEM_ENIGMA_BERRY_E_READER)
itemEffect = gSaveBlock1Ptr->enigmaBerry.itemEffect;
else
itemEffect = gItemEffectTable[item - ITEM_POTION];
itemEffect = gItemEffectTable[item];
if (itemEffect == NULL)
return ITEM_EFFECT_NONE;
if ((itemEffect[0] & ITEM0_DIRE_HIT) || itemEffect[1] || (itemEffect[3] & ITEM3_GUARD_SPEC))
return ITEM_EFFECT_X_ITEM;

View File

@ -5783,9 +5783,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
}
// Skip using the item if it won't do anything
if (!ITEM_HAS_EFFECT(item))
return TRUE;
if (gItemEffectTable[item - ITEM_POTION] == NULL && item != ITEM_ENIGMA_BERRY_E_READER)
if (gItemEffectTable[item] == NULL && item != ITEM_ENIGMA_BERRY_E_READER)
return TRUE;
// Get item effect
@ -5798,7 +5796,7 @@ bool8 PokemonUseItemEffects(struct Pokemon *mon, u16 item, u8 partyIndex, u8 mov
}
else
{
itemEffect = gItemEffectTable[item - ITEM_POTION];
itemEffect = gItemEffectTable[item];
}
// Do item effect
@ -6352,9 +6350,9 @@ u8 GetItemEffectParamOffset(u16 itemId, u8 effectByte, u8 effectBit)
offset = ITEM_EFFECT_ARG_START;
temp = gItemEffectTable[itemId - ITEM_POTION];
temp = gItemEffectTable[itemId];
if (!temp && itemId != ITEM_ENIGMA_BERRY_E_READER)
if (temp != NULL && !temp && itemId != ITEM_ENIGMA_BERRY_E_READER)
return 0;
if (itemId == ITEM_ENIGMA_BERRY_E_READER)
@ -6482,7 +6480,7 @@ u8 *UseStatIncreaseItem(u16 itemId)
}
else
{
itemEffect = gItemEffectTable[itemId - ITEM_POTION];
itemEffect = gItemEffectTable[itemId];
}
gPotentialItemEffectBattler = gBattlerInMenuId;
@ -6556,7 +6554,7 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s
partnerSpecies = GetMonData(tradePartner, MON_DATA_SPECIES, 0);
partnerHeldItem = GetMonData(tradePartner, MON_DATA_HELD_ITEM, 0);
if (partnerHeldItem == ITEM_ENIGMA_BERRY)
if (partnerHeldItem == ITEM_ENIGMA_BERRY_E_READER)
partnerHoldEffect = gSaveBlock1Ptr->enigmaBerry.holdEffect;
else
partnerHoldEffect = ItemId_GetHoldEffect(partnerHeldItem);
@ -7226,7 +7224,7 @@ void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies)
u8 bonus;
heldItem = GetMonData(mon, MON_DATA_HELD_ITEM, 0);
if (heldItem == ITEM_ENIGMA_BERRY)
if (heldItem == ITEM_ENIGMA_BERRY_E_READER)
{
if (gMain.inBattle)
holdEffect = gEnigmaBerries[0].holdEffect;

View File

@ -31,3 +31,27 @@ u16 Random2(void)
gRng2Value = ISO_RANDOMIZE1(gRng2Value);
return gRng2Value >> 16;
}
__attribute__((weak, alias("RandomUniformDefault")))
u32 RandomUniform(enum RandomTag tag, u32 lo, u32 hi);
__attribute__((weak, alias("RandomWeightedArrayDefault")))
u32 RandomWeightedArray(enum RandomTag tag, u32 sum, u32 n, const u8 *weights);
u32 RandomUniformDefault(enum RandomTag tag, u32 lo, u32 hi)
{
return lo + (((hi - lo) * Random()) >> 16);
}
u32 RandomWeightedArrayDefault(enum RandomTag tag, u32 sum, u32 n, const u8 *weights)
{
s32 i, targetSum;
targetSum = (sum * Random()) >> 16;
for (i = 0; i < n - 1; i++)
{
targetSum -= weights[i];
if (targetSum < 0)
return i;
}
return n - 1;
}

View File

@ -3,7 +3,7 @@
SINGLE_BATTLE_TEST("Compound Eyes raises accuracy")
{
PASSES_RANDOMLY(91, 100);
PASSES_RANDOMLY(91, 100, RNG_ACCURACY);
GIVEN {
ASSUME(gBattleMoves[MOVE_THUNDER].accuracy == 70);
PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); };
@ -21,12 +21,11 @@ SINGLE_BATTLE_TEST("Compound Eyes raises accuracy")
// than we expect.
SINGLE_BATTLE_TEST("Compound Eyes does not affect OHKO moves")
{
KNOWN_FAILING;
PASSES_RANDOMLY(30, 100);
PASSES_RANDOMLY(30, 100, RNG_ACCURACY);
GIVEN {
ASSUME(gBattleMoves[MOVE_FISSURE].accuracy == 30);
ASSUME(gBattleMoves[MOVE_FISSURE].effect == EFFECT_OHKO);
PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_TINTED_LENS); };
PLAYER(SPECIES_BUTTERFREE) { Ability(ABILITY_COMPOUND_EYES); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_FISSURE); }

View File

@ -1,9 +1,6 @@
#include "global.h"
#include "test_battle.h"
// TODO: Currently PASSES_RANDOMLY is incapable of testing Cute Charm
// because it only activates 33% of the time, but we only want to
// measure the 50% of the time that the infatuation prevents our move.
SINGLE_BATTLE_TEST("Cute Charm inflicts infatuation on contact")
{
u32 move;

View File

@ -98,7 +98,6 @@ DOUBLE_BATTLE_TEST("Intimidate doesn't activate on an empty field in a double ba
SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack")
{
KNOWN_FAILING; // Issue #2837
GIVEN {
ASSUME(gItems[ITEM_EJECT_BUTTON].holdEffect == HOLD_EFFECT_EJECT_BUTTON);
PLAYER(SPECIES_WOBBUFFET);
@ -123,3 +122,40 @@ SINGLE_BATTLE_TEST("Intimidate and Eject Button force the opponent to Attack")
}
}
}
DOUBLE_BATTLE_TEST("Intimidate activates on an empty slot")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_CROAGUNK);
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_HITMONTOP) { Ability(ABILITY_INTIMIDATE); };
OPPONENT(SPECIES_RALTS);
OPPONENT(SPECIES_AZURILL);
} WHEN {
TURN {
SWITCH(playerLeft, 2);
MOVE(playerRight, MOVE_GUNK_SHOT, target: opponentLeft);
MOVE(opponentRight, MOVE_SPLASH);
}
TURN {
SWITCH(playerLeft, 3);
MOVE(playerRight, MOVE_SPLASH);
}
} SCENE {
MESSAGE("Wobbuffet, that's enough! Come back!");
MESSAGE("Go! Wynaut!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_GUNK_SHOT, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPLASH, opponentRight);
MESSAGE("Wynaut, that's enough! Come back!");
MESSAGE("Go! Hitmontop!");
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
NONE_OF {
MESSAGE("Hitmontop's Intimidate cuts Foe Ralts's attack!");
}
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
MESSAGE("Hitmontop's Intimidate cuts Foe Azurill's attack!");
}
}

View File

@ -16,7 +16,7 @@ SINGLE_BATTLE_TEST("Sand Veil prevents damage from sandstorm")
SINGLE_BATTLE_TEST("Sand Veil reduces accuracy during sandstorm")
{
PASSES_RANDOMLY(4,5);
PASSES_RANDOMLY(4, 5, RNG_ACCURACY);
GIVEN {
ASSUME(gBattleMoves[MOVE_POUND].accuracy == 100);
PLAYER(SPECIES_SANDSHREW) { Ability(ABILITY_SAND_VEIL); };

View File

@ -3,7 +3,7 @@
SINGLE_BATTLE_TEST("Stench has a 10% chance to flinch")
{
PASSES_RANDOMLY(1,10);
PASSES_RANDOMLY(1, 10, RNG_STENCH);
GIVEN {
ASSUME(gBattleMoves[MOVE_TACKLE].power > 0);
PLAYER(SPECIES_GRIMER) { Ability(ABILITY_STENCH); };
@ -17,7 +17,8 @@ SINGLE_BATTLE_TEST("Stench has a 10% chance to flinch")
SINGLE_BATTLE_TEST("Stench does not stack with King's Rock")
{
PASSES_RANDOMLY(1,10);
KNOWN_FAILING;
PASSES_RANDOMLY(1, 10, RNG_STENCH);
GIVEN {
ASSUME(gItems[ITEM_KINGS_ROCK].holdEffect == HOLD_EFFECT_FLINCH);
ASSUME(gBattleMoves[MOVE_TACKLE].power > 0);

View File

@ -8,7 +8,7 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Red Card switches the attacker with a random non-fainted replacement")
{
PASSES_RANDOMLY(1, 2);
PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
OPPONENT(SPECIES_WOBBUFFET);
@ -27,7 +27,7 @@ SINGLE_BATTLE_TEST("Red Card switches the attacker with a random non-fainted rep
DOUBLE_BATTLE_TEST("Red Card switches the target with a random non-battler, non-fainted replacement")
{
PASSES_RANDOMLY(1, 2);
PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_RED_CARD); }
PLAYER(SPECIES_WYNAUT);

View File

@ -10,7 +10,7 @@ SINGLE_BATTLE_TEST("Accuracy controls the proportion of misses")
PARAMETRIZE { move = MOVE_RAZOR_LEAF; }
PARAMETRIZE { move = MOVE_SCRATCH; }
ASSUME(0 < gBattleMoves[move].accuracy && gBattleMoves[move].accuracy <= 100);
PASSES_RANDOMLY(gBattleMoves[move].accuracy, 100);
PASSES_RANDOMLY(gBattleMoves[move].accuracy, 100, RNG_ACCURACY);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@ -27,10 +27,9 @@ SINGLE_BATTLE_TEST("Secondary Effect Chance controls the proportion of secondary
PARAMETRIZE { move = MOVE_THUNDER_SHOCK; }
PARAMETRIZE { move = MOVE_DISCHARGE; }
PARAMETRIZE { move = MOVE_NUZZLE; }
ASSUME(gBattleMoves[move].accuracy == 100);
ASSUME(gBattleMoves[move].effect == EFFECT_PARALYZE_HIT);
ASSUME(0 < gBattleMoves[move].secondaryEffectChance && gBattleMoves[move].secondaryEffectChance <= 100);
PASSES_RANDOMLY(gBattleMoves[move].secondaryEffectChance, 100);
PASSES_RANDOMLY(gBattleMoves[move].secondaryEffectChance, 100, RNG_SECONDARY_EFFECT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@ -70,6 +69,7 @@ SINGLE_BATTLE_TEST("Turn order is determined by speed if priority ties")
SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and speed tie")
{
KNOWN_FAILING; // The algorithm is significantly biased.
PASSES_RANDOMLY(1, 2);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
@ -85,15 +85,29 @@ SINGLE_BATTLE_TEST("Turn order is determined randomly if priority and speed tie"
SINGLE_BATTLE_TEST("Critical hits occur at a 1/24 rate")
{
ASSUME(B_CRIT_CHANCE >= GEN_7);
ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100);
PASSES_RANDOMLY(100 / 24, 100);
PASSES_RANDOMLY(1, 24, RNG_CRITICAL_HIT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SCRATCH); }
} SCENE {
MESSAGE("It's a critical hit!");
MESSAGE("A critical hit!");
}
}
SINGLE_BATTLE_TEST("Slash's critical hits occur at a 1/8 rate")
{
ASSUME(B_CRIT_CHANCE >= GEN_7);
ASSUME(gBattleMoves[MOVE_SLASH].flags & FLAG_HIGH_CRIT);
PASSES_RANDOMLY(1, 8, RNG_CRITICAL_HIT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SLASH); }
} SCENE {
MESSAGE("A critical hit!");
}
}

View File

@ -9,7 +9,7 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Sand Attack lowers Accuracy")
{
ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100);
PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100);
PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100, RNG_ACCURACY);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);

22
test/move_effect_assist.c Normal file
View File

@ -0,0 +1,22 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_ASSIST].effect == EFFECT_ASSIST);
}
SINGLE_BATTLE_TEST("Assist fails if there are no valid moves to choose from")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) {Moves(MOVE_ASSIST, MOVE_CELEBRATE, MOVE_METRONOME, MOVE_ME_FIRST); }
PLAYER(SPECIES_WOBBUFFET) {Moves(MOVE_ASSIST, MOVE_ENDURE, MOVE_DRAGON_TAIL, MOVE_SPOTLIGHT); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_ASSIST); }
} SCENE {
MESSAGE("Wobbuffet used Assist!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSIST, player);
MESSAGE("But it failed!");
}
}

View File

@ -0,0 +1,43 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
//ASSUME(gBattleMoves[MOVE_BARB_BARRAGE].effect == EFFECT_BARB_BARRAGE);
}
SINGLE_BATTLE_TEST("Barb Barrage inflicts poison")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BARB_BARRAGE); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BARB_BARRAGE, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent);
STATUS_ICON(opponent, poison: TRUE);
}
}
SINGLE_BATTLE_TEST("Barb Barrage's power doubles if the target is poisoned/badly poisoned", s16 damage)
{
u32 status1;
PARAMETRIZE { status1 = 0; }
PARAMETRIZE { status1 = STATUS1_POISON; }
PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Status1(status1);}
} WHEN {
TURN { MOVE(player, MOVE_BARB_BARRAGE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BARB_BARRAGE, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[2].damage);
}
}

View File

@ -327,7 +327,6 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes Aurora Veil from p
DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes everything it can")
{
bool32 defogTurn = FALSE;
GIVEN {
ASSUME(gBattleMoves[MOVE_HAIL].effect == EFFECT_HAIL);
ASSUME(gSpeciesInfo[SPECIES_GLALIE].types[0] == TYPE_ICE);
@ -343,33 +342,29 @@ DOUBLE_BATTLE_TEST("Defog lowers evasiveness by 1 and removes everything it can"
TURN { MOVE(playerLeft, MOVE_STICKY_WEB); MOVE(playerRight, MOVE_SPIKES); MOVE(opponentLeft, MOVE_STICKY_WEB); MOVE(opponentRight, MOVE_SPIKES); }
TURN { SWITCH(playerLeft, 2); SWITCH(playerRight, 3); SWITCH(opponentLeft, 2); SWITCH(opponentRight, 3);}
TURN { MOVE(playerLeft, MOVE_TOXIC_SPIKES); MOVE(playerRight, MOVE_STEALTH_ROCK); MOVE(opponentLeft, MOVE_TOXIC_SPIKES); MOVE(opponentRight, MOVE_STEALTH_ROCK); }
TURN { MOVE(playerLeft, MOVE_HAIL); MOVE(playerRight, MOVE_AURORA_VEIL); MOVE(opponentLeft, MOVE_AURORA_VEIL); MOVE(opponentRight, MOVE_STEALTH_ROCK); }
TURN { MOVE(playerLeft, MOVE_REFLECT); MOVE(playerRight, MOVE_LIGHT_SCREEN); MOVE(opponentLeft, MOVE_REFLECT); MOVE(opponentRight, MOVE_LIGHT_SCREEN); }
TURN { MOVE(playerLeft, MOVE_MIST); MOVE(playerRight, MOVE_SAFEGUARD); MOVE(opponentLeft, MOVE_MIST); MOVE(opponentRight, MOVE_SAFEGUARD); }
TURN { defogTurn = TRUE ; MOVE(opponentRight, MOVE_DEFOG, target:playerLeft);}
TURN { MOVE(playerLeft, MOVE_HAIL); MOVE(playerRight, MOVE_AURORA_VEIL); MOVE(opponentLeft, MOVE_AURORA_VEIL); MOVE(opponentRight, MOVE_LIGHT_SCREEN); }
TURN { MOVE(playerLeft, MOVE_REFLECT); MOVE(playerRight, MOVE_LIGHT_SCREEN); MOVE(opponentLeft, MOVE_REFLECT); MOVE(opponentRight, MOVE_SAFEGUARD); }
TURN { MOVE(playerLeft, MOVE_MIST); MOVE(playerRight, MOVE_SAFEGUARD); MOVE(opponentLeft, MOVE_MIST); MOVE(opponentRight, MOVE_DEFOG, target: playerLeft); }
} SCENE {
if (defogTurn == TRUE)
{
MESSAGE("Foe Glalie used Defog!");
MESSAGE("Glalie is protected by MIST!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, opponentRight);
// Player side
MESSAGE("Ally's Reflect wore off!");
MESSAGE("Ally's Light Screen wore off!");
MESSAGE("Ally's Mist wore off!");
MESSAGE("Ally's Aurora Veil wore off!");
MESSAGE("Ally's Safeguard wore off!");
MESSAGE("Foe Glalie used Defog!");
MESSAGE("Glalie is protected by MIST!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_DEFOG, opponentRight);
// Player side
MESSAGE("Ally's Reflect wore off!");
MESSAGE("Ally's Light Screen wore off!");
MESSAGE("Ally's Mist wore off!");
MESSAGE("Ally's Aurora Veil wore off!");
MESSAGE("Ally's Safeguard wore off!");
MESSAGE("The spikes disappeared from the ground around your team!");
MESSAGE("The pointed stones disappeared from around your team!");
MESSAGE("The poison spikes disappeared from the ground around your team!");
MESSAGE("The sticky web has disappeared from the ground around your team!");
MESSAGE("The spikes disappeared from the ground around your team!");
MESSAGE("The pointed stones disappeared from around your team!");
MESSAGE("The poison spikes disappeared from the ground around your team!");
MESSAGE("The sticky web has disappeared from the ground around your team!");
// Opponent side
MESSAGE("The spikes disappeared from the ground around the opposing team!");
MESSAGE("The pointed stones disappeared from around the opposing team!");
MESSAGE("The poison spikes disappeared from the ground around the opposing team!");
MESSAGE("The sticky web has disappeared from the ground around the opposing team!");
}
// Opponent side
MESSAGE("The spikes disappeared from the ground around the opposing team!");
MESSAGE("The pointed stones disappeared from around the opposing team!");
MESSAGE("The poison spikes disappeared from the ground around the opposing team!");
MESSAGE("The sticky web has disappeared from the ground around the opposing team!");
}
}

View File

@ -0,0 +1,138 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_DIRE_CLAW].effect == EFFECT_DIRE_CLAW);
}
// found by brute-force
#define RNG_SLEEP 0xcb0
#define RNG_POISON 0x2BE
#define RNG_PARALYSIS 5
SINGLE_BATTLE_TEST("Dire Claw can inflict poison, paralysis or sleep")
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_DIRE_CLAW); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_SLP) {
STATUS_ICON(opponent, sleep: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PSN) {
STATUS_ICON(opponent, poison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze poison/electric types respectively")
{
u8 statusAnim;
u16 species;
u32 rng;
#if B_PARALYZE_ELECTRIC >= GEN_6
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; }
#endif // B_PARALYZE_ELECTRIC
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; species = SPECIES_ARBOK;}
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_DIRE_CLAW); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PSN) {
NOT STATUS_ICON(opponent, poison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep pokemon with abilities preventing respective statuses")
{
u8 statusAnim;
u16 species, ability;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; }
#if P_GEN_4_POKEMON == TRUE
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; }
#endif // P_GEN_4_POKEMON
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; species = SPECIES_ZANGOOSE; ability = ABILITY_IMMUNITY; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; species = SPECIES_VIGOROTH; ability = ABILITY_VITAL_SPIRIT; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; species = SPECIES_HYPNO; ability = ABILITY_INSOMNIA; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species) {Ability(ability);}
} WHEN {
TURN { MOVE(player, MOVE_DIRE_CLAW); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_SLP) {
NOT STATUS_ICON(opponent, sleep: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PSN) {
NOT STATUS_ICON(opponent, poison: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep a mon which is already statused")
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PSN; rng = RNG_POISON; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = RNG_SLEEP; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_BURN);}
} WHEN {
TURN { MOVE(player, MOVE_DIRE_CLAW); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DIRE_CLAW, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_SLP) {
NOT STATUS_ICON(opponent, sleep: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PSN) {
NOT STATUS_ICON(opponent, poison: TRUE);
}
}
}

View File

@ -9,7 +9,7 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Double Team raises Evasion")
{
ASSUME(gBattleMoves[MOVE_SCRATCH].accuracy == 100);
PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100);
PASSES_RANDOMLY(gBattleMoves[MOVE_SCRATCH].accuracy * 3 / 4, 100, RNG_ACCURACY);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);

View File

@ -0,0 +1,117 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_STONE_AXE].effect == EFFECT_HIT_SET_ENTRY_HAZARD);
ASSUME(gBattleMoves[MOVE_CEASELESS_EDGE].effect == EFFECT_HIT_SET_ENTRY_HAZARD);
}
SINGLE_BATTLE_TEST("Stone Axe / Ceaseless Edge set up hazards after hitting the target")
{
u16 move;
PARAMETRIZE {move = MOVE_STONE_AXE; }
PARAMETRIZE {move = MOVE_CEASELESS_EDGE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, move); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, move, player);
HP_BAR(opponent);
if (move == MOVE_CEASELESS_EDGE) {
MESSAGE("Spikes were scattered all around the opposing team!");
}
else {
MESSAGE("Pointed stones float in the air around the opposing team!");
}
MESSAGE("2 sent out Wobbuffet!");
if (move == MOVE_CEASELESS_EDGE) {
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Foe Wobbuffet is hurt by spikes!");
}
else {
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Pointed stones dug into Foe Wobbuffet!");
}
}
}
SINGLE_BATTLE_TEST("Ceaseless Edge can set up to 3 layers of Spikes")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_CEASELESS_EDGE); }
TURN { MOVE(player, MOVE_CEASELESS_EDGE); }
TURN { MOVE(player, MOVE_CEASELESS_EDGE); }
TURN { MOVE(player, MOVE_CEASELESS_EDGE); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(opponent);
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(opponent);
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(opponent);
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CEASELESS_EDGE, player);
HP_BAR(opponent);
NOT MESSAGE("Spikes were scattered all around the opposing team!");
MESSAGE("2 sent out Wynaut!");
HP_BAR(opponent, damage: maxHP / 4);
MESSAGE("Foe Wynaut is hurt by spikes!");
}
}
SINGLE_BATTLE_TEST("Stone Axe can set up pointed stones only once")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { MOVE(player, MOVE_STONE_AXE); }
TURN { SWITCH(opponent, 1); }
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STONE_AXE, player);
HP_BAR(opponent);
NOT MESSAGE("Pointed stones float in the air around the opposing team!");
MESSAGE("2 sent out Wynaut!");
HP_BAR(opponent, damage: maxHP / 8);
MESSAGE("Pointed stones dug into Foe Wynaut!");
}
}

View File

@ -9,8 +9,7 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Dragon Tail switches the target with a random non-fainted replacement")
{
KNOWN_FAILING; // Only 18/50. Waiting for an improved PASSES_RANDOMLY.
PASSES_RANDOMLY(90 * 1, 100 * 2);
PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@ -27,7 +26,7 @@ SINGLE_BATTLE_TEST("Dragon Tail switches the target with a random non-fainted re
DOUBLE_BATTLE_TEST("Dragon Tail switches the target with a random non-battler, non-fainted replacement")
{
PASSES_RANDOMLY(90 * 1, 100 * 2);
PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);

View File

@ -8,7 +8,7 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Thrash lasts for 2 or 3 turns")
{
PASSES_RANDOMLY(1, 2);
PASSES_RANDOMLY(1, 2, RNG_RAMPAGE_TURNS);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@ -26,7 +26,6 @@ SINGLE_BATTLE_TEST("Thrash lasts for 2 or 3 turns")
SINGLE_BATTLE_TEST("Thrash confuses the user after it finishes")
{
GIVEN {
RNGSeed(0x00000000);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -45,7 +44,6 @@ SINGLE_BATTLE_TEST("Thrash does not confuse the user if it is canceled on turn 1
{
GIVEN {
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5);
RNGSeed(0x00000000);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -61,7 +59,6 @@ SINGLE_BATTLE_TEST("Thrash does not confuse the user if it is canceled on turn 2
{
GIVEN {
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5);
RNGSeed(0x00000000);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@ -78,7 +75,6 @@ SINGLE_BATTLE_TEST("Thrash confuses the user if it is canceled on turn 3 of 3")
KNOWN_FAILING;
GIVEN {
ASSUME(B_RAMPAGE_CANCELLING >= GEN_5);
RNGSeed(0x00000000);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {

View File

@ -8,7 +8,7 @@ ASSUMPTIONS
SINGLE_BATTLE_TEST("Roar switches the target with a random non-fainted replacement")
{
PASSES_RANDOMLY(1, 2);
PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
@ -25,7 +25,7 @@ SINGLE_BATTLE_TEST("Roar switches the target with a random non-fainted replaceme
DOUBLE_BATTLE_TEST("Roar switches the target with a random non-battler, non-fainted replacement")
{
PASSES_RANDOMLY(1, 2);
PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);

View File

@ -6,16 +6,33 @@ ASSUMPTIONS
ASSUME(gBattleMoves[MOVE_HYPNOSIS].effect == EFFECT_SLEEP);
}
SINGLE_BATTLE_TEST("Hypnosis inflicts sleep")
SINGLE_BATTLE_TEST("Hypnosis inflicts 1-3 turns of sleep")
{
u32 turns, count;
ASSUME(B_SLEEP_TURNS >= GEN_5);
PARAMETRIZE { turns = 1; }
PARAMETRIZE { turns = 2; }
PARAMETRIZE { turns = 3; }
PASSES_RANDOMLY(1, 3, RNG_SLEEP_TURNS);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_HYPNOSIS); }
TURN { MOVE(player, MOVE_HYPNOSIS); MOVE(opponent, MOVE_CELEBRATE); }
for (count = 0; count < turns; ++count)
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPNOSIS, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent);
MESSAGE("Foe Wobbuffet fell asleep!");
STATUS_ICON(opponent, sleep: TRUE);
for (count = 0; count < turns; ++count)
{
if (count < turns - 1)
MESSAGE("Foe Wobbuffet is fast asleep.");
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent);
}
MESSAGE("Foe Wobbuffet woke up!");
STATUS_ICON(opponent, none: TRUE);
}
}

View File

@ -28,7 +28,7 @@ SINGLE_BATTLE_TEST("Spikes damage on switch in")
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
for (count = 0; count < layers; ++count) {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
MESSAGE("Spikes were scattered all around the opponent's side!");
MESSAGE("Spikes were scattered all around the opposing team!");
}
MESSAGE("2 sent out Wynaut!");
HP_BAR(opponent, damage: maxHP / divisor);
@ -51,11 +51,11 @@ SINGLE_BATTLE_TEST("Spikes fails after 3 layers")
} SCENE {
s32 maxHP = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
MESSAGE("Spikes were scattered all around the opponent's side!");
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
MESSAGE("Spikes were scattered all around the opponent's side!");
MESSAGE("Spikes were scattered all around the opposing team!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, player);
MESSAGE("Spikes were scattered all around the opponent's side!");
MESSAGE("Spikes were scattered all around the opposing team!");
MESSAGE("Wobbuffet used Spikes!");
MESSAGE("But it failed!");
MESSAGE("2 sent out Wynaut!");

View File

@ -0,0 +1,144 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_TRI_ATTACK].effect == EFFECT_TRI_ATTACK);
}
// found by brute-force
#define RNG_PARALYSIS 0xcb0
#define RNG_BURN 0x2BE
#define RNG_FREEZE 5
SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze")
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice types respectively")
{
u8 statusAnim;
u16 species;
u32 rng;
#if B_PARALYZE_ELECTRIC >= GEN_6
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU;}
#endif // B_PARALYZE_ELECTRIC
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_ARCANINE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; species = SPECIES_GLALIE; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_BRN) {
NOT STATUS_ICON(opponent, burn: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_FRZ) {
NOT STATUS_ICON(opponent, freeze: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilities preventing respective statuses")
{
u8 statusAnim;
u16 species, ability;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_RAICHU; ability = ABILITY_LIGHTNING_ROD; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_JOLTEON; ability = ABILITY_VOLT_ABSORB; }
#if P_GEN_4_POKEMON == TRUE
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; species = SPECIES_ELECTIVIRE; ability = ABILITY_MOTOR_DRIVE; }
#endif // P_GEN_4_POKEMON
#if P_GEN_7_POKEMON == TRUE
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; }
#endif // P_GEN_7_POKEMON
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; species = SPECIES_SEAKING; ability = ABILITY_WATER_VEIL; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species) {Ability(ability);}
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_BRN) {
NOT STATUS_ICON(opponent, burn: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_FRZ) {
NOT STATUS_ICON(opponent, freeze: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
}
}
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is already statused")
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = RNG_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = RNG_BURN; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = RNG_FREEZE; }
GIVEN {
RNGSeed(rng);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Status1(STATUS1_SLEEP);}
} WHEN {
TURN { MOVE(player, MOVE_TRI_ATTACK); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TRI_ATTACK, player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_STATUS, statusAnim, opponent);
if (statusAnim == B_ANIM_STATUS_BRN) {
NOT STATUS_ICON(opponent, burn: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_FRZ) {
NOT STATUS_ICON(opponent, freeze: TRUE);
}
else if (statusAnim == B_ANIM_STATUS_PRZ) {
NOT STATUS_ICON(opponent, paralysis: TRUE);
}
}
}

View File

@ -0,0 +1,27 @@
#include "global.h"
#include "test_battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_VENOSHOCK].effect == EFFECT_VENOSHOCK);
}
SINGLE_BATTLE_TEST("Venoshock's power doubles if the target is poisoned/badly poisoned", s16 damage)
{
u32 status1;
PARAMETRIZE { status1 = 0; }
PARAMETRIZE { status1 = STATUS1_POISON; }
PARAMETRIZE { status1 = STATUS1_TOXIC_POISON; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) {Status1(status1);}
} WHEN {
TURN { MOVE(player, MOVE_VENOSHOCK); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOSHOCK, player);
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[2].damage);
}
}

81
test/random.c Normal file
View File

@ -0,0 +1,81 @@
#include "global.h"
#include "test.h"
#include "random.h"
TEST("RandomUniform 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 = RandomUniformDefault(RNG_NONE, lo, hi);
EXPECT(lo <= r && r <= hi);
}
}
TEST("RandomWeighted generates 0..n-1")
{
u32 n, sum, i;
static const u8 ws[8] = { 1, 1, 1, 1, 1, 1, 1, 1 };
PARAMETRIZE { n = 1; }
PARAMETRIZE { n = 2; }
PARAMETRIZE { n = 3; }
PARAMETRIZE { n = 4; }
ASSUME(n <= ARRAY_COUNT(ws));
for (i = 0, sum = 0; i < n; i++)
sum += ws[i];
for (i = 0; i < 1024; i++)
{
u32 r = RandomWeightedArrayDefault(RNG_NONE, sum, n, ws);
EXPECT(0 <= r && r < n);
}
}
TEST("RandomUniform generates uniform distribution")
{
u32 i, error;
u16 distribution[4];
memset(distribution, 0, sizeof(distribution));
for (i = 0; i < 4096; i++)
{
u32 r = RandomUniformDefault(RNG_NONE, 0, ARRAY_COUNT(distribution));
EXPECT(0 <= r && r < ARRAY_COUNT(distribution));
distribution[r]++;
}
error = 0;
for (i = 0; i < ARRAY_COUNT(distribution); i++)
error += abs(UQ_4_12(0.25) - distribution[i]);
EXPECT_LT(error, UQ_4_12(0.025));
}
TEST("RandomWeighted generates distribution in proportion to the weights")
{
u32 i, sum, error;
static const u8 ws[4] = { 1, 2, 2, 3 };
u16 distribution[ARRAY_COUNT(ws)];
for (i = 0, sum = 0; i < ARRAY_COUNT(ws); i++)
sum += ws[i];
memset(distribution, 0, sizeof(distribution));
for (i = 0; i < 4096; i++)
{
u32 r = RandomWeightedArrayDefault(RNG_NONE, sum, ARRAY_COUNT(ws), ws);
EXPECT(0 <= r && r < ARRAY_COUNT(ws));
distribution[r]++;
}
error = 0;
error += abs(UQ_4_12(0.125) - distribution[0]);
error += abs(UQ_4_12(0.250) - distribution[1]);
error += abs(UQ_4_12(0.250) - distribution[2]);
error += abs(UQ_4_12(0.375) - distribution[3]);
EXPECT_LT(error, UQ_4_12(0.025));
}

View File

@ -72,7 +72,7 @@ SINGLE_BATTLE_TEST("Burn reduces attack by 50%", s16 damage)
SINGLE_BATTLE_TEST("Freeze has a 20% chance of being thawed")
{
PASSES_RANDOMLY(20, 100);
PASSES_RANDOMLY(20, 100, RNG_FROZEN);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
OPPONENT(SPECIES_WOBBUFFET);
@ -90,9 +90,8 @@ SINGLE_BATTLE_TEST("Freeze is thawed by opponent's Fire-type attacks")
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_FREEZE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_EMBER); }
TURN { MOVE(opponent, MOVE_EMBER); MOVE(player, MOVE_CELEBRATE); }
} SCENE {
MESSAGE("Wobbuffet is frozen solid!");
MESSAGE("Foe Wobbuffet used Ember!");
MESSAGE("Wobbuffet was defrosted!");
STATUS_ICON(player, none: TRUE);
@ -145,7 +144,7 @@ SINGLE_BATTLE_TEST("Paralysis reduces speed by 50%")
SINGLE_BATTLE_TEST("Paralysis has a 25% chance of skipping the turn")
{
PASSES_RANDOMLY(25, 100);
PASSES_RANDOMLY(25, 100, RNG_PARALYSIS);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_PARALYSIS); }
OPPONENT(SPECIES_WOBBUFFET);

19
test/terrain_grassy.c Normal file
View File

@ -0,0 +1,19 @@
#include "global.h"
#include "test_battle.h"
SINGLE_BATTLE_TEST("Grassy Terrain recovers 1/16th HP at end of turn")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { MaxHP(100); HP(1); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_GRASSY_TERRAIN); }
} SCENE {
s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP);
MESSAGE("Wobbuffet is healed by the grassy terrain!");
HP_BAR(player, damage: -maxHP / 16);
}
}
TO_DO_BATTLE_TEST("Grassy Terrain increases power of Grass-type moves by 30/50 percent")
TO_DO_BATTLE_TEST("Grassy Terrain decreases power of Earthquake, Magnitude and Bulldoze by 50 percent")

View File

@ -13,6 +13,7 @@ enum TestResult
TEST_RESULT_INVALID,
TEST_RESULT_ERROR,
TEST_RESULT_TIMEOUT,
TEST_RESULT_TODO,
};
struct TestRunner
@ -53,6 +54,16 @@ extern const u8 gTestRunnerI;
extern const char gTestRunnerArgv[256];
extern const struct TestRunner gAssumptionsRunner;
struct FunctionTestRunnerState
{
u8 parameters;
u8 runParameter;
};
extern const struct TestRunner gFunctionTestRunner;
extern struct FunctionTestRunnerState *gFunctionTestRunnerState;
extern struct TestRunnerState gTestRunnerState;
void CB2_TestRunner(void);
@ -62,6 +73,17 @@ void Test_ExitWithResult(enum TestResult, const char *fmt, ...);
s32 MgbaPrintf_(const char *fmt, ...);
#define TEST(_name) \
static void CAT(Test, __LINE__)(void); \
__attribute__((section(".tests"))) static const struct Test CAT(sTest, __LINE__) = \
{ \
.name = _name, \
.filename = __FILE__, \
.runner = &gFunctionTestRunner, \
.data = (void *)CAT(Test, __LINE__), \
}; \
static void CAT(Test, __LINE__)(void)
#define ASSUMPTIONS \
static void Assumptions(void); \
__attribute__((section(".tests"))) static const struct Test sAssumptions = \
@ -138,4 +160,12 @@ s32 MgbaPrintf_(const char *fmt, ...);
#define KNOWN_FAILING \
Test_ExpectedResult(TEST_RESULT_FAIL)
#define PARAMETRIZE if (gFunctionTestRunnerState->parameters++ == gFunctionTestRunnerState->runParameter)
#define TO_DO \
Test_ExpectedResult(TEST_RESULT_TODO)
#define EXPECT_TO_DO \
Test_ExitWithResult(TEST_RESULT_TODO, "%s:%d: EXPECT_TO_DO", gTestRunnerState.test->filename, __LINE__)
#endif

Some files were not shown because too many files have changed in this diff Show More