Yeah, I don't think the sprite is the issue either. Bugtracking is nasty business! When gets me is that I'm not new to this (I've been coding for 24 years, 7 of them in DBPro) and I'm no slouch at coding or bugtracking. With each hour a bug escapes my detection it becomes easier for me to believe that it's caused by an anomoly in DBPro's command set. If I'd never encountered such an anomoly before I'd be sure I'd done something stupid somewhere, but infortunately I have and I am forced to wonder whether I'm doing anything wrong at all.
I have implemented a sync timer and it's returning an expectedly small value (two milliseconds). I also have a mainloop timer which covers the sync as well as all screen updates, and all calculations that take place every iteration, which is returning 80 milliseconds per loop (it's only 21 when I load fewer images). This seems to preclude the SYNC command from the cause, though I never truly suspected it to begin with.
It's a fresh day for me, just got out of bed, so I'm looking at the problem with a hopefully new perspective. I'm going to start throwing thoughts out there as I think them, just in case someone adds two and two where I've missed it:
I use a set of variables to determine how many images are loaded:
usetestnumbers = 1: ` Setting this to 1 causes array dimensioning to be smaller, for faster loading and quicker testing purposes.
select usetestnumbers
case 0
` CORRECT NUMBERS
global numentity = 9
global numhair1 = 132
global numhair2 = 96
global numclothes = 180
global numwings = 12
global numcape = 24
global numacc1 = 102
global numacc2 = 36
global numitems = 255
global numeffects = 1
global numweapons = 8
endcase
case 1
` TEST COMPILE NUMBERS (Smaller, to decrease load times for testing)
global numentity = 9
global numhair1 = 10
global numhair2 = 10
global numclothes = 10
global numwings = 10
global numcape = 10
global numacc1 = 10
global numacc2 = 10
global numitems = 255
global numeffects = 1
global numweapons = 8
endcase
case 2
` MODERATE COMPILE NUMBERS (Middle ground)
global numentity = 9
global numhair1 = 10
global numhair2 = 10
global numclothes = 180
global numwings = 12
global numcape = 24
global numacc1 = 10
global numacc2 = 36
global numitems = 255
global numeffects = 1
global numweapons = 8
endcase
endselect
Then I call the resource loading function. Note that this is the only place in the entire code that the above declared values are referenced. I will include here the entire resource loading function even though only part of it seems relevant:
function loadresources()
hide mouse
` Load the Loading Screen image and music first
set image colorkey 255, 255, 255
load image "Gfx\System\Solodor.bmp", 26010, 1
load image "Gfx\System\Solodor1.bmp", 26011, 1
load image "Gfx\System\Solodor2.bmp", 26012, 1
load image "Gfx\System\Button.jpg", 26013, 1
load sound "Sounds\Thunder4.wav", 14
play sound 14
` ***** MUSIC *****
loadscreentext("Loading Music...")
load music "Music\1.sap", 1: musicname$(1) = "Wildlands"
load music "Music\2.sap", 2: musicname$(2) = "Throwing Fire"
load music "Music\3.sap", 3: musicname$(3) = "Bandits"
load music "Music\4.sap", 4: musicname$(4) = "Escape"
load music "Music\5.sap", 5: musicname$(5) = "Restricted Area"
load music "Music\6.sap", 6: musicname$(6) = "Earth Day"
load music "Music\7.sap", 7: musicname$(7) = "Mutant Dancing"
load music "Music\8.sap", 8: musicname$(8) = "Desert Theme"
load music "Music\9.sap", 9: musicname$(9) = "Solodor Theme"
load music "Music\10.sap", 10: musicname$(10) = "Sad World"
load music "Music\11.sap", 11: musicname$(11) = "Fallen Angel"
load music "Music\12.sap", 12: musicname$(11) = "Credit Roll"
` ***** SOUNDS *****
loadscreentext("Loading Sounds...")
load sound "Sounds\Select.wav", 1
load sound "Sounds\Alert.wav", 2
load sound "Sounds\Bigping.wav", 3
load sound "Sounds\Ding.wav", 4
load sound "Sounds\Ping.wav", 5
load sound "Sounds\Swoosh1.wav", 6
load sound "Sounds\Swoosh2.wav", 7
load sound "Sounds\Text.wav", 8
load sound "Sounds\Triplebeep.wav", 9
load sound "Sounds\Rain.wav", 10
load sound "Sounds\Thunder1.wav", 11
load sound "Sounds\Thunder2.wav", 12
load sound "Sounds\Thunder3.wav", 13
` Skip 14 because we've already loaded it
`load sound "Sounds\Thunder4.wav", 14
load sound "Sounds\Thunder5.wav", 15
load sound "Sounds\Thunder6.wav", 16
load sound "Sounds\Wind1.wav", 17
load sound "Sounds\Levelup.wav", 18
load sound "Sounds\Explode1.wav", 19
load sound "Sounds\Explode2.wav", 20
load sound "Sounds\Explode3.wav", 21
load sound "Sounds\Explode4.wav", 22
load sound "Sounds\XP.wav", 23
load sound "Sounds\Swing.wav", 24
load sound "Sounds\Damage.wav", 25
load sound "Sounds\Inventory.wav", 26
load sound "Sounds\Drag.wav", 27
load sound "Sounds\Drop.wav", 28
load sound "Sounds\Action.wav", 29
load sound "Sounds\Hover.wav", 30
` ***** IMAGES *****
` Tiles occupy image numbers starting at 100 (Not anymore - now they're 33000 - 39999)
` Image 997 is reserved for screenshots
` Image 999 is reserved for the radar
` Entities occupy image numbers starting at 1000: 1000 + entitytype*20+x is the first image of 12 for each entity
` All accessories combined occupy image numbers 4000 to 26000: offset + accessorytype*20+x
` Image numbers 26001 - 27000 are used for various GET IMAGE and sprite manipulation codeshards.
` Images 26001 - 26003 are used for health, mana and XP bar length GET IMAGE code.
` Image numbers 26010 - 26020 are various fullscreen graphics like the loading screen and title background.
` Image numbers 27100 - 27999 are world item sprites.
` Image numbers 28000 - 28999 are projectile images.
` Image numbers 29000 - 31999 are effect animatiions.
` Image numbers 32000 - 33000 are weapons animations.
` Image numbers 33000 - 39999 are the tilesets.
` Image numbers 40000 - 49999 are reserved for item and ability icons.
` Image numbers 50000 - 50999 are for various UI icons and images.
` Image numbers 51000 and up are free.
` Transparent color is black for the HUD
set image colorkey 0, 0, 0
loadscreentext("Loading HUD...")
load image "Gfx\HUD\HUD1.bmp", 30001, 1
load image "Gfx\HUD\Healthbar.bmp", 30002, 1
load image "Gfx\HUD\Manabar.bmp", 30003, 1
load image "Gfx\HUD\XPBar.bmp", 30004, 1
` FREE IMAGE 30005: load image "Gfx\HUD\.bmp", 30005, 1
load image "Gfx\HUD\HUD2.bmp", 30005, 1
`Transparent color is white for these elements
set image colorkey 255, 255, 255
load image "Gfx\HUD\Rain.bmp", 30006, 1
load image "Gfx\HUD\LeftArrow.bmp", 30007, 1
load image "Gfx\HUD\RightArrow.bmp", 30008, 1
load image "Gfx\HUD\Radar.bmp", 30009, 1
load image "Gfx\HUD\Buttons.bmp", 30010, 1
load image "Gfx\HUD\EmptyEquip\Amulet.bmp", 50001, 1
load image "Gfx\HUD\EmptyEquip\Arms.bmp", 50002, 1
load image "Gfx\HUD\EmptyEquip\Belt.bmp", 50003, 1
load image "Gfx\HUD\EmptyEquip\Boots.bmp", 50004, 1
load image "Gfx\HUD\EmptyEquip\Chest.bmp", 50005, 1
load image "Gfx\HUD\EmptyEquip\Cloak.bmp", 50006, 1
load image "Gfx\HUD\EmptyEquip\Gloves.bmp", 50007, 1
load image "Gfx\HUD\EmptyEquip\Head.bmp", 50008, 1
load image "Gfx\HUD\EmptyEquip\Legs.bmp", 50009, 1
load image "Gfx\HUD\EmptyEquip\Ring.bmp", 50010, 1
load image "Gfx\HUD\EmptyEquip\Shield.bmp", 50011, 1
load image "Gfx\HUD\EmptyEquip\Shoulders.bmp", 50012, 1
load image "Gfx\HUD\EmptyEquip\Weapon.bmp", 50013, 1
` Transparent color is black for tiles
set image colorkey 0, 0, 0
loadscreentext("Loading Tileset...")
for x = 1 to 7000
f$ = "Gfx\Tiles\" +str$(x) + ".bmp"
if file exist(f$)
load image f$, 33000 + x, 1
endif
next x
` Transparent color is hot pink for entities and accessories
set image colorkey 255, 0, 255
loadscreentext("Loading Entities (Character)...")
for x = 1 to numentity
loadentity(str$(x), x)
next x
` Load Hair
loadscreentext("Loading Entities (Hair 1)...")
for x = 1 to numhair1
loadaccessory("Hair", "Hair" + str$(x), x)
next x
` Load Hair2
loadscreentext("Loading Entities (Hair 2)...")
for x = 1 to numhair2
loadaccessory("Hair2", "Hair" + str$(x), x)
next x
loadscreentext("Loading Entities (Clothes)...")
` Load Clothes
for x = 1 to numclothes
loadaccessory("Clothes", "Clothes" + str$(x), x)
next x
loadscreentext("Loading Entities (Wings)...")
` Load Wings
for x = 1 to numwings
loadaccessory("Wings", "Wings" + str$(x), x)
next x
loadscreentext("Loading Entities (Cape)...")
` Load Cape
for x = 1 to numcape
loadaccessory("Cape", "Cape" + str$(x), x)
next x
loadscreentext("Loading Entities (Accessory 1)...")
` Load Acc1
for x = 1 to numacc1
loadaccessory("Acc1", "Acc" + str$(x), x)
next x
` Load Acc2
loadscreentext("Loading Entities (Accessory 2)...")
for x = 1 to numacc2
loadaccessory("Acc2", "Acc" + str$(x), x)
next x
` Load Icons
loadscreentext("Loading Icons...")
for x = 0 to 100
f$ = "Gfx\Icons\" +str$(x) + ".png"
if file exist(f$)
load image f$, 40000 + x, 1
endif
next x
` Load Items
loadscreentext("Loading Items...")
defineitems()
for x = 1 to numitems
`27100
f$ = "Gfx\Items\Item" + str$(x) + ".bmp"
if file exist(f$)
load image f$, 27100 + x, 1
endif
next x
` Load Projectiles
loadscreentext("Loading Projectiles...")
load image "Gfx\Projectiles\Fireball.png", 28001, 1
` Load Special Effects
loadscreentext("Loading Effects...")
for x = 1 to numeffects
y = 0
done = 0
while done = 0
inc y
f$ = "Gfx\Effects\" + str$(x) + "\" + str$(y) + ".png"
if file exist(f$)
load image f$, 29000 + x*20 + y, 1
else
done = 1
endif
endwhile
next x
` Load Weapon Animations
loadscreentext("Loading Weapons...")
for x = 1 to numweapons
for y = 1 to 3
f$ = "Gfx\Weapons\" + str$(x) + "\" + str$(y) + ".bmp"
if file exist(f$)
load image f$, 32000 + (x*3) + y
endif
next y
next x
loadscreentext("Done!")
endfunction
The defineitems() function merely clears the array. The loadscreentext() function isn't important either; it just displays the gold 'Solodor' logo with the "Loading Entities..." text that you see at boot in the public alpha.
Here is the loadaccessory() function, called from the loadresources() function above:
function loadaccessory(dir$, name$, accessorytype)
if dir$ = "Hair" then offset = 4000
if dir$ = "Hair2" then offset = 10000
if dir$ = "Clothes" then offset = 13000
if dir$ = "Wings" then offset = 19000
if dir$ = "Cape" then offset = 20000
if dir$ = "Acc1" then offset = 21000
if dir$ = "Acc2" then offset = 24000
for x = 1 to 12
f$ = "Gfx\Accessories\" + dir$ + "\" + name$ + "\" + name$ + "-" + str$(x) + ".bmp"
load image f$, offset + (accessorytype * 20) + x, 1
next x
endfunction
After this code is executed, all of the assets needed by my game are loaded. Just how many images are loaded will be determined by the variable set declared in the first code snippet. If I set usetestnumbers to 0, I get a drastic reduction in framerates. If I set it to 1, I get full speed, and if I set it to 2, I get a somewhat reduced framerate.
Next is the routine that draws from the 7500 images that seem to be in question. It bears some explanation. Each entity is represented by a composite image: first we draw a nude, bald body, over which we draw his hair style, then we draw his clothes, his helmet, then his cape, then his wings, etc, until we have a composite image representing our character. Each entity has a set of arrays (I probably should have used a UDT for this, but for some reason I can't remember, I didn't) that contains item ID numbers (not image ID numbers). The set of instructions below that assign the contents of the WID() array convert the item ID numbers into image ID numbers, and populates the WID() array with the image numbers that will be drawn. The big complex IF statements containing checks against camx, camy, screen width etc are just checks to make sure the object is actually within the bounds of the viewable area of the map before we draw it - ie, we don't draw stuff that is off-camera:
for x = 1 to maxentities
if entityexists(x) and entitymap(x) = currentmap and entityxold(x) > (camx - 50) and entityxold(x) < (camx + screen width() + 50) and entityyold(x) > (camy - 50)
and entityyold(x) < (camy + screen height() + 50)
if entitydir(x) = 2 then direction = 0
if entitydir(x) = 4 then direction = 1
if entitydir(x) = 6 then direction = 2
if entitydir(x) = 8 then direction = 3
for y = 1 to 10
wid(y) = 0
next y
` The index on wid() is the draw order. Cape before wings!
wid(1) = 1001 + (entitygraphic(x) * 20) + direction*3 + animoffset2
wid(2) = 4001 + (entityhair(x) * 20) + direction*3 + animoffset2
wid(8) = 10001 + (entityhair2(x) * 20) + direction*3 + animoffset2
wid(3) = 13001 + (entityclothes(x) * 20) + direction*3 + animoffset2
wid(7) = 19001 + (entitywings(x) * 20) + direction*3 + animoffset2
wid(6) = 20001 + (entitycape(x) * 20) + direction*3 + animoffset2
wid(4) = 21001 + (entityacc1(x) * 20) + direction*3 + animoffset2
wid(5) = 24001 + (entityacc2(x) * 20) + direction*3 + animoffset2
for y = 1 to 10
if entitygraphic(x) > 2 and y <> 1 then wid(y) = 0: ` This stops equipment from drawing if you are not a human male or human female.
if wid(y) <> 0
if image exist(wid(y))
if sprite exist(1) then delete sprite 1
sprite 1, 0, 0, wid(y)
` Adjust appearance of sprite depending on condition of entity
if entityalpha(x) > 0 and entityalpha(x) < 256 then set sprite alpha 1, entityalpha(x)
if entitydead(x)
offset sprite 1, 16, 32
rotate sprite 1, 90
endif
dx = entityxold(x) - camx
dy = entityyold(x) - camy
if dx > -50 and dx < screen width() +50 and dy > -50 and dy < screen height() +50
if entityshake(x)
paste sprite 1, dx + randomshake, dy
else
paste sprite 1, dx, dy
endif
endif
delete sprite 1
endif
endif
next y
endif
next x
if sprite exist(1) then delete sprite 1
I believe that this is all of the relevant code in the entire program. I have started running some more tests, isolating parts of my code (ie, not calling entire functions in the mainloop that perform draw updates or critical calculations). This is resulting in a malfunctioning game, but I am testing my framerates with each exclusion to see if excluding any one function restores framerate. This might point to some culprit code. I feel sure I've either done something noobish or DBPro itself is at fault.
Then again, I suppose one of those things is always to blame for a bug. Can anybody see anything obviously dumb I've done above?