Implement field_name_box (#7697)

Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
mudskipper13 2025-09-15 01:45:18 +07:00 committed by GitHub
parent 5ce49f3cb7
commit 59a214828e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 470 additions and 6 deletions

View File

@ -2654,3 +2654,11 @@
callnative ScriptChangeFollowerNPCBattlePartner
.2byte \battlePartner
.endm
@ Manually buffer a string as the speaker's name for namebox.
@ The next shown message/msgbox will include a namebox, if the provided string is not NULL.
@ An SP_NAME_* constant can also be used, it'll take the name from gSpeakerNamesTable instead.
.macro setspeaker name:req
callnative SetSpeaker
.4byte \name
.endm

View File

@ -459,6 +459,13 @@ JPN = FC 15
ENG = FC 16
PAUSE_MUSIC = FC 17
RESUME_MUSIC = FC 18
SPEAKER = FC 19
@ Speaker names, the order must be matching with include/constants/speaker_names.h
NAME_NONE = 00
NAME_MOM = 01
NAME_PLAYER = 02
NAME_COUNT = 03
@ fonts

View File

@ -62,6 +62,7 @@
#include "constants/union_room.h"
#include "constants/vars.h"
#include "constants/weather.h"
#include "constants/speaker_names.h"
.include "asm/macros.inc"
.include "asm/macros/event.inc"
.include "constants/constants.inc"

View File

@ -0,0 +1,100 @@
# How to Use Namebox
_New implementation made by mudskipper13, originally made by Tustin2121._
## Overview
![Npc Trainers](/docs/tutorials/img/namebox/npc_trainers.gif)
![Pokenav](/docs/tutorials/img/namebox/pokenav.gif)
![Messagebox](/docs/tutorials/img/namebox/msgbox.gif)
This is a broad and self-contained implementation of Tustin2121's namebox feature branch [here](https://github.com/tustin2121/pokeemerald/tree/feature/namebox), which includes the following:
- Cleaner implementation of namebox onto both the field message box _and_ the field PokéNav textbox.
- New configs:
- `OW_NAME_BOX_USE_DYNAMIC_WIDTH` lets the namebox use dynamic window width depending on the speaker's string length.
- When disabled and/or the speaker name is too long, `OW_NAME_BOX_DEFAULT_WIDTH` will be used as the maximum width.
- `OW_NAME_BOX_NPC_TRAINER` lets any approaching NPC trainers shows a namebox in their dialogue automagically.
- `OW_NAME_BOX_DEFAULT_WIDTH` and `OW_NAME_BOX_DEFAULT_HEIGHT` sets the default width and height.
- `OW_NAME_BOX_FOREGROUND_COLOR` and `OW_NAME_BOX_SHADOW_COLOR` sets the default text colors, the background color is handled by the engine.
- `OW_FLAG_SUPPRESS_NAME_BOX` lets you enable/disable the namebox globally, assign a flag from [`include/constants/flags.h`](/include/constants/flags.h) onto this config to be able to use it.
- Added a Speaker Name table, frequently-used names can be stored into `gSpeakerNamesTable` in [`src/data/speaker_names.h`](/src/data/speaker_names.h) and they can accessed by using a `SP_NAME_*` constant defined in [`include/constants/speaker_names.h`](/include/constants/speaker_names.h).
- Added a new scripting macro `setspeaker ([textPointer]/[SP_NAME_*])`.
- Besides a text pointer, it is possible to use the Speaker Name table to set the textPointer with the `gSpeakerNamesTable` array instead.
- Feed it either `NULL` or `SP_NAME_NONE` will remove the namebox instead.
- `release`, `releaseall`, and `closemessage` will automatically remove the namebox, together with the messagebox.
- Added a new text control code/inline text `{SPEAKER NAME_*}`.
- Unlike the `setspeaker` macro, you can only use the `SP_NAME_*` constants for this. It is partly due to the text engine's limitation itself.
- You'll need to add the constants into `charmap.txt` to be able to use them for the same reason as above.
- Feed it `SP_NAME_NONE` to remove the namebox manually.
- Similarly, `release`, `releaseall`, and `closemessage` will automatically remove the namebox, together with the message box.
## Usage
### `setspeaker`
#### Using a text pointer
First, define your speaker's string.
```
Speaker_Jeremy:
.string "Jeremy$"
```
And then in your script, add the `setspeaker` with the speaker's name earlier.
```
...
setspeaker Speaker_Jeremy
...
```
If you are using poryscript, you can also include the string right there with the `setspeaker` aka inline.
```
...
setspeaker("Jeremy")
...
```
#### Using a `SP_NAME_*` constant
Add the `setspeaker` with your constant.
```
setspeaker SP_NAME_JEREMY
```
For instruction on how to add a new Speaker Name, continue [here](#adding-a-new-speaker-name).
### `SPEAKER` inline
The usage is identical to using `setspeaker` with `SP_NAME_*` constant, but instead it's within your _text_ script and uses the constant you added to `charmap.txt`.
```
"{SPEAKER NAME_JEREMY}Yo wassup!"
```
For instruction on how to add a new Speaker Name, continue [here](#adding-a-new-speaker-name).
### Adding a new Speaker Name
1. Add a new constant to [`include/constants/speaker_names.h`](/include/constants/speaker_names.h) just after `SP_NAME_NONE` _and_ before `SP_NAME_COUNT`.
```diff
enum SpeakerNames {
SP_NAME_NONE = 0,
SP_NAME_MOM,
SP_NAME_PLAYER,
+ SP_NAME_JEREMY,
SP_NAME_COUNT
};
```
2. Add an entry to `gSpeakerNamesTable` in [`src/data/speaker_names.h`](/src/data/speaker_names.h) with your newly added constant as the array index.
```diff
const u8 *const gSpeakerNamesTable[SP_NAME_COUNT] =
{
[SP_NAME_MOM] = COMPOUND_STRING("MOM"),
[SP_NAME_PLAYER] = COMPOUND_STRING("{PLAYER}"),
+ [SP_NAME_JEREMY] = COMPOUND_STRING("JEREMY"),
};
```
3. In order for this constant to be usable for `{SPEAKER}` inline, you'll need to add your constant onto [`charmap.txt`](/charmap.txt). **Do note that the order here MUST match with the one in [`include/constants/speaker_names.h`](/include/constants/speaker_names.h)!**
```diff
@ Speaker names, the order must be matching with include/constants/speaker_names.h
NAME_NONE = 00
NAME_MOM = 01
NAME_PLAYER = 02
-NAME_COUNT = 03
+NAME_JEREMY = 03
+NAME_COUNT = 04
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 784 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

View File

@ -110,6 +110,7 @@
#define OW_FLAG_NO_TRAINER_SEE 0 // If this flag is set, trainers will not battle the player unless they're talked to.
#define OW_FLAG_NO_COLLISION 0 // If this flag is set, the player will be able to walk over tiles with collision. Mainly intended for debugging purposes.
#define OW_FLAG_POKE_RIDER 0 // If this flag is set, the player will be able to use fly from the Pokenav Region Map and the Town Map key item by pressing 'R' on a city/location they are able to fly to.
#define OW_FLAG_SUPPRESS_NAME_BOX 0 // If this flag is set, any namebox (whether its from a macro or a code) will not show up until this flag is unset.
#define BATTLE_PYRAMID_RANDOM_ENCOUNTERS FALSE // If set to TRUE, battle pyramid Pokemon will be generated randomly based on the round's challenge instead of hardcoded in src/data/battle_frontier/battle_pyramid_level_50_wild_mons.h (or open_level_wild_mons.h)
@ -142,4 +143,15 @@
// Berry Blender
#define BERRY_BLENDER_THROW_ALL_BERRIES_AT_ONCE TRUE // This is a small little addition, that basically speeds up the animation where all players' berries are thrown into the blender. Self-explanatory I hope!
// Namebox Speaker configs
#define OW_NAME_BOX_USE_DYNAMIC_WIDTH TRUE // When TRUE, the namebox window can use different width depending on the length of the speaker's name.
#define OW_NAME_BOX_NPC_TRAINER FALSE // When TRUE, any approaching NPC trainers will have a namebox shown automagically. The name will be taken from their trainer data.
#define OW_NAME_BOX_DEFAULT_WIDTH 8 // Maximum width of what OW_NAME_BOX_USE_DYNAMIC_WIDTH can set. Also the default width when the config above is set to FALSE (or the dynamic width exceeds this value).
#define OW_NAME_BOX_DEFAULT_HEIGHT 2 // Maximum height of the namebox window.
// Text colors of Namebox. The numbers corresponds to the palette index.
// The BG color is not provided as it always needs to be 0.
#define OW_NAME_BOX_FOREGROUND_COLOR 1
#define OW_NAME_BOX_SHADOW_COLOR 2
#endif // GUARD_CONFIG_OVERWORLD_H

View File

@ -232,6 +232,7 @@
#define EXT_CTRL_CODE_ENG 0x16
#define EXT_CTRL_CODE_PAUSE_MUSIC 0x17
#define EXT_CTRL_CODE_RESUME_MUSIC 0x18
#define EXT_CTRL_CODE_SPEAKER 0x19
#define TEXT_COLOR_TRANSPARENT 0x0
#define TEXT_COLOR_WHITE 0x1

View File

@ -0,0 +1,11 @@
#ifndef GUARD_CONSTANTS_SPEAKER_NAMES_H
#define GUARD_CONSTANTS_SPEAKER_NAMES_H
enum SpeakerNames {
SP_NAME_NONE = 0,
SP_NAME_MOM,
SP_NAME_PLAYER,
SP_NAME_COUNT
};
#endif // GUARD_CONSTANTS_SPEAKER_NAMES_H

17
include/field_name_box.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef GUARD_FIELD_NAME_BOX_H
#define GUARD_FIELD_NAME_BOX_H
extern EWRAM_DATA const u8 *gSpeakerName;
extern const u8 *const gSpeakerNamesTable[];
void TrySpawnNamebox(void);
u32 GetNameboxWindowId(void);
void DestroyNamebox(void);
void FillNamebox(void);
void DrawNamebox(u32 windowId, bool32 copyToVram);
void ClearNamebox(u32 windowId, bool32 copyToVram);
void SetSpeakerName(const u8 *name);
u32 GetNameboxWidth(void);
void TrySpawnAndShowNamebox(const u8 *speaker);
#endif // GUARD_FIELD_NAME_BOX_H

View File

@ -19,5 +19,6 @@ void BufferPokedexRatingForMatchCall(u8 *destStr);
bool32 SelectMatchCallMessage(int trainerId, u8 *str);
void LoadMatchCallWindowGfx(u32 windowId, u32 destOffset, u32 paletteId);
void DrawMatchCallTextBoxBorder(u32 windowId, u32 tileOffset, u32 paletteId);
void RedrawMatchCallTextBoxBorder(void);
#endif //GUARD_MATCH_CALL_H

View File

@ -135,5 +135,6 @@ u8 AddSecondaryPopUpWindow(void);
u8 GetSecondaryPopUpWindowId(void);
void RemoveSecondaryPopUpWindow(void);
void HBlankCB_DoublePopupWindow(void);
void RedrawDialogueFrame(void);
#endif // GUARD_MENU_H

View File

@ -42,6 +42,7 @@
#include "data.h"
#include "vs_seeker.h"
#include "item.h"
#include "field_name_box.h"
#include "constants/battle_frontier.h"
#include "constants/battle_setup.h"
#include "constants/event_objects.h"
@ -1488,9 +1489,19 @@ static const u8 *ReturnEmptyStringIfNull(const u8 *string)
static const u8 *GetIntroSpeechOfApproachingTrainer(void)
{
if (gApproachingTrainerId == 0)
{
if (OW_NAME_BOX_NPC_TRAINER)
gSpeakerName = GetTrainerNameFromId(TRAINER_BATTLE_PARAM.opponentA);
return ReturnEmptyStringIfNull(TRAINER_BATTLE_PARAM.introTextA);
}
else
{
if (OW_NAME_BOX_NPC_TRAINER)
gSpeakerName = GetTrainerNameFromId(TRAINER_BATTLE_PARAM.opponentB);
return ReturnEmptyStringIfNull(TRAINER_BATTLE_PARAM.introTextB);
}
}
const u8 *GetTrainerALoseText(void)

5
src/data/speaker_names.h Normal file
View File

@ -0,0 +1,5 @@
const u8 *const gSpeakerNamesTable[SP_NAME_COUNT] =
{
[SP_NAME_MOM] = COMPOUND_STRING("MOM"),
[SP_NAME_PLAYER] = COMPOUND_STRING("{PLAYER}"),
};

View File

@ -7,6 +7,7 @@
#include "field_message_box.h"
#include "text_window.h"
#include "script.h"
#include "field_name_box.h"
static EWRAM_DATA u8 sFieldMessageBoxMode = 0;
EWRAM_DATA u8 gWalkAwayFromSignpostTimer = 0;
@ -39,7 +40,10 @@ static void Task_DrawFieldMessage(u8 taskId)
task->tState++;
break;
case 1:
u32 nameboxWinId = GetNameboxWindowId();
DrawDialogueFrame(0, TRUE);
if (nameboxWinId != WINDOW_NONE)
DrawNamebox(nameboxWinId, TRUE);
task->tState++;
break;
case 2:
@ -123,6 +127,7 @@ bool8 ShowFieldMessageFromBuffer(void)
static void ExpandStringAndStartDrawFieldMessage(const u8 *str, bool32 allowSkippingDelayWithButtonPress)
{
TrySpawnNamebox();
StringExpandPlaceholders(gStringVar4, str);
AddTextPrinterForMessage(allowSkippingDelayWithButtonPress);
CreateTask_DrawFieldMessage();
@ -138,6 +143,7 @@ void HideFieldMessageBox(void)
{
DestroyTask_DrawFieldMessage();
ClearDialogWindowAndFrame(0, TRUE);
DestroyNamebox();
sFieldMessageBoxMode = FIELD_MESSAGE_BOX_HIDDEN;
}

194
src/field_name_box.c Normal file
View File

@ -0,0 +1,194 @@
#include "global.h"
#include "main.h"
#include "menu.h"
#include "bg.h"
#include "window.h"
#include "text.h"
#include "string_util.h"
#include "international_string_util.h"
#include "script_menu.h"
#include "field_message_box.h"
#include "graphics.h"
#include "script.h"
#include "field_name_box.h"
#include "event_data.h"
#include "match_call.h"
#include "malloc.h"
#include "constants/speaker_names.h"
#include "data/speaker_names.h"
#define NAME_BOX_BASE_TILE_TOTAL (6)
#define NAME_BOX_BASE_TILE_NUM (0x194 - (OW_NAME_BOX_DEFAULT_WIDTH * OW_NAME_BOX_DEFAULT_HEIGHT) - NAME_BOX_BASE_TILE_TOTAL)
static EWRAM_INIT u8 sNameboxWindowId = WINDOW_NONE;
EWRAM_DATA const u8 *gSpeakerName = NULL;
static const u32 sNameBoxDefaultGfx[] = INCBIN_U32("graphics/text_window/name_box.4bpp");
static const u32 sNameBoxPokenavGfx[] = INCBIN_U32("graphics/pokenav/name_box.4bpp");
static void WindowFunc_DrawNamebox(u8, u8, u8, u8, u8, u8);
static void WindowFunc_ClearNamebox(u8, u8, u8, u8, u8, u8);
void TrySpawnNamebox(void)
{
u8 *strbuf = AllocZeroed(32 * sizeof(u8));
if ((OW_FLAG_SUPPRESS_NAME_BOX != 0 && FlagGet(OW_FLAG_SUPPRESS_NAME_BOX)) || gSpeakerName == NULL || !strbuf)
{
// Re-check again in case anything but !strbuf is TRUE.
if (strbuf)
Free(strbuf);
DestroyNamebox();
return;
}
StringExpandPlaceholders(strbuf, gSpeakerName);
u32 fontId = FONT_SMALL;
u32 winWidth = OW_NAME_BOX_DEFAULT_WIDTH;
if (OW_NAME_BOX_USE_DYNAMIC_WIDTH)
{
winWidth = ConvertPixelWidthToTileWidth(GetStringWidth(fontId, strbuf, -1));
if (winWidth > OW_NAME_BOX_DEFAULT_WIDTH)
winWidth = OW_NAME_BOX_DEFAULT_WIDTH;
}
if (sNameboxWindowId != WINDOW_NONE)
{
DestroyNamebox();
RedrawDialogueFrame();
}
bool32 matchCall = IsMatchCallTaskActive();
struct WindowTemplate template =
{
.bg = 0,
.tilemapLeft = 2,
.tilemapTop = 13,
.width = winWidth,
.height = OW_NAME_BOX_DEFAULT_HEIGHT,
.paletteNum = matchCall ? 14 : DLG_WINDOW_PALETTE_NUM,
.baseBlock = 0x194 - (OW_NAME_BOX_DEFAULT_WIDTH * OW_NAME_BOX_DEFAULT_HEIGHT),
};
sNameboxWindowId = AddWindow(&template);
FillNamebox();
u8 colors[3] = {TEXT_COLOR_TRANSPARENT, OW_NAME_BOX_FOREGROUND_COLOR, OW_NAME_BOX_SHADOW_COLOR};
u8 bakColors[3];
int strX = GetStringCenterAlignXOffset(fontId, strbuf, (winWidth * 8));
if (matchCall)
{
colors[1] = 1;
colors[2] = 0;
}
SaveTextColors(&bakColors[0], &bakColors[1], &bakColors[2]);
AddTextPrinterParameterized3(sNameboxWindowId, fontId, strX, 0, colors, 0, strbuf);
RestoreTextColors(&bakColors[0], &bakColors[1], &bakColors[2]);
PutWindowTilemap(sNameboxWindowId);
Free(strbuf);
}
u32 GetNameboxWindowId(void)
{
return sNameboxWindowId;
}
void DestroyNamebox(void)
{
if (sNameboxWindowId == WINDOW_NONE)
return;
ClearNamebox(sNameboxWindowId, TRUE);
ClearWindowTilemap(sNameboxWindowId);
RemoveWindow(sNameboxWindowId);
sNameboxWindowId = WINDOW_NONE;
gSpeakerName = NULL;
}
u32 GetNameboxWidth(void)
{
return gWindows[sNameboxWindowId].window.width;
}
static const u32 *GetNameboxGraphics(void)
{
if (IsMatchCallTaskActive())
return sNameBoxPokenavGfx;
else
return sNameBoxDefaultGfx;
}
void FillNamebox(void)
{
u32 winSize = GetNameboxWidth();
const u32 *gfx = GetNameboxGraphics();
for (u32 i = 0; i < winSize; i++)
{
#define TILE(x) (8 * x)
CopyToWindowPixelBuffer(sNameboxWindowId, &gfx[TILE(1)], TILE_SIZE_4BPP, i);
CopyToWindowPixelBuffer(sNameboxWindowId, &gfx[TILE(4)], TILE_SIZE_4BPP, i + winSize);
#undef TILE
}
}
void DrawNamebox(u32 windowId, bool32 copyToVram)
{
LoadBgTiles(GetWindowAttribute(sNameboxWindowId, WINDOW_BG), GetNameboxGraphics(), 0x0C0, NAME_BOX_BASE_TILE_NUM);
CallWindowFunction(windowId, WindowFunc_DrawNamebox);
PutWindowTilemap(windowId);
if (copyToVram == TRUE)
CopyWindowToVram(windowId, COPYWIN_FULL);
}
void ClearNamebox(u32 windowId, bool32 copyToVram)
{
CallWindowFunction(windowId, WindowFunc_ClearNamebox);
ClearWindowTilemap(windowId);
if (copyToVram == TRUE)
CopyWindowToVram(windowId, COPYWIN_FULL);
}
static void WindowFunc_DrawNamebox(u8 bg, u8 L, u8 T, u8 w, u8 h, u8 p)
{
// left-most
FillBgTilemapBufferRect(bg, NAME_BOX_BASE_TILE_NUM, L - 1, T, 1, 1, p);
FillBgTilemapBufferRect(bg, NAME_BOX_BASE_TILE_NUM + 3, L - 1, T + 1, 1, 1, p);
// right-most
FillBgTilemapBufferRect(bg, NAME_BOX_BASE_TILE_NUM + 2, L + w, T, 1, 1, p);
FillBgTilemapBufferRect(bg, NAME_BOX_BASE_TILE_NUM + 5, L + w, T + 1, 1, 1, p);
}
static void WindowFunc_ClearNamebox(u8 bg, u8 L, u8 T, u8 w, u8 h, u8 p)
{
FillBgTilemapBufferRect(bg, 0, L - 1, T, w + 2, h, 0); // palette doesn't matter
}
void SetSpeaker(struct ScriptContext *ctx)
{
u32 arg = ScriptReadWord(ctx);
const u8 *speaker = NULL;
if (arg < SP_NAME_COUNT)
speaker = gSpeakerNamesTable[arg];
else if (arg >= ROM_START && arg < ROM_END)
speaker = (const u8 *)arg;
gSpeakerName = speaker;
}
// useful for other context e.g. match call
void TrySpawnAndShowNamebox(const u8 *speaker)
{
gSpeakerName = speaker;
TrySpawnNamebox();
if (sNameboxWindowId != WINDOW_NONE)
DrawNamebox(sNameboxWindowId, TRUE);
else // either NULL or SP_NAME_NONE
RedrawDialogueFrame();
}

View File

@ -27,6 +27,7 @@
#include "task.h"
#include "wild_encounter.h"
#include "window.h"
#include "field_name_box.h"
#include "constants/abilities.h"
#include "constants/battle_frontier.h"
#include "constants/event_objects.h"
@ -1321,9 +1322,11 @@ static bool32 MatchCall_PrintIntro(u8 taskId)
{
FillWindowPixelBuffer(tWindowId, PIXEL_FILL(8));
// Ready the message
// Ready the message (and the speaker's name if possible)
if (!sMatchCallState.triggeredFromScript)
SelectMatchCallMessage(sMatchCallState.trainerId, gStringVar4);
TrySpawnAndShowNamebox(gSpeakerName);
InitMatchCallTextPrinter(tWindowId, gStringVar4);
return TRUE;
}
@ -1348,9 +1351,9 @@ static bool32 MatchCall_PrintMessage(u8 taskId)
static bool32 MatchCall_SlideWindowOut(u8 taskId)
{
s16 *data = gTasks[taskId].data;
if (ChangeBgY(0, 0x600, BG_COORD_SUB) <= -0x2000)
if (ChangeBgY(0, 0x600, BG_COORD_SUB) <= -0x4000)
{
FillBgTilemapBufferRect_Palette0(0, 0, 0, 14, 30, 6);
FillBgTilemapBufferRect_Palette0(0, 0, 0, 12, 30, 8);
DestroyTask(tIconTaskId);
RemoveWindow(tWindowId);
CopyBgTilemapBufferToVram(0);
@ -1404,6 +1407,31 @@ static void DrawMatchCallTextBoxBorder_Internal(u32 windowId, u32 tileOffset, u3
FillBgTilemapBufferRect_Palette0(bg, ((paletteId << 12) & 0xF000) | (tileNum + 7), x + width, y + height, 1, 1);
}
static u8 GetMatchCallWindowId(void)
{
if (!IsMatchCallTaskActive())
return WINDOW_NONE;
u32 taskId = FindTaskIdByFunc(ExecuteMatchCall);
return gTasks[taskId].tWindowId;
}
// redraw only the top-half
void RedrawMatchCallTextBoxBorder(void)
{
u32 windowId = GetMatchCallWindowId();
u32 bg = GetWindowAttribute(windowId, WINDOW_BG);
u32 x = GetWindowAttribute(windowId, WINDOW_TILEMAP_LEFT);
u32 y = GetWindowAttribute(windowId, WINDOW_TILEMAP_TOP);
u32 width = GetWindowAttribute(windowId, WINDOW_WIDTH);
u32 tileNum = TILE_MC_WINDOW + GetBgAttribute(bg, BG_ATTR_BASETILE);
u32 paletteId = 14;
FillBgTilemapBufferRect_Palette0(bg, ((paletteId << 12) & 0xF000) | (tileNum + 0), x - 1, y - 1, 1, 1);
FillBgTilemapBufferRect_Palette0(bg, ((paletteId << 12) & 0xF000) | (tileNum + 1), x, y - 1, width, 1);
FillBgTilemapBufferRect_Palette0(bg, ((paletteId << 12) & 0xF000) | (tileNum + 2), x + width, y - 1, 1, 1);
}
static void InitMatchCallTextPrinter(int windowId, const u8 *str)
{
struct TextPrinterTemplate printerTemplate;

View File

@ -22,6 +22,7 @@
#include "task.h"
#include "text_window.h"
#include "window.h"
#include "match_call.h"
#include "config/overworld.h"
#include "constants/songs.h"
@ -341,6 +342,53 @@ void DrawDialogueFrame(u8 windowId, bool8 copyToVram)
CopyWindowToVram(windowId, COPYWIN_FULL);
}
static void WindowFunc_RedrawDialogueFrame(u8 bg, u8 tilemapLeft, u8 tilemapTop, u8 width, u8 height, u8 paletteNum)
{
FillBgTilemapBufferRect(bg,
DLG_WINDOW_BASE_TILE_NUM + 1,
tilemapLeft - 2,
tilemapTop - 1,
1,
1,
DLG_WINDOW_PALETTE_NUM);
FillBgTilemapBufferRect(bg,
DLG_WINDOW_BASE_TILE_NUM + 3,
tilemapLeft - 1,
tilemapTop - 1,
1,
1,
DLG_WINDOW_PALETTE_NUM);
FillBgTilemapBufferRect(bg,
DLG_WINDOW_BASE_TILE_NUM + 4,
tilemapLeft,
tilemapTop - 1,
width - 1,
1,
DLG_WINDOW_PALETTE_NUM);
FillBgTilemapBufferRect(bg,
DLG_WINDOW_BASE_TILE_NUM + 5,
tilemapLeft + width - 1,
tilemapTop - 1,
1,
1,
DLG_WINDOW_PALETTE_NUM);
FillBgTilemapBufferRect(bg,
DLG_WINDOW_BASE_TILE_NUM + 6,
tilemapLeft + width,
tilemapTop - 1,
1,
1,
DLG_WINDOW_PALETTE_NUM);
}
void RedrawDialogueFrame(void)
{
if (IsMatchCallTaskActive())
RedrawMatchCallTextBoxBorder();
else
CallWindowFunction(0, WindowFunc_RedrawDialogueFrame);
}
void DrawStdWindowFrame(u8 windowId, bool8 copyToVram)
{
CallWindowFunction(windowId, WindowFunc_DrawStandardFrame);

View File

@ -706,6 +706,7 @@ u8 GetExtCtrlCodeLength(u8 code)
[EXT_CTRL_CODE_ENG] = 1,
[EXT_CTRL_CODE_PAUSE_MUSIC] = 1,
[EXT_CTRL_CODE_RESUME_MUSIC] = 1,
[EXT_CTRL_CODE_SPEAKER] = 1,
};
u8 length = 0;

View File

@ -12,6 +12,8 @@
#include "menu.h"
#include "dynamic_placeholder_text_util.h"
#include "fonts.h"
#include "field_name_box.h"
#include "constants/speaker_names.h"
static u16 RenderText(struct TextPrinter *);
static u32 RenderFont(struct TextPrinter *);
@ -1228,6 +1230,13 @@ static u16 RenderText(struct TextPrinter *textPrinter)
case EXT_CTRL_CODE_ENG:
textPrinter->japanese = FALSE;
return RENDER_REPEAT;
case EXT_CTRL_CODE_SPEAKER:
{
enum SpeakerNames name = *textPrinter->printerTemplate.currentChar++;
TrySpawnAndShowNamebox(gSpeakerNamesTable[name]);
return RENDER_REPEAT;
}
}
break;
case CHAR_PROMPT_CLEAR:
@ -1418,6 +1427,7 @@ static u32 UNUSED GetStringWidthFixedWidthFont(const u8 *str, u8 fontId, u8 lett
case EXT_CTRL_CODE_SKIP:
case EXT_CTRL_CODE_CLEAR_TO:
case EXT_CTRL_CODE_MIN_LETTER_SPACING:
case EXT_CTRL_CODE_SPEAKER:
++strPos;
break;
case EXT_CTRL_CODE_RESET_FONT:
@ -1566,6 +1576,7 @@ s32 GetStringWidth(u8 fontId, const u8 *str, s16 letterSpacing)
case EXT_CTRL_CODE_ESCAPE:
case EXT_CTRL_CODE_SHIFT_RIGHT:
case EXT_CTRL_CODE_SHIFT_DOWN:
case EXT_CTRL_CODE_SPEAKER:
++str;
break;
case EXT_CTRL_CODE_FONT:
@ -1735,6 +1746,7 @@ u8 RenderTextHandleBold(u8 *pixels, u8 fontId, u8 *str)
case EXT_CTRL_CODE_SKIP:
case EXT_CTRL_CODE_CLEAR_TO:
case EXT_CTRL_CODE_MIN_LETTER_SPACING:
case EXT_CTRL_CODE_SPEAKER:
++strPos;
break;
case EXT_CTRL_CODE_RESET_FONT: