About 2-years ago I wrote a program called, "Fireball." While it had a great effect in igniting the screen, today for my code I don't want it to ignite everything.
So here is a smarter version, that only ignites only the pixels I tell it to, in this case, leaving the text intact but still leaving a very active and flaming fireball trail behind.
Can you think of a way to improve the code without having to write a manual "circle" routine as it does now ? Please let me know !
To see the original program go HERE:
Hey, dw817. I'd suggest using an offscreen buffer for saving and restoring screen content. I find Peek4/Poke4 strongly reduce cpu used for these operations. To render text on top, you can directly draw text every frame, but I prefer overlaying text from sprite memory. That way, you get 2 independent image layers combined with transparency.
I have splitted the code among _init(), _update60() and _draw() to get 60 fps and also measure % cpu used.
Some pico/lua-specific tips:
You don't need to declare loop variables. The 'for' implicitly creates its own local inside the block. In fact, you're declaring a local and then masking it inside the loop with the loop variable. For instance:
function test() local i="this is sparta!" for i=1,3 do print(i) end print(i) end > test() 1 2 3 this is sparta!
Also, pst() could be simpler:
function pst(x,y,c) if (pget(x,y)<6) pset(x,y,c) end
Mainly because pget()'s never going to return any value outside of 0..15.
Personally, I'd inline the pset() code inside hlin(), because the Lua call overhead is similar in magnitude to the content of the abbreviated function above. It doesn't really matter here, since you're not actually doing a lot of hlin()ing, but it would if you were.
Edit: Actually, in SkyBerron's version, the circle() function isn't even used. Never mind, but do take note of how it could be simplified.
Rather than what SkyBerron did, which was to use sprite ram to save the text overlay, I'd suggest you use sget()/sset() instead of pget()/pset() and use sprite ram for the cumulative fireball work, blitting the current image into video memory in _draw(), then simply drawing the text with print() on top. This way you don't need to back up the fireball to an offscreen image buffer every frame.
And, speaking of offscreen image buffers...
SkyBerron's offscreen image buffer code could be simplified a little by using the table keys as the memory addresses, rather than just being array indices and calculating the addresses:
function scr2ib(b) for a=0x6000,0x7ffc,4 do b[a]=peek4(a) end end function ib2scr(b) for a,v in pairs(b) do poke4(a,v) end end
Note that I'm using the unordered pairs() iterator in ib2scr(), but that's okay, because the order we restore the values doesn't matter as long as we restore all of them.
Basically I'm just trying to take advantage of the fact that tables inherently have arbitrary keys in them anyway, so we might as well use them to our advantage. It's no extra cost, and it allows us to get the address for free during the restoration function.
Taking in what I'm understanding, Felice. Wait ... You are saying a FOR/NEXT loop does not ... wait, that's confusing.
??? Oh that's confusing !
Okay, so =IF= I use a local variable ONLY for For/End then ... it doesn't need to be defined at all ?
You know, I ran into this. When I wrote that monster compressor a few days ago, I ran into this problem:
for i=1,8 do print(i) flip() i=6 end
And what do you think the results were ? Strange for me to say the least.
And then it ended instead of being stuck in a loop as it should be. I had to nastily write a buncha REPEAT/UNTIL/+=/-= statements for my compressor cause I needed that index variable to jump all over the place.
So the index EXISTS inside the loop and after that, boom, it's gone. History. Wow.
Any other surprises in PICO like this ?
You'd do well to spend some time reading the language docs on lua.org.
PICO-8 basically incorporates Lua 5.2, I believe, maybe 5.1, I forget. It's not the current 5.3. Just the interpreter, with a few tweaks like the += operator (not native to lua). It doesn't have the standard Lua libraries, so all you really need to get from the site is the language syntax.
There are a few minor things from the standard Lua library that exist in a limited form on PICO-8, but they generally have a somewhat different name that's more "retro", or just removes the namespace, e.g. string.sub(str,begin,end) -> just sub(str,begin,end).
And yeah, loop iterator values are set each time the loop iterates, so you can demolish them and they're still be the next value on the next iteration. You're given the value of the iterator once per iteration, rather than iterator itself. This is because iteration isn't necessarily a linear numeric thing, as with the pairs() iterator I used to hit all of the key,value pairs in the table.
If it helps, maybe think of the inside of the loop as being like a headless function that receives the iterator value as an argument. That's not literally what happens, but conceptually it's pretty close.
The for..end mechanism is designed to do a once-over-with-the-option-to-abort sequence, not something that's more conditional or back-and-forth. Gotta use the tools as they were intended. The tool for the job you described would be a while..end or a repeat..until with a local variable you increment and test according to your custom conditions.
I actually did this. Well, I went to LUA.ORG sometime ago and looked up string commands as I make heavy use of that in just everything I do, including graphics.
I saw this command:
string.find (s, pattern [, init [, plain]])
But couldn't get it to work, tried FIND as well.
Once these commands started to not be in PICO, I wondered what other commands LUA taught that would not benefit me in PICO.
Ah, didn't realize you were active at the moment. I'll stop tweaking the post above.
Like I said, it includes a few minor things, but not very many. As I'm sure you know, zep wants the API to be "cozy", and that means no bloating with lots of algorithmic helpers like find().
With the lua.org docs, if something is inside a module, e.g. table.sort() ("table" being the module), that's not the language, that's a standard library which is built with the language and is delivered to you with the language. Outside of PICO-8, you would be able to rely on the standard libraries being there, but on PICO-8, you only get the actual language plus the small library/api zep has given us.
Going to bed in a bit. Oh I know what it was, SkyBerron. No, I know I can print the text on top.
No, what I wanted to do was make a smart fireball that would ignore ALL colors around it except what I chose, in this case, colors 1-5.
That's why it's kind of tedious and why I can't use CIRC() outright. If you want to rework yours to print the text ONCE only and the fireball will not touch it and it's round, that's what I'm trying to do.
I also tried to create multiple virtual graphic "plates" that lay on top of one another. That was really slow though, even with POKE. Unless there's some way to transfer a STRING or BYTE array directly to the screen and back again.
Felice, will try to apply your methods this in the morning.
See you in the morning. :)
@Felice: thank you, I always learn something from your comments, doesn't matter the thread.
@dw817: the only optimization I found was to avoid (some) per pixel color checks, but didn't know it was a must have feature. Drawing this way is a bit slow, I'm afraid.
It's just something interesting I wanted to do, SB. To be able to create some explosions and stuff that only affected colors I chose and the remaining background was untouched.
I'm going to try and repost this today to use naught but array and that ADD() DEL() stuff. Scan for the circle. Will let you know.
Yes, I've managed to wind up creating all kinds of crazy functions in different programming languages.
I just wish the string-handling had a deeper library (UPPER$(), LOWER$(), FirstCap$(), LEFT$(), RIGHT$(), MID$(), INSTR$(), RINSTR$(), Replace$(), ReplaceFirst$(), ReplaceLast$(), STRING$(), LSET$(), RSET$(), FNPad$(), ZeroPad$(), VarGet$(), VarSet$(), VarClr$(), CHR$(), ASC(), VAL(), STR$(), Hex$(), Oct$(), Bin$(), Char$(), First$(), Last$(), Del$(), Reverse$(), Yank$()) SaveString$(), LoadString$() DirToString$(), Sort$()) Command$()), but that's fine.
I'll get there eventually. :)
Alright ! Here we have it, the very latest in the Fireball saga.
I simply must mention the hurdles I was facing last afternoon and evening whittling away on this thing.
At first I thought, well, okay, we want the yellow circle, but not to interfere with the text. So I wrote a routine that anytime you changed the circle (or was initialized), that a pointer array would record each dot for the circle (at its set size), and record all those dots on the screen first before I plotted the true circle.
That was one idea.
The other was the fire itself. I really struggled with trying to use a pointer array to keep track of every single pixel beneath before they were plotted.
The problem with that is, it didn't always work as one pixel location chosen could definitely match another's and they would "fight" not giving me back the screen contents beneath properly.
I was then considering SkyBerron's unique idea of using the Tile space to record the screen temporarily so as not to lose anything.
Now this would be good for games I did years ago on the TRS-80. Back then his method was a tiny machine-language routine I wrote in Z80 called LDIR.
It was super fast, it definitely recorded the screen to a chunk of memory I didn't need, and then back again.
The problem HERE is that I want my tiles and the only free space to record a screen in PICO (at this time) is the tile space. And I want to be able to record the screen while I plot tiles and then recover the screen once done.
And using SkyBerron's method, I could not plot any tiles - not the normal way anyways. Oh, I suppose I could at the beginning save the tiles to a DIFFERENT array, perhaps a pointer array, and then manually plot one dot at a time for them.
But no, there had to be an easier way. I truly was burning some midnight oil here thinking about this. I was feeling a little like the very fireball that eluded me.
Then I realized, well, pretty much some spoken dialogue from Star Trek VI.
At the time Kirk and company were being attacked by a cloaked Klingon ship. Kirk was up in arms unable to figure out how to shoot at an invisible target.
Spock realized their enemy ship ran on ionized gas, and if so, with the current onboard equipment to catalogue gaseous anomolies ... well, wasn't this actually quite similar to what I was wanting to do ?
To somehow track gaseous anomolies, in this case, the burst of fire ? To which Lieutenant Uhura said:
"Well the thing's gotta have a tailpipe."
Which cinched it for me. I didn't have to keep track of every single particle, beautiful though that might've been. No, I just needed to keep track of where it ... wasn't.
And the sparks would never be on color 0. That's how the sparks died out. They started at 5, then to 4, 3, 2, 1, and finally 0. Beautiful to watch, but maddening to try and track their every chaotic movement.
So all I really needed to do was make a plot routine that would ONLY plot what was beneath if and ONLY if the color I was plotting was zero.
And that did it. Very fast coding considering, and smart fire at that. I didn't even need to keep track of and replace all the pixels for the plotted yellow ball. The incendiary code would do it for me, automatically. :)
So, here is MARK III, Smart Fireball. Thanks with a little effort to Lieutenant Uhura from Star Trek.
[Please log in to post a comment]