-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Savegamesimulator #175
Savegamesimulator #175
Conversation
Added functionality: - Save and load game - Enemy definition overview - Activate game speed via checkbox - Enable game speed from 2 to 128 in steps of [2, 4, 8, 16, 32, 64, 128] ("power of two") Feel free for review and comments
Savegame functionality tested: - save game - query available savegames - load savegame - delete savegame Also done some (mostly automatic) clean ups...
Hi. Thanks again for your contribution. Before diving into the code I would like to make some general comments. First, for your next contribution it would be nice to have the individual improvements also as individual pull requests, possibly containing multiple smaller commits. This makes it easier to discuss and approve them. Save Game: In general I like how the load/save game menus are integrated. In the load game menu I suggest to show only the date and the score but in turn make the screenshots bigger such that the look and feel is close to the change map menu. Also would it be hard to only capture the game view without the menus? Game Speed: Since this is the second PR wanting to run the game faster than 4x, I agree to change this. 128x is maybe a little bit overkill though =) I also think that it is necessary that you can always go back to 1x with a single click. But the checkbox as it is isn't very self explanatory in my opinion. What about a toggle button with the label "Fast" with the same functionality? Enemy FAQ: Nice idea, especially to show the strengths and weaknesses of an enemy. I suggest not to show speed, reward and health because some enemies do not move at a constant speed and reward/health increases over time. Also there seems to be an issue with the layout on very small screens (tried with a Nexus One simulation). What do you think about my suggestions? |
Hi! First of all, thanks for the review. About the package extent, sorry about that. If you want, close this PR. Then I would split my commits, so we can lead a more focused discussion? Savegame Even though I get your concerns about the consistency and visibility, I'd like to show more than only the timestamp and the points. At least wave number and remaining lives should be given to the user. As a compromise, how would you like a "split view"? Instead of a grid view with two columns of savegames in each line, I could build a single column for each line with some textual information on the left and a little bigger screenshot view on the right. Game Speed Enemy FAQ Btw, my local branch has even more improvements:
Well, I think, at least some of them would break the main philosophy of your game, so these will never end up in an PR... |
Splitting the PR I think we can sort it out for this PR. Just keep it in mind because it makes it easier for the reviewer =) Save Game Split view could get tricky for small screens. I think just enlarging the screenshot a bit would do it for a first version. Game Speed What I meant was using a toggle button (like a regular button but which remains "pressed") instead of a checkbox. Longclick is hard to figure out if there is no indication. And of course the build menu should be avoided if possible. Enemy FAQ Ok, let's keep the information. What I forgot to mention is that I would probably rename the menu to something else like "Enemy Stats", because a typical FAQ contains questions and answers. As for the other improvements I am open for everything but if it is something which the average user doesn't need then I would like to have it configurable in the settings such that the game remains easy to use in its default configuration. |
sInstance = this; | ||
mGameFactory = new GameFactory(getApplicationContext()); | ||
} | ||
|
||
public static Context getContext() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No usages. Probably a left over.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is indeed unnecessary in this branch, currently only used in my local branch.
@@ -86,7 +102,7 @@ public void execute() { | |||
} | |||
|
|||
public void saveGame() { | |||
if (mGameEngine.isThreadChangeNeeded()) { | |||
if (mGameEngine.isThreadRunnging() && mGameEngine.isThreadChangeNeeded()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary, in which case is the game thread not running?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was one case, where this additional clause improved something...
I can't remember exactly what it was... I will investigate that.
btw. "Runnging"? Sorry for the typo...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remembered the reason, as I described it for "makeNewSavegame".
Some time ago this if-clause was in the "private void saveGame(..", so it was necessary, since it is called by both, "public void saveGame()" and "public File makeNewSavegame()". Since I have moved the if-clause one level up, the "isThreadRunnging" should be obsolete here, as long as this is not called via another activity.
saveGame("", false); | ||
} | ||
|
||
private void saveGame(final String rootdir, final boolean userSavegame) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not very clear because rootdir
is ignored if userSavegame
is false
. Would simply pass the file name as argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yet another thing, needed for my local branch. Can be changed, of course.
} | ||
}); | ||
return; | ||
}*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this disabled, for capturing the screenshot? At least the saveGame()
method must run on the game thread otherwise there will probably be issues with concurrently accessing the game objects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makeNewSavegame shall only be called via "MenuActivity".
Therefore the thread isn't running at all.
Therefore this if-clause is always FALSE and so it is redundant.
And now I remember the reason for "isThreadRunning" at all.
Assumeed this code is active and "isThreadRunning" is not checked.
The thread is already paused at startup of the "MenuActivity". When you call "makeNewSavegame" from the "MenuActivity", this command will be added to the message queue, which will be worked down when the game continues.
So, you click on "save game" and it is not executed at once, but after you close the "MenuActivity".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand. Will have another look to see if there is a more explicit solution, but if not I think it is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright.
one.delete(); | ||
} | ||
rootdir.delete(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would simply delete the three files and then the directory and log a message if something cannot be deleted. Seems easier to me than to make sure that the directory contains exactly the three files beforehand (which could also be simplified by comparing two HashSets).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got a little "over anxious" when it came to delete something at development time.
So I just left the checks there, since it has already been impemented.
Btw. I am not a native Android/Java developer, so I am sure there are and always be potential for improvement :-D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I think everybody's code has potential for improvement =)
|
||
this.setEnemyType(EnemyType.valueOf(entityName)); | ||
|
||
switch (mResult.getEnemyType()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess you moved the information here to also have access to it in the Enemy FAQ. The downside of these kind of switch cases is that if someone wants to add a new type of enemy, he has to find all the relevant switch cases in the code base and adapt them. I think ideally, in order to add a new type of enemy, you should just have to write your new enemy class and register it in one place (GameFactory) which was the case before.
To access the information in the Enemy FAQ we can also use the EntityRegistry and add a way there to grab all the EnemyProperties.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I tried to use the properties directly from the Enemy Entity, but only the private builder had all the necessary informations. And it ached to build an instance of an Enemy, just to collect its properties.
The EntitiyRegistry would be even better. I just didn't fancy that much trouble :-D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No issues, I can also have a look at it later.
@@ -4,5 +4,6 @@ | |||
None, | |||
Bullet, | |||
Laser, | |||
Explosive | |||
Explosive, | |||
Glue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that Glue
is officially a WeaponType
it would be nice if the GlueEffect
class would check for the strong against Glue
instead of ignoring Flyer
instances specifically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely!
I thought about that, as well.
But I couldn't check, if there are any other ricochet if the current behavour is changed.
} | ||
|
||
DecimalFormat fmt = (value < 1e2f && (!integer || big)) ? fmt1 : fmt0; | ||
DecimalFormat fmt = (usePointedFmt) ? fmt1 : fmt0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the reasoning for this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worked on an option, where the score is always shown and had issues in the whole process.
This is some leftover code change.
It can be reverted, if you want.
private Bitmap extractSingleBmp(EnemyType enemyType) { | ||
int attrId, spriteCount, spriteId; | ||
|
||
switch (enemyType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same argumentation as for the EnemyProperties. Would be better to get the bitmaps directly from the Enemy classes using the EntityRegistry.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but won't that be to much of an hassle?
ViewHolder viewHolder = new ViewHolder(enemyItemView); | ||
String dmp; | ||
|
||
switch (enemyProperties.getEnemyType()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same argumentation as for the EnemyProperties. Would be better to get the bitmaps directly from the Enemy classes using the EntityRegistry.
I also went through the code today and made some comments. I don't expect you to address everything I mentioned. Just tell me if you are finished with the cleanups from your side then I will merge the changes and will have a look at the remaining things. Any feedback for my comments is of course also appreciated. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope, I haven't missed any comment to answer to.
} | ||
}); | ||
return; | ||
}*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
makeNewSavegame shall only be called via "MenuActivity".
Therefore the thread isn't running at all.
Therefore this if-clause is always FALSE and so it is redundant.
And now I remember the reason for "isThreadRunning" at all.
Assumeed this code is active and "isThreadRunning" is not checked.
The thread is already paused at startup of the "MenuActivity". When you call "makeNewSavegame" from the "MenuActivity", this command will be added to the message queue, which will be worked down when the game continues.
So, you click on "save game" and it is not executed at once, but after you close the "MenuActivity".
@@ -86,7 +102,7 @@ public void execute() { | |||
} | |||
|
|||
public void saveGame() { | |||
if (mGameEngine.isThreadChangeNeeded()) { | |||
if (mGameEngine.isThreadRunnging() && mGameEngine.isThreadChangeNeeded()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I remembered the reason, as I described it for "makeNewSavegame".
Some time ago this if-clause was in the "private void saveGame(..", so it was necessary, since it is called by both, "public void saveGame()" and "public File makeNewSavegame()". Since I have moved the if-clause one level up, the "isThreadRunnging" should be obsolete here, as long as this is not called via another activity.
|
||
GameFactory daFac = AnutoApplication.getInstance().getGameFactory(); | ||
WaveManager mWaveManager = daFac.getWaveManager(); | ||
ScoreBoard mScoreBoard = daFac.getScoreBoard(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just went the way of the least resistance ;-)
I exactly tried your suggested way and, as you probably saw, already done that to other classes.
But sometimes I got in trouble because of some cyclic dependencies.
one.delete(); | ||
} | ||
rootdir.delete(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I got a little "over anxious" when it came to delete something at development time.
So I just left the checks there, since it has already been impemented.
Btw. I am not a native Android/Java developer, so I am sure there are and always be potential for improvement :-D
} | ||
} | ||
|
||
public KeyValueStore readSaveGame(final String fileName, final boolean userSavegame) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, sure.
Shall only be needed in local branch.
@@ -93,8 +99,6 @@ public void execute() { | |||
updateRemainingEnemiesCount(); | |||
|
|||
setWaveNumber(mWaveNumber + 1); | |||
setNextWaveReady(false); | |||
nextWaveReadyDelayed(NEXT_WAVE_MIN_DELAY); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And again...local branch...sorry...
If the ready flag is not set this early, my "auto wave" routine starts about 12 waves at once.
But for your branch, this can be built back.
|
||
this.setEnemyType(EnemyType.valueOf(entityName)); | ||
|
||
switch (mResult.getEnemyType()) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I tried to use the properties directly from the Enemy Entity, but only the private builder had all the necessary informations. And it ached to build an instance of an Enemy, just to collect its properties.
The EntitiyRegistry would be even better. I just didn't fancy that much trouble :-D
@@ -4,5 +4,6 @@ | |||
None, | |||
Bullet, | |||
Laser, | |||
Explosive | |||
Explosive, | |||
Glue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely!
I thought about that, as well.
But I couldn't check, if there are any other ricochet if the current behavour is changed.
} | ||
|
||
DecimalFormat fmt = (value < 1e2f && (!integer || big)) ? fmt1 : fmt0; | ||
DecimalFormat fmt = (usePointedFmt) ? fmt1 : fmt0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I worked on an option, where the score is always shown and had issues in the whole process.
This is some leftover code change.
It can be reverted, if you want.
private Bitmap extractSingleBmp(EnemyType enemyType) { | ||
int attrId, spriteCount, spriteId; | ||
|
||
switch (enemyType) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, but won't that be to much of an hassle?
Save Game
Game Speed Enemy FAQ Shall I commit the adjustments in my SavegameSimulator branch, so you can take a look, or how should we continue? Other improvements |
Save Game Game Speed Adjustments Other Improvements Edit: Will go through your code comments later. |
show only - timestamp - score - wave - lives and crop screenshot to maximize map visibility
- renamed 'faq' to 'stats' - resized to run on smaller displays aswell - implemented getStrongAgainst <-> WeaponType.Glue - added some translations - "Reformat Code"
I tried to replace the game speed Talking about smaller devices, I resized the |
|
||
//perhaps build another draw routine, so the game map is better visible | ||
//e.g. an independent theme just for savegame screenshot | ||
draw(canvas); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually like this approach very much!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. And I am serious in my comment.
I already tried some stuff, but never really got satisfied.
A forth, neutral theme would have the positiv effect, that the load game menu would look homogeneous, no matter with which theme the game runs in the meantime.
Of course, beside the aim for a better screenshot visualization of the game map...
if (origin instanceof Tower) { | ||
Tower originTower = (Tower) origin; | ||
|
||
if (!mEnemyProperties.getStrongAgainst().contains(originTower.getWeaponType())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not doing this check in GlueEffect
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right, since modifySpeed
is only used by GlueEffect
, I thought about that approach, too.
But I voted for the current solution, since the getWeakAgainst
/getStrongAgainst
check is here in Enemy
-damage
, as well.
And, well, who knows what cool new Tower
the future will bring us :-)
Hi, thanks for your latest improvements! Regarding the CheckBox / ToggleButton: I will also have a look at it. What would be your recommendation? Keeping the CheckBox? I will check again for the EnemyStatsActivity. If it is ok for you, I would merge the current state and start working on it. Meaning you will have to create new pull requests if you want to add more changes and sync with my repository from time to time. Can we proceed like that or would you like to add something to this pull request? |
CheckBox / ToggleButton What about a Or a As for merging, that would work with me, I have nothing usable to add to this pull request. And I have no problems submitting another PR. Actually, I look forward to it. |
Integrate Savegame in TestUnit
Savegame functionality tested:
Also done some (mostly automatic) clean ups...