Merge pull request #2173 from LOuroboros/yourAffection

Implement affection-now-friendship mechanics
This commit is contained in:
Eduardo Quezada D'Ottone 2022-08-26 14:54:32 -04:00 committed by GitHub
commit bee36edd9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 207 additions and 28 deletions

View File

@ -869,6 +869,7 @@ gBattleAnims_General::
.4byte General_BeakBlastSetUp @ B_ANIM_BEAK_BLAST_SETUP
.4byte General_ShellTrapSetUp @ B_ANIM_SHELL_TRAP_SETUP
.4byte General_ZMoveActivate @ B_ANIM_ZMOVE_ACTIVATE
.4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON
.align 2
gBattleAnims_Special::
@ -24884,6 +24885,27 @@ PrimalReversionParticles:
delay 3
return
General_AffectionHangedOn::
loadspritegfx ANIM_TAG_RED_HEART
loopsewithpan SE_M_CHARM, SOUND_PAN_ATTACKER, 12, 3
createvisualtask AnimTask_SwayMon, 5, 0, 12, 4096, 4, ANIM_ATTACKER
delay 15
launchtask AnimTask_AffectionHangedOn 0x5 0x0
jumpargeq 0x0, FRIENDSHIP_100_TO_149, General_AffectionHangedOn_3Hearts
jumpargeq 0x0, FRIENDSHIP_150_TO_199, General_AffectionHangedOn_4Hearts
jumpargeq 0x0, FRIENDSHIP_200_TO_254, General_AffectionHangedOn_5Hearts
createsprite gRedHeartBurstSpriteTemplate, ANIM_ATTACKER, 3, -384, -31
General_AffectionHangedOn_5Hearts:
createsprite gRedHeartBurstSpriteTemplate, ANIM_ATTACKER, 3, -128, -22
General_AffectionHangedOn_4Hearts:
createsprite gRedHeartBurstSpriteTemplate, ANIM_ATTACKER, 3, 416, -38
General_AffectionHangedOn_3Hearts:
createsprite gRedHeartBurstSpriteTemplate, ANIM_ATTACKER, 3, 160, -32
createsprite gRedHeartBurstSpriteTemplate, ANIM_ATTACKER, 3, -256, -40
createsprite gRedHeartBurstSpriteTemplate, ANIM_ATTACKER, 3, 128, -16
waitforvisualfinish
end
SnatchMoveTrySwapFromSubstitute:
createvisualtask AnimTask_IsAttackerBehindSubstitute, 2
jumprettrue SnatchMoveSwapSubstituteForMon

View File

@ -415,6 +415,41 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectExtremeEvoboost @ EFFECT_EXTREME_EVOBOOST
.4byte BattleScript_EffectTerrainHit @ EFFECT_DAMAGE_SET_TERRAIN
BattleScript_AffectionBasedEndurance::
playanimation BS_TARGET, B_ANIM_AFFECTION_HANGED_ON
printstring STRINGID_TARGETTOUGHEDITOUT
waitmessage B_WAIT_TIME_LONG
return
BattleScript_AffectionBasedStatusHeal::
jumpifstatus BS_ATTACKER, STATUS1_POISON | STATUS1_TOXIC_POISON, BattleScript_AffectionBasedStatus_HealPoisonString
jumpifstatus BS_ATTACKER, STATUS1_SLEEP, BattleScript_AffectionBasedStatus_HealSleepString
jumpifstatus BS_ATTACKER, STATUS1_PARALYSIS, BattleScript_AffectionBasedStatus_HealParalysisString
jumpifstatus BS_ATTACKER, STATUS1_BURN, BattleScript_AffectionBasedStatus_HealBurnString
jumpifstatus BS_ATTACKER, STATUS1_FREEZE, BattleScript_AffectionBasedStatus_HealFreezeString
end2
BattleScript_AffectionBasedStatus_HealPoisonString:
printstring STRINGID_ATTACKEREXPELLEDTHEPOISON
goto BattleScript_AffectionBasedStatusHeal_Continue
BattleScript_AffectionBasedStatus_HealSleepString:
printstring STRINGID_ATTACKERSHOOKITSELFAWAKE
goto BattleScript_AffectionBasedStatusHeal_Continue
BattleScript_AffectionBasedStatus_HealParalysisString:
printstring STRINGID_ATTACKERBROKETHROUGHPARALYSIS
goto BattleScript_AffectionBasedStatusHeal_Continue
BattleScript_AffectionBasedStatus_HealBurnString:
printstring STRINGID_ATTACKERHEALEDITSBURN
goto BattleScript_AffectionBasedStatusHeal_Continue
BattleScript_AffectionBasedStatus_HealFreezeString:
printstring STRINGID_ATTACKERMELTEDTHEICE
BattleScript_AffectionBasedStatusHeal_Continue:
waitmessage B_WAIT_TIME_LONG
clearstatus BS_ATTACKER
waitstate
updatestatusicon BS_ATTACKER
waitstate
end2
BattleScript_EffectSteelBeam::
attackcanceler
attackstring

View File

@ -178,6 +178,7 @@ struct SpecialStatus
u8 dancerOriginalTarget:3;
u8 announceNeutralizingGas:1; // See Cmd_switchineffects
u8 neutralizingGasRemoved:1; // See VARIOUS_TRY_END_NEUTRALIZING_GAS
u8 affectionEndured:1;
s32 dmg;
s32 physicalDmg;
s32 specialDmg;

View File

@ -426,6 +426,8 @@ extern const u8 BattleScript_MagicianActivates[];
extern const u8 BattleScript_BeakBlastSetUp[];
extern const u8 BattleScript_BeakBlastBurn[];
extern const u8 BattleScript_DefDownSpeedUp[];
extern const u8 BattleScript_AffectionBasedStatusHeal[];
extern const u8 BattleScript_AffectionBasedEndurance[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View File

@ -201,5 +201,6 @@ bool32 CanBeParalyzed(u8 battlerId);
bool32 CanBeFrozen(u8 battlerId);
bool32 CanBeConfused(u8 battlerId);
bool32 IsBattlerTerrainAffected(u8 battlerId, u32 terrainFlag);
u32 GetMonFriendshipScore(struct Pokemon *pokemon);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -259,6 +259,7 @@
#define MOVE_RESULT_FOE_ENDURED (1 << 6)
#define MOVE_RESULT_FOE_HUNG_ON (1 << 7)
#define MOVE_RESULT_STURDIED (1 << 8)
#define MOVE_RESULT_FOE_ENDURED_AFFECTION (1 << 9)
#define MOVE_RESULT_NO_EFFECT (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE | MOVE_RESULT_FAILED)
// Battle Weather flags

View File

@ -536,6 +536,7 @@
#define B_ANIM_BEAK_BLAST_SETUP 33
#define B_ANIM_SHELL_TRAP_SETUP 34
#define B_ANIM_ZMOVE_ACTIVATE 35 // Using Z Moves
#define B_ANIM_AFFECTION_HANGED_ON 36
// special animations table (gBattleAnims_Special)
#define B_ANIM_LVL_UP 0

View File

@ -175,6 +175,7 @@
#define B_MULTI_BATTLE_WHITEOUT GEN_8 // In Gen4+, multi battles end when the Player and also their Partner don't have any more Pokémon to fight.
#define B_EVOLUTION_AFTER_WHITEOUT GEN_6 // In Gen6+, Pokemon that qualify for evolution after battle will evolve even if the player loses.
#define B_WILD_NATURAL_ENEMIES TRUE // If set to TRUE, certain wild mon species will attack other species when partnered in double wild battles (eg. Zangoose vs Seviper)
#define B_AFFECTION_MECHANICS FALSE // In Gen6+, there's a stat called affection that can trigger different effects in battle. From LGPE onwards, those effects use friendship instead.
// Animation Settings
#define B_NEW_SWORD_PARTICLE FALSE // If set to TRUE, it updates Swords Dance's particle.

View File

@ -624,8 +624,14 @@
#define STRINGID_ZMOVESTATUP 622
#define STRINGID_ZMOVEHPTRAP 623
#define STRINGID_TERRAINREMOVED 624
#define STRINGID_ATTACKEREXPELLEDTHEPOISON 625
#define STRINGID_ATTACKERSHOOKITSELFAWAKE 626
#define STRINGID_ATTACKERBROKETHROUGHPARALYSIS 627
#define STRINGID_ATTACKERHEALEDITSBURN 628
#define STRINGID_ATTACKERMELTEDTHEICE 629
#define STRINGID_TARGETTOUGHEDITOUT 630
#define BATTLESTRINGS_COUNT 625
#define BATTLESTRINGS_COUNT 631
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View File

@ -182,6 +182,15 @@
#define FRIENDSHIP_EVENT_FAINT_FIELD_PSN 7
#define FRIENDSHIP_EVENT_FAINT_LARGE 8 // If opponent was >= 30 levels higher. See AdjustFriendshipOnBattleFaint
// Constants for GetLeadMonFriendshipScore
#define FRIENDSHIP_NONE 0
#define FRIENDSHIP_1_TO_49 1
#define FRIENDSHIP_50_TO_99 2
#define FRIENDSHIP_100_TO_149 3
#define FRIENDSHIP_150_TO_199 4
#define FRIENDSHIP_200_TO_254 5
#define FRIENDSHIP_MAX 6
#define MAX_FRIENDSHIP 255
#define MAX_SHEEN 255
#define MAX_CONDITION 255

View File

@ -18,6 +18,7 @@
#include "constants/moves.h"
#include "constants/hold_effects.h"
#include "constants/items.h"
#include "constants/pokemon.h"
// function declarations
static void SpriteCB_SpriteToCentreOfSide(struct Sprite *sprite);
@ -7892,3 +7893,12 @@ void AnimTask_TerrainPulse(u8 taskId)
}
DestroyAnimVisualTask(taskId);
}
void AnimTask_AffectionHangedOn(u8 taskId)
{
int side = GetBattlerSide(gBattleAnimTarget);
struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty;
gBattleAnimArgs[0] = GetMonFriendshipScore(&party[gBattlerPartyIndexes[gBattleAnimTarget]]);
DestroyAnimVisualTask(taskId);
}

View File

@ -753,10 +753,22 @@ static const u8 sText_TargetTooHeavy[] = _("But the target\nwas too heavy!");
static const u8 sText_MeteorBeamCharging[] = _("{B_ATK_NAME_WITH_PREFIX} is overflowing\nwith space energy!");
static const u8 sText_HeatingUpBeak[] = _("{B_ATK_NAME_WITH_PREFIX} started\nheating up its beak!");
static const u8 sText_CourtChange[] = _("{B_ATK_NAME_WITH_PREFIX} swapped the battle\neffects affecting each side!");
static const u8 sText_AttackerExpelledThePoison[] = _("{B_ATK_NAME_WITH_PREFIX} managed to\nexpel the poison!");
static const u8 sText_AttackerShookItselfAwake[] = _("{B_ATK_NAME_WITH_PREFIX} shook itself awake!");
static const u8 sText_AttackerBrokeThroughParalysis[] = _("{B_ATK_NAME_WITH_PREFIX} gathered all its energy\nto overcome its paralysis!");
static const u8 sText_AttackerHealedItsBurn[] = _("{B_ATK_NAME_WITH_PREFIX} healed its burn with\nits sheer determination!");
static const u8 sText_AttackerMeltedTheIce[] = _("{B_ATK_NAME_WITH_PREFIX} melted the ice with\nits fiery determination!");
static const u8 sText_TargetToughedItOut[] = _("{B_DEF_NAME_WITH_PREFIX} toughed it out\nto show you its best side!");
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
[STRINGID_TARGETTOUGHEDITOUT - BATTLESTRINGS_TABLE_START] = sText_TargetToughedItOut,
[STRINGID_ATTACKERMELTEDTHEICE - BATTLESTRINGS_TABLE_START] = sText_AttackerMeltedTheIce,
[STRINGID_ATTACKERHEALEDITSBURN - BATTLESTRINGS_TABLE_START] = sText_AttackerHealedItsBurn,
[STRINGID_ATTACKERBROKETHROUGHPARALYSIS - BATTLESTRINGS_TABLE_START] = sText_AttackerBrokeThroughParalysis,
[STRINGID_ATTACKERSHOOKITSELFAWAKE - BATTLESTRINGS_TABLE_START] = sText_AttackerShookItselfAwake,
[STRINGID_ATTACKEREXPELLEDTHEPOISON - BATTLESTRINGS_TABLE_START] = sText_AttackerExpelledThePoison,
[STRINGID_ZPOWERSURROUNDS - BATTLESTRINGS_TABLE_START] = sText_ZPowerSurrounds,
[STRINGID_ZMOVEUNLEASHED - BATTLESTRINGS_TABLE_START] = sText_ZPowerUnleashed,
[STRINGID_ZMOVERESETSSTATS - BATTLESTRINGS_TABLE_START] = sText_ZMoveResetsStats,

View File

@ -59,6 +59,7 @@
#include "constants/songs.h"
#include "constants/trainers.h"
#include "battle_util.h"
#include "constants/pokemon.h"
extern struct Evolution gEvolutionTable[][EVOS_PER_MON];
@ -1730,6 +1731,13 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
if (gFieldStatuses & STATUS_FIELD_GRAVITY)
calc = (calc * 5) / 3; // 1.66 Gravity acc boost
#if B_AFFECTION_MECHANICS == TRUE
// With high affection/friendship there's a chance to evade a move by substracting 10% of its accuracy.
// I can't find exact information about that chance, so I'm just gonna write it as a 20% chance for now.
if (GetMonFriendshipScore(&gPlayerParty[gBattlerPartyIndexes[battlerDef]]) >= FRIENDSHIP_150_TO_199 && (Random() % 100) <= 20)
calc = (calc * 90) / 100;
#endif
return calc;
}
@ -1898,6 +1906,9 @@ s32 CalcCritChanceStage(u8 battlerAtk, u8 battlerDef, u32 move, bool32 recordAbi
+ (holdEffectAtk == HOLD_EFFECT_SCOPE_LENS)
+ 2 * (holdEffectAtk == HOLD_EFFECT_LUCKY_PUNCH && gBattleMons[gBattlerAttacker].species == SPECIES_CHANSEY)
+ 2 * BENEFITS_FROM_LEEK(battlerAtk, holdEffectAtk)
#if B_AFFECTION_MECHANICS == TRUE
+ 2 * (GetMonFriendshipScore(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]) >= FRIENDSHIP_200_TO_254)
#endif
+ (abilityAtk == ABILITY_SUPER_LUCK);
if (critChance >= ARRAY_COUNT(sCriticalHitChance))
@ -1966,6 +1977,8 @@ static void Cmd_adjustdamage(void)
{
u8 holdEffect, param;
u32 moveType;
u32 friendshipScore = GetMonFriendshipScore(&gPlayerParty[gBattlerPartyIndexes[gBattlerTarget]]);
u32 rand = Random() % 100;
GET_MOVE_TYPE(gCurrentMove, moveType);
@ -1981,7 +1994,7 @@ static void Cmd_adjustdamage(void)
gPotentialItemEffectBattler = gBattlerTarget;
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && (Random() % 100) < param)
if (holdEffect == HOLD_EFFECT_FOCUS_BAND && rand < param)
{
RecordItemEffectBattle(gBattlerTarget, holdEffect);
gSpecialStatuses[gBattlerTarget].focusBanded = TRUE;
@ -1998,11 +2011,24 @@ static void Cmd_adjustdamage(void)
RecordItemEffectBattle(gBattlerTarget, holdEffect);
gSpecialStatuses[gBattlerTarget].focusSashed = TRUE;
}
#if B_AFFECTION_MECHANICS == TRUE
else if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER && friendshipScore >= FRIENDSHIP_100_TO_149)
{
if ((friendshipScore == FRIENDSHIP_MAX && rand < 25)
|| (friendshipScore == FRIENDSHIP_200_TO_254 && rand < 20)
|| (friendshipScore == FRIENDSHIP_150_TO_199 && rand < 15)
|| (friendshipScore == FRIENDSHIP_100_TO_149 && rand < 10))
gSpecialStatuses[gBattlerTarget].affectionEndured = TRUE;
}
#endif
if (gBattleMoves[gCurrentMove].effect != EFFECT_FALSE_SWIPE
&& !gProtectStructs[gBattlerTarget].endured
&& !gSpecialStatuses[gBattlerTarget].focusBanded
&& !gSpecialStatuses[gBattlerTarget].focusSashed
#if B_AFFECTION_MECHANICS == TRUE
&& !gSpecialStatuses[gBattlerTarget].affectionEndured
#endif
&& !gSpecialStatuses[gBattlerTarget].sturdied)
goto END;
@ -2023,6 +2049,12 @@ static void Cmd_adjustdamage(void)
gMoveResultFlags |= MOVE_RESULT_STURDIED;
gLastUsedAbility = ABILITY_STURDY;
}
#if B_AFFECTION_MECHANICS == TRUE
else if (gSpecialStatuses[gBattlerTarget].affectionEndured)
{
gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED_AFFECTION;
}
#endif
END:
gBattlescriptCurrInstr++;
@ -2478,6 +2510,16 @@ static void Cmd_resultmessage(void)
{
stringId = STRINGID_BUTITFAILED;
}
#if B_AFFECTION_MECHANICS == TRUE
else if (gMoveResultFlags & MOVE_RESULT_FOE_ENDURED_AFFECTION)
{
gSpecialStatuses[gBattlerTarget].affectionEndured = FALSE;
gMoveResultFlags &= ~MOVE_RESULT_FOE_ENDURED_AFFECTION;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_AffectionBasedEndurance;
return;
}
#endif
else
{
gBattleCommunication[MSG_DISPLAY] = 0;
@ -4020,6 +4062,10 @@ static void Cmd_getexp(void)
gBattleMoveDamage = value + 1;
}
#endif
#if B_AFFECTION_MECHANICS == TRUE
if (GetMonFriendshipScore(&gPlayerParty[gBattleStruct->expGetterMonId]) >= FRIENDSHIP_50_TO_99)
gBattleMoveDamage = (gBattleMoveDamage * 120) / 100;
#endif
if (IsTradedMon(&gPlayerParty[gBattleStruct->expGetterMonId]))
{
@ -11009,6 +11055,13 @@ static void Cmd_tryKO(void)
gMoveResultFlags |= MOVE_RESULT_FOE_HUNG_ON;
gLastUsedItem = gBattleMons[gBattlerTarget].item;
}
#if B_AFFECTION_MECHANICS == TRUE
else if (gSpecialStatuses[gBattlerTarget].affectionEndured)
{
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp - 1;
gMoveResultFlags |= MOVE_RESULT_FOE_ENDURED_AFFECTION;
}
#endif
else
{
gBattleMoveDamage = gBattleMons[gBattlerTarget].hp;

View File

@ -45,6 +45,7 @@
#include "constants/species.h"
#include "constants/trainers.h"
#include "constants/weather.h"
#include "constants/pokemon.h"
extern struct Evolution gEvolutionTable[][EVOS_PER_MON];
@ -2103,6 +2104,26 @@ void TryToRevertMimicry(void)
}
}
u32 GetMonFriendshipScore(struct Pokemon *pokemon)
{
u32 friendshipScore = GetMonData(pokemon, MON_DATA_FRIENDSHIP);
if (friendshipScore == MAX_FRIENDSHIP)
return FRIENDSHIP_MAX;
if (friendshipScore >= 200)
return FRIENDSHIP_200_TO_254;
if (friendshipScore >= 150)
return FRIENDSHIP_150_TO_199;
if (friendshipScore >= 100)
return FRIENDSHIP_100_TO_149;
if (friendshipScore >= 50)
return FRIENDSHIP_50_TO_99;
if (friendshipScore >= 1)
return FRIENDSHIP_1_TO_49;
return FRIENDSHIP_NONE;
}
enum
{
ENDTURN_ORDER,
@ -2131,6 +2152,7 @@ enum
ENDTURN_ION_DELUGE,
ENDTURN_FAIRY_LOCK,
ENDTURN_RETALIATE,
ENDTURN_STATUS_HEAL,
ENDTURN_FIELD_COUNT,
};
@ -2578,6 +2600,22 @@ u8 DoFieldEndTurnEffects(void)
gSideTimers[B_SIDE_OPPONENT].retaliateTimer--;
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_STATUS_HEAL:
for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++)
{
#if B_AFFECTION_MECHANICS == TRUE
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER
&& GetMonFriendshipScore(&gPlayerParty[gBattlerPartyIndexes[gBattlerAttacker]]) >= FRIENDSHIP_150_TO_199
&& (Random() % 100 < 20))
{
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
BattleScriptExecute(BattleScript_AffectionBasedStatusHeal);
break;
}
#endif
}
gBattleStruct->turnCountersTracker++;
break;
case ENDTURN_FIELD_COUNT:
effect++;
break;

View File

@ -65,6 +65,7 @@
#include "constants/weather.h"
#include "constants/metatile_labels.h"
#include "palette.h"
#include "battle_util.h"
EWRAM_DATA bool8 gBikeCyclingChallenge = FALSE;
EWRAM_DATA u8 gBikeCollisions = 0;
@ -939,21 +940,7 @@ u16 GetWeekCount(void)
u8 GetLeadMonFriendshipScore(void)
{
struct Pokemon *pokemon = &gPlayerParty[GetLeadMonIndex()];
if (GetMonData(pokemon, MON_DATA_FRIENDSHIP) == MAX_FRIENDSHIP)
return 6;
if (GetMonData(pokemon, MON_DATA_FRIENDSHIP) >= 200)
return 5;
if (GetMonData(pokemon, MON_DATA_FRIENDSHIP) >= 150)
return 4;
if (GetMonData(pokemon, MON_DATA_FRIENDSHIP) >= 100)
return 3;
if (GetMonData(pokemon, MON_DATA_FRIENDSHIP) >= 50)
return 2;
if (GetMonData(pokemon, MON_DATA_FRIENDSHIP) >= 1)
return 1;
return 0;
return GetMonFriendshipScore(&gPlayerParty[GetLeadMonIndex()]);
}
static void CB2_FieldShowRegionMap(void)