Adds B_VAR_DIFFICULTY and related functions READ DESC (#5337)

Co-authored-by: sbird <sbird@no.tld>
Co-authored-by: Philipp AUER <SBird1337@users.noreply.github.com>
Co-authored-by: Martin Griffin <martinrgriffin@gmail.com>
Co-authored-by: hedara90 <90hedara@gmail.com>
This commit is contained in:
psf 2025-01-04 05:25:03 -08:00 committed by GitHub
parent 03684c6539
commit a7f77ed08d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 1432 additions and 934 deletions

View File

@ -2437,3 +2437,19 @@
.2byte \dest
.endm
.macro increasedifficulty
callnative Script_IncreaseDifficulty
.endm
.macro decreasedifficulty
callnative Script_DecreaseDifficulty
.endm
.macro getdifficulty var:req
callnative Script_GetDifficulty
.endm
.macro setdifficulty difficulty:req
callnative Script_SetDifficulty
.byte \difficulty
.endm

View File

@ -20,6 +20,7 @@
#include "constants/contest.h"
#include "constants/daycare.h"
#include "constants/decorations.h"
#include "constants/difficulty.h"
#include "constants/easy_chat.h"
#include "constants/event_objects.h"
#include "constants/event_object_movement.h"

View File

@ -206,6 +206,7 @@
#define B_VAR_STARTING_STATUS 0 // If this var has a value, assigning a STATUS_FIELD_xx_TERRAIN to it before battle causes the battle to start with that terrain active.
#define B_VAR_STARTING_STATUS_TIMER 0 // If this var has a value greater or equal than 1 field terrains will last that number of turns, otherwise they will last until they're overwritten.
#define B_VAR_WILD_AI_FLAGS 0 // If not 0, you can use this var to add to default wild AI flags. NOT usable with flags above (1 << 15)
#define B_VAR_DIFFICULTY 0 // If not 0, you can use this var to control which difficulty version of a Trainer is loaded. This should be manually set by the developer using Script_SetDifficulty AFTER NewGameInitData has run.
// Sky Battles
#define B_FLAG_SKY_BATTLE 0 // If this flag has a value, the player will be able to engage in scripted Sky Battles.

View File

@ -1129,6 +1129,10 @@
#undef P_FAMILY_PECHARUNT
#define P_FAMILY_PECHARUNT TRUE
// Vars
#undef B_VAR_DIFFICULTY
#define B_VAR_DIFFICULTY VAR_UNUSED_0x404E
// Flags
#undef B_FLAG_SLEEP_CLAUSE
#define B_FLAG_SLEEP_CLAUSE FLAG_SPECIAL_FLAG_UNUSED_0x4003

View File

@ -2,7 +2,8 @@
#ifndef GUARD_CONSTANTS_BATTLE_PARTNERS_H
#define GUARD_CONSTANTS_BATTLE_PARTNERS_H
#define PARTNER_NONE 0
#define PARTNER_STEVEN 1
#define PARTNER_NONE 0
#define PARTNER_STEVEN 1
#define PARTNER_COUNT 2
#endif // GUARD_CONSTANTS_BATTLE_PARTNERS_H

View File

@ -0,0 +1,15 @@
#ifndef GUARD_DIFFICULTY_CONSTANTS_H
#define GUARD_DIFFICULTY_CONSTANTS_H
enum DifficultyLevel
{
DIFFICULTY_EASY,
DIFFICULTY_NORMAL, //If you rename this, the word "Normal" in fprint_trainers must be replaced with the new difficulty name.
DIFFICULTY_HARD,
DIFFICULTY_COUNT,
};
#define DIFFICULTY_MIN 0
#define DIFFICULTY_MAX (DIFFICULTY_COUNT - 1)
#endif // GUARD_DIFFICULTY_CONSTANTS_H

View File

@ -3,6 +3,8 @@
#include "constants/moves.h"
#include "constants/trainers.h"
#include "constants/battle.h"
#include "difficulty.h"
#define MAX_TRAINER_ITEMS 4
@ -164,8 +166,8 @@ extern const union AnimCmd *const sAnims_Trainer[];
extern const struct TrainerSprite gTrainerSprites[];
extern const struct TrainerBacksprite gTrainerBacksprites[];
extern const struct Trainer gTrainers[];
extern const struct Trainer gBattlePartners[];
extern const struct Trainer gTrainers[DIFFICULTY_COUNT][TRAINERS_COUNT];
extern const struct Trainer gBattlePartners[DIFFICULTY_COUNT][PARTNER_COUNT];
extern const struct TrainerClass gTrainerClasses[TRAINER_CLASS_COUNT];
@ -191,71 +193,103 @@ static inline u16 SanitizeTrainerId(u16 trainerId)
static inline const struct Trainer *GetTrainerStructFromId(u16 trainerId)
{
return &gTrainers[SanitizeTrainerId(trainerId)];
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
return &gTrainers[difficulty][sanitizedTrainerId];
}
static inline const u8 GetTrainerClassFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].trainerClass;
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
return gTrainers[difficulty][sanitizedTrainerId].trainerClass;
}
static inline const u8 *GetTrainerClassNameFromId(u16 trainerId)
{
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId);
if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
return gTrainerClasses[gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass].name;
return gTrainerClasses[gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass].name;
return gTrainerClasses[GetTrainerClassFromId(trainerId)].name;
}
static inline const u8 *GetTrainerNameFromId(u16 trainerId)
{
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
enum DifficultyLevel partnerDifficulty = GetBattlePartnerDifficultyLevel(trainerId);
if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
return gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName;
return gTrainers[SanitizeTrainerId(trainerId)].trainerName;
return gBattlePartners[partnerDifficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName;
return gTrainers[difficulty][sanitizedTrainerId].trainerName;
}
static inline const u8 GetTrainerPicFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].trainerPic;
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
return gTrainers[difficulty][sanitizedTrainerId].trainerPic;
}
static inline const u8 GetTrainerStartingStatusFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].startingStatus;
return gTrainers[GetCurrentDifficultyLevel()][SanitizeTrainerId(trainerId)].startingStatus;
}
static inline const bool32 IsTrainerDoubleBattle(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].doubleBattle;
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
return gTrainers[difficulty][sanitizedTrainerId].doubleBattle;
}
static inline const u8 GetTrainerPartySizeFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].partySize;
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
return gTrainers[difficulty][sanitizedTrainerId].partySize;
}
static inline const bool32 DoesTrainerHaveMugshot(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].mugshotColor;
return gTrainers[GetCurrentDifficultyLevel()][SanitizeTrainerId(trainerId)].mugshotColor;
}
static inline const u8 GetTrainerMugshotColorFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].mugshotColor;
return gTrainers[GetCurrentDifficultyLevel()][SanitizeTrainerId(trainerId)].mugshotColor;
}
static inline const u16 *GetTrainerItemsFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].items;
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
return gTrainers[difficulty][sanitizedTrainerId].items;
}
static inline const struct TrainerMon *GetTrainerPartyFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].party;
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
return gTrainers[difficulty][sanitizedTrainerId].party;
}
static inline const bool32 GetTrainerAIFlagsFromId(u16 trainerId)
{
return gTrainers[SanitizeTrainerId(trainerId)].aiFlags;
u32 sanitizedTrainerId = SanitizeTrainerId(trainerId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
return gTrainers[difficulty][sanitizedTrainerId].aiFlags;
}
#endif // GUARD_DATA_H

17
include/difficulty.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef GUARD_DIFFICULTY_H
#define GUARD_DIFFICULTY_H
#include "constants/difficulty.h"
#include "script.h"
enum DifficultyLevel GetCurrentDifficultyLevel(void);
void SetCurrentDifficultyLevel(enum DifficultyLevel);
enum DifficultyLevel GetBattlePartnerDifficultyLevel(u16);
enum DifficultyLevel GetTrainerDifficultyLevel(u16);
void Script_IncreaseDifficulty(void);
void Script_DecreaseDifficulty(void);
void Script_GetDifficulty(void);
void Script_SetDifficulty(struct ScriptContext *);
#endif // GUARD_DIFFICULTY_H

View File

@ -297,9 +297,11 @@ static void PlayerPartnerHandleDrawTrainerPic(u32 battler)
s16 xPos, yPos;
u32 trainerPicId;
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId);
if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
{
trainerPicId = gBattlePartners[gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic;
trainerPicId = gBattlePartners[difficulty][gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic;
xPos = 90;
yPos = (8 - gTrainerBacksprites[trainerPicId].coordinates.size) * 4 + 80;
}
@ -427,9 +429,10 @@ static void PlayerPartnerHandleHealthBarUpdate(u32 battler)
static void PlayerPartnerHandleIntroTrainerBallThrow(u32 battler)
{
const u32 *trainerPal;
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(gPartnerTrainerId);
if (gPartnerTrainerId > TRAINER_PARTNER(PARTNER_NONE))
trainerPal = gTrainerBacksprites[gBattlePartners[gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic].palette.data;
trainerPal = gTrainerBacksprites[gBattlePartners[difficulty][gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerPic].palette.data;
else if (IsAiVsAiBattle())
trainerPal = gTrainerSprites[GetTrainerPicFromId(gPartnerTrainerId)].palette.data;
else

View File

@ -3560,8 +3560,10 @@ struct TrainerSlide
const u8 *msgDynamax;
};
static const struct TrainerSlide sTrainerSlides[] =
static const struct TrainerSlide sTrainerSlides[DIFFICULTY_COUNT][TRAINERS_COUNT] =
{
[DIFFICULTY_NORMAL] =
{
/* Put any trainer slide-in messages inside this array.
Example:
{
@ -3580,7 +3582,14 @@ static const struct TrainerSlide sTrainerSlides[] =
.msgBeforeFirstTurn = sText_GravityIntensified,
.msgDynamax = sText_TargetWokeUp,
},
},
[DIFFICULTY_EASY] =
{
},
[DIFFICULTY_HARD] =
{
*/
},
};
static u32 GetEnemyMonCount(u32 firstId, u32 lastId, bool32 onlyAlive)
@ -3657,118 +3666,120 @@ u32 ShouldDoTrainerSlide(u32 battler, u32 which)
trainerId = gTrainerBattleOpponent_A;
}
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(trainerId);
for (i = 0; i < ARRAY_COUNT(sTrainerSlides); i++)
{
if (trainerId == sTrainerSlides[i].trainerId
&& (((gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && sTrainerSlides[i].isFrontierTrainer)
|| (!(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && !sTrainerSlides[i].isFrontierTrainer)))
if (trainerId == sTrainerSlides[difficulty]->trainerId
&& (((gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && sTrainerSlides[difficulty]->isFrontierTrainer)
|| (!(gBattleTypeFlags & BATTLE_TYPE_FRONTIER) && !sTrainerSlides[difficulty]->isFrontierTrainer)))
{
gBattleScripting.battler = battler;
switch (which)
{
case TRAINER_SLIDE_LAST_SWITCHIN:
if (sTrainerSlides[i].msgLastSwitchIn != NULL && !CanBattlerSwitch(battler))
if (sTrainerSlides[difficulty]->msgLastSwitchIn != NULL && !CanBattlerSwitch(battler))
{
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgLastSwitchIn;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgLastSwitchIn;
return retValue;
}
break;
case TRAINER_SLIDE_LAST_LOW_HP:
if (sTrainerSlides[i].msgLastLowHp != NULL
if (sTrainerSlides[difficulty]->msgLastLowHp != NULL
&& GetEnemyMonCount(firstId, lastId, TRUE) == 1
&& BattlerHPPercentage(battler, LESS_THAN_OR_EQUAL, 4)
&& !gBattleStruct->trainerSlideLowHpMsgDone)
{
gBattleStruct->trainerSlideLowHpMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgLastLowHp;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgLastLowHp;
return retValue;
}
break;
case TRAINER_SLIDE_FIRST_DOWN:
if (sTrainerSlides[i].msgFirstDown != NULL && GetEnemyMonCount(firstId, lastId, TRUE) == GetEnemyMonCount(firstId, lastId, FALSE) - 1)
if (sTrainerSlides[difficulty]->msgFirstDown != NULL && GetEnemyMonCount(firstId, lastId, TRUE) == GetEnemyMonCount(firstId, lastId, FALSE) - 1)
{
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstDown;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgFirstDown;
return retValue;
}
break;
case TRAINER_SLIDE_LAST_HALF_HP:
if (sTrainerSlides[i].msgLastHalfHp != NULL
if (sTrainerSlides[difficulty]->msgLastHalfHp != NULL
&& GetEnemyMonCount(firstId, lastId, TRUE) == GetEnemyMonCount(firstId, lastId, FALSE) - 1
&& BattlerHPPercentage(battler, LESS_THAN_OR_EQUAL, 2) && BattlerHPPercentage(battler, GREATER_THAN, 4)
&& !gBattleStruct->trainerSlideHalfHpMsgDone)
{
gBattleStruct->trainerSlideHalfHpMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgLastHalfHp;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgLastHalfHp;
return TRUE;
}
break;
case TRAINER_SLIDE_FIRST_CRITICAL_HIT:
if (sTrainerSlides[i].msgFirstCriticalHit != NULL && gBattleStruct->trainerSlideFirstCriticalHitMsgState == 1)
if (sTrainerSlides[difficulty]->msgFirstCriticalHit != NULL && gBattleStruct->trainerSlideFirstCriticalHitMsgState == 1)
{
gBattleStruct->trainerSlideFirstCriticalHitMsgState = 2;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstCriticalHit;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgFirstCriticalHit;
return TRUE;
}
break;
case TRAINER_SLIDE_FIRST_SUPER_EFFECTIVE_HIT:
if (sTrainerSlides[i].msgFirstSuperEffectiveHit != NULL
if (sTrainerSlides[difficulty]->msgFirstSuperEffectiveHit != NULL
&& gBattleStruct->trainerSlideFirstSuperEffectiveHitMsgState == 1
&& gBattleMons[battler].hp)
{
gBattleStruct->trainerSlideFirstSuperEffectiveHitMsgState = 2;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstSuperEffectiveHit;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgFirstSuperEffectiveHit;
return TRUE;
}
break;
case TRAINER_SLIDE_FIRST_STAB_MOVE:
if (sTrainerSlides[i].msgFirstSTABMove != NULL
if (sTrainerSlides[difficulty]->msgFirstSTABMove != NULL
&& gBattleStruct->trainerSlideFirstSTABMoveMsgState == 1
&& GetEnemyMonCount(firstId, lastId, TRUE) == GetEnemyMonCount(firstId, lastId, FALSE))
{
gBattleStruct->trainerSlideFirstSTABMoveMsgState = 2;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstSTABMove;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgFirstSTABMove;
return TRUE;
}
break;
case TRAINER_SLIDE_PLAYER_MON_UNAFFECTED:
if (sTrainerSlides[i].msgPlayerMonUnaffected != NULL
if (sTrainerSlides[difficulty]->msgPlayerMonUnaffected != NULL
&& gBattleStruct->trainerSlidePlayerMonUnaffectedMsgState == 1
&& GetEnemyMonCount(firstId, lastId, TRUE) == GetEnemyMonCount(firstId, lastId, FALSE))
{
gBattleStruct->trainerSlidePlayerMonUnaffectedMsgState = 2;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgPlayerMonUnaffected;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgPlayerMonUnaffected;
return TRUE;
}
break;
case TRAINER_SLIDE_MEGA_EVOLUTION:
if (sTrainerSlides[i].msgMegaEvolution != NULL && !gBattleStruct->trainerSlideMegaEvolutionMsgDone)
if (sTrainerSlides[difficulty]->msgMegaEvolution != NULL && !gBattleStruct->trainerSlideMegaEvolutionMsgDone)
{
gBattleStruct->trainerSlideMegaEvolutionMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgMegaEvolution;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgMegaEvolution;
return TRUE;
}
break;
case TRAINER_SLIDE_Z_MOVE:
if (sTrainerSlides[i].msgZMove != NULL && !gBattleStruct->trainerSlideZMoveMsgDone)
if (sTrainerSlides[difficulty]->msgZMove != NULL && !gBattleStruct->trainerSlideZMoveMsgDone)
{
gBattleStruct->trainerSlideZMoveMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgZMove;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgZMove;
return TRUE;
}
break;
case TRAINER_SLIDE_BEFORE_FIRST_TURN:
if (sTrainerSlides[i].msgBeforeFirstTurn != NULL && !gBattleStruct->trainerSlideBeforeFirstTurnMsgDone)
if (sTrainerSlides[difficulty]->msgBeforeFirstTurn != NULL && !gBattleStruct->trainerSlideBeforeFirstTurnMsgDone)
{
gBattleStruct->trainerSlideBeforeFirstTurnMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgBeforeFirstTurn;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgBeforeFirstTurn;
return TRUE;
}
break;
case TRAINER_SLIDE_DYNAMAX:
if (sTrainerSlides[i].msgDynamax != NULL && !gBattleStruct->trainerSlideDynamaxMsgDone)
if (sTrainerSlides[difficulty]->msgDynamax != NULL && !gBattleStruct->trainerSlideDynamaxMsgDone)
{
gBattleStruct->trainerSlideDynamaxMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgDynamax;
gBattleStruct->trainerSlideMsg = sTrainerSlides[difficulty]->msgDynamax;
return TRUE;
}
break;

View File

@ -699,7 +699,7 @@ static const u8 *const *const sPartnerApprenticeTextTables[NUM_APPRENTICES] =
#include "data/battle_frontier/battle_tent.h"
#include "data/partner_parties.h"
const struct Trainer gBattlePartners[] =
const struct Trainer gBattlePartners[DIFFICULTY_COUNT][PARTNER_COUNT] =
{
#include "data/battle_partners.h"
};
@ -1355,6 +1355,7 @@ u8 GetFrontierTrainerFrontSpriteId(u16 trainerId)
u8 GetFrontierOpponentClass(u16 trainerId)
{
u8 trainerClass = 0;
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId);
SetFacilityPtrsGetLevel();
#if FREE_BATTLE_TOWER_E_READER == FALSE
@ -1371,7 +1372,7 @@ u8 GetFrontierOpponentClass(u16 trainerId)
}
else if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
{
trainerClass = gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass;
trainerClass = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerClass;
}
else if (trainerId < FRONTIER_TRAINERS_COUNT)
{
@ -1441,6 +1442,7 @@ static u8 GetFrontierTrainerFacilityClass(u16 trainerId)
void GetFrontierTrainerName(u8 *dst, u16 trainerId)
{
s32 i = 0;
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId);
SetFacilityPtrsGetLevel();
if (trainerId == TRAINER_EREADER)
@ -1457,8 +1459,8 @@ void GetFrontierTrainerName(u8 *dst, u16 trainerId)
}
else if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
{
for (i = 0; gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName[i] != EOS; i++)
dst[i] = gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName[i];
for (i = 0; gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName[i] != EOS; i++)
dst[i] = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName[i];
}
else if (trainerId < FRONTIER_TRAINERS_COUNT)
{
@ -2965,6 +2967,7 @@ static void FillPartnerParty(u16 trainerId)
u32 otID;
u8 trainerName[(PLAYER_NAME_LENGTH * 3) + 1];
s32 ball = -1;
enum DifficultyLevel difficulty = GetBattlePartnerDifficultyLevel(trainerId);
SetFacilityPtrsGetLevel();
if (trainerId > TRAINER_PARTNER(PARTNER_NONE))
@ -2972,10 +2975,10 @@ static void FillPartnerParty(u16 trainerId)
for (i = 0; i < 3; i++)
ZeroMonData(&gPlayerParty[i + 3]);
for (i = 0; i < 3 && i < gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].partySize; i++)
for (i = 0; i < 3 && i < gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].partySize; i++)
{
const struct TrainerMon *partyData = gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].party;
const u8 *partnerName = gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName;
const struct TrainerMon *partyData = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].party;
const u8 *partnerName = gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName;
for (k = 0; partnerName[k] != EOS && k < 3; k++)
{
@ -3046,9 +3049,9 @@ static void FillPartnerParty(u16 trainerId)
}
CalculateMonStats(&gPlayerParty[i + 3]);
StringCopy(trainerName, gBattlePartners[trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName);
StringCopy(trainerName, gBattlePartners[difficulty][trainerId - TRAINER_PARTNER(PARTNER_NONE)].trainerName);
SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_NAME, trainerName);
j = gBattlePartners[SanitizeTrainerId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].encounterMusic_gender >> 7;
j = gBattlePartners[difficulty][SanitizeTrainerId(trainerId - TRAINER_PARTNER(PARTNER_NONE))].encounterMusic_gender >> 7;
SetMonData(&gPlayerParty[i + 3], MON_DATA_OT_GENDER, &j);
}
}

View File

@ -227,7 +227,7 @@ const union AnimCmd *const sAnims_Trainer[] ={
#include "data/trainer_parties.h"
const struct Trainer gTrainers[] =
const struct Trainer gTrainers[DIFFICULTY_COUNT][TRAINERS_COUNT] =
{
#include "data/trainers.h"
};

View File

@ -9,7 +9,7 @@
#line 1 "src/data/battle_partners.party"
#line 1
[PARTNER_NONE] =
[DIFFICULTY_NORMAL][PARTNER_NONE] =
{
#line 3
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
@ -24,7 +24,7 @@
},
},
#line 8
[PARTNER_STEVEN] =
[DIFFICULTY_NORMAL][PARTNER_STEVEN] =
{
#line 9
.trainerName = _("STEVEN"),

File diff suppressed because it is too large Load Diff

95
src/difficulty.c Normal file
View File

@ -0,0 +1,95 @@
#include "global.h"
#include "data.h"
#include "event_data.h"
#include "script.h"
#include "constants/battle.h"
enum DifficultyLevel GetCurrentDifficultyLevel(void)
{
if (!B_VAR_DIFFICULTY)
return DIFFICULTY_NORMAL;
return VarGet(B_VAR_DIFFICULTY);
}
void SetCurrentDifficultyLevel(enum DifficultyLevel desiredDifficulty)
{
if (!B_VAR_DIFFICULTY)
return;
if (desiredDifficulty > DIFFICULTY_MAX)
desiredDifficulty = DIFFICULTY_MAX;
VarSet(B_VAR_DIFFICULTY, desiredDifficulty);
}
enum DifficultyLevel GetBattlePartnerDifficultyLevel(u16 partnerId)
{
enum DifficultyLevel difficulty = GetCurrentDifficultyLevel();
if (partnerId > TRAINER_PARTNER(PARTNER_NONE))
partnerId -= TRAINER_PARTNER(PARTNER_NONE);
if (difficulty == DIFFICULTY_NORMAL)
return DIFFICULTY_NORMAL;
if (gBattlePartners[difficulty][partnerId].party == NULL)
return DIFFICULTY_NORMAL;
return difficulty;
}
enum DifficultyLevel GetTrainerDifficultyLevel(u16 trainerId)
{
enum DifficultyLevel difficulty = GetCurrentDifficultyLevel();
if (difficulty == DIFFICULTY_NORMAL)
return DIFFICULTY_NORMAL;
if (gTrainers[difficulty][trainerId].party == NULL)
return DIFFICULTY_NORMAL;
return difficulty;
}
void Script_IncreaseDifficulty(void)
{
enum DifficultyLevel currentDifficulty;
if (!B_VAR_DIFFICULTY)
return;
currentDifficulty = GetCurrentDifficultyLevel();
if (currentDifficulty++ > DIFFICULTY_MAX)
return;
SetCurrentDifficultyLevel(currentDifficulty);
}
void Script_DecreaseDifficulty(void)
{
enum DifficultyLevel currentDifficulty;
if (!B_VAR_DIFFICULTY)
return;
currentDifficulty = GetCurrentDifficultyLevel();
if (!currentDifficulty)
return;
SetCurrentDifficultyLevel(--currentDifficulty);
}
void Script_GetDifficulty(void)
{
gSpecialVar_Result = GetCurrentDifficultyLevel();
}
void Script_SetDifficulty(struct ScriptContext *ctx)
{
enum DifficultyLevel desiredDifficulty = ScriptReadByte(ctx);
SetCurrentDifficultyLevel(desiredDifficulty);
}

View File

@ -46,6 +46,7 @@
#include "union_room_chat.h"
#include "constants/map_groups.h"
#include "constants/items.h"
#include "difficulty.h"
extern const u8 EventScript_ResetAllMapFlags[];
@ -207,6 +208,7 @@ void NewGameInitData(void)
WipeTrainerNameRecords();
ResetTrainerHillResults();
ResetContestLinkResults();
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
ResetItemFlags();
ResetDexNav();
}

View File

@ -5143,12 +5143,15 @@ s32 GetBattlerMultiplayerId(u16 id)
u8 GetTrainerEncounterMusicId(u16 trainerOpponentId)
{
u32 sanitizedTrainerId = SanitizeTrainerId(trainerOpponentId);
enum DifficultyLevel difficulty = GetTrainerDifficultyLevel(sanitizedTrainerId);
if (InBattlePyramid())
return GetTrainerEncounterMusicIdInBattlePyramid(trainerOpponentId);
else if (InTrainerHillChallenge())
return GetTrainerEncounterMusicIdInTrainerHill(trainerOpponentId);
else
return gTrainers[SanitizeTrainerId(trainerOpponentId)].encounterMusic_gender & (F_TRAINER_FEMALE - 1);
return gTrainers[difficulty][sanitizedTrainerId].encounterMusic_gender & (F_TRAINER_FEMALE - 1);
}
u16 ModifyStatByNature(u8 nature, u16 stat, u8 statIndex)
@ -6088,7 +6091,7 @@ const u8 *GetTrainerPartnerName(void)
{
if (gPartnerTrainerId == TRAINER_PARTNER(PARTNER_STEVEN))
{
return GetTrainerNameFromId(TRAINER_STEVEN);
return GetTrainerNameFromId(PARTNER_STEVEN);
}
else
{

View File

@ -11,16 +11,32 @@
#include "constants/trainers.h"
#include "constants/battle.h"
static const struct Trainer sTestTrainers[] =
#define NUM_TEST_TRAINERS 3
static const struct Trainer sTestTrainers[DIFFICULTY_COUNT][NUM_TEST_TRAINERS] =
{
#include "trainer_control.h"
};
enum DifficultyLevel GetTrainerDifficultyLevelTest(u16 trainerId)
{
enum DifficultyLevel difficulty = GetCurrentDifficultyLevel();
if (difficulty == DIFFICULTY_NORMAL)
return DIFFICULTY_NORMAL;
if (sTestTrainers[difficulty][trainerId].party == NULL)
return DIFFICULTY_NORMAL;
return difficulty;
}
TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon")
{
u32 currTrainer = 0;
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
u8 nickBuffer[20];
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[0], TRUE, BATTLE_TYPE_TRAINER);
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[GetTrainerDifficultyLevelTest(currTrainer)][currTrainer], TRUE, BATTLE_TYPE_TRAINER);
EXPECT(IsMonShiny(&testParty[0]));
EXPECT(!IsMonShiny(&testParty[1]));
@ -94,8 +110,9 @@ TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon")
TEST("CreateNPCTrainerPartyForTrainer generates different personalities for different mons")
{
enum DifficultyLevel difficulty = GetTrainerDifficultyLevelTest(0);
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[0], TRUE, BATTLE_TYPE_TRAINER);
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[difficulty][0], TRUE, BATTLE_TYPE_TRAINER);
EXPECT(testParty[0].box.personality != testParty[1].box.personality);
Free(testParty);
}
@ -160,3 +177,46 @@ TEST("Trainer Class Balls apply to the entire party")
}
Free(testParty);
}
TEST("Difficulty default to Normal is the trainer doesn't have a member for the current diffuculty")
{
SetCurrentDifficultyLevel(DIFFICULTY_EASY);
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
u32 currTrainer = 1;
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[GetTrainerDifficultyLevelTest(currTrainer)][currTrainer], TRUE, BATTLE_TYPE_TRAINER);
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO);
Free(testParty);
}
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (EASY)")
{
SetCurrentDifficultyLevel(DIFFICULTY_EASY);
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
u32 currTrainer = 2;
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[GetTrainerDifficultyLevelTest(currTrainer)][currTrainer], TRUE, BATTLE_TYPE_TRAINER);
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_METAPOD);
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 1);
Free(testParty);
}
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (HARD)")
{
SetCurrentDifficultyLevel(DIFFICULTY_HARD);
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
u32 currTrainer = 2;
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[GetTrainerDifficultyLevelTest(currTrainer)][currTrainer], TRUE, BATTLE_TYPE_TRAINER);
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_ARCEUS);
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 99);
Free(testParty);
}
TEST("Difficulty changes which party if used for NPCs if defined for the difficulty (NORMAL)")
{
SetCurrentDifficultyLevel(DIFFICULTY_NORMAL);
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
u32 currTrainer = 2;
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainers[GetTrainerDifficultyLevelTest(currTrainer)][currTrainer], TRUE, BATTLE_TYPE_TRAINER);
EXPECT(GetMonData(&testParty[0], MON_DATA_SPECIES) == SPECIES_MEWTWO);
EXPECT(GetMonData(&testParty[0], MON_DATA_LEVEL) == 50);
Free(testParty);
}

View File

@ -9,7 +9,7 @@
#line 1 "test/battle/trainer_control.party"
#line 1
[0] =
[DIFFICULTY_NORMAL][0] =
{
#line 2
.trainerName = _("Test1"),
@ -87,3 +87,158 @@
},
},
},
#line 33
#line 40
[DIFFICULTY_NORMAL][1] =
{
#line 34
.trainerName = _("Test2"),
#line 35
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
#line 36
.trainerPic = TRAINER_PIC_RED,
.encounterMusic_gender =
#line 38
TRAINER_ENCOUNTER_MUSIC_MALE,
#line 39
.doubleBattle = FALSE,
.partySize = 1,
.party = (const struct TrainerMon[])
{
{
#line 42
.species = SPECIES_MEWTWO,
.gender = TRAINER_MON_RANDOM_GENDER,
#line 44
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
#line 43
.lvl = 5,
.nature = NATURE_HARDY,
.dynamaxLevel = MAX_DYNAMAX_LEVEL,
},
},
},
#line 45
#line 52
[DIFFICULTY_HARD][1] =
{
#line 46
.trainerName = _("Test2"),
#line 47
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
#line 48
.trainerPic = TRAINER_PIC_RED,
.encounterMusic_gender =
#line 50
TRAINER_ENCOUNTER_MUSIC_MALE,
#line 51
.doubleBattle = FALSE,
.partySize = 1,
.party = (const struct TrainerMon[])
{
{
#line 54
.species = SPECIES_YVELTAL,
.gender = TRAINER_MON_RANDOM_GENDER,
#line 56
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
#line 55
.lvl = 99,
.nature = NATURE_HARDY,
.dynamaxLevel = MAX_DYNAMAX_LEVEL,
},
},
},
#line 57
#line 64
[DIFFICULTY_NORMAL][2] =
{
#line 58
.trainerName = _("Test2"),
#line 59
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
#line 60
.trainerPic = TRAINER_PIC_RED,
.encounterMusic_gender =
#line 62
TRAINER_ENCOUNTER_MUSIC_MALE,
#line 63
.doubleBattle = FALSE,
.partySize = 1,
.party = (const struct TrainerMon[])
{
{
#line 66
.species = SPECIES_MEWTWO,
.gender = TRAINER_MON_RANDOM_GENDER,
#line 68
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
#line 67
.lvl = 50,
.nature = NATURE_HARDY,
.dynamaxLevel = MAX_DYNAMAX_LEVEL,
},
},
},
#line 69
#line 76
[DIFFICULTY_EASY][2] =
{
#line 70
.trainerName = _("Test2"),
#line 71
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
#line 72
.trainerPic = TRAINER_PIC_RED,
.encounterMusic_gender =
#line 74
TRAINER_ENCOUNTER_MUSIC_MALE,
#line 75
.doubleBattle = FALSE,
.partySize = 1,
.party = (const struct TrainerMon[])
{
{
#line 78
.species = SPECIES_METAPOD,
.gender = TRAINER_MON_RANDOM_GENDER,
#line 80
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
#line 79
.lvl = 1,
.nature = NATURE_HARDY,
.dynamaxLevel = MAX_DYNAMAX_LEVEL,
},
},
},
#line 81
#line 88
[DIFFICULTY_HARD][2] =
{
#line 82
.trainerName = _("Test2"),
#line 83
.trainerClass = TRAINER_CLASS_PKMN_TRAINER_1,
#line 84
.trainerPic = TRAINER_PIC_RED,
.encounterMusic_gender =
#line 86
TRAINER_ENCOUNTER_MUSIC_MALE,
#line 87
.doubleBattle = FALSE,
.partySize = 1,
.party = (const struct TrainerMon[])
{
{
#line 90
.species = SPECIES_ARCEUS,
.gender = TRAINER_MON_RANDOM_GENDER,
#line 92
.iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31),
#line 91
.lvl = 99,
.nature = NATURE_HARDY,
.dynamaxLevel = MAX_DYNAMAX_LEVEL,
},
},
},

View File

@ -29,3 +29,63 @@ IVs: 0 HP / 0 Atk / 0 Def / 0 SpA / 0 SpD / 0 Spe
Wynaut
Level: 5
IVs: 0 HP / 0 Atk / 0 Def / 0 SpA / 0 SpD / 0 Spe
=== 1 ===
Name: Test2
Class: Pkmn Trainer 1
Pic: Red
Gender: Male
Music: Male
Double Battle: No
Difficulty: Normal
Mewtwo
Level: 5
=== 1 ===
Name: Test2
Class: Pkmn Trainer 1
Pic: Red
Gender: Male
Music: Male
Double Battle: No
Difficulty: HARD
Yveltal
Level: 99
=== 2 ===
Name: Test2
Class: Pkmn Trainer 1
Pic: Red
Gender: Male
Music: Male
Double Battle: No
Difficulty: Normal
Mewtwo
Level: 50
=== 2 ===
Name: Test2
Class: Pkmn Trainer 1
Pic: Red
Gender: Male
Music: Male
Double Battle: No
Difficulty: Easy
Metapod
Level: 1
=== 2 ===
Name: Test2
Class: Pkmn Trainer 1
Pic: Red
Gender: Male
Music: Male
Double Battle: No
Difficulty: Hard
Arceus
Level: 99

View File

@ -123,6 +123,9 @@ struct Trainer
struct String starting_status;
int starting_status_line;
struct String difficulty;
int difficulty_line;
};
static bool is_empty_string(struct String s)
@ -1195,9 +1198,16 @@ static bool parse_trainer(struct Parser *p, const struct Parsed *parsed, struct
trainer->starting_status_line = value.location.line;
trainer->starting_status = token_string(&value);
}
else if (is_literal_token(&key, "Difficulty"))
{
if (trainer->difficulty_line)
any_error = !set_show_parse_error(p, key.location, "duplicate 'Difficulty'");
trainer->difficulty_line = value.location.line;
trainer->difficulty = token_string(&value);
}
else
{
any_error = !set_show_parse_error(p, key.location, "expected one of 'Name', 'Class', 'Pic', 'Gender', 'Music', 'Items', 'Double Battle', or 'AI'");
any_error = !set_show_parse_error(p, key.location, "expected one of 'Name', 'Class', 'Pic', 'Gender', 'Music', 'Items', 'Double Battle', 'Difficulty', or 'AI'");
}
}
if (!trainer->pic_line)
@ -1618,7 +1628,14 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par
{
struct Trainer *trainer = &parsed->trainers[i];
fprintf(f, "#line %d\n", trainer->id_line);
fprintf(f, " [");
if (is_empty_string(trainer->difficulty))
trainer->difficulty = literal_string("Normal");
else
fprintf(f, "#line %d\n", trainer->difficulty_line);
fprint_constant(f, " [DIFFICULTY",trainer->difficulty);
fprintf(f, "]");
fprintf(f, "[");
fprint_string(f, trainer->id);
fprintf(f, "] =\n");
fprintf(f, " {\n");
@ -1741,17 +1758,17 @@ static void fprint_trainers(const char *output_path, FILE *f, struct Parsed *par
switch (pokemon->gender)
{
case GENDER_ANY:
fprintf(f, " .gender = TRAINER_MON_RANDOM_GENDER,\n");
break;
case GENDER_MALE:
fprintf(f, "#line %d\n", pokemon->header_line);
fprintf(f, " .gender = TRAINER_MON_MALE,\n");
break;
case GENDER_FEMALE:
fprintf(f, "#line %d\n", pokemon->header_line);
fprintf(f, " .gender = TRAINER_MON_FEMALE,\n");
break;
case GENDER_ANY:
fprintf(f, " .gender = TRAINER_MON_RANDOM_GENDER,\n");
break;
case GENDER_MALE:
fprintf(f, "#line %d\n", pokemon->header_line);
fprintf(f, " .gender = TRAINER_MON_MALE,\n");
break;
case GENDER_FEMALE:
fprintf(f, "#line %d\n", pokemon->header_line);
fprintf(f, " .gender = TRAINER_MON_FEMALE,\n");
break;
}
if (!is_empty_string(pokemon->item))