Put random birds on the roofs of your towns! - Pawkkie/Team-Aquas-Asset-Repo GitHub Wiki

Very specific tutorial incoming: having bird Pokémon that randomly spawn on roofs is a stupidly easy way to add some life to your nice little towns!

Note: This tutorial uses Poryscript. If you don't use Poryscript, you shouldjust paste the code in the Poryscript Playground to convert it back to vanilla scripting!

Solution

In Porymap

Creating the object events

Let's start by opening our map and adding several new object events, each using one of the temp flags FLAG_TEMP_X (of course, make sure they're not using a temp flag that you're already using in your map's scripts), and placing them on roofs. Each object event should look a bit like this:

image

Note: If you're using the Followers feature that should very soon be released in Expansion, OBJ_EVENT_GFX_SPECIES(TAILLOW) will work on its own, no need for you to add new graphics.

Collisions

To have the birds display properly and to avoid them escaping from their roof (we wouldn't want that!), make sure your house's collision looks like this:

image

The 4 tiles correspond to the Z coordinate of the birds. That way, the birds will be free to roam on all the 4 tiles, but will not step on the regular 3 tiles.

Metatile layers

Make sure those 4 roof metatiles look like this in the tileset editor:

image

Since they're drawn on the middle layer and not on the top layer, the birds will not be hidden by the roof tiles!

Adding the Bird Behaviour

To add the behaviour for the birds you have two options:

  • In your town's scripts file - This method allows you to configure the behaviour more precicely per map, creating different behaviours in different areas.

  • In your src folder (C) - This method allows you to configure the same behaviour and trigger it more easily from a special/directly from C in RunOnLoadMapScript.

In your town's scripts file

Now let's add something like this to the mapscripts (or edit the MAP_SCRIPT_ON_LOAD if you already have one):

mapscripts YourMap_MapScripts {
+    MAP_SCRIPT_ON_LOAD {
+        // Set all the flags used by the birds so that they start out all hidden
+        setflag(FLAG_TEMP_1)
+        setflag(FLAG_TEMP_2)
+        setflag(FLAG_TEMP_3)
+        setflag(FLAG_TEMP_4)

+        random(4)
+        switch(var(VAR_RESULT)) {
+            case 0:
+                // This will display only the bird(s) using FLAG_TEMP_1
+                clearflag(FLAG_TEMP_1)
+            case 1:
+                // This will display only the bird(s) using FLAG_TEMP_2
+                clearflag(FLAG_TEMP_2)
+            case 2:
+                clearflag(FLAG_TEMP_4)
+            case 3:
+                // Adding a little variety by having several birds at once in this case
+                clearflag(FLAG_TEMP_1)
+                clearflag(FLAG_TEMP_3)
+        }
}

Once you've added bird behaviours like this to each of your maps, you're finished.

Alternatively:

In your src folder (C)

trainer_types.h

In include/constants/trainer_types.h add an entry that will identify your birds. For example:

+ #define TRAINER_TYPE_BIRD               4
  • NOTE: ADD THIS TRAINER TYPE TO YOUR BIRD OBJECT EVENTS THAT YOU CREATED ABOVE.

field_specials.c

Once you've done so, include that header in src/field_specials.c like so:

+ #include "constants/trainer_types.h"

As well as these files (used in the upcoming function):

+ #include "battle_pyramid.h"
+ #include "trainer_hill.h"

and then add this function to the bottom of the file:

#define MAX_BIRD_SPOTS 10

static const u8 randomBirdArray[MAX_BIRD_SPOTS + 1][4] =
{
    [0]  = { 0, 0, 0, 0 },
    [1]  = { 1, 0, 1, 0 },
    [2]  = { 1, 0, 1, 0 },
    [3]  = { 2, 1, 1, 0 },
    [4]  = { 2, 1, 1, 0 },
    [5]  = { 2, 1, 2, 0 },
    [6]  = { 3, 2, 2, 1 },
    [7]  = { 3, 2, 3, 2 },
    [8]  = { 4, 2, 3, 2 },
    [9]  = { 4, 3, 3, 2 },
    [10] = { 4, 3, 4, 3 },
};

void SetRoofBirds(void)
{
    u8 i;
    u8 objectEventCount;
    struct ObjectEventTemplate *template;
    u8 birdIndexes[MAX_BIRD_SPOTS + 1] = {0};
    u32 birdCount = 0;
    if (gMapHeader.events != NULL)
    {
        if (InBattlePyramid())
           objectEventCount = GetNumBattlePyramidObjectEvents();
        else if (InTrainerHill())
           objectEventCount = HILL_TRAINERS_PER_FLOOR;
        else
           objectEventCount = gMapHeader.events->objectEventCount;

        for (i = 0; i < objectEventCount; i++)
        {
            template = &gSaveBlock1Ptr->objectEventTemplates[i];
            if (template->trainerType == TRAINER_TYPE_BIRD && birdCount <= MAX_BIRD_SPOTS)
            {
                birdIndexes[birdCount] = i; // Store index of bird
                birdCount++;
            }
        }
    
        if (birdCount > 0)
        {
            u8 visibleCount = randomBirdArray[birdCount][Random() % 4];
            Shuffle8(birdIndexes, birdCount);
            // Hide random birds until only `visibleCount` remain
            for (i = visibleCount; i < birdCount; i++)
            {
                u8 birdIndex = birdIndexes[i];
                template = &gSaveBlock1Ptr->objectEventTemplates[birdIndex];
                FlagSet(template->flagId);
            }
        }
    }
}

This function identifies object events on the current map by searching for the trainerType: TRAINER_TYPE_BIRD. It then makes a number of those object events visible according to the behaviour defined in randomBirdArray, where the array key is the number of bird spots on the map and the numbers are the amount of birds to make visible (chooses one of the numbers at random).

To make the function run, you have two options:

  • Turn it into a special by adding def_special SetRoofBirds to your data/specials.inc file and calling it within the map script as part of MAP_SCRIPT_ON_LOAD (similar to the poryscript above).

  • Call it directly in RunOnLoadMapScript(void) in scr/script.c, which will make it run on every single map by default (You'll have to add extern void SetRoofBirds(void); just above).

Result

And that's it!

Birbs

Now that your birds are happily hopping around, you can customize them however you want (replacing them with Zubat at night, adding a shiny bird with a very slim chance of showing, etc). Now go, and spread the bird love in your own hack!