Master to Upcoming merge 20251107
This commit is contained in:
commit
c230e54678
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
fetch-depth: 0
|
||||
- name: Install latest mdbook
|
||||
run: |
|
||||
tag=$(curl 'https://api.github.com/repos/rust-lang/mdbook/releases/latest' | jq -r '.tag_name')
|
||||
tag="v0.5.0-beta.1"
|
||||
url="https://github.com/rust-lang/mdbook/releases/download/${tag}/mdbook-${tag}-x86_64-unknown-linux-gnu.tar.gz"
|
||||
mkdir mdbook
|
||||
curl -sSL $url | tar -xz --directory=./mdbook
|
||||
|
||||
8
Makefile
8
Makefile
@ -217,10 +217,18 @@ ALL_LEARNABLES_JSON := $(LEARNSET_HELPERS_BUILD_DIR)/all_learnables.json
|
||||
WILD_ENCOUNTERS_TOOL_DIR := $(TOOLS_DIR)/wild_encounters
|
||||
AUTO_GEN_TARGETS += $(DATA_SRC_SUBDIR)/wild_encounters.h
|
||||
|
||||
MISC_TOOL_DIR := $(TOOLS_DIR)/misc
|
||||
AUTO_GEN_TARGETS += $(INCLUDE_DIRS)/constants/script_commands.h
|
||||
|
||||
$(DATA_SRC_SUBDIR)/wild_encounters.h: $(DATA_SRC_SUBDIR)/wild_encounters.json $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py $(INCLUDE_DIRS)/config/overworld.h $(INCLUDE_DIRS)/config/dexnav.h
|
||||
python3 $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py > $@
|
||||
|
||||
$(INCLUDE_DIRS)/constants/script_commands.h: $(MISC_TOOL_DIR)/make_scr_cmd_constants.py $(DATA_ASM_SUBDIR)/script_cmd_table.inc
|
||||
python3 $(MISC_TOOL_DIR)/make_scr_cmd_constants.py
|
||||
|
||||
$(C_BUILDDIR)/wild_encounter.o: c_dep += $(DATA_SRC_SUBDIR)/wild_encounters.h
|
||||
$(C_BUILDDIR)/trainer_see.o: c_dep += $(INCLUDE_DIRS)/constants/script_commands.h
|
||||
$(C_BUILDDIR)/vs_seeker.o: c_dep += $(INCLUDE_DIRS)/constants/script_commands.h
|
||||
|
||||
PERL := perl
|
||||
SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c
|
||||
|
||||
@ -2683,3 +2683,9 @@
|
||||
callnative SetSpeaker
|
||||
.4byte \name
|
||||
.endm
|
||||
|
||||
@ VS Seeker
|
||||
.macro vsseeker_rematchid rematchId:req
|
||||
callnative NativeVsSeekerRematchId, requests_effects=1
|
||||
.2byte \rematchId
|
||||
.endm
|
||||
|
||||
@ -4406,12 +4406,11 @@ BattleScript_FaintBattler::
|
||||
tryrevertweatherform
|
||||
flushtextbox
|
||||
waitanimation
|
||||
tryactivatesoulheart
|
||||
tryactivatereceiver BS_FAINTED
|
||||
tryactivatesoulheart
|
||||
trytrainerslidemsgfirstoff BS_FAINTED
|
||||
return
|
||||
|
||||
|
||||
BattleScript_GiveExp::
|
||||
setbyte sGIVEEXP_STATE, 0
|
||||
getexp BS_TARGET
|
||||
@ -6547,6 +6546,7 @@ BattleScript_ReceiverActivates::
|
||||
printstring STRINGID_RECEIVERABILITYTAKEOVER
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
settracedability BS_ABILITY_BATTLER
|
||||
switchinabilities BS_ABILITY_BATTLER
|
||||
return
|
||||
|
||||
BattleScript_AbilityHpHeal:
|
||||
|
||||
@ -37,6 +37,7 @@
|
||||
- [How to use Follower NPCs](tutorials/how_to_follower_npc.md)
|
||||
- [Time-Based Encounters](tutorials/how_to_time_of_day_encounters.md)
|
||||
- [How to use Trainer Party Pools](tutorials/how_to_trainer_party_pool.md)
|
||||
- [Vs. Seeker](tutorials/vs_seeker.md)
|
||||
- [Changelog](./CHANGELOG.md)
|
||||
- [1.13.x]()
|
||||
- [Version 1.13.3](changelogs/1.13.x/1.13.3.md)
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
[book]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "."
|
||||
title = "pokeemerald-expansion"
|
||||
|
||||
|
||||
@ -46,6 +46,6 @@ if __name__ == '__main__':
|
||||
sys.exit(0)
|
||||
|
||||
context, book = json.load(sys.stdin)
|
||||
proc_items(book['sections'])
|
||||
proc_items(book['items'])
|
||||
|
||||
print(json.dumps(book))
|
||||
|
||||
115
docs/tutorials/vs_seeker.md
Normal file
115
docs/tutorials/vs_seeker.md
Normal file
@ -0,0 +1,115 @@
|
||||
# `pokemerald-expansion` Vs. Seeker
|
||||
|
||||
## What is the Vs. Seeker?
|
||||
The Vs. Seeker is a Key Item that is used to battle Trainers that the player has battled previously.
|
||||
|
||||
When used, the Vs. Seeker sends out a signal that allows the player to find other Trainers who want a rematch. This signal affects all Trainers that are on-screen. Once used on Trainers that can be rematched, the device cannot be used again until it is charged. The player does this by walking a specific number of steps. The effect on the Trainers wears off if they are battled, the player leaves the area, or the player walks a specific number of steps. If the player attempts to use the Vs. Seeker when it is not fully charged, the player will be told how many steps remain until it is. After the player uses the Vs. Seeker, some Trainers may have their team changed from their first battle.
|
||||
|
||||
## How is the Vs. Seeker enabled?
|
||||
### Users
|
||||
Vs. Seeker functionality is enabled by setting `I_VS_SEEKER_CHARGING` to `TRUE`.
|
||||
|
||||
### Players
|
||||
`ITEM_VS_SEEKER` can only be used outside of battle. It can be used from the bag or registered to be used from the field.
|
||||
|
||||
Usage of the Vs. Seeker will ALWAYS fail unless all of the conditions are met:
|
||||
* Player has at least five badges
|
||||
* There is an NPC on screen that has previously been defeated
|
||||
* Player is not inside of a building
|
||||
* The Vs. Seeker is fully recharged
|
||||
|
||||
#### Charge
|
||||
If the player has `ITEM_VS_SEEKER` and at least five badges, the Vs. Seeker will be charged by walking steps. The Vs. Seeker is fully charged once the player has walked `VSSEEKER_RECHARGE_STEPS`, which is `100` by default. The Vs. Seeker's charge is depleted if the player uses the item.
|
||||
|
||||
### How does Match Call interact with the Vs. Seeker?
|
||||
When `I_VS_SEEKER_CHARGING` is enabled, the Match Call does not function at all. Trainers will never be rematch eligible outside of the use of the Vs. Seeker.
|
||||
|
||||
## How does the Vs. Seeker choose a Trainer?
|
||||
|
||||
When the Vs. Seeker is successfully used, every Trainer on screen is individually queried. There is a 31% chance that the Trainer will want a rematch.
|
||||
Objects listed in `regularTrainersOnLand` or `regularTrainersInWater` are considered Land/Water objects.
|
||||
|
||||
| Status | Is Land/Water Object | Emote | New Movement Type |
|
||||
| --- | --- | --- | --- |
|
||||
| Wants Rematch | Yes | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_COUNTER_CLOCKWISE` |
|
||||
| Wants Rematch | No | `MOVEMENT_ACTION_EMOTE_DOUBLE_EXCL_MARK` | `MOVEMENT_TYPE_FACE_DOWN` |
|
||||
| Does Not Want Rematch | - | `MOVEMENT_ACTION_EMOTE_X` | none |
|
||||
| Has Not Been Fought | - | `MOVEMENT_ACTION_EMOTE_EXCLAMATION_MARK` | none |
|
||||
|
||||
### Rematch Table
|
||||
|
||||
| Sequence | Trainer ID |
|
||||
| ---------- | ---------------- |
|
||||
| 1st Battle | `TRAINER_ROSE_1` |
|
||||
| 2nd Battle | `TRAINER_ROSE_2` |
|
||||
| 3rd Battle | `TRAINER_ROSE_3` |
|
||||
| 4th Battle | `TRAINER_ROSE_4` |
|
||||
| 5th Battle | `TRAINER_ROSE_5` |
|
||||
|
||||
The game determines which version of the Trainer you'll fight next by following these rules:
|
||||
|
||||
1. Start with the next Trainer in the sequence after the one that has been defeated. If there are no more, the battle is against the last listed Trainer.
|
||||
2. If that next Trainer hasn't been unlocked yet, the battle is against the latest available unlocked version.
|
||||
3. If the next Trainer is unlocked but not yet defeated, the battle is against that version.
|
||||
4. If the next Trainer has already been defeated, check the next one in the sequence.
|
||||
|
||||
## How do users implement rematches with the Vs. Seeker?
|
||||
### Existing `pokemerald` Trainers
|
||||
No extra work is required. With the exception of Wally, Gym Leaders and Elite Four, all of the rematchable Trainers in Emerald will work with the Vs. Seeker without any changes.
|
||||
### New Trainers
|
||||
#### Party / `gRematchTable`
|
||||
Each of the rematches for the Trainer must be defined as seperate Trainers in `src/data/trainers.party` and `include/constants/opponents`. For example, `TRAINER_CALVIN_1` also has `TRAINER_CALVIN_2`,`TRAINER_CALVIN_3`,`TRAINER_CALVIN_4`, and `TRAINER_CALVIN_5`.
|
||||
|
||||
Once all of those constants and parties are defined, a new row must be added to `gRematchTable` (located in in `src/battle_setup.c`). The row header should be a rematch ID, which can be added in `include/constants/rematches.h`. The row contents must be the five constants created for the new parties, with the lat argument being the constant of the map (`include/constants/map_groups.h`) where the Trainer is placed.
|
||||
|
||||
If a Trainer is intended to have less than five unique rematch parties, the extra slots can be filled with the last available Trainer ID.
|
||||
|
||||
```c
|
||||
// This Trainer only has two teams.
|
||||
[REMATCH_ROSE] = REMATCH(TRAINER_ROSE_1, TRAINER_ROSE_2, TRAINER_ROSE_2, TRAINER_ROSE_2, TRAINER_ROSE_2, MAP_ROUTE118),
|
||||
```
|
||||
|
||||
WARNING: Rematch IDs should be placed BEFORE `REMATCH_WALLY_VR`. Trainers below that are treated as "special Trainers" that are not triggered by the Vs. Seeker.
|
||||
|
||||
#### Scripts
|
||||
The trainer's object needs to have a script that begins with a method to signify what this object's trainer ID is.
|
||||
|
||||
#### `trainerbattle`
|
||||
```
|
||||
Route103_EventScript_Daisy::
|
||||
trainerbattle_single TRAINER_DAISY, Route103_Text_DaisyIntro, Route103_Text_DaisyDefeated
|
||||
msgbox Route103_Text_DaisyPostBattle, MSGBOX_AUTOCLOSE
|
||||
end
|
||||
```
|
||||
|
||||
Daisy is using one of the `trainerbattle` macros, which has the trainer battle macro in the first command of the script. Most trainers in `pokeemerald` use this pattern.
|
||||
|
||||
##### `vsseeker_rematchid`
|
||||
```
|
||||
Route102_EventScript_Calvin::
|
||||
vsseeker_rematchid TRAINER_CALVIN_1
|
||||
applymovement LOCALID_CALVIN, CalvinMovementTest
|
||||
waitmovement 0
|
||||
trainerbattle_single TRAINER_CALVIN_1, Route102_Text_CalvinIntro, Route102_Text_CalvinDefeated, Route102_EventScript_CalvinRegisterMatchCallAfterBattle
|
||||
specialvar VAR_RESULT, ShouldTryRematchBattle
|
||||
goto_if_eq VAR_RESULT, TRUE, Route102_EventScript_CalvinRematch
|
||||
setvar VAR_0x8004, TRAINER_CALVIN_1
|
||||
specialvar VAR_RESULT, IsTrainerRegistered
|
||||
goto_if_eq VAR_RESULT, FALSE, Route102_EventScript_CalvinTryRegister
|
||||
msgbox Route102_Text_CalvinPostBattle, MSGBOX_DEFAULT
|
||||
release
|
||||
end
|
||||
```
|
||||
|
||||
If the trainer has other script commands before the eventual `trainerbattle` macro, the first command in the script needs to be `vsseeker_rematchid`. This macro does nothing but takes a single argument, which should be the same as the first Trainer ID for this trainer.
|
||||
|
||||
#### `MOVEMENT_TYPE_COUNTER_CLOCKWISE`
|
||||
If you want Trainers to spin once they are eligible for a rematch, their overworld graphics object ID (`include/constants/event_objects.h`) must be listed in either `regularTrainersOnLand` or `regularTrainersInWater`.Otherwise they will adopt the movement type `MOVEMENT_TYPE_FACE_DOWN`.
|
||||
|
||||
## What can be customized about the Vs. Seeker?
|
||||
* **Unlock Conditions**: The next "level" of rematches is unlocked when a specific flag is set. The flags that are currently used in `GetGameProgressFlags` can be changed to flags that better suit your game.
|
||||
* **Recharge Steps**: `VSSEEKER_RECHARGE_STEPS` is initally set to 100, but this value can be changed to any number under 256.
|
||||
* **Badge Requirement**: `HasAtLeastFiveBadges` is used to check if the Vs. Seeker will successfully work. You can customize the number of badges by changing `REMATCH_BADGE_COUNT` or otherwise alterting the function.
|
||||
|
||||
## What are the limitations of the Vs. Seeker?
|
||||
The Vs. Seeker does not currently work with Gym Leaders. There is a bug filed to hopefully fix this in the future.
|
||||
@ -163,6 +163,7 @@ bool32 IsAffectedByPowder(u32 battler, enum Ability ability, enum HoldEffect hol
|
||||
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category);
|
||||
enum MoveComparisonResult AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo);
|
||||
struct SimulatedDamage AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef);
|
||||
bool32 IsAdditionalEffectBlocked(u32 battlerAtk, u32 abilityAtk, u32 battlerDef, u32 abilityDef);
|
||||
struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, uq4_12_t *typeEffectiveness, enum AIConsiderGimmick considerGimmickAtk, enum AIConsiderGimmick considerGimmickDef, u32 weather);
|
||||
bool32 AI_IsDamagedByRecoil(u32 battler);
|
||||
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
|
||||
|
||||
@ -74,6 +74,7 @@ void ConfigureAndSetUpOneTrainerBattle(u8 trainerObjEventId, const u8 *trainerSc
|
||||
void ConfigureTwoTrainersBattle(u8 trainerObjEventId, const u8 *trainerScript);
|
||||
void SetUpTwoTrainersBattle(void);
|
||||
bool32 GetTrainerFlagFromScriptPointer(const u8 *data);
|
||||
bool32 GetRematchFromScriptPointer(const u8 *data);
|
||||
void SetTrainerFacingDirection(void);
|
||||
u8 GetTrainerBattleMode(void);
|
||||
bool8 GetTrainerFlag(void);
|
||||
@ -91,12 +92,14 @@ const u8 *GetTrainerALoseText(void);
|
||||
const u8 *GetTrainerBLoseText(void);
|
||||
const u8 *GetTrainerWonSpeech(void);
|
||||
void UpdateRematchIfDefeated(s32 rematchTableId);
|
||||
void ClearCurrentTrainerWantRematchVsSeeker(void);
|
||||
void IncrementRematchStepCounter(void);
|
||||
void TryUpdateRandomTrainerRematches(u16 mapGroup, u16 mapNum);
|
||||
bool32 DoesSomeoneWantRematchIn(u16 mapGroup, u16 mapNum);
|
||||
bool32 IsRematchTrainerIn(u16 mapGroup, u16 mapNum);
|
||||
u16 GetLastBeatenRematchTrainerId(u16 trainerId);
|
||||
bool8 ShouldTryRematchBattle(void);
|
||||
bool8 ShouldTryRematchBattleForTrainerId(u16 trainerId);
|
||||
bool8 IsTrainerReadyForRematch(void);
|
||||
void ShouldTryGetTrainerScript(void);
|
||||
u16 CountBattledRematchTeams(u16 trainerId);
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
#define I_REPEL_LURE_MENU TRUE // If TRUE, the player is able to choose which Repel/Lure to use once the previous one runs out. Cursor position is saved by VAR_LAST_REPEL_LURE_USED if not 0.
|
||||
|
||||
// Vs. Seeker
|
||||
#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working.
|
||||
#define I_VS_SEEKER_CHARGING 0 // If this flag is assigned, the Vs Seeker functionality will be enabled. When the player has the Vs. Seeker, Match Call rematch functions will stop working. Documentation for the Vs. Seeker can be found in docs/tutorials/vs_seeker.md.
|
||||
|
||||
// ORAS Dowsing Machine
|
||||
#define I_ORAS_DOWSING_FLAG 0 // Replace 0 with an unused flag to enable the Dowsing Machine mechanic from ORAS.
|
||||
|
||||
@ -134,6 +134,7 @@
|
||||
#define OW_POPUP_BW_TIME_MODE OW_POPUP_BW_TIME_NONE // Determines what type of time is shown.
|
||||
#define OW_POPUP_BW_ALPHA_BLEND FALSE // Enables alpha blending/transparency for the pop-ups. Mainly intended to be used with the black color option.
|
||||
// Setting this to TRUE will cause graphical errors with the Day Night System enabled.
|
||||
// It will also cause minor visual glitches of shadow and reflection sprites adjusting their transparency when the pop-up disappear
|
||||
|
||||
// Pokémon Center
|
||||
#define OW_IGNORE_EGGS_ON_HEAL GEN_LATEST // In Gen 4+, the nurse in the Pokémon Center does not heal Eggs on healing machine.
|
||||
@ -154,4 +155,7 @@
|
||||
#define OW_NAME_BOX_FOREGROUND_COLOR 1
|
||||
#define OW_NAME_BOX_SHADOW_COLOR 2
|
||||
|
||||
// Trainer Rematches
|
||||
#define OW_REMATCH_BADGE_COUNT 5 // Number of badges necessary before the match call or vs seeker features allow rematches
|
||||
|
||||
#endif // GUARD_CONFIG_OVERWORLD_H
|
||||
|
||||
238
include/constants/script_commands.h
Normal file
238
include/constants/script_commands.h
Normal file
@ -0,0 +1,238 @@
|
||||
//
|
||||
// DO NOT MODIFY THIS FILE! It is auto-generated by tools/misc/make_scr_cmd_constants.py
|
||||
//
|
||||
#ifndef GUARD_SCR_CMD_CONSTANTS_H
|
||||
#define GUARD_SCR_CMD_CONSTANTS_H
|
||||
|
||||
#define SCR_OP_NOP 0x00
|
||||
#define SCR_OP_NOP1 0x01
|
||||
#define SCR_OP_END 0x02
|
||||
#define SCR_OP_RETURN 0x03
|
||||
#define SCR_OP_CALL 0x04
|
||||
#define SCR_OP_GOTO 0x05
|
||||
#define SCR_OP_GOTO_IF 0x06
|
||||
#define SCR_OP_CALL_IF 0x07
|
||||
#define SCR_OP_GOTO_STD 0x08
|
||||
#define SCR_OP_CALL_STD 0x09
|
||||
#define SCR_OP_GOTO_STD_IF 0x0a
|
||||
#define SCR_OP_CALL_STD_IF 0x0b
|
||||
#define SCR_OP_RETURNRAM 0x0c
|
||||
#define SCR_OP_ENDRAM 0x0d
|
||||
#define SCR_OP_SETMYSTERYEVENTSTATUS 0x0e
|
||||
#define SCR_OP_LOAD_WORD 0x0f
|
||||
#define SCR_OP_LOAD_BYTE 0x10
|
||||
#define SCR_OP_SETPTR 0x11
|
||||
#define SCR_OP_LOADBYTEFROMPTR 0x12
|
||||
#define SCR_OP_SETPTRBYTE 0x13
|
||||
#define SCR_OP_COPYLOCAL 0x14
|
||||
#define SCR_OP_COPYBYTE 0x15
|
||||
#define SCR_OP_SETVAR 0x16
|
||||
#define SCR_OP_ADDVAR 0x17
|
||||
#define SCR_OP_SUBVAR 0x18
|
||||
#define SCR_OP_COPYVAR 0x19
|
||||
#define SCR_OP_SETORCOPYVAR 0x1a
|
||||
#define SCR_OP_COMPARE_LOCAL_TO_LOCAL 0x1b
|
||||
#define SCR_OP_COMPARE_LOCAL_TO_VALUE 0x1c
|
||||
#define SCR_OP_COMPARE_LOCAL_TO_PTR 0x1d
|
||||
#define SCR_OP_COMPARE_PTR_TO_LOCAL 0x1e
|
||||
#define SCR_OP_COMPARE_PTR_TO_VALUE 0x1f
|
||||
#define SCR_OP_COMPARE_PTR_TO_PTR 0x20
|
||||
#define SCR_OP_COMPARE_VAR_TO_VALUE 0x21
|
||||
#define SCR_OP_COMPARE_VAR_TO_VAR 0x22
|
||||
#define SCR_OP_CALLNATIVE 0x23
|
||||
#define SCR_OP_GOTONATIVE 0x24
|
||||
#define SCR_OP_SPECIAL 0x25
|
||||
#define SCR_OP_SPECIALVAR 0x26
|
||||
#define SCR_OP_WAITSTATE 0x27
|
||||
#define SCR_OP_DELAY 0x28
|
||||
#define SCR_OP_SETFLAG 0x29
|
||||
#define SCR_OP_CLEARFLAG 0x2a
|
||||
#define SCR_OP_CHECKFLAG 0x2b
|
||||
#define SCR_OP_INITCLOCK 0x2c
|
||||
#define SCR_OP_DOTIMEBASEDEVENTS 0x2d
|
||||
#define SCR_OP_GETTIME 0x2e
|
||||
#define SCR_OP_PLAYSE 0x2f
|
||||
#define SCR_OP_WAITSE 0x30
|
||||
#define SCR_OP_PLAYFANFARE 0x31
|
||||
#define SCR_OP_WAITFANFARE 0x32
|
||||
#define SCR_OP_PLAYBGM 0x33
|
||||
#define SCR_OP_SAVEBGM 0x34
|
||||
#define SCR_OP_FADEDEFAULTBGM 0x35
|
||||
#define SCR_OP_FADENEWBGM 0x36
|
||||
#define SCR_OP_FADEOUTBGM 0x37
|
||||
#define SCR_OP_FADEINBGM 0x38
|
||||
#define SCR_OP_WARP 0x39
|
||||
#define SCR_OP_WARPSILENT 0x3a
|
||||
#define SCR_OP_WARPDOOR 0x3b
|
||||
#define SCR_OP_WARPHOLE 0x3c
|
||||
#define SCR_OP_WARPTELEPORT 0x3d
|
||||
#define SCR_OP_SETWARP 0x3e
|
||||
#define SCR_OP_SETDYNAMICWARP 0x3f
|
||||
#define SCR_OP_SETDIVEWARP 0x40
|
||||
#define SCR_OP_SETHOLEWARP 0x41
|
||||
#define SCR_OP_GETPLAYERXY 0x42
|
||||
#define SCR_OP_GETPARTYSIZE 0x43
|
||||
#define SCR_OP_ADDITEM 0x44
|
||||
#define SCR_OP_REMOVEITEM 0x45
|
||||
#define SCR_OP_CHECKITEMSPACE 0x46
|
||||
#define SCR_OP_CHECKITEM 0x47
|
||||
#define SCR_OP_CHECKITEMTYPE 0x48
|
||||
#define SCR_OP_ADDPCITEM 0x49
|
||||
#define SCR_OP_CHECKPCITEM 0x4a
|
||||
#define SCR_OP_ADDDECORATION 0x4b
|
||||
#define SCR_OP_REMOVEDECORATION 0x4c
|
||||
#define SCR_OP_CHECKDECOR 0x4d
|
||||
#define SCR_OP_CHECKDECORSPACE 0x4e
|
||||
#define SCR_OP_APPLYMOVEMENT 0x4f
|
||||
#define SCR_OP_APPLYMOVEMENTAT 0x50
|
||||
#define SCR_OP_WAITMOVEMENT 0x51
|
||||
#define SCR_OP_WAITMOVEMENTAT 0x52
|
||||
#define SCR_OP_REMOVEOBJECT 0x53
|
||||
#define SCR_OP_REMOVEOBJECTAT 0x54
|
||||
#define SCR_OP_ADDOBJECT 0x55
|
||||
#define SCR_OP_ADDOBJECTAT 0x56
|
||||
#define SCR_OP_SETOBJECTXY 0x57
|
||||
#define SCR_OP_SHOWOBJECTAT 0x58
|
||||
#define SCR_OP_HIDEOBJECTAT 0x59
|
||||
#define SCR_OP_FACEPLAYER 0x5a
|
||||
#define SCR_OP_TURNOBJECT 0x5b
|
||||
#define SCR_OP_TRAINERBATTLE 0x5c
|
||||
#define SCR_OP_DOTRAINERBATTLE 0x5d
|
||||
#define SCR_OP_GOTOPOSTBATTLESCRIPT 0x5e
|
||||
#define SCR_OP_GOTOBEATENSCRIPT 0x5f
|
||||
#define SCR_OP_CHECKTRAINERFLAG 0x60
|
||||
#define SCR_OP_SETTRAINERFLAG 0x61
|
||||
#define SCR_OP_CLEARTRAINERFLAG 0x62
|
||||
#define SCR_OP_SETOBJECTXYPERM 0x63
|
||||
#define SCR_OP_COPYOBJECTXYTOPERM 0x64
|
||||
#define SCR_OP_SETOBJECTMOVEMENTTYPE 0x65
|
||||
#define SCR_OP_WAITMESSAGE 0x66
|
||||
#define SCR_OP_MESSAGE 0x67
|
||||
#define SCR_OP_CLOSEMESSAGE 0x68
|
||||
#define SCR_OP_LOCKALL 0x69
|
||||
#define SCR_OP_LOCK 0x6a
|
||||
#define SCR_OP_RELEASEALL 0x6b
|
||||
#define SCR_OP_RELEASE 0x6c
|
||||
#define SCR_OP_WAITBUTTONPRESS 0x6d
|
||||
#define SCR_OP_YESNOBOX 0x6e
|
||||
#define SCR_OP_MULTICHOICE 0x6f
|
||||
#define SCR_OP_MULTICHOICEDEFAULT 0x70
|
||||
#define SCR_OP_MULTICHOICEGRID 0x71
|
||||
#define SCR_OP_DRAWBOX 0x72
|
||||
#define SCR_OP_ERASEBOX 0x73
|
||||
#define SCR_OP_DRAWBOXTEXT 0x74
|
||||
#define SCR_OP_SHOWMONPIC 0x75
|
||||
#define SCR_OP_HIDEMONPIC 0x76
|
||||
#define SCR_OP_SHOWCONTESTPAINTING 0x77
|
||||
#define SCR_OP_BRAILLEMESSAGE 0x78
|
||||
#define SCR_OP_GIVEMON 0x79
|
||||
#define SCR_OP_GIVEEGG 0x7a
|
||||
#define SCR_OP_SETMONMOVE 0x7b
|
||||
#define SCR_OP_CHECKFIELDMOVE 0x7c
|
||||
#define SCR_OP_BUFFERSPECIESNAME 0x7d
|
||||
#define SCR_OP_BUFFERLEADMONSPECIESNAME 0x7e
|
||||
#define SCR_OP_BUFFERPARTYMONNICK 0x7f
|
||||
#define SCR_OP_BUFFERITEMNAME 0x80
|
||||
#define SCR_OP_BUFFERDECORATIONNAME 0x81
|
||||
#define SCR_OP_BUFFERMOVENAME 0x82
|
||||
#define SCR_OP_BUFFERNUMBERSTRING 0x83
|
||||
#define SCR_OP_BUFFERSTDSTRING 0x84
|
||||
#define SCR_OP_BUFFERSTRING 0x85
|
||||
#define SCR_OP_POKEMART 0x86
|
||||
#define SCR_OP_POKEMARTDECORATION 0x87
|
||||
#define SCR_OP_POKEMARTDECORATION2 0x88
|
||||
#define SCR_OP_PLAYSLOTMACHINE 0x89
|
||||
#define SCR_OP_SETBERRYTREE 0x8a
|
||||
#define SCR_OP_CHOOSECONTESTMON 0x8b
|
||||
#define SCR_OP_STARTCONTEST 0x8c
|
||||
#define SCR_OP_SHOWCONTESTRESULTS 0x8d
|
||||
#define SCR_OP_CONTESTLINKTRANSFER 0x8e
|
||||
#define SCR_OP_RANDOM 0x8f
|
||||
#define SCR_OP_ADDMONEY 0x90
|
||||
#define SCR_OP_REMOVEMONEY 0x91
|
||||
#define SCR_OP_CHECKMONEY 0x92
|
||||
#define SCR_OP_SHOWMONEYBOX 0x93
|
||||
#define SCR_OP_HIDEMONEYBOX 0x94
|
||||
#define SCR_OP_UPDATEMONEYBOX 0x95
|
||||
#define SCR_OP_GETPOKENEWSACTIVE 0x96
|
||||
#define SCR_OP_FADESCREEN 0x97
|
||||
#define SCR_OP_FADESCREENSPEED 0x98
|
||||
#define SCR_OP_SETFLASHLEVEL 0x99
|
||||
#define SCR_OP_ANIMATEFLASH 0x9a
|
||||
#define SCR_OP_MESSAGEAUTOSCROLL 0x9b
|
||||
#define SCR_OP_DOFIELDEFFECT 0x9c
|
||||
#define SCR_OP_SETFIELDEFFECTARGUMENT 0x9d
|
||||
#define SCR_OP_WAITFIELDEFFECT 0x9e
|
||||
#define SCR_OP_SETRESPAWN 0x9f
|
||||
#define SCR_OP_CHECKPLAYERGENDER 0xa0
|
||||
#define SCR_OP_PLAYMONCRY 0xa1
|
||||
#define SCR_OP_SETMETATILE 0xa2
|
||||
#define SCR_OP_RESETWEATHER 0xa3
|
||||
#define SCR_OP_SETWEATHER 0xa4
|
||||
#define SCR_OP_DOWEATHER 0xa5
|
||||
#define SCR_OP_SETSTEPCALLBACK 0xa6
|
||||
#define SCR_OP_SETMAPLAYOUTINDEX 0xa7
|
||||
#define SCR_OP_SETOBJECTSUBPRIORITY 0xa8
|
||||
#define SCR_OP_RESETOBJECTSUBPRIORITY 0xa9
|
||||
#define SCR_OP_CREATEVOBJECT 0xaa
|
||||
#define SCR_OP_TURNVOBJECT 0xab
|
||||
#define SCR_OP_OPENDOOR 0xac
|
||||
#define SCR_OP_CLOSEDOOR 0xad
|
||||
#define SCR_OP_WAITDOORANIM 0xae
|
||||
#define SCR_OP_SETDOOROPEN 0xaf
|
||||
#define SCR_OP_SETDOORCLOSED 0xb0
|
||||
#define SCR_OP_ADDELEVMENUITEM 0xb1
|
||||
#define SCR_OP_SHOWELEVMENU 0xb2
|
||||
#define SCR_OP_CHECKCOINS 0xb3
|
||||
#define SCR_OP_ADDCOINS 0xb4
|
||||
#define SCR_OP_REMOVECOINS 0xb5
|
||||
#define SCR_OP_SETWILDBATTLE 0xb6
|
||||
#define SCR_OP_DOWILDBATTLE 0xb7
|
||||
#define SCR_OP_SETVADDRESS 0xb8
|
||||
#define SCR_OP_VGOTO 0xb9
|
||||
#define SCR_OP_VCALL 0xba
|
||||
#define SCR_OP_VGOTO_IF 0xbb
|
||||
#define SCR_OP_VCALL_IF 0xbc
|
||||
#define SCR_OP_VMESSAGE 0xbd
|
||||
#define SCR_OP_VBUFFERMESSAGE 0xbe
|
||||
#define SCR_OP_VBUFFERSTRING 0xbf
|
||||
#define SCR_OP_SHOWCOINSBOX 0xc0
|
||||
#define SCR_OP_HIDECOINSBOX 0xc1
|
||||
#define SCR_OP_UPDATECOINSBOX 0xc2
|
||||
#define SCR_OP_INCREMENTGAMESTAT 0xc3
|
||||
#define SCR_OP_SETESCAPEWARP 0xc4
|
||||
#define SCR_OP_WAITMONCRY 0xc5
|
||||
#define SCR_OP_BUFFERBOXNAME 0xc6
|
||||
#define SCR_OP_TEXTCOLOR 0xc7
|
||||
#define SCR_OP_LOADHELP 0xc8
|
||||
#define SCR_OP_UNLOADHELP 0xc9
|
||||
#define SCR_OP_SIGNMSG 0xca
|
||||
#define SCR_OP_NORMALMSG 0xcb
|
||||
#define SCR_OP_COMPAREHIDDENVAR 0xcc
|
||||
#define SCR_OP_SETMODERNFATEFULENCOUNTER 0xcd
|
||||
#define SCR_OP_CHECKMODERNFATEFULENCOUNTER 0xce
|
||||
#define SCR_OP_TRYWONDERCARDSCRIPT 0xcf
|
||||
#define SCR_OP_SETWORLDMAPFLAG 0xd0
|
||||
#define SCR_OP_WARPSPINENTER 0xd1
|
||||
#define SCR_OP_SETMONMETLOCATION 0xd2
|
||||
#define SCR_OP_MOVEROTATINGTILEOBJECTS 0xd3
|
||||
#define SCR_OP_TURNROTATINGTILEOBJECTS 0xd4
|
||||
#define SCR_OP_INITROTATINGTILEPUZZLE 0xd5
|
||||
#define SCR_OP_FREEROTATINGTILEPUZZLE 0xd6
|
||||
#define SCR_OP_WARPMOSSDEEPGYM 0xd7
|
||||
#define SCR_OP_SELECTAPPROACHINGTRAINER 0xd8
|
||||
#define SCR_OP_LOCKFORTRAINER 0xd9
|
||||
#define SCR_OP_CLOSEBRAILLEMESSAGE 0xda
|
||||
#define SCR_OP_MESSAGEINSTANT 0xdb
|
||||
#define SCR_OP_FADESCREENSWAPBUFFERS 0xdc
|
||||
#define SCR_OP_BUFFERTRAINERCLASSNAME 0xdd
|
||||
#define SCR_OP_BUFFERTRAINERNAME 0xde
|
||||
#define SCR_OP_POKENAVCALL 0xdf
|
||||
#define SCR_OP_WARPWHITEFADE 0xe0
|
||||
#define SCR_OP_BUFFERCONTESTNAME 0xe1
|
||||
#define SCR_OP_BUFFERITEMNAMEPLURAL 0xe2
|
||||
#define SCR_OP_DYNMULTICHOICE 0xe3
|
||||
#define SCR_OP_DYNMULTIPUSH 0xe4
|
||||
#define SCR_OP_HIDEFOLLOWER 0xe5
|
||||
|
||||
#endif // GUARD_SCR_CMD_CONSTANTS_H
|
||||
@ -658,10 +658,10 @@ extern const struct Fusion *const gFusionTablePointers[NUM_SPECIES];
|
||||
#if P_FUSION_FORMS
|
||||
#if P_FAMILY_KYUREM
|
||||
#if P_FAMILY_RESHIRAM
|
||||
extern const u16 gKyurenWhiteSwapMoveTable[][2];
|
||||
extern const u16 gKyuremWhiteSwapMoveTable[][2];
|
||||
#endif //P_FAMILY_RESHIRAM
|
||||
#if P_FAMILY_ZEKROM
|
||||
extern const u16 gKyurenBlackSwapMoveTable[][2];
|
||||
extern const u16 gKyuremBlackSwapMoveTable[][2];
|
||||
#endif //P_FAMILY_ZEKROM
|
||||
#endif //P_FAMILY_KYUREM
|
||||
#endif //P_FUSION_FORMS
|
||||
|
||||
@ -143,7 +143,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a != _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_EQ(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_NE(a, b) \
|
||||
@ -151,7 +151,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a == _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_NE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_LT(a, b) \
|
||||
@ -159,7 +159,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a >= _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_LE(a, b) \
|
||||
@ -167,7 +167,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a > _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_LE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_GT(a, b) \
|
||||
@ -175,7 +175,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a <= _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GT(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
|
||||
} while (0)
|
||||
|
||||
#define EXPECT_GE(a, b) \
|
||||
@ -183,7 +183,7 @@ s32 Test_MgbaPrintf(const char *fmt, ...);
|
||||
{ \
|
||||
typeof(a) _a = (a), _b = (b); \
|
||||
if (_a < _b) \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, _a, _b); \
|
||||
Test_ExitWithResult(TEST_RESULT_FAIL, __LINE__, ":L%s:%d: EXPECT_GE(%d, %d) failed", gTestRunnerState.test->filename, __LINE__, a, b); \
|
||||
} while (0)
|
||||
|
||||
struct Benchmark { s32 ticks; };
|
||||
|
||||
@ -9,6 +9,7 @@ void MapResetTrainerRematches(u16 mapGroup, u16 mapNum);
|
||||
void ClearRematchMovementByTrainerId(void);
|
||||
u16 GetRematchTrainerIdVSSeeker(u16 trainerId);
|
||||
bool32 IsVsSeekerEnabled(void);
|
||||
void NativeVsSeekerRematchId(struct ScriptContext *ctx);
|
||||
|
||||
#define VSSEEKER_RECHARGE_STEPS 100
|
||||
|
||||
|
||||
@ -5217,7 +5217,7 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
|
||||
if (gBattleMons[battlerDef].speed > gBattleMons[battlerAtk].speed)
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_GUARD_SPLIT:
|
||||
case EFFECT_GUARD_SPLIT:
|
||||
{
|
||||
u32 atkDefense = gBattleMons[battlerAtk].defense;
|
||||
u32 defDefense = gBattleMons[battlerDef].defense;
|
||||
@ -5761,6 +5761,9 @@ static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, u32 move
|
||||
}
|
||||
else // consider move effects that hinder the target
|
||||
{
|
||||
if (IsAdditionalEffectBlocked(battlerAtk, aiData->abilities[battlerAtk], battlerDef, aiData->abilities[battlerDef]))
|
||||
continue;
|
||||
|
||||
switch (additionalEffect->moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_FLINCH:
|
||||
|
||||
@ -732,6 +732,17 @@ bool32 IsDamageMoveUnusable(struct DamageContext *ctx)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsAdditionalEffectBlocked(u32 battlerAtk, u32 abilityAtk, u32 battlerDef, u32 abilityDef)
|
||||
{
|
||||
if (gAiLogicData->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK)
|
||||
return TRUE;
|
||||
|
||||
if (abilityDef == ABILITY_SHIELD_DUST && !IsMoldBreakerTypeAbility(battlerAtk, abilityAtk))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static inline s32 GetDamageByRollType(s32 dmg, enum DamageRollType rollType)
|
||||
{
|
||||
if (rollType == DMG_ROLL_LOWEST)
|
||||
@ -1099,6 +1110,9 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
|
||||
}
|
||||
else // consider move effects that hinder the target
|
||||
{
|
||||
if (IsAdditionalEffectBlocked(battlerAtk, abilityAtk, battlerDef, abilityDef))
|
||||
continue;
|
||||
|
||||
switch (additionalEffect->moveEffect)
|
||||
{
|
||||
case MOVE_EFFECT_POISON:
|
||||
@ -1133,7 +1147,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
|
||||
case MOVE_EFFECT_SP_DEF_MINUS_1:
|
||||
case MOVE_EFFECT_ACC_MINUS_1:
|
||||
case MOVE_EFFECT_EVS_MINUS_1:
|
||||
if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1)) && noOfHitsToKo != 1)
|
||||
if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_1)) && noOfHitsToKo > 1)
|
||||
return TRUE;
|
||||
break;
|
||||
case MOVE_EFFECT_ATK_MINUS_2:
|
||||
@ -1143,7 +1157,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
|
||||
case MOVE_EFFECT_SP_DEF_MINUS_2:
|
||||
case MOVE_EFFECT_ACC_MINUS_2:
|
||||
case MOVE_EFFECT_EVS_MINUS_2:
|
||||
if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2)) && noOfHitsToKo != 1)
|
||||
if (CanLowerStat(battlerAtk, battlerDef, gAiLogicData, STAT_ATK + (additionalEffect->moveEffect - MOVE_EFFECT_ATK_MINUS_2)) && noOfHitsToKo > 1)
|
||||
return TRUE;
|
||||
break;
|
||||
default:
|
||||
@ -1200,7 +1214,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s
|
||||
case MOVE_EFFECT_ATK_DEF_DOWN:
|
||||
case MOVE_EFFECT_DEF_SPDEF_DOWN:
|
||||
if ((additionalEffect->self && abilityAtk != ABILITY_CONTRARY)
|
||||
|| (noOfHitsToKo != 1 && abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move)))
|
||||
|| (noOfHitsToKo > 1 && !additionalEffect->self && abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move)))
|
||||
return TRUE;
|
||||
break;
|
||||
case MOVE_EFFECT_RECHARGE:
|
||||
@ -1221,7 +1235,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s
|
||||
case MOVE_EFFECT_ACC_PLUS_2:
|
||||
case MOVE_EFFECT_ALL_STATS_UP:
|
||||
if ((additionalEffect->self && abilityAtk == ABILITY_CONTRARY)
|
||||
|| (noOfHitsToKo != 1 && !(abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move))))
|
||||
|| (noOfHitsToKo > 1 && !additionalEffect->self && !(abilityDef == ABILITY_CONTRARY && !DoesBattlerIgnoreAbilityChecks(battlerAtk, abilityAtk, move))))
|
||||
return TRUE;
|
||||
break;
|
||||
default:
|
||||
|
||||
@ -635,15 +635,8 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
|
||||
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
|
||||
gBattleSpritesDataPtr->battlerData[battler].transformSpecies = species = GetGMaxTargetSpecies(species);
|
||||
|
||||
if (B_TRANSFORM_SHINY >= GEN_4)
|
||||
{
|
||||
personalityValue = gTransformedPersonalities[battler];
|
||||
isShiny = gTransformedShininess[battler];
|
||||
}
|
||||
else
|
||||
{
|
||||
personalityValue = GetMonData(mon, MON_DATA_PERSONALITY);
|
||||
}
|
||||
personalityValue = gTransformedPersonalities[battler];
|
||||
isShiny = gTransformedShininess[battler];
|
||||
}
|
||||
|
||||
position = GetBattlerPosition(battler);
|
||||
@ -949,24 +942,23 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo
|
||||
targetSpecies = GetIllusionMonSpecies(battlerDef);
|
||||
else
|
||||
targetSpecies = GetMonData(monDef, MON_DATA_SPECIES);
|
||||
personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY);
|
||||
isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY);
|
||||
gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetSpecies = gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies;
|
||||
if (B_TRANSFORM_SHINY >= GEN_4 && trackEnemyPersonality && !megaEvo)
|
||||
{
|
||||
personalityValue = GetMonData(monDef, MON_DATA_PERSONALITY);
|
||||
isShiny = GetMonData(monDef, MON_DATA_IS_SHINY);
|
||||
}
|
||||
else
|
||||
{
|
||||
personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY);
|
||||
isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY);
|
||||
}
|
||||
}
|
||||
|
||||
if (trackEnemyPersonality)
|
||||
{
|
||||
personalityValue = gDisableStructs[battlerAtk].transformedMonPersonality;
|
||||
isShiny = gDisableStructs[battlerAtk].transformedMonShininess;
|
||||
}
|
||||
else
|
||||
{
|
||||
personalityValue = GetMonData(monAtk, MON_DATA_PERSONALITY);
|
||||
isShiny = GetMonData(monAtk, MON_DATA_IS_SHINY);
|
||||
}
|
||||
HandleLoadSpecialPokePic(!IsOnPlayerSide(battlerAtk),
|
||||
gMonSpritesGfxPtr->spritesGfx[position],
|
||||
targetSpecies,
|
||||
@ -983,10 +975,6 @@ void HandleSpeciesGfxDataChange(u8 battlerAtk, u8 battlerDef, bool32 megaEvo, bo
|
||||
{
|
||||
BlendPalette(paletteOffset, 16, 6, RGB_WHITE);
|
||||
CpuCopy32(&gPlttBufferFaded[paletteOffset], &gPlttBufferUnfaded[paletteOffset], PLTT_SIZEOF(16));
|
||||
if (!IsContest())
|
||||
{
|
||||
gBattleSpritesDataPtr->battlerData[battlerAtk].transformSpecies = targetSpecies;
|
||||
}
|
||||
}
|
||||
|
||||
// dynamax tint
|
||||
|
||||
@ -3005,7 +3005,10 @@ static void ClearSetBScriptingStruct(void)
|
||||
memset(&gBattleScripting, 0, sizeof(gBattleScripting));
|
||||
|
||||
gBattleScripting.windowsType = temp;
|
||||
gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle;
|
||||
if (TESTING)
|
||||
gBattleScripting.battleStyle = OPTIONS_BATTLE_STYLE_SET;
|
||||
else
|
||||
gBattleScripting.battleStyle = gSaveBlock2Ptr->optionsBattleStyle;
|
||||
gBattleScripting.expOnCatch = (B_EXP_CATCH >= GEN_6);
|
||||
gBattleScripting.specialTrainerBattleType = specialBattleType;
|
||||
}
|
||||
@ -5675,6 +5678,7 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void)
|
||||
else
|
||||
gSaveBlock3Ptr->dexNavChain = 0;
|
||||
|
||||
ClearCurrentTrainerWantRematchVsSeeker();
|
||||
gDexNavSpecies = SPECIES_NONE;
|
||||
ResetSpriteData();
|
||||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK
|
||||
|
||||
@ -11139,7 +11139,10 @@ static void Cmd_transformdataexecution(void)
|
||||
gDisableStructs[gBattlerAttacker].disabledMove = MOVE_NONE;
|
||||
gDisableStructs[gBattlerAttacker].disableTimer = 0;
|
||||
gDisableStructs[gBattlerAttacker].transformedMonPersonality = gBattleMons[gBattlerTarget].personality;
|
||||
gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny;
|
||||
if (B_TRANSFORM_SHINY >= GEN_4)
|
||||
gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerTarget].isShiny;
|
||||
else
|
||||
gDisableStructs[gBattlerAttacker].transformedMonShininess = gBattleMons[gBattlerAttacker].isShiny;
|
||||
gDisableStructs[gBattlerAttacker].mimickedMoves = 0;
|
||||
gDisableStructs[gBattlerAttacker].usedMoves = 0;
|
||||
|
||||
|
||||
@ -1131,6 +1131,13 @@ bool32 GetTrainerFlagFromScriptPointer(const u8 *data)
|
||||
TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET);
|
||||
return FlagGet(TRAINER_FLAGS_START + temp->params.opponentA);
|
||||
}
|
||||
|
||||
bool32 GetRematchFromScriptPointer(const u8 *data)
|
||||
{
|
||||
TrainerBattleParameter *temp = (TrainerBattleParameter*)(data + OPCODE_OFFSET);
|
||||
return ShouldTryRematchBattleForTrainerId(temp->params.opponentA);
|
||||
}
|
||||
|
||||
#undef OPCODE_OFFSET
|
||||
|
||||
// Set trainer's movement type so they stop and remain facing that direction
|
||||
@ -1757,6 +1764,20 @@ static void ClearTrainerWantRematchState(const struct RematchTrainer *table, u16
|
||||
#endif //FREE_MATCH_CALL
|
||||
}
|
||||
|
||||
void ClearCurrentTrainerWantRematchVsSeeker(void)
|
||||
{
|
||||
#if FREE_MATCH_CALL == FALSE
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_TRAINER) && FlagGet(I_VS_SEEKER_CHARGING) && (I_VS_SEEKER_CHARGING != 0))
|
||||
{
|
||||
for (u32 i = 0; i < REMATCH_TABLE_ENTRIES; i++)
|
||||
{
|
||||
if (gSaveBlock1Ptr->trainerRematches[i] == TRAINER_BATTLE_PARAM.opponentA)
|
||||
gSaveBlock1Ptr->trainerRematches[i] = 0;
|
||||
}
|
||||
}
|
||||
#endif //FREE_MATCH_CALL
|
||||
}
|
||||
|
||||
static u32 GetTrainerMatchCallFlag(u32 trainerId)
|
||||
{
|
||||
s32 i;
|
||||
@ -1788,12 +1809,16 @@ static bool8 WasSecondRematchWon(const struct RematchTrainer *table, u16 firstBa
|
||||
return FALSE;
|
||||
if (!HasTrainerBeenFought(table[tableId].trainerIds[1]))
|
||||
return FALSE;
|
||||
|
||||
if (I_VS_SEEKER_CHARGING)
|
||||
{
|
||||
if (gSaveBlock1Ptr->trainerRematches[tableId] == 0)
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if FREE_MATCH_CALL == FALSE
|
||||
static bool32 HasAtLeastFiveBadges(void)
|
||||
static bool32 HasEnoughBadgesForRematch(void)
|
||||
{
|
||||
s32 i, count;
|
||||
|
||||
@ -1801,7 +1826,7 @@ static bool32 HasAtLeastFiveBadges(void)
|
||||
{
|
||||
if (FlagGet(gBadgeFlags[i]) == TRUE)
|
||||
{
|
||||
if (++count >= 5)
|
||||
if (++count >= OW_REMATCH_BADGE_COUNT)
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@ -1815,7 +1840,7 @@ static bool32 HasAtLeastFiveBadges(void)
|
||||
void IncrementRematchStepCounter(void)
|
||||
{
|
||||
#if FREE_MATCH_CALL == FALSE
|
||||
if (!HasAtLeastFiveBadges())
|
||||
if (!HasEnoughBadgesForRematch())
|
||||
return;
|
||||
|
||||
if (IsVsSeekerEnabled())
|
||||
@ -1831,7 +1856,7 @@ void IncrementRematchStepCounter(void)
|
||||
#if FREE_MATCH_CALL == FALSE
|
||||
static bool32 IsRematchStepCounterMaxed(void)
|
||||
{
|
||||
if (HasAtLeastFiveBadges() && gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
|
||||
if (HasEnoughBadgesForRematch() && gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
@ -1871,10 +1896,15 @@ u16 GetLastBeatenRematchTrainerId(u16 trainerId)
|
||||
|
||||
bool8 ShouldTryRematchBattle(void)
|
||||
{
|
||||
if (IsFirstTrainerIdReadyForRematch(gRematchTable, TRAINER_BATTLE_PARAM.opponentA))
|
||||
return ShouldTryRematchBattleForTrainerId(TRAINER_BATTLE_PARAM.opponentA);
|
||||
}
|
||||
|
||||
bool8 ShouldTryRematchBattleForTrainerId(u16 trainerId)
|
||||
{
|
||||
if (IsFirstTrainerIdReadyForRematch(gRematchTable, trainerId))
|
||||
return TRUE;
|
||||
|
||||
return WasSecondRematchWon(gRematchTable, TRAINER_BATTLE_PARAM.opponentA);
|
||||
return WasSecondRematchWon(gRematchTable, trainerId);
|
||||
}
|
||||
|
||||
bool8 IsTrainerReadyForRematch(void)
|
||||
|
||||
@ -9767,7 +9767,6 @@ bool32 PickupHasValidTarget(u32 battler)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// TODO: Pass down weather as an arg
|
||||
bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags)
|
||||
{
|
||||
if (gBattleWeather & weatherFlags && HasWeatherEffect())
|
||||
|
||||
@ -2432,6 +2432,7 @@ static void CalculatePokeblock(struct BlenderBerry *berries, struct Pokeblock *p
|
||||
}
|
||||
|
||||
// Factor in max RPM and round
|
||||
multiuseVar = maxRPM / 333 + 100;
|
||||
for (i = 0; i < FLAVOR_COUNT; i++)
|
||||
{
|
||||
s32 remainder;
|
||||
|
||||
@ -38,14 +38,14 @@ const struct Fusion *const gFusionTablePointers[NUM_SPECIES] =
|
||||
#if P_FUSION_FORMS
|
||||
#if P_FAMILY_KYUREM
|
||||
#if P_FAMILY_RESHIRAM
|
||||
const u16 gKyurenWhiteSwapMoveTable[][2] =
|
||||
const u16 gKyuremWhiteSwapMoveTable[][2] =
|
||||
{
|
||||
{MOVE_SCARY_FACE, MOVE_FUSION_FLARE},
|
||||
{MOVE_GLACIATE, MOVE_ICE_BURN},
|
||||
};
|
||||
#endif //P_FAMILY_RESHIRAM
|
||||
#if P_FAMILY_ZEKROM
|
||||
const u16 gKyurenBlackSwapMoveTable[][2] =
|
||||
const u16 gKyuremBlackSwapMoveTable[][2] =
|
||||
{
|
||||
{MOVE_SCARY_FACE, MOVE_FUSION_BOLT},
|
||||
{MOVE_GLACIATE, MOVE_FREEZE_SHOCK},
|
||||
|
||||
@ -13015,7 +13015,7 @@ const struct SpeciesInfo gSpeciesInfoGen1[] =
|
||||
.height = 5,
|
||||
.weight = 130,
|
||||
.description = COMPOUND_STRING(
|
||||
"It esembles an ancient Poké Ball design.\n"
|
||||
"It resembles an ancient Poké Ball design.\n"
|
||||
"When excited, it discharges the electric\n"
|
||||
"current it has stored in its belly, then\n"
|
||||
"lets out a great, uproarious laugh."),
|
||||
|
||||
@ -1512,15 +1512,7 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
|
||||
case PARTY_ACTION_SEND_MON_TO_BOX:
|
||||
{
|
||||
u8 partyId = (u8)*slotPtr;
|
||||
if (partyId == 0 || ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && partyId == 1))
|
||||
{
|
||||
// Can't select if mon is currently on the field
|
||||
PlaySE(SE_FAILURE);
|
||||
DisplayPartyMenuMessage(gText_CannotSendMonToBoxActive, FALSE);
|
||||
ScheduleBgCopyTilemapToVram(2);
|
||||
gTasks[taskId].func = Task_ReturnToChooseMonAfterText;
|
||||
}
|
||||
else if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2))
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2))
|
||||
{
|
||||
// Can't select if mon doesn't belong to you
|
||||
PlaySE(SE_FAILURE);
|
||||
@ -6431,11 +6423,11 @@ static void Task_TryItemUseFusionChange(u8 taskId)
|
||||
#if P_FAMILY_KYUREM
|
||||
#if P_FAMILY_RESHIRAM
|
||||
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
|
||||
SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, FUSE_MON);
|
||||
SwapFusionMonMoves(mon, gKyuremWhiteSwapMoveTable, FUSE_MON);
|
||||
#endif //P_FAMILY_RESHIRAM
|
||||
#if P_FAMILY_ZEKROM
|
||||
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK)
|
||||
SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, FUSE_MON);
|
||||
SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, FUSE_MON);
|
||||
#endif //P_FAMILY_ZEKROM
|
||||
#endif //P_FAMILY_KYUREM
|
||||
if (gTasks[taskId].moveToLearn != 0)
|
||||
@ -6446,11 +6438,11 @@ static void Task_TryItemUseFusionChange(u8 taskId)
|
||||
#if P_FAMILY_KYUREM
|
||||
#if P_FAMILY_RESHIRAM
|
||||
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_WHITE)
|
||||
SwapFusionMonMoves(mon, gKyurenWhiteSwapMoveTable, UNFUSE_MON);
|
||||
SwapFusionMonMoves(mon, gKyuremWhiteSwapMoveTable, UNFUSE_MON);
|
||||
#endif //P_FAMILY_RESHIRAM
|
||||
#if P_FAMILY_ZEKROM
|
||||
if (gTasks[taskId].tExtraMoveHandling == SWAP_EXTRA_MOVES_KYUREM_BLACK)
|
||||
SwapFusionMonMoves(mon, gKyurenBlackSwapMoveTable, UNFUSE_MON);
|
||||
SwapFusionMonMoves(mon, gKyuremBlackSwapMoveTable, UNFUSE_MON);
|
||||
#endif //P_FAMILY_ZEKROM
|
||||
#endif //P_FAMILY_KYUREM
|
||||
if ( gTasks[taskId].tExtraMoveHandling == FORGET_EXTRA_MOVES)
|
||||
|
||||
@ -3799,18 +3799,18 @@ static const u8 *GetLetterGrade(u32 stat)
|
||||
static const u8 gText_GradeA[] = _("A");
|
||||
static const u8 gText_GradeS[] = _("S");
|
||||
|
||||
if (stat > 0 && stat <= 15)
|
||||
return gText_GradeD;
|
||||
else if (stat > 15 && stat <= 25)
|
||||
return gText_GradeC;
|
||||
else if (stat > 26 && stat <= 29)
|
||||
return gText_GradeB;
|
||||
else if (stat == 30)
|
||||
return gText_GradeA;
|
||||
else if (stat == 31)
|
||||
return gText_GradeS;
|
||||
else
|
||||
if (stat <= 0)
|
||||
return gText_GradeF;
|
||||
else if (stat <= 15)
|
||||
return gText_GradeD;
|
||||
else if (stat <= 25)
|
||||
return gText_GradeC;
|
||||
else if (stat <= 29)
|
||||
return gText_GradeB;
|
||||
else if (stat <= 30)
|
||||
return gText_GradeA;
|
||||
else
|
||||
return gText_GradeS;
|
||||
}
|
||||
|
||||
static void BufferLeftColumnStats(void)
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "constants/event_objects.h"
|
||||
#include "constants/event_object_movement.h"
|
||||
#include "constants/field_effects.h"
|
||||
#include "constants/script_commands.h"
|
||||
#include "constants/trainer_types.h"
|
||||
|
||||
// this file's functions
|
||||
@ -466,7 +467,7 @@ static u8 CheckTrainer(u8 objectEventId)
|
||||
struct ScriptContext ctx;
|
||||
if (RunScriptImmediatelyUntilEffect(SCREFF_V1 | SCREFF_SAVE | SCREFF_HARDWARE | SCREFF_TRAINERBATTLE, trainerBattlePtr, &ctx))
|
||||
{
|
||||
if (*ctx.scriptPtr == 0x5c) // trainerbattle
|
||||
if (*ctx.scriptPtr == SCR_OP_TRAINERBATTLE)
|
||||
trainerBattlePtr = ctx.scriptPtr;
|
||||
else
|
||||
trainerBattlePtr = NULL;
|
||||
@ -490,7 +491,18 @@ static u8 CheckTrainer(u8 objectEventId)
|
||||
else if (trainerBattlePtr)
|
||||
{
|
||||
if (GetTrainerFlagFromScriptPointer(trainerBattlePtr))
|
||||
return 0;
|
||||
{
|
||||
//If there is a rematch, we want to trigger the approach sequence
|
||||
if (GetRematchFromScriptPointer(trainerBattlePtr))
|
||||
{
|
||||
trainerBattlePtr = NULL;
|
||||
numTrainers = 0xFF;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -28,9 +28,12 @@
|
||||
#include "constants/items.h"
|
||||
#include "constants/maps.h"
|
||||
#include "constants/songs.h"
|
||||
#include "constants/script_commands.h"
|
||||
#include "constants/trainer_types.h"
|
||||
#include "constants/field_effects.h"
|
||||
|
||||
// Documentation for the Vs. Seeker can be found in docs/tutorials/vs_seeker.md.
|
||||
|
||||
enum
|
||||
{
|
||||
VSSEEKER_NOT_CHARGED,
|
||||
@ -230,6 +233,9 @@ bool8 UpdateVsSeekerStepCounter(void)
|
||||
|
||||
if (!I_VS_SEEKER_CHARGING) return FALSE;
|
||||
|
||||
// This condition helps in case your save file is switching between vs seeker and matchcall
|
||||
if (gSaveBlock1Ptr->trainerRematchStepCounter > VSSEEKER_RECHARGE_STEPS && gSaveBlock1Ptr->trainerRematchStepCounter <= 0xFF)
|
||||
gSaveBlock1Ptr->trainerRematchStepCounter = 0;
|
||||
if (CheckBagHasItem(ITEM_VS_SEEKER, 1))
|
||||
{
|
||||
if ((gSaveBlock1Ptr->trainerRematchStepCounter & 0xFF) < VSSEEKER_RECHARGE_STEPS)
|
||||
@ -376,8 +382,11 @@ static void GatherNearbyTrainerInfo(void)
|
||||
if (templates[objectEventIdx].trainerType != TRAINER_TYPE_NORMAL && templates[objectEventIdx].trainerType != TRAINER_TYPE_BURIED)
|
||||
continue;
|
||||
|
||||
u16 trainerIdx = GetTrainerFlagFromScript(templates[objectEventIdx].script);
|
||||
if (trainerIdx == TRAINER_NONE)
|
||||
continue;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].script = templates[objectEventIdx].script;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].trainerIdx = GetTrainerFlagFromScript(templates[objectEventIdx].script);
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].trainerIdx = trainerIdx;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].localId = templates[objectEventIdx].localId;
|
||||
TryGetObjectEventIdByLocalIdAndMap(templates[objectEventIdx].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objectEventId);
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].objectEventId = objectEventId;
|
||||
@ -707,9 +716,31 @@ static u16 GetTrainerFlagFromScript(const u8 *script)
|
||||
{
|
||||
// The trainer flag is located 3 bytes (command + flags + localIdA) from the script pointer, assuming the trainerbattle command is first in the script.
|
||||
// Because scripts are unaligned, and because the ARM processor requires shorts to be 16-bit aligned, this function needs to perform explicit bitwise operations to get the correct flag.
|
||||
script += 3;
|
||||
u16 trainerFlag = script[0];
|
||||
trainerFlag |= script[1] << 8;
|
||||
u16 trainerFlag;
|
||||
switch (script[0])
|
||||
{
|
||||
case SCR_OP_TRAINERBATTLE:
|
||||
script += 3;
|
||||
trainerFlag = script[0];
|
||||
trainerFlag |= script[1] << 8;
|
||||
break;
|
||||
case SCR_OP_CALLNATIVE:
|
||||
u32 callnativeFunc = (((((script[4] << 8) + script[3]) << 8) + script[2]) << 8) + script[1];
|
||||
if (callnativeFunc == ((u32)NativeVsSeekerRematchId | 0xA000000)) // | 0xA000000 corresponds to the request_effects=1 version of the function
|
||||
{
|
||||
script += 5;
|
||||
trainerFlag = script[0];
|
||||
trainerFlag |= script[1] << 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
trainerFlag = TRAINER_NONE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
trainerFlag = TRAINER_NONE;
|
||||
break;
|
||||
}
|
||||
return trainerFlag;
|
||||
}
|
||||
|
||||
@ -788,6 +819,14 @@ static u8 GetCurVsSeekerResponse(s32 vsSeekerIdx, u16 trainerIdx)
|
||||
}
|
||||
#endif //FREE_MATCH_CALL
|
||||
|
||||
void NativeVsSeekerRematchId(struct ScriptContext *ctx)
|
||||
{
|
||||
u16 trainerId = ScriptReadHalfword(ctx);
|
||||
if (ctx->breakOnTrainerBattle && HasTrainerBeenFought(trainerId) && !ShouldTryRematchBattleForTrainerId(trainerId))
|
||||
StopScript(ctx);
|
||||
}
|
||||
|
||||
|
||||
static void StartAllRespondantIdleMovements(void)
|
||||
{
|
||||
#if FREE_MATCH_CALL == FALSE
|
||||
|
||||
@ -1,4 +1,63 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
DOUBLE_BATTLE_TEST("Receiver copies ally's ability when they faint and immediately activates it")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(!gAbilitiesInfo[ABILITY_INTIMIDATE].cantBeCopied);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); }
|
||||
OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); HP(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft);
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER);
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
|
||||
} THEN {
|
||||
EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2);
|
||||
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Receiver copies ally's ability when they faint and can activate it on future moves")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(!gAbilitiesInfo[ABILITY_WATER_ABSORB].cantBeCopied);
|
||||
ASSUME(GetMoveType(MOVE_WATER_GUN) == TYPE_WATER);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); }
|
||||
OPPONENT(SPECIES_LANTURN) { Ability(ABILITY_WATER_ABSORB); HP(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); MOVE(playerRight, MOVE_WATER_GUN, target: opponentLeft); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft);
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER);
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_WATER_ABSORB);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, playerRight);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Receiver copies ally's Soul Heart and immediately activates it")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(!gAbilitiesInfo[ABILITY_SOUL_HEART].cantBeCopied);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PASSIMIAN) { Ability(ABILITY_RECEIVER); }
|
||||
OPPONENT(SPECIES_MAGEARNA) { Ability(ABILITY_SOUL_HEART); HP(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentRight); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft);
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_RECEIVER);
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_SOUL_HEART);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponentLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Write Receiver (Ability) test titles")
|
||||
|
||||
@ -188,3 +188,31 @@ SINGLE_BATTLE_TEST("Shield Dust does not prevent ability stat changes")
|
||||
MESSAGE("Vivillon's Speed fell!");
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI will score secondary effects against shield dust correctly")
|
||||
{
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_DUSTOX){ Ability(ABILITY_SHIELD_DUST); Moves(MOVE_GUST); }
|
||||
OPPONENT(SPECIES_SUNFLORA){ Ability(ABILITY_EARLY_BIRD); Moves(MOVE_MYSTICAL_FIRE, MOVE_FIERY_DANCE); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(player, MOVE_GUST);
|
||||
EXPECT_MOVE(opponent, MOVE_FIERY_DANCE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI will score secondary effects against shield dust correctly when it has Mold Breaker")
|
||||
{
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_DUSTOX){ Ability(ABILITY_SHIELD_DUST); Moves(MOVE_GUST); }
|
||||
OPPONENT(SPECIES_SUNFLORA){ Ability(ABILITY_MOLD_BREAKER); Moves(MOVE_MYSTICAL_FIRE, MOVE_FIERY_DANCE); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(player, MOVE_GUST);
|
||||
EXPECT_MOVE(opponent, MOVE_MYSTICAL_FIRE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,6 +188,7 @@ TEST("Difficulty default to Normal is the trainer doesn't have a member for the
|
||||
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[GetTrainerDifficultyLevelTest(currTrainer)][currTrainer], TRUE, BATTLE_TYPE_TRAINER);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO);
|
||||
Free(testParty);
|
||||
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
|
||||
}
|
||||
|
||||
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (EASY)")
|
||||
@ -199,6 +200,7 @@ TEST("Difficulty changes which party if used for NPCs if defined for the difficu
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METAPOD);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 1);
|
||||
Free(testParty);
|
||||
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
|
||||
}
|
||||
|
||||
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (HARD)")
|
||||
@ -210,6 +212,7 @@ TEST("Difficulty changes which party if used for NPCs if defined for the difficu
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARCEUS);
|
||||
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 99);
|
||||
Free(testParty);
|
||||
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
|
||||
}
|
||||
|
||||
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (NORMAL)")
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include <stdarg.h>
|
||||
#include "fake_rtc.h"
|
||||
#include "global.h"
|
||||
#include "gpu_regs.h"
|
||||
#include "load_save.h"
|
||||
@ -184,6 +185,13 @@ void TestRunner_CheckMemory(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void ClearSaveBlocks(void)
|
||||
{
|
||||
ClearSav1();
|
||||
ClearSav2();
|
||||
ClearSav3();
|
||||
}
|
||||
|
||||
void CB2_TestRunner(void)
|
||||
{
|
||||
top:
|
||||
@ -201,17 +209,13 @@ top:
|
||||
gTestRunnerState.filterMode = DetectFilterMode(gTestRunnerArgv);
|
||||
|
||||
MoveSaveBlocks_ResetHeap();
|
||||
ClearSav1();
|
||||
ClearSav2();
|
||||
ClearSav3();
|
||||
|
||||
gIntrTable[7] = Intr_Timer2;
|
||||
|
||||
gSaveBlock2Ptr->optionsBattleStyle = OPTIONS_BATTLE_STYLE_SET;
|
||||
|
||||
// The current test restarted the ROM (e.g. by jumping to NULL).
|
||||
if (gPersistentTestRunnerState.address != 0)
|
||||
{
|
||||
ClearSaveBlocks();
|
||||
gTestRunnerState.test = __start_tests;
|
||||
while ((uintptr_t)gTestRunnerState.test != gPersistentTestRunnerState.address)
|
||||
{
|
||||
@ -255,6 +259,7 @@ top:
|
||||
break;
|
||||
|
||||
case STATE_ASSIGN_TEST:
|
||||
ClearSaveBlocks();
|
||||
while (1)
|
||||
{
|
||||
if (gTestRunnerState.test == __stop_tests)
|
||||
|
||||
@ -1557,6 +1557,7 @@ static void TearDownBattle(void)
|
||||
// Zero out the parties, data in them could potentially carry over
|
||||
ZeroPlayerPartyMons();
|
||||
ZeroEnemyPartyMons();
|
||||
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
|
||||
|
||||
FreeMonSpritesGfx();
|
||||
FreeBattleSpritesData();
|
||||
|
||||
25
tools/misc/make_scr_cmd_constants.py
Normal file
25
tools/misc/make_scr_cmd_constants.py
Normal file
@ -0,0 +1,25 @@
|
||||
import re
|
||||
|
||||
SCR_CMD_PAT = re.compile(r"\tscript_cmd_table_entry (\w+)\s+\w+,\s+[\w=]+\s+@( 0x[0-9a-f]+)")
|
||||
|
||||
def main():
|
||||
output = [
|
||||
"//",
|
||||
"// DO NOT MODIFY THIS FILE! It is auto-generated by tools/misc/make_scr_cmd_constants.py",
|
||||
"//",
|
||||
"#ifndef GUARD_SCR_CMD_CONSTANTS_H",
|
||||
"#define GUARD_SCR_CMD_CONSTANTS_H\n",
|
||||
]
|
||||
|
||||
with open("data/script_cmd_table.inc", "r") as f:
|
||||
for line in f.readlines():
|
||||
if match := re.match(SCR_CMD_PAT, line):
|
||||
new_line = "#define " + match.group(1) + match.group(2)
|
||||
output.append(new_line)
|
||||
|
||||
output.append("\n#endif // GUARD_SCR_CMD_CONSTANTS_H\n")
|
||||
with open("include/constants/script_commands.h", "w+") as f:
|
||||
f.write('\n'.join(output))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@ -391,35 +391,33 @@ def PrintWildMonHeadersContent():
|
||||
timeCounter += 1
|
||||
PrintEncounterHeaders(TabStr(2) + "},")
|
||||
PrintEncounterHeaders(tabStr + "},")
|
||||
|
||||
if labelCount + 1 == headerStructTable[group][label]["encounterTotalCount"]:
|
||||
PrintEncounterHeaders(tabStr + "{")
|
||||
PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(MAP_UNDEFINED)},")
|
||||
PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(MAP_UNDEFINED, labelCount + 1)},")
|
||||
|
||||
nullCount = 0
|
||||
while nullCount < TIMES_OF_DAY_COUNT:
|
||||
if nullCount == 0:
|
||||
PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =")
|
||||
PrintEncounterHeaders(TabStr(2)+ "{")
|
||||
|
||||
PrintEncounterHeaders(f"{TabStr(3)}[{TIME_OF_DAY.vals[nullCount]}] = ")
|
||||
|
||||
nullIndex = 0
|
||||
while nullIndex <= len(fieldData) - 1:
|
||||
if nullIndex == 0:
|
||||
PrintEncounterHeaders(TabStr(3) + "{")
|
||||
|
||||
PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(nullIndex)} = NULL,")
|
||||
|
||||
if nullIndex == len(fieldData) - 1:
|
||||
PrintEncounterHeaders(TabStr(3) + "},")
|
||||
|
||||
nullIndex += 1
|
||||
nullCount += 1
|
||||
PrintEncounterHeaders(TabStr(2) + "},")
|
||||
PrintEncounterHeaders(tabStr + "},")
|
||||
labelCount += 1
|
||||
PrintEncounterHeaders(tabStr + "{")
|
||||
PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(MAP_UNDEFINED)},")
|
||||
PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(MAP_UNDEFINED, labelCount + 1)},")
|
||||
|
||||
nullCount = 0
|
||||
while nullCount < TIMES_OF_DAY_COUNT:
|
||||
if nullCount == 0:
|
||||
PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =")
|
||||
PrintEncounterHeaders(TabStr(2)+ "{")
|
||||
|
||||
PrintEncounterHeaders(f"{TabStr(3)}[{TIME_OF_DAY.vals[nullCount]}] = ")
|
||||
|
||||
nullIndex = 0
|
||||
while nullIndex <= len(fieldData) - 1:
|
||||
if nullIndex == 0:
|
||||
PrintEncounterHeaders(TabStr(3) + "{")
|
||||
|
||||
PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(nullIndex)} = NULL,")
|
||||
|
||||
if nullIndex == len(fieldData) - 1:
|
||||
PrintEncounterHeaders(TabStr(3) + "},")
|
||||
|
||||
nullIndex += 1
|
||||
nullCount += 1
|
||||
PrintEncounterHeaders(TabStr(2) + "},")
|
||||
PrintEncounterHeaders(tabStr + "},")
|
||||
groupCount += 1
|
||||
PrintEncounterHeaders("};")
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user