Sunday, July 25, 2010

Three Laws of Robotics

  1. A robot may not injure a human being or, through inaction, allow a human being to come to harm.
  2. A robot must obey any orders given to it by human beings, except where such orders would conflict with the First Law.
  3. A robot must protect its own existence as long as such protection does not conflict with the First or Second Law.

It's clear that droids in Paradroid violate the first two laws, but that doesn't mean that they have to violate the third law as well. After all, Elvex dreamt of world without the first two laws.

So... why would a droid with any brains rush straight into explosion it surely knows will harm it? The most common situation where you see that happening is when a high-speed droid (like 834) shoots a low level droid in front of it and then runs (or flies, 834 is an anti-grav droid) into the explosion, destroying itself. I see no reason for that, so the droid must stop. The fastest way for detecting this was to give every patrol route segment an unique number and update droid info when it leaves a waypoint. When droid is destroyed its type is changed to explosion, but most of other attributes remain. That means that when checking for future collisions I can discard most of the droids/explosions by checking the route number only. That still leaves collisions on or near waypoints, but I have to start somewhere (and I hope I can forget more exact check later ;)

There are 433 waypoint exit directions in total so it was more efficient to write a subroutine to enumerate all routes on fly than including them in the data. That routine is less than 130 bytes long, static data plus depacking code would be at least twice the size. I also needed one 256-byte table to be able to look up the route number fast; 32 waypoints with 8 possible directions form an 8-bit index into that table. I can update route number with "lda waypoint_num; asl; asl; asl; ora dir; tay; lda routes,y; sta droidRoute,x" (that happens only when droid leaves waypoint) and check for impending collisions with "lda droidRoute,x; cmp droidRoute,y" - only if routes match I need to check for the distance between two objects. Nice and fast, now I need to play some games to check if I can see the difference.

Later I can use the same data to check if another droid is in a security droid's way, and if that's the case the security droids may decide to destroy a low-level droid to serve a greater good - to protect the ship.


A robot will guard its own existence with lethal antipersonnel weaponry, because a robot is bloody expensive.
- David Langford

Sunday, July 18, 2010

Not dead yet, part 2

It may have taken two years, but here is the newest and greatest version!

Both original and Metal Edition graphics are now in the same executable, droids have slightly modified AI, high score saving should work with most common expansions (you may have to disable your disk speeder cartridge though) and there is a new frontend. Can you name all the games from where I borrowed something?

Some minor changes including but not limited to

  • orange/red alert increases enemy fire probability half/full ship
  • competition mode - same droids every time, has separate high score file
  • no pacifist bonus if disruptor used
  • droid centered on lift when deck changes - no more exit through wall

File update: two bugfixes to eliminate crash on startup (hopefully one of them does the trick), one minor visual fix and adding disruptor to firing statistics. No more easy accuracy bonus by using 711/742 only!



Friday, January 1, 2010

Crash Boom Bang!

Ever tried disrupting the last droid on ship to smithereens and entering lift when the explosion was still going on? Not? Good. That would have crashed the game right there. Very annoying, especially if you had just quadrupled your all time high score...

What are the changes of someone completing a ship and entering lift before deck is shut down? I don't know, but I managed to do so! Then I reproduced it on purpose to make sure there is a bug. Now I have to hunt it down.

Edit: Found it! It's the same bug which causes "pacifist takeover" (not shooting any droids - except with disruptor!) crash randomly at the same point. In Time is of the Essence I gave you the code for play are top split. In Irq_118() I use this to stabilize raster regardless of how many sprites are over the split:



lda $dc04 ; [1,15] ([2,15] if NTSC/Drean)
eor #$0f ; [14,0] ([13,0])
sta .j3+1
.j3 bpl *+2 ; jump into the delay code
 
; entering at offset 0 delays 16 cycles,
; entering at offset 14 delays 2 cycles
;
; OP_CMP_IMM is opcode for CMP #immediate (2 cycles),
; OP_CMP_ZP is opcode for COM $zeropage (3 cycles)
 
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_ZP
nop

Guess what happens if CIA timer underflows before the interrupt code is executed? It will jump randomly forward and CPU ends in la-la land. Unfortunately there is some code which needs to be run with interrupts disabled, and if top split is delayed because of it... BOOM!

The solution? I can use software semaphore to protect shared resources and avoif disabling interrupts. Nice and clean solution, but why should I do that when I can kludge around the problem by waiting for raster position after the split before disabling IRQ... "bit $d012; bpl *-3; sei" fixes the bug.

Wednesday, December 30, 2009

NTSC Blues

Why didn't anyone remind me that it would be nice if the new front-end worked on NTSC as well...

Answers on a postcard, please.

Sunday, July 6, 2008

Time is of the Essence

After reading about Mike Dailly's raster split trouble I decided to write a bit about how the top split is done in Paradroid Redux.



There are three things to do when playing area begins:

  • change background color
  • start character display
  • start sprite display
The first one is just $d021 change, other two require a bit of trickery.

To mask vertically scrolling characters one would usually use illegal graphics mode (Extended Color Mode comined with Bit Map Mode and/or Multi Color Mode) or sprites. The first method produces black pixels so it isn't usable unless background is black, and the second one is unusable if sprites need to cross the split.

So, it's time for some font trickery. Raster interrupt several lines above the split changes to blank font, and then the actual split interrupt changes $D018 to display correct character data. This means "wasting" $0800 bytes for the blank font, but half of that memory is used as temporary buffer elsewhere so actual cost of clean split is one kilobyte.


Clipping sprites cleanly requires trickery as well as there is no way to start sprite display from the middle of graphic data. One way to achieve clipping is clearing top of sprite graphics if it overflows top split, but that would waste time both when clearing the memory and when running extra animator/digit generator rounds to restore top of sprite when more of it gets shown. Another way to achieve clipping is to put sprite at non-visible x-coordinate and then change them at the correct line. However, there is no time to change multiple registers in time.

Guess what? $D018 comes to rescue once again. There is an extra screen where the sprite pointers point to blank sprites. When the time comes, split interrupt doesn't only change font (bits 1-3 of $D018) but also displayed screen (bits 4-7 of $D018). Nice and easy solution but it requires a blank screen, another one kilobyte wasted. No, not really - there is no reason why one half of the blank font couldn't be used for blank screen. What that means is that sprite clipping is practically free as there is no extra memory required and $D018 needs to be written anyway for character blanking.


There is one problem with the screen change though. While VIC-II reads font data and sprite pointers & sprite data every line, character pointers (screen data) are only read on every eighth line. This means that while sprite and font changes are immediate, screen change affects display 0-7 lines later. To overcome this the top line of playing area is copied onto blank screen so character pointers are correct when $D018 is changed.

As blank screen is located inside blank font this copying creates yet another problem. Blank font isn't that blank any more. The topmost line is at SCREEN + $140, which means that chars $28-$2c aren't blank and will produce garbage if they appear on the topmost line. The easiest way to avoid that is to not use those chars inside playing area at all, so that's what is done. The same problem happens because of blank sprite pointers at SCREEN + $3F8, char $7F. That one is unused as well.


And what does all this has to do with timing problems Mike mentioned?

VIC-II doesn't have time to fetch sprite data when CPU is not using memory bus, so it has to stop CPU momentarily whenever sprites are active. Just how many cycles VIC-II steals from CPU depends on which sprites are active, and this causes a timing hell as you have to change registers when C64 is inside the side border area to avoid flicker. With $D021 change you have all the side border (23 cycles) to change it, but $D018 is trickier. Sprite data is fetched very early, the first three sprites get their data read at the end of previous line. This means that if $D018 write is late sprites will stay blank for one extra line.

To get register writes done at the very beginning of side border area the game uses CIA timer to stabilize raster timing. During game init CIA1 timer A is started, running through 63-65 cycles depending on VIC-II version. This means that $DC04 is always synchronized with current display X position. IRQ only needs to read $DC04 and skip that many cycles.


Did I forget bad lines? Every eight line VIC-II needs to read character pointers and that's not possible without stopping CPU for most of the raster line. This means that there is absolutely no time for anything unnecessary. In the case the split happens on a bad line the game triggers raster IRQ two lines above split, prepares next interrupt at the correct line and then preloads $D018/$D021 values and executes two-cycle instructions until IRQ happens. That guarantees minimum interrupt latency. When raster interrupt happens it will just write those two registers, clean up the stack (this second interrupt pushed status register and return address into stack) and jumps into the common code.



Nothing explains code better than source, so here it is. Only relevant parts are shown, and for clarity I've removed all assembly directives which were there to make sure branches don't span page boundaries (which would add one cycle to the branch).

       IRQ at line 95, prepare for split
 
...
 
lda #$10
ora _vScroll
sta $d011
cmp #$16
bne .057
 
; special case for bad line

lda #<Irq_116
sta $fffe
 
lda _d018+1
sta _d018b+1
lda _d021+1
sta _d021b+1
lda #116
bne .x1 ; jmp
 
; normal case
 
.057 lda #<Irq_118
sta $fffe
lda #118
 
.x1 sta $d012
 
...
 
;----------------------------------------------------------------
 
; this one used when ($d011 & 7) = 6, stuffs
; d018/d021 as fast as possible at raster 118
 
subroutine
Irq_116 pha
sty .yr+1
cld
 
lda #<Irq_118b
sta $fffe
lda #>Irq_118b
sta $ffff
lda #118
sta $d012
inc $d019
 
; 118/15 = 7.8 so this one is executed 8 times
 
sbc #15
bcs *-2 ; 8*5-1=39 cycles
 
; preload registers and execute 2-cycle
; instuctions until next IRQ happens
 
_d018b lda #scr_GAME
_d021b ldy #0
cli
repeat 16
cli ; 32 cycles wasted
repend
 
; now is the time to write registers, we always enter
; via interrupt as the above code never runs this far
 
Irq_118b
sta $d018
sty $d021
 
; clean up stack and continue normal IRQ code
 
pla ; flags
pla ; PC lo
pla ; PC hi, always != 0
bne .irq0 ; jmp
 
;----------------------------------------------------------------
 
; normal case, use timer value to stabilize
; raster regardless of sprites over the split
 
Irq_118
pha
sty .yr+1
cld
 
lda $dc04 ; [1,15] ([2,15] if NTSC/Drean)
eor #$0f ; [14,0] ([13,0])
sta .j3+1
.j3 bpl *+2 ; jump into the delay code
 
; entering at offset 0 delays 16 cycles,
; entering at offset 14 delays 2 cycles
;
; OP_CMP_IMM is opcode for CMP #immediate (2 cycles),
; OP_CMP_ZP is opcode for COM $zeropage (3 cycles)
 
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_IMM
cmp #OP_CMP_ZP
nop
 
_d021 ldy #0
_d018 lda #scr_GAME
sta $d018
sty $d021
 
; continue with interrupt
.irq0 ...

Monday, June 23, 2008

Have a nice summer solstice(ish)



To honor the sun I give you a new release - or two releases actually.



My plan was to build an unified version with both graphic sets held in memory all the time, but that didn't happen due lack of memory. Too bad, having different graphics on different decks would have given some more variety to the game.

I did manage to fit all necessary data into memory, but as I had to
EOR fonts together there was no way to swap between them without temporary 2 KB buffer. You really can't unpack LZ data and EOR it simultaneously, which took me way too long to realize.

While it would have been possible to do the EOR in two passes with the 1 KB buffer I do have, I didn't bother with that as I would have to drop dual graphics as soon as I need the memory back anyway.



Oh, I did fix one single pixel bug in the hires font too :)



Edit: I also added single pixel bug into Metal Edition - when you clear deck the first time, there may be extra pixel in the background star. That one is gone as soon as you move a bit vertically, so I won't do another build just to fix it.

Sunday, April 20, 2008

Not quite dead yet

Here is little something for you who fear that the project is dead. Not much have changed though:
  • Subgame should now take 10 seconds regardless whether you're playing on C64 or C128. That's still slightly longer than original, but way better than 12 seconds.

  • Game should finally be Drean 64 compatible. I thought I put the code for this into the game 18 months ago, but apparently I didn't. Well, who's going to send me a Drean 64/128 so I can actually test it?

  • Most likely there are some other changes too, it was six weeks ago when I last touched the source. The only reason I did it now was to fix the download link.
Note: if you for some reason want to archive every single release, then do yourself a favor. Don't use build number as filename part! It was never meant for that. Instead, parse it as BB-DDMMYY where BB is daily build count, DD is day of month, MM is month and YY is year. Then reorder these as YYMMDDBB and when using that as part of filename you get chronologically sorted list.

Edit: Drean compatibility is now confirmed, thanks to the_woz. Check out his blog, especially Drean-specific entries.

Tuesday, December 25, 2007

Have a nice winter solstice

For those too busy to read any further, click here.


Important: archive updated January 2nd, you need to delete old high score file as it's not compatible any more.


Due to some rather unfortunate events in the family I haven't had as much time for PR as I would have liked to, so there are no major changes. Minor changes include:

  • fixed all but one of known bugs.
  • 2500 bonus points if you clear a ship without shooting any enemy droids. Note that if you hit a single droid on ship one, you won't get bonus even if you clear ship two without any shooting as hit counter is preserved from one ship to the next (same is done with accuracy calculation).
  • you can reduce enemy droid pulser count in the subgame by damaging them. This doesn't have much effect with the higher class droids, and you will have to cope with whatever energy the droid has left...
  • background stars are a bit more interesting now.
  • as always, it's slightly smaller and faster :)

As I haven't had much time to test this one, report any oddities please.


Changes which didn't make it to this version:

  • subgame bonus points for 11-1 / 12-0 wins. No time to fix the bugs caused by this...
  • raiders. You know, those annoying rogue droids in Paradroid'90.


Even if my time for coding has been limited, that doesn't mean that I haven't thought about the game during the slow times at work. I'm positive that the actual playing area can be enlarged by t least one character row. With C128 I think it might be possible to do two or three additional rows without the game slowing down. We'll see if I ever have time for that.

Sunday, October 7, 2007

I'm sane, thank you :P

Contrary to some other claims I'm not crazy, or at least I imagine so.

That's not the only error in Paradroid talk page - so here we have (drumroll, please)

The Definite C64 Paradroid Version Guide

  • Paradroid (original), 1985

  • Paradroid Competition Edition, 1986
    This one is identical to the original, except that it has some vertical blank waits removed. That allows the game run faster most of the time.
    Scroll code is unchanged, whoever wrote that it was enhanced clearly hasn't disassembled all versions and done comparisons between them...

  • Paradroid Metal Edition a.k.a. Heavy Metal Paradroid, 1986
    Minor changes, mostly allowing the use of multicolor chars, remaining ones save couple of cycles and/or bytes here and there.
    Uses C128 2 MHz mode in top/bottom border for higher speed.
    Fixes the decimal mode flag bug which causes weird sound fx in earlier versions.
    Some scroll text changes - this includes two bad chars which seem to be in every original ME tape!

  • Paradroid Redux, 2006-
    Nanos gigantium humeris insidentes.

Monday, September 24, 2007

Tweaking

Too little time for anything major (yet!) but scoring and subgame have seen some little changes.


  • Bonus score if you do well in subgame. 20% bonus for each remaining pulser if you win 11-1, 40% if you win 12-0

  • 2000 point bonus if you're wimp and use only transfer to overtake the ship. Not enough to compensate for score lost by not shooting droids, as I don't want to encourage that kind of cowardism ;)

  • Shoot droids to pieces before transferring to them - their pulser count reduces by one for every 16 points of damage. Don't forget that you have to cope with whatever energy they have left, though!



In addition to adding small things I've also discarded some ideas

  • Grenades/mines. What to do when droid explodes a mine but there are no free sprites?

  • Two player mode through link cable. That cuts down sprites available for enemy droids and fire, lowering the difficulty considerably. With fever sprites it's also harder to hide the fact that the game teleports droids away if it runs out sprites.


I have doubts about transport pads as well. These would transfer player within a deck, but that would require resetting droid positions to avoid several visibility problems. And that would mean teleporting all droids, meaning you could face the same robot you were running away just a second or two ago half a deck away. You may say that there isn't much realism in the game, but that makes it even more important to preserve what's left of it!

Wednesday, September 5, 2007

Wasting time

How can one waste gazillions of cycles to mirror one sprite? Quite easily, just forget speed and concentrate on compact code.



MirrorSprite
 
ldy #0
sty src
sty .5+1
 
; src = A<<6 | $4000
 
sec
ror
ror src
lsr
ror src
sta src+1
 
; ptr = X<<6 | $4000
 
txa
sec
ror
ror .5+1
lsr
ror .5+1
sta .5+2
 
; get sprite multicolor flag
 
ldy #$3F
lda (src),y
sta tmp2 ; b7=1 if multicolor
 
lda #60
 
.1 tax ; x=60,61,62, 57,58,59, ... 3,4,5, 0,1,2
 
lda #3
sta bytesLeft
 
.2 dey ; y=62,61,60, 59,58,57, ... 5,4,3, 2,1,0
lda (src),y
sta tmp1
lda #$01
.3 lsr tmp1 ; 5
bit tmp2 ; 8
bpl .4 ;10 hires, 8 loops 1 bit each
 
php ;13 else multicolor, 4 loops 2 bits each
lsr tmp1 ;18
rol ;20
plp ;24
.4 rol ;26
bcc .3 ;29 8*16=128 / 4*29=116
.5 sta $8000,x ; 63*128=8064
 
inx
dec bytesLeft
bne .2
 
txa
; sec ; asserted with "bcc .3"
sbc #6
bpl .1
 
rts
 


Hey, I just realized I can make it at least one byte sorter! ;)

Update: cycle counts were way off...

Tuesday, September 4, 2007

Byte Liberation Front

I keep surprising myself with all the memory I can squeeze out when I start trying. When reordering the multicolor charset from the Metal Edition Paradroid I finally took a good peek at the alpha charset. Eliminating duplicate chars and blanks freed another 31*8 bytes and couple more in graphics font. There are several 8-byte data areas I can scatter anywhere in the memory, but to keep it neater I should do some rearranging. Finding the correct data block in binary file whenever I want to change something doesn't sound fun to me.

In case you're wondering about those five blank chars below "8" - "c" and why they aren't used... Chars $28-$2c (top halves of those 1*2 characters) are at offset $0140 and need 40 bytes. (Not so) interestingly that's the same offset and size as 10th character line on screen, which is the first line used for the deck display. To change all sprite pointers and displayed charset with single $d018 write I simply switch from blank screen + blank font to actual game screen and graphics at the correct line. However, VIC-II has already read the character pointers at that time (from blank screen) which means that I have to copy the top line from game screen into the blank screen. That in turn makes chars $28-$2c visible if used because the blank screen is actually the first half of the blank font... So, those chars are unusable as graphic data both in alpha font and in in-game graphics font. That however doesn't mean that they can't be used for anything else ;)



From graphics to sound effects... I found out that I can move the main SFX table partially into the RAM under I/O at a cost of ten cycles every time an effect is started. This filled up the hard-to-use $D000 RAM completely, and freed 160+ bytes elsewhere.



Back to graphics with something which is only an idea so far. Compressing droid parts separately would gain ~600 bytes, but I can do even better. If sprites are decompressed into one continuous block, I can use the previous sprite data as codebook which would give better compression at no extra cost. Extra time used for decompression isn't anything to worry about, no one has complained about the sprite mirroring delay yet and that one takes about 15,000 cycles per sprite, 60,000 cycles in total - even if mirrored sprites aren't used at all!



All these summed up give me more than one kilobyte to play with - but what should I fill it with?

Sunday, August 26, 2007

Lies, damned lies, and statistics

I needed something to use when deciding which tables benefit most from move to zero page, so I counted words and their occurences in all source files. In addition to giving me the info I wanted, it revealed something else.



Paradroid Redux Top 30 ML Instructions


1549 lda 202 adc 119 bmi
1362 sta 175 lsr 108 iny
679 jsr 167 bcc 106 clc
394 ldx 163 stx 100 sbc
380 ldy 151 bpl 85 dec
313 bne 144 asl 84 eor
251 rts 133 bcs 83 ora
245 beq 131 inc 82 bit
219 cmp 130 sty 81 sec
210 and 124 jmp 76 dex



Disclaimer: Above counts include some code which is commented out, and I didn't expand all macros either.

Saturday, August 25, 2007

Zero page roundup

I finally took the time to change all zero page variable declarations from

var = $02
to
var ds.b 1
and removed all variables which aren't used any more. I suspected I would end up with 30 to 40 free zero page locations, but it turned out to be about 70 bytes! I swiftly used half of it for the two most used object variable tables (16 bytes each), which makes accessing them both faster and smaller. I guess those tables really are accessed a lot, code size was reduced by over 100 bytes... I still have room for another two tables there, but I have to find the ones which gain most cycles/bytes.



Complete list of bug fixes since the previous release:


  • Droid teleportation doesn't send them outside the deck any more.

  • Waypoint chars are placed onto deck map every time when exiting lift, meaning no more completely confuzed droid army.

  • Door status is restored when exiting lift, so droids won't get stuck against them any more.

  • The first deck entered after load had waypoint magic chars visible, as they aren't hidden in EnterShip() routine but in EnterDeck(). Instead of wasting three bytes to add one JSR call, I changed the font so magic chars are there after load :)

  • Background stars are now really disabled during the intro sequence, that avoids random colors for lower half of "7" char. I disabled them earlier, but that broke when I added more run-time randomizing.



Important bits here.



Update


Two more bugs fixed, one remains... Intro text scrolled beyond the end of page sometimes, and out-of-sight droids were paralyzed on C128. The remaining bug allows droid go through wall in certain conditions, something which I didn't notice on emulator.

Friday, August 24, 2007

Squish 'em

Two bugs found and fixed, both of them having the same root cause. Whenever you change deck in lift, it's built immediately, even if you don't actually enter that deck. This avoids a delay when you exit the lift. However, if you then decide to go back to the deck where you entered lift after, game keeps previous droid/deck status intact but deck map is reset to the default state.

Now, the two bugs:


  1. All waypoints are now marked with magic chars for faster detection, and I didn't restore them when re-entering deck. Mea culpa. Now waypoints are decompressed/marked correctly evrey time. This means that some work is done twice when entering a new deck, but it's fast enough to be unnoticeable.

  2. Doors keep their state on re-entry, but deck map has all doors closed after decompression. If droid had opened a door when you caused level build in the lift, droid got stuck against/in the middle of a door which was visually closed, but logically open. This bug was there from the beginning. I added some code to restore door visual state when entering a deck, and it seems to work. Doing this with minimum amount of new code made door code resemble spaghetti tho.



New beta out during this weekend, with almost all known bugs fixed. Well, make that all known bugs fixed if I have time for it :)

Sunday, August 19, 2007

Another week gone by

And so time passed by with nothing extraordinarily great done...



I found a bug in the latest beta - it teleports droids to the great unknown because I didn't adjust the teleport routine when I changed the waypoint system. Fixing that didn't take long, but with the released beta you need to enter another deck and come back to get droids back. Just visiting a lift doesn't work.



Multicolor is almost working. Almost, but not quite. I really should have done multicolor changes too when I changed the hires font...



I saved 70 bytes in the background star drawing by combining the top and bottom routine, it's not time critical so I could shrink the code without worrying about the speed. Adding random stars and blinking took most of the freed space, but I guess I can accept that.



I've used some of the new droid-specific data in old routines, speeding them up slightly as well as making them smaller. Inserting new droid AI makes every droid take slightly longer time to run, so I need every cycle I can get. I want to make big bad droids get irritated by the smaller ones when alarm is high if possible. I can already see 834 bumping into 329, shooting it out of the way and then rushing into the explosion. :)



One thing to consider is to make two passes through the droids when running them, handling visible ones in the first round and the remaining ones in the second. That way I could exit early if it looks like I'm running out of time. Sorting out-of-view droids by their distance would make it work even better, but sorting takes time...

Monday, August 13, 2007

How about a nice game of chess?

No chess here, I'm afraid. However, if you prefer more action then try the latest version of Paradroid Redux instead. Every bug listed here has been fixed (I hope).



I guess I now can go back to do multicolor changes. I really should have reordered the MC font when I did the hires one, now I need to dig out old notes telling which char ended where.



Update

It seems that this version finally runs solid 25 Hz on PAL C64 when I remove all sanity checks. It does cheat a little, but if you can't notice it that doesn't matter, does it? NTSC C128 should do 30 Hz easily - I want one!

Sunday, August 12, 2007

Objects want to be free - but only be freed once

This weekend wasn't all bad in spite of having both my birthday and wedding anniversary (yes, my wife wanted to make sure i wouldn't forget the latter! ;) as I found the reason for the double-free bug - or at least one of the reasons.




I outlined the collision check loop(s) in an earlier entry. What's not shown in that code snippet is caching of outer loop object type, done to speed up the collision type decision. Even when the outer loop object gets removed in the collision the inner loop still continues afterwards. Check out the picture: the laser bolt colliding with two explosions is the object which gets removed twice. If the outer object type wasn't cached then all remaining collisions with it would be NOPs.

I have at least three ways to fix this:


  1. don't cache the object type

  2. exit the inner loop when outer loop object gets removed

  3. check object type against -1 (free object) every time before freeing one


I will do #2 although it means duplicating some code, as I hate to waste cycles for checking for special cases unless it's absolutely necessary. I will rethink my decision if object removal still bugs.

Wednesday, August 8, 2007

Hum Bug Betatesters

No one tells me about bugs... so I just have to play the game to find them myself! :)


  • Droid library had two bugs: every droid class text was the player class, and droid entries went from 0 to 23 instead of 1 to 24.

  • Droid count in console was wrong, it ignored some droids.

  • Deck layout horizontal scroll was broken. You all did know you can scroll it now, didn't you?

  • Maintenance deck had one droid too many on the left part, one too little in the right one. This one you really should have noticed!

  • Remaining bugs:

    • Radar is borken.

    • Lift ignores short fire button press just after up/down move.

    • Game tries to free same object twice, this causes a freeze when my sanity check catches it.




I bet there are more, but the last one is the most serious one. Thanks to trurl for first spotting it, now I can cause it too. The easiest way to do it is to enter ship 8 upper cargo when alert is red, then wait in the middle of bottom left room when berzerk droids circle around you shooting like there's no tomorrow. Well, without cheating there is no tomorrow for you...



Oh yeah, forget about the constant energizer animation part of the previous entry - I dropped it ages ago. I just hadn't played any of the official versions for a while, so when testing the Metal Edition it struck me as a difference to Competition Edition.

Saturday, August 4, 2007

Morphing into multicolor mode


I made a quick disassembly of Paradroid Metal Edition to see how much I need to change the code to make it possible to use either hires or multicolor graphics. The answer: not much. Color tables are different because only the lowest eight colors are available for character color and because of extra entries for multicolor registers. To use ME graphics I only have to reserve some extra space for the bigger tables and new title screen and add minimal amount of code. As I can do the patching inside the init routine which gets overwritten when game starts this will cost less than 80 bytes. The only thing that concerns me is the time taken by constant energizer animation, that's almost 4 lines more than changing the deck map. Does anybody notice if I drop it? ;)



The disassembly also showed that while Competition Edition was a quick hack to get something out for Xmas (in a double pack with Uridium Plus) Metal Edition got some more attention. Andrew Braybrook removed code which was disabled in the Competition Edition (raster checks which made sure that the original didn't run faster than 16 Hz), and enabled 2 MHz mode in the upper/lower border for C128 users. He also fixed the decimal mode bug which broke SFX randomly and adjusted background sounds for the faster frame rate, among other small things. Some of these changes will end up in Paradroid Redux, no doubt about that.



Too bad that I dislike Morpheus-like graphics in Paradroid myself. Anyway, those people who think that Metal Edition has the "correct" graphics can soon enjoy Paradroid Redux as much as fans of the original.