Simple Modifications Directory Page 20

Started by Hiroshi Sotomura December 30th, 2018 2:48 PM
  • 249080 views
  • 477 replies

セケツ

ポケハック初心者

Non-binary
Seen 2 Hours Ago
Posted August 7th, 2023
57 posts
6.7 Years
[PokeEmerald] A Personal Fix for the 16x16 doors anim tutorial

Just a personal & unproven discovery which posted here for anyone that needs it. Although I don't know whether it is WRONG or NOT until NOW. So please forgive my ignorance.
So, Several days ago, I accidentally found there was something wrong with the "16x16 door animation tutorial" (which enables PokeEmerald itself to display the 16x16 doors animations from PokeFirered) for the updated PokeEmerald, which was distributed here long time ago.
The Original Tutorial is here for anyone not knowing what I'm talking about:
https://github.com/scizz/darkfire/commit/b666ad85d3e22ac134fd24ce0bfe84f0e1e4a773
Well, I did nothing but just copied the original lines provided by that tutorial. And here is a picture of what had happened: When a single door was open or closed, only 1/4 of its original animation graphic was shown properly. And, moreover, the other part became glitchly pixels, for sure
Spoiler:


I had checked the code over and over again, and made sure there was no mistake in my copy & paste process. And the animation of my newly added door was nothing but a graphic file grabbed directly & straightly from the original PokeFirered.
Luckily, I was happened to be lucky enough to find one possible solution. For anyone interested in it, here it is:
First, after implementing the codes from the tutorial, jump to the function named "CopyDoorTilesToVram", and change the lines below:

From:
Spoiler:
CpuFastSet(gfx->tiles + frame->offset, (void *)(VRAM + 0x7F80), 0x20);

to:

Spoiler:
CpuFastSet(gfx->tiles + frame->offset, (void *)(VRAM + TILE_OFFSET_4BPP(DOOR_TILE_START_SIZE_3), 4 * TILE_SIZE_4BPP);


Then, add one completely new definition before that function itself:

Spoiler:
#define DOOR_TILE_START_SIZE_3 (NUM_TITLES_TOTAL - 4)


Here is a picture of what it looked like after being fixed:

Spoiler:


And that's pretty much of it, I wonder whether anyone here trying port the door animation of FRLG has met the same problem. Also, if there is anything wrong, please forgive my ignorance & leave a message to me.
Thanks.
"Many years later, as he standed in front of the hall of fame alone, the boy coming from Littleroot Town was to remember that distant summer's day when Prof. Birch gave him a little Mudkip."
Seen 1 Day Ago
Posted 1 Day Ago
362 posts
5.9 Years
Run Scripts on Door Entry (Emerald)

This modification allows you to use trigger events with animated doors in addition to warp events.



Modification:
Spoiler:
1. Open src/field_screen_effect.h and add the following functions:
static void Task_DoDoorScript(u8 taskId)
{
    struct Task *task = &gTasks[taskId];
    u8 objEventId = GetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_PLAYER, 0, 0);
    s16 *x = &task->data[2];
    s16 *y = &task->data[3];

    switch (task->tState)
    {
    case 0: //open door
        FreezeObjectEvents();
        PlayerGetDestCoords(x, y);
        PlaySE(GetDoorSoundEffect(*x, *y - 1));
        task->data[1] = FieldAnimateDoorOpen(*x, *y - 1);
        task->tState++;
        break;
    case 1: //wait for door to open, then walk into door
        if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
        {
            ObjectEventClearHeldMovementIfActive(&gObjectEvents[objEventId]);
            ObjectEventSetHeldMovement(&gObjectEvents[objEventId], MOVEMENT_ACTION_WALK_NORMAL_UP);
            task->tState++;
        }
        break;
    case 2: //wait for movement, then close door
        if (IsPlayerStandingStill())
        {
            task->data[1] = FieldAnimateDoorClose(*x, *y - 1);
            ObjectEventClearHeldMovementIfFinished(&gObjectEvents[objEventId]);
            SetPlayerVisibility(FALSE);
            task->tState++;
        }
        break;
    case 3: //wait for door to close, then start script
        if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
        {
            ScriptContext_Enable();
            task->tState++;
        }
	break;
    case 4: //wait for script to complete, then open door
        if(ScriptContext_IsEnabled() == FALSE)
        {
            LockPlayerFieldControls();
            task->data[1] = FieldAnimateDoorOpen(*x, *y - 1);
            task->tState++;
        }
	    break;
    case 5: //wait for door to reopen, then exit
        if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
	    {
            task->tState = 0;
	        task->func = Task_ExitDoor;
	    }
        break;
    }
}

void DoDoorScript(void)
{
    LockPlayerFieldControls();
    CreateTask(Task_DoDoorScript, 10);
}
2. Add the following declaration in include/field_screen_effect.h
void DoDoorScript(void);
3. Add this code block here here in src/field_control_avatar.c:
         if (MetatileBehavior_IsWarpDoor(metatileBehavior) == TRUE)
         {
            if (TryStartCoordEventScript(position) == TRUE)
            {
                DoDoorScript();
                return TRUE;
            }

             warpEventId = GetWarpEventAtMapPosition(&gMapHeader, position);

And that's it.
Here are all the changes in diff format:
Spoiler:

diff --git a/include/field_screen_effect.h b/include/field_screen_effect.h
index 6dc9b077d..fb71af838 100644
--- a/include/field_screen_effect.h
+++ b/include/field_screen_effect.h
@@ -24,6 +24,7 @@ void DoWarp(void);
 void DoDiveWarp(void);
 void DoWhiteFadeWarp(void);
 void DoDoorWarp(void);
+void DoDoorScript(void);
 void DoFallWarp(void);
 void DoEscalatorWarp(u8 metatileBehavior);
 void DoLavaridgeGymB1FWarp(void);
diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c
index 3a0c97e8c..ad95a2d9b 100644
--- a/src/field_control_avatar.c
+++ b/src/field_control_avatar.c
@@ -845,6 +845,12 @@ static bool8 TryDoorWarp(struct MapPosition *position, u16 metatileBehavior, u8
 
         if (MetatileBehavior_IsWarpDoor(metatileBehavior) == TRUE)
         {
+            if (TryStartCoordEventScript(position) == TRUE)
+            {
+                DoDoorScript();
+                return TRUE;
+            }
+
             warpEventId = GetWarpEventAtMapPosition(&gMapHeader, position);
             if (warpEventId != WARP_ID_NONE && IsWarpMetatileBehavior(metatileBehavior) == TRUE)
             {
diff --git a/src/field_screen_effect.c b/src/field_screen_effect.c
index 616444116..50c8e4669 100644
--- a/src/field_screen_effect.c
+++ b/src/field_screen_effect.c
@@ -44,6 +44,7 @@ static bool32 WaitForWeatherFadeIn(void);
 static void Task_SpinEnterWarp(u8 taskId);
 static void Task_WarpAndLoadMap(u8 taskId);
 static void Task_DoDoorWarp(u8 taskId);
+static void Task_DoDoorScript(u8 taskId);
 static void Task_EnableScriptAfterMusicFade(u8 taskId);
 
 // data[0] is used universally by tasks in this file as a state for switches
@@ -519,6 +520,12 @@ void DoDoorWarp(void)
     CreateTask(Task_DoDoorWarp, 10);
 }
 
+void DoDoorScript(void)
+{
+    LockPlayerFieldControls();
+    CreateTask(Task_DoDoorScript, 10);
+}
+
 void DoFallWarp(void)
 {
     DoDiveWarp();
@@ -727,6 +734,64 @@ static void Task_DoDoorWarp(u8 taskId)
     }
 }
 
+static void Task_DoDoorScript(u8 taskId)
+{
+    struct Task *task = &gTasks[taskId];
+    u8 objEventId = GetObjectEventIdByLocalIdAndMap(OBJ_EVENT_ID_PLAYER, 0, 0);
+    s16 *x = &task->data[2];
+    s16 *y = &task->data[3];
+
+    switch (task->tState)
+    {
+    case 0: //open door
+        FreezeObjectEvents();
+        PlayerGetDestCoords(x, y);
+        PlaySE(GetDoorSoundEffect(*x, *y - 1));
+        task->data[1] = FieldAnimateDoorOpen(*x, *y - 1);
+        task->tState++;
+        break;
+    case 1: //wait for door to open, then walk into door
+        if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
+        {
+            ObjectEventClearHeldMovementIfActive(&gObjectEvents[objEventId]);
+            ObjectEventSetHeldMovement(&gObjectEvents[objEventId], MOVEMENT_ACTION_WALK_NORMAL_UP);
+            task->tState++;
+        }
+        break;
+    case 2: //wait for movement, then close door
+        if (IsPlayerStandingStill())
+        {
+            task->data[1] = FieldAnimateDoorClose(*x, *y - 1);
+            ObjectEventClearHeldMovementIfFinished(&gObjectEvents[objEventId]);
+            SetPlayerVisibility(FALSE);
+            task->tState++;
+        }
+        break;
+    case 3: //wait for door to close, then start script
+        if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
+        {
+            ScriptContext_Enable();
+            task->tState++;
+        }
+        break;
+    case 4: //wait for script to complete, then open door
+        if(ScriptContext_IsEnabled() == FALSE)
+        {
+            LockPlayerFieldControls();
+            task->data[1] = FieldAnimateDoorOpen(*x, *y - 1);
+            task->tState++;
+        }
+        break;
+    case 5: //wait for door to reopen, then exit
+        if (task->data[1] < 0 || gTasks[task->data[1]].isActive != TRUE)
+        {
+            task->tState = 0;
+            task->func = Task_ExitDoor;
+        }
+        break;
+    }
+}
+
 static void Task_DoContestHallWarp(u8 taskId)
 {
     struct Task *task = &gTasks[taskId];



Usage:
Spoiler:
Simply place a trigger event on top of a metatile with the MB_ANIMATED_DOOR metatile behavior.
If the trigger's variable's value matches the trigger's var value field, the player will be able the enter the door. Otherwise the door won't open.

The script execution starts as soon as the door animation starts. You probably want to start your script with a waitstate command to make it start running only after the door has closed.

After the script has ended, the player will exit the door.

Here's the script used in the example gif above:
Example_Script::
waitstate      @ Wait for player to enter the door
msgbox Text_Example MSGBOX_AUTOCLOSE
end

Lunos

Random Uruguayan User

Male
Montevideo (Uruguay)
Seen 3 Hours Ago
Posted 2 Weeks Ago
3,040 posts
15 Years
Enable the usage of the SPECIES_UNOWN_(LETTER) constants for party creation [Em]
Earlier today, someone in Pret's Discord server talked about how they wanted to assign specific Unown letter forms to a trainer, and I took a shot at it because I was bored.
I didn't put this on a branch because I did it very quickly and it's simple to do too. It's not worth the hassle.
Like a handful of things in the game, the Unown letters are tied to the Pokémon's Personality ID.
With that in mind and focusing on the chunk of
CreateBoxMon
that assigns the PID to a Pokémon it's generating, we can easily mod that PID to match whatever Unown form we want based on conditions.
Here's a code diff.
diff --git a/src/pokemon.c b/src/pokemon.c
index 7bc9f3bee3..d6dc7d8565 100644
--- a/src/pokemon.c
+++ b/src/pokemon.c
@@ -2195,6 +2195,24 @@ void CreateMon(struct Pokemon *mon, u16 species, u8 level, u8 fixedIV, u8 hasFix
     CalculateMonStats(mon);
 }

+static u32 UpdatePIDForUnownForms(u16 species, u32 ogPersonalityValue)
+{
+    u32 newPersonalityValue;
+    u32 actualLetter;
+    u16 nature, gender;
+
+    do
+    {
+        newPersonalityValue = Random32();
+        actualLetter = GET_UNOWN_LETTER(newPersonalityValue);
+    }
+    while (actualLetter != (species - NUM_SPECIES)
+        && nature != GetNatureFromPersonality(ogPersonalityValue)
+        && gender != GetGenderFromSpeciesAndPersonality(SPECIES_UNOWN, ogPersonalityValue));
+
+    return newPersonalityValue;
+}
+
 void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV, u8 hasFixedPersonality, u32 fixedPersonality, u8 otIdType, u32 fixedOtId)
 {
     u8 speciesName[POKEMON_NAME_LENGTH + 1];
@@ -2205,9 +2223,21 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV,
     ZeroBoxMonData(boxMon);

     if (hasFixedPersonality)
-        personality = fixedPersonality;
+    {
+        if (species >= SPECIES_UNOWN_B && species <= SPECIES_UNOWN_QMARK)
+        {
+            personality = UpdatePIDForUnownForms(species, fixedPersonality);
+            species = SPECIES_UNOWN;
+        }
+        else
+        {
+            personality = fixedPersonality;
+        }
+    }
     else
+    {
         personality = Random32();
+    }

     SetBoxMonData(boxMon, MON_DATA_PERSONALITY, &personality);


With these changes, you can easily drop in a
SPECIES_UNOWN_F
in Youngster Calvin's party data, or a
SPECIES_UNOWN_X
in the wild encounters list of Route 101, and they'll work as expected.


Note: This is specifically aimed towards people who don't use the Pokeemerald-expansion, which already separated each Unown letter into their own individual species.
Note2: Functionally speaking, this makes
CreateMonWithGenderNatureLetter
obsolete.


And that's pretty much it.