※.脚本中的逻辑关系
1.YES OR NO 脚本选择
前面的几节知识都没有用到逻辑关系,是直白的脚本命令堆放,但是如果我们需要一个这样的脚本呢:
①和一个NPC说话
②他问:今天天气好吗?
③回答是就送你一个伤药
④回答否就说:也许明天是个好天气
显然,用现有的脚本知识是无法达到我们的目的的.
现在来看,将脚本导向不同结果的第一种方式:boxset5.
前面曾说过boxset6,boxset5和boxset6的最大区别就在于boxset5可以弹出关于YES和NO的选择,来看示例:
复制内容到剪贴板
代码:
#org $talk
lock
faceplayer
message $1
boxset5
compare LASTRESULT 0x1
if b_true goto $give
message $2
boxset 6
release
end
#org $give
giveitem 13 1
message $3
boxset 6
release
end
#org $1
$1 1 =How's the weather?
#org $2
$2 1 =Maybe tomorrow is\na good day.
#org $3
$3 1 =You obtain a potion!
有点长是不是?新东西也稍稍多了点,不过冷静思考下,新出现的只有:
复制内容到剪贴板
代码:
boxset5
compare LASTRESULT 1
if b_true goto $give
而已.
首先是boxset5.boxset5会弹出"YES\NO"的选择,当玩家进行选择后,就轮到compare LASTRESULT 1了.
boxset5选择后的结果作为一个值被存入了一个叫LASTRESULT(中文意为"最后结果")的公共变量之中.
紧接着,compare这个命令负责进行对比,把LASTRESULT中的结果和0x1进行对比.
0x1是什么呢?它代表"是",也就是说这一步就是在考证玩家选取的是不是YES
if b_true goto $give,就是如果对比后没有差异,二者完全相同,就命令脚本跳转到$give部分.
而否则的话就要继续原脚本的执行.
注:b_ture也等同于b_==或者 1,三种写法均正确.
通过boxset5式的选择,脚本最终走向了两个方向.
但是别急,LASTRESULT还有更多的把戏,如果b_true的位置换成以下:
复制内容到剪贴板
代码:
B_<< 小于 (0)
B_== 等于 (1)
B_>> 大于 (2)
B_<= 小于等于 (3)
B_>= 大于等于 (4)
B_!= 不等于 (5)
那么可以分得的情况就更加丰富了.
2.setflag 脚本递进
虽然上面的脚本已经满足了我们的基本要求,但在得到了伤药之后和该NPC对话,仍旧会不断触发赠送剧情,这就成了一个BUG.
于是,我们需要在剧情中给已经发生过的打个记号,我们需要用setflag 设立标识.
setflag只有一个参数,就是标识的存放地点.我们这样写:setflag 0x201.
但问题也来了,游戏原剧情本身就占去了很多位置,那么我们自己的标识该在哪放呢?通常,位于0x200 - 0x29F, 0x500-0x79F, 0x1000-0x109F的位置都是空的(注意,16进制的数字是按0123456789ABCDEF逐步递升的,比十进制多了6个),可供我们自由使用的.当然,除了这些还有不少,你可以自己没事试着玩.
系统中,有些标识是很有用的,比如下面这些:
复制内容到剪贴板
代码:
火叶:
0x820 - 第一个 徽章
0x821 - 第二个 徽章
0x822 - 第三个 徽章
0x823 - 第四个 徽章
0x824 - 第五个 徽章
0x825 - 第六个 徽章
0x826 - 第七个 徽章
0x827 - 第八个 徽章
0x828 - "精灵"菜单
0x829 - "图鉴"菜单
0x82F - 跑鞋
红蓝宝石:
0x800 - "精灵"菜单
0x801 - "图鉴"菜单
0x802 - 小光赠送的那个机器 菜单
0x807 - 第一个 徽章
0x808 - 第二个 徽章
0x809 - 第三个 徽章
0x80A - 第四个 徽章
0x80B - 第五个 徽章
0x80C - 第六个 徽章
0x80D - 第七个 徽章
0x80E - 第八个 徽章
0x860 - 跑鞋
绿宝石:
0x860 - "精灵"菜单
0x861 - "图鉴"菜单
0x862 - 小光赠送的那个机器 菜单
现在我们来看看setflag的实际应用:
复制内容到剪贴板
代码:
#org $talk
lock
faceplayer
checkflag 0x201
compare LASTRESULT 0x1
if b_true goto $2
message $1
boxset5
compare LASTRESULT 0x1
if b_true goto $give
message $2
boxset 6
release
end
#org $give
setflag 0x201
giveitem 13 1
release
end
#org $1
$1 1 =How's the weather?
#org $2
$2 1 =Maybe tomorrow is\na good day.
这里,我们在上节的脚本中稍做了优化,增加了checkflag和setflag,使得赠送的情节不会重复发生.
checkflag的格式和setflag相同,后面唯一的参数就是标识位置,它的作用是检查指定位置是否设立了标识.
之后的
compare LASTRESULT 0x1
if b_true goto $2
则是在做后续处理,原理和上节相同,不再解释了.以后类似的结构还会经常见到,大家一定要学会在不同场合适当的拼装这些脚本段.
关于flag,还有一个可以清除的命令,它的格式是:clearflag 0x201,和前两者携带参数相同.
3.goto,gosub and call 脚本跳转
在前面我们已经见过goto了,它的用法就是goto $pointer,携带的唯一参数就是地址.
类似的,gosub也是这样用:gosub $pointer,同样携带的是地址作为参数.另外,go和goto也像可以带这样的参数:goto 0x80000,这里0x80000也是地址.
但是二者却有一个很大的不同.goto将从一个脚本跳进另一个脚本,之后不会再自动回到原脚本;而gosub则会在跳转到的脚本运行完毕时回到原脚本.
最后的call适合于跳至ROM中已写入的脚本.
call也有自己的特殊性质,那就是必须加return回到原脚本,来看示例:
复制内容到剪贴板
代码:
#org $take
givepokemon 4 5 0
message $4
boxset 5
compare LASTRESULT 0x1
if b_true gosub $name
message $5
boxset 6
release
end
#org $name
call 0x1A74EB
return
这是我从别的地方截取的脚本,在这里我们可以清楚的看到,为了让脚本回归,尽管已经用了gosub,但仍在call后追加了return.
4.callasm 脚本追加
如果自己懂得很深的GBA ROM运作机理,可以向ROM中手动追加信息.比如在ROM中实现指定闪光精灵出现.
以下是外国hack达人Mastermind_X创造的闪光精灵战脚本示例:
复制内容到剪贴板
代码:
#org $start
callasm 0x71B771
#raw 0x00
wildbattle 0x82 0x1E 0x0
special 0x138
pause 0x101
callasm 0x71B771
end
其中,
0x0071B7A内需手动输入:
复制内容到剪贴板
代码:
23 71 B7 71 08 00 B6 82 00 1E 00 00 00 25 38 01
0x0071B7B内需手动输入:
复制内容到剪贴板
代码:
28 01 01 23 71 B7 71 08 02 FF FF FF FF FF FF FF
可以看到,callasm只携带了一个参数:地址.
因为原理复杂,这里也就不做解释了,大家知道有callasm这个命令就行了
3.movements 移动
复制内容到剪贴板
代码:
#org $start
checkflag 0x828
if b_true goto $done
message $1
boxset 6
applymovement 0x04 $move
pausemove 0x0
applymovement 0xFF $move2
pause 0x30
message $2
boxset 6
playsound 0x13E
nop
applymovement 0x04 $move3
applymovement 0xFF $move3
pausemove 0x0
fadesound 0x12C
nop
release
end
#org $done
release
end
#org $move
#raw 0x62 0x12 0x12 0x12 0x12 0xFE
#org $move2
#raw 0x03 0xFE
#org $move3
#raw 0x13 0x13 0x13 0x13 0xFE
#org $1
$1 1 =Waaaiiiitttt!!!
#org $2
$2 1 =You can't go out there\nwithout your own Pokemon.\pWild Pokemon will hurt you.
以上引用的是某外国脚本教程当中的例子.作为一个引子,它确实很不错,该有的基本都有了.
例子中一个NPC拦住了没有精灵的主角.(类比于红蓝宝石中的初始镇出口剧情)
#raw,applymovement, pausemove, pause, playsound, fadesound──新命令挺多的哈...我们逐个来.
关于#raw,playsound,fadesound我暂时还不想多说,留到以后,现在的重点是移动.
但凡使用移动系的命令,都必须先使用applymovement进行引导.
applymovement后要跟两个参数,一个是人物ID,一个是移动脚本的存放地址.
人物ID可以在advance map中选中要移动的人物,即可在侧栏中看到ID.一般的,ID总是在10进制的0~256内,转成16进制就是0x0~0xFE.不过你见过0xFF么?它表示的是主角的ID.
PS:也就是说,ROM运行时,一个ID占只一个字节,这样也就不难理解ROM中为什么不能简单的添加人物了──因为1个字节只能存放256个不同的数字.
移动脚本的存放地址不难理解,它就像message命令后跟的那个地址类似,存放着具体的移动脚本.
我们把脚本拆开,单独抽取第一段移动脚本的片段来看:
复制内容到剪贴板
代码:
#org $start
applymovement 0x04 $move
pausemove 0
release
end
#org $move
#raw 0x62 0x12 0x12 0x12 0x12 0xFE
去掉若干不相干的部分,以上就是纯粹的移动脚本了.
首先,千万注意:移动脚本不要加lock和faceplayer!!!
复制内容到剪贴板
代码:
#raw 0x62 0x12 0x12 0x12 0x12 0xFE
表示什么呢?首先,拿出#raw...0xFE.#raw...0xFE的组合是移动所必须的,少了它俩就没法运行.
而在#raw...0xFE的中间,就是你所要的移动的命令,它精确到了每一步的方向以及速度,甚至其它非移动的动作!
在不同的ROM中,具体动作的符号不同,以下是它们的列表:
复制内容到剪贴板
代码:
红蓝宝石
0x54 ' Hide
0x55 ' Show
0x56 ' Alert
0x57 ' Question
0x58 ' Love
0x5A ' Pokeball
0x10 ' Delay0
0x11 ' Delay1
0x12 ' Delay2
0x13 ' Delay3
0x14 ' Delay4
' Step
0x00 ' Down0
0x01 ' Up0
0x02 ' Left0
0x03 ' Right0
0x04 ' Down1
0x05 ' Up1
0x06 ' Left1
0x07 ' Right1
0x08 ' Down2
0x09 ' Up2
0x0A ' Left2
0x0B ' Right2
0x17 ' Left3
0x18 ' Right3
0x15 ' Down3
0x16 ' Up3
0x2D ' Down4
0x2E ' Up4
0x2F ' Left4
0x30 ' Right4
' Running
0x35 ' RunDown
0x36 ' RunUp
0x37 ' RunLeft
0x38 ' RunRight
0x7E ' RunDown2
0x7F ' RunUp2
0x80 ' RunLeft2
0x81 ' RunRight2
' Hop & Jump
0x0C ' HopTileDown
0x0D ' HopTileUp
0x0E ' HopTileLeft
0x0F ' HopTileRight
0x3A ' HighHopDown
0x3B ' HighHopUp
0x3C ' HighHopLeft
0x3D ' HighHopRight
0x46 ' HopDown
0x47 ' HopUp
0x48 ' HopLeft
0x49 ' HopRight
0x4A ' HopDown180
0x4B ' HopUp180
0x4C ' HopLeft180
0x4D ' HopRight180
0x42 ' JumpDown
0x43 ' JumpUp
0x44 ' JumpLeft
0x45 ' JumpRight
' Straf (May have glitches)
0x19 ' StDown1
0x1A ' StUp1
0x1B ' StLeft1
0x1C ' StRight1
0x1D ' StDown2
0x1E ' StUp2
0x1F ' StLeft2
0x20 ' StRight2
0x21 ' StDown3
0x22 ' StUp3
0x23 ' StLeft3
0x24 ' StRight3
0x25 ' StDown4
0x26 ' StUp4
0x27 ' StLeft4
0x28 ' StRight4
0x6A ' StDown1i
0x6B ' StUp1i
0x6C ' StLeft1i
0x6D ' StRight1i
0x6E ' StDown5
0x6F ' StUp5
0x70 ' StLeft5
0x71 ' StRight5
'Special
0x31 ' SlideFaceDown
0x32 ' SlideFaceUp
0x33 ' SlideFaceLeft
0x34 ' SlideFaceRight
0x86 ' IceSlideDown
0x87 ' IceSlideUp
0x88 ' IceSlideLeft
0x89 ' IceSlideRight
' Glitchy
0x3E ' Up0A
0x3F ' Down0A
0x4E ' Down0B
0x63 ' Up0B
0x65 ' Right0A
0x66 ' RunStopLoopDown
0x67 ' RunStopLoopUp
0x68 ' RunStopLoopLeft
0x69 ' RunStopLoopRight
0x72 ' Down15
0x73 ' Up15
0x74 ' Left15
0x75 ' Right15
0x7A ' Down6
0x7B ' Up6
0x7C ' Left6
0x7D ' Right6
0x82 ' Down7
0x83 ' Up7
0x84 ' Left7
0x85 ' Right7
' EXIT
0xFE
火/叶
Face Down 0x00
Face Up 0x01
Face Left 0x02
Face Right 0x03
Step Down (Very Slow) 0x08
Step Up (Very Slow) 0x09
Step Left (Very Slow) 0x0A
Step Right (Very Slow) 0x0B
Step Down (Slow) 0x0C
Step Up (Slow) 0x0D
Step Left (Slow) 0x0E
Step Right (Slow) 0x0F
Step Down (Normal) 0x10
Step Up (Normal) 0x11
Step Left (Normal) 0x12
Step Right (Normal) 0x13
Jump Down 2 Squares 0x14
Jump Up 2 Squares 0x15
Jump Left 2 Squares 0x16
Jump Right 2 Squares 0x17
Step Down (Fast) 0x1D
Step Up (Fast) 0x1E
Step Left (Fast) 0x1F
Step Right (Fast) 0x20
Step on the Spot Down 0x21
Step on the Spot Up 0x22
Step on the Spot Left 0x23
Step on the Spot Right 0x24
Step on the Spot Down (Fast) 0x25
Step on the Spot Up (Fast) 0x26
Step on the Spot Left (Fast) 0x27
Step on the Spot Right (Fast) 0x28
Step on the Spot Down (Very Fast) 0x29
Step on the Spot Up (Very Fast) 0x2A
Step on the Spot Left (Very Fast) 0x2B
Step on the Spot Right (Very Fast) 0x2C
Face Down (Non-Instant) 0x2D
Face Up (Non-Instant) 0x2E
Face Left (Non-Instant) 0x2F
Face Right (Non-Instant) 0x30
Slide Down 0x31
Slide Up 0x32
Slide Left 0x33
Slide Right 0x34
Slide Down On Right Foot 0x3D
Slide Up On Right Foot 0x3E
Slide Left On Right Foot 0x3F
Slide Right On Right Foot 0x40
Slide Down On Left Foot 0x41
Slide Up On Left Foot 0x42
Slide Left On Left Foot 0x43
Slide Right On Left Foot 0x44
Face Player 0x4A
Face Away from Player 0x4B
Jump Down 1 Square 0x4E
Jump Up 1 Square 0x4F
Jump Left 1 Square 0x50
Jump Right 1 Square 0x51
Jump in Place (Facing Down) 0x52
Jump in Place (Facing Up) 0x53
Jump in Place (Facing Left) 0x54
Jump in Place (Facing Right) 0x55
Jump in Place (Facing Down->Up) 0x56
Jump in Place (Facing Up->Down) 0x57
Jump in Place (Facing Left->Right) 0x58
Jump in Place (Facing Right->Left) 0x59
Disappear 0x60
Reappear 0x61
"!" box popup 0x62
"?" box popup 0x63
"X" box popup 0x64
"!!" box popup 0x65
"^_^" box popup 0x66
翻译的话我看就不必了,都是极其简单的词语,后面的数字多表示速度,如left3表示向左以3倍速走动.
不过移动脚本仍欠缺一样东西──停止
现在来说说 pausemove 与 pause
按老外的习惯,pausemove一般用于立即结束移动部分,因而搭配的参数是 0x0;
而pause则用于移动之后再等待数个时间单位,它搭配的参数要这样写:0x10 + (0x10乘时间单位数).
比如,pause 0x50 就代表等待4个时间单位后继续之后的脚本.
至于 pausemove 与 pause的位置嘛,我想开始的例子里已经清楚的显示了.
搞清楚上面所说的了么?搞清的话看看这段:
复制内容到剪贴板
代码:
#org $start
applymovement 0x04 $move1
pausemove 0x0
applymovement 0xFF $move2
pause 0x30
applymovement 0x04 $move3
applymovement 0xFF $move3
pausemove 0x0
release
end
#org $move1
#raw 0x12 0x12 0x12 0x12 0x12 0xFE
#org $move2
#raw 0x04 0xFE
#org $move3
#raw 0x13 0x13 0x13 0x13 0xFE
注意到了吗?
复制内容到剪贴板
代码:
applymovement 0x04 $move3
applymovement 0xFF $move3
是两个人物在执行同一个移动,这就是跟随移动了.想要NPC带着主角去一个地方吗?就用这个技巧实现吧!
4.playsound,fadesound,cry播放音乐
一直认为PM游戏里的音乐很不错,那么改版中何不来点音乐呢?
在上节移动脚本当中,有一处playsound.playsound的作用就是让游戏开始一段指定的音乐,回顾一下脚本:
playsound 0x13E
nop
playsound一共就一个参数,那就是曲子的编码.
以下是火叶的音乐对应编码:
复制内容到剪贴板
代码:
0x108 - Safari Zone
0x112 - Team Rocket Celadon Hideout, Bruno
0x113 - Elite 4
0x117 - Cinnabar Island
0x118 - Lavender Town
0x11F - Viridian Forest, Diglett Cave, Seafoam Islands
0x120 - Mt Moon, Victory Road, Rock Tunnel
0x121 - Power Plant, Pokemon Mansion
0x123 - Route Song 2
0x124 - Route Song 1
0x125 - Route Song 3
0x126 - Route Song 4
0x127 - Elite 4, Champion
0x12C - Pallet Town
0x12F - Pokemon Center, Mart
0x130 - S.S. Anne
0x132 - Pokemon Tower, Agatha
0x133 - Team Rocket Silph Co.
0x134 - Cerulean City, Fushia
0x135 - Celadon
0x139 - Vermillion
0x13A - Viridian, Pewter, Saffron
0x14B - Mt.Ember/Icefall Cave
0x14D - Island Routes
0x14E - Dotted Hole
0x14F - Island 1-3
0x150 - Island 4-5
0x151 - Island 6-7
0x158 - Trainer Tower
翻译就算了吧...
遗憾的是,老外没有给出宝石音乐的编码列表,不过从AM中大家可以查出.
慢!!!
你难道不想问问nop是怎么回事么?
对于playsound命令,原本在ROM中是需要4个字节的,其中就包含了那个唯一的参数:曲目编码.但是Pokescript的作者却忽略了最后两个字节,于是这个命令在写入的时候就会漏掉2个字节,威胁到整个ROM的安全运行.为了避免由此产生的BUG,我们用pokescript中一个用来填空的命令nop来填充.Pokescript中大概有5个左右的命令编译失误,所以稍后我们还会见到nop.
现在我们的游戏已经不再考虑原地图中音乐的要求了,而是播放我们所要求的音乐...一直播放...即使你已经不再需要它还是继续刚才的音乐...就这么一直么?是的,直到遇见命令...
fadesound
fadesound的作用正如它的名字,它将用你指定的音乐来代替playsound,之后进入其它带有音乐的地图就回到正常的音乐中了.所以fadesound所要求的音乐通常是原地图中的音乐,这样听起来就会很自然.
它的格式是:
复制内容到剪贴板
代码:
fadesound 0x12C
nop
0x12C对应乐曲编码,和之前的playsound完全相同,同时再次由于Pokescript编译问题,补入一个nop.
那么cry命令呢?cry和两个sound命令不同,虽然也是在地图中作用,但它响一次且立即自动回到原地图音乐中.
它的格式是:
复制内容到剪贴板
代码:
cry 库代码 PM代码
nop
nop
首先,再次由于Pokescript编者失误,损失掉两个字节,用两个nop替补.
其次,我要稍微说下"库"了.在PM的ROM中,地图也罢,人物形象也罢,都会分组存放在不同的库中,这个库就像仓库一样.同样,PM的叫声也被存放到了一个独立的库中,每个PM都以自己代码为名拥有一段叫声.
这样,
复制内容到剪贴板
代码:
cry 0xA1 6
nop
nop
就表示从库0xA1中抽取代码为6的叫声,而6就是喷火龙.
再多说几句关于数字的问题.对于Pokescript,凡是0x都认为是16进制数字,而不带0x的则认为是10进制.但无论哪一种,最终都会以16进制的形式写入ROM,所以请务必核实你所要写的数,转换的话可以用Windows自带的计算器选择科学型.
fanfare与jingle
游戏中我们时常在得到物品时听到独特的声音,或者你记得精灵中心的治疗声么?
jingle专门负责触发得到物品的声音,它没有任何参数,故而也就没有什么选择的余地.举例:
复制内容到剪贴板
代码:
#org $StartChik
givepokemon 152 5 0
jingle
message $GotChick
$GotChick 1 = You Recieved a Chikorita
boxset 6
end
那么fanfare呢?和jingle的表现形式相同,但它可以选择所要的音乐,所以含带一个参数.
fanfare 0x13E - 表示得到物品或者PM的声音
fanfare 0x100 - 表示精灵中心医治精灵的声音
一般就用它们两个参数.举例:
复制内容到剪贴板
代码:
#org $StartChik
lock
faceplayer
givepokemon 152 5 0
fanfare 0x13E
message $GotChick
$GotChick 1 = You Recieved a Chikorita
boxset 6
end
不过说到fanfare,确实还要谈到另外一个问题.
不知道大家在游戏中接收道具的时候注意到没有,无论你按A或者B多少次,接收的声音和文字并不可能立即停止和消失,而是在音乐播放完的时候再按A一次才能让文字消失.这是怎么回事呢?
现在就要看看这段脚本了:
复制内容到剪贴板
代码:
#org $take
lock
faceplayer
givepokemon 4 5 0
fanfare 0x13E
message $3
boxset 4
waitfanfare
#raw 0x68
release
end
用之前学过的内容来看,新命令只有:waitfanfare, #raw 0x68和boxset 4了.
先说boxset 4吧.boxset 4从表面上来看,它和boxset 6是一样的,都是普通的文本框.但是boxset 4具有一个boxset 6不具有的特点,就是不会自动关闭,即便你按A.这样的话,游戏就会一直卡在文本框,而无法继续.
那么如果不能关闭,游戏如何进行下去呢?我们用#raw 0x68来结束boxset 4.
这时,我们如果在二者之间加上一个waitfanfare命令你猜怎样?等到音乐播放完文本框才允许关闭!
OK,现在问题解决了.
重要软件下载: