News:

Printed Amstrad Addict magazine announced, check it out here!

Main Menu

CPCRetrodev 2021 : Fitzroy Dives Deep

Started by awergh, 08:36, 14 November 21

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

awergh

Fitzroy Dives Deep
As per the ongoing tradition I wrote another Fitzroy game for CPCRetroDev.
The game disk and instructions can be found here:
https://awergh.itch.io/fitzroy-dives-deep
I have also attached the recent binary (v1.3) to the end of this post.

I also tried to use images in attachments but could not figure out how to add them inline so imgur it is.
Feel free to give me feedback and report some bugs as I am sure there are things to fix that I don't know about. It seemed like every time I added a new level I found a bunch of bugs.
If you don't want to read my long post just grab the disk file and play it (you have been warned that this post might go on for a while  :P ).

Fitzroy Dives Deep is unquestionably my most successful game with seventh place :D (last year was my least successful but I won't dwell on it too much).
This clearly means I did a lot more things right or less things wrong (whichever way you wish to look at it).

Rather than asking a bunch of questions (like I have some years) that no one seems to answer I will describe a bit more of my process as well as lessons learned.
I will try to add some screenshots because Gryzor said he likes having those.


A few things I learnt from the last few years for this game were:
1. Base the idea on a gameplay idea rather than a subject matter. When I decided to use sea mines I had no idea what the game was and spent a whole lot of time doing research without having a good idea on what sort of game I wanted to make. What I ended up was somewhat accurate to the idea but the core game was not that compelling.

2. Make sure the game is approachable, having some instructions is fine but avoid something really complicated.
Fitzroy in the Dark can be a little bit hard to understand how to use the inventory, but Fitzroy goes Sweeping is really hard to understand without instructions (Which I forgot to include in the submission arghh.).

3. Be careful not to make the levels barren or bare. Fitzroy in the dark has these big levels for you to explore but they aren't particularly distinct (at least not when its dark) and there isn't enough stuff or enemies to make them memorable.
I also effectively created a game that required you to make a map but I doubt many people have the patience for that.

4. Get the movement and controls right early on. Fitzroy in the dark was rather clunky initially with the selecting a square and then moving.
Fitzroy Dives Deep originally had up to jump which was OK originally but having a dedicated jump button made for a better experience. It also allowed me to use the jump key when in the water which made the water movement more interesting and faster.

That isn't to say my game is bug free. I haven't ever released a game without any bugs (like most people I expect) even if cpc-power only lists them for some games.


There are three bugs I know of that are intermittent.
1. A tile is sometimes drawn randomly on the screen, this is usually near the right edge but does not affect gameplay. (I very carefully made a video where this did not happen.)
I suspect it could be related to my backbuffer but I didn't really look into it. I haven't seen this happen when using wincpctelera.
There is a spot on the valves level where it is consistent, but I never got around to looking into it.

2. One of the snail's spawns in the wrong spot on the first level, I thought using memset on the enemy array before initialising would fix this, but it still sometimes happens. The memset fixed the game from crashing every time you reload a level though.

3. The game completely crashes at random (game breaking); I have no idea what causes this one as at other times it will be perfect playthrough which makes it hard to reproduce unless you happen to snapshot at the right time. I would have spent more time on this, but I didn't have much time and it was intermittent.
I am reasonably certain it is related to enemy initialisation issues. Intermittent CPC only bugs are always the hardest to figure out.


Not so intermittent bugs (workarounds exist)
1. When you boost upwards it doesn't always go to the right to top, I partially fixed this, but it does not work in all circumstances. Instead of Fitzroy going to the top of the screen he will be one pixel off. The workaround is that when the player boosts in water up to a solid block they also press up to get all the way to the top.
I feel this is a little bit inconvenient and adds a bit of an artificial difficulty to the game. It hopefully shouldn't be too hard to fix, I also assume no one would want me to keep this for the added difficulty?

2. On the last level there is a spot you change rooms where can you end up one pixel into a tile and seem stuck until you press up or left (can't remember which).
I considered this insignificant so haven't had a look at all why this happens.


A few things I learnt for this game:
1. How to use Arkos Tracker 2 (Thanks Arnaud and Targhan for their help), it is nice to finally have a game with sound and music.
Technically last years game had one sound but that doesn't count for much. Arkos 2 is also way better to use than Arkos 1.

2. How to use a backbuffer. Previously I have saved the video memory of an actor (player or enemy) as they move and then redraw it later. This time I used a backbuffer which was more reliable. However instead of sacrificing 16KB for the whole screen I used a backbuffer that was 4 tiles big (256 bytes).
This worked reasonably well and saved me some memory but there is a bit of slowdown because of the backbuffer.
Perhaps there is a better strategy of populating the backbuffer from the map where the actor was and drawing it at the actor's previous position and then doing the same thing at the actor's new position.
I suspect I have not got it completely right but at least there are no leftovers on the screen.

3. Limit adding new features near the end of the development time. I tried to avoid adding features in October.
This also applies to graphics where it worked out much better finalising the graphics and palette sooner (as usual I make sure to support the green screen even if I am the only person who ever uses this option).
I did spend more times on graphics this year which means they are better, but they remain fairly simple.
The more practice I have at making graphics the better I will get. I still managed to change the main character sprite and one of the interactable objects at the last minute, but I mostly stuck to this.

4. Try not to spend too much time on tools (more on that later).


More on the original idea
Back to the original game idea. The original idea was to build a game based around an underground cave system.
Some of it will be underwater and some of it will be dry. I specifically drew inspiration from Caves of Doom (A Mastertronic game that isn't that significant except that I got it with my 464)


and Scubaventure (Not a CPC game but I played a lot of apogee platformers which is why I know this game).



I had thought of a game where you do the levels once without water and then come back a second time with them filled with water.
The idea was that you would have to navigate differently the second time because the environment changed.
I ended up building levels where bits of the level were empty and other parts were filled instead which was much easier to design.

I feel that drawing inspiration from some games that weren't considered classics means you can easily learn from deficiencies in those games and avoid ending up building an inferior clone of some other game.
My biggest gripe with Caves of Doom (besides it being impossible to play with the keyboard) is that it requires pixel perfect movement and just about everything you touch instantly kills you.
You get a lot of lives, but it isn't enough unless you have a lot of practice or modify the level with the in-game editor (this is a super neat feature of the game actually).
With my game I tried to make sure you didn't have to have pixel perfect movement but instead there is something you can use to line yourself up with.
If you want to have pixel perfect movement you can probably play the game a bit faster but that's up to you.


Difficulty
Overall, I am happy with how the game works and the progression through the levels but it has been suggested that the game is a little bit too hard. I have some ideas around adding a little bit more health and giving a way to replenish lives, but I'm open to ideas for this.

The challenge with difficulty is that I don't want the game to be too easy (Xyphoe commented on his stream that a lot of the games were a bit too easy) but I also want the game to be hard enough to give players a challenge without making it unapproachable or impossible for most people.
I feel that because the CPC has a lot of very hard games there are people who are very good at hard games but not everyone wants a near impossible challenge.


Editor
Now we get to a bit more on my process. Each year I create a map editor to design my game levels (Except for Fitzroy and the Infestation which had autogenerated levels.).
I have noticed that my editors are very iterative in that I build a very similar editor thus writing similar (improved but similar) code each year for the same thing.
In response to this I decided back in April to write a library for my editor so that the bulk of the functionality could be contained in the library, and I wouldn't have to rewrite it every year for each game editor.
This certainly helped reduce the amount of time I spent on the editor however I still spent a fair amount of time because it was not complete when I started work on the game in July.
Next year it will be even better as the library is far more complete then when I started using it.


This of course raises the question why don't I just use Tiled and save the effort of writing my own? Part of it is that I enjoy writing the editor, I also don't write much C++, so this is a good opportunity to do so.
The editor also fits into my process really well (or my process fits around my editor, not sure if it makes a difference).


This is what my editor looks like (we can see most of the test level in this screenshot). The level is made up of 3x3 Mode 0 screens.
Generally, how a level is designed is that I draw in major level features and then I fill in each individual screen with content until the level is completely filled in.


I then play the level and find its completely unplayable but that is simply how the process works out.
Sometimes I will test screens individually (this is what I did for later levels) rather than waiting for the whole level to be complete.
This is useful when I'm not sure if what I am trying to do is possible.
This is what one of my levels looked like early on:


The part of my process that the editor really helps with is how I design the graphics. I do all my graphics in Retro Game Asset Studio (RGAS).
The format it uses is a JSON file and as pointed out by someone on the forum (don't remember who) you can read the file for your own automation purposes.

Here we have the tiles RGAS file open in RGAS.


From the editor I can select any of the graphics I have created in RGAS from the tile menu.


I can then plop them into the editor as I see fit.



This year I extensively used it to try the graphics out as I was designing them as the colours match what you will see in the CPC reasonably closely.




Here is me trying out some metal tile designs to work out which look best.Here is me trying out some metal tile designs to work out which look best.


Wincpctelera
I feel I can't mention my process without mentioning Arnaud's Wincpctelera.
This continues to be fantastic and makes it easy to quickly debug my game C code without having to drop down to the assembly.
It doesn't solve all the problems as some things don't happen that happen for the CPC version but it covers most things.

The wincpctelera version looks very similar as you can but it does play a bit faster which is both good and bad so you have to make sure it works on the CPC.


What's next
Looks like we made it almost to the end (or you skipped all the "boring stuff" to look at the what's next).
For next I would like to fix any bugs that I didn't fix in the initial development. Particularly the intermittent bugs which can result in the game crashing. I would also like to address the game difficulty which I think is a little bit too hard but without compromising the game thus making it too easy.
Also, there was one extra level I had hoped to make. If you look in the source code, you will find my test map that I showed earlier big1.c which is filling the space for a tenth level in the game. This might be a v1.2 but I plan to finish this map and add it to the game.

One final thought
While the editor works great for me to build levels in the game its even more interesting to see what other people can do with it. User generated content is often the best kind of content.
If people are interested, I can provide the editor and the RGAS graphics files and let people try build some levels.
If I had some complete levels, I could create a cpcwiki version of the game with a new set of levels, this would be something like an expansion or addon version of the game.
The only concern is that to really test the level you need to be able to compile the game which might be a bit of a barrier to entry.
An option would be to try extend the wincpctelera version of the game to read in my json map files but it does play a bit faster which while a reasonable way to test a level is possible it isn't perfect. Often a level is actually easier on the CPC version because it plays a bit slower.
Let me know if this is an interesting idea, graphics could possibly be enhanced this way to (I hope I can keep my green screen support though).


Change Log
v1.1 (Build 525)


       
  • Fixed memory corruption crash from stopping Arkos sound outside an interrupt.
  • Make sure you cannot go above top of screen.
  • Combined shoot and use to a single control.
  • Moved spike near top left entrance of middle right room to make room transition easier on second level.
  • Expanded the right most wall in the centre middle room to make room boundary clearer on second level.
  • Removed spike near bottom right room entrance on the intro level to make room change easier.
  • Made centre middle exit more obvious on the intro level to make room change easier.
  • Entrance to bottom mid room is now normal water on valves level to prevent instant death.
  • Fixed clipping through solid blocks underwater.
  • Added additional health to the game to make it slightly easier to play.
  • Fixed water boosting just off block bug.
  • Improved boost pixel off bug fix when water at top of screen.
  • Fixed bug where a tile can be drawn seemingly randomly on screen due to a futureYTile value of 255 (-1).
  • Fixed bug where a valve could be used multiple times to cheat valve objective action count.
v1.2 (Build 578)

       
  • Removed yellow pixels from the U character in the font.
  • Removed an unnecessary pipe from collect and place pipes level ( level 9 ).
  • Add a dialog to allow the player to accept whether they want to restore from checkpoint or restart the level at the beginning.
  • Play switch sound effect when activating checkpoint.
  • Moved checkpoint in health water level ( level 8 ) so that it is not above a spike.
  • Changed oxygen cylinders to oxygen water in health water level ( level 8 ) to make sure the level is possible when loading from checkpoint.
  • Reduced the length of Player.c function logicPlayer() by splitting the function into several smaller functions. This was to make it easier to compile the game on low memory systems. This also provided a significant memory saving (when the right code was moved out).
  • Reduced number of points that can be gained on the first level.
  • Reduced number of points that can be gained on the second level.
  • Extra lives for points can only be awarded on easy and normal difficulties.
  • On the end of each level if the player has accumulated about 30K points an extra life will be awarded.
  • Added brutal difficulty (previously was hard).
  • Normal difficulty becomes hard difficulty (same as normal but no checkpoints).
  • Checkpoints can now only be used in easy and normal difficulty.
  • Replaced cpct_getScreenPtr with cpctm_screenPtr when used with constants to save memory.
  • Replaced cpct_px2byteM0 with cpctm_px2byteM0 when used with constants to save memory.
  • Display pipe count in collect and place pipes objective level.
  • Added collect and place pipes map to game as level 9.
  • Added checkpoints to game levels.
  • Draw an active checkpoint graphic onscreen if the player has activated it. (Only one checkpoint can be active at a time)
  • Created graphics for checkpoints (both active and inactive).
  • Added a checkpoint interactable that the player can activate to save their place within a level. If they die after a checkpoint has been saved it will start the player at the checkpoint location instead of the start of the level. (The state of the level and the player items will remain the same.)
  • Implemented activating a linked interactable when placing pipe.
v1.3 (Build 598)

       
  • Fixed bug where following collision with the player an enemy would start moving backwards.
  • Fixed unavoidable enemy in water health level (level eight) that could occur after the cracked tiles were removed.
  • Added sound for placing a pipe (repair sound).
  • Fixed a bug where the player sprite would cycle between walk and jump when standing on a floating platform.
  • Added sound when an enemy is hit by a projectile.
  • Added sound for opening a door.
  • Add player walking sprite to animate the player when walking on land.
  • Add player sprite for when they are jumping or falling on land.
  • Implemented flipping enemy sprites (enemies now turn around).

ervin

I wish I could give you more than one Like for that fantastic post.
Congratulations on finishing a very good game, and on your most successful game yet.
It's very encouraging to watch your games getting better each year.
Well done!!!

Arnaud

Congratulation once again for your 7th place, here small ideas or improvements :

Gameplay:

  • Avoid when changing rooms direct walking on trap / monster or death.
  • Better identification of exits or add walls to the places where you can't get out the room.
  • Remove some traps in the underwater areas, managing the limited oxygen is already complicated.
  • Add a small life bonus on some objects.
  • When we die, restart in the current room.
  • I think the Use key can be removed, the Shoot key can be used instead (and not shot if in front of interactive object).

Visual:

  • Add a small animation for the movements.
  • Mirror enemy sprite according its direction.
  • Make a little change of palette on interruption to make a visual effect of water.

If you make a sequel  ;D, I would have liked a game like Antiriad (or Metroid) where you unlock zones (and go back) as you acquire (and keep) equipment.

skylas

Congratulations! I think it is the best of your games so far!
Web: https://amstradsakis.blogspot.com
Twitter: https://twitter.com/AmstradSakis
My programs (only BASIC):
RETRO-LOADSHEET ON AMSTRAD CPC!
PENALTY KICKS!
CAPITAL QUIZ!
CAPITAL QUIZ 2! (Reverse edition)
HEADS OR TAILS (ΚΟΡΩΝΑ/ΓΡΑΜΜΑΤΑ)
HEART CHASER 1,2,3!
BARBOUTI!
STROOPIE!
BUDRUMI!
ART WAR!
BATTLE OF LENINGRAD!
AMSTABOO!
RODOLFO SKYLARRIENTE!

awergh

#4
Thats for all the kind words and suggestions. :)

I have a fix for the tile that randomly drew on the screen (usually to the right edge).




Turns out that because I was not setting futureYTileTop = 0 in Player.c as below that futureYTileTop would be 255 when processCell was called on the next line.
This would obviously get the wrong cell, in the first level where this happened you would jump and end up with the oxygen as well as the tile being drawn when it shouldn't be.



if (futureYTileTop == 255)
{
   // Check that the last cell in the room above is empty.
   if (processCellInRoom(occupiedXTileLeft, SCREEN_TILES_Y - 1, currentRoomX, currentRoomY - 1) == CELL_EMPTY &&
      processCellInRoom(occupiedXTileRight, SCREEN_TILES_Y - 1, currentRoomX, currentRoomY - 1) == CELL_EMPTY)
   {
....
   }
   else
   {
      // Set the position to the maximum position so you can scrape right against the top of the screen when jumping.
      player.object.position.y = 0;

      // Solid above the player so set velocity to 0 so they start falling.
      player.object.velocity.y = 0;

      // Reset futureYTile for the top of the screen.
      // Otherwise processCell will have a 255 which will get
      // the wrong cell and cause it to draw the wrong tiles on screen.
      futureYTileTop = 0;
   }
}

// Process the content of the future cells and perform collision detection.
occupiedXFutureYTopLeft = processCell(occupiedXTileLeft, futureYTileTop);
occupiedXFutureYTopRight = processCell(occupiedXTileRight, futureYTileTop);


Quote from: Arnaud on 15:05, 14 November 21Gameplay:
Avoid when changing rooms direct walking on trap / monster or death.
There shouldn't be any monsters when you walk into a room, I thought I avoided that.
I would have said there wasn't anywhere you could die instantly when changing rooms but you showed me otherwise yesterday.
Every trap should be avoidable as long as you know its there, I tried to avoid being unfair but I might not have succeeded every time.
I was probably particularly unforgiving in the last level.

Quote
Better identification of exits or add walls to the places where you can't get out the room.
Are you meaning places like this where you can't see where to go.
Perhaps that was a bit harsh for in the first level in this case.


I do have space for a few more tile sprites, would an arrow be helpful or is there a more subtle way to do it?
I probably should have put the strawberry right next to it.

Quote
Remove some traps in the underwater areas, managing the limited oxygen is already complicated.
Add a small life bonus on some objects.
There is a health item, I have clearly been a bit stingy in using it.
I will have a look to add it in more than just two levels.

Quote
When we die, restart in the current room.
The trouble is I don't know what is a safe position.
I suppose I could save the position as soon as you change room though and then restore you to that position (but could that position still be safe).
While I have avoided putting enemies next to entrances it is perfectly possible to create levels that do that.
I was thinking more along the lines of letting you replenish your lives by getting sufficient score but I'm not quite sure how much and I don't think there is an equal amount of points available in each level.
Alternatively I decide that one item (such as the circuit board) gives you an extra life. I think Jazz Jackrabbit does that but I might be wrong there.

Quote
I think the Use key can be removed, the Shoot key can be used instead (and not shot if in front of interactive object).
At the moment I can't think of a time where you want to shoot when you're right next to a switch or something.
Even if there was it would probably be an acceptable difficulty for the player to manage.
Crystal Caves does a similar thing with using the shoot key for use as well with no noticeable down sides.
This would make the game playable with a two button joystick which would be nice (even if I don't have one).

Quote from: Arnaud on 15:05, 14 November 21
Visual:
Add a small animation for the movements.
I've never done animation in my games, it is something I should figure out.

Quote
Mirror enemy sprite according its direction.
Making enemies that are recognisable like snails means this is a bit obvious.
I thought about flipping a little but not enough to do anything about it (time running out and such).
I didn't want to store the sprite for every enemy as that is 15*64 bytes which seemed like too big a sacrifice.

I could have a temporary enemy variable (only 64 bytes) which I memcopy the enemy sprite into and flip the temporary variable before drawing it (depending on direction of the enemy).
I thought this might be slow but I didn't try it.

The other option I noticed was cpct_drawSpriteHFlip_at but that requires 256 bytes for the flip table but I didn't actually get around to trying it.

Quote
Make a little change of palette on interruption to make a visual effect of water.
Is it still a little change if I have to change many of the graphics though? I did something like this for Fitzroy in the dark but how many of my 16 colours would I have to sacrifice for this to work?

Quote
If you make a sequel  , I would have liked a game like Antiriad (or Metroid) where you unlock zones (and go back) as you acquire (and keep) equipment.


I've never made a direct sequel to one of my games. I do have an idea for a Fitzroy in the Dark sequel but I might have a completely different idea by the time July comes around next year.
In this game having not implemented carrying inventory items across levels I decided it allowed for more interesting level design when you had to go find items every level.
Haven't played Antiriad or Metroid but doing a game with zones that you come back to could be interesting. Not sure what you need to do in order to make the zones interesting the second time unless they are partly locked because you don't have sufficient items or the hoards of enemies are too challenging the first time.
It could always be your next game .

Arnaud

#5
Hi,

Quote from: awergh on 13:35, 15 November 21Are you meaning places like this where you can't see where to go.
I do have space for a few more tile sprites, would an arrow be helpful or is there a more subtle way to do it?.

Exactly, you could make something like that :
[attach=1,msg209167]

Quote from: awergh on 13:35, 15 November 21I suppose I could save the position as soon as you change room though and then restore you to that position (but could that position still be safe).
Yes it's the good solution

Quote from: awergh on 13:35, 15 November 21
The other option I noticed was cpct_drawSpriteHFlip_at but that requires 256 bytes for the flip table but I didn't actually get around to trying it.
Use this compilation argument :
Z80CCFLAGS    := --peep-file ${CPCT_PATH}/tools/sdcc-3.6.8-r9946/peep/z88dk_speculative_peepholes.def --max-allocs-per-node 10000 --opt-code-size

Highest address   = 0000A59C
to
Highest address   = 0000A3E1

You have 443-Bytes free to use the flip table  :D

Quote from: awergh on 13:35, 15 November 21Is it still a little change if I have to change many of the graphics though? I did something like this for Fitzroy in the dark but how many of my 16 colours would I have to sacrifice for this to work?
Yes a color is sacrified, but it seems this color is used only for water.

awergh


Quote from: Arnaud on 19:53, 15 November 21[size=78%]Exactly, you could make something like that :[/size]
I will have a look to see if there are some places I should adjust in the earlier levels.
I don't think it is too unreasonable to do it occasionally during the harder levels though.
I think when I made the first couple of levels I had a greater intention to do secrets but later on I didn't bother.

Quote from: Arnaud on 19:53, 15 November 21Use this compilation argument :
Code: [Select]
Z80CCFLAGS    := --peep-file ${CPCT_PATH}/tools/sdcc-3.6.8-r9946/peep/z88dk_speculative_peepholes.def --max-allocs-per-node 10000 --opt-code-size

Highest address   = 0000A59C
to
Highest address   = 0000A3E1

You have 443-Bytes free to use the flip table 

Always happy to have extra memory especially if its easy :) but what does that command actually do? Usually you don't get extra free memory for free?

Quote from: Arnaud on 19:53, 15 November 21Yes a color is sacrified, but it seems this color is used only for water.
Are you suggesting to in the interrupt change the Cyan to Blue or a different blue (eg bright blue) or do you mean something slightly different.
I don't think cyan is used a lot but it is used a few times I think (without actually looking right now).

Arnaud

Quote from: awergh on 14:09, 16 November 21
Always happy to have extra memory especially if its easy :) but what does that command actually do? Usually you don't get extra free memory for free?
It uses peephole definition provided with sdcc (Wikipedia : Peephole optimization involves changing the small set of instructions to an equivalent set that has better performance)
and compilation code optimization for size

Quote from: awergh on 14:09, 16 November 21
Are you suggesting to in the interrupt change the Cyan to Blue or a different blue (eg bright blue) or do you mean something slightly different.
Yes that it.




awergh

#8
Quote from: Arnaud on 15:05, 14 November 21When we die, restart in the current room.
I had some more thoughts about this.
While enemies could be a problem I am going to assume map design will prevent this from happening and I can use activateCollisionTimer() to give the player temporary immunity in case there is an enemy there.

However I have a more difficult problem, if the initial cell in the room is underwater then it might be unsafe.
Is the best plan a compromise, when the player has no oxygen when they die to move back to the start of the level (the level is reset) but if they have oxygen then restart them at the start of the room (regardless of whether the cell is underwater or not).

Initially I had considered restarting the level if the start of the room is underwater but I need to take into account oxygen.
You could get into a position where you die from 0 oxygen and respawn at the start of a level where you are safe but everything else kills you if you move from there as oxygen may not be accessible from that point in the level.


I don't want to have to rely on level design to fix all my problems, after all someone else might design a level sometime :) .


Arnaud

#9
I haven't thought about oxygen underwater, there is not easy solution finally  ::)
Maybe a checkpoint ?

awergh

Quote from: Arnaud on 19:27, 22 November 21
I haven't think about oxygen underwater, there is not easy solution finally  ::)
Maybe a checkpoint ?
Yes seemed like a good idea and then you realise there is an edge case that makes it hard to implement without side effects.
However a checkpoint is an interesting idea. While I already have 31 interactable objects one of those is part of the machinery which could be changed to a tile (I think) which would free up the one we need.
It can't be like the most traditional checkpoint where you restore the game to the state at that checkpoint instead the player would move back to the location of the checkpoint when they die.
We would have to rely on map design to make sure the checkpoint is safe but I think this is an OK assumption.
If they have no oxygen when they died they will have no oxygen at the checkpoint. This is something that they will have to decide as the checkpoint would be optional.


I prefer the idea of making checkpoints only useable on easy and normal though so hard can remain the same challenge.
This would be a v1.2 feature I think. I am hoping to combine the shooting and use controls and then do a bit more testing and release that (mostly bug fixes) as v1.1.
Then more substantial things can go in v1.2.

Nesu

#11
Congratulations for your improvements in game quality and contest results!
I think I've found some "secret screens" :
[attach=1,msg209577]
Just jumping to the right immediately at the beginning of the game (or a new life), FitzRoy can access to the secret area "Fitzroy Goes Wrongscreen" :)

Some random recomendations for your code:

I've noticed many calls to cpct_getScreenPtr using only constants as parameters. You only have to do the calculation in the CPC if any of the parameters is a variable.
When all parameters are constants use cpctm_screenPtr macro instead. Applying that to files Hud.c  Dialog.c and Menu.c, +160 bytes can be saved.

drawCellAtPositionSafe() function (at Map.c) is not used anywhere in the game. It's small but can be removed. Didn't check if there is more unused code.

I would not recommend making functions as long as logicPlayer() (at Player.c). SDCC does not like functions wich are too long or complex.
Maybe is because my PC is quite old, but it refused to compile logicPlayer. I had to split the function into two separate functions in order to succesfully compile the code.

Keep on improving! 

awergh

#12
Quote from: Nesu on 22:03, 25 November 21Congratulations for your improvements in game quality and contest results!
I think I've found some "secret screens" :

Just jumping to the right immediately at the beginning of the game (or a new life), FitzRoy can access to the secret area "Fitzroy Goes Wrongscreen"
I like the idea of a secret area but yes clearly a bug there which I have fixed this evening (makes it into v1.1).
Perhaps Fitzroy goes Wrong is a future game title but I'm not sure what it would be about.

Quote
Some random recomendations for your code:

I've noticed many calls to cpct_getScreenPtr using only constants as parameters. You only have to do the calculation in the CPC if any of the parameters is a variable.
When all parameters are constants use cpctm_screenPtr macro instead. Applying that to files Hud.c  Dialog.c and Menu.c, +160 bytes can be saved.
Recommendations are good (my code doesn't get reviewed much). I didn't realise that I'll make sure to look into that soon and keep it in mind in the future.


Quote
drawCellAtPositionSafe() function (at Map.c) is not used anywhere in the game. It's small but can be removed. Didn't check if there is more unused code.
That is one I did know about, this was a leftover that didn't get completely removed because I didn't completely run out of memory but I have removed it recently.
Sometimes I use cppcheck to find things like this but it doesn't always give helpful suggestions.


Quote
I would not recommend making functions as long as logicPlayer() (at Player.c). SDCC does not like functions wich are too long or complex.
Maybe is because my PC is quite old, but it refused to compile logicPlayer. I had to split the function into two separate functions in order to succesfully compile the code.

Keep on improving!

Thanks thats the plan  ;D , yes logicPlayer() ballooned in size and I didn't really think about how big it might be getting.
Looks like I can't compile my game on a Linux VM with only 1GB RAM (it gets stuck at Player) so it might be the same problem as you have.




I have managed to reproduce the game crashing bug (I hope there is only the one) in the attached snapshot.
If you use the valve on the left and then pick up the oxygen it will corrupt the screen and crash.
Something is causing it to write over memory but I am not sure what.

awergh

Haven't made any progress with the bug.
It seems that you pickup the item and land on the solid tile there will then be screen corruption and or crashing.

If I put a breakpoint in inventorycontainsItem(CellType) 0x3BF3 it will stop as it picks up the item.
I then had a breakpoint at logicPlayer 0xCC7 and ran through that 5 times.
After that when it does a vsync it will crash.

I assume vsync is not actually the cause of the problem and instead something happened in the previous iterations of the game loop.
However no idea how to work it out. I assume something is writing to where it shouldn't which is OK for a little while but then results in catastrophic failure.

Nesu

Quote from: awergh on 12:10, 26 November 21
I have managed to reproduce the game crashing bug (I hope there is only the one) in the attached snapshot.
If you use the valve on the left and then pick up the oxygen it will corrupt the screen and crash.
Something is causing it to write over memory but I am not sure what.
I think the crash maybe is caused by corruption of the music. Using this snapshot in my PC I can't hear any sound using WinApe. Using RVM I can hear the music but it sounds bad.
At the point of the snapshot, changing soundMode (Address A574) to 1 (music off) allows the game to continue when picking the oxygen. Being more selective, just disabling the call for PLAYER_ARKOS_PLAYSOUNDEFFECT ( writing '0' to bytes 457C-4586 ) also seems to work . I think that the snapshot has already something corrupted about the sound.
While reviewing I found in interruptHandler() that you are playing sound every 7 interrupts, as you are using post-increment soundCounter_++. If you intended to play every 6 interrupts, I think the only consequence of this should be a slower music, not a corruption, but I'm not sure.
Also, same as with cpct_getScreenPtr, found several calls to cpct_px2byteM0 function with constants as parameters, instead of using cpctm_px2byteM0 macro
As a way to continue searching the problem I would suggest to compare the snapshot of the error, with a previously saved snapshot with the music playing properly. So you can find which memory addresses has changed.  Then use the map file to see what's in there.




awergh

#15
Quote from: Nesu on 00:18, 29 November 21
I think the crash maybe is caused by corruption of the music. Using this snapshot in my PC I can't hear any sound using WinApe. Using RVM I can hear the music but it sounds bad.
At the point of the snapshot, changing soundMode (Address A574) to 1 (music off) allows the game to continue when picking the oxygen. Being more selective, just disabling the call for PLAYER_ARKOS_PLAYSOUNDEFFECT ( writing '0' to bytes 457C-4586 ) also seems to work . I think that the snapshot has already something corrupted about the sound.
While reviewing I found in interruptHandler() that you are playing sound every 7 interrupts, as you are using post-increment soundCounter_++. If you intended to play every 6 interrupts, I think the only consequence of this should be a slower music, not a corruption, but I'm not sure.
Also, same as with cpct_getScreenPtr, found several calls to cpct_px2byteM0 function with constants as parameters, instead of using cpctm_px2byteM0 macro
As a way to continue searching the problem I would suggest to compare the snapshot of the error, with a previously saved snapshot with the music playing properly. So you can find which memory addresses has changed.  Then use the map file to see what's in there.


Oops I was testing with WinAPE muted so I didn't notice it wasn't working.
Comparing two binaries (should have done this programmatically but didn't) most of the differences are in Arkos or things that are expected to be different (counters, high scores, player)


I am using PlayerAkm.asm for music with a slight modification for volume as suggested by Targhan.
Does Arkos use self modifying code at all?
http://www.julien-nevo.com/arkostracker/index.php/forums/topic/adjusting-music-volume/


Yes looks like I made a mistake with music interrupts but can't change it now as that is the way the music is expected to be played (once the game is out that can't change  :) ). Thanks for pointing out yet more macros for me to look out for, I guess I haven't really looked into the macros in cpctelera.


I've attached the map file for reference.

Arnaud

Quote from: awergh on 11:22, 29 November 21
Does Arkos use self modifying code at all?

Yes Arkos2 use self modifying code

awergh

That makes it a bit harder to work out what has changed then.
Is it possible that I am using Arkos wrongly.

In runGameSession() I call initSound() which will call PLAYER_ARKOS_INITSOUNDEFFECTS.
Next I call playMusic() which will cause PLAYER_ARKOS_INIT to be called in the interrupt handler.

At the end of the game session stopSound which calls PLAYER_ARKOS_STOP will be run.

Is it possible I should only initialise once instead of doing it every game session?
I could have an empty subsong to stop the music but I assume that PLAYER_ARKOS_STOP should be enough.

Arnaud

You can try to initialize sound before starting your interruption, this is what is done in my games.

    // Disable the firmware to prevent it from interfering with setVideoMode.
    cpct_disableFirmware();

    // Set video mode to mode 0.
    cpct_setVideoMode(0);

    // Initialises arkos 2 sounds.
    initSound();

    // Starts playing the game music.
    playMusic(0);
   
    // Setup interruptHandler for keyboard scanning.
    cpct_setInterruptHandler(interruptHandler);

awergh

Still experimenting but maybe progressing.
It is possible to reproduce it if you start a game and exit straight away (proceeding to do that a few dozen times will eventually cause it to crash).
Return TRUE from exitDialog() and commenting out the press any key functions in displayEndScreen() helps make this faster (also speeding up WinAPE can help for this).

Now my first attempt to only initialise Arkos once didn't work as it still reproduced the problem.
When I commented out all Arkos functions it appeared that it worked no problems.
I haven't tested enough but when PLAYER_ARKOS_STOP() only was commented out it appeared to work but not fully sure on that.
Maybe the empty subsong instead of stopping arkos is the way to go instead of using the stop function.

Arnaud

#20
I have not to reproduce the problem, but when in your menu you can just set  :

soundMode = NO_SOUND_DISPLAY_MENU;

to simply just not to call playSound() in your interruption.

And when game start, put the right value :

soundMode = SOUND_MODE_ON;

Thus not need for the function PLAYER_ARKOS_STOP()

awergh

I had a go with not using PLAYER_ARKOS_STOP() and updating soundMode to disable sound but it results in a hanging note which is much worse.
Tomorrow I will have a go with the empty subsong idea.

Nesu

Found that stopSound() calls PLAYER_ARKOS_STOP() outside interrupts. PLAYER_ARKOS_STOP() must be called only inside intrerrupts, as it changes the stack pointer for its calculations:_PLY_AKM_STOP::
PLY_AKM_STOP: ld (PLY_AKM_SENDPSGREGISTEREND+1),sp
  (...)
  jp PLY_AKM_SENDPSG

PLY_AKM_SENDPSG: ld sp,#PLY_AKM_TRACK3_DATA_END
(...)     

Arnaud

#23
Well done @Nesu  :)

@awergh I discover my C binding tutorial has big flaws  :'(

Like CPCTelera do, it shall have two functions type, interrupt safe (to use in interrupt) and other that disable interrupt :

extern void PLAYER_ARKOS_PLAY();
extern void PLAYER_ARKOS_STOP();

// Interrupt safe
extern void PLAYER_ARKOS_PLAY_IT();
extern void PLAYER_ARKOS_STOP_IT();


_PLAYER_ARKOS_STOP_IT::
    jp   PLY_AKM_STOP
_PLAYER_ARKOS_PLAY_IT::
    jp   PLY_AKM_PLAY
   
_PLAYER_ARKOS_STOP::
    di
    call PLY_AKM_STOP
    ei
    ret
_PLAYER_ARKOS_PLAY::
    di
    call PLY_AKM_PLAY
    ei
    ret


StopSound function modification :

void stopSound()
{
    if (soundMode)
    {
        // Turns off the sound if enabled.
        soundMode = SOUND_MODE_OFF;

#ifndef WINCPCTELERA
PLAYER_ARKOS_STOP();
#endif
    }
}



awergh

Thanks both of you as I have now fixed the problem  ;D  (I really appreciate it as intermittent bugs are the hardest to fix.).
I didn't see your interrupt safe stop so I did without it but I'll have a look for v1.2.
I have updated the itch.io page with the binaries for v1.1 along with the source code.
I have not incorporated many the suggests as I wanted to fix the bugs first, for v1.2 I will be a bit more adventurous with the changes which is also much more fun  :) .

Change Log for v1.1 (Build 525)

- Fixed memory corruption crash from stopping Arkos sound outside an interrupt.
- Make sure you cannot go above top of screen.
- Combined shoot and use to a single control.
- Moved spike near top left entrance of middle right room to make room transition easier on second level.
- Expanded the right most wall in the centre middle room to make room boundary clearer on second level.
- Removed spike near bottom right room entrance on the intro level to make room change easier.
- Made centre middle exit more obvious on the intro level to make room change easier.
- Entrance to bottom mid room is now normal water on valves level to prevent instant death.
- Fixed clipping through solid blocks underwater.
- Added additional health to the game to make it slightly easier to play.
- Fixed water boosting just off block bug.
- Improved boost pixel off bug fix when water at top of screen.
- Fixed bug where a tile can be drawn seemingly randomly on screen due to a futureYTile value of 255 (-1).
- Fixed bug where a valve could be used multiple times to cheat valve objective action count.

Powered by SMFPacks Menu Editor Mod