pic

头像系统实现

在这个文件夹创建:include\dialogue_portraits.h

#ifndef GUARD_DIALOGUE_PORTRAITS_H
#define GUARD_DIALOGUE_PORTRAITS_H

#include "global.h"

// 头像位置定义
#define PORTRAIT_LEFT_X         2   // 左侧头像位置
#define PORTRAIT_RIGHT_X        21  // 右侧头像位置
#define PORTRAIT_Y              7   // 对话框上方位置

// 头像状态枚举
enum PortraitState {
    PORTRAIT_STATE_NONE = 0,
    PORTRAIT_STATE_SHOWING,
    PORTRAIT_STATE_VISIBLE,
    PORTRAIT_STATE_HIDING
};

// 头像结构体
struct DialoguePortrait {
    u8 spriteId;           // 精灵ID
    u8 state;              // 当前状态
    u8 currentFrame;       // 当前帧
    u8 timer;              // 动画计时器
    u16 paletteTag;        // 调色板标签
    u16 gfxTag;            // 图形标签
};

// 函数声明
void InitDialoguePortraitSystem(void);
void CleanupDialoguePortraitSystem(void);
bool8 ShowDialoguePortrait(u8 portraitId, u8 side, u16 gfxTag, u16 paletteTag);
bool8 ShowDialoguePortraitWithFlip(u8 portraitId, u8 side, u16 gfxTag, u16 paletteTag, bool8 hFlip, bool8 vFlip);
void HideDialoguePortrait(u8 side);
void UpdateDialoguePortraits(void);
bool8 ArePortraitsActive(void);
void SetDialoguePortraitFlip(u8 side, bool8 hFlip, bool8 vFlip);

#endif // GUARD_DIALOGUE_PORTRAITS_H

在这个文件夹创建:src\dialogue_portraits.c

#include "global.h"
#include "dialogue_portraits.h"
#include "window.h"
#include "menu.h"
#include "sprite.h"
#include "malloc.h"
#include "task.h"
#include "text_window.h"
#include "constants/trainers.h"
#include "data.h"
#include "decompress.h"
#include "constants/songs.h"
#include "portrait_data.h"

// 自定义头像ID范围定义
#define CUSTOM_PORTRAIT_ID_START  1000
#define CUSTOM_PORTRAIT_ID_END    1999

// 头像系统全局变量
static EWRAM_DATA struct DialoguePortrait sPortraits[2] = {0};
static EWRAM_DATA bool8 sPortraitSystemInitialized = FALSE;

// 自定义头像资源声明
extern const u8 gHeadPortrait_001_Gfx[];
extern const u16 gHeadPortrait_001_Pal[];
extern const u8 gHeadPortrait_002_Gfx[];
extern const u16 gHeadPortrait_002_Pal[];

// 加载自定义头像资源
static bool8 LoadCustomPortrait(u16 portraitId, u16 *gfxTag, u16 *paletteTag)
{
    u16 localId = portraitId - CUSTOM_PORTRAIT_ID_START + 1;

    // 根据ID加载对应的自定义头像资源
    const u8 *gfxData = NULL;
    const u16 *palData = NULL;

    switch (localId) {
        case 1:
            gfxData = gHeadPortrait_001_Gfx;
            palData = gHeadPortrait_001_Pal;
            break;
        case 2:
            gfxData = gHeadPortrait_002_Gfx;
            palData = gHeadPortrait_002_Pal;
            break;
        default:
            return FALSE;  // 不支持的自定义头像ID
    }

    if (!gfxData || !palData)
        return FALSE;

    // 加载自定义头像图形
    struct CompressedSpriteSheet spriteSheet = {
        .data = (const void *)gfxData,
        .size = 0x800,  // 64x64像素,4bpp
        .tag = portraitId + 0x3000  // 使用0x3000系列标签避免与训练师冲突
    };
    LoadCompressedSpriteSheet(&spriteSheet);

    // 加载自定义头像调色板
    struct SpritePalette spritePalette = {
        .data = (const void *)palData,
        .tag = portraitId + 0x4000  // 使用0x4000系列标签
    };
    LoadSpritePalette(&spritePalette);

    *gfxTag = portraitId + 0x3000;
    *paletteTag = portraitId + 0x4000;

    return TRUE;
}

// 初始化头像系统
void InitDialoguePortraitSystem(void)
{
    // 如果已经初始化,先清理
    if (sPortraitSystemInitialized) {
        CleanupDialoguePortraitSystem();
    }

    // 初始化头像数据
    memset(sPortraits, 0, sizeof(sPortraits));

    // 初始化资源标签为无效值
    for (int i = 0; i < 2; i++) {
        sPortraits[i].spriteId = 0xFF;
        sPortraits[i].gfxTag = 0xFFFF;
        sPortraits[i].paletteTag = 0xFFFF;
    }

    sPortraitSystemInitialized = TRUE;
}

// 清理头像系统
void CleanupDialoguePortraitSystem(void)
{
    if (!sPortraitSystemInitialized)
        return;

    // 销毁精灵
    if (sPortraits[0].spriteId != 0xFF && sPortraits[0].spriteId < MAX_SPRITES) {
        DestroySprite(&gSprites[sPortraits[0].spriteId]);
    }
    if (sPortraits[1].spriteId != 0xFF && sPortraits[1].spriteId < MAX_SPRITES) {
        DestroySprite(&gSprites[sPortraits[1].spriteId]);
    }

    memset(sPortraits, 0, sizeof(sPortraits));
    sPortraitSystemInitialized = FALSE;
}

// 显示头像
bool8 ShowDialoguePortrait(u8 portraitId, u8 side, u16 gfxTag, u16 paletteTag)
{
    if (!sPortraitSystemInitialized || side > 1)
        return FALSE;

    struct DialoguePortrait *portrait = &sPortraits[side];

    // 如果已经有头像显示,先隐藏并清理
    if (portrait->state != PORTRAIT_STATE_NONE) {
        HideDialoguePortrait(side);
    }

    // 加载头像图形资源
    if (gfxTag >= CUSTOM_PORTRAIT_ID_START && gfxTag <= CUSTOM_PORTRAIT_ID_END) {
        // 使用自定义头像
        if (!LoadCustomPortrait(gfxTag, &portrait->gfxTag, &portrait->paletteTag)) {
            return FALSE;
        }
    } else if (gfxTag < TRAINER_PIC_COUNT) {
        // 使用系统预定义的训练师图像
        const struct TrainerSprite *trainerSprite = &gTrainerSprites[gfxTag];

        // 加载图像数据
        struct CompressedSpriteSheet spriteSheet = {
            .data = trainerSprite->frontPic.data,
            .size = trainerSprite->frontPic.size,
            .tag = gfxTag + 0x1000  // 使用唯一标签避免冲突
        };
        LoadCompressedSpriteSheet(&spriteSheet);

        // 加载调色板数据
        struct SpritePalette spritePalette = {
            .data = trainerSprite->palette.data,
            .tag = gfxTag + 0x2000  // 使用唯一标签避免冲突
        };
        LoadSpritePalette(&spritePalette);

        // 更新标签以便后续使用
        portrait->gfxTag = gfxTag + 0x1000;
        portrait->paletteTag = gfxTag + 0x2000;
    } else {
        // 对于无效的ID,直接返回失败
        return FALSE;
    }

    // 创建精灵来显示头像
    struct SpriteTemplate spriteTemplate = {
        .tileTag = portrait->gfxTag,
        .paletteTag = portrait->paletteTag,
        .oam = &(const struct OamData){
            .y = 0,
            .affineMode = ST_OAM_AFFINE_OFF,
            .objMode = ST_OAM_OBJ_NORMAL,
            .bpp = ST_OAM_4BPP,
            .shape = SPRITE_SHAPE(64x64),
            .x = 0,
            .size = SPRITE_SIZE(64x64),
            .tileNum = 0,
            .priority = 1,
            .paletteNum = 0,
        },
        .anims = gDummySpriteAnimTable,
        .images = NULL,
        .affineAnims = gDummySpriteAffineAnimTable,
        .callback = SpriteCallbackDummy
    };

    // 计算精灵位置
    u16 x, y;
    if (side == 0) { // 左侧
        x = PORTRAIT_LEFT_X * 8 + 32;  // 窗口中心位置
    } else { // 右侧
        x = PORTRAIT_RIGHT_X * 8 + 32;
    }
    y = PORTRAIT_Y * 8 + 32;

    portrait->spriteId = CreateSprite(&spriteTemplate, x, y, 0);
    if (portrait->spriteId == MAX_SPRITES) {
        // 创建精灵失败,清理资源
        FreeSpritePaletteByTag(portrait->paletteTag);
        FreeSpriteTilesByTag(portrait->gfxTag);
        portrait->gfxTag = 0xFFFF;
        portrait->paletteTag = 0xFFFF;
        return FALSE;
    }

    // 设置精灵属性
    struct Sprite *sprite = &gSprites[portrait->spriteId];
    sprite->oam.priority = 1;  // 在对话框之上
    sprite->subpriority = 4;

    // 设置状态
    portrait->state = PORTRAIT_STATE_SHOWING;
    portrait->currentFrame = 0;
    portrait->timer = 0;

    return TRUE;
}

// 显示头像(带翻转参数)
bool8 ShowDialoguePortraitWithFlip(u8 portraitId, u8 side, u16 gfxTag, u16 paletteTag, bool8 hFlip, bool8 vFlip)
{
    if (!ShowDialoguePortrait(portraitId, side, gfxTag, paletteTag))
        return FALSE;

    // 设置翻转状态
    SetDialoguePortraitFlip(side, hFlip, vFlip);

    return TRUE;
}

// 隐藏头像
void HideDialoguePortrait(u8 side)
{
    if (!sPortraitSystemInitialized || side > 1)
        return;

    struct DialoguePortrait *portrait = &sPortraits[side];

    if (portrait->state == PORTRAIT_STATE_NONE)
        return;

    // 销毁精灵
    if (portrait->spriteId != 0xFF && portrait->spriteId < MAX_SPRITES) {
        DestroySprite(&gSprites[portrait->spriteId]);
        portrait->spriteId = 0xFF;
    }

    // 释放资源
    if (portrait->gfxTag != 0xFFFF) {
        FreeSpritePaletteByTag(portrait->paletteTag);
        FreeSpriteTilesByTag(portrait->gfxTag);
    }

    // 重置所有状态和标签
    portrait->state = PORTRAIT_STATE_NONE;
    portrait->currentFrame = 0;
    portrait->timer = 0;
    portrait->gfxTag = 0xFFFF;
    portrait->paletteTag = 0xFFFF;
}

// 更新头像
void UpdateDialoguePortraits(void)
{
    if (!sPortraitSystemInitialized)
        return;

    for (int i = 0; i < 2; i++) {
        struct DialoguePortrait *portrait = &sPortraits[i];

        switch (portrait->state) {
            case PORTRAIT_STATE_SHOWING:
                portrait->timer++;
                if (portrait->timer > 10) {  // 显示动画持续10帧
                    portrait->state = PORTRAIT_STATE_VISIBLE;
                }
                break;

            case PORTRAIT_STATE_VISIBLE:
                // 可以在这里添加呼吸动画等效果
                portrait->timer++;
                if (portrait->timer > 60) {  // 每秒更新一次
                    portrait->timer = 0;
                    portrait->currentFrame = (portrait->currentFrame + 1) % 2;
                }
                break;

            case PORTRAIT_STATE_HIDING:
                portrait->timer++;
                if (portrait->timer > 10) {  // 隐藏动画持续10帧
                    portrait->state = PORTRAIT_STATE_NONE;
                }
                break;

            default:
                break;
        }
    }
}

// 设置头像翻转
void SetDialoguePortraitFlip(u8 side, bool8 hFlip, bool8 vFlip)
{
    if (!sPortraitSystemInitialized || side > 1)
        return;

    struct DialoguePortrait *portrait = &sPortraits[side];

    // 检查是否有有效的精灵
    if (portrait->spriteId == 0xFF || portrait->spriteId >= MAX_SPRITES)
        return;

    // 检查精灵是否处于活动状态
    if (portrait->state == PORTRAIT_STATE_NONE)
        return;

    struct Sprite *sprite = &gSprites[portrait->spriteId];

    // 设置精灵的翻转属性
    sprite->hFlip = hFlip;
    sprite->vFlip = vFlip;

    // 直接设置OAM翻转位,不使用异或操作
    sprite->oam.matrixNum &= 0x7;  // 清除翻转位
    if (hFlip)
        sprite->oam.matrixNum |= (1 << 3);  // 设置水平翻转位
    if (vFlip)
        sprite->oam.matrixNum |= (1 << 4);  // 设置垂直翻转位
}

// 检查是否有头像活动
bool8 ArePortraitsActive(void)
{
    if (!sPortraitSystemInitialized)
        return FALSE;

    return (sPortraits[0].state != PORTRAIT_STATE_NONE || 
            sPortraits[1].state != PORTRAIT_STATE_NONE);
}

在这个文件夹创建:src\portrait_data.c

#include "global.h"

// 自定义头像资源定义
// 这些将由工具链从graphics/head/文件夹中的PNG和PAL文件自动生成

// 头像001 - 玩家头像
const u8 gHeadPortrait_001_Gfx[] = INCBIN_U8("graphics/head/head_001.4bpp.smol");
const u16 gHeadPortrait_001_Pal[] = INCBIN_U16("graphics/head/head_001.gbapal");

// 头像002 - 预留给其他角色
const u8 gHeadPortrait_002_Gfx[] = INCBIN_U8("graphics/head/head_002.4bpp.smol");
const u16 gHeadPortrait_002_Pal[] = INCBIN_U16("graphics/head/head_002.gbapal");

修改这个文件:src\field_message_box.c

#include "global.h"
#include "menu.h"
#include "string_util.h"
#include "task.h"
#include "text.h"
#include "match_call.h"
#include "field_message_box.h"
#include "text_window.h"
#include "script.h"
#include "dialogue_portraits.h"  //新增代码
void InitFieldMessageBox(void)
{
    sFieldMessageBoxMode = FIELD_MESSAGE_BOX_HIDDEN;
    gTextFlags.canABSpeedUpPrint = FALSE;
    gTextFlags.useAlternateDownArrow = FALSE;
    gTextFlags.autoScroll = FALSE;
    gTextFlags.forceMidTextSpeed = FALSE;
    InitDialoguePortraitSystem();   //新增代码
}
#define tState data[0]

static void Task_DrawFieldMessage(u8 taskId)
{
    struct Task *task = &gTasks[taskId];

    switch (task->tState)
    {
        case 0:
            if (gMsgIsSignPost)
                LoadSignPostWindowFrameGfx();
            else
                LoadMessageBoxAndBorderGfx();
            task->tState++;
            break;
        case 1:
           DrawDialogueFrame(0, TRUE);
           task->tState++;
           break;
        case 2:
            UpdateDialoguePortraits();   //新增代码
            if (RunTextPrintersAndIsPrinter0Active() != TRUE)
            {
                sFieldMessageBoxMode = FIELD_MESSAGE_BOX_HIDDEN;
                DestroyTask(taskId);
            }
    }
}
void HideFieldMessageBox(void)
{
    DestroyTask_DrawFieldMessage();
    ClearDialogWindowAndFrame(0, TRUE);
    CleanupDialoguePortraitSystem();  //新增代码
    sFieldMessageBoxMode = FIELD_MESSAGE_BOX_HIDDEN;
}

这个文件添加代码:data\specials.inc

#include "dialogue_portraits.h"   //新增代码
// 头像系统special函数实现
void Special_InitDialoguePortraitSystem(void)
{
    InitDialoguePortraitSystem();
}

void Special_ShowDialoguePortrait(void)
{
    u8 side = gSpecialVar_0x8004;      // 0=左侧,1=右侧
    u8 portraitId = gSpecialVar_0x8005; // 头像ID
    u16 gfxTag = gSpecialVar_0x8006;    // 图形标签
    u16 paletteTag = gSpecialVar_0x8007; // 调色板标签

    ShowDialoguePortrait(portraitId, side, gfxTag, paletteTag);
}

void Special_ShowDialoguePortraitWithFlip(void)
{
    u8 side = gSpecialVar_0x8004;      // 0=左侧,1=右侧
    u8 portraitId = gSpecialVar_0x8005; // 头像ID
    u16 gfxTag = gSpecialVar_0x8006;    // 图形标签
    u16 paletteTag = gSpecialVar_0x8007; // 调色板标签
    bool8 hFlip = gSpecialVar_0x8008;   // 水平翻转
    bool8 vFlip = gSpecialVar_0x8009;   // 垂直翻转

    ShowDialoguePortraitWithFlip(portraitId, side, gfxTag, paletteTag, hFlip, vFlip);
}

void Special_HideDialoguePortrait(void)
{
    u8 side = gSpecialVar_0x8004; // 0=左侧,1=右侧
    HideDialoguePortrait(side);
}

void Special_UpdateDialoguePortraits(void)
{
    UpdateDialoguePortraits();
}

void Special_CleanupDialoguePortraitSystem(void)
{
    CleanupDialoguePortraitSystem();
}

void Special_SetDialoguePortraitFlip(void)
{
    u8 side = gSpecialVar_0x8004;  // 0=左侧,1=右侧
    bool8 hFlip = gSpecialVar_0x8005; // 水平翻转
    bool8 vFlip = gSpecialVar_0x8006; // 垂直翻转

    SetDialoguePortraitFlip(side, hFlip, vFlip);
}

新增文件:include\portrait_data.h

#ifndef GUARD_PORTRAIT_DATA_H
#define GUARD_PORTRAIT_DATA_H

// 自定义头像资源声明
// 这些将由工具链从graphics/head/文件夹中的PNG和PAL文件生成

extern const u8 gHeadPortrait_001_Gfx[];
extern const u16 gHeadPortrait_001_Pal[];
extern const u8 gHeadPortrait_002_Gfx[];
extern const u16 gHeadPortrait_002_Pal[];

#endif // GUARD_PORTRAIT_DATA_H

文件新增代码:include\constants\field_specials.h

// 头像系统Special函数常量
#define SPECIAL_INIT_DIALOGUE_PORTRAIT_SYSTEM    0x200
#define SPECIAL_SHOW_DIALOGUE_PORTRAIT           0x201
#define SPECIAL_HIDE_DIALOGUE_PORTRAIT           0x202
#define SPECIAL_UPDATE_DIALOGUE_PORTRAITS        0x203
#define SPECIAL_CLEANUP_DIALOGUE_PORTRAIT_SYSTEM  0x204

文件新增代码:graphics_file_rules.mk

### Custom Trainer Portraits ###
# Define custom graphics directory
CUSTOMGFXDIR := graphics/head

# Convert PNG to 4bpp format for trainer portraits
$(CUSTOMGFXDIR)/head_001.4bpp: %.4bpp: %.png
    $(GFX) $< $@ -num_tiles 64 -Wnum_tiles

# Convert 4bpp to compressed .4bpp.smol format
$(CUSTOMGFXDIR)/head_001.4bpp.smol: %.4bpp.smol: %.4bpp
    $(SMOL) -w $< $@

# Extract palette from PNG and convert to GBAPAL format
$(CUSTOMGFXDIR)/head_001.gbapal: %.gbapal: %.png
    $(GFX) $< $@ -palette

脚本测试示例:

Text_PortraitFlipIntro:
    .string "这是头像翻转功能演示。\n我们将展示各种翻转效果。$"

Text_PortraitNormal:
    .string "这是正常方向的头像。$"

Text_PortraitHFlip:
    .string "这是水平翻转的头像。\n头像现在面向左边。$"

Text_PortraitVFlip:
    .string "这是垂直翻转的头像。\n头像现在上下颠倒。$"

Text_PortraitBothFlip:
    .string "这是同时水平和垂直翻转的头像。\n相当于180度旋转。$"

Text_PortraitRestored:
    .string "恢复正常方向的头像。$"

Text_DualPortraitIntro:
    .string "现在演示双人对话中的翻转效果。$"

Text_LeftCharacterNormal:
    .string "左边角色:你好!我是面向右边的。$"

Text_RightCharacterFlipped:
    .string "右边角色:你好!我现在面向左边。$"

Text_BothFacingForward:
    .string "两个角色都面向前方了!$"

Text_DynamicFlipIntro:
    .string "演示动态翻转效果,模拟角色转头。$"

Text_FacingRight:
    .string "面向右边...$"

Text_FacingLeft:
    .string "面向左边...$"

Text_FacingRightAgain:
    .string "又面向右边了!$"

Text_DemoComplete:
    .string "头像翻转功能演示完成!$"

// 示例1:基本头像显示和翻转
EventScript_PortraitFlipBasic::
    lock
    faceplayer
    
    // 初始化头像系统
    special Special_InitDialoguePortraitSystem
    
    msgbox Text_PortraitFlipIntro, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 显示左侧头像(使用自定义头像ID)
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 0      // 头像ID 0 (未使用)
    setvar VAR_0x8006, 1000   // 自定义头像ID: head_001.png
    setvar VAR_0x8007, 0      // 调色板标签 (未使用)
    special Special_ShowDialoguePortrait
    
    msgbox Text_PortraitNormal, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 水平翻转头像
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 1      // hFlip=TRUE
    setvar VAR_0x8006, 0      // vFlip=FALSE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_PortraitHFlip, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 垂直翻转头像
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 0      // hFlip=FALSE
    setvar VAR_0x8006, 1      // vFlip=TRUE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_PortraitVFlip, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 同时水平和垂直翻转
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 1      // hFlip=TRUE
    setvar VAR_0x8006, 1      // vFlip=TRUE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_PortraitBothFlip, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 恢复正常方向
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 0      // hFlip=FALSE
    setvar VAR_0x8006, 0      // vFlip=FALSE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_PortraitRestored, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 隐藏头像
    setvar VAR_0x8004, 0      // 左侧
    special Special_HideDialoguePortrait
    
    // 清理头像系统
    special Special_CleanupDialoguePortraitSystem
    
    release
    end

// 示例2:双人对话中的翻转效果
EventScript_PortraitFlipDual::
    lock
    faceplayer
    
    // 初始化头像系统
    special Special_InitDialoguePortraitSystem
    
    msgbox Text_DualPortraitIntro, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 显示两个头像
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 0      // 头像ID 0
    setvar VAR_0x8006, 1001     // 训练师ID: Brendan
    setvar VAR_0x8007, 0      // 调色板标签
    special Special_ShowDialoguePortrait
    
    setvar VAR_0x8004, 1      // 右侧
    setvar VAR_0x8005, 1      // 头像ID 1
    setvar VAR_0x8006, 1000     // 训练师ID: May
    setvar VAR_0x8007, 0      // 调色板标签
    special Special_ShowDialoguePortrait
    
    // 左侧角色说话(正常面向右边)
    msgbox Text_LeftCharacterNormal, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 右侧角色说话(水平翻转面向左边)
    setvar VAR_0x8004, 1      // 右侧
    setvar VAR_0x8005, 1      // hFlip=TRUE
    setvar VAR_0x8006, 0      // vFlip=FALSE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_RightCharacterFlipped, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 两个角色都面向前方
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 0      // hFlip=FALSE
    setvar VAR_0x8006, 0      // vFlip=FALSE
    special Special_SetDialoguePortraitFlip
    
    setvar VAR_0x8004, 1      // 右侧
    setvar VAR_0x8005, 0      // hFlip=FALSE
    setvar VAR_0x8006, 0      // vFlip=FALSE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_BothFacingForward, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 隐藏头像
    setvar VAR_0x8004, 0      // 左侧
    special Special_HideDialoguePortrait
    
    setvar VAR_0x8004, 1      // 右侧
    special Special_HideDialoguePortrait
    
    // 清理头像系统
    special Special_CleanupDialoguePortraitSystem
    
    release
    end

// 示例3:动态翻转效果(模拟角色转头)
EventScript_PortraitFlipDynamic::
    lock
    faceplayer
    
    // 初始化头像系统
    special Special_InitDialoguePortraitSystem
    
    msgbox Text_DynamicFlipIntro, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 显示头像
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 0      // 头像ID 0
    setvar VAR_0x8006, 1000   // 自定义头像ID
    setvar VAR_0x8007, 0      // 调色板标签
    special Special_ShowDialoguePortrait
    
    // 正常方向(面向右边)
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 0      // hFlip=FALSE
    setvar VAR_0x8006, 0      // vFlip=FALSE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_FacingRight, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    delay 30
    
    // 水平翻转(面向左边)
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 1      // hFlip=TRUE
    setvar VAR_0x8006, 0      // vFlip=FALSE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_FacingLeft, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    delay 30
    
    // 再次翻转回来
    setvar VAR_0x8004, 0      // 左侧
    setvar VAR_0x8005, 0      // hFlip=FALSE
    setvar VAR_0x8006, 0      // vFlip=FALSE
    special Special_SetDialoguePortraitFlip
    
    msgbox Text_FacingRightAgain, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    // 隐藏头像
    setvar VAR_0x8004, 0      // 左侧
    special Special_HideDialoguePortrait
    
    // 清理头像系统
    special Special_CleanupDialoguePortraitSystem
    
    msgbox Text_DemoComplete, MSGBOX_DEFAULT
    waitmessage
    waitbuttonpress
    
    release
    end

———————————————–分割线—————————————————

脚本封装更新

因为之前的写法过于臃肿,所以需要改进一下脚本的写法

定义语义化宏常量:constants\portrait_constants.inc

// 头像系统常量定义

// 头像位置常量
#define PORTRAIT_LEFT      0
#define PORTRAIT_RIGHT     1

// 头像翻转常量
#define FLIP_NONE          0
#define FLIP_HORIZONTAL    1
#define FLIP_VERTICAL      1
#define FLIP_BOTH          1

// 头像系统变量别名
#define PORTRAIT_POS       VAR_0x8004
#define PORTRAIT_ID        VAR_0x8005
#define PORTRAIT_GFX_TAG   VAR_0x8006
#define PORTRAIT_PAL_TAG   VAR_0x8007
#define PORTRAIT_H_FLIP    VAR_0x8008
#define PORTRAIT_V_FLIP    VAR_0x8009
#define PORTRAIT_FLIP_H    VAR_0x8005
#define PORTRAIT_FLIP_V    VAR_0x8006

在这个文件constants\constants.inc新增一行:

.include "constants/portrait_constants.inc"

封装常用操作为宏

// ==================== Portrait System Macros ====================

// Portrait position constants
.set PORTRAIT_LEFT, 0
.set PORTRAIT_RIGHT, 1

// Portrait flip constants
.set FLIP_NONE, 0
.set FLIP_HORIZONTAL, 1
.set FLIP_VERTICAL, 1
.set FLIP_BOTH, 1

// Portrait system variable aliases
.set PORTRAIT_POS, VAR_0x8004
.set PORTRAIT_ID, VAR_0x8005
.set PORTRAIT_GFX_TAG, VAR_0x8006
.set PORTRAIT_PAL_TAG, VAR_0x8007
.set PORTRAIT_H_FLIP, VAR_0x8008
.set PORTRAIT_V_FLIP, VAR_0x8009
.set PORTRAIT_FLIP_H, VAR_0x8005
.set PORTRAIT_FLIP_V, VAR_0x8006

// Show portrait macro
.macro SHOW_PORTRAIT pos, id, gfx_tag, pal_tag
    setvar PORTRAIT_POS, \pos
    setvar PORTRAIT_ID, \id
    setvar PORTRAIT_GFX_TAG, \gfx_tag
    setvar PORTRAIT_PAL_TAG, \pal_tag
    special Special_ShowDialoguePortrait
.endm

// Show flipped portrait macro
.macro SHOW_PORTRAIT_FLIP pos, id, gfx_tag, pal_tag, h_flip, v_flip
    setvar PORTRAIT_POS, \pos
    setvar PORTRAIT_ID, \id
    setvar PORTRAIT_GFX_TAG, \gfx_tag
    setvar PORTRAIT_PAL_TAG, \pal_tag
    setvar PORTRAIT_H_FLIP, \h_flip
    setvar PORTRAIT_V_FLIP, \v_flip
    special Special_ShowDialoguePortraitWithFlip
.endm

// Hide portrait macro
.macro HIDE_PORTRAIT pos
    setvar PORTRAIT_POS, \pos
    special Special_HideDialoguePortrait
.endm

// Set portrait flip macro
.macro SET_PORTRAIT_FLIP pos, h_flip, v_flip
    setvar PORTRAIT_POS, \pos
    setvar PORTRAIT_FLIP_H, \h_flip
    setvar PORTRAIT_FLIP_V, \v_flip
    special Special_SetDialoguePortraitFlip
.endm

改进后的脚本示例:

// ==================== 头像系统测试脚本 ====================

EventScript_PortraitSystemTest::
	lock
	faceplayer
	msgbox Text_PortraitTestIntro, MSGBOX_DEFAULT
	call EventScript_PortraitBasicTest
	call EventScript_PortraitFlipTest
	call EventScript_PortraitCustomTest
	call EventScript_PortraitDualTest
	call EventScript_PortraitAdvancedTest
	msgbox Text_PortraitTestComplete, MSGBOX_DEFAULT
	release
	end

// 基础功能测试
EventScript_PortraitBasicTest::
	msgbox Text_BasicTestStart, MSGBOX_DEFAULT
	special Special_InitDialoguePortraitSystem
	
	SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
	delay 30
	msgbox Text_LeftPortraitShown, MSGBOX_DEFAULT
	
	SHOW_PORTRAIT PORTRAIT_RIGHT, 2, 2, 2
	delay 30
	msgbox Text_RightPortraitShown, MSGBOX_DEFAULT
	
	HIDE_PORTRAIT PORTRAIT_LEFT
	delay 15
	msgbox Text_LeftPortraitHidden, MSGBOX_DEFAULT
	
	HIDE_PORTRAIT PORTRAIT_RIGHT
	delay 15
	msgbox Text_RightPortraitHidden, MSGBOX_DEFAULT
	
	special Special_CleanupDialoguePortraitSystem
	return

// 翻转功能测试
EventScript_PortraitFlipTest::
	msgbox Text_FlipTestStart, MSGBOX_DEFAULT
	special Special_InitDialoguePortraitSystem
	
	SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
	delay 30
	msgbox Text_NormalPortrait, MSGBOX_DEFAULT
	
	SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_HORIZONTAL, FLIP_NONE
	delay 30
	msgbox Text_HorizontalFlip, MSGBOX_DEFAULT
	
	SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_NONE, FLIP_VERTICAL
	delay 30
	msgbox Text_VerticalFlip, MSGBOX_DEFAULT
	
	SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_HORIZONTAL, FLIP_VERTICAL
	delay 30
	msgbox Text_BothFlip, MSGBOX_DEFAULT
	
	SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_NONE, FLIP_NONE
	delay 30
	msgbox Text_NormalAgain, MSGBOX_DEFAULT
	
	HIDE_PORTRAIT PORTRAIT_LEFT
	delay 15
	
	SHOW_PORTRAIT_FLIP PORTRAIT_RIGHT, 3, 3, 3, FLIP_HORIZONTAL, FLIP_NONE
	delay 30
	msgbox Text_DirectFlipPortrait, MSGBOX_DEFAULT
	
	HIDE_PORTRAIT PORTRAIT_RIGHT
	delay 15
	
	special Special_CleanupDialoguePortraitSystem
	return

// 自定义头像测试
EventScript_PortraitCustomTest::
	msgbox Text_CustomTestStart, MSGBOX_DEFAULT
	special Special_InitDialoguePortraitSystem
	
	SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1000, 2000
	delay 30
	msgbox Text_CustomPortrait1, MSGBOX_DEFAULT
	
	SHOW_PORTRAIT_FLIP PORTRAIT_RIGHT, 2, 1001, 2001, FLIP_HORIZONTAL, FLIP_NONE
	delay 30
	msgbox Text_CustomPortrait2, MSGBOX_DEFAULT
	
	HIDE_PORTRAIT PORTRAIT_LEFT
	delay 15
	HIDE_PORTRAIT PORTRAIT_RIGHT
	delay 15
	
	special Special_CleanupDialoguePortraitSystem
	return

// 双头像对话测试
EventScript_PortraitDualTest::
	msgbox Text_DualTestStart, MSGBOX_DEFAULT
	special Special_InitDialoguePortraitSystem
	
	SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
	SHOW_PORTRAIT PORTRAIT_RIGHT, 2, 2, 2
	delay 30
	
	msgbox Text_Dialogue1, MSGBOX_DEFAULT
	delay 20
	
	HIDE_PORTRAIT PORTRAIT_LEFT
	delay 15
	msgbox Text_Dialogue2, MSGBOX_DEFAULT
	delay 20
	
	SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
	delay 15
	HIDE_PORTRAIT PORTRAIT_RIGHT
	delay 15
	msgbox Text_Dialogue3, MSGBOX_DEFAULT
	delay 20
	
	SHOW_PORTRAIT PORTRAIT_RIGHT, 2, 2, 2
	delay 15
	msgbox Text_Dialogue4, MSGBOX_DEFAULT
	delay 20
	
	HIDE_PORTRAIT PORTRAIT_LEFT
	delay 15
	HIDE_PORTRAIT PORTRAIT_RIGHT
	delay 15
	
	special Special_CleanupDialoguePortraitSystem
	return

// 高级功能测试
EventScript_PortraitAdvancedTest::
	msgbox Text_AdvancedTestStart, MSGBOX_DEFAULT
	special Special_InitDialoguePortraitSystem
	
	SHOW_PORTRAIT PORTRAIT_LEFT, 1, 1, 1
	delay 30
	msgbox Text_DynamicFlipStart, MSGBOX_DEFAULT
	
	call EventScript_FlipAnimation
	call EventScript_TrainerGallery
	
	HIDE_PORTRAIT PORTRAIT_LEFT
	delay 15
	
	special Special_CleanupDialoguePortraitSystem
	return

// 翻转动画效果
EventScript_FlipAnimation::
	SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_HORIZONTAL, FLIP_NONE
	delay 20
	
	SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_NONE, FLIP_VERTICAL
	delay 20
	
	SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_HORIZONTAL, FLIP_VERTICAL
	delay 20
	
	SET_PORTRAIT_FLIP PORTRAIT_LEFT, FLIP_NONE, FLIP_NONE
	delay 20
	
	return

// 训练师头像画廊
EventScript_TrainerGallery::
	SHOW_PORTRAIT PORTRAIT_RIGHT, 10, 10, 10
	delay 30
	
	HIDE_PORTRAIT PORTRAIT_RIGHT
	delay 15
	
	return

// ====================
// 文本定义
// ====================

Text_PortraitTestIntro::
	.string "头像系统测试开始!\n"
	.string "将测试所有头像功能。$"

Text_BasicTestStart::
	.string "基础功能测试:\n"
	.string "测试头像的显示和隐藏。$"

Text_LeftPortraitShown::
	.string "左侧头像已显示。\n"
	.string "这是训练师的头像。$"

Text_RightPortraitShown::
	.string "右侧头像已显示。\n"
	.string "这是另一个训练师。$"

Text_LeftPortraitHidden::
	.string "左侧头像已隐藏。$"

Text_RightPortraitHidden::
	.string "右侧头像已隐藏。\n"
	.string "基础测试完成。$"

Text_FlipTestStart::
	.string "翻转功能测试:\n"
	.string "测试头像的各种翻转效果。$"

Text_NormalPortrait::
	.string "这是正常的头像。$"

Text_HorizontalFlip::
	.string "现在是水平翻转状态。\n"
	.string "头像被左右翻转了。$"

Text_VerticalFlip::
	.string "现在是垂直翻转状态。\n"
	.string "头像被上下翻转了。$"

Text_BothFlip::
	.string "现在是双向翻转状态。\n"
	.string "头像同时进行了水平和垂直翻转。$"

Text_NormalAgain::
	.string "恢复到正常状态。$"

Text_DirectFlipPortrait::
	.string "这是直接显示的翻转头像。\n"
	.string "一步到位,无需等待!$"

Text_CustomTestStart::
	.string "自定义头像测试:\n"
	.string "测试自定义头像资源。$"

Text_CustomPortrait1::
	.string "这是自定义头像1。\n"
	.string "使用自定义的图形和调色板。$"

Text_CustomPortrait2::
	.string "这是自定义头像2。\n"
	.string "并且带有水平翻转效果。$"

Text_DualTestStart::
	.string "双头像对话测试:\n"
	.string "模拟对话场景中的头像切换。$"

Text_Dialogue1::
	.string "玩家:你好!\n"
	.string "我是这个地区的训练师。$"

Text_Dialogue2::
	.string "NPC:欢迎来到我们的城镇!\n"
	.string "这里有很多强大的训练师。$"

Text_Dialogue3::
	.string "玩家:听起来很有挑战性!\n"
	.string "我很期待与他们对战。$"

Text_Dialogue4::
	.string "NPC:祝你好运!\n"
	.string "如果你需要帮助,随时来找我。$"

Text_AdvancedTestStart::
	.string "高级功能测试:\n"
	.string "测试动态效果和特殊功能。$"

Text_DynamicFlipStart::
	.string "现在演示动态翻转效果:$"

Text_PortraitTestComplete::
	.string "所有头像功能测试完成!\n"
	.string "系统运行正常。$"

// ====================
// 使用说明
// ====================

/*
使用方法:
1. 将此脚本添加到你的地图脚本文件中
2. 创建一个NPC事件,调用 EventScript_PortraitSystemTest
3. 与NPC对话即可开始测试

测试内容:
- 基础显示/隐藏功能
- 水平/垂直翻转效果
- 自定义头像资源
- 双头像对话场景
- 动态翻转动画
- 训练师头像画廊

注意事项:
- 确保已正确实现头像系统的所有函数
- 自定义头像需要预先准备好图形和调色板资源
- 训练师ID需要根据实际情况调整
- 可以根据需要调整delay的时间长度
*/
© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容