Fitzroy goes sweepingNow that the CPCRetroDev games and results have been released it is time to post my new game Fitzroy goes sweeping, give some thoughts and hopefully receive some useful feedback. I will try to avoid writing an essay like last year :P .
Besides I didn't really get an answer to most of my questions last year so it's probably for the best not to ask too many questions.If you don't want to read all my blathering (actually doesn't look like much) feel free to skip to the bottom with the screenshots, game and user manual (the one that I didn't submit in the zip fil
e :doh: )
.BackgroundOnce again I made a game about Fitzroy. I have somewhat fallen into the Roland trap in that my games do not relate to each other very much but that's what happens when you try different genres. (The name is somewhat inspired by Roland goes digging and Roland goes square bashing but has more relation to Roland Ahoy, admittedly I haven't actually played any of those games.) This year I wrote a turn based strategy game based on sea mines. (Last year was turn based as well but it may not have been noticeable because turns were fast) This is very loosely related to last year in that Fitzroy in the Dark featured mines in one of the levels although not of the naval variety.
ApproachI approached this game by doing some research about a subject matter instead of having a definitive game idea. The trouble with building a game from a subject is that I didn't figure out the game genre until about September (I started writing code on the game in July). The side effect of this was that I had to rework existing code once I worked out it was a strategy game. Given my tendency to be pragmatic and move on once something works instead of doing it the best way my design is somewhat compromised. The other problem was a procrastinated with the research and didn't finish it all in June\July otherwise I might have picked a genre sooner.
Submission and resultsRegardless, I am happy with what I submitted but the game is a little bit clunky and you really need the user manual to understand how to play it. Unless of course you like to discover things for yourself in which case go ahead play without the user manual :P .
If you have any feedback on how to make the game better feel free to offer suggestions and I will see what works and submit an updated v1.1.
At the time of initial writing (I wrote it in advance because why not) I don't know how I went but I am predicting I didn't do especially well and probably didn't make the top 36 which is a little bit unfortunate. If I hadn't forgot to submit the user manual :doh:
with my game the judges might have been able to figure out a bit better how to actually play my game. Oh well I'm sure I won't forget it next year :)
. (The next day: yes I was right......, not much more to say about it really).Having written two games that some people have had difficulty figuring out I might make a more accessible\approachable game next year.
How to playRead the user manual it explains how to place units, move around and defuse\destroy mines.
If something needs further explanation I will add it here and update the user manual as well.
If the game gets in an odd state try skipping a turn as that can usually fix the problem.
So I quickly wrote a bit of a summary that may end up in v1.1 but the user manual is still more detailed:
The general aim is to clear all the sea mines in the water.
Some of the levels also have additional objectives such as leading ships to safety or rescuing some cargo.
The objectives are listed on the screen before the unit selection screen as well as some of the units involved
in playing the level.
You deploy a unit to cell within the starting zone (it will tell you if you are selecting a cell outside the zone).
The deploy\switch key (space by default) can switch between showing the cursor square or it can directly move the unit.
When you move a unit you can only move one cell per turn. If you have the cursor shown you can move it to a cell
and press the target key (T by default) to set a target to move to over multiple turns.
To defuse\action with a unit you will have to have it selected (black on the hud around the unit) and switch to it (space) and then press the defuse\action key (X by default).
For example with a diver you can go next to a mine and press X and it will defuse the mine over
a number of turns. If you only have one unit all the turns will be very quick. However if you have multiple units deployed (more then likely)
you will need to complete all the turns for each of the units. If a unit is busy (defusing or moving to a target) you
will not need to do anything to complete its turn (it will remain red on the hud).
Things to fix and v1.1I have a few things to fix for v1.1 but feedback would be helpful on the best way to fix some of them. Of course if there are actual bugs I will fix those too assuming they aren't features :P
- The game does not tell you why you failed a level and just kicks you back to the level screen. (Fixed v1.1)
- Game money is broken, I discovered just before submitting I had a game breaking bug where if you lost a level your money would not get refunded. Also if you press escape on the unit selection screen and then press no you also lose your money. Instead of fixing this I added 22000 to all difficulties rather than trying to fix it. (Fixed v1.1)
- The game is a bit clunky, now I don't know the best way to fix this one. That is probably what I am most interested in getting feedback on. The game includes two movement modes, either moving a unit one cell at a time (each move is a turn) or you can move the cursor square and press the target key (default T) to move the unit multiple cells over a number of turns. (Improved v1.1)
- Maybe add some help to the game, this was planned but skipped as I ran out of time. (Improved v1.1)
- Level balancing and difficulty. With time running out I didn't really do much testing of the game at all. I am hopeful that all the levels are possible but there is no guarantee. If there is something that is unfair or game breaking I'd like to know so I can adjust the map.
- Sound was problematic, this year I managed to have sound but for whatever reason I could not seem to add any additional sounds as nothing would play. In the end I gave up and I think next year I will use Arkos Tracker 2 as it seems to be better supported even if it does not integrate with cpctelera.
Changelogv1.1 (Build 447)
- When you lose a level it will now show you which objective you failed causing you to lose the level before returning to the level screen.
- Added one unit must survive objective to objective screen.
- Player must complete at least one level to get a high score.
- Separate money counts for game and level session.
- No longer lose money if press escape on unit selection screen.
- Restored starting money to previous defaults.
- Can now use the unit action on a unit without it first being made active (deploy).
- Can now set a target using the deploy key instead of the target key.
- The target key has been removed as it is no longer required.
- Fixed mine explosion radius at screen edge.
- Fixed screen corruption when switching units after scrolling.
- Added help mode to show possible cells that a player can move to.
- Added help mode to show possible cells that a player can apply actions (defuse) to.
Hi awergh.
Thanks for the detailed info, I'm looking forward to playing this one!
So I thought I had done enough things for v1.1 but I found a game breaking bug. :'(
Turns out it was in v1.0 but I hadn't found it in my testing.
Unfortunately it doesn't show up on wincpctelera so I have to figure out the cause the hard way.
What happens is I switch unit which moves the screen and then the screen gets corrupted and the game crashes back to basic.
I do have a snapshot and it starts going bad in drawHud() in particular convertNumberToString() for displaying turns on the screen but I suspect the badness has already happened by then.
Moving the program counter to the end of drawHud() just meant I got different screen corruption.
Are there any good strategies for working out the cause? I can fairly consistently reproduce the error but I don't have a set of actions that will definitely cause it.
Is the screen corruption present when removing DrawHud function ?
Quote from: Arnaud on 11:53, 06 December 20
Is the screen corruption present when removing DrawHud function ?
Yes I tried commenting it out so the hud is never drawn but it still corrupted.
Not sure if the corruption happened faster because I switched units more or not.
I don't see the bug with v1.0 cpcretrodev.
I have 3 units on screen and i switch betwen them without crash how reproduce the problem ?
So I don't have an exact reproduction but I've been consistent with 4 units (2 of each diver).
I then move around the 4 units (setting targets that are a decent distance away, occasionally defusing mines (don't know if this is important)), I think the important thing is that the units are on different parts of the screen.
Then sometime seemingly randomly I will switch to a different unit (the same as any other time as far as I can tell) and it will crash to basic.
I have attached my sna file (v1.1) all you need to do is switch unit and it will crash straight away.
Here snapshot of V1.0 with same game configuration, i don't see the bug.
I moved the units around in your snapshot a bit more so now its one unit switch away from crashing.
This is the problem I don't have an exact reproduction but by moving enough I can get the issue consistently.
Some ideas :
- Add --max-allocs-per-node 10000 in Z80CCFLAGS : adding this line solve unexplainable crashes in one of my project, i always use it now. No idea why.
- Correction of compilation warnings (ex : src/MapScreen.c:98: warning 112: function 'exitDialog' implicit declaration)
- Create a game start configuration ready to test with units already placed
Adding --max-allocs-per-node 10000 didn't seem to make a difference or at least I managed to get the same problem.
I'm a bit slack with warnings for my games, this is slightly amusing to me as I spent much of my day at work fixing warnings.
Yes I am currently experimenting with commenting out as much code as possible to see narrow down the issue.
My suspicion is it is either to do with how I am moving the screen (this was super fiddly to get right) or with how I am setting targets and running the logic to move units to the target.
Quote from: awergh on 10:15, 07 December 20
I moved the units around in your snapshot a bit more so now its one unit switch away from crashing.
This is the problem I don't have an exact reproduction but by moving enough I can get the issue consistently.
Hi, I've been doing a little bit of research, hope it can help.
With this snapshot, when space is pressed, clearIndicator() [Player.c ] calls drawCell() with wrong value for x
src/Player.c:39: drawCell(moveIndicator.x - xScroll, moveIndicator.y - yScroll, cell)
moveIndicator.x-xScroll=-1 so drawCell is called as drawCell(0xFF,0x16,0x00)
That causes a wrong memory calculation at drawCell and a bunch of zeros are written over the program code.
Quote from: Nesu on 15:39, 10 December 20
Hi, I've been doing a little bit of research, hope it can help.
With this snapshot, when space is pressed, clearIndicator() [Player.c ] calls drawCell() with wrong value for x
src/Player.c:39: drawCell(moveIndicator.x - xScroll, moveIndicator.y - yScroll, cell)
moveIndicator.x-xScroll=-1 so drawCell is called as drawCell(0xFF,0x16,0x00)
That causes a wrong memory calculation at drawCell and a bunch of zeros are written over the program code.
Brilliant ;D ;D ;D
, you are absolutely right I was passing negative numbers to drawCell where I don't do any error checking.I really appreciate finding the problem for me as I might have taken a few more days to find it.
These are the two functions I changed to add some error checking for
moveIndicator.x - xScroll and moveIndicator.y - yScroll.And I just noticed a bug, whoops (fixed it in the snippit), which means I'll wait until tomorrow to release v1.1 so I have time to test it since somehow it is 1:56AM :P
.
void clearIndicator()
{
u8 cell = MAPA(moveIndicator.x, moveIndicator.y);
u16 mine = 0;
u8 scrolledIndicatorX = moveIndicator.x - xScroll;
u8 scrolledIndicatorY = moveIndicator.y - yScroll;
// Make sure it is within bounds.
if (scrolledIndicatorX < SCREEN_TILES_X && scrolledIndicatorY < SCREEN_TILES_Y)
{
// Draw over the old position for the move indicator.
drawCell(scrolledIndicatorX, scrolledIndicatorY, cell);
// If the cell has a mine then we will need to make that is redrawn as well.
if ((mine = findMine(moveIndicator.x, moveIndicator.y)))
{
u8* mapMemory = cpct_getScreenPtr(CPCT_VMEM_START, xPositions[scrolledIndicatorX], yPositions[scrolledIndicatorY]);
drawMine(mapMines[mine], mapMemory);
}
}
}
void drawCursor(u8 xScroll, u8 yScroll)
{
// Make sure it is in within bounds.
if (displayCursor && (moveIndicator.x - xScroll) < SCREEN_TILES_X && (moveIndicator.y - yScroll) < SCREEN_TILES_Y)
{
u8* memory = cpct_getScreenPtr(CPCT_VMEM_START, (moveIndicator.x - xScroll) * TILE_WIDTH_BYTES, (moveIndicator.y - yScroll) * TILE_HEIGHT);
// Normally shows the standard indicator which is currently white on all levels expect plant levels.
cpct_drawSpriteMasked(G_cursor_white, memory, TILE_WIDTH_BYTES, TILE_HEIGHT);
// Check if the cursor is being used for adding a new unit to the map.
if (selectedUnitSlot != 255 && units[selectedUnitSlot].deployed == FALSE)
{
// Check if the move indicator is outside of the zone.
if (level_zone[currentLevel] == NULL ||
(moveIndicator.x < level_zone[currentLevel][0] || moveIndicator.x > level_zone[currentLevel][2] ||
moveIndicator.y < level_zone[currentLevel][1] || moveIndicator.y > level_zone[currentLevel][3]))
{
// If the indicator is outside the zone display a red slash over the top of the cursor.
cpct_drawSpriteMasked(G_unit_destroyed_x, memory, TILE_WIDTH_BYTES, TILE_HEIGHT);
}
}
}
}
Having tested it again and it is all good so now I can release v1.1 :) . See first post attachments for v1.1 game and manual.
I haven't fixed all the bugs but I have done a block of changes that I am happy with and then I can hopefully include any feedback in v1.2 along with a few more bug fixes.
The game should be a little bit less clunky as you no longer have to use the deploy key (space by default) before you can perform an action such as defusing.
Also removing the dedicated travel key and using the deploy key to set a target feels a lot more natural I think.
To provide some clarity on where units can move and what cells are affected by actions I have added a help mode.
You can toggle between help off, move help (screenshot 3) and action help (screenshot 4).
In move help it will show which of the four directions a unit can move (all squares along a units edge must be passable to move though).
In action help it will show the cells that a unit will affect if you use the action key (eg defuse).
I have included an updated copy of the source code in case anyone is interested.
Changelogv1.1 (Build 447)
- When you lose a level it will now show you which objective you failed causing you to lose the level before returning to the level screen.
- Added one unit must survive objective to objective screen.
- Player must complete at least one level to get a high score.
- Separate money counts for game and level session.
- No longer lose money if press escape on unit selection screen.
- Restored starting money to previous defaults.
- Can now use the unit action on a unit without it first being made active (deploy).
- Can now set a target using the deploy key instead of the target key.
- The target key has been removed as it is no longer required.
- Fixed mine explosion radius at screen edge.
- Fixed screen corruption when switching units after scrolling.
- Added help mode to show possible cells that a player can move to.
- Added help mode to show possible cells that a player can apply actions (defuse) to.
Quote from: Nesu on 15:39, 10 December 20
Hi, I've been doing a little bit of research, hope it can help.
With this snapshot, when space is pressed, clearIndicator() [Player.c ] calls drawCell() with wrong value for x
src/Player.c:39: drawCell(moveIndicator.x - xScroll, moveIndicator.y - yScroll, cell)
moveIndicator.x-xScroll=-1 so drawCell is called as drawCell(0xFF,0x16,0x00)
That causes a wrong memory calculation at drawCell and a bunch of zeros are written over the program code.
How have make link between snapshot and the code ? I have not found how load the NOI file while debugging.
First I noticed that with v10_further_moves.sna snapshot I could press ESC to return to main menu and start a new game. However, when loading the snapshot and pressing space, ESC returns to main menu but program crashes trying to start a new game. So I assumed that pressing space at that point destroys part of the code.
Then I saved a new snapshot just after loading v10_further_moves and pressing space, and compared the two files.That showed that some memory bytes in program code had changed to 0, first of them located at 0x01D1. Then with WinApe debugger, 'Breakpoint on write' on that address showed that it was changed from adress 92D2. Acording to my file obj/game.map it was part of cpct_drawSolidBox. However, every time you compile memory locations may change, and my map file was not the one generated with the snapshot, so I had to check comparing winape code debugger with cpctelera/src/sprites/cpct_drawSolidBox.asm and cpct_drawSolidBox_cbindings.s. It was indeed drawSolidBox, just some bytes away from what my map file said.
A breakpoint at the start of drawSolidBox (9245), shows that is called with memory address 01D1 as destination, and it's return address is 439D. Now my map file completely missed what was in 439D, so I searched at the code files for a drawSolidBox call. Luckily the first one where i found matches was on Map.c. Comparing obj/Map.rst with winape debugger pointed to drawCell(), starting at 42F0. It's called for x=FF and with return adress 2020. Again my map file made no sense. Searching for calls to 'drawCell' pointed to several files. First file macht was not it, but second match was on Player.c. Comparing winape debugger to obj/Player.rel pointed to clearIndicator().
Quote from: Nesu on 13:20, 12 December 20
First I noticed that with v10_further_moves.sna snapshot I could press ESC to return to main menu and start a new game. However, when loading the snapshot and pressing space, ESC returns to main menu but program crashes trying to start a new game. So I assumed that pressing space at that point destroys part of the code.
Then I saved a new snapshot just after loading v10_further_moves and pressing space, and compared the two files.That showed that some memory bytes in program code had changed to 0, first of them located at 0x01D1. Then with WinApe debugger, 'Breakpoint on write' on that address showed that it was changed from adress 92D2. Acording to my file obj/game.map it was part of cpct_drawSolidBox. However, every time you compile memory locations may change, and my map file was not the one generated with the snapshot, so I had to check comparing winape code debugger with cpctelera/src/sprites/cpct_drawSolidBox.asm and cpct_drawSolidBox_cbindings.s. It was indeed drawSolidBox, just some bytes away from what my map file said.
A breakpoint at the start of drawSolidBox (9245), shows that is called with memory address 01D1 as destination, and it's return address is 439D. Now my map file completely missed what was in 439D, so I searched at the code files for a drawSolidBox call. Luckily the first one where i found matches was on Map.c. Comparing obj/Map.rst with winape debugger pointed to drawCell(), starting at 42F0. It's called for x=FF and with return adress 2020. Again my map file made no sense. Searching for calls to 'drawCell' pointed to several files. First file macht was not it, but second match was on Player.c. Comparing winape debugger to obj/Player.rel pointed to clearIndicator().
Well done !