Posts for FatRatKnight

Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I posted my lua script right here. It shows quite a few things which may help in viewing this run. I realized I was holding on to it and no one would see it... Until I posted it just now anyway. Good grief, the old run's picture is also the statue battle. I guess we're doomed in perpetually putting that statue on display, aren't we? I get pretty low on HP twice, but yeah... Just isn't as fun when I can no longer honestly say Paul's condition implies he wouldn't remain intact for the trip back to town. The new route worked out pretty well. It certainly sped up some of the slower parts while adding in something new. Really, that REFRESH POTION is powerful. Thanks for the encode. As usual, having them around helps those who are unable or unwilling to use the emulator to see runs like this. Again, thanks. Glad to know my subtitles can serve as a reminder. That's part of the reason they exist: Information. The other part is entertainment.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I've already completed the run, which can be seen here. Regardless, I am holding on to a lua script, one that shows some information. I think I should post it now. A lua script for FCEUX 2.1.3-interim. the gui.box works differently between 2.1.2 and 2.1.3-interim for whatever reason. This pretty much displays the HP, shields, and venom of everything. It also shows an approximation of food and water, mana, and the 60-frame timer. It even points out certain active timers and when you can cast your next spell. In spite of this information on screen, I've rarely used this script in my run...
--*****************************************************************************
Draw= {}     --Draw[digit](left,top,color)
--*****************************************************************************
-- A set of functions to draw specific digits.

function Draw.D0(left, top, color)
    gui.box(left  ,top  ,left+2,top+4,0,color)
end

function Draw.D1(left, top, color)
    gui.line(left  ,top+4,left+2,top+4,color)
    gui.line(left+1,top  ,left+1,top+3,color)
    gui.pixel(left  ,top+1,color)
end

function Draw.D2(left, top, color)
    gui.line(left  ,top  ,left+2,top  ,color)
    gui.line(left  ,top+3,left+2,top+1,color)
    gui.line(left  ,top+4,left+2,top+4,color)
    gui.pixel(left  ,top+2,color)
    gui.pixel(left+2,top+2,color)
end

function Draw.D3(left, top, color)
    gui.line(left  ,top  ,left+1,top  ,color)
    gui.line(left  ,top+2,left+1,top+2,color)
    gui.line(left  ,top+4,left+1,top+4,color)
    gui.line(left+2,top  ,left+2,top+4,color)
end

function Draw.D4(left, top, color)
    gui.line(left  ,top  ,left  ,top+2,color)
    gui.line(left+2,top  ,left+2,top+4,color)
    gui.pixel(left+1,top+2,color)
end

function Draw.D5(left, top, color)
    gui.line(left  ,top  ,left+2,top  ,color)
    gui.line(left  ,top+1,left+2,top+3,color)
    gui.line(left  ,top+4,left+2,top+4,color)
    gui.pixel(left  ,top+2,color)
    gui.pixel(left+2,top+2,color)
end

function Draw.D6(left, top, color)
    gui.box(left  ,top+2,left+2,top+4,0,color)
    gui.line(left  ,top  ,left+2,top  ,color)
    gui.pixel(left  ,top+1,color)
end

function Draw.D7(left, top, color)
    gui.line(left  ,top  ,left+1,top  ,color)
    gui.line(left+2,top  ,left+1,top+4,color)
end

function Draw.D8(left, top, color)
    gui.box(left,top,left+2,top+4,0,color)
    gui.pixel(left+1,top+2,color)
end

function Draw.D9(left, top, color)
    gui.box(left  ,top  ,left+2,top+2,0,color)
    gui.line(left  ,top+4,left+2,top+4,color)
    gui.pixel(left+2,top+3,color)
end


Draw[0],Draw[1],Draw[2],Draw[3],Draw[4]=Draw.D0,Draw.D1,Draw.D2,Draw.D3,Draw.D4
Draw[5],Draw[6],Draw[7],Draw[8],Draw[9]=Draw.D5,Draw.D6,Draw.D7,Draw.D8,Draw.D9
--*****************************************************************************
function DrawNum(right, top, Number, color, bkgnd)
--*****************************************************************************
-- Paints the input number as right-aligned.
-- Returns the x position where it would paint another digit.
-- It only works with integers. Rounds fractions toward zero.

    local Negative= false
    if Number < 0 then
        Number= -Number
        Negative= true
    end

    Number= math.floor(Number)
    if not color then color= "white" end
    if not bkgnd then bkgnd= "clear" end

    if Number < 1 then
        gui.box(right+1,top-1,right-2,top+5,bkgnd,bkgnd)
        Draw[0](right-2,top,color)
        right= right-4
    end

    while (Number >= 1) do
        local digit= Number % 10
        Number= math.floor(Number/10)

        gui.box(right+1,top-1,right-2,top+5,bkgnd,bkgnd)
        Draw[digit](right-2,top,color)
        right= right-4
    end

    if Negative then
        gui.box(right+1,top-1,right-2,top+5,bkgnd,bkgnd)
        gui.line(right, top+2,right-2,top+2,color)
        right= right-4
    end
    gui.line(right+1,top-1,right+1,top+5,bkgnd)
    return right
end


local MemRead= memory.readbyte

--*****************************************************************************
function within(input, low, high)
--*****************************************************************************
    return (input >= low) and (input <= high)
end

--*****************************************************************************
function ShowHP()
--*****************************************************************************
if memory.readbyte(0x0005) ~= 0x48 then return end
local ShClr= {"#FFFFFFFF","#00FF00FF","#FF8000FF","#00FFFFFF"}
local background= "#000000FF"
local ShX= { 0,13, 0,13}
local ShY= { 0, 0, 7, 7}

    local SCR_x= MemRead(0x0094) + MemRead(0x0095)*256
    local SCR_y= MemRead(0x0096) + MemRead(0x0097)*256

    for i= 0, 3 do
        local xpos= MemRead(0x04AC+i) + MemRead(0x04B0+i)*256
        local ypos= MemRead(0x04B4+i) + MemRead(0x04B8+i)*256
        xpos , ypos= xpos - SCR_x -2  ,  ypos - SCR_y -28

        local HV= MemRead(0x04BC+i)

        if (HV > 0) and within(xpos,-15,270) and within(ypos,0,170) then
            xpos= math.min(math.max(xpos,9),239)
            DrawNum(xpos,ypos,HV,"white",background)

            HV= MemRead(0x04C0+i)
            if HV > 0 then
                DrawNum(xpos+13,ypos,HV,"green",background)
            end

            for Sh= 1, 4 do
                Shield= MemRead(0x04CC +4*Sh +i)

                if Shield > 0 then
                    local DX , DY= xpos+ShX[Sh]  ,  ypos+10+ShY[Sh]
                    DrawNum(DX,DY, Shield, background, ShClr[Sh])
                end
            end
        end
    end
end


--*****************************************************************************
function TimerDance()
--*****************************************************************************
local tmr= MemRead(0x005A)
local clr= "white"
if tmr == 0 then clr= "red" end

    gui.box( 250,222,251,163+tmr,0,clr)
    gui.line(249,222,252,222,clr)
    gui.line(249,162,252,162,clr)
end

--*****************************************************************************
function FoodBar()
--*****************************************************************************
local Fd= math.floor((MemRead(0x0053)+3)/4)
local Wt= math.floor((MemRead(0x0054)+3)/4)
    gui.box(  1,158,  6,223,0,"white")

    if Fd > 0 then  gui.box(  2,222,  3,223-Fd,"green"    )  end
    if Wt > 0 then  gui.box(  4,222,  5,223-Wt,"#0040FFFF")  end
end



--*****************************************************************************
function NextSpell()
--*****************************************************************************
local MUD= MemRead(0x04F3)
local count= 0

    for i= 0, 2 do
        if MemRead(0x054D+i) < 128 then
            count= count+1
        end
    end

    if     MUD == 0 then
        MUD= 1
    elseif MUD == 1 then
        MUD= 0
    else
        MUD= 2
    end

    for i= 1, 3 do
        local H, Hc= 133, ((MUD-i)%3)*8
        local color= "blue"
        if i == 3 then color= "white" end

        if count >= i then
            gui.box(3,H+Hc+2,4,H+Hc+3,0,color)
        else
            gui.box(1,H+Hc  ,6,H+Hc+5,0,color)
        end
    end
end


--             Attack      Meditate    Fly         Speed
--               Weight      Light       Slomo       Fire Doom!
local timers= {0x06C9     ,0x06D5     ,0x06CB     ,0x04E7,
                 0x04EB,     0x06CC,     0x04E4,     0x06E3}
local colors= {"#FF4000FF","#00FF00FF","#00FFFFFF","#FFFF00FF",
                 "#C0C0C0FF","#FFFFFFFF","#FF00FFFF","#FFFF00FF"}
--*****************************************************************************
function SpellTimers()
--*****************************************************************************

    for i= 1, 8 do
        temp= MemRead(timers[i])
        if temp > 0 then
            if temp >= 100 then  --Draw HI
                local y= 7*i
                gui.line(1,y  ,1,y+4,colors[i])
                gui.line(3,y  ,3,y+4,colors[i])
                gui.line(5,y  ,7,y  ,colors[i])
                gui.line(5,y+4,7,y+4,colors[i])
                gui.line(6,y+1,6,y+3,colors[i])
                gui.pixel(2,y+2,colors[i])
            else
                DrawNum(7,7*i,temp,colors[i])
            end
        end
    end
end

--*****************************************************************************
function zammie()
--*****************************************************************************
    ShowHP()
    TimerDance()
    FoodBar()
    DrawNum(254, 1,MemRead(0x004B)+MemRead(0x004C)*256,"#0080FFFF","black")
    DrawNum(254, 8,MemRead(0x004D)+MemRead(0x004E)*256,"#00C0FFFF","black")
    SpellTimers()
    NextSpell()

end
emu.registerafter(zammie)
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
Well, here's an improvement of around 35 seconds. Syncs with Old PPU and New PPU. Not submitting due to lack of dancing at the end. Besides, I wasn't aggressive enough with my mana usage. But I basically changed every segment throughout the game, as follows: TOWN - Slotermeyer's strategy of JUMPs first was the better choice. Unfortunately, the act of getting the LETTER erases the JUMP spell from my list of spells for some reason... Also snatched up 500 gold! WILDERNESS - Used EXORCISE on the plant monster instead of LIGHTNING. LAKE - Bought an extra mana potion with the extra gold, using excess mana beyond the 1250 mark on FLEET FOOT (4). Also jumped off a shell and almost into water... Almost! More efficient gargoyle battle. FOREST - Aggressive use of FLEET FOOT. EXORCISE against mushroom spirit. CAVERNS - Aggressive use of FLEET FOOT. Sidetracked to grab a REFRESH POTION. DUNGEONS - I JUMP throughout this whole place, using the massive load of mana I got from the side-path. Yes, JUMP is faster than FLEET FOOT, at a slightly higher mana cost. CASTLE - A few other JUMPs and an extra FLEET FOOT. EXORCISE the flame demon. TOWER - Skip the mana fountain. The mana from the REFRESH POTION used in the DUNGEONS is still lasting. TOWER TOP - EXORCISE versus Abadon. EXORCISE wins! About 280 mana to spare One thing to check is how much more aggressively can I cast my speedy spells. There doesn't appear to be much room to squeeze in much more. Also, I can't make fun of how Paul manages to return home with critical injuries, malnutrition, and deadly venom anymore! The REFRESH POTION is too powerful! Then again, it's hard for viewers to see such stats.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I'm working slowly as ever, but progress does exist. But because I'm working as slow as I am, I had this fear that if ever I lose my progress, I'll never want to continue. It may be short bit of progress each time, but time has been spent in every frame. http://infinitefactors.org/misc/Lee/Guardian%20Legend%20Online_BackUp.zip As such, I'm keeping an online backup. I'm also providing access to this backup to anyone who wants to know where I'm currently at. Whenever I think about it, I will save a new backup to the same location, but don't expect frequent updates to this backup. Check every several days, if you want to see the new seconds I tacked on. When I finally get Corridor 1 done, I'll be sure to announce that fact.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I will add my voice against this rejection as well. The one this run should obsolete, by all intents and purposes, has more "known improvements" than this run. And proven by this run that the older one has possible improvements, by virtue of this run being faster. And by a significant margin. Why invoke the rule? Why should the rule apply to this run and not the old one? And how do you know there's going to be, within a reasonable time-frame, that an improvement will come? Why invoke the rule? If it's formality, then it's not a good form if it's bringing up this sort of storm on this forum. Rules can always change. Why should the rule apply to this run and not the old one? If your answer is knowledge, the fact that no one knew of improvements back then, then why should a factor outside of either run call for a rejection? And how do you know there's going to be, within a reasonable time-frame, that an improvement will come? I don't honestly expect a good answer unless the author knows himself or herself well. Or you, the judge, work on the run yourself. If the moment the next run will be submitted is indefinite, then what are we doing without the best run in the meantime?
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
Actually, the weapons can speed up boss fights. Going straight to Corridor 1 first thing will certainly give a slower boss fight at the end, since you must wait for your 4 shots at a time to finish "exploding". And that orb up there isn't going to pick itself up later on! At this stage of the game, the saber does 2 damage every 8 frames, while the other two weapons I pick up can only deal 1 damage in the same span of time. Then, of course, I can shoot the boss as well, which adds 1 damage for each shot, 3 shots at a time with certain sub-weapons readied. I haven't measured the time between these shots, but it's definitely longer than 8 frames. Do note the mini-boss I destroyed. It has 16 HP. I shoot it 6 times with the basic gun (in two groups of 3 shots) and strike it 5 times with my saber. Without the saber, I would need 16 shots (in four groups of 4 shots), which will definitely take more time. The other bosses certainly have more than 16 HP to worry about. ... And there's apparently an 8-frame rule on when sub-weapons can damage bosses. The sub-weapon can also be in contact with some boss in the other 7 frames, with the effect that your basic gun and yourself can't even touch the boss while the sub-weapon is inside the boss. With the saber, you could simply activate it every 8 frames and leave it off the other 7 so you can shoot away efficiently. Other weapons have the problem of existing for chunks of frames at a time. But I won't "demonstrate" only the saber weapon for every boss I come in contact with. Although it would likely be the fastest strategy for most bosses, I want to avoid repetitive use of the same weapon, never showing the beauty of what all the other weapons have to offer. I'll come up with ways of giving useful screen time for every weapon. Even the grenades and that spinny shield.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
Well, cleaned up the inactivity around that boss and beat it a few frames faster. Oh, and also did some stuff in Area 0. My own gut feeling is to ignore all that is said here and grab everything unconditionally. Even the full heals that serves no purpose other than to fill the Guardian's health back to full and are marked off by the password system. While it can be argued that I do too much, I will have unambiguously fulfilled the 100% goal. When I clearly collect "useless" items, the reason why I do it might be transmitted well enough, as it's in a shop or a conspicuous item box, and what completionist would leave those alone? However, by doing so, I feel as though I ignore everything said here simply because I feel it's right and the rest of the world is wrong. The main thing I should be concerned about is what my audience thinks, not me. I'm still not 100% clear about the 100% goal, but I know what is ideal for me. I'm still working out what will be ideal for others. Here's the impressions I've got so far: Useless free items. Skip them? I say no, but the audience says yes. Useless shops. Skip them? I say no, but the audience says yes. The SHIELD conundrum. Skip a mini-boss? I say no. Audience is largely unclear, leaning no. Rate of Fire power-ups. These will have no clue they even work. Skip them? I say no. Audience is unclear. I can still TAS some more before the answers to these questions become important. I want to know, as a person unfamiliar, a competent player, or an actual speed-runner, what is the best option you'd want to see?
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
Hopefully now you see why I am considering getting all items unconditionally, using my definition of 100% in that it changes the password -- I dodge tough questions like these. Unfortunately, we're currently basing 100% on what we can see of our inventory, so we're stuck with dealing with these questions. In my list, I mention that there are 8 EEs (Scratch that, I miscounted. It's 9). I also mention that there are 4 free ones floating about. This implies that the other 5 are from minibosses or corridors. There's no avoiding picking up at least two of them from the corridors, and the remaining three would come from minibosses. So now the question is, should I bother the minibosses here? Whatever we pick for the SHIELD case should apply here as well. My thoughts for the 7 SHIELDs are... Actually, I'm not even sure myself. I am leaning towards stomping them all anyway, but I wanted to word the question such that the conflict between "avoid wasteful pick-ups" and "kill everything" is made clear, in the sort of form that would make one think. It made you think, right? As for the Rate of Fire pickups, how often one can shoot is generally limited by the boss's ability to delay your next shots. Your basic shots turn into this little explosion as it hits a boss, and that explosion takes a while to disappear. The pick-ups won't speed that up, so any time saved by grabbing five of them would likely be no more than a handful of frames. Compared to the many seconds it would take to get them in the first place. Time will almost undoubtedly be lost in trying to get them. The real point is, someone who knows the game will spot this curious disregard for this power-up. I claim it's 100%, but I won't actually have any decent explanation why I didn't max out this technically unseen stat. Sure, I'd fool everyone who doesn't know the game, but those who do might feel somewhat cheated. Although it would waste time, I'm quite for picking up the Rate of Fire pick-ups.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
In that case, it will be important to know which items there are too much of: 5/3 Orb 4/3 Quick Wave 4/3 Spinny Shield 4/3 Grenade 4/3 Fireball 4/3 Sprites 9/? EE 6/5 Rate of Fire boosters 9/6 SHIELD I suppose it's safe to assume that the shops and free item spots are optional, while the mini-bosses and corridors are all mandatory. Alright, EE. There are four free ones scattered about completely unguarded. The first EE I pick up is all I need to "max out" the weapon -- Further EE pick-ups only add the number of shots I get with it, which can be found from enemies after I get my first EE. Should I bother getting these four free ones? There are only two free SHIELD pick-ups. The remaining 7 are found from various corridors and mini-bosses, which is one more than enough to max out SHIELD. Shall I slay all the mini-bosses knowing I'll get a useless SHIELD power-up at the end? And while we're on the subject of visually useless items, what about the Rate of Fire power-ups? Under TAS conditions, the first 4 do nothing (turbo button pressing), and it is only of benefit after I get my fifth. There's no update in the status screen, I will not make use of these power-ups until I get the fifth, and most viewers will never be able to tell any difference. Therefore, should I bother even revealing the existence of such an item? It's quite possible that I can avoid ever seeing its sprite, and all instances of these items are free or from shops. These are the kinds of questions that come up in my head. I'm sure the answers will fall towards "no", "yes", and "yes", but I thought I should ask some tough questions that will help me know where 100% begins and ends. I have other questions, but it's all largely a matter of figuring out which ones are the best ones to skip, which I'll try to answer on my own. EDIT: I fixed the EE count. Yea, I'm bad at counting.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
shallow wrote:
I wouldn't be so concerned about not killing everything on the screen. (...)
I'm not even worried to begin with. I'm just trying to get opinions here, and yours is definitely one of them. The later stages are definitely full of enemies alright, and trying to kill them all might end up forcing me to resort to a certain set of weapons only. This doesn't fit well with my goal of showing off weapons, correct?
shallow wrote:
I think the death warp is probably a good idea, but just off the top of my head I don't know which areas exactly it'd be most useful.
One such use I can think of is to pick up all items in Area 1, then beat the corridor that's quite deep in Area 1, then promptly kill myself off. Should save me from having to walk all the way back out of Area 1.
shallow wrote:
Also, if you complete the corridor with 0 health, you'll still gain a little health from getting whatever item the corridor gives. (...)
The weapons restore exactly 2 units of health. It's true that I'll never leave the corridor room at 0 health, but chipping myself down to 0 against its boss will still leave me with 2 health to lose instead of something uncomfortably higher.
shallow wrote:
Is it really necessary to show off all 3 levels of each sub-weapon? (...)
I think it is, but that's my opinion. The viewer may wonder why I'm showing all three levels of that grenade weapon, but why did this TASer skip over the level 2 fireball? What did the level 2 fireball look like?! Gah! I'm torching the place because I never got to see it! Rawraraarrr! Okay, maybe not that extreme, but why skip over displaying weapon levels when it's just one pause transition away? Sure, they may seem similar, especially the saber weapons, but how will the viewer know that if I skip things? That's my reasoning why I want to show off all weapon levels.
shallow wrote:
It is my opinion that you should not visit any shops whose upgrades are obsolete. (...)
It appears your definition of 100% is The Guardian's inventory, and probably corridor completion as well. My definition is "does it change the password with no way of changing that aspect again?" Your argument has a pretty good basis, in that why am I wasting time with something that won't improve me in any form? I like the elegance that, if I record a password, then if I visit the area, it's already done or collected. I'll need to hear more thoughts from others.
shallow wrote:
(...) only level 3 is faster (and may be more powerful, but I'm not sure on that) (...)
The damage chart I worked up might be useful...
pw = Depends on ATTACK stat.

Orb       : pw    pw    pw
Side Waves: pw    pw    pw
Quick Wave:  1     2     6
Shield    : pw    pw    pw
Grenade   : 16    32    48
Fireball  : 16    24    32
Sprites   : pw    pw    pw
Spin Shot :  4     7    10
Laser     :  1     1     1
Saber     : pw*2  pw*4  pw*8
Dbl Saber : pw*2  pw*4  pw*8

Basic shots: pw
Contact with enemy: pw
Contact with boss: 0


1 ATTACK  g    : pw= 1
2 ATTACK  gg   : pw= 2
3 ATTACK  ggg  : pw= 3
4 ATTACK  gggg : pw= 5

0 SHIELD       : pw=64

Each hit on a boss caps at 5 damage.
0 SHIELD will override the ATTACK stat.
0 SHIELD is reached only by password.
Lv2 and Lv3 sabers deal 0 damage with 0 SHIELD
The names here are rather arbitrary.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I've beaten the first boss. But I need to check a few things like how many frames I can save on the boss and the blocks of inactivity before and after said boss. Sure, I fail to hit a lot of things. However, "maximum kills" or "maximum score" isn't my primary goal. Being quirky and showing off is, especially when I can get the music itself involved. Things will still be done in a timely manner, and I'll do my best to compare the fastest strategy with what I do instead. As for the boss, it seems as though the same bullet can hit multiple turrets at once, which explains why I park myself in between turrets. Part of how I rip the many turrets apart so quickly. I'll let things sink in for a few days before I continue. As for planning, the first thing I should check is whether death warps can be useful. Complete a corridor with 0 health remaining, then let some enemy finish you off after you win and collect the item. If we find the death warp useful, it could be a useful way of getting around after going deep into some area. There are 12 sub-weapons, not counting NO USE. 11 of them have 3 levels each, so that's 34 different effects to show off. Each pause will eat up some frames, and there will be no way of getting those frames back when considering most switches. The single beam saber is the optimal weapon for most bosses, and switching out of it will likely just slow you down. Great to keep the saber for any%, but it kind of becomes stale in 100%, which is why I'm considering showing off a bit rather than aim for fastest 100% time. What's the definition of 100%? Later on, there are shops that can give no further benefit, but you can shop at those places anyway. Doing so is a permanent effect that's recorded in the password system. Also, there are items in which their only effect is to restore you to full health. Yet these are also recorded in the password system for collection. Should I grab those as well? Okay, done with some long-term concerns I'm thinking about, now for the short-term... There are 5 weapons in reach before I drop myself in Corridor 1, but two of them are quite out of the way. I may want to grab a hold of three of them before jumping into Corridor 1. But after Corridor 1, if the death warps prove to be useful, I may want to head straight into Area 2 first, then clean out Area 1. Basically, after I'm done with Area 2, I stroll into Area 1, collect all the power-ups in the area, demolish the boss in Corridor 11, then promptly die in the next room. This would warp us back to the Area 2 save room, much closer to Area 3. ... At least, that's what I'm thinking. Obviously, I need to test things. I also need to clean up my inactivity around the opening Corridor boss, as well.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I have started my own run. Currently someplace in the opening corridor. Used FCEUX 2.1.2. Config -> PPU -> New PPU. At the moment, I'm trying to decide between shooting that last ring of comets or sitting in the middle like I did and escape without damage. I'm planning to try a 100% run. I also plan to show off all levels of every weapon, even at the expense of time. As a 100% run, I feel some variety is desired so as to differentiate it from any%. Would like to know how I'm doing so far, even though I haven't completed the opening corridor yet.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I'm just breaking the game a few times. None of them any good for speed, but the point is entertainment. Those save slots do prove useful to show up to five different scenes in one movie file. Scene 1 - Paul makes it to town even when dead! What the hey?! Scene 2 - Did I create a portal with "death" and "new area" triggers? Scene 3 - Boat guy wasn't intended to be used more than once... Scene 4 - Anti-grav spikes plus UNKNOWN... spell = a mess Scene 5 - Here's what happens if I don't get through the door in time... I'm thinking about trying to improve my run. Maybe not actively trying, but the thought is there.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
The latest script tends to be here, I believe. The Multitrack.lua script has a rather steep learning curve, as evidenced by the fact that adelikat told me himself. I'll take his word for it, considering how much effort I was putting into explaining things. As of the latest revision, it only works with FCEUX 2.1.2. Earlier versions of FCEUX doesn't have all the fixes and latest lua functions that this script uses. Anyway, time for instructions... Basic edits: When you run the script, a message from the script itself lets you know you've started running it. You'll also see a mostly white bunch of symbols, with the middle of these symbols surrounded by a green rectangle. Whatever could they mean? This is the input display for the frames surrounding the current frame. You can move this around by using the mouse, just drag it around with the left mouse button! I'll explain this display later. This script uses up to as many as 26 buttons off your keyboard! However, I'll start with 8 of them. By default, the arrow keys, "J", "K", "L", and "O" are linked to the joypad buttons. If you tap one of these keys, you'll note that some red symbol inside the box turned green. It's letting you know that, if you frame advance, these buttons will be pressed. If you don't like these buttons, change them in the script itself. I will refer to them as script-joypad buttons. Try out frame advance for a bit! As you do so, you'll notice the white above the box turns red. Your green stuff may scroll upwards through the display as well. The script also has rewind built-in, default key being "R", so you can back up as well, conveniently. The white symbols mean the script has yet to even see that frame. The red means the particular button wasn't pressed, and the green means it was. Already, with the 8 buttons, frame advance, and rewind, this is a rather powerful editing tool. Keep playing around with the buttons a bit, get yourself used to it a bit. Oh, wondering how to access player 2 through this script? The player switch is default to "S", and hitting it will change some displayed number to 2. This will let the script buttons affect player 2 now. Hit it again, and if you haven't changed the script, it will display both players' input. Once more to get back to player 1. If you need player 3 or 4, edit the script, find "local players=", and change the number you see there. Control: Now, there are ways to change how the control works. Hold "space". You'll see a bunch of words show up. Tap any of the 8 script-joypad buttons, and you'll see a word change. These words mean stuff as follows: "Both": Default setting. Uses both "Keys" and "List" logic. "Keys": The script will react to the script-joypad buttons. "List": The script will read its own list to determine button-presses. "Off": The script will not interfere with the button. This is for individual players, so "Both" for player 1 doesn't mean "Both" for player 2. Changing these to "List" or "Off" prevents the script buttons from working, good for avoiding mistakes or just turning off control of the script buttons. Changing to "Keys" or "Off" makes the script ignore its list, good for getting rid of input you don't want recorded. Finally, there's the "home" and "end" keys. The script does allow for the normal joypad input through the emulator's own set-up. In fact, the controller can also negate stored input in the script's list. By tapping "end", you prevent the controllers from adding to the input list, or allowing it once again. By tapping "home", the same for removing input from the list. Display: As I already said, you can use the mouse to move the display around. With numlock on, you can also use "numpad2", "numpad4", "numpad6", and "numpad8" to move the display a bit more precisely. You can change how much input is shown with "numpad1", "numpad3", "numpad7", and "numpad9". Tapping "pageup" or "pagedown" will change how opaque the display is. If you don't want to see the display, you can do that. That's about all that needs to be said. It helps to see what you're doing, so make sure you adjust whatever is needed! Odds and ends: Buttons I didn't mention until now goes here. "numpad5" will set whether the script will immediately apply changed input to its list. For the most part, all it really does is turn that center box white. However, if you want to change things while rewinding, this will ensure that your script-joypad button presses will stick to the input list as you're going backwards. "insert" and "delete", as my default assignment suggests, will insert or delete frames from the input list. This will shove input aside so that if you really needed your input through frames 254~497 to actually be on 255~498, a single press of "insert" at frame 254 is all you need. Finally, I should have covered every button the script uses. Remember, they're all defaults, and I recommend opening the script to change the keys around to suit your needs. Silly little tricks: Stuff that I found is rather neat the script works that way is put here. You don't actually need to run a movie to run this script. I'm not sure how often the need will arise, but the possibility is there. However, the script has no long-term way of storing what you put in there, so if you do want to keep what you did, you had better darn well record a movie through FCEUX's usual features! The script will always record button presses as the emulator plays out. Whether through player input or movie, everything gets added into the list. As such, you can play a movie on read-only, run this script, then let the movie play out while you allow the script to gather its input. You could also start at any point -- Starting the script at frame 0 is entirely optional. Thanks to inserts and the fact the script picks up input on movie read-only, you can make a pseudo-macro by playing out a loop you want done, set the movie on read-only, go to start of loop, then hit "insert" a few times. Once you've inserted enough blank frames for exactly one loop, let the read-only fill in the blanks. Re-load, repeat. Nowhere near as convenient as actual macros, but it's a solution, should you need it. Remember, the script's primary purpose is its editing powers. You can record some silly movie, play it back with the script running, then make pinpoint edits as needed. The secondary purpose is multitrack recording. Yes, it's called multitrack.lua, but apparently, the multitrack part is its secondary purpose. Well... It's about time I documented how to use the darn script. I'm probably still missing some stuff, but I hope this helps...
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
What is the mechanism for replenishing your library of spells? I assume there's some sort of draw pile or something. How exactly is each turn worked out? Like this? - Draw a card - Move - Cast spell Anyway, some spell idea I'm playing around with for your board game... Familiar: Creates a cute little critter at target spot. - If something occupies said spot so that no one may enter it, the spell fails outright. - Any amount of damage kills the poor critter. You heartless demon! - If a damaging spell is launched at the same moment the critter is summoned, the critter takes a hit. - This critter can "pick up" and later "drop" a spell marker, so what the spell targets can change. - Lasts forever. Or until the game ends or critter takes damage. - Can move around just like any mage. So cute when it moves! Mild variations includes one that can survive a single hit of any damage (after that, becomes rather fragile), and one that can move two spaces at once (Yeep! Dodgy to the max!). What I wrote is a bit wordy. Probably should be condensed a little if you plan to write stuff on the cards.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I have previously made an attempt at porting this script to Snes9x 1.43, but to no success. The main reason being that gui.register does not call its function multiple times while paused in Snes9x. I am not sure if changes have been made since then. A simple test, if you wish...
local i=0
function tryme()
    i= i+1
    gui.text( 1 , 10 , i )
end
gui.register(tryme)

while true do
    snes9x.frameadvance()
end
If the number continuously goes up while emulation is paused, then gui.register is called repeatedly while paused. If the number freezes along with emulation when paused, gui.register is called only once, a case which I can't port this script with its full functionality. Incidentally, I stopped putting updates here. All my updates go straight to the SVN development now...
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
Recall my slight change to joypad.set(). I did add in a feature which I never really reminded you about. The option to invert user input by using a string, as a result, went unmentioned in the help file.
There are 3 possible values, true, false, and nil. True will turn the button on, false will turn it off. Nil will leave it unchanged (allowing the user to control it).
I recommend a change in the documentation, something like this as a possibility:
There are 4 possible values: true, false, nil, and "invert". True will force the button on, false will force it off. Nil leaves control up to the user, and "invert" will reverse the user's input. Thanks to our awesome spellchecking, you can horribly misspell "invert" and it'll still work! (read: any string works. "invert" is simply a convention)
EDIT: Now that I think about it, looking at the changes since last release, I see now you can catch stateloads from lua. Now I can get rid of my own loadstate detection code and substitute the built-in function. I think I'll get to work on the multi-track script a bit more. Maybe I can clean up a few things... EDIT2: Now that I read through it more, there's more stuff you haven't yet documented in the help file. In that case, I'm guessing it's just a matter of time on your hands. Or priorities. Or, whatever it may be.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
Here's an example of locking a player out until you win. Both players are done by me, no bots. While TASing that up, I believe there is still a defense against SLAVE. Basically, match up some cookies at the right time. Get some cookies started and match 'em right as SLAVE is getting applied to you. When an opponent ends up making you match something, it breaks you out of SLAVE, but if you get a match going, I believe it is possible to trick the game into allowing you to break SLAVE. In any case, I want to show off some more quirks in a 2-player match. I'm actually thinking about submitting a movie with a couple of TASed players beating each other up. SNES version would likely be preferable, as you can actually see the cookies moving smoothly and things might make sense, but NES is so much easier as I can use my multitrack script. Ease isn't a good excuse for a good movie, though. After all, even without an objective goal like "fastest time" or "maximum score", one can still create entertainment. There's a few good published videos where speed isn't a goal. Lastly, I may well have given up on that bot. I'm having troubles messing around with the RNG... I believe the RNG changes only when the game needs a random number, such as determining cookies or a new power, but it still eludes me.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
Well, I'm far from completing the bot, but I can already come to one conclusion: It is possible to permanently disable your opponent. Here's the list of evidence that suggests the possibility of a permanent disable: - The SLAVE power lasts 420 frames - Powers refresh every 300 frames - Powers always alternate between targeting 1P or 2P - It is possible to manipulate what power you get - 5 Yoshi cookies are generated when you clear two normal lines at once - It will take a rather short time to set up two normal lines to clear at once So basically, once you fire off a SLAVE at the opponent, the game will refresh a new power targeting you. But, by waiting 300 frames, a new power will take its place, targeting your opponent once again. Since SLAVE lasts 420 frames, you still have 120 frames where the opponent can't act. During which, you could match up more Yoshi cookies and re-apply a SLAVE, giving you another 420 frames of relentless control. But what are the chances of another SLAVE showing up, you ask? Why, this is a TAS-level bot we're talking about. This is one that will actively manipulate luck in order to force SLAVE to show up every time. And how does one manipulate the powers? Change the RNG. All it will take is studying how the RNG works for a little bit, and then do it. My current bot on display here takes an average of approximately 120 frames to match up one line. It is not optimal. Double-lines shouldn't take much longer to make, if at all, giving you 5 Yoshi cookies to use well before the 420 frames are up. Even if you mysteriously have 3 each of 4 types of cookies and the rest is the fifth type, you have time to make a junk match to help set up a double-match. But the whole thing hinges on luck manipulation. To what extent can we manipulate the luck? Cursor movements alone certainly isn't enough. I'll just have to search harder. But I did change what power shows up by doing different stuff. So basically, SLAVE-lock your opponent by manipulating luck and making double-matches. Just make sure you don't accidentally match a line in the opponent's grid, or you'll break the SLAVE effect. I now want to create a movie that demonstrates this. See if it really is possible. If it is, this would be the "unbeatable" strategy to follow under TAS conditions. Basically, whoever can SLAVE the other first wins.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
FractalFusion wrote:
Interesting script. However, I'm puzzled as to why coroutines were used. Perhaps for speed?
I wanted to avoid going through several hundred (maybe thousands, if you're insane) stored subtitles every frame. I only want to deal with subtitles that should remain active. So, yes, it's for speed. That, and I wanted some practice with co-routines. This ended up being a decent enough challenge to get the concept cemented in. For reference, my E.V.O. subtitles have 178 instances of subtitles. I don't want to deal with going through all 178 only to find out I shouldn't display any of them at the time. Then do it all over again 1/60 of a second later. Other than that, the script you posted is real simple and has no obvious bugs. Even gets around the problem of dealing with subtitles that aren't in chronological order. Mine fails pretty badly in that regard. As for the size of the script, yours is plenty more compact than mine. Clearly, you have the advantage when few subtitles are concerned. When we start getting people who wants a four-digit count of subtitle instances, I'm sure a little speed would be desired...
Highness wrote:
What about something that also converts input files to subtitle files for files that are encoded on the site?
I'm not quite sure what you're asking here... Do you mean turning subtitles displayed in the emulation into a separate stream for multimedia files, like a .mkv or something? If that is what you're asking, I have no experience in that regard.
Post subject: Subtitles lua script
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I created a basic subtitles script for my E.V.O. run. That's about as much as I cared to do at the time: Have something to display texts that I want. Then zidanax wanted to use it and made minor changes to it. Seeing as there existed demand for it, I put effort into perfecting it. I actually made a slightly more advanced version involving linked lists, but it breaks horribly in FCEUX for some reason, by crashing it on script close. The version I'm posting is the array-based version. Just paste this into a file, save it as a .lua file, and run it through an emulator. If it works, you should see three different subtitles in a few seconds. You'd probably want to change them at some point, so open up the file with some sort of text editor and look for the first line of asterisks, *******. Examine that bit of text that follows to figure out what to do. [code lua]--Subtitles intended for Snes9x. Might work for other emulators. --Script mostly by FatRatKnight, zidanax added some modifications, then FatRatKnight went rampaging to make the perfect script -- Features: -- Adjustible position -- Variable duration -- Any arbitrary number of lines. -- Multiple instances of subtitles! -- Array-based version. -- Linked list seems to break horribly in FCEUX, by crashing it on close. -- This is why I'd rather hand out the array-based version. local emu if FCEU then emu= FCEU elseif snes9x then emu= snes9x elseif gens then emu= gens end local Time, Duration, x,y= {}, {}, {},{} local LineArray= {} local i= 1 -- Can take an arbitrary number of lines -- Its name is one letter long to save bytes. Lots of them. function S(st,sd , sx,sy , ...) --Time, Y-pos, duration, lines if arg.n <= 0 then return end -- Sanity Time,Duration , x,y= st,sd , sx,sy for z= 1, arg.n do if not LineArray[z] then LineArray[z]= {} end LineArray[z]= arg[z] end i=i+1 end --***************************************************************************** -- Begin text --Format: --S( Frame to appear, duration , X-pos , Y-pos , Line1, Line2, ... , LineN) -- It does not like it when things aren't in chronological order! S( 200, 800, 30, 30,"Just messing around.") S( 500, 400, 40, 39,"You can specify","an arbitrary","number of lines,","or overlapping subs!") S( 600, 500, 30,100,"This is simply a sample test.") --End text --***************************************************************************** -- Initializes important stuff. Then goes on to display said stuff. -- In co-routine form so I can create multiple instances of subtitles. function CoRoutine_DispMsg(Index, DurationOverride) if not Index then return end local D , X,Y= Duration[Index] , x[Index], y[Index] local L= {} if DurationOverride then D= DurationOverride end local n= 1 while LineArray[n] do L[n]= LineArray[n][Index] n= n+1 end n= n-1 coroutine.yield() -- Initialized. Stop here for a= 1, D do for b= 1, n do if L then gui.text( X , Y +9*b, L ) end end if coroutine.yield() == "die" then return end end end local routines= {} local LastRoutine= 0 -- Finds the correct index on state load or lua open -- Loads all text that should exist on stateload, with proper endpoints -- Will also kill previously existing subtitles function FindIndex() if LastRoutine > 0 then for j= 1, LastRoutine do coroutine.resume(routines[j],"die") routines[j]= nil end LastRoutine= 0 end i= 1 local fc= movie.framecount() while Time and Time <= fc do local endframe= Time + Duration if endframe > fc then LastRoutine= LastRoutine+1 routines[LastRoutine]= coroutine.create(CoRoutine_DispMsg) coroutine.resume( routines[LastRoutine] , i , endframe-fc ) end i= i+1 end end -- Load-state stability if savestate.registerload then savestate.registerload(FindIndex) -- Well, it exists. Use it! else -- Implement pseudo load-state detector local fc, lastfc= 0, 0 gui.register( function () fc= movie.framecount() if (fc ~= lastfc) and (fc-1 ~= lastfc) then FindIndex() end lastfc= fc end) end -- Executes co-routines. Will also clear away dead ones. function RoutineJunk() local a= 1 while a <= LastRoutine do if coroutine.status(routines[a]) == "dead" then for j= a, LastRoutine do routines[j]= routines[j+1] end LastRoutine= LastRoutine-1 else coroutine.resume(routines[a]) a= a+1 end end if FCEU then gui.drawpixel(1,1,"clear") end -- FCEUX display tends to stick end --***************************************************************************** -- Finally at main body of executable code FindIndex() while true do -- In case of state-load after the last subtitle is loaded -- Wait until the right time for the next subtitle -- Loads new subtitle routine, if any should show up, -- then runs all current routines. while Time do if Time <= movie.framecount() then LastRoutine= LastRoutine+1 routines[LastRoutine]= coroutine.create(CoRoutine_DispMsg) coroutine.resume(routines[LastRoutine],i) -- Trigger initalize code i= i+1 end RoutineJunk() emu.frameadvance() end local timer= 0 -- Bleh... Cute, random timer. -- Idle loop. Basically sit there and do nothing. while not Time do RoutineJunk() -- To get those last messages... timer= timer+1 emu.frameadvance() if timer >= 72000 then gui.text(0,200,"You have an active script...") end if timer >=216000 then gui.text(0,210,"If you want hidden messages, open the script") end end end[/code] This is simply a general-purpose subtitles script that should, short of drawing shapes and colors, cover everything you need related to subtitles.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
You know, I realized I forgot some suggested screenshots in my submission text... Suggested screenshots: 17970 - Mirror match? 71721 - Double smash 85938 - Red mess 126451 - Buried boss Suggestive screenshots: (... No comment) 101028 - ... Really, no comment! 139388 - I will especially "no comment" this.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I just had to go on a quest to perfect a freaking awesome subtitles script. (EDIT2: Link removed) May as well not clutter too much here. Now you can insert any number of lines without a need to change code to support that many lines! Hey, maybe you want four or five lines at times! EDIT: Now it allows multiple subtitles at once! Threw in variable x position for whatever reason. I can foresee a desire to have a variable x-position and multiple different subtitles at once. Someone's going to want that, somewhere, someday. (EDIT: And now I've inserted just that.) It's going to happen, man! But hey, I decided to learn how to use functions that take a variable number of arguments, so I'm getting more practice. And co-routines and threads are fun as well, having just now tried it out.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
I'm not really much of a developer, but I think gui.register calls the function each time the emulator updates its display. For some reason, FCEUX was given a reason to update its display even when paused, perhaps so it can fade out its own emulator-related messages. Side effect being that it ends up repeatedly calling lua's gui.register function, because after all, it is updating its display. I have a feeling the fact it even works is unintended, especially since any documentation states that it's meant to be "Used when that 1 frame delay for rendering is not acceptable." But this is one of those cases where an unintended feature, the fact it's called repeatedly while paused and not just once, works very strongly in our favor. But that's what I think. That the fact my multitrack script even works is due to a glitch. One which we should cause other emulators to break in exactly the same fashion, quick. Another thing I'm thinking about is the drawing routines of other emulators. When lua pastes things into the screen, does it blow away the graphics of the game to do so? By that I mean, if I were to save-state with lua, then get rid of lua, play a few frames then load-state, will I see the lua junk in the freshly loaded state? That's one more thing to check.
Editor, Experienced Forum User, Published Author, Skilled player (1176)
Joined: 9/27/2008
Posts: 1085
It still has the fix for when you run out of subtitles and want to re-load. Try out the current script, run through all its subtitles, and then load an earlier state. You'll probably never see them again. Without a fix, anyway.