• Our software update is now concluded. You will need to reset your password to log in. In order to do this, you will have to click "Log in" in the top right corner and then "Forgot your password?".
  • Welcome to PokéCommunity! Register now and join one of the best fan communities on the 'net to talk Pokémon and more! We are not affiliated with The Pokémon Company or Nintendo.

Converting Field Moves to Hold Items

19
Posts
4
Years
  • NOTICE: OUTDATED (will edit when I have the time/energy just getting back into working with decomp gen 3. been out a few months, so I will be rusty.)

    Since even game freak have stepped away from HMs I thought it a bad idea to include them in my hack, especially with hoenn having 8 of them. It is the first full feature rather than small quality of life change that I have implemented and it turned out well. This seems like a good item to write my first forum guide with seeing as it is something most modern hacks look for design-wise. so here we are. if you want the code of the feature you can find it on my github (can't post links yet as I haven't made enough posts, but you can find me at LiaBeryl with the same profile picture). now on to the guide.

    we'll start with making the items that we will be using. they won't have any function linked to them through the item scripts so making them is really easy. just go into \include\constants\items.h and at the bottom right before
    Code:
    #define ITEMS_COUNT 377
    place the item IDs. you can name them anything, this is what I went with.
    Code:
    #define ITEM_CUT 377
    #define ITEM_ROCK_SMASH 378
    #define ITEM_STRENGTH 379
    #define ITEM_SURF 380
    #define ITEM_FLASH 381
    #define ITEM_FLY 382
    #define ITEM_DIVE 383
    #define ITEM_WATERFALL 384
    #define ITEM_TELEPORT 385
    #define ITEM_DIG 386
    #define ITEM_SECRET_POWER 387
    #define ITEM_MILK_DRINK 388
    #define ITEM_SOFT_BOILED 389
    #define ITEM_SWEET_SCENT 390
    and finally for this file, update the item count (in this case 391).
    this file is just declaring how to convert item names from readable to u16(unsigned 16 bit integer) which is the type you use when using items as variables.

    next at the bottom of \src\data\items.h place the item definitions. you can see the anatomy of the struct in the empty entry at the top. I'll place that here while I go through it.
    Code:
    [ITEM_NONE] =
        {
            .name = _("????????"),
            .itemId = ITEM_NONE,
            .price = 0,
            .description = sDummyDesc,
            .pocket = POCKET_ITEMS,
            .type = 4,
            .fieldUseFunc = ItemUseOutOfBattle_CannotUse,
            .secondaryId = 0,
        },

    .name is the string it shows when scrolling through the bag. you will get an error if this is too long

    .itemId is the link to the constants file, just giving it access to the number you assigned it. I don't <i>think</i> that this needs to be the same as the bracketed name of the entry, but please don't have them different. that can only cause of headache for you and possibly the compiler.

    .price is the buy price. sell price is half of this. if you want it to be unsellable just set it to 0

    .pocket is the pocket it will go in. this is an enumerator just the same as the item ids.

    .type I have no idea what this is. I kept it at default and it didn't break so I'm fine with that.

    .fieldUseFunc the function called when you select the use option from the bag menu.

    .secondaryId I have no idea what this is. same as type, the default didn't break, so I don't need to.

    now for the optional ones you will see throughout the array

    .importance a boolean (0/1, false/true). 0 acts as a normal item; 1 can only have one, can't give to pokemon, and can't toss

    .unk19 this is on all of the key items but I don't know what it does

    .battleUsage no idea. I think it is a key for a switch called before the item is used in battle.

    .battleUseFunc same as itemUseFunc just for when in battle. don't know though, haven't tested


    now that we understand what we are working with we can put in our items. they are really simple. just copy ITEM_NONE and replace the name id and description.
    here is cut, I used the same format for all the other items we declared (I linked them all to the same description for the scope of the guide)
    Code:
    [ITEM_CUT] =
        {
            .name = _("CUT"),
            .itemId = ITEM_CUT,
            .price = 0,
            .description = sFItemDesc,
            .pocket = POCKET_ITEMS,
            .type = 4,
            .fieldUseFunc = ItemUseOutOfBattle_CannotUse,
            .secondaryId = 0,
        },

    next we go to \src\data\text\item_descriptions.h and fill out that description.
    here is mine:
    Code:
    static const u8 sFItemDesc[] = _(
    	"lets the pokemon\n"
    	"holding it change\n"
    	"the world");
    you can probably figure it out without me explaining. it won't throw an error if your lines are too long this time, it will just look bad.

    and next we go to \src\data\item_icon_table.h to link the items to their icon and it's palette. don't worry, this is the last section on items. we get to code soon.
    here is cut as an example of an entry again.
    Code:
    [ITEM_CUT] = {gItemIcon_OldSeaMap, gItemIconPalette_OldSeaMap},
    I just used the old sea map icon as a placeholder for the guide. so far all I have done is use the pre-existing icons and palette swapped them through this table, so I don't know where to find the game data arrays for these. sorry.

    now if you cheat in the items with an item ball next to your house or some other method, you will have them show up in your bag with icon and description and can give them to a pokemon. but they don't do anything yet. that's what we will work on next. it comes in 3 parts: the backend scripts, the party menu interaction, and the player controller interaction. most of the field moves are called through script pointers on the map objects you find in the world, so once we finish the back end, we just need to worry about putting the buttons in the party menu and then surf dive and waterfall since those involve player movement and not just map events or single call functions.

    we'll start with the the two frontend bits, as they are easier (seeing as they are C and not Assembly with kindly macros).

    we start in \include\field_player_avatar.h. rip out the line declaring the function PartyHasMonWithSurf(void). replace it with
    Code:
    bool8 PartyHasMonWithWaterEvent(u16 fMove);
    this is just a better function. which we will define in the source file for this header in \src\field_player_avatar.c the reason it is better is because it contains the check for surf, but it also has checks for dive and waterfall. something that the game does not have in the front end. it just has a badge check there, which is fine. but I don't like the badge restriction, so I put this in. here is the definition:
    Code:
    bool8 PartyHasMonWithWaterEvent(u16 fMove)
    {
        u8 i;
    
    	switch(fMove)
    	{
    		case(MOVE_SURF):
    			if (!TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING))
    			{
    				for (i = 0; i < PARTY_SIZE; i++)
    				{
    					if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == SPECIES_NONE)
    						break;
    					if (MonKnowsMove(&gPlayerParty[i], MOVE_SURF)||GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) == ITEM_SURF)
    						return TRUE;
    				}
    			}
    			break;
    		case(MOVE_DIVE):
    			if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING))
    			{
    				for (i = 0; i < PARTY_SIZE; i++)
    				{
    					if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == SPECIES_NONE)
    						break;
    					if (MonKnowsMove(&gPlayerParty[i], MOVE_DIVE) || GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) == ITEM_DIVE)
    					return TRUE;
    				}
    				
    			}
    			break;
    		case(MOVE_WATERFALL):
    			if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING))
    			{
    				for (i = 0; i < PARTY_SIZE; i++)
    				{
    					if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == SPECIES_NONE)
    						break;
    					if (MonKnowsMove(&gPlayerParty[i], MOVE_WATERFALL) || GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) == ITEM_WATERFALL)
    					return TRUE;
    				}
    				
    			}
    	}
        return FALSE;
    }
    if you understand c style languages and have used this codebase for a little bit I'm pretty sure you can read this without me saying anything. I'll explain the basic concept in case you don't want to read it though. It's a switch with a case for each of the 3 water field moves and then for each of them, it checks for each of the players pokemon if it either knows the requisite move, or is holding the requisite item. this could have been streamlined if bothered to find a place to put a struct to equate the moves to the items, but I didn't know where to put it, so I didn't bother. It's just one more conditional so it doesn't really matter.

    next we go to the spot where I probably should have made that struct: \src\party_menu.c. you will need to find sFieldMoves and copy it just below itself replacing all the moves with the items (keep the terminator the same, yes it looks ugly but this means we can use the same functions for them both).
    Code:
    static const u16 sFieldItems[] =																						
    {																															
        ITEM_CUT, ITEM_FLASH, ITEM_ROCK_SMASH, ITEM_STRENGTH, ITEM_SURF, ITEM_FLY, ITEM_DIVE, ITEM_WATERFALL, ITEM_TELEPORT,	
        ITEM_DIG, ITEM_SECRET_POWER, ITEM_MILK_DRINK, ITEM_SOFT_BOILED, ITEM_SWEET_SCENT, FIELD_MOVE_TERMINATOR				
    };

    next look for the function CreateActionList. switch around the for loops (just cut and swap the whole loop declaration). and then after the inside loop, copy the check for moves, but replace sFieldMoves with sFieldItems and the check for the pokemon's moves with a check for it's held item. here is what the function should look like when you are done with it:
    Code:
    static void CreateActionList(struct Pokemon *mons, u8 slotId)
    {
        u8 i, j;
        gUnknown_0203CEC4->listSize = 0;
        AppendToList(gUnknown_0203CEC4->actions, &gUnknown_0203CEC4->listSize, MENU_SUMMARY);
        for (j = 0; sFieldMoves[j] != FIELD_MOVE_TERMINATOR; j++)
        {
            for (i = 0; i < MAX_MON_MOVES; i++)
            {
                if (GetMonData(&mons[slotId], i + MON_DATA_MOVE1) == sFieldMoves[j] )
                {
                    AppendToList(gUnknown_0203CEC4->actions, &gUnknown_0203CEC4->listSize, j + MENU_FIELD_MOVES);
                    break;
                }
            }
    		if(GetMonData(&mons[slotId], MON_DATA_HELD_ITEM) == sFieldItems[j])
    		{
    			AppendToList(gUnknown_0203CEC4->actions, &gUnknown_0203CEC4->listSize, j + MENU_FIELD_MOVES);
    		}
        }
    
        if (!InBattlePike())
        {
            if (GetMonData(&mons[1], MON_DATA_SPECIES) != SPECIES_NONE)
                AppendToList(gUnknown_0203CEC4->actions, &gUnknown_0203CEC4->listSize, MENU_SWITCH);
            if (ItemIsMail(GetMonData(&mons[slotId], MON_DATA_HELD_ITEM)))
                AppendToList(gUnknown_0203CEC4->actions, &gUnknown_0203CEC4->listSize, MENU_MAIL);
            else
                AppendToList(gUnknown_0203CEC4->actions, &gUnknown_0203CEC4->listSize, MENU_ITEM);
        }
        AppendToList(gUnknown_0203CEC4->actions, &gUnknown_0203CEC4->listSize, MENU_CANCEL1);
    }

    last thing for this file is there is a referance to the surf check function we killed. we need to replace that with the one we made. just search for the old function and replace it (it's in SetUpFieldMove_Surf). make sure to add the parameter for surf, here is what it should look like:
    Code:
    PartyHasMonWithWaterEvent(MOVE_SURF)

    now for the last bit of fontend. go to \src\field_control_avatar.c change the surf check to a water event check and add the checks to dive and waterfall. you want *getInteractedWaterScript, and TrySetupDiveDownScript. I would not add it to TrySetupDiveEmergeScript as the player could throw away their dive item while down there and soft lock themselves. add the checks on the same line as the badge checks.

    now for the backend. we will start by adding the thing that will make it so we don't need to touch almost any assembly. to do that we need a variable to act as a switch condition. go to \include\constants\vars.h and replace some unused non-temp non-special variable with the variable name you plan on using, in this case, VAR_ITEMMOVECACHE.

    next in \data\specials.inc add def_special Special_CheckPartyFieldEvent anywhere in the list. now we give a definition to it. go to \src\field_specials.c and anywhere add
    Code:
    void Special_CheckPartyFieldEvent(void)
    {
    	u8 i;
    	u16 moveCache, itemCache;
    	moveCache = VarGet(VAR_ITEMMOVECACHE);
    	
    	switch(moveCache)
    	{
    		case(MOVE_CUT):
    			itemCache = ITEM_CUT;
    			break;
    		case(MOVE_ROCK_SMASH):
    			itemCache = ITEM_ROCK_SMASH;
    			break;
    		case(MOVE_STRENGTH):
    			itemCache = ITEM_STRENGTH;
    			break;
    		case(MOVE_WATERFALL):
    			itemCache = ITEM_WATERFALL;
    			break;
    		case(MOVE_DIVE):
    			itemCache = ITEM_DIVE;
    			break;
    		case(MOVE_SURF):
    			itemCache = ITEM_SURF;
    	}
    	
    	for(i=0; i<PARTY_SIZE; i++)
    	{
    		if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == SPECIES_NONE)
    		{
                break;
    		}
            if (GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM) == itemCache)
    		{
    			VarSet(VAR_RESULT, i);
    			return;
    		}
    		   
    	}
    	VarSet(VAR_RESULT, 6);
    	
    	if(VarGet(VAR_RESULT)==6)
    	{
    		for(i=0; i<PARTY_SIZE; i++)
    		{
    			if (GetMonData(&gPlayerParty[i], MON_DATA_SPECIES) == SPECIES_NONE)
    			{
                   return;
    			}
    			if (MonKnowsMove(&gPlayerParty[i],moveCache))
    			{
    				VarSet(VAR_RESULT, i);
    			}
    		   
    		}
    	}		
    }
    it's exactly the same as we did in the check for water events, just using the output of the script command checkpartymove, as that is what this will replace. it doesn't need to care about all of the field moves because most of them are only called from the party menu. the only ones it cares about are map events and player movement events.

    finally we edit the scripts themselves.
    in \data\event_scripts.s in EventScript_UseSurf: replace
    Code:
    checkpartymove MOVE_SURF
    with
    Code:
    setvar VAR_ITEMMOVECACHE, MOVE_SURF
    	special Special_CheckPartyFieldEvent
    special is the command to run one of the special functions (functions made to support the assembly scripting).
    now we do the same thing for the other field moves which are placed in \data\scripts\field_move_scripts.inc. again replace
    Code:
    checkpartymove MOVE_CUT
    with
    Code:
    setvar VAR_ITEMMOVECACHE, MOVE_CUT
    	special Special_CheckPartyFieldEvent
    and do the same for all of the other instances of checkpartymove, using the appropriate move pointer for each.


    all that's left is to build and run. you will probably want to either integrate this with another program or add some debug controls so have access to the items added to test.
     
    Last edited:
    180
    Posts
    6
    Years
    • Seen Apr 15, 2024
    next we go to the spot where I probably should have made that struct: \src\party_menu.c. you will need to find sFieldMoves and copy it just below itself replacing all the moves with the items (keep the terminator the same, yes it looks ugly but this means we can use the same functions for them both).
    Code:
    static const u16 sFieldItems[] =																						
    {																															
        ITEM_CUT, ITEM_FLASH, ITEM_ROCK_SMASH, ITEM_STRENGTH, ITEM_SURF, ITEM_FLY, ITEM_DIVE, ITEM_WATERFALL, ITEM_TELEPORT,	
        ITEM_DIG, ITEM_SECRET_POWER, ITEM_MILK_DRINK, ITEM_SOFT_BOILED, ITEM_SWEET_SCENT, FIELD_MOVE_TERMINATOR				
    };
    I can't found that one, i know it won't be with the items, but i can't find the moves either...
    Where do i have to paste that line?
     

    Lunos

    Random Uruguayan User
    3,114
    Posts
    15
    Years
  • next look for the function CreateActionList.
    You seem to be using an outdated version of Pokeemerald. That function is currently called SetPartyMonFieldSelectionActions.
    You're also using certain labels that look different now, than they do in the function your presenting.
    For example, gUnknown_0203CEC4 is now sPartyMenuInternal.

    It would probably be helpful if you could explain certain parts of the tutorial better. Like:
    last thing for this file is there is a referance to the surf check function we killed. we need to replace that with the one we made. just search for the old function and replace it (it's in SetUpFieldMove_Surf). make sure to add the parameter for surf, here is what it should look like:
    I personally didn't understand it all too well, I think?
    I suppose that it's a matter of replacing gPostMenuFieldCallback = FieldCallback_Surf; with gPostMenuFieldCallback = PartyHasMonWithWaterEvent(MOVE_SURF);, but I'm not sure.

    Having a more consistent or straightforward explanation would be nice.
    I'll give this tutorial a go later, and see how it goes.

    EDIT:
    in \data\event_scripts.s in EventScript_UseSurf:
    That script is currently located in data/scripts/surf.inc.
    I can't found that one, i know it won't be with the items, but i can't find the moves either...
    Where do i have to paste that line?
    If I understood this correctly, you have to paste that list of constants in whichever part of src/party_menu.c you want, but outside of a function.

    Ex:
    N1K8JG9.png
     
    Last edited:
    Back
    Top