├── private
└── cache
│ └── retriever
│ └── catalog.xml
├── bin
├── Sokoban.apk
├── classes.dex
├── resources.ap_
├── res
│ └── crunch
│ │ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-ldpi
│ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-xxhdpi
│ │ └── ic_launcher.png
│ │ └── drawable-xxxhdpi
│ │ └── ic_launcher.png
├── dexedLibs
│ ├── annotations-ac272055c18327fea198dc88f4e60a33.jar
│ └── androidframework-f7e2f39978d06bc9456c9f44f2627b49.jar
├── R.txt
└── AndroidManifest.xml
├── res
├── drawable
│ └── splash.jpg
├── drawable-hdpi
│ └── ic_launcher.png
├── drawable-ldpi
│ └── ic_launcher.png
├── drawable-mdpi
│ └── ic_launcher.png
├── drawable-xhdpi
│ └── ic_launcher.png
├── drawable-xxhdpi
│ └── ic_launcher.png
├── drawable-xxxhdpi
│ └── ic_launcher.png
├── values
│ ├── strings.xml
│ └── styles.xml
└── layout
│ ├── main.xml
│ └── splash.xml
├── assets
├── audio
│ ├── game
│ │ ├── goal.wav
│ │ ├── music.mp3
│ │ └── complete.wav
│ └── menu
│ │ └── selection.wav
├── image
│ ├── menu
│ │ ├── Logo.png
│ │ ├── Button.png
│ │ ├── Splash.jpg
│ │ ├── Twitter.png
│ │ ├── Youtube.png
│ │ ├── Background.jpg
│ │ ├── Facebook.png
│ │ ├── MenuCancel.png
│ │ ├── Instructions.png
│ │ └── MenuConfirm.png
│ └── game
│ │ ├── Sprites.png
│ │ ├── LevelNext.png
│ │ ├── MenuExit.png
│ │ ├── MenuPause.png
│ │ ├── MenuReset.png
│ │ ├── MenuSoundOff.png
│ │ ├── MenuSoundOn.png
│ │ ├── LevelPrevious.png
│ │ ├── MenuUndoEnabled.png
│ │ ├── LevelIconComplete.png
│ │ ├── MenuUndoDisabled.png
│ │ └── LevelIconIncomplete.png
├── font
│ ├── game
│ │ └── Default.ttf
│ └── menu
│ │ └── Default.ttf
└── text
│ └── cEasy.txt
├── .settings
└── org.eclipse.jdt.core.prefs
├── local.properties
├── .classpath
├── project.properties
├── ant.properties
├── src
└── com
│ └── gamesbykevin
│ └── sokoban
│ ├── level
│ ├── ILevel.java
│ ├── ILevels.java
│ ├── tile
│ │ ├── Tile.java
│ │ ├── Wall.java
│ │ ├── Goal.java
│ │ ├── Floor.java
│ │ ├── TileHelper.java
│ │ └── Block.java
│ ├── LevelInfo.java
│ ├── LevelHelper.java
│ └── Levels.java
│ ├── game
│ ├── IGame.java
│ └── controller
│ │ ├── IController.java
│ │ └── Controller.java
│ ├── player
│ ├── IPlayer.java
│ ├── PlayerHelper.java
│ └── Player.java
│ ├── storage
│ ├── scorecard
│ │ ├── Score.java
│ │ └── ScoreCard.java
│ └── settings
│ │ └── Settings.java
│ ├── screen
│ ├── GameScreen.java
│ ├── PauseScreen.java
│ ├── ExitScreen.java
│ ├── ScreenManager.java
│ ├── GameoverScreen.java
│ └── MenuScreen.java
│ ├── target
│ └── Target.java
│ ├── Splash.java
│ ├── ai
│ └── AI.java
│ ├── MainActivity.java
│ ├── assets
│ └── Assets.java
│ ├── thread
│ └── MainThread.java
│ └── panel
│ └── GamePanel.java
├── proguard-project.txt
├── .project
└── AndroidManifest.xml
/private/cache/retriever/catalog.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/bin/Sokoban.apk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/Sokoban.apk
--------------------------------------------------------------------------------
/bin/classes.dex:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/classes.dex
--------------------------------------------------------------------------------
/bin/resources.ap_:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/resources.ap_
--------------------------------------------------------------------------------
/res/drawable/splash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/res/drawable/splash.jpg
--------------------------------------------------------------------------------
/assets/audio/game/goal.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/audio/game/goal.wav
--------------------------------------------------------------------------------
/assets/audio/game/music.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/audio/game/music.mp3
--------------------------------------------------------------------------------
/assets/image/menu/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/Logo.png
--------------------------------------------------------------------------------
/assets/font/game/Default.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/font/game/Default.ttf
--------------------------------------------------------------------------------
/assets/font/menu/Default.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/font/menu/Default.ttf
--------------------------------------------------------------------------------
/assets/image/game/Sprites.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/Sprites.png
--------------------------------------------------------------------------------
/assets/image/menu/Button.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/Button.png
--------------------------------------------------------------------------------
/assets/image/menu/Splash.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/Splash.jpg
--------------------------------------------------------------------------------
/assets/image/menu/Twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/Twitter.png
--------------------------------------------------------------------------------
/assets/image/menu/Youtube.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/Youtube.png
--------------------------------------------------------------------------------
/assets/audio/game/complete.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/audio/game/complete.wav
--------------------------------------------------------------------------------
/assets/audio/menu/selection.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/audio/menu/selection.wav
--------------------------------------------------------------------------------
/assets/image/game/LevelNext.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/LevelNext.png
--------------------------------------------------------------------------------
/assets/image/game/MenuExit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/MenuExit.png
--------------------------------------------------------------------------------
/assets/image/game/MenuPause.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/MenuPause.png
--------------------------------------------------------------------------------
/assets/image/game/MenuReset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/MenuReset.png
--------------------------------------------------------------------------------
/assets/image/menu/Background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/Background.jpg
--------------------------------------------------------------------------------
/assets/image/menu/Facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/Facebook.png
--------------------------------------------------------------------------------
/assets/image/menu/MenuCancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/MenuCancel.png
--------------------------------------------------------------------------------
/assets/image/game/MenuSoundOff.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/MenuSoundOff.png
--------------------------------------------------------------------------------
/assets/image/game/MenuSoundOn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/MenuSoundOn.png
--------------------------------------------------------------------------------
/assets/image/menu/Instructions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/Instructions.png
--------------------------------------------------------------------------------
/assets/image/menu/MenuConfirm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/menu/MenuConfirm.png
--------------------------------------------------------------------------------
/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/res/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/assets/image/game/LevelPrevious.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/LevelPrevious.png
--------------------------------------------------------------------------------
/assets/image/game/MenuUndoEnabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/MenuUndoEnabled.png
--------------------------------------------------------------------------------
/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/res/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/assets/image/game/LevelIconComplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/LevelIconComplete.png
--------------------------------------------------------------------------------
/assets/image/game/MenuUndoDisabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/MenuUndoDisabled.png
--------------------------------------------------------------------------------
/assets/image/game/LevelIconIncomplete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/assets/image/game/LevelIconIncomplete.png
--------------------------------------------------------------------------------
/bin/res/crunch/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/res/crunch/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/bin/res/crunch/drawable-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/res/crunch/drawable-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/bin/res/crunch/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/res/crunch/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Sokoban
4 |
5 |
--------------------------------------------------------------------------------
/bin/res/crunch/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/res/crunch/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/bin/res/crunch/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/res/crunch/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/bin/res/crunch/drawable-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/res/crunch/drawable-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/bin/dexedLibs/annotations-ac272055c18327fea198dc88f4e60a33.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/dexedLibs/annotations-ac272055c18327fea198dc88f4e60a33.jar
--------------------------------------------------------------------------------
/bin/dexedLibs/androidframework-f7e2f39978d06bc9456c9f44f2627b49.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gamesbykevin/Sokoban-Android/HEAD/bin/dexedLibs/androidframework-f7e2f39978d06bc9456c9f44f2627b49.jar
--------------------------------------------------------------------------------
/.settings/org.eclipse.jdt.core.prefs:
--------------------------------------------------------------------------------
1 | eclipse.preferences.version=1
2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
3 | org.eclipse.jdt.core.compiler.compliance=1.6
4 | org.eclipse.jdt.core.compiler.source=1.6
5 |
--------------------------------------------------------------------------------
/bin/R.txt:
--------------------------------------------------------------------------------
1 | int drawable ic_launcher 0x7f020000
2 | int drawable splash 0x7f020001
3 | int layout main 0x7f030000
4 | int layout splash 0x7f030001
5 | int string app_name 0x7f040000
6 | int style AppBaseTheme 0x7f050000
7 | int style AppTheme 0x7f050001
8 |
--------------------------------------------------------------------------------
/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/res/layout/splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must *NOT* be checked into Version Control Systems,
5 | # as it contains information specific to your local configuration.
6 |
7 | # location of the SDK. This is only used by Ant
8 | # For customization when using a Version Control System, please read the
9 | # header note.
10 | sdk.dir=C:\\Users\\GOD\\AppData\\Local\\Android\\android-sdk
11 |
--------------------------------------------------------------------------------
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/project.properties:
--------------------------------------------------------------------------------
1 | # This file is automatically generated by Android Tools.
2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED!
3 | #
4 | # This file must be checked in Version Control Systems.
5 | #
6 | # To customize properties used by the Ant build system edit
7 | # "ant.properties", and override values to adapt the script to your
8 | # project structure.
9 | #
10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
12 |
13 | # Project target.
14 | target=android-15
15 | android.library.reference.1=../AndroidFramework
16 |
--------------------------------------------------------------------------------
/ant.properties:
--------------------------------------------------------------------------------
1 | # This file is used to override default values used by the Ant build system.
2 | #
3 | # This file must be checked into Version Control Systems, as it is
4 | # integral to the build system of your project.
5 |
6 | # This file is only used by the Ant script.
7 |
8 | # You can use this to override default values such as
9 | # 'source.dir' for the location of your java source folder and
10 | # 'out.dir' for the location of your output folder.
11 |
12 | # You can also use it define how the release builds are signed by declaring
13 | # the following properties:
14 | # 'key.store' for the location of your keystore and
15 | # 'key.alias' for the name of the key to use.
16 | # The password will be asked during the build when you use the 'release' target.
17 |
18 |
--------------------------------------------------------------------------------
/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/ILevel.java:
--------------------------------------------------------------------------------
1 | /*
2 | * To change this license header, choose License Headers in Project Properties.
3 | * To change this template file, choose Tools | Templates
4 | * and open the template in the editor.
5 | */
6 | package com.gamesbykevin.sokoban.level;
7 |
8 | /**
9 | * Required methods for level
10 | * @author GOD
11 | */
12 | public interface ILevel
13 | {
14 | /**
15 | * Load the level with the given line
16 | * @param line The line containing characters for the layout of our level
17 | * @throws Exception if problem loading the current line
18 | */
19 | public void load(final String line) throws Exception;
20 |
21 | /**
22 | * Logic to update level
23 | */
24 | public void update();
25 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/ILevels.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level;
2 |
3 | import com.gamesbykevin.androidframework.resources.Disposable;
4 |
5 | import android.graphics.Canvas;
6 | import android.graphics.Paint;
7 |
8 | /**
9 | * Required methods for a level
10 | * @author GOD
11 | */
12 | public interface ILevels extends Disposable
13 | {
14 | /**
15 | * Logic to reset the level
16 | * @throws Exception
17 | */
18 | public void reset() throws Exception;
19 |
20 | /**
21 | * Render the level
22 | * @param canvas Object where we write pixel data
23 | * @param paint Paint Object
24 | * @throws Exception
25 | */
26 | public void render(final Canvas canvas, final Paint paint) throws Exception;
27 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/game/IGame.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.game;
2 |
3 | import com.gamesbykevin.androidframework.resources.Disposable;
4 | import com.gamesbykevin.sokoban.assets.Assets;
5 |
6 | import android.graphics.Canvas;
7 |
8 | /**
9 | * Game interface methods
10 | * @author GOD
11 | */
12 | public interface IGame extends Disposable
13 | {
14 | /**
15 | * Logic to restart the game with the same settings
16 | * @param key Unique key of text file we want to load
17 | * @throws Exception
18 | */
19 | public void reset(final Assets.TextKey key) throws Exception;
20 |
21 | /**
22 | * Logic to render the game
23 | * @param canvas
24 | * @throws Exception
25 | */
26 | public void render(final Canvas canvas) throws Exception;
27 | }
28 |
--------------------------------------------------------------------------------
/proguard-project.txt:
--------------------------------------------------------------------------------
1 | # To enable ProGuard in your project, edit project.properties
2 | # to define the proguard.config property as described in that file.
3 | #
4 | # Add project specific ProGuard rules here.
5 | # By default, the flags in this file are appended to flags specified
6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt
7 | # You can edit the include path and order by changing the ProGuard
8 | # include property in project.properties.
9 | #
10 | # For more details, see
11 | # http://developer.android.com/guide/developing/tools/proguard.html
12 |
13 | # Add any project specific keep options here:
14 |
15 | # If your project uses WebView with JS, uncomment the following
16 | # and specify the fully qualified class name to the JavaScript interface
17 | # class:
18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
19 | # public *;
20 | #}
21 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | Sokoban
4 |
5 |
6 |
7 |
8 |
9 | com.android.ide.eclipse.adt.ResourceManagerBuilder
10 |
11 |
12 |
13 |
14 | com.android.ide.eclipse.adt.PreCompilerBuilder
15 |
16 |
17 |
18 |
19 | org.eclipse.jdt.core.javabuilder
20 |
21 |
22 |
23 |
24 | com.android.ide.eclipse.adt.ApkBuilder
25 |
26 |
27 |
28 |
29 |
30 | com.android.ide.eclipse.adt.AndroidNature
31 | org.eclipse.jdt.core.javanature
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/tile/Tile.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level.tile;
2 |
3 | import com.gamesbykevin.androidframework.base.Entity;
4 |
5 | /**
6 | * A single tile in the game
7 | * @author GOD
8 | */
9 | public abstract class Tile extends Entity
10 | {
11 | /**
12 | * The different types of tiles in our game
13 | */
14 | public enum Type
15 | {
16 | Floor,
17 | Wall,
18 | Block,
19 | Goal
20 | }
21 |
22 | //the type of tile
23 | private final Type type;
24 |
25 | /**
26 | * Create a new tile
27 | * @param type The type of tile
28 | */
29 | public Tile(final Type type)
30 | {
31 | this.type = type;
32 | }
33 |
34 | /**
35 | * Get the type
36 | * @return The type of tile this is "Floor, Wall, Goal, Block, etc..."
37 | */
38 | public Type getType()
39 | {
40 | return this.type;
41 | }
42 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/player/IPlayer.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.player;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 |
6 | import com.gamesbykevin.androidframework.resources.Disposable;
7 | import com.gamesbykevin.sokoban.level.Level;
8 |
9 | /**
10 | * Player interface
11 | * @author GOD
12 | */
13 | public interface IPlayer extends Disposable
14 | {
15 | /**
16 | * Logic to reset player
17 | * @param level The current level
18 | */
19 | public void reset(final Level level);
20 |
21 | /**
22 | * Update the player
23 | * @param level The current level
24 | */
25 | public void update(final Level level);
26 |
27 | /**
28 | * Render the player
29 | * @param canvas Object used to write pixel data
30 | * @param paint Object containing font metrics
31 | * @throws Exception
32 | */
33 | public void render(final Canvas canvas, final Paint paint) throws Exception;
34 | }
35 |
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/game/controller/IController.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.game.controller;
2 |
3 | import android.graphics.Canvas;
4 | import com.gamesbykevin.androidframework.resources.Disposable;
5 |
6 | /**
7 | * Each controller needs to have these methods
8 | * @author GOD
9 | */
10 | public interface IController extends Disposable
11 | {
12 | public void reset();
13 |
14 | /**
15 | * Update logic when motion event occurs
16 | * @param event Motion Event
17 | * @param x x-coordinate
18 | * @param y y-coordinate
19 | * @throws Exception
20 | * @return true if motion event was applied, false otherwise
21 | */
22 | public boolean update(final int action, final float x, final float y) throws Exception;
23 |
24 | /**
25 | * Common logic to update the controller
26 | */
27 | public void update() throws Exception;
28 |
29 | /**
30 | * Render our controller
31 | * @param canvas Object to write pixels to
32 | * @throws Exception
33 | */
34 | public void render(final Canvas canvas) throws Exception;
35 | }
36 |
--------------------------------------------------------------------------------
/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
16 |
23 |
24 |
25 |
26 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/bin/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
16 |
23 |
24 |
25 |
26 |
27 |
28 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/storage/scorecard/Score.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.storage.scorecard;
2 |
3 | /**
4 | * The score for a level
5 | * @author GOD
6 | */
7 | public final class Score
8 | {
9 | //the level this score is for
10 | private final int level;
11 |
12 | //the number of moves
13 | private int moves;
14 |
15 | //the time duration
16 | private long time;
17 |
18 | protected Score(final int level, final int moves, final long time)
19 | {
20 | //assign default values
21 | this.level = level;
22 | this.moves = moves;
23 | this.time = time;
24 | }
25 |
26 | /**
27 | * Get the level index
28 | * @return The level this score is for
29 | */
30 | public int getLevel()
31 | {
32 | return this.level;
33 | }
34 |
35 | /**
36 | * Get the moves
37 | * @return The number of moves for this score
38 | */
39 | public int getMoves()
40 | {
41 | return this.moves;
42 | }
43 |
44 | /**
45 | * Get the time duration
46 | * @return The duration for this score (milliseconds)
47 | */
48 | public long getTime()
49 | {
50 | return this.time;
51 | }
52 |
53 | /**
54 | * Set the number of moves
55 | * @param moves The number of moves to complete the level
56 | */
57 | protected void setMoves(final int moves)
58 | {
59 | this.moves = moves;
60 | }
61 |
62 | /**
63 | * Set the time
64 | * @param time The duration to complete the level
65 | */
66 | protected void setTime(final long time)
67 | {
68 | this.time = time;
69 | }
70 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/LevelInfo.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level;
2 |
3 | /**
4 | * This class will maintain the information of a level.
5 | * It will be used to help load a level from a given text file
6 | * @author GOD
7 | */
8 | public class LevelInfo
9 | {
10 | //the start and end line of a given level
11 | private final int start, end, cols, rows;
12 |
13 | //the text description of the level
14 | private final String levelDescription;
15 |
16 | public LevelInfo(final int start, final int end, final int cols, final String levelDescription)
17 | {
18 | this.start = start;
19 | this.end = end;
20 | this.cols = cols;
21 | this.rows = end - start + 1;
22 | this.levelDescription = levelDescription;
23 | }
24 |
25 | /**
26 | * The start line number
27 | * @return The line # in the text file where the level starts
28 | */
29 | public int getLineStart()
30 | {
31 | return this.start;
32 | }
33 |
34 | /**
35 | * The end line number
36 | * @return The line # in the text file where the level ends
37 | */
38 | public int getLineEnd()
39 | {
40 | return this.end;
41 | }
42 |
43 | /**
44 | * Get the columns
45 | * @return The number of columns for this level
46 | */
47 | public int getCols()
48 | {
49 | return this.cols;
50 | }
51 |
52 | /**
53 | * Get the rows
54 | * @return The number of rows for this level
55 | */
56 | public int getRows()
57 | {
58 | return this.rows;
59 | }
60 |
61 | /**
62 | * Get the level description
63 | * @return The level description text we want to display to the user
64 | */
65 | public String getLevelDescription()
66 | {
67 | return this.levelDescription;
68 | }
69 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/tile/Wall.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level.tile;
2 |
3 | import com.gamesbykevin.androidframework.anim.Animation;
4 | import com.gamesbykevin.androidframework.resources.Images;
5 | import com.gamesbykevin.sokoban.assets.Assets;
6 |
7 | /**
8 | * Wall
9 | * @author GOD
10 | */
11 | public final class Wall extends Tile
12 | {
13 | public enum Style
14 | {
15 | Gray, Brown, Black, Beige
16 | }
17 |
18 | public Wall(final Style style)
19 | {
20 | super(Tile.Type.Wall);
21 |
22 | setAnimation(style);
23 | }
24 |
25 | public final void setAnimation(final Style style)
26 | {
27 | int x = 0, y = 0, d = TileHelper.ANIMATION_DIMENSION;
28 |
29 | //set dimensions
30 | super.setWidth(d);
31 | super.setHeight(d);
32 |
33 | switch (style)
34 | {
35 | case Gray:
36 | x = TileHelper.getSpriteSheetX(0);
37 | y = TileHelper.getSpriteSheetY(4);
38 | break;
39 |
40 | case Brown:
41 | x = TileHelper.getSpriteSheetX(0);
42 | y = TileHelper.getSpriteSheetY(5);
43 | break;
44 |
45 | case Black:
46 | x = TileHelper.getSpriteSheetX(1);
47 | y = TileHelper.getSpriteSheetY(0);
48 | break;
49 |
50 | case Beige:
51 | x = TileHelper.getSpriteSheetX(1);
52 | y = TileHelper.getSpriteSheetY(1);
53 | break;
54 | }
55 |
56 | //if animation doesn't exist, add it
57 | if (getSpritesheet().get(style) == null)
58 | {
59 | //map the style
60 | getSpritesheet().add(style, new Animation(Images.getImage(Assets.ImageGameKey.Sprites), x, y, d, d));
61 | }
62 |
63 | //assign this style as the current
64 | getSpritesheet().setKey(style);
65 | }
66 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/screen/GameScreen.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.screen;
2 |
3 | import android.graphics.Canvas;
4 | import com.gamesbykevin.androidframework.resources.Disposable;
5 | import com.gamesbykevin.androidframework.screen.Screen;
6 | import com.gamesbykevin.sokoban.assets.Assets;
7 | import com.gamesbykevin.sokoban.game.Game;
8 |
9 | /**
10 | * The game screen that contains the game
11 | * @author GOD
12 | */
13 | public class GameScreen implements Screen, Disposable
14 | {
15 | //our object containing the main game functionality
16 | private Game game;
17 |
18 | //our main screen reference
19 | private final ScreenManager screen;
20 |
21 | public GameScreen(final ScreenManager screen)
22 | {
23 | this.screen = screen;
24 | }
25 |
26 | protected Game getGame()
27 | {
28 | return this.game;
29 | }
30 |
31 | /**
32 | * Create game object
33 | * @throws Exception
34 | */
35 | public void createGame() throws Exception
36 | {
37 | if (getGame() == null)
38 | this.game = new Game(screen);
39 |
40 | //reset the game
41 | getGame().reset(Assets.TextKey.values()[screen.getScreenOptions().getIndex(OptionsScreen.ButtonKey.Difficulty)]);
42 | }
43 |
44 | /**
45 | * Reset any necessary screen elements here
46 | */
47 | @Override
48 | public void reset()
49 | {
50 | //anything need to be reset here
51 | }
52 |
53 | @Override
54 | public boolean update(final int action, final float x, final float y) throws Exception
55 | {
56 | if (getGame() != null)
57 | getGame().update(action, x, y);
58 |
59 | return true;
60 | }
61 |
62 | @Override
63 | public void update() throws Exception
64 | {
65 | if (getGame() != null)
66 | getGame().update();
67 | }
68 |
69 | @Override
70 | public void render(final Canvas canvas) throws Exception
71 | {
72 | //render game if exists
73 | if (getGame() != null)
74 | getGame().render(canvas);
75 | }
76 |
77 | @Override
78 | public void dispose()
79 | {
80 | if (game != null)
81 | {
82 | game.dispose();
83 | game = null;
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/tile/Goal.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level.tile;
2 |
3 | import com.gamesbykevin.androidframework.anim.Animation;
4 | import com.gamesbykevin.androidframework.resources.Images;
5 | import com.gamesbykevin.sokoban.assets.Assets;
6 |
7 | /**
8 | * Goal
9 | * @author GOD
10 | */
11 | public final class Goal extends Tile
12 | {
13 | public enum Style
14 | {
15 | Brown, Beige, Gray, Purple,
16 | Blue, Black, Red, Yellow,
17 | }
18 |
19 | public Goal(final Style style)
20 | {
21 | super(Tile.Type.Goal);
22 |
23 | setAnimation(style);
24 | }
25 |
26 | public final void setAnimation(final Style style)
27 | {
28 | int x = 0, y = 384, d = TileHelper.ANIMATION_GOAL_DIMENSION;
29 |
30 | //set dimensions
31 | super.setWidth(d);
32 | super.setHeight(d);
33 |
34 | switch (style)
35 | {
36 | case Brown:
37 | x = (0 * TileHelper.ANIMATION_GOAL_DIMENSION);
38 | break;
39 |
40 | case Beige:
41 | x = (1 * TileHelper.ANIMATION_GOAL_DIMENSION);
42 | break;
43 |
44 | case Gray:
45 | x = (2 * TileHelper.ANIMATION_GOAL_DIMENSION);
46 | break;
47 |
48 | case Purple:
49 | x = (3 * TileHelper.ANIMATION_GOAL_DIMENSION);
50 | break;
51 |
52 | case Blue:
53 | x = (4 * TileHelper.ANIMATION_GOAL_DIMENSION);
54 | break;
55 |
56 | case Black:
57 | x = (5 * TileHelper.ANIMATION_GOAL_DIMENSION);
58 | break;
59 |
60 | case Red:
61 | x = (6 * TileHelper.ANIMATION_GOAL_DIMENSION);
62 | break;
63 |
64 | case Yellow:
65 | x = (7 * TileHelper.ANIMATION_GOAL_DIMENSION);
66 | break;
67 | }
68 |
69 | //if animation doesn't exist, add it
70 | if (getSpritesheet().get(style) == null)
71 | {
72 | //map the style
73 | getSpritesheet().add(style, new Animation(Images.getImage(Assets.ImageGameKey.Sprites), x, y, d, d));
74 | }
75 |
76 | //assign this style as the current
77 | getSpritesheet().setKey(style);
78 | }
79 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/tile/Floor.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level.tile;
2 |
3 | import com.gamesbykevin.androidframework.anim.Animation;
4 | import com.gamesbykevin.androidframework.resources.Images;
5 | import com.gamesbykevin.sokoban.assets.Assets;
6 |
7 | /**
8 | * Floor
9 | * @author GOD
10 | */
11 | public final class Floor extends Tile
12 | {
13 | public enum Style
14 | {
15 | Beige1, Beige2,
16 | Green1, Green2,
17 | Brown1, Brown2,
18 | Gray1, Gray2
19 | }
20 |
21 | public Floor(final Style style)
22 | {
23 | super(Tile.Type.Floor);
24 |
25 | setAnimation(style);
26 | }
27 |
28 | public final void setAnimation(final Style style)
29 | {
30 | int x = 0, y = 0, d = TileHelper.ANIMATION_DIMENSION;
31 |
32 | //set dimensions
33 | super.setWidth(d);
34 | super.setHeight(d);
35 |
36 | switch (style)
37 | {
38 | case Beige1:
39 | x = TileHelper.getSpriteSheetX(1);
40 | y = TileHelper.getSpriteSheetY(2);
41 | break;
42 |
43 | case Beige2:
44 | x = TileHelper.getSpriteSheetX(2);
45 | y = TileHelper.getSpriteSheetY(0);
46 | break;
47 |
48 | case Green1:
49 | x = TileHelper.getSpriteSheetX(1);
50 | y = TileHelper.getSpriteSheetY(3);
51 | break;
52 |
53 | case Green2:
54 | x = TileHelper.getSpriteSheetX(2);
55 | y = TileHelper.getSpriteSheetY(1);
56 | break;
57 |
58 | case Brown1:
59 | x = TileHelper.getSpriteSheetX(1);
60 | y = TileHelper.getSpriteSheetY(4);
61 | break;
62 |
63 | case Brown2:
64 | x = TileHelper.getSpriteSheetX(2);
65 | y = TileHelper.getSpriteSheetY(2);
66 | break;
67 |
68 | case Gray1:
69 | x = TileHelper.getSpriteSheetX(1);
70 | y = TileHelper.getSpriteSheetY(5);
71 | break;
72 |
73 | case Gray2:
74 | x = TileHelper.getSpriteSheetX(2);
75 | y = TileHelper.getSpriteSheetY(3);
76 | break;
77 | }
78 |
79 | //if animation doesn't exist, add it
80 | if (getSpritesheet().get(style) == null)
81 | {
82 | //map the style
83 | getSpritesheet().add(style, new Animation(Images.getImage(Assets.ImageGameKey.Sprites), x, y, d, d));
84 | }
85 |
86 | //assign this style as the current
87 | getSpritesheet().setKey(style);
88 | }
89 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/target/Target.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.target;
2 |
3 | import com.gamesbykevin.androidframework.base.Cell;
4 | import com.gamesbykevin.androidframework.resources.Disposable;
5 |
6 | /**
7 | * The target class contains the current location and a destination
8 | * Used to move the blocks and the player
9 | * @author GOD
10 | */
11 | public class Target extends Cell implements Disposable
12 | {
13 | //destination location
14 | private Cell destination;
15 |
16 | //is this target at a goal
17 | private boolean goal = false;
18 |
19 | //store the previous location in case we want to undo
20 | private double previousCol, previousRow;
21 |
22 | /**
23 | * Create a new Target with the specified location
24 | * @param col Column
25 | * @param row Row
26 | */
27 | public Target(final double col, final double row)
28 | {
29 | super(col, row);
30 |
31 | //assign current location as the destination
32 | this.destination = new Cell(col, row);
33 |
34 | //assign the previous location
35 | this.previousCol = col;
36 | this.previousRow = row;
37 | }
38 |
39 | @Override
40 | public void dispose()
41 | {
42 | this.destination = null;
43 | }
44 |
45 | /**
46 | * Flag this target located at a goal
47 | * @param goal true = yes, false = no
48 | */
49 | public void setGoal(final boolean goal)
50 | {
51 | this.goal = goal;
52 | }
53 |
54 | /**
55 | * Is this target at a goal?
56 | * @return true = yes, false = no
57 | */
58 | public boolean hasGoal()
59 | {
60 | return this.goal;
61 | }
62 |
63 | /**
64 | * Reset the location to the previous
65 | */
66 | public void undo()
67 | {
68 | setCol(this.previousCol);
69 | setRow(this.previousRow);
70 |
71 | setDestination(getCol(), getRow());
72 | }
73 |
74 | /**
75 | * Assign the destination, as well as the current location
76 | * @param col Column of our destination
77 | * @param row Row of our destination
78 | */
79 | public void setDestination(final double col, final double row)
80 | {
81 | //assign current in case of undo
82 | this.previousCol = getCol();
83 | this.previousRow = getRow();
84 |
85 | //assign destination location
86 | this.destination.setCol(col);
87 | this.destination.setRow(row);
88 | }
89 |
90 | /**
91 | * Get the destination
92 | * @return The location of the destination
93 | */
94 | public Cell getDestination()
95 | {
96 | return this.destination;
97 | }
98 |
99 | /**
100 | * Do we have the destination?
101 | * @return true if the current location matches the target location, false otherwise
102 | */
103 | public boolean hasDestination()
104 | {
105 | return (getCol() == getDestination().getCol() && getRow() == getDestination().getRow());
106 | }
107 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/Splash.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban;
2 |
3 | import com.gamesbykevin.sokoban.assets.Assets;
4 |
5 | import android.app.Activity;
6 | import android.content.Intent;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.view.Window;
10 | import android.view.WindowManager;
11 |
12 | /**
13 | * Splash loading screen
14 | * @author GOD
15 | */
16 | public class Splash extends Activity
17 | {
18 | /**
19 | * Time delay to show the splash screen
20 | */
21 | private static final long TIME_DELAY = 1000L;
22 |
23 | /**
24 | * Called when the activity is first created
25 | * @param savedInstanceState
26 | */
27 | @Override
28 | public void onCreate(Bundle savedInstanceState)
29 | {
30 | //turn the title off
31 | super.requestWindowFeature(Window.FEATURE_NO_TITLE);
32 |
33 | //set the screen to full screen
34 | super.getWindow().setFlags(
35 | WindowManager.LayoutParams.FLAG_FULLSCREEN,
36 | WindowManager.LayoutParams.FLAG_FULLSCREEN
37 | );
38 |
39 | //call parent create
40 | super.onCreate(savedInstanceState);
41 |
42 | //set our content view to show the image
43 | super.setContentView(R.layout.splash);
44 |
45 | try
46 | {
47 | //load the game assets
48 | Assets.load(this);
49 | }
50 | catch (Exception e)
51 | {
52 | e.printStackTrace();
53 | }
54 | }
55 |
56 | /**
57 | * Override the finish call
58 | */
59 | @Override
60 | public void finish()
61 | {
62 | super.finish();
63 | }
64 |
65 | /**
66 | * Part of the activity life cycle
67 | */
68 | @Override
69 | public void onStart()
70 | {
71 | //call parent functionality
72 | super.onStart();
73 |
74 | //add a delay to show the splash image before starting main activity
75 | new Handler().postDelayed(new Runnable() {
76 |
77 | /**
78 | * Run will be executed once the time delay is over
79 | */
80 | @Override
81 | public void run() {
82 |
83 | //start our main activity
84 | startActivity(new Intent(Splash.this, MainActivity.class));
85 |
86 | //close this activity
87 | finish();
88 | }
89 | }, TIME_DELAY);
90 | }
91 |
92 | /**
93 | * Part of the activity life cycle
94 | */
95 | @Override
96 | public void onStop()
97 | {
98 | super.onStop();
99 | }
100 |
101 | /**
102 | * Part of the activity life cycle
103 | */
104 | @Override
105 | public void onDestroy()
106 | {
107 | //perform final cleanup
108 | super.onDestroy();
109 | }
110 |
111 | /**
112 | * Part of the activity life cycle
113 | */
114 | @Override
115 | public void onPause()
116 | {
117 | super.onPause();
118 | }
119 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/storage/settings/Settings.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.storage.settings;
2 |
3 | import android.app.Activity;
4 | import com.gamesbykevin.androidframework.io.storage.Internal;
5 | import com.gamesbykevin.androidframework.resources.Audio;
6 | import com.gamesbykevin.sokoban.screen.OptionsScreen;
7 | import com.gamesbykevin.sokoban.screen.OptionsScreen.ButtonKey;
8 |
9 | /**
10 | * Save the settings to the internal storage
11 | * @author GOD
12 | */
13 | public final class Settings extends Internal
14 | {
15 | //our options screen reference object
16 | private final OptionsScreen screen;
17 |
18 | /**
19 | * This string will separate each setting
20 | */
21 | private static final String SEPARATOR = ";";
22 |
23 | public Settings(final OptionsScreen screen, final Activity activity)
24 | {
25 | super("Settings", activity);
26 |
27 | //store our screen reference object
28 | this.screen = screen;
29 |
30 | //if content exists load it
31 | if (super.getContent().toString().trim().length() > 0)
32 | {
33 | try
34 | {
35 | //split the content into an array
36 | final String[] data = super.getContent().toString().split(SEPARATOR);
37 |
38 | for (int index = 0; index < ButtonKey.values().length; index++)
39 | {
40 | //get the index value in this array element
41 | int indexValue = Integer.parseInt(data[index]);
42 |
43 | //restore settings
44 | screen.setIndex(ButtonKey.values()[index], indexValue);
45 |
46 | //if the sound option, we need to flag the audio enabled/disabled
47 | if (ButtonKey.values()[index] == ButtonKey.Sound)
48 | Audio.setAudioEnabled(indexValue == 0);
49 | }
50 | }
51 | catch (Exception e)
52 | {
53 | e.printStackTrace();
54 | }
55 | }
56 |
57 | //make sure the text in the buttons are aligned
58 | screen.reset();
59 | }
60 |
61 | /**
62 | * Save the settings to the internal storage
63 | */
64 | @Override
65 | public void save()
66 | {
67 | try
68 | {
69 | //remove all existing content
70 | super.getContent().delete(0, super.getContent().length());
71 |
72 | //save every option we have in our options screen
73 | for (ButtonKey key : ButtonKey.values())
74 | {
75 | //add the data to our string builder
76 | super.getContent().append(screen.getButtons().get(key).getIndex());
77 |
78 | //add delimiter to separate each setting
79 | super.getContent().append(SEPARATOR);
80 | }
81 |
82 | //remove the last character on the end
83 | super.getContent().deleteCharAt(super.getContent().length() - 1);
84 |
85 | //save data
86 | super.save();
87 | }
88 | catch (Exception e)
89 | {
90 | e.printStackTrace();
91 | }
92 | }
93 |
94 | @Override
95 | public void dispose()
96 | {
97 | super.dispose();
98 | }
99 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/ai/AI.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.ai;
2 |
3 | import java.util.List;
4 |
5 | import com.gamesbykevin.androidframework.resources.Files;
6 | import com.gamesbykevin.sokoban.assets.Assets;
7 | import com.gamesbykevin.sokoban.level.Level;
8 | import com.gamesbykevin.sokoban.player.Player;
9 | import com.gamesbykevin.sokoban.player.PlayerHelper;
10 | import com.gamesbykevin.sokoban.thread.MainThread;
11 |
12 | /**
13 | * What he ai class will do is analyze a text file and move the player according to those instructions
14 | */
15 | public class AI
16 | {
17 | //list of instructions to follow
18 | private String instructions = "";
19 |
20 | //the position of the instructions String
21 | private int index = 0;
22 |
23 | /**
24 | * The string representing the instruction to move left
25 | */
26 | private static final String LEFT = "L";
27 |
28 | /**
29 | * The string representing the instruction to move right
30 | */
31 | private static final String RIGHT = "R";
32 |
33 | /**
34 | * The string representing the instruction to move down
35 | */
36 | private static final String DOWN = "D";
37 |
38 | /**
39 | * The string representing the instruction to move up
40 | */
41 | private static final String UP = "U";
42 |
43 | //the list of solutions for each level
44 | private List levels;
45 |
46 | /**
47 | * Create the ai to solve the level
48 | */
49 | public AI()
50 | {
51 | //set our list of levels
52 | this.setLevels(Assets.TextAiInstructionsKey.SOLVED_EASY_A);
53 |
54 | //start at the beginning
55 | this.reset(0);
56 | }
57 |
58 | /**
59 | * Assign the levels we want to solve
60 | * @param key The unique identifier of the list of levels we want to solve
61 | */
62 | public final void setLevels(Assets.TextAiInstructionsKey key)
63 | {
64 | //get all the lines in the text file
65 | this.levels = Files.getText(key).getLines();
66 | }
67 |
68 | /**
69 | * Reset the ai for the specified level
70 | * @param levelIndex The desired level to solve
71 | */
72 | public final void reset(final int levelIndex)
73 | {
74 | //set the text position back at 0
75 | this.index = 0;
76 |
77 | //get the data for this level
78 | String[] data = this.levels.get(levelIndex).split(" ");
79 |
80 | //the instructions are always the last piece of data
81 | this.instructions = data[data.length - 1].trim();
82 |
83 | if (MainThread.DEBUG)
84 | System.out.println("Index=" + index + ",Steps=" + instructions);
85 | }
86 |
87 | public void update(final Player player, final Level level)
88 | {
89 | //stay inbounds
90 | if (index >= instructions.length())
91 | return;
92 |
93 | //make sure we have not yet selected the player
94 | if (!player.isSelected())
95 | {
96 | //make sure the player is at the current target as well
97 | if (player.hasTarget())
98 | {
99 | //make sure blocks aren't moving
100 | if (!level.hasDestination())
101 | return;
102 |
103 | //flag selected true
104 | player.setSelected(true);
105 |
106 | //determine which direction we are moving
107 | if (instructions.substring(index, index + 1).equalsIgnoreCase(DOWN))
108 | {
109 | player.setTarget(player.getCol(), player.getRow() + 1);
110 | }
111 | else if (instructions.substring(index, index + 1).equalsIgnoreCase(UP))
112 | {
113 | player.setTarget(player.getCol(), player.getRow() - 1);
114 | }
115 | else if (instructions.substring(index, index + 1).equalsIgnoreCase(LEFT))
116 | {
117 | player.setTarget(player.getCol() - 1, player.getRow());
118 | }
119 | else if (instructions.substring(index, index + 1).equalsIgnoreCase(RIGHT))
120 | {
121 | player.setTarget(player.getCol() + 1, player.getRow());
122 | }
123 |
124 | //calculate the targets
125 | PlayerHelper.calculateTargets(player, level);
126 |
127 | //move to the next instruction
128 | this.index++;
129 | }
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/screen/PauseScreen.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.screen;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.view.MotionEvent;
8 |
9 | import com.gamesbykevin.androidframework.resources.Disposable;
10 | import com.gamesbykevin.androidframework.resources.Font;
11 | import com.gamesbykevin.androidframework.screen.Screen;
12 |
13 | import com.gamesbykevin.sokoban.assets.Assets;
14 | import com.gamesbykevin.sokoban.panel.GamePanel;
15 |
16 | /**
17 | * The pause screen
18 | * @author ABRAHAM
19 | */
20 | public class PauseScreen implements Screen, Disposable
21 | {
22 | /**
23 | * Custom message displayed on screen
24 | */
25 | private static final String MESSAGE = "Paused";
26 |
27 | //the dimensions of the text message above
28 | private final int pixelW, pixelH;
29 |
30 | //our main screen reference
31 | private final ScreenManager screen;
32 |
33 | //object to paint background
34 | private Paint paint;
35 |
36 | //store the previous state
37 | private ScreenManager.State previous;
38 |
39 | public PauseScreen(final ScreenManager screen)
40 | {
41 | //store our parent reference
42 | this.screen = screen;
43 |
44 | //create paint text object
45 | this.paint = new Paint();
46 | this.paint.setColor(Color.WHITE);
47 | this.paint.setTextSize(64f);
48 | this.paint.setTypeface(Font.getFont(Assets.FontMenuKey.Default));
49 |
50 | //create temporary rectangle
51 | Rect tmp = new Rect();
52 |
53 | //get the rectangle around the message
54 | paint.getTextBounds(MESSAGE, 0, MESSAGE.length(), tmp);
55 |
56 | //store the dimensions
57 | pixelW = tmp.width();
58 | pixelH = tmp.height();
59 | }
60 |
61 | /**
62 | * Set the previous state.
63 | * We need this, so when un-pause we know where to go back
64 | * @param previous The previous state
65 | */
66 | public void setStatePrevious(final ScreenManager.State previous)
67 | {
68 | //only store if not pause
69 | if (previous != ScreenManager.State.Paused)
70 | this.previous = previous;
71 | }
72 |
73 | /**
74 | * Get the previous state
75 | * @return The previous state before the game was paused
76 | */
77 | public ScreenManager.State getStatePrevious()
78 | {
79 | return this.previous;
80 | }
81 |
82 | /**
83 | * Reset any necessary screen elements here
84 | */
85 | @Override
86 | public void reset()
87 | {
88 | //do we need anything here
89 | }
90 |
91 | @Override
92 | public boolean update(final int action, final float x, final float y) throws Exception
93 | {
94 | if (action == MotionEvent.ACTION_UP)
95 | {
96 | //return to the previous state
97 | screen.setState(previous);
98 |
99 | //no need to return additional events
100 | return false;
101 | }
102 |
103 | //return additional motion events
104 | return true;
105 | }
106 |
107 | @Override
108 | public void update() throws Exception
109 | {
110 | //nothing needed to update here
111 | }
112 |
113 | @Override
114 | public void render(final Canvas canvas) throws Exception
115 | {
116 | if (paint != null)
117 | {
118 | //calculate middle
119 | final int x = (GamePanel.WIDTH / 2) - (pixelW / 2);
120 | final int y = (GamePanel.HEIGHT / 2) - (pixelH / 2);
121 |
122 | //draw text
123 | canvas.drawText(MESSAGE, x, y, paint);
124 | }
125 | }
126 |
127 | @Override
128 | public void dispose()
129 | {
130 | if (paint != null)
131 | paint = null;
132 | }
133 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/tile/TileHelper.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level.tile;
2 |
3 | /**
4 | * TileHelper methods
5 | * @author GOD
6 | */
7 | public class TileHelper
8 | {
9 | /**
10 | * The default dimension of a single animation for a tile
11 | */
12 | public static final int ANIMATION_DIMENSION = 64;
13 |
14 | /**
15 | * The default dimension of a tile
16 | */
17 | public static final int DEFAULT_DIMENSION = 64;
18 |
19 | /**
20 | * The default dimension of the goal
21 | */
22 | public static final int ANIMATION_GOAL_DIMENSION = 32;
23 |
24 | /**
25 | * Get the sprite sheet coordinate
26 | * @param col Column
27 | * @return The x coordinate where the animation resides on the sprite sheet
28 | */
29 | public static int getSpriteSheetX(final int col)
30 | {
31 | return (col * DEFAULT_DIMENSION);
32 | }
33 |
34 | /**
35 | * Get the sprite sheet coordinate
36 | * @param row Row
37 | * @return The y coordinate where the animation resides on the sprite sheet
38 | */
39 | public static int getSpriteSheetY(final int row)
40 | {
41 | return (row * DEFAULT_DIMENSION);
42 | }
43 |
44 |
45 | /**
46 | * Is this tile a wall?
47 | * @param tile The tile we want to check
48 | * @return true = yes, false = no
49 | */
50 | public static boolean isWall(final Tile tile)
51 | {
52 | return isWall(tile.getType());
53 | }
54 |
55 | /**
56 | * Is this tile a wall?
57 | * @param type The tile type we want to check
58 | * @return true = yes, false = no
59 | */
60 | public static boolean isWall(final Tile.Type type)
61 | {
62 | //if type is null, return false
63 | if (type == null)
64 | return false;
65 |
66 | switch (type)
67 | {
68 | case Wall:
69 | return true;
70 |
71 | default:
72 | return false;
73 | }
74 | }
75 |
76 | /**
77 | * Is this tile the floor?
78 | * @param tile The tile we want to check
79 | * @return true = yes, false = no
80 | */
81 | public static boolean isFloor(final Tile tile)
82 | {
83 | return isFloor(tile.getType());
84 | }
85 |
86 | /**
87 | * Is this tile the floor?
88 | * @param type The tile type we want to check
89 | * @return true = yes, false = no
90 | */
91 | public static boolean isFloor(final Tile.Type type)
92 | {
93 | //if type is null, return false
94 | if (type == null)
95 | return false;
96 |
97 | switch (type)
98 | {
99 | case Floor:
100 | return true;
101 |
102 | default:
103 | return false;
104 | }
105 | }
106 |
107 |
108 | /**
109 | * Is this tile a block?
110 | * @param tile The tile we want to check
111 | * @return true = yes, false = no
112 | */
113 | public static boolean isBlock(final Tile tile)
114 | {
115 | return isBlock(tile.getType());
116 | }
117 |
118 | /**
119 | * Is this tile a block?
120 | * @param type The tile type we want to check
121 | * @return true = yes, false = no
122 | */
123 | public static boolean isBlock(final Tile.Type type)
124 | {
125 | //if type is null, return false
126 | if (type == null)
127 | return false;
128 |
129 | switch (type)
130 | {
131 | case Block:
132 | return true;
133 |
134 | default:
135 | return false;
136 | }
137 | }
138 |
139 | /**
140 | * Is this tile the goal?
141 | * @param tile The tile we want to check
142 | * @return true = yes, false = no
143 | */
144 | public static boolean isGoal(final Tile tile)
145 | {
146 | return isGoal(tile.getType());
147 | }
148 |
149 | /**
150 | * Is this tile the goal?
151 | * @param type The tile type we want to check
152 | * @return true = yes, false = no
153 | */
154 | public static boolean isGoal(final Tile.Type type)
155 | {
156 | //if type is null, return false
157 | if (type == null)
158 | return false;
159 |
160 | switch (type)
161 | {
162 | case Goal:
163 | return true;
164 |
165 | default:
166 | return false;
167 | }
168 | }
169 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.os.Bundle;
7 | import android.view.Window;
8 | import android.view.WindowManager;
9 |
10 | import com.gamesbykevin.sokoban.panel.GamePanel;
11 |
12 | public class MainActivity extends Activity
13 | {
14 | //our game panel
15 | private GamePanel panel;
16 |
17 | /**
18 | * Our web site address where more games can be found
19 | */
20 | public static final String WEBPAGE_MORE_GAMES_URL = "http://gamesbykevin.com";
21 |
22 | /**
23 | * The web address where this game can be rated
24 | */
25 | public static final String WEBPAGE_RATE_URL = "https://play.google.com/store/apps/details?id=com.gamesbykevin.sokoban";
26 |
27 | /**
28 | * The url that contains the instructions for the game
29 | */
30 | public static final String WEBPAGE_GAME_INSTRUCTIONS_URL = "http://gamesbykevin.com/2015/10/13/sokoban-2/";
31 |
32 | /**
33 | * The face book url
34 | */
35 | public static final String WEBPAGE_FACEBOOK_URL = "https://facebook.com/gamesbykevin";
36 |
37 | /**
38 | * The twitter url
39 | */
40 | public static final String WEBPAGE_TWITTER_URL = "https://twitter.com/gamesbykevin";
41 |
42 | /**
43 | * The youtube url
44 | */
45 | public static final String WEBPAGE_YOUTUBE_URL = "https://youtube.com/gamesbykevin";
46 |
47 | /**
48 | * Called when the activity is first created
49 | * @param savedInstanceState
50 | */
51 | @Override
52 | public void onCreate(Bundle savedInstanceState)
53 | {
54 | //turn the title off
55 | super.requestWindowFeature(Window.FEATURE_NO_TITLE);
56 |
57 | //set the screen to full screen
58 | super.getWindow().setFlags(
59 | WindowManager.LayoutParams.FLAG_FULLSCREEN,
60 | WindowManager.LayoutParams.FLAG_FULLSCREEN
61 | );
62 |
63 | //call parent create
64 | super.onCreate(savedInstanceState);
65 |
66 | //if the panel has not been created
67 | if (getGamePanel() == null)
68 | {
69 | //create the game panel
70 | setGamePanel(new GamePanel(this));
71 |
72 | //add callback to the game panel to intercept events
73 | getGamePanel().getHolder().addCallback(getGamePanel());
74 | }
75 |
76 | //set the content view to our game
77 | setContentView(getGamePanel());
78 | }
79 |
80 | /**
81 | * Override the finish call
82 | */
83 | @Override
84 | public void finish()
85 | {
86 | //cleanup game panel if it exists
87 | if (getGamePanel() != null)
88 | {
89 | getGamePanel().dispose();
90 | setGamePanel(null);
91 | }
92 |
93 | //call parent
94 | super.finish();
95 | }
96 |
97 | /**
98 | * Part of the activity life cycle
99 | */
100 | @Override
101 | public void onStart()
102 | {
103 | //call parent
104 | super.onStart();
105 | }
106 |
107 | /**
108 | * Part of the activity life cycle
109 | */
110 | @Override
111 | public void onStop()
112 | {
113 | //call parent
114 | super.onStop();
115 | }
116 |
117 | /**
118 | * Part of the activity life cycle
119 | */
120 | @Override
121 | public void onDestroy()
122 | {
123 | //finish the activity
124 | this.finish();
125 |
126 | //perform final cleanup
127 | super.onDestroy();
128 | }
129 |
130 | /**
131 | * Part of the activity life cycle
132 | */
133 | @Override
134 | public void onPause()
135 | {
136 | super.onPause();
137 | }
138 |
139 | /**
140 | * Navigate to the desired web page
141 | * @param url The desired url
142 | */
143 | public void openWebpage(final String url)
144 | {
145 | //create action view intent
146 | Intent intent = new Intent(Intent.ACTION_VIEW);
147 |
148 | //the content will be the web page
149 | intent.setData(Uri.parse(url));
150 |
151 | //start this new activity
152 | startActivity(intent);
153 | }
154 |
155 | /**
156 | * Get the game panel.
157 | * @return The object containing our game logic, assets, threads, etc...
158 | */
159 | private GamePanel getGamePanel()
160 | {
161 | return this.panel;
162 | }
163 |
164 | /**
165 | * Assign the game panel
166 | * @param panel The game panel
167 | */
168 | private void setGamePanel(final GamePanel panel)
169 | {
170 | this.panel = panel;
171 | }
172 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/tile/Block.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level.tile;
2 |
3 | import com.gamesbykevin.androidframework.anim.Animation;
4 | import com.gamesbykevin.androidframework.resources.Images;
5 | import com.gamesbykevin.sokoban.assets.Assets;
6 |
7 | /**
8 | * Block
9 | * @author GOD
10 | */
11 | public final class Block extends Tile
12 | {
13 | public enum Style
14 | {
15 | Yellow, Red, Beige, Black, Brown, Purple, Blue, Gray
16 | }
17 |
18 | /**
19 | * Most tiles will only have 1 animation, but the block will have an alternate
20 | */
21 | public enum State
22 | {
23 | Default, Alternate
24 | }
25 |
26 | /**
27 | * Create new block
28 | * @param style The color of the block
29 | */
30 | public Block(final Style style)
31 | {
32 | super(Tile.Type.Block);
33 |
34 | //set animation
35 | setAnimation(style);
36 | }
37 |
38 | public final void setAnimation(final Style style)
39 | {
40 | int x, y, d = TileHelper.ANIMATION_DIMENSION;
41 |
42 | final Assets.ImageGameKey key = Assets.ImageGameKey.Sprites;
43 |
44 | //set dimensions
45 | super.setWidth(d);
46 | super.setHeight(d);
47 |
48 | switch (style)
49 | {
50 | case Yellow:
51 | x = TileHelper.getSpriteSheetX(3);
52 | y = TileHelper.getSpriteSheetY(0);
53 | getSpritesheet().add(State.Default, new Animation(Images.getImage(key), x, y, d, d));
54 |
55 | x = TileHelper.getSpriteSheetX(2);
56 | y = TileHelper.getSpriteSheetY(4);
57 | getSpritesheet().add(State.Alternate, new Animation(Images.getImage(key), x, y, d, d));
58 | break;
59 |
60 | case Red:
61 | x = TileHelper.getSpriteSheetX(2);
62 | y = TileHelper.getSpriteSheetY(5);
63 | getSpritesheet().add(State.Default, new Animation(Images.getImage(key), x, y, d, d));
64 |
65 | x = TileHelper.getSpriteSheetX(4);
66 | y = TileHelper.getSpriteSheetY(1);
67 | getSpritesheet().add(State.Alternate, new Animation(Images.getImage(key), x, y, d, d));
68 | break;
69 |
70 | case Beige:
71 | x = TileHelper.getSpriteSheetX(3);
72 | y = TileHelper.getSpriteSheetY(1);
73 | getSpritesheet().add(State.Default, new Animation(Images.getImage(key), x, y, d, d));
74 |
75 | x = TileHelper.getSpriteSheetX(5);
76 | y = TileHelper.getSpriteSheetY(1);
77 | getSpritesheet().add(State.Alternate, new Animation(Images.getImage(key), x, y, d, d));
78 | break;
79 |
80 | case Black:
81 | x = TileHelper.getSpriteSheetX(3);
82 | y = TileHelper.getSpriteSheetY(2);
83 | getSpritesheet().add(State.Default, new Animation(Images.getImage(key), x, y, d, d));
84 |
85 | x = TileHelper.getSpriteSheetX(5);
86 | y = TileHelper.getSpriteSheetY(0);
87 | getSpritesheet().add(State.Alternate, new Animation(Images.getImage(key), x, y, d, d));
88 | break;
89 |
90 | case Brown:
91 | x = TileHelper.getSpriteSheetX(3);
92 | y = TileHelper.getSpriteSheetY(4);
93 | getSpritesheet().add(State.Default, new Animation(Images.getImage(key), x, y, d, d));
94 |
95 | x = TileHelper.getSpriteSheetX(4);
96 | y = TileHelper.getSpriteSheetY(4);
97 | getSpritesheet().add(State.Alternate, new Animation(Images.getImage(key), x, y, d, d));
98 | break;
99 |
100 | case Purple:
101 | x = TileHelper.getSpriteSheetX(4);
102 | y = TileHelper.getSpriteSheetY(2);
103 | getSpritesheet().add(State.Default, new Animation(Images.getImage(key), x, y, d, d));
104 |
105 | x = TileHelper.getSpriteSheetX(4);
106 | y = TileHelper.getSpriteSheetY(0);
107 | getSpritesheet().add(State.Alternate, new Animation(Images.getImage(key), x, y, d, d));
108 | break;
109 |
110 | case Blue:
111 | x = TileHelper.getSpriteSheetX(3);
112 | y = TileHelper.getSpriteSheetY(3);
113 | getSpritesheet().add(State.Default, new Animation(Images.getImage(key), x, y, d, d));
114 |
115 | x = TileHelper.getSpriteSheetX(4);
116 | y = TileHelper.getSpriteSheetY(5);
117 | getSpritesheet().add(State.Alternate, new Animation(Images.getImage(key), x, y, d, d));
118 | break;
119 |
120 | case Gray:
121 | x = TileHelper.getSpriteSheetX(3);
122 | y = TileHelper.getSpriteSheetY(5);
123 | getSpritesheet().add(State.Default, new Animation(Images.getImage(key), x, y, d, d));
124 |
125 | x = TileHelper.getSpriteSheetX(4);
126 | y = TileHelper.getSpriteSheetY(3);
127 | getSpritesheet().add(State.Alternate, new Animation(Images.getImage(key), x, y, d, d));
128 | break;
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/screen/ExitScreen.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.screen;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.graphics.Rect;
7 | import android.view.MotionEvent;
8 |
9 | import com.gamesbykevin.androidframework.awt.Button;
10 | import com.gamesbykevin.androidframework.resources.Audio;
11 | import com.gamesbykevin.androidframework.resources.Disposable;
12 | import com.gamesbykevin.androidframework.resources.Images;
13 | import com.gamesbykevin.androidframework.screen.Screen;
14 | import com.gamesbykevin.sokoban.assets.Assets;
15 | import com.gamesbykevin.sokoban.panel.GamePanel;
16 | import java.util.HashMap;
17 |
18 | /**
19 | * The exit screen, when the player wants to go back to the menu
20 | * @author GOD
21 | */
22 | public class ExitScreen implements Screen, Disposable
23 | {
24 | /**
25 | * Custom message displayed on screen
26 | */
27 | private static final String MESSAGE = "Go back to menu?";
28 |
29 | //where our message is to be rendered
30 | private final int messageX, messageY;
31 |
32 | //our main screen reference
33 | private final ScreenManager screen;
34 |
35 | //object to paint background
36 | private Paint paint;
37 |
38 | //all of the buttons for the player to control
39 | private HashMap buttons;
40 |
41 | /**
42 | * The dimensions of the buttons
43 | */
44 | private static final int BUTTON_DIMENSION = 96;
45 |
46 | /**
47 | * Font size for the message
48 | */
49 | private static final float MESSAGE_FONT_SIZE = 28f;
50 |
51 | public ExitScreen(final ScreenManager screen)
52 | {
53 | //store our parent reference
54 | this.screen = screen;
55 |
56 | //create paint text object
57 | this.paint = new Paint(screen.getPaint());
58 | this.paint.setColor(Color.WHITE);
59 | this.paint.setTextSize(MESSAGE_FONT_SIZE);
60 |
61 | //create temporary rectangle
62 | Rect tmp = new Rect();
63 |
64 | //get the rectangle around the message
65 | paint.getTextBounds(MESSAGE, 0, MESSAGE.length(), tmp);
66 |
67 | //calculate where text message is to be rendered
68 | messageX = (GamePanel.WIDTH / 2) - (tmp.width() / 2);
69 | messageY = (GamePanel.HEIGHT / 2) - (tmp.height() / 2);
70 |
71 | //create buttons
72 | this.buttons = new HashMap();
73 | this.buttons.put(Assets.ImageMenuKey.Cancel, new Button(Images.getImage(Assets.ImageMenuKey.Cancel)));
74 | this.buttons.put(Assets.ImageMenuKey.Confirm, new Button(Images.getImage(Assets.ImageMenuKey.Confirm)));
75 |
76 | //position the buttons below the message
77 | final int y = messageY + tmp.height();
78 |
79 | //position buttons
80 | this.buttons.get(Assets.ImageMenuKey.Confirm).setX(messageX);
81 | this.buttons.get(Assets.ImageMenuKey.Confirm).setY(y);
82 | this.buttons.get(Assets.ImageMenuKey.Cancel).setX(messageX + tmp.width() - BUTTON_DIMENSION);
83 | this.buttons.get(Assets.ImageMenuKey.Cancel).setY(y);
84 |
85 | //set the bounds of each button
86 | for (Button button : buttons.values())
87 | {
88 | button.setWidth(BUTTON_DIMENSION);
89 | button.setHeight(BUTTON_DIMENSION);
90 | button.updateBounds();
91 | }
92 | }
93 |
94 | /**
95 | * Reset any necessary screen elements here
96 | */
97 | @Override
98 | public void reset()
99 | {
100 | //do we need anything here
101 | }
102 |
103 | @Override
104 | public boolean update(final int action, final float x, final float y) throws Exception
105 | {
106 | if (action == MotionEvent.ACTION_UP)
107 | {
108 | if (buttons.get(Assets.ImageMenuKey.Cancel).contains(x, y))
109 | {
110 | //if cancel, go back to game
111 | screen.setState(ScreenManager.State.Running);
112 |
113 | //play sound effect
114 | Audio.play(Assets.AudioMenuKey.Selection);
115 |
116 | //return true;
117 | return false;
118 | }
119 | else if (buttons.get(Assets.ImageMenuKey.Confirm).contains(x, y))
120 | {
121 | //if confirm, go back to menu
122 | screen.setState(ScreenManager.State.Ready);
123 |
124 | //play sound effect
125 | Audio.play(Assets.AudioMenuKey.Selection);
126 |
127 | //return false;
128 | return false;
129 | }
130 | }
131 |
132 | //yes we want additional motion events
133 | return true;
134 | }
135 |
136 | @Override
137 | public void update() throws Exception
138 | {
139 | //nothing needed to update here
140 | }
141 |
142 | @Override
143 | public void render(final Canvas canvas) throws Exception
144 | {
145 | if (paint != null)
146 | {
147 | //draw text
148 | canvas.drawText(MESSAGE, messageX, messageY, paint);
149 | }
150 |
151 | buttons.get(Assets.ImageMenuKey.Cancel).render(canvas);
152 | buttons.get(Assets.ImageMenuKey.Confirm).render(canvas);
153 | }
154 |
155 | @Override
156 | public void dispose()
157 | {
158 | if (buttons != null)
159 | {
160 | for (Button button : buttons.values())
161 | {
162 | if (button != null)
163 | {
164 | button.dispose();
165 | button = null;
166 | }
167 | }
168 |
169 | buttons.clear();
170 | buttons = null;
171 | }
172 |
173 | if (paint != null)
174 | paint = null;
175 | }
176 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/assets/Assets.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.assets;
2 |
3 | import android.app.Activity;
4 |
5 | import com.gamesbykevin.androidframework.resources.*;
6 |
7 | /**
8 | * This class will contain our game assets
9 | * @author GOD
10 | */
11 | public class Assets
12 | {
13 | /**
14 | * The directory where audio sound effect resources are kept
15 | */
16 | private static final String DIRECTORY_MENU_AUDIO = "audio/menu";
17 |
18 | /**
19 | * The directory where audio sound effect resources are kept
20 | */
21 | private static final String DIRECTORY_GAME_AUDIO = "audio/game";
22 |
23 | /**
24 | * The directory where image resources are kept for the menu
25 | */
26 | private static final String DIRECTORY_MENU_IMAGE = "image/menu";
27 |
28 | /**
29 | * The directory where image resources are kept for the game
30 | */
31 | private static final String DIRECTORY_GAME_IMAGE = "image/game";
32 |
33 | /**
34 | * The directory where font resources are kept
35 | */
36 | private static final String DIRECTORY_MENU_FONT = "font/menu";
37 |
38 | /**
39 | * The directory where font resources are kept
40 | */
41 | private static final String DIRECTORY_GAME_FONT = "font/game";
42 |
43 | /**
44 | * The directory where our text files are kept
45 | */
46 | private static final String DIRECTORY_TEXT = "text";
47 |
48 | /**
49 | * The directory where our text files containing the ai instructions are kept
50 | */
51 | private static final String DIRECTORY_TEXT_SOLVED = "solved";
52 |
53 | /**
54 | * The different fonts used in our game.
55 | * Order these according to the file name in the "font" assets folder.
56 | */
57 | public enum FontMenuKey
58 | {
59 | Default
60 | }
61 |
62 | /**
63 | * The different fonts used in our game.
64 | * Order these according to the file name in the "font" assets folder.
65 | */
66 | public enum FontGameKey
67 | {
68 | Default
69 | }
70 |
71 | /**
72 | * The different images for our menu items
73 | */
74 | public enum ImageMenuKey
75 | {
76 | Background,
77 | Button,
78 | Facebook,
79 | Instructions,
80 | Logo,
81 | Cancel,
82 | Confirm,
83 | Splash,
84 | Twitter,
85 | Youtube
86 | }
87 |
88 | /**
89 | * The different images in our game.
90 | * Order these according to the file name in the "image" assets folder.
91 | */
92 | public enum ImageGameKey
93 | {
94 | LevelIconComplete,
95 | LevelIconIncomplete,
96 | LevelNext,
97 | LevelPrevious,
98 | Exit,
99 | Pause,
100 | Reset,
101 | SoundOff,
102 | SoundOn,
103 | UndoDisabled,
104 | UndoEnabled,
105 | Sprites
106 | }
107 |
108 | /**
109 | * The key of each text file.
110 | * Order these according to the file name in the "text" assets folder.
111 | */
112 | public enum TextKey
113 | {
114 | Easy_A("Easy A"),
115 | Easy_B("Easy B"),
116 | Easy_C("Easy C"),
117 | Easy_D("Easy D"),
118 | Medium_A("Medium A"),
119 | Medium_B("Medium B"),
120 | Medium_C("Medium C"),
121 | Medium_D("Medium D"),
122 | Hard_A("Hard A"),
123 | Hard_B("Hard B"),
124 | Hard_C("Hard C"),
125 | Hard_D("Hard D");
126 |
127 | private final String desc;
128 |
129 | private TextKey(final String desc)
130 | {
131 | this.desc = desc;
132 | }
133 |
134 | public String getDesc()
135 | {
136 | return this.desc;
137 | }
138 | }
139 |
140 | public enum TextAiInstructionsKey
141 | {
142 | SOLVED_EASY_A, SOLVED_EASY_B, SOLVED_EASY_C, SOLVED_EASY_D,
143 | SOLVED_MEDIUM_A, SOLVED_MEDIUM_B, SOLVED_MEDIUM_C, SOLVED_MEDIUM_D,
144 | SOLVED_HARD_A, SOLVED_HARD_B, SOLVED_HARD_C, SOLVED_HARD_D
145 | }
146 |
147 | /**
148 | * The key of each sound in our game.
149 | * Order these according to the file name in the "audio" assets folder.
150 | */
151 | public enum AudioMenuKey
152 | {
153 | Selection
154 | }
155 |
156 | /**
157 | * The key of each sound in our game.
158 | * Order these according to the file name in the "audio" assets folder.
159 | */
160 | public enum AudioGameKey
161 | {
162 | LevelComplete,
163 | Goal,
164 | Music
165 | }
166 |
167 | /**
168 | * Load all assets
169 | * @param activity Object containing AssetManager needed to load assets
170 | * @throws Exception
171 | */
172 | public static final void load(final Activity activity) throws Exception
173 | {
174 | //load all images for the menu
175 | Images.load(activity, ImageMenuKey.values(), DIRECTORY_MENU_IMAGE, true);
176 |
177 | //load all fonts for the menu
178 | Font.load(activity, FontMenuKey.values(), DIRECTORY_MENU_FONT, true);
179 |
180 | //load all audio for the menu
181 | Audio.load(activity, AudioMenuKey.values(), DIRECTORY_MENU_AUDIO, true);
182 |
183 | //load images for the game
184 | Images.load(activity, ImageGameKey.values(), DIRECTORY_GAME_IMAGE, true);
185 |
186 | //load all audio for the game
187 | Audio.load(activity, AudioGameKey.values(), DIRECTORY_GAME_AUDIO, true);
188 |
189 | //load all fonts for the game
190 | Font.load(activity, FontGameKey.values(), DIRECTORY_GAME_FONT, true);
191 |
192 | //load all text files
193 | Files.load(activity, TextKey.values(), DIRECTORY_TEXT, true);
194 |
195 | //load the text files containing the ai instructions to solve a level
196 | Files.load(activity, TextAiInstructionsKey.values(), DIRECTORY_TEXT_SOLVED, true);
197 | }
198 |
199 | /**
200 | * Recycle assets
201 | */
202 | public static void recycle()
203 | {
204 | try
205 | {
206 | Images.dispose();
207 | Font.dispose();
208 | Audio.dispose();
209 | Files.dispose();
210 | }
211 | catch (Exception e)
212 | {
213 | e.printStackTrace();
214 | }
215 | }
216 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/storage/scorecard/ScoreCard.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.storage.scorecard;
2 |
3 | import android.app.Activity;
4 |
5 | import com.gamesbykevin.androidframework.io.storage.Internal;
6 | import com.gamesbykevin.sokoban.game.Game;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Here we will track the completed levels and save it to the internal storage
13 | * @author GOD
14 | */
15 | public final class ScoreCard extends Internal
16 | {
17 | //list of scores
18 | private List scores;
19 |
20 | /**
21 | * New score separator string
22 | */
23 | public static final String NEW_LEVEL_DELIMITER = ";";
24 |
25 | /**
26 | * This string will separate the data contained within a single score
27 | */
28 | public static final String LEVEL_DATA_DELIMITER = "-";
29 |
30 | //our game reference object
31 | private final Game game;
32 |
33 | /**
34 | * Create our score card
35 | * @param game Our game reference object
36 | * @param activity Our activity object
37 | * @param desc The unique name of the score card
38 | */
39 | public ScoreCard(final Game game, final Activity activity, final String desc)
40 | {
41 | //there will be a score card for each difficulty
42 | super("ScoreCard_" + desc, activity);
43 |
44 | //store our game reference object
45 | this.game = game;
46 |
47 | //create new score
48 | this.scores = new ArrayList();
49 |
50 | //make sure content exists before we try to load it
51 | if (super.getContent().toString().trim().length() > 0)
52 | {
53 | //load file with each level on a new line
54 | final String[] scores = super.getContent().toString().split(NEW_LEVEL_DELIMITER);
55 |
56 | //load each level into our array
57 | for (int index = 0; index < scores.length; index++)
58 | {
59 | //split level data
60 | String[] data = scores[index].split(LEVEL_DATA_DELIMITER);
61 |
62 | //get the information
63 | final int level = Integer.parseInt(data[0]);
64 | final int moves = Integer.parseInt(data[1]);
65 | long time;
66 |
67 | try
68 | {
69 | time = Long.parseLong(data[2]);
70 | }
71 | catch (Exception e)
72 | {
73 | time = (long)Double.parseDouble(data[2]);
74 | }
75 |
76 | //load the score to our list
77 | update(level, moves, time);
78 | }
79 | }
80 | }
81 |
82 | /**
83 | * Do we have the score?
84 | * We want the score of the specified level
85 | * @param level The level index
86 | * @return true if the specified score exists, false otherwise
87 | */
88 | public boolean hasScore(final int level)
89 | {
90 | return (getScore(level) != null);
91 | }
92 |
93 | /**
94 | * Do we have the score?
95 | * We will get the score of the current level to see if exists
96 | * @return true if the specified score exists, false otherwise
97 | */
98 | public boolean hasScore()
99 | {
100 | //use the current level index, and colors
101 | return hasScore(game.getLevels().getLevelSelect().getLevelIndex());
102 | }
103 |
104 | /**
105 | * Get the score object.
106 | * We will get the score of the current level
107 | * @return The score object of the current level, if not found null is returned
108 | */
109 | public Score getScore()
110 | {
111 | return getScore(game.getLevels().getLevelSelect().getLevelIndex());
112 | }
113 |
114 | /**
115 | * Get the score object of the specified level
116 | * @param level The level index
117 | * @return The score object of the specified level, if not found null is returned
118 | */
119 | public Score getScore(final int level)
120 | {
121 | //check our list to see if the specified score is better
122 | for (Score score : scores)
123 | {
124 | //if the level and colors match return the score
125 | if (score.getLevel() == level)
126 | return score;
127 | }
128 |
129 | //score was not found
130 | return null;
131 | }
132 |
133 | /**
134 | * Update the score for the specified level.
135 | * If the score does not exist, it will be added.
136 | * @param level The specified level
137 | * @param moves The number of moves to solve the level
138 | * @param time The duration it took to solve the level
139 | * @return true if updating the score was successful, false otherwise
140 | */
141 | public boolean update(final int level, final int moves, final long time)
142 | {
143 | //get the score for the level
144 | final Score score = getScore(level);
145 |
146 | //if the score does not exist, add it
147 | if (score == null)
148 | {
149 | //score does not exist, so add it
150 | scores.add(new Score(level, moves, time));
151 | }
152 | else
153 | {
154 | //only update if the new score is less time and has <= the existing moves
155 | if (moves <= score.getMoves() && time < score.getTime())
156 | {
157 | //update the moves
158 | score.setMoves(moves);
159 |
160 | //update the time
161 | score.setTime(time);
162 | }
163 | else
164 | {
165 | //the score was not better, return false
166 | return false;
167 | }
168 | }
169 |
170 | //save the score
171 | save();
172 |
173 | //score was updated
174 | return true;
175 | }
176 |
177 | /**
178 | * Save the scores to the internal storage
179 | */
180 | @Override
181 | public void save()
182 | {
183 | //remove all existing content
184 | super.getContent().delete(0, super.getContent().length());
185 |
186 | //save each score
187 | for (Score score : scores)
188 | {
189 | //if content exists, add delimiter to separate each score
190 | if (super.getContent().length() > 0)
191 | super.getContent().append(NEW_LEVEL_DELIMITER);
192 |
193 | //write level, size, and time
194 | super.getContent().append(score.getLevel());
195 | super.getContent().append(LEVEL_DATA_DELIMITER);
196 | super.getContent().append(score.getMoves());
197 | super.getContent().append(LEVEL_DATA_DELIMITER);
198 | super.getContent().append(score.getTime());
199 | }
200 |
201 | //save the content to physical internal storage location
202 | super.save();
203 | }
204 |
205 | @Override
206 | public void dispose()
207 | {
208 | super.dispose();
209 |
210 | if (scores != null)
211 | {
212 | scores.clear();
213 | scores = null;
214 | }
215 | }
216 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/player/PlayerHelper.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.player;
2 |
3 | import com.gamesbykevin.sokoban.level.Level;
4 | import com.gamesbykevin.sokoban.level.tile.TileHelper;
5 | import com.gamesbykevin.sokoban.target.Target;
6 | import com.gamesbykevin.sokoban.thread.MainThread;
7 |
8 | /**
9 | * Player helper methods
10 | * @author GOD
11 | */
12 | public class PlayerHelper
13 | {
14 | /**
15 | * Assign the specified player the correct walking animation
16 | * @param player The player we want to assign animation
17 | */
18 | public static void startWalking(final Player player)
19 | {
20 | if (player.getCol() != player.getTarget().getCol())
21 | {
22 | //if heading east
23 | if (player.getCol() < player.getTarget().getCol())
24 | {
25 | player.setAnimation(Player.Key.WalkEast);
26 | }
27 | else
28 | {
29 | player.setAnimation(Player.Key.WalkWest);
30 | }
31 | }
32 | else if (player.getRow() != player.getTarget().getRow())
33 | {
34 | //if heading south
35 | if (player.getRow() < player.getTarget().getRow())
36 | {
37 | player.setAnimation(Player.Key.WalkSouth);
38 | }
39 | else
40 | {
41 | player.setAnimation(Player.Key.WalkNorth);
42 | }
43 | }
44 | }
45 |
46 | /**
47 | * Assign the specified player the correct idle animation
48 | * @param player The player we want to assign animation
49 | */
50 | public static void stopWalking(final Player player)
51 | {
52 | //set idle animation
53 | switch (player.getAnimation())
54 | {
55 | case WalkEast:
56 | player.setAnimation(Player.Key.IdleEast);
57 | break;
58 |
59 | case WalkWest:
60 | player.setAnimation(Player.Key.IdleWest);
61 | break;
62 |
63 | case WalkNorth:
64 | player.setAnimation(Player.Key.IdleNorth);
65 | break;
66 |
67 | case WalkSouth:
68 | default:
69 | player.setAnimation(Player.Key.IdleSouth);
70 | break;
71 | }
72 | }
73 |
74 | /**
75 | * Manage the specified player's velocity and update (col, row) location
76 | * @param player The player we want to manage
77 | */
78 | public static void manageVelocity(final Player player)
79 | {
80 | //velocity will depend if debugging
81 | final double velocity = (MainThread.DEBUG) ? Player.VELOCITY_DEBUG : Player.VELOCITY;
82 |
83 | if (player.getCol() < player.getTarget().getCol())
84 | {
85 | if (player.getCol() + velocity >= player.getTarget().getCol())
86 | {
87 | player.setCol(player.getTarget().getCol());
88 | }
89 | else
90 | {
91 | player.setCol(player.getCol() + velocity);
92 | }
93 | }
94 | else if (player.getCol() > player.getTarget().getCol())
95 | {
96 | if (player.getCol() - velocity <= player.getTarget().getCol())
97 | {
98 | player.setCol(player.getTarget().getCol());
99 | }
100 | else
101 | {
102 | player.setCol(player.getCol() - velocity);
103 | }
104 | }
105 | else if (player.getRow() < player.getTarget().getRow())
106 | {
107 | if (player.getRow() + velocity >= player.getTarget().getRow())
108 | {
109 | player.setRow(player.getTarget().getRow());
110 | }
111 | else
112 | {
113 | player.setRow(player.getRow() + velocity);
114 | }
115 | }
116 | else if (player.getRow() > player.getTarget().getRow())
117 | {
118 | if (player.getRow() - velocity <= player.getTarget().getRow())
119 | {
120 | player.setRow(player.getTarget().getRow());
121 | }
122 | else
123 | {
124 | player.setRow(player.getRow() - velocity);
125 | }
126 | }
127 | }
128 |
129 | /**
130 | * Calculate the targets.
131 | * We will determine the target of the player, as well as the neighboring block (if exists)
132 | * @param player The player we are checking
133 | * @param level Current level in play
134 | */
135 | public static void calculateTargets(final Player player, final Level level)
136 | {
137 | //make sure all targets in a level have the current destination marked
138 | for (Target block : level.getCurrent())
139 | {
140 | //assign current location so the undo is correct
141 | block.setDestination(block.getCol(), block.getRow());
142 | }
143 |
144 | //the neighboring locations
145 | final int col1, row1;
146 | final int col2, row2;
147 |
148 | //determine where the neighbor locations are
149 | if (player.getCol() < player.getTarget().getCol())
150 | {
151 | col1 = (int)player.getCol() + 1;
152 | row1 = (int)player.getRow();
153 | col2 = col1 + 1;
154 | row2 = row1;
155 | }
156 | else if (player.getCol() > player.getTarget().getCol())
157 | {
158 | col1 = (int)player.getCol() - 1;
159 | row1 = (int)player.getRow();
160 | col2 = col1 - 1;
161 | row2 = row1;
162 | }
163 | else if (player.getRow() < player.getTarget().getRow())
164 | {
165 | col1 = (int)player.getCol();
166 | row1 = (int)player.getRow() + 1;
167 | col2 = col1;
168 | row2 = row1 + 1;
169 | }
170 | else
171 | {
172 | //(player.getRow() > player.getTarget().getRow())
173 | col1 = (int)player.getCol();
174 | row1 = (int)player.getRow() - 1;
175 | col2 = col1;
176 | row2 = row1 - 1;
177 | }
178 |
179 | //if there is a wall directly next door
180 | if (TileHelper.isWall(level.getType(col1, row1)))
181 | {
182 | //we won't move the target
183 | player.setTarget(player.getCol(), player.getRow());
184 | }
185 | else
186 | {
187 | //if there is a block next door, lets see if we can move it
188 | if (level.getBlock(col1, row1) != null)
189 | {
190 | //if there is a wall or block on the other end, the player won't be able to move
191 | if (TileHelper.isWall(level.getType(col2, row2)) || level.getBlock(col2, row2) != null)
192 | {
193 | //we won't move the target
194 | player.setTarget(player.getCol(), player.getRow());
195 | }
196 | else
197 | {
198 | //player can move to the next location
199 | player.setTarget(col1, row1);
200 |
201 | //move the block as well
202 | level.getBlock(col1, row1).setDestination(col2, row2);
203 |
204 | //increase move count
205 | player.setMoves(player.getMoves() + 1);
206 | }
207 | }
208 | else
209 | {
210 | //player can move to the next location
211 | player.setTarget(col1, row1);
212 |
213 | //increase move count
214 | player.setMoves(player.getMoves() + 1);
215 | }
216 | }
217 | }
218 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/level/LevelHelper.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.level;
2 |
3 | import com.gamesbykevin.sokoban.level.tile.*;
4 | import com.gamesbykevin.sokoban.panel.GamePanel;
5 | import com.gamesbykevin.sokoban.target.Target;
6 |
7 | import java.util.HashMap;
8 | import java.util.ArrayList;
9 | import java.util.List;
10 |
11 | /**
12 | * Level Helper methods
13 | * @author GOD
14 | */
15 | public class LevelHelper
16 | {
17 | private static final int WALL_COLOR_GRAY = 0;
18 | private static final int WALL_COLOR_BROWN = 1;
19 | private static final int WALL_COLOR_BLACK = 2;
20 | private static final int WALL_COLOR_BEIGE = 3;
21 |
22 | /**
23 | * Create the tiles and select animations
24 | * @param tiles The list of tiles for our level
25 | */
26 | public static void createTiles(final HashMap tiles)
27 | {
28 | //create new list of possible block styles
29 | List styles = new ArrayList();
30 |
31 | //pick random wall
32 | final int random = GamePanel.RANDOM.nextInt(4);
33 |
34 | //handle the animation accordingly
35 | switch (random)
36 | {
37 | case WALL_COLOR_GRAY:
38 |
39 | //create wall
40 | tiles.put(Tile.Type.Wall, new Wall(Wall.Style.Gray));
41 |
42 | //create floor
43 | tiles.put(Tile.Type.Floor, new Floor(GamePanel.RANDOM.nextBoolean() ? Floor.Style.Gray1 : Floor.Style.Gray2));
44 |
45 | //add possible styles
46 | styles.add(Block.Style.Red);
47 | styles.add(Block.Style.Yellow);
48 | styles.add(Block.Style.Blue);
49 | styles.add(Block.Style.Purple);
50 | styles.add(Block.Style.Black);
51 | styles.add(Block.Style.Brown);
52 | break;
53 |
54 | case WALL_COLOR_BROWN:
55 |
56 | //create wall
57 | tiles.put(Tile.Type.Wall, new Wall(Wall.Style.Brown));
58 |
59 | //create floor
60 | tiles.put(Tile.Type.Floor, new Floor(GamePanel.RANDOM.nextBoolean() ? Floor.Style.Brown1 : Floor.Style.Brown2));
61 |
62 | //add possible styles
63 | styles.add(Block.Style.Red);
64 | styles.add(Block.Style.Yellow);
65 | styles.add(Block.Style.Blue);
66 | styles.add(Block.Style.Purple);
67 | styles.add(Block.Style.Gray);
68 | styles.add(Block.Style.Black);
69 | break;
70 |
71 | case WALL_COLOR_BLACK:
72 |
73 | //create wall
74 | tiles.put(Tile.Type.Wall, new Wall(Wall.Style.Black));
75 |
76 | //create floor
77 | tiles.put(Tile.Type.Floor, new Floor(GamePanel.RANDOM.nextBoolean() ? Floor.Style.Green1 : Floor.Style.Green2));
78 |
79 | //add possible styles
80 | styles.add(Block.Style.Red);
81 | styles.add(Block.Style.Yellow);
82 | styles.add(Block.Style.Blue);
83 | styles.add(Block.Style.Purple);
84 | styles.add(Block.Style.Beige);
85 | styles.add(Block.Style.Gray);
86 | styles.add(Block.Style.Brown);
87 | break;
88 |
89 | default:
90 | case WALL_COLOR_BEIGE:
91 |
92 | //create wall
93 | tiles.put(Tile.Type.Wall, new Wall(Wall.Style.Beige));
94 |
95 | //create floor
96 | tiles.put(Tile.Type.Floor, new Floor(GamePanel.RANDOM.nextBoolean() ? Floor.Style.Beige1 : Floor.Style.Beige2));
97 |
98 | //add possible styles
99 | styles.add(Block.Style.Red);
100 | styles.add(Block.Style.Yellow);
101 | styles.add(Block.Style.Blue);
102 | styles.add(Block.Style.Purple);
103 | styles.add(Block.Style.Brown);
104 | styles.add(Block.Style.Black);
105 | break;
106 | }
107 |
108 | //pick random index
109 | final int index = GamePanel.RANDOM.nextInt(styles.size());
110 |
111 | //assign the goal color based on the random chosen block color
112 | switch (styles.get(index))
113 | {
114 | case Red:
115 | tiles.put(Tile.Type.Goal, new Goal(Goal.Style.Red));
116 | break;
117 |
118 | case Yellow:
119 | tiles.put(Tile.Type.Goal, new Goal(Goal.Style.Yellow));
120 | break;
121 |
122 | case Blue:
123 | tiles.put(Tile.Type.Goal, new Goal(Goal.Style.Blue));
124 | break;
125 |
126 | case Purple:
127 | tiles.put(Tile.Type.Goal, new Goal(Goal.Style.Purple));
128 | break;
129 |
130 | case Brown:
131 | tiles.put(Tile.Type.Goal, new Goal(Goal.Style.Brown));
132 | break;
133 |
134 | case Black:
135 | tiles.put(Tile.Type.Goal, new Goal(Goal.Style.Black));
136 | break;
137 |
138 | case Beige:
139 | tiles.put(Tile.Type.Goal, new Goal(Goal.Style.Beige));
140 | break;
141 |
142 | default:
143 | case Gray:
144 | tiles.put(Tile.Type.Goal, new Goal(Goal.Style.Gray));
145 | break;
146 | }
147 |
148 | //add block to hash map
149 | tiles.put(Tile.Type.Block, new Block(styles.get(index)));
150 | }
151 |
152 | /**
153 | * Get the x-coordinate of the specified column
154 | * @param level Current level of play
155 | * @param col column location
156 | * @return coordinate of specified location
157 | */
158 | public static final double getX(final Level level, final double col)
159 | {
160 | return level.getStartX() + (col * TileHelper.DEFAULT_DIMENSION);
161 | }
162 |
163 | /**
164 | * Get the y-coordinate of the specified row
165 | * @param level Current level of play
166 | * @param row row location
167 | * @return coordinate of specified location
168 | */
169 | public static final double getY(final Level level, final double row)
170 | {
171 | return level.getStartY() + (row * TileHelper.DEFAULT_DIMENSION);
172 | }
173 |
174 | /**
175 | * Get the column
176 | * @param level Current level of game play
177 | * @param x x-coordinate
178 | * @return The columns of the specified coordinate
179 | */
180 | public static final double getCol(final Level level, final double x)
181 | {
182 | if (x < level.getStartX())
183 | return -1;
184 |
185 | return ((x - level.getStartX()) / TileHelper.DEFAULT_DIMENSION);
186 | }
187 |
188 | /**
189 | * Get the row
190 | * @param level Current level of game play
191 | * @param y y-coordinate
192 | * @return The row of the specified coordinate
193 | */
194 | public static final double getRow(final Level level, final double y)
195 | {
196 | if (y < level.getStartY())
197 | return -1;
198 |
199 | return ((y - level.getStartY()) / TileHelper.DEFAULT_DIMENSION);
200 | }
201 |
202 | /**
203 | * Has the level been completed?
204 | * @param level The current level we want to check
205 | * @return true if all blocks are at a goal, false otherwise
206 | */
207 | public static final boolean hasCompleted(final Level level)
208 | {
209 | //check all targets
210 | for (Target target : level.getCurrent())
211 | {
212 | //if 1 target is not at the goal, the level is not complete
213 | if (!target.hasGoal())
214 | return false;
215 | }
216 |
217 | //all targets are at a goal, return true
218 | return true;
219 | }
220 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/thread/MainThread.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.thread;
2 |
3 | import android.graphics.Canvas;
4 | import android.view.SurfaceHolder;
5 | import com.gamesbykevin.androidframework.anim.Animation;
6 |
7 | import com.gamesbykevin.sokoban.panel.GamePanel;
8 |
9 | /**
10 | * Our main thread containing the game loop
11 | * @author ABRAHAM
12 | */
13 | public class MainThread extends Thread
14 | {
15 | /**
16 | * Is debug mode enabled?
17 | */
18 | public static final boolean DEBUG = true;
19 |
20 | //the assigned frames per second for this game
21 | public static final int FPS = 30;
22 |
23 | //our game panel
24 | private final GamePanel panel;
25 |
26 | //area where game play is rendered
27 | private final SurfaceHolder holder;
28 |
29 | //is the thread running
30 | private boolean running;
31 |
32 | //our canvas to render image(s)
33 | private Canvas canvas;
34 |
35 | /**
36 | * Default time to sleep our thread when paused
37 | */
38 | private static final long DEFAULT_SLEEP = 250;
39 |
40 | /**
41 | * When the game is terminated and recycling variables,
42 | * this is the maximum number of attempts to stop the thread
43 | */
44 | public static final int COMPLETE_THREAD_ATTEMPTS = 1000;
45 |
46 | //do we pause the update/render
47 | private boolean pause = false;
48 |
49 | public MainThread(SurfaceHolder holder, GamePanel panel)
50 | {
51 | //call parent constructor
52 | super();
53 |
54 | //assign surface holder reference object
55 | this.holder = holder;
56 |
57 | //assign game panel reference object
58 | this.panel = panel;
59 | }
60 |
61 | @Override
62 | public void run()
63 | {
64 | //track total time elapsed to calculate fps
65 | long totalTime = 0;
66 |
67 | //the frame count
68 | int frames = 0;
69 |
70 | //the expected amount of time per each update
71 | final long targetTime = (Animation.MILLISECONDS_PER_SECOND / FPS);
72 |
73 | try
74 | {
75 | //continue to loop while the thread is running
76 | while (isRunning())
77 | {
78 | //if the game is paused we won't continue
79 | if (isPaused())
80 | {
81 | //sleep for a default time
82 | sleep(DEFAULT_SLEEP);
83 |
84 | //we are paused so we can't continue
85 | continue;
86 | }
87 |
88 | //get the start time of this update
89 | final long startTime = System.nanoTime();
90 |
91 | //assign the canvas null
92 | setCanvas(null);
93 |
94 | try
95 | {
96 | //attempt to lock the canvas to edit the pixels of the surface
97 | setCanvas(getHolder().lockCanvas());
98 |
99 | //make sure no other threads are accessing the holder
100 | synchronized (getHolder())
101 | {
102 | //update our game panel
103 | getPanel().update();
104 |
105 | //if the canvas object was obtained and we did not pause, render
106 | if (getCanvas() != null && !isPaused())
107 | getPanel().draw(getCanvas());
108 | }
109 | }
110 | catch (Exception e)
111 | {
112 | e.printStackTrace();
113 | }
114 | finally
115 | {
116 | //remove the lock (if possible)
117 | if (getCanvas() != null)
118 | {
119 | try
120 | {
121 | //render the pixels on the canvas to the screen
122 | getHolder().unlockCanvasAndPost(getCanvas());
123 | }
124 | catch (Exception e)
125 | {
126 | e.printStackTrace();
127 | }
128 | }
129 | }
130 |
131 | //calculate the number of milliseconds elapsed
132 | final long timeMillis = (System.nanoTime() - startTime) / Animation.NANO_SECONDS_PER_MILLISECOND;
133 |
134 | //determine the amount of time to sleep
135 | long waitTime = targetTime - timeMillis;
136 |
137 | //make sure the wait time is at least 1 millisecond
138 | if (waitTime < 1)
139 | waitTime = 1;
140 |
141 | try
142 | {
143 | //sleep the thread
144 | sleep(waitTime);
145 | }
146 | catch (Exception e)
147 | {
148 | e.printStackTrace();
149 | }
150 |
151 | //if we are debugging, print the fps
152 | if (DEBUG)
153 | {
154 | //calculate the total time passed
155 | totalTime += System.nanoTime() - startTime;
156 |
157 | //increase the frame count
158 | frames++;
159 |
160 | //if the frame count = the assigned fps
161 | if (frames == FPS)
162 | {
163 | //calculate the average fps
164 | final double fpsAverage = (double)Animation.MILLISECONDS_PER_SECOND / ((double)(totalTime / frames) / Animation.NANO_SECONDS_PER_MILLISECOND);
165 |
166 | //reset these values
167 | frames = 0;
168 | totalTime = 0;
169 |
170 | //display the average
171 | System.out.println("Average FPS " + fpsAverage);
172 | }
173 | }
174 | }
175 | }
176 | catch (Exception ex)
177 | {
178 | ex.printStackTrace();
179 | }
180 | finally
181 | {
182 | //stop thread
183 | this.setRunning(false);
184 | }
185 | }
186 |
187 | /**
188 | * Set the pause flag.
189 | * If we are pausing the game we will make sure the canvas is not locked
190 | * @param pause true if you want to prevent the game panel update and render, false otherwise
191 | */
192 | public void setPause(final boolean pause)
193 | {
194 | this.pause = pause;
195 | }
196 |
197 | /**
198 | * Is the thread paused?
199 | * @return true = we will not update or render the game panel object, false = otherwise
200 | */
201 | public boolean isPaused()
202 | {
203 | return this.pause;
204 | }
205 |
206 | /**
207 | * Assign the thread to run.
208 | * @param running true the thread will continue to loop, false the thread will finish
209 | */
210 | public void setRunning(final boolean running)
211 | {
212 | this.running = running;
213 | }
214 |
215 | /**
216 | * Is the thread set to run?
217 | * @return true = yes, false = no
218 | */
219 | public boolean isRunning()
220 | {
221 | return this.running;
222 | }
223 |
224 | /**
225 | * Assign the canvas
226 | * @param canvas The desired canvas object
227 | */
228 | private void setCanvas(final Canvas canvas)
229 | {
230 | this.canvas = canvas;
231 | }
232 |
233 | /**
234 | * Get the canvas
235 | * @return The object we want to write pixel data to
236 | */
237 | private Canvas getCanvas()
238 | {
239 | return this.canvas;
240 | }
241 |
242 | /**
243 | * Get the game panel
244 | * @return The game panel object reference
245 | */
246 | private GamePanel getPanel()
247 | {
248 | return this.panel;
249 | }
250 |
251 | /**
252 | * Get the surface holder
253 | * @return The surface holder reference object
254 | */
255 | private SurfaceHolder getHolder()
256 | {
257 | return this.holder;
258 | }
259 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/game/controller/Controller.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.game.controller;
2 |
3 | import com.gamesbykevin.androidframework.awt.Button;
4 |
5 | import android.graphics.Canvas;
6 | import android.view.MotionEvent;
7 |
8 | import com.gamesbykevin.androidframework.resources.Audio;
9 | import com.gamesbykevin.androidframework.resources.Images;
10 |
11 | import com.gamesbykevin.sokoban.assets.Assets;
12 | import com.gamesbykevin.sokoban.game.Game;
13 | import com.gamesbykevin.sokoban.panel.GamePanel;
14 | import com.gamesbykevin.sokoban.screen.ScreenManager;
15 |
16 | import java.util.HashMap;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | /**
22 | * This class will be our game controller
23 | * @author GOD
24 | */
25 | public class Controller implements IController
26 | {
27 | //all of the buttons for the player to control
28 | private HashMap buttons;
29 |
30 | //our game object reference
31 | private final Game game;
32 |
33 | //location of reset button
34 | private final static int RESET_X = GamePanel.WIDTH - 64;
35 | private final static int RESET_Y = 0;
36 |
37 | //location of undo button
38 | private final static int UNDO_X = GamePanel.WIDTH - 64;
39 | private final static int UNDO_Y = 96;
40 |
41 | //is the undo button enabled
42 | private boolean enabled = false;
43 |
44 | /**
45 | * Default Constructor
46 | * @param game Object game object reference
47 | */
48 | public Controller(final Game game)
49 | {
50 | //assign object reference
51 | this.game = game;
52 |
53 | //create temp list
54 | List tmp = new ArrayList();
55 | tmp.add(Assets.ImageGameKey.SoundOff);
56 | tmp.add(Assets.ImageGameKey.SoundOn);
57 | tmp.add(Assets.ImageGameKey.Pause);
58 | tmp.add(Assets.ImageGameKey.Exit);
59 | tmp.add(Assets.ImageGameKey.Reset);
60 | tmp.add(Assets.ImageGameKey.UndoEnabled);
61 | tmp.add(Assets.ImageGameKey.UndoDisabled);
62 |
63 | //create new list of buttons
64 | this.buttons = new HashMap();
65 |
66 | //add button controls
67 | for (Assets.ImageGameKey key : tmp)
68 | {
69 | this.buttons.put(key, new Button(Images.getImage(key)));
70 | }
71 |
72 | //reset button
73 | this.buttons.get(Assets.ImageGameKey.Reset).setX(RESET_X);
74 | this.buttons.get(Assets.ImageGameKey.Reset).setY(RESET_Y);
75 |
76 | //undo button
77 | this.buttons.get(Assets.ImageGameKey.UndoEnabled).setX(UNDO_X);
78 | this.buttons.get(Assets.ImageGameKey.UndoEnabled).setY(UNDO_Y);
79 | this.buttons.get(Assets.ImageGameKey.UndoDisabled).setX(UNDO_X);
80 | this.buttons.get(Assets.ImageGameKey.UndoDisabled).setY(UNDO_Y);
81 |
82 | int x = 40;
83 | final int y = 710;
84 | final int incrementX = 160;
85 |
86 | this.buttons.get(Assets.ImageGameKey.SoundOff).setX(x);
87 | this.buttons.get(Assets.ImageGameKey.SoundOff).setY(y);
88 | this.buttons.get(Assets.ImageGameKey.SoundOn).setX(x);
89 | this.buttons.get(Assets.ImageGameKey.SoundOn).setY(y);
90 |
91 | x += incrementX;
92 | this.buttons.get(Assets.ImageGameKey.Pause).setX(x);
93 | this.buttons.get(Assets.ImageGameKey.Pause).setY(y);
94 |
95 | x += incrementX;
96 | this.buttons.get(Assets.ImageGameKey.Exit).setX(x);
97 | this.buttons.get(Assets.ImageGameKey.Exit).setY(y);
98 |
99 | //assign boundary
100 | for (Assets.ImageGameKey key : tmp)
101 | {
102 | this.buttons.get(key).updateBounds();
103 | }
104 | }
105 |
106 | /**
107 | * Get our game object reference
108 | * @return Our game object reference
109 | */
110 | private Game getGame()
111 | {
112 | return this.game;
113 | }
114 |
115 | /**
116 | * Update the controller based on the motion event
117 | * @param event Motion Event
118 | * @param x (x-coordinate)
119 | * @param y (y-coordinate)
120 | * @return true if motion event was applied, false otherwise
121 | * @throws Exception
122 | */
123 | public boolean update(final int action, final float x, final float y) throws Exception
124 | {
125 | //check if there was a change
126 | boolean change = false;
127 |
128 | //track the motion events
129 | if (action == MotionEvent.ACTION_UP)
130 | {
131 | //check each button in our list
132 | for (Button button : buttons.values())
133 | {
134 | if (button != null && button.isVisible() && button.contains(x, y))
135 | {
136 | if (button.isPressed())
137 | {
138 | //if contained within the coordinates flag released true
139 | button.setReleased(true);
140 |
141 | //flag change true
142 | change = true;
143 | }
144 | else
145 | {
146 | //if this button wasn't pressed previous, reset
147 | reset();
148 | }
149 | }
150 | else
151 | {
152 | //else flag released false
153 | button.setReleased(false);
154 | }
155 | }
156 | }
157 | else if (action == MotionEvent.ACTION_DOWN)
158 | {
159 | //check each button in our list
160 | for (Button button : buttons.values())
161 | {
162 | if (button != null && button.isVisible() && button.contains(x, y))
163 | {
164 | //if contained within the coordinates flag pressed true
165 | button.setPressed(true);
166 |
167 | //flag change true
168 | change = true;
169 | }
170 | else
171 | {
172 | //else flag pressed false
173 | button.setPressed(false);
174 | }
175 | }
176 | }
177 |
178 | //return if any change was made
179 | return change;
180 | }
181 |
182 | @Override
183 | public void reset()
184 | {
185 | if (buttons != null)
186 | {
187 | //determine which button is displayed
188 | buttons.get(Assets.ImageGameKey.SoundOn).setVisible(Audio.isAudioEnabled());
189 | buttons.get(Assets.ImageGameKey.SoundOff).setVisible(!Audio.isAudioEnabled());
190 |
191 | //reset all buttons
192 | for (Button button : buttons.values())
193 | {
194 | if (button != null)
195 | {
196 | button.setPressed(false);
197 | button.setReleased(false);
198 | }
199 | }
200 | }
201 |
202 | //disable the undo button
203 | setDisabled();
204 | }
205 |
206 | /**
207 | * Determined what button was pressed
208 | */
209 | public void update() throws Exception
210 | {
211 | //we can't continue if the list is null
212 | if (buttons == null)
213 | return;
214 |
215 | //check each button to see what changes
216 | for (Assets.ImageGameKey key : buttons.keySet())
217 | {
218 | //get the current button
219 | Button button = buttons.get(key);
220 |
221 | //if this button has been pressed and released
222 | if (button.isPressed() && button.isReleased())
223 | {
224 | //determine next steps
225 | switch (key)
226 | {
227 | case Pause:
228 | //change the state to paused
229 | getGame().getScreen().setState(ScreenManager.State.Paused);
230 | break;
231 |
232 | case Exit:
233 | //change to the exit confirm screen
234 | getGame().getScreen().setState(ScreenManager.State.Exit);
235 | break;
236 |
237 | case SoundOn:
238 | case SoundOff:
239 | //flip the audio setting
240 | Audio.setAudioEnabled(!Audio.isAudioEnabled());
241 |
242 | //make sure the correct button is showing
243 | if (Audio.isAudioEnabled())
244 | {
245 | //play song
246 | Audio.play(Assets.AudioGameKey.Music, true);
247 | }
248 | else
249 | {
250 | //if audio is not enabled, stop all sound
251 | Audio.stop();
252 | }
253 | break;
254 |
255 | case Reset:
256 | //reset current level
257 | getGame().flagLevelReset();
258 | break;
259 |
260 | case UndoEnabled:
261 | case UndoDisabled:
262 |
263 | //make sure the button is enabled
264 | if (this.enabled)
265 | {
266 | //disable button
267 | setDisabled();
268 |
269 | //undo the previous move
270 | getGame().undo();
271 | }
272 | break;
273 |
274 | default:
275 | throw new Exception("Key is not handled here: " + key.toString());
276 | }
277 |
278 | //reset all buttons
279 | reset();
280 | }
281 | }
282 | }
283 |
284 | /**
285 | * Make the undo button enabled
286 | */
287 | public void setEnabled()
288 | {
289 | this.enabled = true;
290 | }
291 |
292 | /**
293 | * Make the undo button disabled
294 | */
295 | public void setDisabled()
296 | {
297 | this.enabled = false;
298 | }
299 |
300 | /**
301 | * Recycle objects
302 | */
303 | @Override
304 | public void dispose()
305 | {
306 | if (buttons != null)
307 | {
308 | for (Button button : buttons.values())
309 | {
310 | if (button != null)
311 | {
312 | button.dispose();
313 | button = null;
314 | }
315 | }
316 |
317 | buttons.clear();
318 | buttons = null;
319 | }
320 | }
321 |
322 | /**
323 | * Render the controller
324 | * @param canvas Write pixel data to this canvas
325 | * @throws Exception
326 | */
327 | @Override
328 | public void render(final Canvas canvas) throws Exception
329 | {
330 | //draw the buttons
331 | if (buttons != null)
332 | {
333 | buttons.get(Audio.isAudioEnabled() ? Assets.ImageGameKey.SoundOn : Assets.ImageGameKey.SoundOff).render(canvas);
334 | buttons.get(Assets.ImageGameKey.Pause).render(canvas);
335 | buttons.get(Assets.ImageGameKey.Exit).render(canvas);
336 | buttons.get(Assets.ImageGameKey.Reset).render(canvas);
337 | buttons.get((enabled) ? Assets.ImageGameKey.UndoEnabled : Assets.ImageGameKey.UndoDisabled).render(canvas);
338 | }
339 | }
340 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/panel/GamePanel.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.panel;
2 |
3 | import android.graphics.Canvas;
4 | import android.view.MotionEvent;
5 | import android.view.SurfaceHolder;
6 | import android.view.SurfaceView;
7 | import com.gamesbykevin.androidframework.resources.Audio;
8 | import com.gamesbykevin.androidframework.resources.Disposable;
9 | import com.gamesbykevin.sokoban.screen.ScreenManager.State;
10 | import com.gamesbykevin.sokoban.MainActivity;
11 | import com.gamesbykevin.sokoban.assets.Assets;
12 | import com.gamesbykevin.sokoban.screen.ScreenManager;
13 | import com.gamesbykevin.sokoban.thread.MainThread;
14 |
15 | import java.util.Random;
16 |
17 | /**
18 | * Game Panel class
19 | * @author GOD
20 | */
21 | public class GamePanel extends SurfaceView implements SurfaceHolder.Callback, Disposable
22 | {
23 | /**
24 | * Our random object used to make random decisions
25 | */
26 | public static Random RANDOM = new Random(System.nanoTime());
27 |
28 | //default dimensions of window for this game
29 | public static final int WIDTH = 480;
30 | public static final int HEIGHT = 800;
31 |
32 | //the reference to our activity
33 | private final MainActivity activity;
34 |
35 | //the object containing our game screens
36 | private ScreenManager screen;
37 |
38 | //our main game thread
39 | private MainThread thread;
40 |
41 | //did motion event down happen
42 | private boolean down = false;
43 |
44 | //get the ratio of the users screen compared to the default dimensions for the motion event
45 | private float scaleMotionX, scaleMotionY;
46 |
47 | //get the ratio of the users screen compared to the default dimensions for the render
48 | private float scaleRenderX, scaleRenderY;
49 |
50 | /**
51 | * Create a new game panel
52 | * @param activity Our main activity reference
53 | */
54 | public GamePanel(final MainActivity activity)
55 | {
56 | //call to parent constructor
57 | super(activity);
58 |
59 | //store context
60 | this.activity = activity;
61 |
62 | //make game panel focusable = true so it can handle events
63 | super.setFocusable(true);
64 | }
65 |
66 | /**
67 | * Get the main game thread.
68 | * If the main thread does not exist, it will be created
69 | * @return The main game thread
70 | */
71 | private MainThread getThread()
72 | {
73 | return this.thread;
74 | }
75 |
76 | /**
77 | * Get the screen manager
78 | * @return The screen manager containing all our screens
79 | */
80 | private ScreenManager getScreen()
81 | {
82 | return this.screen;
83 | }
84 |
85 | @Override
86 | public void dispose()
87 | {
88 | //it could take several attempts to stop the thread
89 | boolean retry = true;
90 |
91 | //count number of attempts to complete thread
92 | int count = 0;
93 |
94 | //here we will attempt to stop the thread
95 | while (retry && count <= MainThread.COMPLETE_THREAD_ATTEMPTS)
96 | {
97 | try
98 | {
99 | //increase count
100 | count++;
101 |
102 | if (getThread() != null)
103 | {
104 | //set running false, to stop the infinite loop
105 | getThread().setRunning(false);
106 |
107 | //wait for thread to finish
108 | getThread().join();
109 | }
110 |
111 | //if we made it here, we were successful
112 | retry = false;
113 | }
114 | catch (InterruptedException e)
115 | {
116 | e.printStackTrace();
117 | }
118 | }
119 |
120 | //make thread null
121 | this.thread = null;
122 |
123 | //assign null
124 | RANDOM = null;
125 |
126 | if (screen != null)
127 | {
128 | screen.dispose();
129 | screen = null;
130 | }
131 |
132 | //recycle all asset objects
133 | Assets.recycle();
134 | }
135 |
136 | /**
137 | * Get the activity
138 | * @return The activity reference
139 | */
140 | public final MainActivity getActivity()
141 | {
142 | return this.activity;
143 | }
144 |
145 | @Override
146 | public boolean performClick()
147 | {
148 | //call parent
149 | super.performClick();
150 |
151 | //return true
152 | return true;
153 | }
154 |
155 | @Override
156 | public boolean onTouchEvent(final MotionEvent event)
157 | {
158 | try
159 | {
160 | if (getScreen() != null)
161 | {
162 | //adjust the coordinates
163 | final float x = event.getRawX() * getScaleMotionX();
164 | final float y = event.getRawY() * getScaleMotionY();
165 |
166 | switch (event.getAction())
167 | {
168 | case MotionEvent.ACTION_DOWN:
169 |
170 | //flag motion down occurred
171 | down = true;
172 | break;
173 |
174 | case MotionEvent.ACTION_UP:
175 |
176 | //if we have previously action down
177 | if (down)
178 | {
179 | //flag false
180 | down = false;
181 |
182 | //perform click
183 | performClick();
184 | }
185 | break;
186 | }
187 |
188 | //update the screen/game etc.. with the specified motion events
189 | return getScreen().update(event.getActionMasked(), x, y);
190 | }
191 | }
192 | catch (Exception e)
193 | {
194 | e.printStackTrace();
195 | }
196 |
197 | return super.onTouchEvent(event);
198 | }
199 |
200 | /**
201 | * Now that the surface has been created we can create our game objects
202 | * @param holder Object used to track events
203 | */
204 | @Override
205 | public void surfaceCreated(SurfaceHolder holder)
206 | {
207 | try
208 | {
209 | //load assets
210 | Assets.load(getActivity());
211 |
212 | //create if null
213 | if (RANDOM == null)
214 | RANDOM = new Random(System.nanoTime());
215 |
216 | //create the thread if it doesn't exist
217 | if (getThread() == null)
218 | this.thread = new MainThread(getHolder(), this);
219 |
220 | //if the thread hasn't been started yet
221 | if (!getThread().isRunning())
222 | {
223 | //start the thread
224 | getThread().setRunning(true);
225 | getThread().start();
226 | }
227 |
228 | //flag the thread as not paused
229 | getThread().setPause(false);
230 |
231 | //store the ratio for the motion event
232 | this.scaleMotionX = (float)GamePanel.WIDTH / getWidth();
233 | this.scaleMotionY = (float)GamePanel.HEIGHT / getHeight();
234 |
235 | //store the ratio for the render
236 | this.scaleRenderX = getWidth() / (float)GamePanel.WIDTH;
237 | this.scaleRenderY = getHeight() / (float)GamePanel.HEIGHT;
238 | }
239 | catch (Exception e)
240 | {
241 | e.printStackTrace();
242 | }
243 | }
244 |
245 | @Override
246 | public void surfaceDestroyed(SurfaceHolder holder)
247 | {
248 | //pause the game
249 | if (getScreen() != null)
250 | {
251 | //stop all audio while paused
252 | Audio.stop();
253 |
254 | //flag the thread as paused
255 | getThread().setPause(false);
256 |
257 | //set the state
258 | getScreen().setState(State.Paused);
259 | }
260 | else
261 | {
262 | //if the screen does not exist, just exit the game
263 | getActivity().finish();
264 | }
265 | }
266 |
267 | @Override
268 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
269 | {
270 | //does anything need to be done here?
271 | }
272 |
273 | /**
274 | * Update the game state
275 | */
276 | public void update()
277 | {
278 | try
279 | {
280 | //make sure the screen is created first before the thread starts
281 | if (getScreen() == null)
282 | {
283 | //load all assets
284 | Assets.load(getActivity());
285 |
286 | //create new screen manager
287 | this.screen = new ScreenManager(this);
288 | }
289 | else
290 | {
291 | getScreen().update();
292 | }
293 | }
294 | catch (Exception e)
295 | {
296 | e.printStackTrace();
297 | }
298 | }
299 |
300 | /**
301 | * Get the x scale factor for the motion event
302 | * @return The x ratio of the user's width compared to the default width
303 | */
304 | private float getScaleMotionX()
305 | {
306 | return this.scaleMotionX;
307 | }
308 |
309 | /**
310 | * Get the y scale factor for the motion event
311 | * @return The y ratio of the user's height compared to the default height
312 | */
313 | private float getScaleMotionY()
314 | {
315 | return this.scaleMotionY;
316 | }
317 |
318 | /**
319 | * Get the x scale factor for the render
320 | * @return The x ratio of the user's width compared to the default width
321 | */
322 | private float getScaleRenderX()
323 | {
324 | return this.scaleRenderX;
325 | }
326 |
327 | /**
328 | * Get the y scale factor for the render
329 | * @return The y ratio of the user's height compared to the default height
330 | */
331 | private float getScaleRenderY()
332 | {
333 | return this.scaleRenderY;
334 | }
335 |
336 | @Override
337 | public void onDraw(Canvas canvas)
338 | {
339 | draw(canvas);
340 | }
341 |
342 | @Override
343 | public void draw(Canvas canvas)
344 | {
345 | if (canvas != null)
346 | {
347 | //store the canvas state
348 | final int savedState = canvas.save();
349 |
350 | try
351 | {
352 | //make sure the screen object exists
353 | if (getScreen() != null)
354 | {
355 | //scale to the screen size
356 | canvas.scale(getScaleRenderX(), getScaleRenderY());
357 |
358 | //render the main screen containing the game and other screens
359 | getScreen().render(canvas);
360 | }
361 | }
362 | catch (Exception e)
363 | {
364 | e.printStackTrace();
365 | }
366 |
367 | //restore previous canvas state
368 | canvas.restoreToCount(savedState);
369 | }
370 | }
371 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/player/Player.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.player;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Paint;
5 |
6 | import com.gamesbykevin.androidframework.anim.Animation;
7 | import com.gamesbykevin.androidframework.base.Cell;
8 | import com.gamesbykevin.androidframework.base.Entity;
9 | import com.gamesbykevin.androidframework.resources.Images;
10 | import com.gamesbykevin.androidframework.text.TimeFormat;
11 | import com.gamesbykevin.sokoban.assets.Assets;
12 | import com.gamesbykevin.sokoban.level.Level;
13 | import com.gamesbykevin.sokoban.level.LevelHelper;
14 | import com.gamesbykevin.sokoban.level.tile.TileHelper;
15 | import com.gamesbykevin.sokoban.panel.GamePanel;
16 | import com.gamesbykevin.sokoban.thread.MainThread;
17 |
18 | /**
19 | * The player that moves the blocks
20 | * @author GOD
21 | */
22 | public class Player extends Entity implements IPlayer
23 | {
24 | //the target for the player when it is moved
25 | private Cell target;
26 |
27 | //did the user select the player
28 | private boolean selected = false;
29 |
30 | //speed to move
31 | public static final double VELOCITY = 0.25;
32 |
33 | //player velocity when debugging
34 | public static final double VELOCITY_DEBUG = 0.1;
35 |
36 | //the number of moves the player has made
37 | public int moves = 0;
38 |
39 | //store the previous location in case we want to undo
40 | private double previousCol, previousRow;
41 |
42 | /**
43 | * The different animations for the player
44 | */
45 | public enum Key
46 | {
47 | WalkEast,
48 | WalkWest,
49 | WalkNorth,
50 | WalkSouth,
51 | IdleEast,
52 | IdleWest,
53 | IdleNorth,
54 | IdleSouth
55 | }
56 |
57 | //track the time (milliseconds)
58 | private long totalTime = 0L;
59 |
60 | /**
61 | * The amount of milliseconds that is passed per update
62 | */
63 | private static final long TIME_LAPSED_UPDATE = (1000 / MainThread.FPS);
64 |
65 | /**
66 | * Location where player stat's are rendered
67 | */
68 | public static final int INFO_X = 5;
69 |
70 | /**
71 | * Location where player stat's are rendered
72 | */
73 | public static final int INFO_Y = 25;
74 |
75 | /**
76 | * Location where a player's personal best stat's are rendered
77 | */
78 | public static final int PERSONAL_BEST_INFO_X = 250;
79 |
80 | /**
81 | * Location where a player's personal best stat's are rendered
82 | */
83 | public static final int PERSONAL_BEST_INFO_Y = 25;
84 |
85 | /**
86 | * Create a new player
87 | */
88 | public Player()
89 | {
90 | //create new destination
91 | this.target = new Cell();
92 |
93 | //delay between each frame
94 | final int delay = 250;
95 |
96 | //sprite key
97 | final Assets.ImageGameKey key = Assets.ImageGameKey.Sprites;
98 |
99 | //our animation object
100 | Animation animation;
101 |
102 | //walk east animation
103 | animation = new Animation(Images.getImage(key), 320, 128, 42, 58, 2, 1, 2);
104 | animation.setDelay(delay);
105 | animation.setLoop(true);
106 | getSpritesheet().add(Key.WalkEast, animation);
107 |
108 | //walk west animation
109 | animation = new Animation(Images.getImage(key), 320, 187, 42, 58, 2, 1, 2);
110 | animation.setDelay(delay);
111 | animation.setLoop(true);
112 | getSpritesheet().add(Key.WalkWest, animation);
113 |
114 | //walk north animation
115 | animation = new Animation(Images.getImage(key), 320, 305, 37, 60, 2, 1, 2);
116 | animation.setDelay(delay);
117 | animation.setLoop(true);
118 | getSpritesheet().add(Key.WalkNorth, animation);
119 |
120 | //walk south animation
121 | animation = new Animation(Images.getImage(key), 320, 245, 37, 59, 2, 1, 2);
122 | animation.setDelay(delay);
123 | animation.setLoop(true);
124 | getSpritesheet().add(Key.WalkSouth, animation);
125 |
126 | //idle facing east animation
127 | animation = new Animation(Images.getImage(key), 320, 128, 42, 58);
128 | getSpritesheet().add(Key.IdleEast, animation);
129 |
130 | //idle facing west animation
131 | animation = new Animation(Images.getImage(key), 320, 187, 42, 58);
132 | getSpritesheet().add(Key.IdleWest, animation);
133 |
134 | //idle facing north animation
135 | animation = new Animation(Images.getImage(key), 384, 0, 37, 60);
136 | getSpritesheet().add(Key.IdleNorth, animation);
137 |
138 | //idle facing south animation
139 | animation = new Animation(Images.getImage(key), 384, 65, 37, 59);
140 | getSpritesheet().add(Key.IdleSouth, animation);
141 |
142 | //default animation
143 | setAnimation(Key.IdleSouth);
144 | }
145 |
146 | @Override
147 | public void reset(final Level level)
148 | {
149 | //default animation
150 | setAnimation(Key.IdleSouth);
151 |
152 | //player is not selected
153 | setSelected(false);
154 |
155 | //set start location
156 | setCol(level.getStart());
157 | setRow(level.getStart());
158 |
159 | //set target
160 | setTarget(getCol(), getRow());
161 |
162 | //reset location
163 | updateXY(level);
164 |
165 | //reset moves back to 0
166 | setMoves(0);
167 |
168 | //reset timer
169 | this.totalTime = 0;
170 | }
171 |
172 | /**
173 | * Get the time
174 | * @return The total time elapsed
175 | */
176 | public long getTime()
177 | {
178 | return this.totalTime;
179 | }
180 |
181 | /**
182 | * Get the moves count
183 | * @return The total number of moves
184 | */
185 | public int getMoves()
186 | {
187 | return this.moves;
188 | }
189 |
190 | /**
191 | * Set the moves
192 | * @param moves The moves count
193 | */
194 | public void setMoves(final int moves)
195 | {
196 | this.moves = moves;
197 | }
198 |
199 | /**
200 | * Is the player selected
201 | * @return true=yes, false=no
202 | */
203 | public boolean isSelected()
204 | {
205 | return this.selected;
206 | }
207 |
208 | /**
209 | * Flag the player as selected
210 | * @param selected true=yes, false=no
211 | */
212 | public void setSelected(final boolean selected)
213 | {
214 | this.selected = selected;
215 | }
216 |
217 | /**
218 | * Assign the animation
219 | * @param key The key of the animation we want
220 | */
221 | public final void setAnimation(final Key key)
222 | {
223 | //assign animation
224 | getSpritesheet().setKey(key);
225 |
226 | //assign the dimensions based on the image dimensions
227 | super.setWidth(getSpritesheet().get().getImage().getWidth());
228 | super.setHeight(getSpritesheet().get().getImage().getHeight());
229 | }
230 |
231 | /**
232 | * Get the animation
233 | * @return The animation of the current key, if not set null is returned
234 | */
235 | public final Key getAnimation()
236 | {
237 | if (getSpritesheet().getKey() == null)
238 | return null;
239 |
240 | //return result
241 | return (Key)getSpritesheet().getKey();
242 | }
243 |
244 | /**
245 | * Do we have the target?
246 | * @return true if the player location matches the target location, false otherwise
247 | */
248 | public boolean hasTarget()
249 | {
250 | return (getCol() == getTarget().getCol() && getRow() == getTarget().getRow());
251 | }
252 |
253 | /**
254 | * Get the destination
255 | * @return The location where the player is headed
256 | */
257 | public Cell getTarget()
258 | {
259 | return this.target;
260 | }
261 |
262 | /**
263 | * Set the destination, and store the current location
264 | * @param col Column
265 | * @param row Row
266 | */
267 | public void setTarget(final double col, final double row)
268 | {
269 | //store in case we use the undo button
270 | this.previousCol = getCol();
271 | this.previousRow = getRow();
272 |
273 | this.target.setCol(col);
274 | this.target.setRow(row);
275 | }
276 |
277 | /**
278 | * Reset the location to the previous
279 | */
280 | public void undo()
281 | {
282 | setCol(this.previousCol);
283 | setRow(this.previousRow);
284 |
285 | setTarget(getCol(), getRow());
286 | }
287 |
288 | /**
289 | * Update the (x,y) location for the player
290 | * @param level The level we are interacting with
291 | */
292 | public void updateXY(final Level level)
293 | {
294 | //get start destination
295 | final double x = LevelHelper.getX(level, getCol());
296 | final double y = LevelHelper.getY(level, getRow());
297 |
298 | //place in the center
299 | setX(x + (TileHelper.DEFAULT_DIMENSION / 2) - (getWidth() / 2));
300 | setY(y + (TileHelper.DEFAULT_DIMENSION / 2) - (getHeight() / 2));
301 | }
302 |
303 | /**
304 | * Update the player
305 | * @param level The current level
306 | */
307 | public void update(final Level level)
308 | {
309 | //add the difference to the total time
310 | this.totalTime += TIME_LAPSED_UPDATE;
311 |
312 | if (!hasTarget())
313 | {
314 | //update the current animation
315 | getSpritesheet().get().update();
316 |
317 | //assign appropriate walking animation
318 | PlayerHelper.startWalking(this);
319 |
320 | //else the player can move, so update velocity and (column, row) since we can move
321 | PlayerHelper.manageVelocity(this);
322 |
323 | //if we made it to the target
324 | if (hasTarget())
325 | {
326 | //player is no longer selected
327 | setSelected(false);
328 |
329 | //stop the walking animation
330 | PlayerHelper.stopWalking(this);
331 | }
332 |
333 | updateRenderCoordinates(level);
334 | }
335 | }
336 |
337 | public void updateRenderCoordinates(final Level level)
338 | {
339 | if (level.canFitWindow())
340 | {
341 | //update (x,y) render coordinates
342 | updateXY(level);
343 | }
344 | else
345 | {
346 | //locate middle of screen
347 | final int middleX = (GamePanel.WIDTH / 2) - (TileHelper.DEFAULT_DIMENSION / 2);
348 | final int middleY = (GamePanel.HEIGHT / 2) - (TileHelper.DEFAULT_DIMENSION / 2);
349 |
350 | //set the start location (x,y) relative to where the player start is
351 | level.setStartLocation(
352 | middleX - (int)(getCol() * TileHelper.DEFAULT_DIMENSION),
353 | middleY - (int)(getRow() * TileHelper.DEFAULT_DIMENSION));
354 |
355 | //update (x,y) render coordinates
356 | updateXY(level);
357 | }
358 | }
359 |
360 | @Override
361 | public void dispose()
362 | {
363 | super.dispose();
364 |
365 | target = null;
366 | }
367 |
368 | @Override
369 | public void render(final Canvas canvas, final Paint paint) throws Exception
370 | {
371 | //render player animation
372 | super.render(canvas);
373 |
374 | //render current move count
375 | canvas.drawText("" + getMoves(), INFO_X, INFO_Y * 2, paint);
376 |
377 | //draw timer
378 | canvas.drawText(TimeFormat.getDescription(TimeFormat.FORMAT_3, getTime()), INFO_X, INFO_Y * 3, paint);
379 | }
380 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/screen/ScreenManager.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.screen;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import com.gamesbykevin.androidframework.anim.Animation;
7 | import com.gamesbykevin.androidframework.base.Entity;
8 | import com.gamesbykevin.androidframework.resources.Audio;
9 |
10 | import com.gamesbykevin.androidframework.resources.Disposable;
11 | import com.gamesbykevin.androidframework.resources.Font;
12 | import com.gamesbykevin.androidframework.resources.Images;
13 | import com.gamesbykevin.androidframework.screen.Screen;
14 | import com.gamesbykevin.sokoban.assets.Assets;
15 | import com.gamesbykevin.sokoban.panel.GamePanel;
16 |
17 | import java.util.HashMap;
18 |
19 | /**
20 | * This class will contain the game screens
21 | * @author ABRAHAM
22 | */
23 | public final class ScreenManager implements Screen, Disposable
24 | {
25 | //the background image
26 | private Entity background;
27 |
28 | /**
29 | * These are the different states in our game
30 | */
31 | public enum State
32 | {
33 | Ready, Running, Paused, Options, Exit, GameOver
34 | }
35 |
36 | //the current state of the game
37 | private State state = State.Ready;
38 |
39 | //our game panel
40 | private final GamePanel panel;
41 |
42 | //the screens in our main screen
43 | private HashMap screens;
44 |
45 | //the object representing the button text
46 | private Paint paintButton;
47 |
48 | /**
49 | * The x-coordinate where we want the logo to be displayed
50 | */
51 | public static final int LOGO_X = 40;
52 |
53 | /**
54 | * The y-coordinate where we want the logo to be displayed
55 | */
56 | public static final int LOGO_Y = 40;
57 |
58 | /**
59 | * The x-coordinate where we want to start putting the buttons
60 | */
61 | public static final int BUTTON_X = (GamePanel.WIDTH / 2) - (MenuScreen.BUTTON_WIDTH / 2);
62 |
63 | /**
64 | * The y-coordinate where we want to start putting the buttons
65 | */
66 | public static final int BUTTON_Y = 150;
67 |
68 | /**
69 | * The y-coordinate spacing between each button
70 | */
71 | public static final int BUTTON_Y_INCREMENT = MenuScreen.BUTTON_HEIGHT + (int)(MenuScreen.BUTTON_HEIGHT * .5);
72 |
73 | /**
74 | * The y-coordinate spacing between each button
75 | */
76 | public static final int BUTTON_X_INCREMENT = 250;
77 |
78 | /**
79 | * The alpha visibility to apply when darkening the background
80 | */
81 | public static final int ALPHA_DARK = 175;
82 |
83 | /**
84 | * Default font size
85 | */
86 | public static final float DEFAULT_FONT_SIZE = 16f;
87 |
88 | /**
89 | * Create our main screen
90 | * @param panel The reference to our game panel
91 | */
92 | public ScreenManager(final GamePanel panel)
93 | {
94 | //create a new background
95 | this.background = new Entity();
96 |
97 | //assign position, size
98 | this.background.setX(0);
99 | this.background.setY(0);
100 | this.background.setWidth(GamePanel.WIDTH);
101 | this.background.setHeight(GamePanel.HEIGHT);
102 |
103 | //add animation to sprite sheet
104 | this.background.getSpritesheet().add(Assets.ImageMenuKey.Background, new Animation(Images.getImage(Assets.ImageMenuKey.Background)));
105 |
106 | //store our game panel reference
107 | this.panel = panel;
108 |
109 | //create new hash map
110 | this.screens = new HashMap();
111 | this.screens.put(State.Ready, new MenuScreen(this));
112 | this.screens.put(State.Paused, new PauseScreen(this));
113 | this.screens.put(State.Exit, new ExitScreen(this));
114 | this.screens.put(State.Options, new OptionsScreen(this));
115 | this.screens.put(State.GameOver, new GameoverScreen(this));
116 | this.screens.put(State.Running, new GameScreen(this));
117 |
118 | //default to the ready state
119 | setState(State.Ready);
120 | }
121 |
122 | @Override
123 | public boolean update(final int action, final float x, final float y) throws Exception
124 | {
125 | return getScreen(getState()).update(action, x, y);
126 | }
127 |
128 | /**
129 | * Get the paint object
130 | * @return The object controlling the text on the buttons
131 | */
132 | public Paint getPaint()
133 | {
134 | if (paintButton == null)
135 | {
136 | //create new paint object
137 | this.paintButton = new Paint();
138 |
139 | //set the font object
140 | this.paintButton.setTypeface(Font.getFont(Assets.FontMenuKey.Default));
141 |
142 | //set the text size
143 | this.paintButton.setTextSize(DEFAULT_FONT_SIZE);
144 |
145 | //set the color
146 | this.paintButton.setColor(Color.WHITE);
147 | }
148 |
149 | //return object
150 | return this.paintButton;
151 | }
152 |
153 | /**
154 | * Update runtime logic here (if needed)
155 | * @throws Exception
156 | */
157 | @Override
158 | public void update() throws Exception
159 | {
160 | getScreen(getState()).update();
161 | }
162 |
163 | /**
164 | * Get the game panel
165 | * @return Our game panel object reference
166 | */
167 | public GamePanel getPanel()
168 | {
169 | return this.panel;
170 | }
171 |
172 | public State getState()
173 | {
174 | return this.state;
175 | }
176 |
177 | public Screen getScreen(final State state)
178 | {
179 | return screens.get(state);
180 | }
181 |
182 | public GameoverScreen getScreenGameover()
183 | {
184 | return (GameoverScreen)screens.get(State.GameOver);
185 | }
186 |
187 | public PauseScreen getScreenPaused()
188 | {
189 | return (PauseScreen)screens.get(State.Paused);
190 | }
191 |
192 | public GameScreen getScreenGame()
193 | {
194 | return (GameScreen)screens.get(State.Running);
195 | }
196 |
197 | public OptionsScreen getScreenOptions()
198 | {
199 | return (OptionsScreen)screens.get(State.Options);
200 | }
201 |
202 | /**
203 | * Change the state
204 | * @param state The state of the game. Running, Paused, Ready, Game Over, etc..
205 | */
206 | public void setState(final State state)
207 | {
208 | //if pausing store the previous state
209 | if (state == State.Paused)
210 | {
211 | //set the previous state
212 | getScreenPaused().setStatePrevious(getState());
213 | }
214 | else if (state == State.GameOver && getState() != State.Paused)
215 | {
216 | //reset screen
217 | getScreen(state).reset();
218 | }
219 |
220 | //if we are not in running, but we will now be
221 | if (getState() != State.Running && state == State.Running)
222 | {
223 | //stop all sound
224 | Audio.stop();
225 |
226 | //play song
227 | Audio.play(Assets.AudioGameKey.Music, true);
228 | }
229 | else if (state == State.GameOver)
230 | {
231 | Audio.stop(Assets.AudioGameKey.Music);
232 | }
233 | else
234 | {
235 | //stop all sound
236 | Audio.stop();
237 | }
238 |
239 | //assign the state
240 | this.state = state;
241 | }
242 |
243 | public void render(final Canvas canvas) throws Exception
244 | {
245 | //if the canvas is null we can't render anything here
246 | if (canvas == null)
247 | return;
248 |
249 | //fill background
250 | canvas.drawColor(Color.BLACK);
251 |
252 | //draw the background
253 | background.render(canvas);
254 |
255 | //render the game
256 | getScreenGame().render(canvas);
257 |
258 | //render the appropriate screen
259 | switch (getState())
260 | {
261 | case Ready:
262 |
263 | //darken the background if game exists
264 | if (getScreenGame().getGame() != null)
265 | darkenBackground(canvas);
266 |
267 | //draw menu
268 | if (getScreen(getState()) != null)
269 | getScreen(getState()).render(canvas);
270 | break;
271 |
272 | case Running:
273 | //game is already rendered, don't need to do anything here
274 | break;
275 |
276 | case Paused:
277 |
278 | //if the previous state is not running, render it
279 | if (getScreenPaused().getStatePrevious() != State.Running)
280 | getScreen(getScreenPaused().getStatePrevious()).render(canvas);
281 |
282 | //darken background
283 | darkenBackground(canvas);
284 |
285 | if (getScreen(getState()) != null)
286 | getScreen(getState()).render(canvas);
287 | break;
288 |
289 | case Options:
290 |
291 | //darken the background if game exists
292 | if (getScreenGame().getGame() != null)
293 | darkenBackground(canvas);
294 |
295 | if (getScreen(getState()) != null)
296 | getScreen(getState()).render(canvas);
297 | break;
298 |
299 | case Exit:
300 | //darken background
301 | darkenBackground(canvas);
302 |
303 | if (getScreen(getState()) != null)
304 | getScreen(getState()).render(canvas);
305 | break;
306 |
307 | case GameOver:
308 |
309 | //render game over info
310 | getScreen(getState()).render(canvas);
311 | break;
312 |
313 | //this shouldn't happen
314 | default:
315 | throw new Exception("Undefined state " + state.toString());
316 | }
317 | }
318 |
319 | /**
320 | * Reset any necessary screen elements here
321 | */
322 | @Override
323 | public void reset()
324 | {
325 | //do we need anything here
326 | }
327 |
328 | /**
329 | * Draw an overlay over the background
330 | * @param canvas Object we are writing pixel data to
331 | * @param alpha The visibility of the overlay range from 0 (0% visible) - 255 (100% visible)
332 | */
333 | public static final void darkenBackground(final Canvas canvas, int alpha)
334 | {
335 | //keep in range
336 | if (alpha < 0)
337 | alpha = 0;
338 | if (alpha > 255)
339 | alpha = 255;
340 |
341 | //darken background
342 | canvas.drawARGB(alpha, 0, 0, 0);
343 | }
344 |
345 | /**
346 | * Draw an overlay over the background
347 | * @param canvas Object we are writing pixel data to
348 | */
349 | public static final void darkenBackground(final Canvas canvas)
350 | {
351 | //darken background
352 | darkenBackground(canvas, ALPHA_DARK);
353 | }
354 |
355 | @Override
356 | public void dispose()
357 | {
358 | if (background != null)
359 | {
360 | background.dispose();
361 | background = null;
362 | }
363 |
364 | if (paintButton != null)
365 | paintButton = null;
366 |
367 | if (screens != null)
368 | {
369 | for (Screen screen : screens.values())
370 | {
371 | if (screen != null)
372 | {
373 | screen.dispose();
374 | screen = null;
375 | }
376 | }
377 |
378 | screens.clear();
379 | screens = null;
380 | }
381 | }
382 | }
--------------------------------------------------------------------------------
/src/com/gamesbykevin/sokoban/screen/GameoverScreen.java:
--------------------------------------------------------------------------------
1 | package com.gamesbykevin.sokoban.screen;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Rect;
5 | import android.util.SparseArray;
6 | import android.view.MotionEvent;
7 |
8 | import com.gamesbykevin.androidframework.awt.Button;
9 | import com.gamesbykevin.androidframework.resources.Audio;
10 | import com.gamesbykevin.androidframework.resources.Disposable;
11 | import com.gamesbykevin.androidframework.resources.Images;
12 | import com.gamesbykevin.androidframework.screen.Screen;
13 | import com.gamesbykevin.sokoban.screen.MenuScreen;
14 | import com.gamesbykevin.sokoban.screen.ScreenManager;
15 | import com.gamesbykevin.sokoban.MainActivity;
16 | import com.gamesbykevin.sokoban.assets.Assets;
17 | import com.gamesbykevin.sokoban.panel.GamePanel;
18 |
19 | /**
20 | * The game over screen
21 | * @author GOD
22 | */
23 | public class GameoverScreen implements Screen, Disposable
24 | {
25 | //our main screen reference
26 | private final ScreenManager screen;
27 |
28 | //the message to display
29 | private String message = "";
30 |
31 | //where we draw the image
32 | private int messageX = 0, messageY = 0;
33 |
34 | //time we have displayed text
35 | private long time;
36 |
37 | /**
38 | * The amount of time to wait until we render the game over menu
39 | */
40 | private static final long DELAY_MENU_DISPLAY = 1250L;
41 |
42 | //do we display the menu
43 | private boolean display = false;
44 |
45 | /**
46 | * The text to display for the new game
47 | */
48 | private static final String BUTTON_TEXT_NEW_GAME = "Next";
49 |
50 | /**
51 | * The text to display to retry
52 | */
53 | private static final String BUTTON_TEXT_REPLAY = "Retry";
54 |
55 | /**
56 | * The text to display for level select
57 | */
58 | private static final String BUTTON_TEXT_LEVEL_SELECT = "Levels";
59 |
60 | /**
61 | * The text to display for the menu
62 | */
63 | private static final String BUTTON_TEXT_MENU = "Menu";
64 |
65 | //list of buttons
66 | private SparseArray