Updated follower messages.

Added remote build scripts/workflows.
Fixed followers getting stuck when entering doors.
This commit is contained in:
Ariel Antonitis 2021-02-28 20:40:43 -05:00
parent 9952a47a64
commit 8dd1811ce5
9 changed files with 181 additions and 102 deletions

24
.github/workflows/buildrom.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Build ROM
on:
push:
tags:
- 'v*'
jobs:
build_rom:
runs-on: ubuntu-latest
container:
image: arantonitis/devkitgba
steps:
- uses: actions/checkout@v2
- name: Build & install agbcc
run: |
cd ..
git clone --depth 1 https://github.com/pret/agbcc
cd agbcc
./build.sh
./install.sh ../pokeemerald
- name: Build ROM
run: |
make

1
.gitignore vendored
View File

@ -34,5 +34,6 @@ porymap.project.cfg
*.a
.fuse_hidden*
.ccls-cache/*
.ropeproject/
overworld/
*.sna

View File

@ -1,5 +1,5 @@
gText_FollowerLovesYou::
.string "{STR_VAR_1} jumps and grins at you.\pIts expression is welling up with love!$"
.string "{STR_VAR_1} is regarding you with\nadoration!$"
gText_FollowerLostInThought::
.string "{STR_VAR_1} seems lost in thought.$"
@ -7,8 +7,8 @@ gText_FollowerLostInThought::
gText_FollowerDefault::
.string "ERROR 404: Script not found.$"
gText_FollowerHasWetFeet::
.string "{STR_VAR_1} frowns at you. It shuffles\nits wet feet.$"
gText_FollowerUnhappyToBeWet::
.string "{STR_VAR_1} looks unhappy to be wet.$"
gText_FollowerSplashesAround::
.string "{STR_VAR_1} splashes around happily!$"
@ -23,17 +23,29 @@ gText_FollowerHappyRain::
.string "{STR_VAR_1} seems to be happy\nabout the rain!$"
gText_FollowerMetLocation::
.string "{STR_VAR_1} is looking around fondly.\pIt seems familiar with this place.$"
.string "{STR_VAR_1} looks around fondly.\nIt seems familiar with this place.$"
gText_FollowerSkeptical::
.string "{STR_VAR_1} gives you a skeptical look...\pWhat could it mean?$"
.string "{STR_VAR_1} looks skeptical...\pWhat could it mean?$"
gText_FollowerAppraising::
.string "{STR_VAR_1} looks you up and down...\pIt carefully follows\nbehind you.$"
.string "{STR_VAR_1} carefully follows behind.$"
gText_FollowerHappyWalk::
.string "{STR_VAR_1} skips happily behind you!$"
gText_FollowerAboutToFall::
.string "{STR_VAR_1} seems to be about\nto fall over!$"
gText_FollowerTryingToKeepUp::
.string "{STR_VAR_1} is trying very hard\nto keep up with you...$"
gText_FollowerIsShivering::
.string "{STR_VAR_1} is shivering.$"
gText_FollowerBurnPainful::
.string "{STR_VAR_1}'s burn looks painful!$'"
.macro playfirstmoncry
callfunc ScrFunc_playfirstmoncry
.endm
@ -46,6 +58,8 @@ callfunc ScrFunc_bufferlivemonspeciesname
EventScript_Follower::
lock
faceplayer
bufferlivemonspeciesname 0
playfirstmoncry
callfunc ScrFunc_getfolloweraction
checkpartymove MOVE_FLY
compare VAR_RESULT 6
@ -63,29 +77,21 @@ EventScript_FollowerEnd::
end
EventScript_FollowerHappyWalk::
bufferlivemonspeciesname 0
playfirstmoncry
msgbox gText_FollowerHappyWalk, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerAppraising::
bufferlivemonspeciesname 0
playfirstmoncry
msgbox gText_FollowerAppraising, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerSkeptical::
bufferlivemonspeciesname 0
playfirstmoncry
msgbox gText_FollowerSkeptical, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerMetLocation::
bufferlivemonspeciesname 0
playfirstmoncry
applymovement 0xFE Common_Movement_QuestionMark
waitmoncry
waitmovement 0xFE
@ -93,49 +99,60 @@ EventScript_FollowerMetLocation::
return
EventScript_FollowerUnhappyFace::
bufferlivemonspeciesname 0
playfirstmoncry
msgbox gText_FollowerUnhappyFace, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerHappyRain::
bufferlivemonspeciesname 0
playfirstmoncry
msgbox gText_FollowerHappyRain, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerHasWetFeet::
bufferlivemonspeciesname 0
playfirstmoncry
msgbox gText_FollowerHasWetFeet, MSGBOX_DEFAULT
EventScript_FollowerUnhappyToBeWet::
msgbox gText_FollowerUnhappyToBeWet, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerSplashesAbout::
bufferlivemonspeciesname 0
playfirstmoncry
applymovement 0xFE FollowerSplashMovement
waitmovement 0xFE
msgbox gText_FollowerSplashesAround, MSGBOX_DEFAULT
return
EventScript_FollowerLovesYou::
playfirstmoncry
applymovement 0xFE ContestHall_Movement_Heart
waitmovement 0xFE
waitmoncry
bufferlivemonspeciesname 0
msgbox gText_FollowerLovesYou, MSGBOX_DEFAULT
return
EventScript_FollowerAboutToFall::
msgbox gText_FollowerAboutToFall, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerTryingToKeepUp::
msgbox gText_FollowerTryingToKeepUp, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerIsShivering::
msgbox gText_FollowerIsShivering, MSGBOX_DEFAULT
waitmoncry
return
EventScript_FollowerBurnPainful::
msgbox gText_FollowerBurnPainful, MSGBOX_DEFAULT
waitmoncry
return
EnterPokeballMovement::
.byte 0x9F @ EnterPokeball
step_end
FollowerSplashMovement::
jump_in_place_left_right
jump_in_place_left_right
FollowerSplashMovement:: @ defined in movement.inc
jump_in_place_down
delay_4
jump_in_place_down
face_player
step_end

View File

@ -2,7 +2,7 @@
#define GUARD_EVENT_SCRIPTS_H
extern const u8 EventScript_Follower[];
extern const u8 EventScript_FollowerHasWetFeet[];
extern const u8 EventScript_FollowerUnhappyToBeWet[];
extern const u8 EventScript_FollowerSplashesAbout[];
extern const u8 EventScript_FollowerLovesYou[];
extern const u8 EventScript_FollowerUnhappyFace[];
@ -12,6 +12,10 @@ extern const u8 EventScript_FollowerEnd[];
extern const u8 EventScript_FollowerSkeptical[];
extern const u8 EventScript_FollowerAppraising[];
extern const u8 EventScript_FollowerHappyWalk[];
extern const u8 EventScript_FollowerAboutToFall[];
extern const u8 EventScript_FollowerTryingToKeepUp[];
extern const u8 EventScript_FollowerIsShivering[];
extern const u8 EventScript_FollowerBurnPainful[];
extern const u8 EnterPokeballMovement[];
extern const u8 EventScript_TestSignpostMsg[];
@ -253,48 +257,48 @@ extern const u8 gTVWhatsNo1InHoennTodayText05[];
extern const u8 gTVWhatsNo1InHoennTodayText06[];
extern const u8 gTVWhatsNo1InHoennTodayText07[];
extern const u8 gTVWhatsNo1InHoennTodayText08[];
extern const u8 TVSecretBaseSecrets_Text_Intro[];
extern const u8 TVSecretBaseSecrets_Text_Intro[];
extern const u8 TVSecretBaseSecrets_Text_WhatWillPlayerDoNext1[];
extern const u8 TVSecretBaseSecrets_Text_WhatWillPlayerDoNext2[];
extern const u8 TVSecretBaseSecrets_Text_TookXStepsBeforeLeaving[];
extern const u8 TVSecretBaseSecrets_Text_BaseFailedToInterestPlayer[];
extern const u8 TVSecretBaseSecrets_Text_PlayerEnjoyedBase[];
extern const u8 TVSecretBaseSecrets_Text_BaseFailedToInterestPlayer[];
extern const u8 TVSecretBaseSecrets_Text_PlayerEnjoyedBase[];
extern const u8 TVSecretBaseSecrets_Text_PlayerHugeFanOfBase[];
extern const u8 TVSecretBaseSecrets_Text_Outro[];
extern const u8 TVSecretBaseSecrets_Text_StoppedMoving1[];
extern const u8 TVSecretBaseSecrets_Text_StoppedMoving2[];
extern const u8 TVSecretBaseSecrets_Text_UsedChair[];
extern const u8 TVSecretBaseSecrets_Text_UsedBalloon[];
extern const u8 TVSecretBaseSecrets_Text_UsedTent[];
extern const u8 TVSecretBaseSecrets_Text_UsedPlant[];
extern const u8 TVSecretBaseSecrets_Text_UsedGoldShield[];
extern const u8 TVSecretBaseSecrets_Text_UsedSilverShield[];
extern const u8 TVSecretBaseSecrets_Text_UsedGlassOrnament[];
extern const u8 TVSecretBaseSecrets_Text_UsedTV[];
extern const u8 TVSecretBaseSecrets_Text_UsedMudBall[];
extern const u8 TVSecretBaseSecrets_Text_UsedBag[];
extern const u8 TVSecretBaseSecrets_Text_UsedCushion[];
extern const u8 TVSecretBaseSecrets_Text_HitCushion[];
extern const u8 TVSecretBaseSecrets_Text_HuggedCushion[];
extern const u8 TVSecretBaseSecrets_Text_BattledWon[];
extern const u8 TVSecretBaseSecrets_Text_BattledLost[];
extern const u8 TVSecretBaseSecrets_Text_DeclinedBattle[];
extern const u8 TVSecretBaseSecrets_Text_UsedPoster[];
extern const u8 TVSecretBaseSecrets_Text_UsedNoteMat[];
extern const u8 TVSecretBaseSecrets_Text_BattledDraw[];
extern const u8 TVSecretBaseSecrets_Text_UsedSpinMat[];
extern const u8 TVSecretBaseSecrets_Text_UsedSandOrnament[];
extern const u8 TVSecretBaseSecrets_Text_UsedDesk[];
extern const u8 TVSecretBaseSecrets_Text_UsedBrick[];
extern const u8 TVSecretBaseSecrets_Text_UsedSolidBoard[];
extern const u8 TVSecretBaseSecrets_Text_UsedFence[];
extern const u8 TVSecretBaseSecrets_Text_UsedGlitterMat[];
extern const u8 TVSecretBaseSecrets_Text_UsedTire[];
extern const u8 TVSecretBaseSecrets_Text_UsedStand[];
extern const u8 TVSecretBaseSecrets_Text_Outro[];
extern const u8 TVSecretBaseSecrets_Text_StoppedMoving1[];
extern const u8 TVSecretBaseSecrets_Text_StoppedMoving2[];
extern const u8 TVSecretBaseSecrets_Text_UsedChair[];
extern const u8 TVSecretBaseSecrets_Text_UsedBalloon[];
extern const u8 TVSecretBaseSecrets_Text_UsedTent[];
extern const u8 TVSecretBaseSecrets_Text_UsedPlant[];
extern const u8 TVSecretBaseSecrets_Text_UsedGoldShield[];
extern const u8 TVSecretBaseSecrets_Text_UsedSilverShield[];
extern const u8 TVSecretBaseSecrets_Text_UsedGlassOrnament[];
extern const u8 TVSecretBaseSecrets_Text_UsedTV[];
extern const u8 TVSecretBaseSecrets_Text_UsedMudBall[];
extern const u8 TVSecretBaseSecrets_Text_UsedBag[];
extern const u8 TVSecretBaseSecrets_Text_UsedCushion[];
extern const u8 TVSecretBaseSecrets_Text_HitCushion[];
extern const u8 TVSecretBaseSecrets_Text_HuggedCushion[];
extern const u8 TVSecretBaseSecrets_Text_BattledWon[];
extern const u8 TVSecretBaseSecrets_Text_BattledLost[];
extern const u8 TVSecretBaseSecrets_Text_DeclinedBattle[];
extern const u8 TVSecretBaseSecrets_Text_UsedPoster[];
extern const u8 TVSecretBaseSecrets_Text_UsedNoteMat[];
extern const u8 TVSecretBaseSecrets_Text_BattledDraw[];
extern const u8 TVSecretBaseSecrets_Text_UsedSpinMat[];
extern const u8 TVSecretBaseSecrets_Text_UsedSandOrnament[];
extern const u8 TVSecretBaseSecrets_Text_UsedDesk[];
extern const u8 TVSecretBaseSecrets_Text_UsedBrick[];
extern const u8 TVSecretBaseSecrets_Text_UsedSolidBoard[];
extern const u8 TVSecretBaseSecrets_Text_UsedFence[];
extern const u8 TVSecretBaseSecrets_Text_UsedGlitterMat[];
extern const u8 TVSecretBaseSecrets_Text_UsedTire[];
extern const u8 TVSecretBaseSecrets_Text_UsedStand[];
extern const u8 TVSecretBaseSecrets_Text_BrokeDoor[];
extern const u8 TVSecretBaseSecrets_Text_UsedDoll[];
extern const u8 TVSecretBaseSecrets_Text_UsedSlide[];
extern const u8 TVSecretBaseSecrets_Text_UsedSlideButDidntGoDown[];
extern const u8 TVSecretBaseSecrets_Text_UsedDoll[];
extern const u8 TVSecretBaseSecrets_Text_UsedSlide[];
extern const u8 TVSecretBaseSecrets_Text_UsedSlideButDidntGoDown[];
extern const u8 TVSecretBaseSecrets_Text_UsedJumpMat[];
extern const u8 gTVSafariFanClubText00[];
extern const u8 gTVSafariFanClubText01[];

View File

@ -210,7 +210,7 @@ struct ObjectEvent
/*0x22*/ union __attribute__((packed)) {
u8 playerCopyableMovement;
struct __attribute__((packed)) {
u16 species:11; // 11 bits for 2048 species
u16 species:11; // 11 bits; 2048 species
u16 form:4;
u16 shiny:1;
} mon;

9
remote_build.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/bash
# Rsync repo to build machine; build and scp the ROM back
set +v
set -e
git push build --force # ssh://merrbot:/home/ubuntu/pokeemerald
ssh merrbot "source /etc/profile.d/devkit-env.sh && cd pokeemerald && git reset --hard && git checkout dexnav && make"
scp merrbot:pokeemerald/pokeemerald.gba romhack.gba

View File

@ -1622,7 +1622,7 @@ static const struct ObjectEventGraphicsInfo * SpeciesToGraphicsInfo(u16 species,
return graphicsInfo->tileTag != 0xFFFF ? &gObjectEventGraphicsInfo_Dusclops : graphicsInfo;
}
// Set graphics & sprite for a follower object event by species
// Set graphics & sprite for a follower object event by species & shininess.
static void FollowerSetGraphics(struct ObjectEvent *objectEvent, u16 species, u8 form, bool8 shiny) {
const struct ObjectEventGraphicsInfo *graphicsInfo = SpeciesToGraphicsInfo(species, form);
objectEvent->graphicsId = OBJ_EVENT_GFX_OW_MON;
@ -1708,51 +1708,65 @@ bool8 ScrFunc_getfolloweraction(struct ScriptContext *ctx) // Essentially a big
u16 value;
u16 species;
u32 behavior;
s16 health_percent;
u8 map_region;
u8 friendship;
const u8 *message_choices[10] = {0};
u8 n_choices = 0;
struct ObjectEvent *objEvent = GetFollowerObject();
struct Pokemon *mon = GetFirstLiveMon();
if (mon == NULL) {
ScriptCall(ctx, EventScript_FollowerLovesYou);
return FALSE;
}
if (!Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType)) // only return to flying if map type is relevant
// If map is not flyable, set the script to jump past the fly check
if (!Overworld_MapTypeAllowsTeleportAndFly(gMapHeader.mapType))
ScriptJump(ctx, EventScript_FollowerEnd);
behavior = MapGridGetMetatileBehaviorAt(objEvent->currentCoords.x, objEvent->currentCoords.y);
species = GetMonData(mon, MON_DATA_SPECIES);
// Puddle splash or wet feet
// 1. Puddle splash or wet feet
if (MetatileBehavior_IsPuddle(behavior) || MetatileBehavior_IsShallowFlowingWater(behavior)) {
if (SpeciesHasType(species, TYPE_FIRE)) {
ScriptCall(ctx, EventScript_FollowerHasWetFeet);
return FALSE;
} else if (SpeciesToGraphicsInfo(species, 0)->tracks) { // if follower is grounded
ScriptCall(ctx, EventScript_FollowerSplashesAbout);
return FALSE;
}
if (SpeciesHasType(species, TYPE_FIRE))
message_choices[n_choices++] = EventScript_FollowerUnhappyToBeWet;
else if (SpeciesToGraphicsInfo(species, 0)->tracks) // if follower is grounded
message_choices[n_choices++] = EventScript_FollowerSplashesAbout;
}
// Weather-based messages
// 2. Weather-based messages
if (GetCurrentWeather() == WEATHER_RAIN || GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM) {
if (SpeciesHasType(species, TYPE_FIRE)) {
ScriptCall(ctx, EventScript_FollowerUnhappyFace);
return FALSE;
} else if (SpeciesHasType(species, TYPE_WATER) || SpeciesHasType(species, TYPE_GRASS)) {
ScriptCall(ctx, EventScript_FollowerHappyRain);
return FALSE;
}
}
// Location-based messages
if (GetMonData(mon, MON_DATA_MET_LOCATION) == GetCurrentRegionMapSectionId()) {
ScriptCall(ctx, EventScript_FollowerMetLocation);
return FALSE;
if (SpeciesHasType(species, TYPE_FIRE))
message_choices[n_choices++] = EventScript_FollowerUnhappyFace;
else if (SpeciesHasType(species, TYPE_WATER) || SpeciesHasType(species, TYPE_GRASS))
message_choices[n_choices++] = EventScript_FollowerHappyRain;
}
// 3. Health & status-based messages
health_percent = mon->hp * 100 / mon->maxHP;
if (health_percent <= 20)
message_choices[n_choices++] = EventScript_FollowerAboutToFall;
else if (health_percent < 50 || mon->status & 0x40) // STATUS1_PARALYSIS
message_choices[n_choices++] = EventScript_FollowerTryingToKeepUp;
// 4. More status messages
if (mon->status & (0x20 | 0x8)) // STATUS1_FREEZE | STATUS1_POISON
message_choices[n_choices++] = EventScript_FollowerIsShivering;
else if (mon->status & 0x10) // STATUS1_BURN
message_choices[n_choices++] = EventScript_FollowerBurnPainful;
// 5. Location-based messages
map_region = GetCurrentRegionMapSectionId(); // defined in region_map_sections.h
if (GetMonData(mon, MON_DATA_MET_LOCATION) == map_region)
message_choices[n_choices++] = EventScript_FollowerMetLocation;
// 6. Friendship-based messages
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP);
if (friendship <= 80)
ScriptCall(ctx, EventScript_FollowerSkeptical);
message_choices[n_choices++] = EventScript_FollowerSkeptical;
else if (friendship <= 170)
ScriptCall(ctx, EventScript_FollowerAppraising);
message_choices[n_choices++] = EventScript_FollowerAppraising;
else if (friendship < 255)
ScriptCall(ctx, EventScript_FollowerHappyWalk);
message_choices[n_choices++] = EventScript_FollowerHappyWalk;
else // Max friendship
ScriptCall(ctx, EventScript_FollowerLovesYou);
message_choices[n_choices++] = EventScript_FollowerLovesYou;
if (!n_choices)
ScriptCall(ctx, EventScript_FollowerLovesYou); // Default in case of no choices
else
ScriptCall(ctx, message_choices[Random() % min(n_choices, ARRAY_COUNT(message_choices))]);
return FALSE;
}

View File

@ -1415,7 +1415,7 @@ void FieldCB_FallWarpExit(void)
Overworld_PlaySpecialMapMusic();
WarpFadeInScreen();
ScriptContext2_Enable();
FreezeObjectEvents();
FreezeObjectEvents(); // TODO: How does this interact with follower pokemon?
CreateTask(Task_FallWarpFieldEffect, 0);
gFieldCallback = NULL;
}
@ -1567,7 +1567,7 @@ static void Task_EscalatorWarpOut(u8 taskId)
static bool8 EscalatorWarpOut_Init(struct Task *task)
{
FreezeObjectEvents();
FreezeObjectEvents(); // TODO: Follower pokemon interaction
CameraObjectReset2();
StartEscalator(task->tGoingUp);
task->tState++;
@ -1945,7 +1945,7 @@ static void Task_LavaridgeGymB1FWarp(u8 taskId)
static bool8 LavaridgeGymB1FWarpEffect_Init(struct Task *task, struct ObjectEvent *objectEvent, struct Sprite *sprite)
{
FreezeObjectEvents();
FreezeObjectEvents(); // TODO: Follower pokemon interaction
CameraObjectReset2();
SetCameraPanningCallback(NULL);
gPlayerAvatar.preventStep = TRUE;
@ -2062,7 +2062,7 @@ static void Task_LavaridgeGymB1FWarpExit(u8 taskId)
static bool8 LavaridgeGymB1FWarpExitEffect_Init(struct Task *task, struct ObjectEvent *objectEvent, struct Sprite *sprite)
{
CameraObjectReset2();
FreezeObjectEvents();
FreezeObjectEvents(); // TODO: Follower pokemon interaction
gPlayerAvatar.preventStep = TRUE;
objectEvent->invisible = TRUE;
task->data[0]++;
@ -2138,7 +2138,7 @@ static void Task_LavaridgeGym1FWarp(u8 taskId)
static bool8 LavaridgeGym1FWarpEffect_Init(struct Task *task, struct ObjectEvent *objectEvent, struct Sprite *sprite)
{
FreezeObjectEvents();
FreezeObjectEvents(); // TODO: Follower pokemon interaction
CameraObjectReset2();
gPlayerAvatar.preventStep = TRUE;
objectEvent->fixedPriority = 1;
@ -2227,7 +2227,7 @@ void SpriteCB_AshPuff(struct Sprite *sprite)
void StartEscapeRopeFieldEffect(void)
{
ScriptContext2_Enable();
FreezeObjectEvents();
FreezeObjectEvents(); // TODO: Follower pokemon interaction
CreateTask(Task_EscapeRopeWarpOut, 80);
}
@ -3071,7 +3071,7 @@ u8 FldEff_RayquazaSpotlight(void)
struct Sprite *sprite = &gSprites[spriteId];
sprite->oam.priority = 1;
sprite->oam.paletteNum = 4; // TODO: What palette should this Raquaza use?
sprite->oam.paletteNum = 4; // TODO: What (dynamic) palette should this Raquaza use?
sprite->data[0] = 0;
sprite->data[1] = 0;
sprite->data[2] = 0;

View File

@ -682,6 +682,7 @@ static void Task_DoDoorWarp(u8 taskId)
struct Task *task = &gTasks[taskId];
s16 *x = &task->data[2];
s16 *y = &task->data[3];
struct ObjectEvent *followerObject = GetFollowerObject();
switch (task->tState)
{
@ -689,6 +690,15 @@ static void Task_DoDoorWarp(u8 taskId)
FreezeObjectEvents();
PlayerGetDestCoords(x, y);
PlaySE(GetDoorSoundEffect(*x, *y - 1));
if (followerObject) { // Put follower into pokeball
// TODO: ClearObjectEventMovement (
followerObject->singleMovementActive = 0;
ObjectEventClearHeldMovement(followerObject);
gSprites[followerObject->spriteId].data[1] = 0;
gSprites[followerObject->spriteId].animCmdIndex = 0; // Needed because of weird animCmdIndex stuff
// )
ObjectEventSetHeldMovement(followerObject, MOVEMENT_ACTION_ENTER_POKEBALL);
}
task->data[1] = FieldAnimateDoorOpen(*x, *y - 1);
task->tState = 1;
break;