diff options
-rw-r--r-- | .github/workflows/build.yml | 44 | ||||
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | asm/macros/event.inc | 4 | ||||
-rw-r--r-- | include/constants/contest.h | 1 | ||||
-rw-r--r-- | include/event_object_lock.h | 4 | ||||
-rwxr-xr-x | src/battle_anim_throw.c | 2 | ||||
-rw-r--r-- | src/battle_dome.c | 8 | ||||
-rw-r--r-- | src/battle_main.c | 6 | ||||
-rw-r--r-- | src/battle_script_commands.c | 4 | ||||
-rw-r--r-- | src/contest.c | 27 | ||||
-rw-r--r-- | src/event_object_lock.c | 8 | ||||
-rw-r--r-- | src/pokemon_summary_screen.c | 20 | ||||
-rw-r--r-- | src/scrcmd.c | 10 | ||||
-rw-r--r-- | src/union_room.c | 2 | ||||
-rw-r--r-- | src/wild_encounter.c | 4 |
15 files changed, 115 insertions, 49 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 822b386ea..de3a35c40 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,11 +7,29 @@ on: jobs: build: - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest + env: + GAME_VERSION: EMERALD + GAME_REVISION: 0 + GAME_LANGUAGE: ENGLISH + MODERN: 0 + COMPARE: 1 steps: - name: Checkout uses: actions/checkout@master + - name: Checkout syms + uses: actions/checkout@master + with: + path: symbols + ref: symbols + + - name: Checkout agbcc + uses: actions/checkout@master + with: + path: agbcc + repository: pret/agbcc + - name: Install binutils run: sudo apt install gcc-arm-none-eabi binutils-arm-none-eabi # build-essential, git, and libpng-dev are already installed @@ -20,16 +38,18 @@ jobs: - name: Install agbcc run: | - git clone https://github.com/pret/agbcc.git - cd agbcc ./build.sh ./install.sh ../ + working-directory: agbcc - name: Compare - run: make -j${nproc} compare + run: make -j${nproc} all syms - name: Modern - run: make -j${nproc} modern + env: + MODERN: 1 + COMPARE: 0 + run: make -j${nproc} all - name: Webhook if: ${{ github.event_name == 'push' }} @@ -38,3 +58,17 @@ jobs: CALCROM_DISCORD_WEBHOOK_AVATAR_URL: https://i.imgur.com/38BQHdd.png CALCROM_DISCORD_WEBHOOK_URL: ${{ secrets.CALCROM_DISCORD_WEBHOOK_URL }} run: sh .github/calcrom/webhook.sh pokeemerald + + - name: Move symfiles + if: ${{ github.event_name == 'push' }} + run: | + cp -v *.sym symbols/ + + - name: Update symfiles + if: ${{ github.event_name == 'push' }} + uses: EndBug/add-and-commit@v7 + with: + branch: symbols + cwd: "./symbols" + add: "*.sym" + message: ${{ github.event.commits[0].message }} @@ -20,6 +20,7 @@ endif PREFIX := arm-none-eabi- OBJCOPY := $(PREFIX)objcopy +OBJDUMP := $(PREFIX)objdump AS := $(PREFIX)as LD := $(PREFIX)ld @@ -27,6 +28,7 @@ LD := $(PREFIX)ld # note: the makefile must be set up so MODERNCC is never called # if MODERN=0 MODERNCC := $(PREFIX)gcc +PATH_MODERNCC := PATH=$(TOOLCHAIN)/bin:PATH $(MODERNCC) ifeq ($(OS),Windows_NT) EXE := .exe @@ -75,6 +77,7 @@ SHELL := /bin/bash -o pipefail ELF = $(ROM:.gba=.elf) MAP = $(ROM:.gba=.map) +SYM = $(ROM:.gba=.sym) C_SUBDIR = src GFLIB_SUBDIR = gflib @@ -103,11 +106,11 @@ OBJ_DIR := $(OBJ_DIR_NAME) LIBPATH := -L ../../tools/agbcc/lib LIB := $(LIBPATH) -lgcc -lc -L../../libagbsyscall -lagbsyscall else -CC1 = $(shell $(MODERNCC) --print-prog-name=cc1) -quiet +CC1 = $(shell $(PATH_MODERNCC) --print-prog-name=cc1) -quiet override CFLAGS += -mthumb -mthumb-interwork -O2 -mabi=apcs-gnu -mtune=arm7tdmi -march=armv4t -fno-toplevel-reorder -Wno-pointer-to-int-cast -g ROM := $(MODERN_ROM_NAME) OBJ_DIR := $(MODERN_OBJ_DIR_NAME) -LIBPATH := -L "$(dir $(shell $(MODERNCC) -mthumb -print-file-name=libgcc.a))" -L "$(dir $(shell $(MODERNCC) -mthumb -print-file-name=libnosys.a))" -L "$(dir $(shell $(MODERNCC) -mthumb -print-file-name=libc.a))" +LIBPATH := -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libgcc.a))" -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libnosys.a))" -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libc.a))" LIB := $(LIBPATH) -lc -lnosys -lgcc -L../../libagbsyscall -lagbsyscall endif @@ -129,6 +132,8 @@ FIX := tools/gbafix/gbafix$(EXE) MAPJSON := tools/mapjson/mapjson$(EXE) JSONPROC := tools/jsonproc/jsonproc$(EXE) +PERL := perl + TOOLDIRS := $(filter-out tools/agbcc tools/binutils,$(wildcard tools/*)) TOOLBASE = $(TOOLDIRS:tools/%=%) TOOLS = $(foreach tool,$(TOOLBASE),tools/$(tool)/$(tool)$(EXE)) @@ -153,7 +158,7 @@ infoshell = $(foreach line, $(shell $1 | sed "s/ /__SPACE__/g"), $(info $(subst # Disable dependency scanning for clean/tidy/tools # Use a separate minimal makefile for speed # Since we don't need to reload most of this makefile -ifeq (,$(filter-out all rom compare modern berry_fix libagbsyscall,$(MAKECMDGOALS))) +ifeq (,$(filter-out all rom compare modern berry_fix libagbsyscall syms,$(MAKECMDGOALS))) $(call infoshell, $(MAKE) -f make_tools.mk) else NODEP ?= 1 @@ -211,6 +216,8 @@ all: rom tools: $(TOOLDIRS) +syms: $(SYM) + $(TOOLDIRS): @$(MAKE) -C $@ @@ -428,3 +435,10 @@ berry_fix: libagbsyscall: @$(MAKE) -C libagbsyscall TOOLCHAIN=$(TOOLCHAIN) MODERN=$(MODERN) + +################### +### Symbol file ### +################### + +$(SYM): $(ELF) + $(OBJDUMP) -t $< | sort -u | grep -E "^0[2389]" | $(PERL) -p -e 's/^(\w{8}) (\w).{6} \S+\t(\w{8}) (\S+)$$/\1 \2 \3 \4/g' > $@ diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 915fc7142..73aef33b5 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -813,12 +813,12 @@ .byte 0x68 .endm - @ Ceases movement for all Objects on-screen. + @ Freezes all objects immediately except the player. The player is frozen once their movement is finished. .macro lockall .byte 0x69 .endm - @ If the script was called by an Object, then that Object's movement will cease. + @ Freezes all objects immediately except the player and the selected object. The player and selected object are frozen once their movement is finished. .macro lock .byte 0x6a .endm diff --git a/include/constants/contest.h b/include/constants/contest.h index 28b02e9c0..775dcbe62 100644 --- a/include/constants/contest.h +++ b/include/constants/contest.h @@ -4,6 +4,7 @@ #define APPLAUSE_METER_SIZE 5 #define CONTEST_NUM_APPEALS 5 #define CONTEST_LAST_APPEAL (CONTEST_NUM_APPEALS - 1) +#define MAX_CONTEST_MOVE_HEARTS 8 #define LINK_CONTEST_FLAG_IS_LINK (1 << 0) #define LINK_CONTEST_FLAG_IS_WIRELESS (1 << 1) diff --git a/include/event_object_lock.h b/include/event_object_lock.h index 9d31a25fd..0b1f5f098 100644 --- a/include/event_object_lock.h +++ b/include/event_object_lock.h @@ -2,9 +2,9 @@ #define GUARD_EVENT_OBJECT_LOCK_H bool8 IsFreezePlayerFinished(void); -void ScriptFreezeObjectEvents(void); bool8 IsFreezeSelectedObjectAndPlayerFinished(void); -void LockSelectedObjectEvent(void); +void FreezeObjects_WaitForPlayer(void); +void FreezeObjects_WaitForPlayerAndSelected(void); void FreezeForApproachingTrainers(void); bool8 IsFreezeObjectAndPlayerFinished(void); void ScriptUnfreezeObjectEvents(void); diff --git a/src/battle_anim_throw.c b/src/battle_anim_throw.c index 83768b476..4064e250c 100755 --- a/src/battle_anim_throw.c +++ b/src/battle_anim_throw.c @@ -1420,7 +1420,7 @@ static void MakeCaptureStars(struct Sprite *sprite) LoadBallParticleGfx(BALL_MASTER); for (i = 0; i < ARRAY_COUNT(sCaptureStars); i++) { - u8 spriteId = CreateSprite(&sBallParticleSpriteTemplates[4], sprite->pos1.x, sprite->pos1.y, subpriority); + u8 spriteId = CreateSprite(&sBallParticleSpriteTemplates[BALL_MASTER], sprite->pos1.x, sprite->pos1.y, subpriority); if (spriteId != MAX_SPRITES) { gSprites[spriteId].sDuration = 24; diff --git a/src/battle_dome.c b/src/battle_dome.c index 59e418a06..ea1e5abba 100644 --- a/src/battle_dome.c +++ b/src/battle_dome.c @@ -1211,8 +1211,8 @@ static const u8 gUnknown_0860D1A0[DOME_TOURNAMENT_TRAINERS_COUNT / 2][DOME_ROUND static const u8 gUnknown_0860D1C0[DOME_TOURNAMENT_TRAINERS_COUNT] = {0, 15, 8, 7, 3, 12, 11, 4, 1, 14, 9, 6, 2, 13, 10, 5}; -// Each tourney trainer has a text describing their potential to win, depending on their seed ranking for the current tourney -// Dome Ace Tucker has their own separate potential text +// The first line of text on a trainers info card. It describes their potential to win, based on their seed in the tournament tree. +// Dome Ace Tucker has their own separate potential text. static const u8 *const sBattleDomePotentialTexts[DOME_TOURNAMENT_TRAINERS_COUNT + 1] = { BattleDome_Text_Potential1, // Highest potential @@ -1234,7 +1234,7 @@ static const u8 *const sBattleDomePotentialTexts[DOME_TOURNAMENT_TRAINERS_COUNT BattleDome_Text_PotentialDomeAceTucker, }; -// The first line of text on a trainers info card that gives information about their battle style (dependent on their party's moves) +// The second line of text on a trainers info card. It gives information about their battle style (dependent on their party's moves). static const u8 *const sBattleDomeOpponentStyleTexts[NUM_BATTLE_STYLES] = { [DOME_BATTLE_STYLE_RISKY] = BattleDome_Text_StyleRiskDisaster, @@ -1271,7 +1271,7 @@ static const u8 *const sBattleDomeOpponentStyleTexts[NUM_BATTLE_STYLES] = [DOME_BATTLE_STYLE_UNUSED4] = BattleDome_Text_StyleSampleMessage4, }; -// The second line of text on a trainers info card that gives information about their party's stat spread +// The third line of text on a trainers info card. It that gives information about their party's stat spread (based on their Pokémon's effort values and Nature). static const u8 *const sBattleDomeOpponentStatsTexts[] = { BattleDome_Text_EmphasizesHPAndAtk, // DOME_TEXT_TWO_GOOD_STATS and DOME_TEXT_HP start here diff --git a/src/battle_main.c b/src/battle_main.c index 001e2ec17..977bcbd9c 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1961,10 +1961,10 @@ static u8 CreateNPCTrainerParty(struct Pokemon *party, u16 trainerNum, bool8 fir if (gTrainers[trainerNum].doubleBattle == TRUE) personalityValue = 0x80; - else if (gTrainers[trainerNum].encounterMusic_gender & 0x80) - personalityValue = 0x78; + else if (gTrainers[trainerNum].encounterMusic_gender & F_TRAINER_FEMALE) + personalityValue = 0x78; // Use personality more likely to result in a female Pokémon else - personalityValue = 0x88; + personalityValue = 0x88; // Use personality more likely to result in a male Pokémon for (j = 0; gTrainers[trainerNum].trainerName[j] != EOS; j++) nameHash += gTrainers[trainerNum].trainerName[j]; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 15e44dd75..d927ffb76 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3269,7 +3269,7 @@ static void Cmd_getexp(void) if (viaExpShare) // at least one mon is getting exp via exp share { - *exp = calculatedExp / 2 / viaSentIn; + *exp = SAFE_DIV(calculatedExp / 2, viaSentIn); if (*exp == 0) *exp = 1; @@ -3279,7 +3279,7 @@ static void Cmd_getexp(void) } else { - *exp = calculatedExp / viaSentIn; + *exp = SAFE_DIV(calculatedExp, viaSentIn); if (*exp == 0) *exp = 1; gExpShareExp = 0; diff --git a/src/contest.c b/src/contest.c index 720bea180..84d00d8ee 100644 --- a/src/contest.c +++ b/src/contest.c @@ -258,6 +258,11 @@ enum { #define TAG_BLINK_EFFECT_CONTESTANT2 0x80EA #define TAG_BLINK_EFFECT_CONTESTANT3 0x80EB +#define TILE_FILLED_APPEAL_HEART 0x5012 +#define TILE_FILLED_JAM_HEART 0x5014 +#define TILE_EMPTY_APPEAL_HEART 0x5035 +#define TILE_EMPTY_JAM_HEART 0x5036 + enum { SLIDER_HEART_ANIM_NORMAL, SLIDER_HEART_ANIM_DISAPPEAR, @@ -3203,27 +3208,25 @@ static void PrintContestMoveDescription(u16 a) ContestBG_FillBoxWithIncrementingTile(0, categoryTile, 0x0b, 0x1f, 0x05, 0x01, 0x11, 0x01); ContestBG_FillBoxWithIncrementingTile(0, categoryTile + 0x10, 0x0b, 0x20, 0x05, 0x01, 0x11, 0x01); + // Appeal hearts if (gContestEffects[gContestMoves[a].effect].appeal == 0xFF) numHearts = 0; else numHearts = gContestEffects[gContestMoves[a].effect].appeal / 10; - if (numHearts > 8) - numHearts = 8; - // Filled-in hearts - ContestBG_FillBoxWithTile(0, 0x5035, 0x15, 0x1f, 0x08, 0x01, 0x11); - // Empty hearts - ContestBG_FillBoxWithTile(0, 0x5012, 0x15, 0x1f, numHearts, 0x01, 0x11); + if (numHearts > MAX_CONTEST_MOVE_HEARTS) + numHearts = MAX_CONTEST_MOVE_HEARTS; + ContestBG_FillBoxWithTile(0, TILE_EMPTY_APPEAL_HEART, 0x15, 0x1f, MAX_CONTEST_MOVE_HEARTS, 0x01, 0x11); + ContestBG_FillBoxWithTile(0, TILE_FILLED_APPEAL_HEART, 0x15, 0x1f, numHearts, 0x01, 0x11); + // Jam hearts if (gContestEffects[gContestMoves[a].effect].jam == 0xFF) numHearts = 0; else numHearts = gContestEffects[gContestMoves[a].effect].jam / 10; - if (numHearts > 8) - numHearts = 8; - // Filled-in hearts - ContestBG_FillBoxWithTile(0, 0x5036, 0x15, 0x20, 0x08, 0x01, 0x11); - // Empty hearts - ContestBG_FillBoxWithTile(0, 0x5014, 0x15, 0x20, numHearts, 0x01, 0x11); + if (numHearts > MAX_CONTEST_MOVE_HEARTS) + numHearts = MAX_CONTEST_MOVE_HEARTS; + ContestBG_FillBoxWithTile(0, TILE_EMPTY_JAM_HEART, 0x15, 0x20, MAX_CONTEST_MOVE_HEARTS, 0x01, 0x11); + ContestBG_FillBoxWithTile(0, TILE_FILLED_JAM_HEART, 0x15, 0x20, numHearts, 0x01, 0x11); FillWindowPixelBuffer(WIN_MOVE_DESCRIPTION, PIXEL_FILL(0)); Contest_PrintTextToBg0WindowStd(WIN_MOVE_DESCRIPTION, gContestEffectDescriptionPointers[gContestMoves[a].effect]); diff --git a/src/event_object_lock.c b/src/event_object_lock.c index dec2d7906..179c72813 100644 --- a/src/event_object_lock.c +++ b/src/event_object_lock.c @@ -40,7 +40,7 @@ bool8 IsFreezePlayerFinished(void) } -void ScriptFreezeObjectEvents(void) +void FreezeObjects_WaitForPlayer(void) { FreezeObjectEvents(); CreateTask(Task_FreezePlayer, 80); @@ -82,7 +82,9 @@ bool8 IsFreezeSelectedObjectAndPlayerFinished(void) } } -void LockSelectedObjectEvent(void) +// Freeze all objects immediately except the selected object and the player. +// The selected object and player are frozen once their movement is finished. +void FreezeObjects_WaitForPlayerAndSelected(void) { u8 taskId; FreezeObjectEventsExceptOne(gSelectedObjectEvent); @@ -144,6 +146,8 @@ static void Task_FreezeObjectAndPlayer(u8 taskId) DestroyTask(taskId); } +// Freeze all objects immediately except the player and the approaching trainers. +// The approaching trainers and player are frozen once their movement is finished void FreezeForApproachingTrainers(void) { u8 trainerObjectId1, trainerObjectId2, taskId; diff --git a/src/pokemon_summary_screen.c b/src/pokemon_summary_screen.c index 8f16321b2..31505b7c9 100644 --- a/src/pokemon_summary_screen.c +++ b/src/pokemon_summary_screen.c @@ -119,6 +119,11 @@ enum SPRITE_ARR_ID_COUNT = SPRITE_ARR_ID_MOVE_SELECTOR2 + MOVE_SELECTOR_SPRITES_COUNT }; +#define TILE_EMPTY_APPEAL_HEART 0x1039 +#define TILE_FILLED_APPEAL_HEART 0x103A +#define TILE_FILLED_JAM_HEART 0x103C +#define TILE_EMPTY_JAM_HEART 0x103D + static EWRAM_DATA struct PokemonSummaryScreenData { /*0x00*/ union { @@ -2645,29 +2650,30 @@ static void DrawContestMoveHearts(u16 move) if (move != MOVE_NONE) { + // Draw appeal hearts u8 effectValue = gContestEffects[gContestMoves[move].effect].appeal; if (effectValue != 0xFF) effectValue /= 10; - for (i = 0; i < 8; i++) + for (i = 0; i < MAX_CONTEST_MOVE_HEARTS; i++) { if (effectValue != 0xFF && i < effectValue) - tilemap[(i / 4 * 32) + (i & 3) + 0x1E6] = 0x103A; + tilemap[(i / 4 * 32) + (i & 3) + 0x1E6] = TILE_FILLED_APPEAL_HEART; else - tilemap[(i / 4 * 32) + (i & 3) + 0x1E6] = 0x1039; + tilemap[(i / 4 * 32) + (i & 3) + 0x1E6] = TILE_EMPTY_APPEAL_HEART; } + // Draw jam hearts effectValue = gContestEffects[gContestMoves[move].effect].jam; - if (effectValue != 0xFF) effectValue /= 10; - for (i = 0; i < 8; i++) + for (i = 0; i < MAX_CONTEST_MOVE_HEARTS; i++) { if (effectValue != 0xFF && i < effectValue) - tilemap[(i / 4 * 32) + (i & 3) + 0x226] = 0x103C; + tilemap[(i / 4 * 32) + (i & 3) + 0x226] = TILE_FILLED_JAM_HEART; else - tilemap[(i / 4 * 32) + (i & 3) + 0x226] = 0x103D; + tilemap[(i / 4 * 32) + (i & 3) + 0x226] = TILE_EMPTY_JAM_HEART; } } } diff --git a/src/scrcmd.c b/src/scrcmd.c index 0ee20d1c6..f53483978 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -1203,6 +1203,8 @@ bool8 ScrCmd_turnvobject(struct ScriptContext *ctx) return FALSE; } +// lockall freezes all object events except the player immediately. +// The player is frozen after waiting for their current movement to finish. bool8 ScrCmd_lockall(struct ScriptContext *ctx) { if (IsUpdateLinkStateCBActive()) @@ -1211,12 +1213,14 @@ bool8 ScrCmd_lockall(struct ScriptContext *ctx) } else { - ScriptFreezeObjectEvents(); + FreezeObjects_WaitForPlayer(); SetupNativeScript(ctx, IsFreezePlayerFinished); return TRUE; } } +// lock freezes all object events except the player and the selected object immediately. +// The player and selected object are frozen after waiting for their current movement to finish. bool8 ScrCmd_lock(struct ScriptContext *ctx) { if (IsUpdateLinkStateCBActive()) @@ -1227,12 +1231,12 @@ bool8 ScrCmd_lock(struct ScriptContext *ctx) { if (gObjectEvents[gSelectedObjectEvent].active) { - LockSelectedObjectEvent(); + FreezeObjects_WaitForPlayerAndSelected(); SetupNativeScript(ctx, IsFreezeSelectedObjectAndPlayerFinished); } else { - ScriptFreezeObjectEvents(); + FreezeObjects_WaitForPlayer(); SetupNativeScript(ctx, IsFreezePlayerFinished); } return TRUE; diff --git a/src/union_room.c b/src/union_room.c index 372792a13..8d02a260d 100644 --- a/src/union_room.c +++ b/src/union_room.c @@ -4419,7 +4419,7 @@ static void HandleCancelActivity(bool32 setData) static void UR_EnableScriptContext2AndFreezeObjectEvents(void) { ScriptContext2_Enable(); - ScriptFreezeObjectEvents(); + FreezeObjects_WaitForPlayer(); } static u8 GetActivePartnerSpriteGenderParam(struct WirelessLink_URoom *data) diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 767fbe4e7..8bcb17605 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -162,7 +162,7 @@ static u8 ChooseWildMonIndex_Land(void) return 8; else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_8 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_9) return 9; - else if (rand == ENCOUNTER_CHANCE_LAND_MONS_SLOT_9) + else if (rand >= ENCOUNTER_CHANCE_LAND_MONS_SLOT_9 && rand < ENCOUNTER_CHANCE_LAND_MONS_SLOT_10) return 10; else return 11; @@ -215,7 +215,7 @@ static u8 ChooseWildMonIndex_Fishing(u8 rod) wildMonIndex = 7; if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_7 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_8) wildMonIndex = 8; - if (rand == ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_8) + if (rand >= ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_8 && rand < ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_9) wildMonIndex = 9; break; } |