Post subject: Fire Emblem - The Sacred Stones lua display encode.
Editor, Emulator Coder, Site Developer
Joined: 5/11/2011
Posts: 1108
Location: Murka
I did this a while ago, don't think it was ever published. I'm going to write a bit about it here. First, I got a LUA script from the author that put things he watched on the screen during the run. I then modified the script for layout and positioning, and captured the game twice: once with the script running, once without it running: The idea is to use AVISynth to put the two recordings together into one encode. Here's the LUA script used:
Language: lua

function nextrng(r1, r2, r3) return AND(XOR(SHIFT(r3, 5), SHIFT(r2, -11), SHIFT(r1, -1), SHIFT(r2,15)),0xFFFF) end function rngsim(n) local rngbase=0x03000000 local result = { memory.readword(rngbase+4), memory.readword(rngbase+2), memory.readword(rngbase+0) } for i = 4, n do result[i] = nextrng(result[i-3],result[i-2],result[i-1]) end return result end local phit = 0x0203A550 local pdmg = 0x0203E1BE local pcrt = 0x0203A556 local ehit = 0x0203A5D0 local edmg = 0x0203E1BC local ecrt = 0x0203A5D6 no255 = function (addr) local ret = memory.readbyte (addr) if ret == 255 then return "" else return ret end end nolong = function (addr) local ret = no255 (addr) if ret == 100 then return "100" else return " " .. ret end end gui.register (function () gui.box (0, 0, 239, 159, "#999999FF", "#999999FF") local nsim = 20 local rngs = rngsim(nsim) for i = 1, nsim do gui.text(228, 8*(i-1), string.format("%3d", rngs[i]/655)) end gui.text(210,0,"RNG1:") gui.text(210,8,"RNG2:") gui.text(210,16,"RNG3:") gui.text(210,24,"Next:") gui.text(1,0,"Player") gui.text(1,8,"Hit:" .. nolong(phit)) gui.text(1,16,"Dmg:" .. nolong(pdmg)) gui.text(1,24,"Crit:" .. no255(pcrt)) gui.text(1,40,"Enemy") gui.text(1,48,"Hit:" .. nolong(ehit)) gui.text(1,56,"Dmg:" .. nolong(edmg)) gui.text(1,64,"Crit:" .. no255(ecrt)) -- emu.frameadvance() end)
Other than the information in the original script, the essential changes are positioning and a grey out box to hide things. It will make more sense when I post the avisynth script (which I will do as soon as it's done re-running them...)
Editor, Emulator Coder, Site Developer
Joined: 5/11/2011
Posts: 1108
Location: Murka
After the two dumps are done, you can put them together with avisynth. I take a left sidebar, right sidebar, and bottom sidebar from the lua encode (the bottom sidebar is just input display, for which I captured the VBA OSD), and slap them onto the original clip. The rest is just fine details. The result: The original encode script:
Language: avisynth

AviSource ("..\avi\1843M.avi", pixel_type = "RGB32") ConvertToRGB24 Trim (0, 293066) FadeOut2 (200) clip1 = last clip2 = ImageSource (file="..\..\logo\dracula_240_160_ver3.png", start=0, end=119, fps=clip1.FrameRate) #add blank audio to logo clip3 = BlankClip (clip2, audio_rate=clip1.AudioRate, channels=clip1.AudioChannels, sample_type="16bit") clip4 = AudioDub (clip2, clip3) last = clip4 + clip1 #subtitles #frames are relative to new start (120 extra at beginning) Subtitle ("Fire Emblem: The Sacred Stones\nPlayed by Vykan12\nPlaying Time: 1:08:51.20\nRerecord count:34140\n", \ y=0, align=8, first_frame=1636, last_frame=1939, size=12, text_color=$20FFFFFF, halo_color=$20000000, lsp=1, font="Arial") Subtitle ("This is a tool-assisted recording.\nFor details, visit http://TASVideos.org", \ y=0, align=8, first_frame=1940, last_frame=2239, size=12, text_color=$20FFFFFF, halo_color=$20000000, lsp=1, font="Arial")
The lua display script:
Language: avisynth

AviSource ("..\avi\1843MT.avi", pixel_type = "RGB32") # lua encode ConvertToRGB24 Trim (0, 250251) #pad beginning and end BlankClip (last, 120, color=$000000) + last FadeOut2 (200) last + BlankClip (last, 42815, color=$000000) og = last leftclip = StackVertical (og.Crop (0, 0, 32, 130), og.Crop (0, 100, 32, 30)) rightclip = og.Crop (208, 0, 32, 160) #bottom clip needs to be made extra wide bottomclip = StackHorizontal ( \ og.Crop (0, 118, 100, 10), \ og.Crop (10, 129, 120, 10), \ og.Crop (0, 149, 84, 10)) clipvideo = AviSource ("1843M.avs") StackHorizontal (leftclip, clipvideo, rightclip) StackVertical (last, bottomclip) AudioDub (last, clipvideo) #ConvertToRGB24
It shouldn't be too hard to follow if you're familiar with lua and avisynth, hopefully.
Active player (279)
Joined: 4/30/2009
Posts: 791
For the FE6 run, I'd like to be able to work from this script: Download showstats.lua
Language: lua

uHP = 0x02039224 uStr = 0x02039226 uSkl = 0x02039227 uSpd = 0x02039228 uLck = 0x0203922B uDef = 0x02039229 uRes = 0x0203922A uHit = 0x02039276 uAtk = 0x0203926C uDf = 0x0203926E uCrit = 0x0203927C uWp = 0x0203925C uWpUs = 0x0203925F uAS = 0x02039270 uRnk = 0x0203923A eHP = 0x020392A0 eStr = 0x020392A2 eSkl = 0x020392A3 eSpd = 0x020392A4 eLck = 0x020392A7 eDef = 0x020392A5 eRes = 0x020392A6 eHit = 0x020392F2 eAtk = 0x020392E8 eDf = 0x020392EA eCrit = 0x020392F8 eWp = 0x020392DA eWpUs = 0x020392DB eAS = 0x020392EC eRnk = 0x020392B6 battle = 0x060000D6 staff = 0x0202187B --0x060000D6 --0x060000DA --0x060000DE --0x06000AE2 --0x06000AFE --0x06000B42 --0x06000B5E Weapons = {"None" , "Iron Sword", "Iron Blade", "Steel Sword", "Silver Sword", "Slim Sword", "Poison Sword", "Brave Sword", "Light Brand", "Durandal", "Armorslayer", "Rapier", "Killing Edge", "Lancereaver", "Wo Dao", "Sword of Seals", "Iron Lance", "Steel Lance", "Silver Lance", "Slim Lance", "Poison Lance", "Brave Lance", "Javelin", "Malte", "Horseslayer", "Killer Lance", "Axereaver", "Iron Axe", "Steel Axe", "Silver Axe", "Poison Axe", "Brave Axe", "Hand Axe", "Armads", "Hammer", "Killer Axe", "Swordreaver", "Devil Axe", "Haldberd", "Iron Bow", "Steel Bow", "Silver Bow", "Poison Bow", "Killer Bow", "Brave Bow", "Short Bow", "Longbow", "Miurgre", "Ballista", "Iron Ballista", "Killer Ballista", "Fire", "Thunder", "Fimbulvetr", "Elfire", "Aircalibur", "Fenrir", "Bolting", "Forblaze", "Lightning", "Divine", "Purge", "Aureola", "Flux", "Nosferatu", "Eclipse", "Apocalypse", "Heal", "Mend", "Recover", "Physic", "Fortify", "Warp", "Rescue", "Restore", "Silence", "Sleep", "Torch", "Hammerne", "Watch", "Berserk", "Unlock", "Barrier", "Fire Stone", "Divine Stone", "Demon Stone", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Dark Breath", "Eckesachs", "Steel Blade", "Silver Blade", "Al's Sword", "Gant's Lance", "Tiena's Staff", "Saint's Staff", "Wyrmslayer", "", "", "", "", "Runesword", "Spear", "Tomahawk"} function getdmg(atk, def) if atk == -1 or atk == 255 then return "--" else if atk-def > 0 then return atk-def else return 0 end end end function generaterankliteral(val) if(val >= 1 and val <50>= 51 and val <100>= 101 and val <150>= 151 and val <200>= 201 and val <250> 0) then rnks = rnks + 1 if(rnks == 1) then str[1] = generaterankname(i+1) .. ": " .. memory.readbyteunsigned(Rnk+i) .. " / " .. generaterankliteral(memory.readbyteunsigned(Rnk+i)) elseif (rnks == 2) then str[1] = str[1] .. " - " .. generaterankname(i+1) .. ": " .. memory.readbyteunsigned(Rnk+i) .. " / " .. generaterankliteral(memory.readbyteunsigned(Rnk+i)) elseif (rnks == 3) then str[2] = generaterankname(i+1) .. ": " .. memory.readbyteunsigned(Rnk+i) .. " / " .. generaterankliteral(memory.readbyteunsigned(Rnk+i)) elseif (rnks == 4) then str[2] = str[2] .. " - " .. generaterankname(i+1) .. ": " .. memory.readbyteunsigned(Rnk+i) .. " / " .. generaterankliteral(memory.readbyteunsigned(Rnk+i)) end end end return str end function nullstats(stat) if stat == 255 or stat == -1 then return "--" else return stat end end function printstats() gui.text(8,4,"Attacker\n\n HP:\nStr:\nSkl:\nSpd:\nLck:\nDef:\nRes:\n\nHit:\nDmg:\nCrt:\n\nWpn:\n AS:") gui.text(28,20,string.format("%s\n%s\n%s\n%s\n%s\n%s\n%s\n\n%s\n%s\n%s\n\n%s\n%s",memory.readbyte(uHP),memory.readbyte(uStr),memory.readbyte(uSkl),memory.readbyte(uSpd),memory.readbyte(uLck),memory.readbyte(uDef),memory.readbyte(uRes),nullstats(memory.readbyte(uHit)),getdmg(memory.readbyte(uAtk),memory.readbyte(eDf)),nullstats(memory.readbyte(uCrit)),Weapons[memory.readbyteunsigned(uWp)+1],nullstats(memory.readbyte(uAS)))) gui.text(200,4,"Defender\n\n :HP\n :Str\n :Skl\n :Spd\n :Lck\n :Def\n :Res\n\n :Hit\n :Dmg\n :Crt\n\n :Wpn\n :AS") gui.text(204,20,string.format("%s\n%s\n%s\n%s\n%s\n%s\n%s\n\n%s\n%s\n%s\n\n\n%s",memory.readbyte(eHP),memory.readbyte(eStr),memory.readbyte(eSkl),memory.readbyte(eSpd),memory.readbyte(eLck),memory.readbyte(eDef),memory.readbyte(eRes),nullstats(memory.readbyte(eHit)),getdmg(memory.readbyte(eAtk),memory.readbyte(uDf)),nullstats(memory.readbyte(eCrit)),nullstats(memory.readbyte(eAS)))) gui.text(212-string.len(Weapons[memory.readbyteunsigned(eWp)+1])*4,116, Weapons[memory.readbyteunsigned(eWp)+1]) local str = generateranks(uRnk) gui.text(8,140,str[1] .. "\n" .. str[2]) local str = generateranks(eRnk) gui.text(232-string.len(str[1])*4,140,str[1]) gui.text(232-string.len(str[2])*4,148,str[2]) end while true do if (memory.readbyte(battle) == 0 and (memory.readbyte(uWp) ~= 104 and memory.readbyte(uWp) ~= 105)) or (memory.readbyte(staff) == 0 and (memory.readbyte(uWp) == 75 or memory.readbyte(uWp) == 76 or memory.readbyte(uWp) == 80)) then printstats() end emu.frameadvance() end
Note that the memory addresses are different for each of the GBA games, so you'd have to do a bit of work to adapt this script for FE7 and FE8. My next task for this script would be to include sprites to represent the various weapon skills, character portrait and maybe the weapon sprites too, I know it's possible given how the Sonic HUD script works, so any help to make this a reality will be appreciated - I'll get the sprites organised myself as a starting point. EDIT: I figure the actual display would come across something like this: I realise getting images to pop up would be somewhat ambitious, but I'm aware it can be done, as the Sonic HUD script shows. Could always simplify it without the images, but it would make it easier in terms of screen real estate to show stuff like weapon skills and which units are fighting, as many units use the same map sprites.