Merge branch '_RHH/master' into _RHH/upcoming
# Conflicts: # include/battle_util.h # src/battle_ai_main.c
This commit is contained in:
commit
2b40e79ac3
20 changed files with 429 additions and 163 deletions
14
Makefile
14
Makefile
|
@ -384,7 +384,7 @@ ifeq ($(NODEP),1)
|
|||
$(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.c
|
||||
ifeq (,$(KEEP_TEMPS))
|
||||
@echo "$(CC1) <flags> -o $@ $<"
|
||||
@$(CPP) $(CPPFLAGS) $< | $(PREPROC) $< charmap.txt -i | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ -
|
||||
@$(CPP) $(CPPFLAGS) $< | $(PREPROC) -i $< charmap.txt | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ -
|
||||
else
|
||||
@$(CPP) $(CPPFLAGS) $< -o $(C_BUILDDIR)/$*.i
|
||||
@$(PREPROC) $(C_BUILDDIR)/$*.i charmap.txt | $(CC1) $(CFLAGS) -o $(C_BUILDDIR)/$*.s
|
||||
|
@ -396,7 +396,7 @@ define C_DEP
|
|||
$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2)
|
||||
ifeq (,$$(KEEP_TEMPS))
|
||||
@echo "$$(CC1) <flags> -o $$@ $$<"
|
||||
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
|
||||
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) -i $$< charmap.txt | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
|
||||
else
|
||||
@$$(CPP) $$(CPPFLAGS) $$< -o $$(C_BUILDDIR)/$3.i
|
||||
@$$(PREPROC) $$(C_BUILDDIR)/$3.i charmap.txt | $$(CC1) $$(CFLAGS) -o $$(C_BUILDDIR)/$3.s
|
||||
|
@ -411,7 +411,7 @@ ifeq ($(NODEP),1)
|
|||
$(GFLIB_BUILDDIR)/%.o: $(GFLIB_SUBDIR)/%.c $$(c_dep)
|
||||
ifeq (,$(KEEP_TEMPS))
|
||||
@echo "$(CC1) <flags> -o $@ $<"
|
||||
@$(CPP) $(CPPFLAGS) $< | $(PREPROC) $< charmap.txt -i | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ -
|
||||
@$(CPP) $(CPPFLAGS) $< | $(PREPROC) -i $< charmap.txt | $(CC1) $(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $(AS) $(ASFLAGS) -o $@ -
|
||||
else
|
||||
@$(CPP) $(CPPFLAGS) $< -o $(GFLIB_BUILDDIR)/$*.i
|
||||
@$(PREPROC) $(GFLIB_BUILDDIR)/$*.i charmap.txt | $(CC1) $(CFLAGS) -o $(GFLIB_BUILDDIR)/$*.s
|
||||
|
@ -423,7 +423,7 @@ define GFLIB_DEP
|
|||
$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2)
|
||||
ifeq (,$$(KEEP_TEMPS))
|
||||
@echo "$$(CC1) <flags> -o $$@ $$<"
|
||||
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
|
||||
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) -i $$< charmap.txt | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
|
||||
else
|
||||
@$$(CPP) $$(CPPFLAGS) $$< -o $$(GFLIB_BUILDDIR)/$3.i
|
||||
@$$(PREPROC) $$(GFLIB_BUILDDIR)/$3.i charmap.txt | $$(CC1) $$(CFLAGS) -o $$(GFLIB_BUILDDIR)/$3.s
|
||||
|
@ -436,11 +436,11 @@ endif
|
|||
|
||||
ifeq ($(NODEP),1)
|
||||
$(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.s
|
||||
$(PREPROC) $< charmap.txt | $(CPP) -I include - | $(AS) $(ASFLAGS) -o $@
|
||||
$(PREPROC) $< charmap.txt | $(CPP) -I include - | $(PREPROC) -i $$< charmap.txt | $(AS) $(ASFLAGS) -o $@
|
||||
else
|
||||
define SRC_ASM_DATA_DEP
|
||||
$1: $2 $$(shell $(SCANINC) -I include -I "" $2)
|
||||
$$(PREPROC) $$< charmap.txt | $$(CPP) -I include - | $$(AS) $$(ASFLAGS) -o $$@
|
||||
$$(PREPROC) $$< charmap.txt | $$(CPP) -I include - | $$(PREPROC) -ie $$< charmap.txt | $$(AS) $$(ASFLAGS) -o $$@
|
||||
endef
|
||||
$(foreach src, $(C_ASM_SRCS), $(eval $(call SRC_ASM_DATA_DEP,$(patsubst $(C_SUBDIR)/%.s,$(C_BUILDDIR)/%.o, $(src)),$(src))))
|
||||
endif
|
||||
|
@ -458,7 +458,7 @@ endif
|
|||
|
||||
ifeq ($(NODEP),1)
|
||||
$(DATA_ASM_BUILDDIR)/%.o: $(DATA_ASM_SUBDIR)/%.s
|
||||
$(PREPROC) $< charmap.txt | $(CPP) -I include - | $(AS) $(ASFLAGS) -o $@
|
||||
$(PREPROC) $< charmap.txt | $(CPP) -I include - | $(PREPROC) -ie $$< charmap.txt | $(AS) $(ASFLAGS) -o $@
|
||||
else
|
||||
$(foreach src, $(REGULAR_DATA_ASM_SRCS), $(eval $(call SRC_ASM_DATA_DEP,$(patsubst $(DATA_ASM_SUBDIR)/%.s,$(DATA_ASM_BUILDDIR)/%.o, $(src)),$(src))))
|
||||
endif
|
||||
|
|
|
@ -9,7 +9,7 @@ pokeemerald-expansion is a decomp hack base project based off pret's [pokeemeral
|
|||
If you use pokeemerald-expansion in your hack, please add RHH (Rom Hacking Hideout) to your credits list. Optionally, you can list the version used, so it can help players know what features to expect.
|
||||
You can phrase it as the following:
|
||||
```
|
||||
Based off RHH's pokeemerald-expansion v1.8.0 https://github.com/rh-hideout/pokeemerald-expansion/
|
||||
Based off RHH's pokeemerald-expansion v1.8.5 https://github.com/rh-hideout/pokeemerald-expansion/
|
||||
```
|
||||
|
||||
## What features are included?
|
||||
|
|
|
@ -218,10 +218,10 @@ struct SpecialStatus
|
|||
u8 statLowered:1;
|
||||
u8 lightningRodRedirected:1;
|
||||
u8 restoredBattlerSprite: 1;
|
||||
u8 traced:1;
|
||||
u8 faintedHasReplacement:1;
|
||||
u8 focusBanded:1;
|
||||
u8 focusSashed:1;
|
||||
u8 unused:1;
|
||||
// End of byte
|
||||
u8 sturdied:1;
|
||||
u8 stormDrainRedirected:1;
|
||||
|
|
|
@ -32,8 +32,6 @@ enum {
|
|||
ABILITYEFFECT_IMMUNITY,
|
||||
ABILITYEFFECT_SYNCHRONIZE,
|
||||
ABILITYEFFECT_ATK_SYNCHRONIZE,
|
||||
ABILITYEFFECT_TRACE1,
|
||||
ABILITYEFFECT_TRACE2,
|
||||
ABILITYEFFECT_MOVE_END_OTHER,
|
||||
ABILITYEFFECT_NEUTRALIZINGGAS,
|
||||
ABILITYEFFECT_FIELD_SPORT, // Only used if B_SPORT_TURNS >= GEN_6
|
||||
|
|
|
@ -2667,7 +2667,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_UPPER_HAND:
|
||||
if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_IsSlower(battlerAtk, battlerDef, move) || GetMovePriority(battlerDef, move) < 1 || GetMovePriority(battlerDef, move) > 3) // Opponent going first or not using priority move
|
||||
if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_IsSlower(battlerAtk, battlerDef, move) || GetMovePriority(battlerDef, predictedMove) < 1 || GetMovePriority(battlerDef, predictedMove) > 3) // Opponent going first or not using priority move
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_PLACEHOLDER:
|
||||
|
|
|
@ -3854,8 +3854,6 @@ static void TryDoEventsBeforeFirstTurn(void)
|
|||
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0)
|
||||
return;
|
||||
}
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_TRACE1, 0, 0, 0, 0) != 0)
|
||||
return;
|
||||
// Check all switch in items having effect from the fastest mon to slowest.
|
||||
while (gBattleStruct->switchInItemsCounter < gBattlersCount)
|
||||
{
|
||||
|
|
|
@ -7133,8 +7133,7 @@ bool32 DoSwitchInAbilities(u32 battler)
|
|||
return (TryPrimalReversion(battler)
|
||||
|| AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0)
|
||||
|| (gBattleWeather & B_WEATHER_ANY && WEATHER_HAS_EFFECT && AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0))
|
||||
|| (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0))
|
||||
|| AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0));
|
||||
|| (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY && AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0)));
|
||||
}
|
||||
|
||||
static void UpdateSentMonFlags(u32 battler)
|
||||
|
@ -7282,6 +7281,14 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
|
|||
|
||||
gDisableStructs[battler].truantSwitchInHack = 0;
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (i != battler
|
||||
&& GetBattlerAbility(i) == ABILITY_TRACE
|
||||
&& AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, i, 0, 0, 0))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE))
|
||||
return TRUE;
|
||||
else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0))
|
||||
|
@ -9249,7 +9256,6 @@ static void Cmd_various(void)
|
|||
case VARIOUS_RESET_SWITCH_IN_ABILITY_BITS:
|
||||
{
|
||||
VARIOUS_ARGS();
|
||||
gSpecialStatuses[battler].traced = FALSE;
|
||||
gSpecialStatuses[battler].switchInAbilityDone = FALSE;
|
||||
break;
|
||||
}
|
||||
|
@ -9482,7 +9488,6 @@ static void Cmd_various(void)
|
|||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0);
|
||||
AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, battler, 0, 0, 0);
|
||||
AbilityBattleEffects(ABILITYEFFECT_TRACE2, battler, 0, 0, 0);
|
||||
AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -4313,6 +4313,50 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
gBattleScripting.battler = battler;
|
||||
switch (gLastUsedAbility)
|
||||
{
|
||||
case ABILITY_TRACE:
|
||||
{
|
||||
u32 chosenTarget;
|
||||
u32 target1;
|
||||
u32 target2;
|
||||
|
||||
if (gSpecialStatuses[battler].switchInAbilityDone)
|
||||
break;
|
||||
if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_TRACED)
|
||||
break;
|
||||
|
||||
side = (BATTLE_OPPOSITE(GetBattlerPosition(battler))) & BIT_SIDE;
|
||||
target1 = GetBattlerAtPosition(side);
|
||||
target2 = GetBattlerAtPosition(side + BIT_FLANK);
|
||||
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
{
|
||||
if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0
|
||||
&& !gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0)
|
||||
chosenTarget = GetBattlerAtPosition((RandomPercentage(RNG_TRACE, 50) * 2) | side), effect++;
|
||||
else if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0)
|
||||
chosenTarget = target1, effect++;
|
||||
else if (!gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0)
|
||||
chosenTarget = target2, effect++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0)
|
||||
chosenTarget = target1, effect++;
|
||||
}
|
||||
|
||||
if (effect != 0)
|
||||
{
|
||||
BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3);
|
||||
gBattleResources->flags->flags[battler] &= ~RESOURCE_FLAG_TRACED;
|
||||
gBattleStruct->tracedAbility[battler] = gLastUsedAbility = gBattleMons[chosenTarget].ability;
|
||||
RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability
|
||||
battler = gBattlerAbility = gBattleScripting.battler = battler;
|
||||
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget])
|
||||
PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABILITY_IMPOSTER:
|
||||
if (IsBattlerAlive(BATTLE_OPPOSITE(battler))
|
||||
&& !(gBattleMons[BATTLE_OPPOSITE(battler)].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE))
|
||||
|
@ -4650,13 +4694,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
effect++;
|
||||
}
|
||||
break;
|
||||
case ABILITY_TRACE:
|
||||
if (!(gSpecialStatuses[battler].traced))
|
||||
{
|
||||
gBattleResources->flags->flags[battler] |= RESOURCE_FLAG_TRACED;
|
||||
gSpecialStatuses[battler].traced = TRUE;
|
||||
}
|
||||
break;
|
||||
case ABILITY_CLOUD_NINE:
|
||||
case ABILITY_AIR_LOCK:
|
||||
if (!gSpecialStatuses[battler].switchInAbilityDone)
|
||||
|
@ -6132,48 +6169,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
}
|
||||
}
|
||||
break;
|
||||
case ABILITYEFFECT_TRACE1:
|
||||
case ABILITYEFFECT_TRACE2:
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (gBattleMons[i].ability == ABILITY_TRACE && (gBattleResources->flags->flags[i] & RESOURCE_FLAG_TRACED))
|
||||
{
|
||||
u32 chosenTarget;
|
||||
u32 side = (BATTLE_OPPOSITE(GetBattlerPosition(i))) & BIT_SIDE; // side of the opposing Pokémon
|
||||
u32 target1 = GetBattlerAtPosition(side);
|
||||
u32 target2 = GetBattlerAtPosition(side + BIT_FLANK);
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
{
|
||||
if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0
|
||||
&& !gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0)
|
||||
chosenTarget = GetBattlerAtPosition((RandomPercentage(RNG_TRACE, 50) * 2) | side), effect++;
|
||||
else if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0)
|
||||
chosenTarget = target1, effect++;
|
||||
else if (!gAbilitiesInfo[gBattleMons[target2].ability].cantBeTraced && gBattleMons[target2].hp != 0)
|
||||
chosenTarget = target2, effect++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0)
|
||||
chosenTarget = target1, effect++;
|
||||
}
|
||||
|
||||
if (effect != 0)
|
||||
{
|
||||
BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3);
|
||||
gBattleResources->flags->flags[i] &= ~RESOURCE_FLAG_TRACED;
|
||||
gBattleStruct->tracedAbility[i] = gLastUsedAbility = gBattleMons[chosenTarget].ability;
|
||||
RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability
|
||||
battler = gBattlerAbility = gBattleScripting.battler = i;
|
||||
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget])
|
||||
PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABILITYEFFECT_NEUTRALIZINGGAS:
|
||||
// Prints message only. separate from ABILITYEFFECT_ON_SWITCHIN bc activates before entry hazards
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
|
|
|
@ -14718,6 +14718,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
|
|||
.metronomeBanned = TRUE,
|
||||
.additionalEffects = ADDITIONAL_EFFECTS({
|
||||
.moveEffect = B_UPDATED_MOVE_DATA >= GEN_7 ? MOVE_EFFECT_DEF_PLUS_2: MOVE_EFFECT_DEF_PLUS_1,
|
||||
.self = TRUE,
|
||||
.chance = 50,
|
||||
}),
|
||||
.contestEffect = CONTEST_EFFECT_USER_MORE_EASILY_STARTLED,
|
||||
|
|
|
@ -12801,7 +12801,6 @@ static const struct LevelUpMove sBasculinWhiteStripedLevelUpLearnset[] = {
|
|||
};
|
||||
|
||||
static const struct LevelUpMove sBasculegionLevelUpLearnset[] = {
|
||||
LEVEL_UP_MOVE( 0, MOVE_DIRE_CLAW),
|
||||
LEVEL_UP_MOVE( 1, MOVE_SHADOW_BALL),
|
||||
LEVEL_UP_MOVE( 1, MOVE_TAIL_WHIP),
|
||||
LEVEL_UP_MOVE( 1, MOVE_WATER_GUN),
|
||||
|
|
|
@ -79,3 +79,20 @@ SINGLE_BATTLE_TEST("Trace will copy an opponent's ability whenever it has the ch
|
|||
MESSAGE("Ralts TRACED Foe Torchic's Blaze!");
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Trace respects the turn order")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_DEOXYS_SPEED) { Speed(40); Ability(ABILITY_PRESSURE); }
|
||||
PLAYER(SPECIES_GARDEVOIR) { Speed(20); Ability(ABILITY_TRACE); }
|
||||
OPPONENT(SPECIES_HIPPOWDON) { Speed(10); Ability(ABILITY_SAND_STREAM); }
|
||||
OPPONENT(SPECIES_DEOXYS_SPEED) { Speed(30); Ability(ABILITY_PRESSURE); }
|
||||
} WHEN {
|
||||
TURN { }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(playerLeft, ABILITY_PRESSURE);
|
||||
ABILITY_POPUP(opponentRight, ABILITY_PRESSURE);
|
||||
ABILITY_POPUP(playerRight, ABILITY_TRACE);
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_SAND_STREAM);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,3 +116,19 @@ SINGLE_BATTLE_TEST("Upper Hand is boosted by Sheer Force")
|
|||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI won't use Upper Hand unless it has seen a priority move")
|
||||
{
|
||||
u16 move;
|
||||
PARAMETRIZE { move = MOVE_TACKLE; }
|
||||
PARAMETRIZE { move = MOVE_QUICK_ATTACK; }
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
ASSUME(gMovesInfo[MOVE_QUICK_ATTACK].priority == 1);
|
||||
PLAYER(SPECIES_WOBBUFFET) {Moves(move); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_UPPER_HAND, MOVE_KARATE_CHOP); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, move); EXPECT_MOVE(opponent, MOVE_KARATE_CHOP); }
|
||||
TURN { MOVE(player, move); EXPECT_MOVE(opponent, move == MOVE_QUICK_ATTACK ? MOVE_UPPER_HAND : MOVE_KARATE_CHOP); }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,10 +3,10 @@ CXX ?= g++
|
|||
CXXFLAGS := -std=c++11 -O2 -Wall -Wno-switch -Werror
|
||||
|
||||
SRCS := asm_file.cpp c_file.cpp charmap.cpp preproc.cpp string_parser.cpp \
|
||||
utf8.cpp
|
||||
utf8.cpp io.cpp
|
||||
|
||||
HEADERS := asm_file.h c_file.h char_util.h charmap.h preproc.h string_parser.h \
|
||||
utf8.h
|
||||
utf8.h io.h
|
||||
|
||||
ifeq ($(OS),Windows_NT)
|
||||
EXE := .exe
|
||||
|
|
|
@ -27,33 +27,12 @@
|
|||
#include "utf8.h"
|
||||
#include "string_parser.h"
|
||||
#include "../../gflib/characters.h"
|
||||
#include "io.h"
|
||||
|
||||
AsmFile::AsmFile(std::string filename) : m_filename(filename)
|
||||
AsmFile::AsmFile(std::string filename, bool isStdin, bool doEnum) : m_filename(filename)
|
||||
{
|
||||
FILE *fp = std::fopen(filename.c_str(), "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
|
||||
|
||||
std::fseek(fp, 0, SEEK_END);
|
||||
|
||||
m_size = std::ftell(fp);
|
||||
|
||||
if (m_size < 0)
|
||||
FATAL_ERROR("File size of \"%s\" is less than zero.\n", filename.c_str());
|
||||
else if (m_size == 0)
|
||||
return; // Empty file
|
||||
|
||||
m_buffer = new char[m_size + 1];
|
||||
|
||||
std::rewind(fp);
|
||||
|
||||
if (std::fread(m_buffer, m_size, 1, fp) != 1)
|
||||
FATAL_ERROR("Failed to read \"%s\".\n", filename.c_str());
|
||||
|
||||
m_buffer[m_size] = 0;
|
||||
|
||||
std::fclose(fp);
|
||||
m_buffer = ReadFileToBuffer(filename.c_str(), isStdin, &m_size);
|
||||
m_doEnum = doEnum;
|
||||
|
||||
m_pos = 0;
|
||||
m_lineNum = 1;
|
||||
|
@ -65,6 +44,7 @@ AsmFile::AsmFile(std::string filename) : m_filename(filename)
|
|||
AsmFile::AsmFile(AsmFile&& other) : m_filename(std::move(other.m_filename))
|
||||
{
|
||||
m_buffer = other.m_buffer;
|
||||
m_doEnum = other.m_doEnum;
|
||||
m_pos = other.m_pos;
|
||||
m_size = other.m_size;
|
||||
m_lineNum = other.m_lineNum;
|
||||
|
@ -174,6 +154,8 @@ Directive AsmFile::GetDirective()
|
|||
return Directive::String;
|
||||
else if (CheckForDirective(".braille"))
|
||||
return Directive::Braille;
|
||||
else if (CheckForDirective("enum"))
|
||||
return Directive::Enum;
|
||||
else
|
||||
return Directive::Unknown;
|
||||
}
|
||||
|
@ -527,6 +509,70 @@ void AsmFile::OutputLine()
|
|||
}
|
||||
}
|
||||
|
||||
// parses an assumed C `enum`. Returns false if `enum { ...` is not matched
|
||||
bool AsmFile::ParseEnum()
|
||||
{
|
||||
if (!m_doEnum)
|
||||
return false;
|
||||
|
||||
long fallbackPosition = m_pos;
|
||||
std::string headerFilename = "";
|
||||
long currentHeaderLine = SkipWhitespaceAndEol();
|
||||
std::string enumName = ReadIdentifier();
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
long enumCounter = 0;
|
||||
long symbolCount = 0;
|
||||
|
||||
if (m_buffer[m_pos] != '{') // assume assembly macro, otherwise assume enum and report errors accordingly
|
||||
{
|
||||
m_pos = fallbackPosition - 4;
|
||||
return false;
|
||||
}
|
||||
|
||||
currentHeaderLine += FindLastLineNumber(headerFilename);
|
||||
m_pos++;
|
||||
for (;;)
|
||||
{
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
std::string currentIdentName = ReadIdentifier();
|
||||
if (!currentIdentName.empty())
|
||||
{
|
||||
std::printf("# %ld \"%s\"\n", currentHeaderLine, headerFilename.c_str());
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
if (m_buffer[m_pos] == '=')
|
||||
{
|
||||
m_pos++;
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
enumCounter = ReadInteger(headerFilename, currentHeaderLine);
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
}
|
||||
std::printf(".equiv %s, %ld\n", currentIdentName.c_str(), enumCounter);
|
||||
enumCounter++;
|
||||
symbolCount++;
|
||||
}
|
||||
else if (symbolCount == 0)
|
||||
{
|
||||
RaiseError("%s:%ld: empty enum is invalid", headerFilename.c_str(), currentHeaderLine);
|
||||
}
|
||||
|
||||
if (m_buffer[m_pos] != ',')
|
||||
{
|
||||
currentHeaderLine += SkipWhitespaceAndEol();
|
||||
if (m_buffer[m_pos++] == '}' && m_buffer[m_pos++] == ';')
|
||||
{
|
||||
ExpectEmptyRestOfLine();
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
RaiseError("unterminated enum from included file %s:%ld", headerFilename.c_str(), currentHeaderLine);
|
||||
}
|
||||
}
|
||||
m_pos++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Asserts that the rest of the line is empty and moves to the next one.
|
||||
void AsmFile::ExpectEmptyRestOfLine()
|
||||
{
|
||||
|
@ -599,3 +645,130 @@ void AsmFile::RaiseWarning(const char* format, ...)
|
|||
{
|
||||
DO_REPORT("warning");
|
||||
}
|
||||
|
||||
// Skips Whitespace including newlines and returns the amount of newlines skipped
|
||||
int AsmFile::SkipWhitespaceAndEol()
|
||||
{
|
||||
int newlines = 0;
|
||||
while (m_buffer[m_pos] == '\t' || m_buffer[m_pos] == ' ' || m_buffer[m_pos] == '\n')
|
||||
{
|
||||
if (m_buffer[m_pos] == '\n')
|
||||
newlines++;
|
||||
m_pos++;
|
||||
}
|
||||
return newlines;
|
||||
}
|
||||
|
||||
// returns the last line indicator and its corresponding file name without modifying the token index
|
||||
int AsmFile::FindLastLineNumber(std::string& filename)
|
||||
{
|
||||
long pos = m_pos;
|
||||
long linebreaks = 0;
|
||||
while (m_buffer[pos] != '#' && pos >= 0)
|
||||
{
|
||||
if (m_buffer[pos] == '\n')
|
||||
linebreaks++;
|
||||
pos--;
|
||||
}
|
||||
|
||||
if (pos < 0)
|
||||
RaiseError("line indicator for header file not found before `enum`");
|
||||
|
||||
pos++;
|
||||
while (m_buffer[pos] == ' ' || m_buffer[pos] == '\t')
|
||||
pos++;
|
||||
|
||||
if (!IsAsciiDigit(m_buffer[pos]))
|
||||
RaiseError("malformatted line indicator found before `enum`, expected line number");
|
||||
|
||||
unsigned n = 0;
|
||||
int digit = 0;
|
||||
while ((digit = ConvertDigit(m_buffer[pos++], 10)) != -1)
|
||||
n = 10 * n + digit;
|
||||
|
||||
while (m_buffer[pos] == ' ' || m_buffer[pos] == '\t')
|
||||
pos++;
|
||||
|
||||
if (m_buffer[pos++] != '"')
|
||||
RaiseError("malformatted line indicator found before `enum`, expected filename");
|
||||
|
||||
while (m_buffer[pos] != '"')
|
||||
{
|
||||
unsigned char c = m_buffer[pos++];
|
||||
|
||||
if (c == 0)
|
||||
{
|
||||
if (pos >= m_size)
|
||||
RaiseError("unexpected EOF in line indicator");
|
||||
else
|
||||
RaiseError("unexpected null character in line indicator");
|
||||
}
|
||||
|
||||
if (!IsAsciiPrintable(c))
|
||||
RaiseError("unexpected character '\\x%02X' in line indicator", c);
|
||||
|
||||
if (c == '\\')
|
||||
{
|
||||
c = m_buffer[pos];
|
||||
RaiseError("unexpected escape '\\%c' in line indicator", c);
|
||||
}
|
||||
|
||||
filename += c;
|
||||
}
|
||||
|
||||
return n + linebreaks - 1;
|
||||
}
|
||||
|
||||
std::string AsmFile::ReadIdentifier()
|
||||
{
|
||||
long start = m_pos;
|
||||
if (!IsIdentifierStartingChar(m_buffer[m_pos]))
|
||||
return std::string();
|
||||
|
||||
m_pos++;
|
||||
|
||||
while (IsIdentifierChar(m_buffer[m_pos]))
|
||||
m_pos++;
|
||||
|
||||
return std::string(&m_buffer[start], m_pos - start);
|
||||
}
|
||||
|
||||
long AsmFile::ReadInteger(std::string filename, long line)
|
||||
{
|
||||
bool negate = false;
|
||||
int radix = 10;
|
||||
if (!IsAsciiDigit(m_buffer[m_pos]))
|
||||
{
|
||||
if (m_buffer[m_pos++] == '-')
|
||||
negate = true;
|
||||
else
|
||||
RaiseError("expected number in included file %s:%ld", filename.c_str(), line);
|
||||
}
|
||||
|
||||
if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'x')
|
||||
{
|
||||
radix = 16;
|
||||
m_pos += 2;
|
||||
}
|
||||
else if (m_buffer[m_pos] == '0' && m_buffer[m_pos + 1] == 'b')
|
||||
{
|
||||
radix = 2;
|
||||
m_pos += 2;
|
||||
}
|
||||
else if (m_buffer[m_pos] == '0' && IsAsciiDigit(m_buffer[m_pos+1]))
|
||||
{
|
||||
radix = 8;
|
||||
m_pos++;
|
||||
}
|
||||
|
||||
long n = 0;
|
||||
int digit;
|
||||
|
||||
while ((digit = ConvertDigit(m_buffer[m_pos], radix)) != -1)
|
||||
{
|
||||
n = n * radix + digit;
|
||||
m_pos++;
|
||||
}
|
||||
|
||||
return negate ? -n : n;
|
||||
}
|
||||
|
|
|
@ -31,13 +31,14 @@ enum class Directive
|
|||
Include,
|
||||
String,
|
||||
Braille,
|
||||
Enum,
|
||||
Unknown
|
||||
};
|
||||
|
||||
class AsmFile
|
||||
{
|
||||
public:
|
||||
AsmFile(std::string filename);
|
||||
AsmFile(std::string filename, bool isStdin, bool doEnum);
|
||||
AsmFile(AsmFile&& other);
|
||||
AsmFile(const AsmFile&) = delete;
|
||||
~AsmFile();
|
||||
|
@ -49,9 +50,11 @@ public:
|
|||
bool IsAtEnd();
|
||||
void OutputLine();
|
||||
void OutputLocation();
|
||||
bool ParseEnum();
|
||||
|
||||
private:
|
||||
char* m_buffer;
|
||||
bool m_doEnum;
|
||||
long m_pos;
|
||||
long m_size;
|
||||
long m_lineNum;
|
||||
|
@ -68,6 +71,10 @@ private:
|
|||
void RaiseError(const char* format, ...);
|
||||
void RaiseWarning(const char* format, ...);
|
||||
void VerifyStringLength(int length);
|
||||
int SkipWhitespaceAndEol();
|
||||
int FindLastLineNumber(std::string& filename);
|
||||
std::string ReadIdentifier();
|
||||
long ReadInteger(std::string filename, long line);
|
||||
};
|
||||
|
||||
#endif // ASM_FILE_H
|
||||
|
|
|
@ -30,56 +30,16 @@
|
|||
#include "char_util.h"
|
||||
#include "utf8.h"
|
||||
#include "string_parser.h"
|
||||
#include "io.h"
|
||||
|
||||
CFile::CFile(const char * filenameCStr, bool isStdin)
|
||||
{
|
||||
FILE *fp;
|
||||
|
||||
if (isStdin) {
|
||||
fp = stdin;
|
||||
if (isStdin)
|
||||
m_filename = std::string{"<stdin>/"}.append(filenameCStr);
|
||||
} else {
|
||||
fp = std::fopen(filenameCStr, "rb");
|
||||
else
|
||||
m_filename = std::string(filenameCStr);
|
||||
}
|
||||
|
||||
std::string& filename = m_filename;
|
||||
|
||||
if (fp == NULL)
|
||||
FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename.c_str());
|
||||
|
||||
m_size = 0;
|
||||
m_buffer = (char *)malloc(CHUNK_SIZE + 1);
|
||||
if (m_buffer == NULL) {
|
||||
FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename.c_str());
|
||||
}
|
||||
|
||||
std::size_t numAllocatedBytes = CHUNK_SIZE + 1;
|
||||
std::size_t bufferOffset = 0;
|
||||
std::size_t count;
|
||||
|
||||
while ((count = std::fread(m_buffer + bufferOffset, 1, CHUNK_SIZE, fp)) != 0) {
|
||||
if (!std::ferror(fp)) {
|
||||
m_size += count;
|
||||
|
||||
if (std::feof(fp)) {
|
||||
break;
|
||||
}
|
||||
|
||||
numAllocatedBytes += CHUNK_SIZE;
|
||||
bufferOffset += CHUNK_SIZE;
|
||||
m_buffer = (char *)realloc(m_buffer, numAllocatedBytes);
|
||||
if (m_buffer == NULL) {
|
||||
FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename.c_str());
|
||||
}
|
||||
} else {
|
||||
FATAL_ERROR("Failed to read \"%s\". (error: %s)", filename.c_str(), std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer[m_size] = 0;
|
||||
|
||||
std::fclose(fp);
|
||||
m_buffer = ReadFileToBuffer(filenameCStr, isStdin, &m_size);
|
||||
|
||||
m_pos = 0;
|
||||
m_lineNum = 1;
|
||||
|
|
|
@ -56,6 +56,4 @@ private:
|
|||
void RaiseWarning(const char* format, ...);
|
||||
};
|
||||
|
||||
#define CHUNK_SIZE 4096
|
||||
|
||||
#endif // C_FILE_H
|
||||
|
|
51
tools/preproc/io.cpp
Normal file
51
tools/preproc/io.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#include "preproc.h"
|
||||
#include "io.h"
|
||||
#include <string>
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
|
||||
char *ReadFileToBuffer(const char *filename, bool isStdin, long *size)
|
||||
{
|
||||
FILE *fp;
|
||||
if (isStdin)
|
||||
fp = stdin;
|
||||
else
|
||||
fp = std::fopen(filename, "rb");
|
||||
|
||||
if (fp == NULL)
|
||||
FATAL_ERROR("Failed to open \"%s\" for reading.\n", filename);
|
||||
|
||||
*size = 0;
|
||||
char *buffer = (char *)malloc(CHUNK_SIZE + 1);
|
||||
if (buffer == NULL) {
|
||||
FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename);
|
||||
}
|
||||
|
||||
std::size_t numAllocatedBytes = CHUNK_SIZE + 1;
|
||||
std::size_t bufferOffset = 0;
|
||||
std::size_t count;
|
||||
|
||||
while ((count = std::fread(buffer + bufferOffset, 1, CHUNK_SIZE, fp)) != 0) {
|
||||
if (!std::ferror(fp)) {
|
||||
*size += count;
|
||||
|
||||
if (std::feof(fp)) {
|
||||
break;
|
||||
}
|
||||
|
||||
numAllocatedBytes += CHUNK_SIZE;
|
||||
bufferOffset += CHUNK_SIZE;
|
||||
buffer = (char *)realloc(buffer, numAllocatedBytes);
|
||||
if (buffer == NULL) {
|
||||
FATAL_ERROR("Failed to allocate memory to process file \"%s\"!", filename);
|
||||
}
|
||||
} else {
|
||||
FATAL_ERROR("Failed to read \"%s\". (error: %s)", filename, std::strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
buffer[*size] = 0;
|
||||
|
||||
std::fclose(fp);
|
||||
return buffer;
|
||||
}
|
8
tools/preproc/io.h
Normal file
8
tools/preproc/io.h
Normal file
|
@ -0,0 +1,8 @@
|
|||
#ifndef IO_H_
|
||||
#define IO_H_
|
||||
|
||||
#define CHUNK_SIZE 4096
|
||||
|
||||
char *ReadFileToBuffer(const char *filename, bool isStdin, long *size);
|
||||
|
||||
#endif // IO_H_
|
|
@ -20,11 +20,14 @@
|
|||
|
||||
#include <string>
|
||||
#include <stack>
|
||||
#include <unistd.h>
|
||||
#include "preproc.h"
|
||||
#include "asm_file.h"
|
||||
#include "c_file.h"
|
||||
#include "charmap.h"
|
||||
|
||||
static void UsageAndExit(const char *program);
|
||||
|
||||
Charmap* g_charmap;
|
||||
|
||||
void PrintAsmBytes(unsigned char *s, int length)
|
||||
|
@ -43,11 +46,12 @@ void PrintAsmBytes(unsigned char *s, int length)
|
|||
}
|
||||
}
|
||||
|
||||
void PreprocAsmFile(std::string filename)
|
||||
void PreprocAsmFile(std::string filename, bool isStdin, bool doEnum)
|
||||
{
|
||||
std::stack<AsmFile> stack;
|
||||
|
||||
stack.push(AsmFile(filename));
|
||||
stack.push(AsmFile(filename, isStdin, doEnum));
|
||||
std::printf("# 1 \"%s\"\n", filename.c_str());
|
||||
|
||||
for (;;)
|
||||
{
|
||||
|
@ -66,7 +70,7 @@ void PreprocAsmFile(std::string filename)
|
|||
switch (directive)
|
||||
{
|
||||
case Directive::Include:
|
||||
stack.push(AsmFile(stack.top().ReadPath()));
|
||||
stack.push(AsmFile(stack.top().ReadPath(), false, doEnum));
|
||||
stack.top().OutputLocation();
|
||||
break;
|
||||
case Directive::String:
|
||||
|
@ -83,6 +87,12 @@ void PreprocAsmFile(std::string filename)
|
|||
PrintAsmBytes(s, length);
|
||||
break;
|
||||
}
|
||||
case Directive::Enum:
|
||||
{
|
||||
if (!stack.top().ParseEnum())
|
||||
stack.top().OutputLine();
|
||||
break;
|
||||
}
|
||||
case Directive::Unknown:
|
||||
{
|
||||
std::string globalLabel = stack.top().GetGlobalLabel();
|
||||
|
@ -109,9 +119,9 @@ void PreprocCFile(const char * filename, bool isStdin)
|
|||
cFile.Preproc();
|
||||
}
|
||||
|
||||
char* GetFileExtension(char* filename)
|
||||
const char* GetFileExtension(const char* filename)
|
||||
{
|
||||
char* extension = filename;
|
||||
const char* extension = filename;
|
||||
|
||||
while (*extension != 0)
|
||||
extension++;
|
||||
|
@ -130,35 +140,64 @@ char* GetFileExtension(char* filename)
|
|||
return extension;
|
||||
}
|
||||
|
||||
static void UsageAndExit(const char *program)
|
||||
{
|
||||
std::fprintf(stderr, "Usage: %s [-i] [-e] SRC_FILE CHARMAP_FILE\nwhere -i denotes if input is from stdin\n -e enables enum handling\n", program);
|
||||
std::exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3 || argc > 4)
|
||||
int opt;
|
||||
const char *source = NULL;
|
||||
const char *charmap = NULL;
|
||||
bool isStdin = false;
|
||||
bool doEnum = false;
|
||||
|
||||
/* preproc [-i] [-e] SRC_FILE CHARMAP_FILE */
|
||||
while ((opt = getopt(argc, argv, "ie")) != -1)
|
||||
{
|
||||
std::fprintf(stderr, "Usage: %s SRC_FILE CHARMAP_FILE [-i]\nwhere -i denotes if input is from stdin\n", argv[0]);
|
||||
return 1;
|
||||
switch (opt)
|
||||
{
|
||||
case 'i':
|
||||
isStdin = true;
|
||||
break;
|
||||
case 'e':
|
||||
doEnum = true;
|
||||
break;
|
||||
default:
|
||||
UsageAndExit(argv[0]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_charmap = new Charmap(argv[2]);
|
||||
if (optind + 2 != argc)
|
||||
UsageAndExit(argv[0]);
|
||||
|
||||
char* extension = GetFileExtension(argv[1]);
|
||||
source = argv[optind + 0];
|
||||
charmap = argv[optind + 1];
|
||||
|
||||
g_charmap = new Charmap(charmap);
|
||||
|
||||
const char* extension = GetFileExtension(source);
|
||||
|
||||
if (!extension)
|
||||
FATAL_ERROR("\"%s\" has no file extension.\n", argv[1]);
|
||||
|
||||
if ((extension[0] == 's') && extension[1] == 0)
|
||||
PreprocAsmFile(argv[1]);
|
||||
else if ((extension[0] == 'c' || extension[0] == 'i') && extension[1] == 0) {
|
||||
if (argc == 4) {
|
||||
if (argv[3][0] == '-' && argv[3][1] == 'i' && argv[3][2] == '\0') {
|
||||
PreprocCFile(argv[1], true);
|
||||
} else {
|
||||
FATAL_ERROR("unknown argument flag \"%s\".\n", argv[3]);
|
||||
}
|
||||
} else {
|
||||
PreprocCFile(argv[1], false);
|
||||
}
|
||||
} else
|
||||
{
|
||||
PreprocAsmFile(source, isStdin, doEnum);
|
||||
}
|
||||
else if ((extension[0] == 'c' || extension[0] == 'i') && extension[1] == 0)
|
||||
{
|
||||
if (doEnum)
|
||||
FATAL_ERROR("-e is invalid for C sources\n");
|
||||
PreprocCFile(source, isStdin);
|
||||
}
|
||||
else
|
||||
{
|
||||
FATAL_ERROR("\"%s\" has an unknown file extension of \"%s\".\n", argv[1], extension);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue