宝可梦营地 - 训练师的冒险驿站 - 改版教程
type: channel
link: https://pokedream.cn/index.php/category/%E6%94%B9%E7%89%88%E6%95%99%E7%A8%8B/
items: [GBA教程]使用tiled搭配porytiles快捷处理地图块
items: [NDS教程]四代地图制作指南1 - 介绍
items: [GBA]ER2.65V2 简略汉化版 + 全特性介绍文本
items: [NDS教程]四代技能动画脚本教程
items: trainer 脚本指令详解
items: [GBA教程]新增道具
items: [GBA教程]跳过开头动画、卡车、取名以及默认固定男女主
items: [GBA教程]新增（非替换）NPC的行走图
items: [GBA教程]实现静态CG图显示
items: [GBA教程]pokeemerald新增NPC头像显示，并支持镜像翻转

[GBA教程]使用tiled搭配porytiles快捷处理地图块
type: item
date: 2026-07-31T19:21:57+08:00
description: 前言本教程仅适用于WSL反编译游戏，binary改版请自行摸索为了解决切割地图块过于繁琐的问题，这里介绍一套使用Tiled拼地图块并用porytile处理进反编译项目的方式，跳过了切割重复素材和拼地图块的过程。准备1.处理好的地图块素材，每个素材代表一个色板，必须是索引模式的16色素材2.Tiled3.porytile，(由于老外的项目比较司马，没有发布编译好的软件，这里提供一个我编译后的版本)https://pan.baidu.com/s/1xpoR5-rSkdV1tah484Gb4A 提取码: j2kq4.porymap用Tiled拼地图块打开Tiled，选择新建地图，块大小设置8x8，地图大小宽度必须为16，高度不得超过128新建完毕后右下角切换到图块集，把处理好的地图块素材拖到窗口，生成一个新的tsx文件为了能更好的浏览地图块的效果，可以勾选设置透明度，把颜色设置为素材图片的背景色，这样在窗口里显示的地图块不会有背景颜色注意：请勾选嵌入地图右键图层窗口，新建两个图块层，保证有且仅有三个图层，这三个图层上的地图块会被设置到porymap里的上中下层，接下来将地图块素材组合拼到左边的地图上，最后保存成tiled.tmx处理tmx文件把拼好的tiled.tmx文件放到需要修改的地图块文件内，比如要修改原版T0的general地图块，则把tmx放到data/tilesets/primary/general文件夹，并把porytiles放到项目的根目录下保存下面的python脚本和porytiles放到同一处#!/usr/bin/env python3 """ Tiled地图分层导出工具 """ import os import sys import pytmx from PIL import Image from pathlib import Path import subprocess if len(sys.argv) < 3: print("使用方法：python <py文件> <地图块名字> <T0或者T1>") sys.exit(1) runOnWindow = False tileset_type = sys.argv[2] if tileset_type == "0": project_path = f"data/tilesets/primary/{sys.argv[1]}/" else: primary_path = f"data/tilesets/primary/{sys.argv[3]}/" project_path = f"data/tilesets/secondary/{sys.argv[1]}/" tmx = pytmx.TiledMap(project_path + "tiled.tmx") output_path = project_path + "porytile" color = (255, 0, 255) # Window路径转为Ubuntu def windows_to_ubuntu_path(win_path): if runOnWindow == True: return win_path path = win_path.replace('\\', '/') # 处理盘符 if ':' in path: drive, rest = path.split(':', 1) drive_letter = drive.lower().strip() return f'/mnt/{drive_letter}{rest}' return path # 获取指定id图块的原图片路径 def GetTilesetSource(index): # 只有一张图块 if len(tmx.tilesets) == 1: return tmx.tilesets[0].source, 0 for i, tileset in enumerate(tmx.tilesets): if index < tmx.tilesets[i + 1].firstgid: return tileset.source, i return None, 0 def copy_8x8_block(src_image, src_x, src_y, dest_image, dest_x, dest_y): """ 从源图像复制8x8像素块到目标图像 参数: src_image: 源图像 (Image对象) src_x, src_y: 源图像中的起始坐标 dest_image: 目标图像 (Image对象) dest_x, dest_y: 目标图像中的起始坐标 """ # 1. 从源图像裁剪8x8区域 block = src_image.crop(…

[NDS教程]四代地图制作指南1 - 介绍
type: item
date: 2026-07-31T19:18:30+08:00
description: 本文转载自https://ds-pokemon-hacking.github.io/docs/generation-iv/guides/mapping/第四代宝可梦地图制作入门本页面旨在帮助你了解与第四代任天堂DS宝可梦游戏（钻石、珍珠、白金、心金、魂银版本）地图定义相关的组件，并理解如何与这些组件交互以创建和编辑自定义地图。概念、约束、结构与指导层级/结构与第四代宝可梦ROM改版的许多元素一样，数据结构始于标头，此层级内的项目会继承和/或共享信息。概要结构的简单轮廓如下：标头区域数据地图纹理包 (.nsbtx)建筑纹理包 (.nsbtx)全局动画文件 (.nsbta)*矩阵地图 (.nsbmd)地形数据 (.bdhc)通行数据 (.per)背景声音数据 (.bgs)*建筑数据 (.bld)其他各种与地图不直接相关的数据和文件（仅限心金魂银）*这些数据中，有些在更高层级中是唯一的（例如，每个地图文件必须存在且仅存在一套通行数据），而其他数据则具有不同的关系（一个矩阵可能包含一个或多个地图文件的一个或多个实例，反之，任意数量的标头可能使用同一个地图纹理包）。地图第四代宝可梦游戏中的地图模型是单一的3D模型，在概念上可划分为1024个网格方块（32x32）的网格。每个网格方块在3D建模软件（如Blender）中查看时相当于16米，因此地图名义尺寸为512米见方。然而，大多数常用的ROM改版工具会应用0.0625的比例因子（例如，此比例在DSPRE地图编辑器的“建筑”选项卡中可见）。为简单起见，本指南将每个1/32的分区称为一个“单位”距离；因此，一个地图名义上是32个单位宽乘32个单位深，每个网格方块是一个单位宽乘一个单位深。地图模型可以扩展到32x32单位的方形区域之外，但“地图”层级以下的数据不能。实际上，这可能用于在地图边界之外提供额外的纯视觉效果元素，或让模型元素/几何体跨越相邻地图之间的边界。一个单位（一个网格方块的宽度）大致相当于：玩家行走图的宽度。玩家或其他行走图在游戏中能完成的最小完整移动距离。每个地图网格方块都附有来自较低层级（低于“地图”层级）的一些数据点：属性文件扩展名详情地形数据.bdhc每个地形“板块”可能与一个网格方块相交零次或多次。这些板块代表玩家和其他主世界精灵可以在（每个网格方块上）横向移动的Z轴高度，它们可能与“地面”平行，也可能倾斜以允许上升或下降。地形板块的边界在水平方向上（X和Y轴）必须遵循网格方块边界，但在垂直方向上（Z轴）可以设置为更小的距离分割。移动权限.per每个网格方块必须包含以下各项的数据：--> 碰撞 一小部分值，用于确定对主世界精灵移动的重大限制，以及一些相关声音（例如，在心金魂银中，值04允许移动，并在玩家精灵在此网格方块上移动时产生草丛沙沙声；而值80则阻止任何主世界精灵在此网格方块上移动，无论地形数据如何）。--> 类型 更大的值集，提供游戏内的附加功能（例如，在心金魂银中，02是在玩家与网格方块相交时触发高草动画以及（如果填充了相应的遭遇表和遭遇率）触发野生宝可梦遭遇所需的权限类型）。背景声音.bgs每个声音“板块”可能与一个网格方块相交零次或多次。这些板块决定了当玩家精灵位于该网格方块上时，是否会播放特定的背景声音。仅限心金魂银。编辑地图与工具抽象如上所述，在这些游戏中，每个地图都是一个独立的3D模型，并为该模型及其内部的1024个网格方块分配了各种附加属性。特别重要的是，除了每个地图的单个3D模型之外，常用的（且通常更复杂的）3D模型也可以“放置”在此之上，这些被称为“建筑”（无论它们是否实际代表一个建筑物）。可以通过多种方式改变原始（或“原版”）地图模型：编辑3D模型：从游戏中导出完整的3D模型，在3D建模软件（如Blender）中进行修改，然后重新导入。放置附加建筑：通过使用DSPRE工具在地图上放置更多建筑实例（无论是来自原版游戏的建筑还是自定义建筑），进行增量更改（添加以前不存在的东西，例如一片高草或一个悬崖）。重建地图：使用PDSMS工具，利用其基于图块的资产放置系统，重建地图（对原始地图进行小幅或大幅更改），然后重新导入。重新贴图：仅替换模型的纹理，而不改变几何体，可以通过更改.nsbtx中的图像纹理（同时保留相同的名称）来实现。选项三通常是对地图进行重大更改（或涉及移除地图某些元素的更改）的最容易上手的方法。选项一可能适合有这些工具使用经验的人，但也有限制（因为更改仅应用于该地图模型的特定实例，此处投入的努力不能直接惠及其他地图）。值得注意的是，在某些情况下，原始地图难以在PDSMS中原样重建，因为某些元素无法与基于图块的资产放置系统很好地配合。图块为了在PDSMS中创建地图，必须创建组件模型，这些是可重用的组件，包含整体地图的某个（通常较小的）方…

[GBA]ER2.65V2 简略汉化版 + 全特性介绍文本
type: item
date: 2026-07-30T16:05:20+08:00
description: 下载地址： 提取码: dikj下载请使用浏览器搜索功能恶臭 ： 攻击获得+10%的畏缩几率.连续攻击技能的每一击单独计算畏缩几率.当该宝可梦在场时,剧毒地形的回合数不会减少.降雨 ： 降雨特性会在宝可梦进入战斗时降下雨水,持续8回合（携带潮湿岩石时为12回合）.雨天使水系招式威力提升50%,火系伤害降低50%.部分招式和特性在雨天下有特殊效果.无法覆盖原始天气.加速 ： 该宝可梦每在场完成一个完整的回合,其速度在回合结束时提升1级.入场的当回合不会触发.战斗盔甲 ： 受到的伤害降低20%（x0.8）,与其他减伤效果乘法叠加.此外,免疫击中要害,会心一击将作为普通攻击处理,并且不会触发攻击要害的效果（例如“至骨”的流血效果）.结实 ： 当该宝可梦HP全满时,能够承受任何一次攻击并至少留下1点HP.此效果如同携带了气势披带,因此无法抵御连续攻击技能或后续攻击（例如“余震”、“雷召”）.湿气 ： 如果该宝可梦与另一只宝可梦发生接触（无论是攻击方还是防御方）,对方的属性会变为纯水属性.此效果在连续招式或优先度招式命中时立即发生,可以在对手的连续攻击过程中消除其本系加成.柔软 ： 该宝可梦受到的所有反伤伤害减半；包括具有比例反伤的招式（如勇鸟猛攻）、具有撞伤风险的招式（如飞膝踢）、或固定反伤的招式（如钢炮光线）.麻痹状态无法影响该宝可梦,且获得此特性时,若已处于麻痹状态则会立刻治愈.沙隐 ： 在沙暴中,对手的命中率变为原来的1/1.25,且自身免疫沙暴伤害.会被无防御或破格特性识破.静电 ： 无论该宝可梦是攻击方还是被攻击方,都有几率使对手陷入麻痹.非接触攻击有10%几率麻痹,接触攻击有30%几率麻痹.蓄电 ： 该宝可梦能够吸收电属性招式,将其转化为能量,恢复其最大HP的25%.对电属性伤害及其附加效果（如电磁波的麻痹）完全免疫.对于水/飞行属性宝可梦来说,是消除4倍电系弱点的完美选择.储水 ： 该宝可梦免疫水属性招式,并且受到水属性招式攻击时会恢复其最大HP的25%.迟钝 ： 该宝可梦免疫着迷、惊吓、威吓和挑衅.在获得此特性时,如果已经处于上述任意状态,则会立刻解除.无关天气 ： 上场时清除所有天气,并且在该宝可梦在场期间使所有天气效果（包括原始天气）失效.天气仍可以被设置,但在该宝可梦下场前,不会提供任何伤害加成或触发与天气相关的特性.复眼 ： 该宝可梦的招式命中率变为1.3倍.不眠 ： 防止任何方式陷入睡眠状态,包括催眠招式、特性（如哈欠）以及其他可能导致睡眠的效果.使用招式睡觉将完全失败.若在获得此特性前已处于睡眠状态（例如通过烦恼种子或特性交换）,则会立刻醒来.可被破格等特性识破变色 ： 受到伤害前,将自身转变为防御效果最佳的纯属性.优先考虑免疫而非抗性.每回合仅触发一次.免疫 ： 防止中毒.所有毒属性伤害减半.与其他减伤效果乘法叠加.获得此特性时若已中毒,则立刻治愈.引火 ： 引火特性使宝可梦完全免疫火属性招式.受到火属性攻击后,自身的火属性招式威力提升50%,直到下场为止.多次被火属性攻击命中不会使效果叠加.鳞粉 ： 阻挡所有攻击招式的追加效果.免疫所有入场陷阱.免疫所有粉末类招式,包括催眠粉、麻痹粉、毒粉、蘑菇孢子、棉孢子、愤怒粉、粉末和魔法粉.我行我素 ： 免疫混乱、威吓和惊吓.获得此特性时若已混乱,则立刻解除混乱状态.吸盘 ： 防止被招式或红牌强制替换下场.威吓 ： 进入战斗时,降低所有对手宝可梦的攻击1级.影子游戏 ： 阻止所有不具有影子游戏特性或非幽灵属性的对手替换下场.携带逃脱按键或使用替换招式（如快速折返）的宝可梦可以逃脱如果在战斗中中途替换上场,效果将在下一回合生效.粗糙皮肤 ： 对使用接触类招式的攻击者造成其最大HP1/8的伤害.对于连续攻击招式,每次命中都会触发.神奇守护 ： 只有效果绝佳的招式或间接伤害能够伤害到该宝可梦.其他所有直接攻击造成的伤害均为零.中毒或特性伤害等其他来源的伤害照常生效.飘浮 ： 该宝可梦免疫地面属性招式以及其他地面效果,如撒菱和场地状态.飞行属性招式伤害提升25%.孢子 ： 受到接触类招式攻击时,有30%的几率使攻击者陷入睡眠、麻痹或中毒状态.状态类型随机选择.同步 ： 当被施加非瞬间异常状态（睡眠除外）时,攻击者也会陷入相同的状态.此效果可穿透替身.若该宝可梦在被施加异常状态时能够治愈自身,则会在治愈前先将异常状态施加给攻击者.恒净之躯 ： 免疫所有由招式或特性引起的能力降低效果.包括因使用过热等招式导致的自身能力降低.自然回复 ： 替换下场时,治愈所有异常状态.避雷针 ： 该宝可梦会吸引电属性招式,并对其免疫.此外,受到电属性招式攻击后,自身最高的攻击能力提升1级.天恩 ： 自身攻击招式的追加效果触发几率加倍.悠游自如 ： 下雨天气时,速度提升50%.叶绿素 ： 大晴天天…

[NDS教程]四代技能动画脚本教程
type: item
date: 2026-07-30T16:03:13+08:00
description: HG-Engine 背景切换与粒子系统指令教程 一、粒子系统指令 loadparticlefromspa - 加载粒子文件 loadparticlefromspa [粒子编号] [粒子文件编号] 功能说明： 将指定的粒子文件加载到内存中的指定编号位置。后续播放粒子效果时需要使用这个编号来引用。参数说明： 参数 类型 说明 粒子编号 整数 内存槽位编号（0-15），用于后续引用 粒子文件编号 整数 SPA文件中的粒子资源ID 使用示例： // 将371号粒子加载到0号内存槽 loadparticlefromspa 0, 371 // 将374号粒子加载到1号内存槽 loadparticlefromspa 1, 374 注意事项： 粒子编号通常从0开始顺序使用 同一编号重复加载会覆盖之前的粒子数据 建议在战斗开始前预先加载所需粒子 二、背景切换指令 changebg - 切换战斗背景 changebg [背景编号] [flag] 功能说明： 改变当前的战斗背景。需要配合 waitforchangebg 等待背景加载完成。 参数说明： 参数 类型 说明 背景编号 整数 目标背景的ID flag 整数（位掩码） 切换效果标志，控制背景的显示方式和运动行为 flag 参数详解 flag 是一个32位整数值，由低16位的切换模式和高16位的子系统功能组合而成。 低16位：切换动作模式 // 低16位（0x0000FFFF）控制背景切换的过渡效果 #define WEDEF_HAIKEI_MODE_BLEND (0) // 混合模式：新背景从透明逐渐显现 #define WEDEF_HAIKEI_MODE_FADE (1) // 淡入淡出模式：先黑屏，再显示新背景 #define WEDEF_HAIKEI_MODE_NONE (2) // 无效果：直接切换 #define WEDEF_HAIKEI_MODE_END (3) // 结束标记（内部使用） // 恢复时的模式（用于背景切换完成后恢复到正常状态） #define WEDEF_HAIKEI_RC_MODE_BLEND (0) // 恢复时使用混合模式 #define WEDEF_HAIKEI_RC_MODE_FADE (1) // 恢复时使用淡入淡出 #define WEDEF_HAIKEI_RC_MODE_NONE (2) // 恢复时无效果 高16位：子系统功能标志 高16位控制背景的运动、光栅效果、调色板动画等附加功能。这些标志可以按位或（|）组合使用。 // 高16位（0xFFFF0000）控制背景的运动和其他特效 #define WEDEF_HAIKEI_SBIT_NONE (0x0000 << 16) = 0x00000000 = 0 // 无任何功能 #define WEDEF_HAIKEI_SBIT_MOVE (0x0002 << 16) = 0x00020000 = 131072 // 背景开始移动 #define WEDEF_HAIKEI_SBIT_STOP (0x0004 << 16) = 0x00040000 = 262144 // 背景停止移动 #define WEDEF_HAIKEI_SBIT_EXIT (0x0008 << 16) = 0x00080000 = 524288 // 子系统停止 #define WEDEF_HAIKEI_SBIT_NONE_DROP (0x0010 << 16) = 0x00100000 = 1048576 // 无掉落效果 #define WEDEF_HAIKEI_SBIT_LASTER (0x0020 << 16) = 0x00200000 = 2097152 // 启用光栅效果 #define WEDEF_HAIKEI_SBIT_LASTER_STOP (0x0040 << 16) = 0x00400000 = 4194304 // 停止光栅效果 #define WEDEF_HAIKEI_SBIT_PLANM (0x0080 << 16) = 0x00800000 = 8388608 // BG调色板动画开始 #define WEDEF_HAIKEI_SBIT_PLANM_STOP (0x0100 << 16) = 0x01000000 = 16777216 // BG调色板动画停止 #define WEDEF_HAIKEI_SBIT_DEF_BG_MOVE (0x0200 << 16) = 0x02000000 = 33554432 // 背景先行滚动 waitforchangebg - 等待背景切换完成 waitforchangebg 功能说明： 等待 changebg 指令的背景加载和切换效果完全完成。必须在 c…

trainer 脚本指令详解
type: item
date: 2026-07-30T08:55:45+08:00
description: 在 Pokémon 反编译项目中，"trainer" 相关的脚本指令用于触发训练家对战。核心指令是 trainerbattle ，它通过一系列宏封装来简化使用。下面从底层到上层逐一说明。一、底层操作码在 script_commands.h 中定义了 4 个相关的操作码：二、核心宏 trainerbattle （最底层）定义在 asm/macros/event.inc 。.macro trainerbattle type:req, localIdA:req, trainer_a:req, intro_text_a:req, lose_text_a:req, event_script_a:req, localIdB:req, trainer_b:req, intro_text_b:req, lose_text_b:req, event_script_b:req, victory_text:req, cannot_battle:req, isDouble:req, playMusicA:req, playMusicB:req, isRematch:req, rival_battle_flags=0它编码成一个二进制结构，C 端的对应结构体定义在 battle_setup.h ：typedef union PACKED TrainerBattleParameter{struct PACKED _TrainerBattleParameter { u8 isDoubleBattle:1; // 位0：是否双打 u8 isRematch:1; // 位1：是否再战 u8 playMusicA:1; // 位2：是否播放训练家A的音乐 u8 playMusicB:1; // 位3：是否播放训练家B的音乐 u8 mode:4; // 位4-7：战斗模式 u8 objEventLocalIdA; // 训练家A的地图对象ID u16 opponentA; // 训练家A的ID（TRAINER_xxx） u8 *introTextA; // 训练家A的出战文本指针 u8 *defeatTextA; // 训练家A的战败文本指针 u8 *battleScriptRetAddrA;// 训练家A的战后脚本指针 u8 objEventLocalIdB; // 训练家B的地图对象ID u16 opponentB; // 训练家B的ID u8 *introTextB; // 训练家B的出战文本 u8 *defeatTextB; // 训练家B的战败文本 u8 *battleScriptRetAddrB;// 训练家B的战后脚本 u8 *victoryText; // 胜利文本（早期劲敌使用） u8 *cannotBattleText; // 无法对战时的文本（双打时PM不足） u8 rivalBattleFlags; // 劲敌战斗标志位 } params; u8 data[sizeof(struct _TrainerBattleParameter)];} TrainerBattleParameter;三、战斗模式常量定义在 battle_setup.h ：#define TRAINER_BATTLE_SINGLE 0 // 普通单打define TRAINER_BATTLE_CONTINUE_SCRIPT_NO_MUSIC 1 // 单打+继续脚本（无音乐切换）define TRAINER_BATTLE_CONTINUE_SCRIPT 2 // 单打+继续脚本define TRAINER_BATTLE_SINGLE_NO_INTRO_TEXT 3 // 单打（无出战文本）define TRAINER_BATTLE_DOUBLE 4 // 双打define TRAINER_BATTLE_REMATCH 5 // 再战（单打）define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE 6 // 双打+继续脚本define TRAINER_BATTLE_REMATCH_DOUBLE 7 // 再战（双打）define TRAINER_BATTLE_CONTINUE_SCRIPT_DOUBLE_NO_MUSIC 8 // 双打+继续脚本（无音乐切换）define TRAINER_BATTLE_TWO_TRAINERS_NO_INTRO 13 // 双打（两名独立训练家，无出场文本）define TRAINER_BATTLE_EARLY_RIVAL 14 // 早期劲敌战斗四、常用封装的宏（实际开发中使用的）4.1 trainerbattle_single — 普通单打对战.macro trainerbattle_single trai…

[GBA教程]新增道具
type: item
date: 2026-07-30T08:53:57+08:00
description: 一共需要修改四个文件。include\constants\items.h新增道具名称，例如：ITEM_TREE_OF_TIME_LEAF = 874,src\data\items.h新增道具信息，例如：[ITEM_TREE_OF_TIME_LEAF] ={ .name = ITEM_NAME("时之树的叶子"), .price = 500, .description = COMPOUND_STRING( "来自时之树的\n" "神秘叶子。据说拥有\n穿越时空的力量。"), .pocket = POCKET_KEY_ITEMS, .sortType = ITEM_TYPE_UNCATEGORIZED, .type = ITEM_USE_BAG_MENU, .fieldUseFunc = ItemUseOutOfBattle_CannotUse, .iconPic = gItemIcon_Leaf, .iconPalette = gItemIconPalette_Leaf, }, src\data\graphics\items.h 新增素材引用代码： const u32 gItemIcon_Leaf[] = INCBIN_U32("graphics/items/icons/leaf.4bpp.smol");const u16 gItemIconPalette_Leaf[] = INCBIN_U16("graphics/items/icons/leaf.gbapal");include\graphics.h新增素材定义：extern const u32 gItemIcon_Leaf[];extern const u16 gItemIconPalette_Leaf[];

[GBA教程]跳过开头动画、卡车、取名以及默认固定男女主
type: item
date: 2026-07-30T08:51:56+08:00
description: 修改这个src\main_menu.c文件的函数Task_NewGameBirchSpeech_BoyOrGirlstatic void Task_NewGameBirchSpeech_BoyOrGirl(u8 taskId) { // 直接设置男主性别 gSaveBlock2Ptr->playerGender = MALE; // 设置默认男主名字（使用第一个预设名字） NewGameBirchSpeech_SetDefaultPlayerName(0); // 跳过性别选择和取名流程，直接进入游戏 BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); gTasks[taskId].func = Task_NewGameBirchSpeech_Cleanup; }修改Task_HandleMainMenuAPressed函数，当选择"从头开始"时，跳过博士介绍的初始剧情，直接设置男主性别和名字，然后调用CB2_NewGame函数进入游戏。switch (action) { case ACTION_NEW_GAME: default: gPlttBufferUnfaded[0] = RGB_BLACK; gPlttBufferFaded[0] = RGB_BLACK; // 直接设置男主性别 gSaveBlock2Ptr->playerGender = MALE; // 设置默认男主名字（使用第一个预设名字） NewGameBirchSpeech_SetDefaultPlayerName(0); // 跳过博士介绍的初始剧情，直接进入游戏 SetMainCallback2(CB2_NewGame); DestroyTask(taskId); break; 这里删除了gTasks[taskId].func = Task_NewGameBirchSpeech_Init; 这个文件src\overworld.c，修改CB2_NewGame函数 void CB2_NewGame(void){FieldClearVBlankHBlankCallbacks(); StopMapMusic(); ResetSafariZoneFlag_(); NewGameInitData(); ResetInitialPlayerAvatarState(); PlayTimeCounter_Start(); ScriptContext_Init(); UnlockPlayerFieldControls(); gFieldCallback = NULL; // 跳过卡车动画 gFieldCallback2 = NULL; DoMapLoadLoop(&gMain.state); SetFieldVBlankCallback(); SetMainCallback1(CB1_Overworld); SetMainCallback2(CB2_Overworld); 修改SetUpCopyrightScreen函数，让它在版权封面结束后直接跳转到标题屏幕，而不是继续播放介绍动画。 case COPYRIGHT_START_INTRO: if (UpdatePaletteFade()) break; // 跳过介绍动画，直接进入标题屏幕 SetMainCallback2(CB2_InitTitleScreen); if (gMultibootProgramStruct.gcmb_field_2 != 0) { if (gMultibootProgramStruct.gcmb_field_2 == 2) { // check the multiboot ROM header game code to see if we already did this if (*(u32 *)(EWRAM_START + 0xAC) == COLOSSEUM_GAME_CODE) { CpuCopy16(&gMultiBootProgram_PokemonColosseum_Start, (void *)EWRAM_START, sizeof(gMultiBootProgram_PokemonColosseum_Start)); *(u32 *)(EWRAM_START + 0xAC) = COLOSSEUM_GAME_CODE; } GameCubeMultiBoot_ExecuteProgram(&gMultibootProgramStruct); } }

[GBA教程]新增（非替换）NPC的行走图
type: item
date: 2026-07-30T08:50:45+08:00
description: 以行走图mingdi（冥帝）为例。在event_objects.h中添加mingdi的枚举值，并更新NUM_OBJ_EVENT_GFX和的OBJ_EVENT_PAL_TAG_DYNAMIC值。#define OBJ_EVENT_GFX_MINGDI 243 #define NUM_OBJ_EVENT_GFX 244#define OBJ_EVENT_PAL_TAG_MINGDI 0x1124define OBJ_EVENT_PAL_TAG_DYNAMIC 0x1125在object_event_graphics.h文件中添加新的精灵数据定义，为mingdi创建图像和调色板引用。const u32 gObjectEventPic_Mingdi[] = INCBIN_U32("graphics/object_events/pics/people/mingdi.4bpp");const u16 gObjectEventPal_Mingdi[] = INCBIN_U16("graphics/object_events/pics/people/mingdi.gbapal");在event_object_movement.c文件的sObjectEventSpritePalettes数组中添加mingdi的色板条目，以便游戏能够加载和使用这个色板。{gObjectEventPal_Mingdi, OBJ_EVENT_PAL_TAG_MINGDI}, 在object_event_pic_tables.h文件中为mingdi创建图像表，使用标准的行走动画结构。static const struct SpriteFrameImage sPicTable_Mingdi[] = {overworld_ascending_frames(gObjectEventPic_Mingdi, 2, 4),};在object_event_graphics_info.h文件的末尾，在那里添加新的图形信息结构gObjectEventGraphicsInfo_Mingdiconst struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Mingdi = {.tileTag = TAG_NONE, .paletteTag = OBJ_EVENT_PAL_TAG_MINGDI, .reflectionPaletteTag = OBJ_EVENT_PAL_TAG_NONE, .size = 256, .width = 16, .height = 32, .paletteSlot = PALSLOT_NPC_1, .shadowSize = SHADOW_SIZE_M, .inanimate = FALSE, .compressed = FALSE, .tracks = TRACKS_FOOT, .oam = &gObjectEventBaseOam_16x32, .subspriteTables = sOamTables_16x32, .anims = sAnimTable_Standard, .images = sPicTable_Mingdi, .affineAnims = gDummySpriteAffineAnimTable,};在object_event_graphics_info_pointers.h文件中添加mingdi的指针引用。[OBJ_EVENT_GFX_MINGDI] = &gObjectEventGraphicsInfo_Mingdi,在object_event_graphics_info_pointers.h文件中添加gObjectEventGraphicsInfo_Mingdi的外部声明。extern const struct ObjectEventGraphicsInfo gObjectEventGraphicsInfo_Mingdi;在spritesheet_rules.mk添加编译规则$(OBJEVENTGFXDIR)/people/mingdi.4bpp: %.4bpp: %.png$(GFX) $< $@ -mwidth 2 -mheight 4

[GBA教程]实现静态CG图显示
type: item
date: 2026-07-29T17:07:02+08:00
description: 在src/scrcmd.c 文件末尾添加 ShowCG 相关的函数和数据结构。#define PLAY_CG (0) #define PLAY_MESSAGE (1) #define WAIT_BUTTON (2) #define PLAY_FADE (3) #define PLAY_END (4) #define PAUSE_TIME (5) static const struct BgTemplate sBgTemplates[] = { { .bg = 0, .charBaseIndex = 3, .mapBaseIndex = 31, .screenSize = 0, .paletteMode = 0, .priority = 0, .baseTile = 0 }, { .bg = 2, .charBaseIndex = 0, .mapBaseIndex = 29, .screenSize = 0, .paletteMode = 1, .priority = 3, .baseTile = 0 }, }; static const struct WindowTemplate sWindowTemplates[] = { { .bg = 0, .tilemapLeft = 3, .tilemapTop = 15, .width = 24, .height = 4, .paletteNum = 15, .baseBlock = 0x010 }, DUMMY_WIN_TEMPLATE, }; //tilemap单独提供下载，放在对应文件夹即可 //添加CG图 static const u8 sDefaultCG_TileMap[] = INCBIN_U8("graphics/cg/raw.bin.lz"); static const u8 sCGA1_Tiles[] = INCBIN_U8("graphics/cg/CG1.8bpp.lz"); static const u8 sCGA1_Pal[] = INCBIN_U8("graphics/cg/CG1.gbapal"); static const u8 sCGA2_Tiles[] = INCBIN_U8("graphics/cg/CG2.8bpp.lz"); static const u8 sCGA2_Pal[] = INCBIN_U8("graphics/cg/CG2.gbapal"); //CG图编号 static const u8* sCGTable[][10] = { [0] = {sCG1_Tiles, sCG1_Pal}, [1] = {sCG2_Tiles, sCG2_Pal}, }; static const u8 sText_Color[] = {0, 1, 2}; //对话表编号 static const u8* sCGMessage[] = { [0] = COMPOUND_STRING("?:如果时间能永远\n停留在这个时刻就好了。\p洛汐:是啊。"), [1] = COMPOUND_STRING("洛汐:你知道启明星吗？\n就是在太阳升起前，\l最后那颗最亮的星星。\p?:当然知道啊。"), }; //CG表编号 static const u8 sCGAnimList[][60] = { [0] = { PLAY_CG, 0, // 播放CG PLAY_MESSAGE, 0, // 播放对话 WAIT_BUTTON, 0, // 等待按钮 PLAY_CG, 1, // 播放cg PLAY_MESSAGE, 1, // 播放对话 WAIT_BUTTON, 0, // 等待按钮 PLAY_END // 结束 }, }; static void VblankCB_ShowCG(void) { LoadOam(); ProcessSpriteCopyRequests(); TransferPlttBuffer(); } static void CB2_ShowCGWait(void) { RunTasks(); DoScheduledBgTilemapCopiesToVram(); UpdatePaletteFade(); } static void CB2_ShowCG(void) { SetVBlankCallback(NULL); SetGpuReg(REG_OFFSET_DISPCNT, 0); SetGpuReg(REG_OFFSET_BG3CNT, 0); SetGpuReg(REG_OFFSET_BG2CNT, 0); SetGpuReg(REG_OFFSET_BG1CNT, 0); SetGpuReg(REG_OFFSET_BG0CNT,…

[GBA教程]pokeemerald新增NPC头像显示，并支持镜像翻转
type: item
date: 2026-07-29T17:05:08+08:00
description: 头像系统实现在这个文件夹创建：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 1000define 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…