Jump to content

Cybearg

Members
  • Posts

    951
  • Joined

  • Last visited

Everything posted by Cybearg

  1. Well, by that logic, why develop for a long-past console like the 2600 at all when you could develop for a modern system? It's all about the challenge of working within limited resources, and that's a big part of the fun in developing for retro consoles.
  2. Sorry for the mess-ups and gaffes. Just trying to help! :I I know that there's player 0 and player 1 sprites there. Then you've got the monsters and one or two other enemies that go across the screen, so as you say, it makes sense that you're short. I'd definitely say split the one group of four into two groups of two if you can, especially if they all have a shared hitbox. It's almost impossible to miss them since the missiles go right through and hit the ones behind them, but if you have two enemies that move separately, the player has to strategize as to which one to hit first. This is especially true since the risk isn't so much to the player getting hit, since there's infinite respawn so long as there's a player 1 just chilling at the bottom of the screen, so any way you can make it more challenging for the player to get all enemies destroyed before getting past the player is a good thing (within reason of course). And it's no problem, stephena! I didn't realize that there were developers around, and it was entirely from my own ignorance, anyway, so don't worry on it.
  3. Binary array won't work because the values have to change based on whether or not the player has been over them, methinks. I don't see how pfcolors will work like theloon suggests because you need the colors (black and white) to do the actual gameplay. I think that the only way is using RAM SuperChip. It may be a bit complicated programming wise and may gobble up a lot of cycles, so there may be a better way of doing this, but I think that you need to, as I say above, set aside 40 varibles and bit operations to account for whether a pfpixel is 0 (not touched) or 1 (touched). If the player moved the red cursor to a pfpixel, it'd flip the bit to on and the player wouldn't be allowed to move a cursor onto a pfpixel location that had a bit that was flipped on. After the player was finished, they'd hit the trigger and every pfpixel that had a bit flipped to 1 would then reverse itself, either turning on if it was off or off if it was on. Does that make sense?
  4. I guess it's just a question of where to put the drawKernel subroutine in the .asm file, then. The .bas file is attached and it compiles just fine, it's just a question of where the .drawKernel is located relative to everything else. If it's at the start of the .asm file, when the game runs, it just gets a black screen after the title screen, then kicks back to the title screen again. If it's right before drawKernel:, there is a mess of drawn pfpixel blocks at the top of the screen which, when a shot is fired, sometimes are hit and count as collision and all, but it's clearly not as it's meant to be. If I call it later, after the DrawGap part in the .asm file, no blocks get drawn, but the heart is still there and working. That is, of course, assuming that I'm meant to chop out everything under COLOR FLICKER entirely and simply call the assembly subroutine. Wish I had a copy of your working demo's .asm file. Then I could kind of see how the parts work together. heartkernel.asm heartbreak.bas
  5. Changed the 2 to a 1. When you say ... Do you mean jump into the subroutine? Because the compiler doesn't recognize DrawKernel as a valid subroutine since it's not defined outside of the asm ... end code. Gah, sorry for the annoyance of all the questions for simple stuff. EDIT: Oh, wait... Just looked at this, which explains how this sort of thing is supposed to work. From what I can tell, the .nameHere parts of Assembly are the actual subroutines, not the big DrawKernel: stuff, so I need to call one of those .nameHere parts. So which subroutine is meant to be called to begin the whole draw process? I see one for .storeShot (do I need to call that when the ball is generated?) .storeblock 1-16 (which I assume I need to call for each of the blocks being generated?) and then things like .loopThreeBlocks and .loopTwoCloseBlocks, etc., which I'm not sure how they would be called correctly. Also, I included the .asm file at the end of everything rather than the beginning, like so: rem end loop 800 goto draw_loop asm include "heartkern.asm" end
  6. Well, instead of trying to include it with a .inc file, I tried just dropping it in asm ... end before everything else. It compiles with 94 bytes to spare on the ROM, which is great! ... Though nothing seems to happen. That may be because I... need to go back and delete out all the stuff about the flickering? From what you said, the kernel takes the color values directly from the memory addresses of the first sixteen variables, though keep in mind that those variables always have some value. If a block is dead, the color "2" is written to it. Why not black? Because there are black blocks in later levels and I needed a way to distinguish a black block from a dead block. That wasn't the case in the version you made the kernel for. If it draws the pfpixels directly through the kernel without the aid of the color flicker code: rem ============== rem COLOR FLICKER rem ============== 100 rem first, set levelcolor[level] into another variable for bitwise operations temp4 = levelcolors[level] 105 rem increment flicker flicker = flicker + 1 & 7 rem determines if a color is being used if setbits[flicker] & temp4 then goto 110 goto 105 data screen_add 2, 2, 7, 15, 23, 31, 39, 42, 42, 41, 37, 28, 20, 12, 5, 1 end data block_bit 128, 8, 1, 8, 16, 8, 1, 8, 128, 16, 1, 4, 8, 4, 1, 16 end data setbits 1, 2, 4, 8, 10, 32, 64, 128 end 110 rem cycle through pfpixels, turning them on if they are equal to the color being drawn temp6 = base for temp5 = 0 to 15 if a[temp6] = blkc[flicker] then temp2 = screen_add[temp5] : $A4[temp2] = $A4[temp2] | block_bit[temp5] temp6 = temp6 + 1 & 15 next rem set playfield to current color COLUPF = blkc[flicker] ... Which I only partially understand since bogax's brilliant reworking, will it still be able to account for that kind of thing? Currently, I just get a black screen, but I'm likely doing something wrong or maybe the COLOR FLICKER code needs to be removed in order to get it to all work together. heartbreak.bas
  7. Thank you SO MUCH! That demo looks great! This asm file is meant to be either included directly (as you say above) or chopped into pieces and added to batariBasic in asm statements, correct? It would be very helpful if it wasn't necessary to search for and replace/add to stuff on every new bB iteration. What kind of set-up for the colors is necessary with the kernel? Since it doesn't use flickering, all those flicker statements should be removed, I assume, but then what would the structure of the color calls look like? Or does this assembly code replace that part? Is there any risk of me accidentally using register $FA? Is it something that I'm liable to make be not equal to 0 or are you just informing me about that? Also, can the scope of this kernel extend beyond the limits of Heartbreak in particular? What is its actual full potential and what all can be done with it? EDIT: I have no experience with this and can't truly read assembly, though I sorta-kinda-maybe get the gist of what that new kernel is doing, kinda? Anyway, since I don't know how a new kernel like this is supposed to join together with the existing stuff, I tried making a tweaked includes file and added this line to the start of heartbreak.bas: includesfile heartbreak.inc The includes file is just the standard include with a mention of heartkern.asm thrown in there after all the standard kernels are loaded. That's apparently wrong, since heartbreak.bas won't compile through bB due to: heartbreak.bas.asm (421): error: Unknown Mnemonic 'rtsstart'. I hate to bother Omega with having to explain all this to a complete newbie, so if anyone else can clue me in as to how these puzzle pieces fit together, that'd be much appreciated. Since I can't upload the .inc file type, this is what heartbreak.inc looks like: ; ; Inclues go below - order is crucial, since this is also the order in which ; they will appear in the generated assembly file ; ; header file 2600basicheader.asm ; standard kernel: two players, two missiles, a ball and an asymmetric playfield. std_kernel.asm ; standard startup routine. startup.asm ; below are collections of subroutines and functions ; if you have any more to add, put them immediately below this line. pf_drawing.asm pf_scrolling.asm std_routines.asm heartkern.asm ; The overscan routine goes below. it sets up sprites for the std_kernel. ; if you have any routines that will not run until the overscan period, ; put them immediately below this line. std_overscan.asm ; below is the generated batari Basic file bB.asm ; score graphics. score_graphics.asm ; below is the footer, which contains the score digits and startup vectors. ; If you want to create your own custom score digits, you may hack the file below, ; but first you should rename it to something else. 2600basicfooter.asm heartbreak.bas heartkern.asm
  8. No offense! I meant it in the sense that, out of the box, I saw rolling and other things demonstrating the problems inherent with a real 2600 in z26 but not in Stella. Awesome that there are ways to replicate it (and no, I don't like z26's colors more), and no offense is intended. That was just a side-note to explain that I was assuming that's what the game would look like on a TV, though it may not.
  9. I had planned to use rand16 but simply ran out of variables to do it with. Glad to see that there are some sneaky variables that I can rope into helping me out.
  10. Tried the version at the top. At first, I was confused about what was going on. I know it's still a work in progress, but I think that sound effects to demonstrate if hits are doing damage (since otherwise the foursome of monsters just kind of wiggle when hit like I tickled them). I played in z26 (which seems like it's more accurate at replicating the way a real 2600 looks compared to Stella) and everything was very flickery from what I saw. The life bar, the buildings, the monsters, and the planes were all flickering colors and the planes were flickering between being a block and a rectangle. I thought either this was intended or some rare occurrence until I tried recording it with FRAPS then watched the footage. Things look fine in after the first few seconds, but the entire game looked like it does in those first few seconds for me, with everything flickering. Also, the score seems to reset and repeat, which I assume wasn't intended.http://youtu.be/PhVRcm10XMY Overall, I think it's neat! I like the little car-blocks moving at the bottom and the buildings scrolling past and it's a great use of pfheights, methinks. I have a couple suggestions. 1. You were probably going to do this anyway, but I'd say break off those groups of four monsters so that the monsters move individually. They can come at the player from different angles/patterns, creating pinch maneuvers that forces the player to get out of the way, etc. On a similar note, maybe (depending on if this would playtest well or not), make colliding with the buildings below to be a bad thing, allowing the player to dip between the buildings here and there but forcing the player to move back up into the more dangerous upper region. That would keep the player moving up and down. 2. I found that I played always on the far left side of the screen and never really moved right, even though I could. I'd recommend doing something to force the player out of the far left of the screen, such as something dropping down from above to drive the player right, or maybe the monsters could move in pinch maneuvers, as mentioned above, which connect at the left, forcing the player to move right to avoid being hit. Also, some kind of boss after a while would be neat as well.
  11. I had the same bug as Pigglets happen at my first try. First off, big kudos! My favorite part of this? The sound effects. The humming of the plane and the way it shifts as you accelerate/decelerate and the rumble of the guns is enormously satisfying and pleasant to hear, which is surprising for a system that can be infamous for its annoying bleepy-bloopyness. It took me a little bit to realize that the thing on the right was a radar screen, but it was a neat idea, though I'm not sure if I see how it was implemented here. Now, the bomber would flicker around and come in and out of view, but that seemed more like a bug (and one that got me killed a number of times when it disappeared and then dropped into me without my realizing), so it would be nice to have that fixed. Note: I played this on z26. Also, depending on whether you anticipate it to be played primarily on emulators or mostly on real 2600s, I'd suggest adding in a restart when the player fires on the KIA screen. You probably have it tied to the reset switch or something, though, so maybe it's just me who wants that. Overall very neat! Just a bug or two that can really mess one up, but fun and, as I said, fantastic sound effects. Flying and gunning is oh-so rewarding. I'd like to see more types of enemies and planes and maybe, as Piggles suggested, some kind of boss? While I like the idea of the radar screen, assuming that the bomber flickering in and out is a bug and not intentional, it's not really utilized in the gameplay at all, since it only displays what I can already see. Unless it gets used for a particular gameplay reason that really relies on it, I'd suggest going multisprite so that there are (at least at later stages) more enemies to dogfight with and more room to move around. Just my two cents.
  12. Does it need to be a specific space? Is there any visual cue to indicate to the player which of those it would be? Or is the player flipping between whether they can move over black or white? It seemed fully functional to me with the ability to move back and forth as-is. What does the flipping do? Could you post a sample video of the DS game or something demonstrating how it should look? If you need to remember specifically which pfpixel was touched before without anything to work off of (such as the ability to check whether a pfpixel is active or not), the only way I can see doing it is by setting aside a series of bitmapped variables to represent the "okay" and "not-okay" movement states of the 4x8 playfield blocks. This would, of course, require 4 x 10 = 40 variables, which aren't normally available but may be doable with a superchip. EDIT: No, silly me, you could just affect the direct variables that already represent the on/off states of the playfield, right? Well, that is, unless there needs to be no visual distinction as to whether or not you can move in a certain place, but this would be a simple way to affect the playfield.
  13. It feels good to make it work in the smallest ROM possible. At least for me it does.
  14. I don't see what makes them so smelly. Neat, though! Yeah, I can certainly see why you'd think of it. And I'll add that rand16 in there--I know that I seemed to roll up the same direction multiple times quite often, it seemed. I've attached the latest version (not that there have been a whole lot of changes--mostly just a few tweaks here and there with some additions and suggestions). heartbreak.bas heartbreak.bas.bin
  15. Niiiice. I loves me some extra variables! These function just like any other but I guess the names are set at var 44/45/46/47?
  16. Ahem... Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! Thank you! My appreciation knows no bounds! No rush, but I'm massively curious, so if you do make progress, please do pop in and say a thing or two about how it's going. Again, thanks much! I was starting to panic that no one would be willing/able to do it.
  17. That's awesome. Is there some way to utilize that to help with the whole flickering problem? I've made a couple additional little tweaks that have hopefully streamlined things here and there. It's hard to imagine, but it's actually running smoothly at all times, so far as I can tell. heartbreak.bas.bin heartbreak.bas
  18. Wow, thanks a lot! That works fantastically! ... I have to say I don't understand it, though. It's drawing pfpixels without referencing the pfpixel locations data set I made before and without even saying "pfpixel." How is that?
  19. Does "if pulse then goto 10" have any actual cycle advantage over "if pulse <> 0 then goto 10"? Because if not, I'd like to keep things as unobtuse as possible. I'd be able to save even more cycles here: if flicker = 0 && temp4{0} then goto 110 if flicker = 1 && temp4{1} then goto 110 if flicker = 2 && temp4{2} then goto 110 if flicker = 3 && temp4{3} then goto 110 if flicker = 4 && temp4{4} then goto 110 if flicker = 5 && temp4{5} then goto 110 if flicker = 6 && temp4{6} then goto 110 if flicker = 7 && temp4{7} then goto 110 and here: if temp4{0} && temp2 >= colorodds[temp3] then a[temp1] = blkc[0]: temp3 = temp3 + 1 if temp4{1} && temp2 >= colorodds[temp3] then a[temp1] = blkc[1]: temp3 = temp3 + 1 if temp4{2} && temp2 >= colorodds[temp3] then a[temp1] = blkc[2]: temp3 = temp3 + 1 if temp4{3} && temp2 >= colorodds[temp3] then a[temp1] = blkc[3]: temp3 = temp3 + 1 if temp4{4} && temp2 >= colorodds[temp3] then a[temp1] = blkc[4]: temp3 = temp3 + 1 if temp4{5} && temp2 >= colorodds[temp3] then a[temp1] = blkc[5]: temp3 = temp3 + 1 if temp4{6} && temp2 >= colorodds[temp3] then a[temp1] = blkc[6]: temp3 = temp3 + 1 if temp4{7} && temp2 >= colorodds[temp3] then a[temp1] = blkc[7]: blkcount = blkcount - 1 ... if you know of a way for me to refer to a bit operation with a variable, such as: if temp4{temp3} && temp2 >= colorodds[temp3] then a[temp1] = blkc[temp3]: temp3 = temp3 + 1 Anyway, there are probably ways for me to improve my code, but I have as many weeks of programming experience as you can count on one bird-flip, so it's unlikely that I'm going to catch everything.
  20. Ahh, I heard about those switch constants but didn't know how they work. Still don't, heh. if pulse then goto 10 What does this do? Is that a simplified check to see if it's above 0? Anyway, I did try having the heart beat check not looked at every cycle, but the downside of that is that changes, such as losing a heart from losing 3 lives or getting a new level generated, take a moment to show an effect on the heart, so there's a lagged reaction. Plus, it doesn't actually save me any cycles. I tried and it's 272 before and after the change. According to z26, the flicker resulting from that 272 scanline count really isn't so bad, though it may get worse (or better?) if I get that kernel I've been waiting for (hint hint hint).
  21. Every 45 cycles. That's what causes the heart to beat and the colors to change, so it's necessary to constantly be watching to make sure that the colors don't change too early. I've managed to simplify it a little by reducing some of the use of cooldown and simplifying a bit there. I'm still going over-budget, though: 1. During level generation, frames hit 263 and 264 scanlines. 2. Once the level has been generated, scanlines remain steady at around 272 (at level 1--the fewer of the same color there is on-screen, the fewer the scanline excess). Successfully hitting blocks removes scanlines, about 1 a piece, though I really can't see any code that can be trimmed out for those things. heartbreak.bas.bin heartbreak.bas
  22. Wish I'd have known about this before now. I've used cyclescore in the past and, while it flashed red here and there, it was always over 0, so I figured I was fine. The biggest drop happens when generating a new level, so I went back and tried to spread the generation across sixteen frames instead of all in one, but I still seem to be getting constant scanlines of 272, plus the block at twelve o'clock is often missing until you move the joystick left or right. What is sucking up all the cycles, how can I streamline it further, and why is the block at 0 not rendering out immediately at level generation? (that's not rhetorical--I'm asking for help with this) rem rem ********************************** rem *heartbreak.bin * rem *A re-imagning of breakout with a lonely heart trapped in a void... * rem *Cybearg (aka Nic) * rem *cybearg.plays@gmail.com * rem ********************************** rem ============== rem INITIALIZATION rem ============== rem set up settings/optimization set optimization speed rem set up my variables dim base = q dim blkcount = r dim level = s dim cooldown=t dim anglevar=u dim pulse=v dim flicker=w dim offset=x dim ballcolor=y dim heartcolor=z rem set offset to a number greater than 0 to allow for "negative" values offset = 50 rem set heartcolor to begin at red heartcolor = blkc[0] rem set player 0's location player0x=80:player0y=48 rem set base blockcount blkcount = 16 rem ========== rem MAIN LOOP rem ========== draw_loop rem clear the playfield pfclear rem increment the multi-purpose cooldown variable cooldown = cooldown + 1 rem mute sound at 7 cooldown cycles or after 20 cycles if there's been a titlescreen restart if cooldown = 7 || cooldown = 70 then AUDV0 = 0 : AUDV1 = 0 rem exit for stage generation if pulse > 100 then goto 775 rem =============== rem BEATING HEART rem =============== rem increment pulse counter pulse = pulse +1 rem cooldown to make sure that player doesn't leave the title screen by firing a ball if pulse = 45 && cooldown >= 50 then cooldown = 0 rem after 45 cycles, increment color if pulse = 45 then pulse = 0 rem set base player sprite player0: %00000000 %00000000 %00000000 %00000000 %00000000 %00000000 %00000000 %00000000 end rem if the player has zero lives, skip this part if lives = 0 then goto 410 rem set up possible colors data blkc 66, 28, 150, 46, 214, 102, 14, 00 end rem special occasion for first level if level = 0 then heartcolor = blkc[0]: goto 10 rem special occasion for second level if level = 1 && heartcolor = blkc[0] && pulse = 0 then heartcolor = blkc[1]: goto 10 if level = 1 && heartcolor = blkc[1] && pulse = 0 then heartcolor = blkc[0]: goto 10 rem increment the colors if heartcolor = blkc[1] && pulse = 0 then heartcolor = blkc[2]: goto 10 if heartcolor = blkc[0] && pulse = 0 then heartcolor = blkc[1]: goto 10 if heartcolor = blkc[2] && pulse = 0 then heartcolor = blkc[0] 10 rem set the playfield colors COLUP0 = heartcolor rem turn the score red if player is on the last life if lives = 1 then scorecolor = 64 else scorecolor = 10 rem swap the heart sprite for 5 cycles to create illusion of beating if pulse < 40 then goto 40 else goto 44 rem values for the up-beat (40 out of 45 cycles) 40 if lives > 6 then goto 50 41 if lives > 3 then goto 60 42 if lives > 0 then goto 70 rem values for the down-beat (5 out of 45 cycles) 44 if lives > 6 then goto 60 45 if lives > 3 then goto 70 46 if lives > 0 then goto 80 rem the various player 0 heart sprites 50 player0: %00010000 %00111000 %01111100 %11111110 %11111110 %11111110 %01101100 %00000000 end 55 goto 100 60 player0: %00000000 %00010000 %00111000 %01111100 %01111100 %00101000 %00000000 %00000000 end 65 goto 100 70 player0: %00000000 %00000000 %00010000 %00111000 %00101000 %00000000 %00000000 %00000000 end 80 rem ============== rem COLOR FLICKER rem ============== 100 rem set up possible pfpixel locations data ctbl 16, 20, 24, 27, 28, 27, 24, 20, 16, 12, 8, 5, 4, 5, 8, 12 end data rtbl 0, 0, 1, 3, 5, 7, 9, 10, 10, 10, 9, 7, 5, 3, 1, 0 end rem first, set levelcolor[level] into another variable for bitwise operations temp4 = levelcolors[level] 105 rem reset flicker if flicker = 8 then flicker = 0 rem determines if a color is being used if flicker = 0 && temp4{0} then goto 110 if flicker = 1 && temp4{1} then goto 110 if flicker = 2 && temp4{2} then goto 110 if flicker = 3 && temp4{3} then goto 110 if flicker = 4 && temp4{4} then goto 110 if flicker = 5 && temp4{5} then goto 110 if flicker = 6 && temp4{6} then goto 110 if flicker = 7 && temp4{7} then goto 110 rem if not, skip it and try the next color flicker = flicker + 1 goto 105 110 rem cycle through pfpixels, turning them on if they are equal to the color being drawn temp6 = base for temp5 = 0 to 15 if a[temp6] = blkc[flicker] then pfpixel ctbl[temp5] rtbl[temp5] on temp6 = temp6 + 1 & 15 next rem set playfield to current color COLUPF = blkc[flicker] flicker = flicker + 1 rem ================== rem MAIN DRAWSCREEN rem ================== rem main drawscreen drawscreen rem skip to COLOR MOVEMENT goto 415 rem ============= rem TITLE SCREEN rem ============= 410 rem set the beat of the title screen heart to the ongoing pulse if pulse < 40 then goto 413 else goto 412 rem the two possible title screen frames 412 playfield: ................................ ...X.X.XXX..X..XX.XXX........... ...X.X.X...X.X.X.X.X............ ...XXX.XX..XXX.XX..X............ ...X.X.XXX.X.X.X.X.X............ .............................. ...........XX..XX..XXX..X..X.X.. ....X.X....X.X.X.X.X...X.X.X.X.. ....XXX....XX..XX..XX..XXX.XX... .....X.....X.X.X.X.XXX.X.X.X.X.. ...........XXX.................. end rem skip to color goto 414 413 playfield: ................................ ...X.X.XXX..X..XX.XXX........... ...X.X.X...X.X.X.X.X............ ...XXX.XX..XXX.XX..X............ ...X.X.XXX.X.X.X.X.X............ ................................ ...XX.XX...XX..XX..XXX..X..X.X.. ..XXXXXXX..X.X.X.X.X...X.X.X.X.. ...XXXXX...XX..XX..XX..XXX.XX... ....XXX....X.X.X.X.XXX.X.X.X.X.. .....X.....XXX.................. end 414 rem color the title screen playfield COLUPF = blkc[0] rem drawscreen for titlescreen only drawscreen rem skip everything else and wait for a trigger goto 760 rem ================= rem COLOR MOVEMENT rem ================= 415 rem NOTE: offset base is 50, so the numbers below determine how sensitive the controls are rem determine block offset if joy0left && offset < 55 then offset = offset + 1 if joy0right && offset > 45 then offset = offset - 1 rem adjust block offset 420 if offset = 55 goto 435 if offset = 45 goto 445 rem if no adjustments are necessary, skip this part goto 500 rem rotate left 435 base = base + 1 & 15 goto 450 rem move all blocks to the right (clockwise) 445 base = base - 1 & 15 rem reset the offset 450 offset = 50 rem ============ rem PLAYER FIRE rem ============ 500 rem checks to see that a few cool-down measures are a go, then moves the ball (player 1) to the center of the heart (player 0) rem cooldown ensures that the player cannot fire while a ball is at play rem cooldown also ensures the player doesn't exit a title screen by firing a ball prematurely if joy0fire && lives > 0 && player1x = 0 && player1y = 0 && cooldown < 50 then player1x=80:player1y=48 else goto 600 rem moves the ball's current color from heartcolor (set in the BEATING HEART section) to ballcolor for safe-keeping ballcolor = heartcolor rem set player 1's sprite player1: %00000000 %00000000 %00000000 %00011000 %00011000 %00000000 %00000000 %00000000 end rem generate random number to determine ball movement rem variables ensure that ball speed/angle does not vary once ball has been generated anglevar = (rand/32) rem ========= rem THE BALL rem ========= 600 rem skip everything dealing with the ball if it is at its resting position (0,0) if player1x = 0 && player1y = 0 then goto 800 rem maintain the color of the ball at the moment of firing COLUP1 = ballcolor rem all ball movement possibilities rem allows for ball movement in a direction every 45 degrees 650 rem cooldown is used to manage the speed of the ball if cooldown > 8 then cooldown = 0 rem x motion table data xmtbl 50, 51, 51, 51, 50, 49, 49, 49 end rem y motion table data ymtbl 51, 51, 50, 49, 49, 49, 50, 51 end rem separate out cardinal from diagonal directions if anglevar <> 0 && anglevar <> 2 && anglevar <> 4 && anglevar <> 6 then goto 670 else goto 680 670 rem movements for diagonal directions (45, 125, 225, 315) -- applies a cooldown to ensure that diagonals do not move too fast if cooldown <> 1 && cooldown <> 3 && cooldown <> 5 && cooldown <> 7 then player1x = player1x + xmtbl[anglevar] - 50: player1y = player1y + ymtbl[anglevar] - 50: goto 700 else goto 700 680 rem movement values for cardinal (0, 90, 180, 270 degrees) directions -- applies a cooldown to ensure that cardinal directions do not move too fast if cooldown <> 3 && cooldown <> 7 then player1x = player1x + xmtbl[anglevar] - 50 if cooldown <> 3 && cooldown <> 7 then player1y = player1y + ymtbl[anglevar] - 50 rem ==================== rem COLLISION DETECTION rem ==================== 700 rem check to see if player 1 is out of bounds if player1x = 25 || player1y = 0 then goto 750 if player1x=133 || player1y = 140 then goto 750 rem check for collision -- if not, bypass everything else as we wait for a collision if !collision(playfield,player1) then goto 800 705 rem double anglevar in order to determine how the number relates to a 0-15 range rather than a 0-7 range temp4 = 4 - anglevar temp4 = temp4 + temp4 + base & 15 rem checks for various hit-types, currently only consists of a single possibility, but there will be more for secondary colors, white, and black rem if one of the primary colors if a[temp4] = ballcolor then goto good_hit rem if orange if a[temp4] = blkc[3] && ballcolor = blkc[0] then goto good_hit if a[temp4] = blkc[3] && ballcolor = blkc[1] then goto good_hit rem if green if a[temp4] = blkc[4] && ballcolor = blkc[1] then goto good_hit if a[temp4] = blkc[4] && ballcolor = blkc[2] then goto good_hit rem if purple if a[temp4] = blkc[5] && ballcolor = blkc[0] then goto good_hit if a[temp4] = blkc[5] && ballcolor = blkc[2] then goto good_hit rem if white if a[temp4] = blkc[6] && lives < 9 then lives = lives + 1: goto good_hit if a[temp4] = blkc[6] then goto good_hit rem in case there's a mistake and a dead block is counted as a hit, make sure that it doesn't cost the player a life if a[temp4] = 2 then goto 800 rem if the hit is bad... bad_hit rem lose a life lives = lives - 1 rem play bad hit sound AUDV1 = 6 AUDC1 = 6 AUDF1 = 16 goto 725 rem if the hit is good... good_hit rem set current block to dead a[temp4] = 2: blkcount = blkcount - 1 rem increment score score = score + 25 rem play good hit sound AUDV1 = 6 AUDC1 = 12 AUDF1 = 19 rem reset sound cooldown counter 725 if lives > 0 then cooldown = 0 else cooldown = 50: pulse = 0 750 rem reset player 1 location, unlocking player 0 fire player1x = 0: player1y = 0 rem ================== rem STAGE GENERATION rem ================== 760 rem exits title screen and generates a new stage if joy0fire && lives = 0 && cooldown < 50 then score = 0: pulse = 100: cooldown = 50: level = 0: blkcount = 16: goto 775 rem checks if all blocks have been destroyed if blkcount > 0 then goto 800 rem if so, then score bonus points for completing stage and continue if lives > 6 then score = score + 600: goto 770 if lives > 3 then score = score + 400: goto 770 if lives > 0 then score = score + 200 770 rem increment the level counter level = level + 1 if level = 14 then level = 6 pulse = 100: blkcount = 16 rem generates new stage by looping through and inserting new colors taken from data set under MAIN LOOP 775 rem set up data sets for level generation rem defines the odds of a color for each stage data colorodds 0, 0, 128, 0, 85, 170, 0, 42, 84, 126, 168, 210, 0, 80, 160, 240, 247, 0, 40, 80, 120, 160, 200, 240, 247, 0, 60, 120, 180, 200, 220, 240, 245, 0, 60, 120, 180, 195, 210, 225, 230 end rem saves space by indicating where the colorodds index should begin, per level data startindex 0, 1, 3, 3, 6, 1, 12, 12, 17, 17, 25, 25, 33, 33 end rem defines which color checks are in a stage data levelcolors %00000001, %00000011, %00000111, %00111000, %00111111, %11000000, %11000111, %11000111, %11111111, %11111111, %11111111, %11111111, %11111111, %11111111 end rem determine what the level's index is temp3 = startindex[level] temp4 = levelcolors[level] rem begin the actual generation temp1 = pulse - 100 780 rem generate a random number temp2 = rand rem determine what the level's index is temp3 = startindex[level] rem if a color is in a level and its number has been rolled, set a pfpixel to its color, then increment the index if temp4{0} && temp2 >= colorodds[temp3] then a[temp1] = blkc[0]: temp3 = temp3 + 1 if temp4{1} && temp2 >= colorodds[temp3] then a[temp1] = blkc[1]: temp3 = temp3 + 1 if temp4{2} && temp2 >= colorodds[temp3] then a[temp1] = blkc[2]: temp3 = temp3 + 1 if temp4{3} && temp2 >= colorodds[temp3] then a[temp1] = blkc[3]: temp3 = temp3 + 1 if temp4{4} && temp2 >= colorodds[temp3] then a[temp1] = blkc[4]: temp3 = temp3 + 1 if temp4{5} && temp2 >= colorodds[temp3] then a[temp1] = blkc[5]: temp3 = temp3 + 1 if temp4{6} && temp2 >= colorodds[temp3] then a[temp1] = blkc[6]: temp3 = temp3 + 1 if temp4{7} && temp2 >= colorodds[temp3] then a[temp1] = blkc[7]: blkcount = blkcount - 1 785 rem complete reset by giving full lives and reset if pulse = 116 then lives = 9: flicker = 0: pulse = 0 else pulse = pulse + 1 rem end loop 800 goto draw_loop heartbreak.bas heartbreak.bas.bin
  23. So going over scanlines (that is, having too many lines rendered on-screen) means that there are too many cycles being used? Well, what do I do to fix it?
  24. Alright. The game is essentially done. I buckled down and wrote out all the levels and a way for the flicker area to only display the colors that are relevant for a certain level. Basically now I'm just waiting for someone to volunteer to do that kernel so I can get rid of this flickering. rem rem ********************************** rem *heartbreak.bin * rem *A re-imagning of breakout with a lonely heart trapped in a void... * rem *Cybearg (aka Nic) * rem *cybearg.plays@gmail.com * rem ********************************** rem ============== rem INITIALIZATION rem ============== rem set up settings/optimization set optimization speed rem set up my variables dim base = q dim blkcount = r dim level = s dim cooldown=t dim anglevar=u dim pulse=v dim flicker=w dim offset=x dim ballcolor=y dim heartcolor=z rem set offset to a number greater than 0 to allow for "negative" values offset = 50 rem set heartcolor to begin at red heartcolor = blkc[0] rem set player 0's location player0x=80:player0y=48 rem set base blockcount blkcount = 16 rem ========== rem MAIN LOOP rem ========== draw_loop rem clear the playfield pfclear rem increment the multi-purpose cooldown variable cooldown = cooldown + 1 rem mute sound at 7 cooldown cycles or after 20 cycles if there's been a titlescreen restart if cooldown = 7 || cooldown = 70 then AUDV0 = 0 : AUDV1 = 0 rem =============== rem BEATING HEART rem =============== rem increment pulse counter pulse = pulse +1 rem cooldown to make sure that player doesn't leave the title screen by firing a ball if pulse = 45 && cooldown >= 50 then cooldown = 0 rem after 45 cycles, increment color if pulse = 45 then pulse = 0 rem set base player sprite player0: %00000000 %00000000 %00000000 %00000000 %00000000 %00000000 %00000000 %00000000 end rem if the player has zero lives, skip this part if lives = 0 then goto 410 rem set up possible colors data blkc 66, 28, 150, 46, 214, 102, 14, 00 end rem special occasion for first level if level = 0 then heartcolor = blkc[0]: goto 10 rem special occasion for second level if level = 1 && heartcolor = blkc[0] && pulse = 0 then heartcolor = blkc[1]: goto 10 if level = 1 && heartcolor = blkc[1] && pulse = 0 then heartcolor = blkc[0]: goto 10 rem increment the colors if heartcolor = blkc[1] && pulse = 0 then heartcolor = blkc[2]: goto 10 if heartcolor = blkc[0] && pulse = 0 then heartcolor = blkc[1]: goto 10 if heartcolor = blkc[2] && pulse = 0 then heartcolor = blkc[0] 10 rem set the playfield colors COLUP0 = heartcolor rem turn the score red if player is on the last life if lives = 1 then scorecolor = 64 else scorecolor = 10 rem swap the heart sprite for 5 cycles to create illusion of beating if pulse < 40 then goto 40 else goto 44 rem values for the up-beat (40 out of 45 cycles) 40 if lives > 6 then goto 50 41 if lives > 3 then goto 60 42 if lives > 0 then goto 70 rem values for the down-beat (5 out of 45 cycles) 44 if lives > 6 then goto 60 45 if lives > 3 then goto 70 46 if lives > 0 then goto 80 rem the various player 0 heart sprites 50 player0: %00010000 %00111000 %01111100 %11111110 %11111110 %11111110 %01101100 %00000000 end 55 goto 100 60 player0: %00000000 %00010000 %00111000 %01111100 %01111100 %00101000 %00000000 %00000000 end 65 goto 100 70 player0: %00000000 %00000000 %00010000 %00111000 %00101000 %00000000 %00000000 %00000000 end 80 rem ============== rem COLOR FLICKER rem ============== 100 rem set up possible pfpixel locations data ctbl 16, 20, 24, 27, 28, 27, 24, 20, 16, 12, 8, 5, 4, 5, 8, 12 end data rtbl 0, 0, 1, 3, 5, 7, 9, 10, 10, 10, 9, 7, 5, 3, 1, 0 end rem first, set levelcolor[level] into another variable for bitwise operations temp4 = levelcolors[level] 105 rem reset flicker if flicker = 8 then flicker = 0 rem determines if a color is being used if flicker = 0 && temp4{0} then goto 110 if flicker = 1 && temp4{1} then goto 110 if flicker = 2 && temp4{2} then goto 110 if flicker = 3 && temp4{3} then goto 110 if flicker = 4 && temp4{4} then goto 110 if flicker = 5 && temp4{5} then goto 110 if flicker = 6 && temp4{6} then goto 110 if flicker = 7 && temp4{7} then goto 110 rem if not, skip it and try the next color flicker = flicker + 1 goto 105 110 rem cycle through pfpixels, turning them on if they are equal to the color being drawn temp6 = base for temp5 = 0 to 15 if a[temp6] = blkc[flicker] then pfpixel ctbl[temp5] rtbl[temp5] on temp6 = temp6 + 1 & 15 next rem set playfield to current color COLUPF = blkc[flicker] flicker = flicker + 1 rem ================== rem MAIN DRAWSCREEN rem ================== rem main drawscreen drawscreen rem skip to COLOR MOVEMENT goto 415 rem ============= rem TITLE SCREEN rem ============= 410 rem set the beat of the title screen heart to the ongoing pulse if pulse < 40 then goto 413 else goto 412 rem the two possible title screen frames 412 playfield: ................................ ...X.X.XXX..X..XX.XXX........... ...X.X.X...X.X.X.X.X............ ...XXX.XX..XXX.XX..X............ ...X.X.XXX.X.X.X.X.X............ .............................. ...........XX..XX..XXX..X..X.X.. ....X.X....X.X.X.X.X...X.X.X.X.. ....XXX....XX..XX..XX..XXX.XX... .....X.....X.X.X.X.XXX.X.X.X.X.. ...........XXX.................. end rem skip to color goto 414 413 playfield: ................................ ...X.X.XXX..X..XX.XXX........... ...X.X.X...X.X.X.X.X............ ...XXX.XX..XXX.XX..X............ ...X.X.XXX.X.X.X.X.X............ ................................ ...XX.XX...XX..XX..XXX..X..X.X.. ..XXXXXXX..X.X.X.X.X...X.X.X.X.. ...XXXXX...XX..XX..XX..XXX.XX... ....XXX....X.X.X.X.XXX.X.X.X.X.. .....X.....XXX.................. end 414 rem color the title screen playfield COLUPF = blkc[0] rem drawscreen for titlescreen only drawscreen rem skip everything else and wait for a trigger goto 760 rem ================= rem COLOR MOVEMENT rem ================= 415 rem NOTE: offset base is 50, so the numbers below determine how sensitive the controls are rem determine block offset if joy0left && offset < 55 then offset = offset + 1 if joy0right && offset > 45 then offset = offset - 1 rem adjust block offset 420 if offset = 55 goto 435 if offset = 45 goto 445 rem if no adjustments are necessary, skip this part goto 500 rem rotate left 435 base = base + 1 & 15 goto 450 rem move all blocks to the right (clockwise) 445 base = base - 1 & 15 rem reset the offset 450 offset = 50 rem ============ rem PLAYER FIRE rem ============ 500 rem checks to see that a few cool-down measures are a go, then moves the ball (player 1) to the center of the heart (player 0) rem cooldown ensures that the player cannot fire while a ball is at play rem cooldown also ensures the player doesn't exit a title screen by firing a ball prematurely if joy0fire && lives > 0 && player1x = 0 && player1y = 0 && cooldown < 50 then player1x=80:player1y=48 else goto 600 rem moves the ball's current color from heartcolor (set in the BEATING HEART section) to ballcolor for safe-keeping ballcolor = heartcolor rem set player 1's sprite player1: %00000000 %00000000 %00000000 %00011000 %00011000 %00000000 %00000000 %00000000 end rem generate random number to determine ball movement rem variables ensure that ball speed/angle does not vary once ball has been generated anglevar = (rand/32) rem ========= rem THE BALL rem ========= 600 rem skip everything dealing with the ball if it is at its resting position (0,0) if player1x = 0 && player1y = 0 then goto 800 rem maintain the color of the ball at the moment of firing COLUP1 = ballcolor rem all ball movement possibilities rem allows for ball movement in a direction every 45 degrees 650 rem cooldown is used to manage the speed of the ball if cooldown > 8 then cooldown = 0 rem x motion table data xmtbl 50, 51, 51, 51, 50, 49, 49, 49 end rem y motion table data ymtbl 51, 51, 50, 49, 49, 49, 50, 51 end rem separate out cardinal from diagonal directions if anglevar <> 0 && anglevar <> 2 && anglevar <> 4 && anglevar <> 6 then goto 670 else goto 680 670 rem movements for diagonal directions (45, 125, 225, 315) -- applies a cooldown to ensure that diagonals do not move too fast if cooldown <> 1 && cooldown <> 3 && cooldown <> 5 && cooldown <> 7 then player1x = player1x + xmtbl[anglevar] - 50: player1y = player1y + ymtbl[anglevar] - 50: goto 700 else goto 700 680 rem movement values for cardinal (0, 90, 180, 270 degrees) directions -- applies a cooldown to ensure that cardinal directions do not move too fast if cooldown <> 3 && cooldown <> 7 then player1x = player1x + xmtbl[anglevar] - 50 if cooldown <> 3 && cooldown <> 7 then player1y = player1y + ymtbl[anglevar] - 50 rem ==================== rem COLLISION DETECTION rem ==================== 700 rem check to see if player 1 is out of bounds if player1x = 25 || player1y = 0 then goto 750 if player1x=133 || player1y = 140 then goto 750 rem check for collision -- if not, bypass everything else as we wait for a collision if !collision(playfield,player1) then goto 800 705 rem double anglevar in order to determine how the number relates to a 0-15 range rather than a 0-7 range temp4 = 4 - anglevar temp4 = temp4 + temp4 + base & 15 rem checks for various hit-types, currently only consists of a single possibility, but there will be more for secondary colors, white, and black rem if one of the primary colors if a[temp4] = ballcolor then goto good_hit rem if orange if a[temp4] = blkc[3] && ballcolor = blkc[0] then goto good_hit if a[temp4] = blkc[3] && ballcolor = blkc[1] then goto good_hit rem if green if a[temp4] = blkc[4] && ballcolor = blkc[1] then goto good_hit if a[temp4] = blkc[4] && ballcolor = blkc[2] then goto good_hit rem if purple if a[temp4] = blkc[5] && ballcolor = blkc[0] then goto good_hit if a[temp4] = blkc[5] && ballcolor = blkc[2] then goto good_hit rem if white if a[temp4] = blkc[6] && lives < 9 then lives = lives + 1: goto good_hit if a[temp4] = blkc[6] then goto good_hit rem in case there's a mistake and a dead block is counted as a hit, make sure that it doesn't cost the player a life if a[temp4] = 2 then goto 800 rem if the hit is bad... bad_hit rem lose a life lives = lives - 1 rem play bad hit sound AUDV1 = 6 AUDC1 = 6 AUDF1 = 16 goto 725 rem if the hit is good... good_hit rem set current block to dead a[temp4] = 2: blkcount = blkcount - 1 rem increment score score = score + 25 rem play good hit sound AUDV1 = 6 AUDC1 = 12 AUDF1 = 19 rem reset sound cooldown counter 725 if lives > 0 then cooldown = 0 else cooldown = 50: pulse = 0 750 rem reset player 1 location, unlocking player 0 fire player1x = 0: player1y = 0 rem ================== rem STAGE GENERATION rem ================== 760 rem exits title screen and generates a new stage if joy0fire && lives = 0 && cooldown < 50 then score = 0: pulse = 0: cooldown = 50: level = 0: goto 775 rem checks if all blocks have been destroyed if blkcount > 0 then goto 800 rem if so, then score bonus points for completing stage and continue if lives > 6 then score = score + 600: goto 770 if lives > 3 then score = score + 400: goto 770 if lives > 0 then score = score + 200 770 rem increment the level counter level = level + 1 if level = 14 then level = 6 rem generates new stage by looping through and inserting new colors taken from data set under MAIN LOOP 775 rem set up data sets for level generation rem defines the odds of a color for each stage data colorodds 0, 0, 128, 0, 85, 170, 0, 42, 84, 126, 168, 210, 0, 80, 160, 240, 247, 0, 40, 80, 120, 160, 200, 240, 247, 0, 60, 120, 180, 200, 220, 240, 245, 0, 60, 120, 180, 195, 210, 225, 230 end rem saves space by indicating where the colorodds index should begin, per level data startindex 0, 1, 3, 3, 6, 1, 12, 12, 17, 17, 25, 25, 33, 33 end rem defines which color checks are in a stage data levelcolors %00000001, %00000011, %00000111, %00111000, %00111111, %11000000, %11000111, %11000111, %11111111, %11111111, %11111111, %11111111, %11111111, %11111111 end rem determine what the level's index is temp3 = startindex[level] temp4 = levelcolors[level] rem set blockcount to 16 now, because there may be reason to diminish it blkcount = 16 rem begin the actual generation temp1 = 0 780 if temp1 = 16 then goto 785 rem generate a random number temp2 = rand rem determine what the level's index is temp3 = startindex[level] rem if a color is in a level and its number has been rolled, set a pfpixel to its color, then increment the index if temp4{0} && temp2 >= colorodds[temp3] then a[temp1] = blkc[0]: temp3 = temp3 + 1 if temp4{1} && temp2 >= colorodds[temp3] then a[temp1] = blkc[1]: temp3 = temp3 + 1 if temp4{2} && temp2 >= colorodds[temp3] then a[temp1] = blkc[2]: temp3 = temp3 + 1 if temp4{3} && temp2 >= colorodds[temp3] then a[temp1] = blkc[3]: temp3 = temp3 + 1 if temp4{4} && temp2 >= colorodds[temp3] then a[temp1] = blkc[4]: temp3 = temp3 + 1 if temp4{5} && temp2 >= colorodds[temp3] then a[temp1] = blkc[5]: temp3 = temp3 + 1 if temp4{6} && temp2 >= colorodds[temp3] then a[temp1] = blkc[6]: temp3 = temp3 + 1 if temp4{7} && temp2 >= colorodds[temp3] then a[temp1] = blkc[7]: blkcount = blkcount - 1 temp1 = temp1 + 1: goto 780 785 rem complete reset by giving full lives and reset lives = 9: flicker = 0 rem end loop 800 goto draw_loop I've still got 867 bytes to spare on the ROM. I could go back and split a variable or two into nybbles, but I'll worry about that later. heartbreak.bas heartbreak.bas.bin
  25. I know how Hex and binary work; I just find counting in decimal to be more intuitive. Initially I had decided that because I didn't see any way to determine which block was being hit. Ranges of values were simpler there because I knew that the color corresponded to the indexed location of the block that was hit. Then theloon suggested that I use the direction of the ball's movement to determine which block was being hit, which allowed me to cut down my code from this: 700 rem check to see if player 1 is out of bounds if player1x = 25 || player1y = 0 then goto 750 if player1x=133 || player1y = 140 then goto 750 rem minimum collision data locations (x, y) data btbl 78, 4, 109, 12, 125, 44, 109, 76, 77, 84, 45, 76, 29, 44, 45, 12 end rem check each of the 8 possible collision locations for temp4 = 0 to 14 step 2 temp3 = temp4 + 1 rem set up temporary variables to contain the full range of the x and y potentials temp5 = btbl[temp4] + 4 temp6 = btbl[temp3] + 8 rem if player1x is within the range for the current block and player1y is within the range for the current block, there is a collision if player1x > btbl[temp4] && player1x < temp5 && player1y > btbl[temp3] && player1y < temp6 then goto 710 rem otherwise, check the next block next rem if no collision is detected, skip to the end of the main loop goto 800 rem check for collision type 710 rem check if the block is already dead if a[temp4] = 0 then goto 800 rem check ball color against block color if a[temp4]<>ballcolor then goto bad_hit to this: 700 rem check to see if player 1 is out of bounds if player1x = 25 || player1y = 0 then goto 750 if player1x=133 || player1y = 140 then goto 750 rem check for collision -- if not, bypass everything else as we wait for a collision if !collision(playfield,player1) then goto 800 705 rem double anglevar in order to determine how the number relates to a 0-15 range rather than a 0-7 range temp4 = 4 - anglevar temp4 = temp4 + temp4 + base & 15 rem checks for various hit-types, currently only consists of a single possibility, but there will be more for secondary colors, white, and black if a[temp4] = ballcolor then goto good_hit ... All while reducing cycles by cutting out a for-loop and passing off the collision detection to hardware that's specifically meant for that purpose. It seems like a win-win to me.
×
×
  • Create New...