Compare commits

..

No commits in common. "dev" and "master" have entirely different histories.
dev ... master

97 changed files with 316 additions and 3106 deletions

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.me.pacman" >
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:isGame="true"
android:appCategory="game"
android:label="@string/app_name"
android:theme="@style/GdxTheme" >
<activity
android:name="com.me.pacman.AndroidLauncher"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|screenLayout">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

View File

@ -1,90 +0,0 @@
android {
buildToolsVersion "29.0.2"
compileSdkVersion 29
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
packagingOptions {
exclude 'META-INF/robovm/ios/robovm.xml'
}
defaultConfig {
applicationId "com.me.pacman"
minSdkVersion 14
targetSdkVersion 29
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
// called every time gradle gets executed, takes the native dependencies of
// the natives configuration, and extracts them to the proper libs/ folders
// so they get packed with the APK.
task copyAndroidNatives {
doFirst {
file("libs/armeabi/").mkdirs()
file("libs/armeabi-v7a/").mkdirs()
file("libs/arm64-v8a/").mkdirs()
file("libs/x86_64/").mkdirs()
file("libs/x86/").mkdirs()
configurations.natives.files.each { jar ->
def outputDir = null
if (jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a")
if (jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a")
if(jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi")
if(jar.name.endsWith("natives-x86_64.jar")) outputDir = file("libs/x86_64")
if(jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86")
if(outputDir != null) {
copy {
from zipTree(jar)
into outputDir
include "*.so"
}
}
}
}
}
tasks.whenTaskAdded { packageTask ->
if (packageTask.name.contains("package")) {
packageTask.dependsOn 'copyAndroidNatives'
}
}
task run(type: Exec) {
def path
def localProperties = project.file("../local.properties")
if (localProperties.exists()) {
Properties properties = new Properties()
localProperties.withInputStream { instr ->
properties.load(instr)
}
def sdkDir = properties.getProperty('sdk.dir')
if (sdkDir) {
path = sdkDir
} else {
path = "$System.env.ANDROID_HOME"
}
} else {
path = "$System.env.ANDROID_HOME"
}
def adb = path + "/platform-tools/adb"
commandLine "$adb", 'shell', 'am', 'start', '-n', 'com.me.pacman/com.me.pacman.AndroidLauncher'
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@ -1,45 +0,0 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
-verbose
-dontwarn android.support.**
-dontwarn com.badlogic.gdx.backends.android.AndroidFragmentApplication
-dontwarn com.badlogic.gdx.utils.GdxBuild
-dontwarn com.badlogic.gdx.physics.box2d.utils.Box2DBuild
-dontwarn com.badlogic.gdx.jnigen.BuildTarget*
-dontwarn com.badlogic.gdx.graphics.g2d.freetype.FreetypeBuild
-keep class com.badlogic.gdx.controllers.android.AndroidControllers
-keepclassmembers class com.badlogic.gdx.backends.android.AndroidInput* {
<init>(com.badlogic.gdx.Application, android.content.Context, java.lang.Object, com.badlogic.gdx.backends.android.AndroidApplicationConfiguration);
}
-keepclassmembers class com.badlogic.gdx.physics.box2d.World {
boolean contactFilter(long, long);
void beginContact(long);
void endContact(long);
void preSolve(long, long);
void postSolve(long, long);
boolean reportFixture(long);
float reportRayFixture(long, float, float, float, float, float);
}

View File

@ -1,9 +0,0 @@
# This file is used by the Eclipse ADT plugin. It is unnecessary for IDEA and Android Studio projects, which
# configure Proguard and the Android target via the build.gradle file.
# To enable ProGuard to work with Eclipse ADT, uncomment this (available properties: sdk.dir, user.home)
# and ensure proguard.jar in the Android SDK is up to date (or alternately reduce the android target to 23 or lower):
# proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-rules.pro
# Project target.
target=android-19

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon
xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_background_color"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -1,21 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:pathData="M72.76,69.051A24,24 0,0 1,46.166 76.177,24 24,0 0,1 30.375,53.624 24,24 0,0 1,46.166 31.072,24 24,0 0,1 72.76,38.197L54.375,53.624Z"
android:strokeAlpha="1"
android:strokeWidth="0.74999523"
android:fillColor="#ffff00"
android:strokeColor="#ffff00"
android:fillAlpha="1"/>
<path
android:pathData="M51,35.75L60,35.75A1.5,3 0,0 1,61.5 38.75L61.5,39.75A1.5,3 0,0 1,60 42.75L51,42.75A1.5,3 0,0 1,49.5 39.75L49.5,38.75A1.5,3 0,0 1,51 35.75z"
android:strokeAlpha="1"
android:strokeWidth="0"
android:fillColor="#1a1a1a"
android:strokeColor="#1a1a1a"
android:fillAlpha="1"
android:strokeLineCap="butt"/>
</vector>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_background_color">#000000FF</color>
</resources>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Pac-Dude</string>
</resources>

View File

@ -1,12 +0,0 @@
<resources>
<style name="GdxTheme" parent="android:Theme">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:colorBackgroundCacheHint">@null</item>
<item name="android:windowAnimationStyle">@android:style/Animation</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowContentOverlay">@null</item>
<item name="android:windowFullscreen">true</item>
</style>
</resources>

View File

@ -1,17 +0,0 @@
package com.me.pacman;
import android.os.Bundle;
import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import com.me.pacman.PacDude;
public class AndroidLauncher extends AndroidApplication {
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AndroidApplicationConfiguration config = new AndroidApplicationConfiguration();
initialize(new PacDude(), config);
}
}

View File

@ -10,7 +10,8 @@ buildscript {
google() google()
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.1'
} }
} }
@ -48,28 +49,6 @@ project(":desktop") {
} }
} }
project(":android") {
apply plugin: "android"
configurations { natives }
dependencies {
implementation project(":core")
api "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86"
natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64"
api "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86"
natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64"
}
}
project(":core") { project(":core") {
apply plugin: "java-library" apply plugin: "java-library"

View File

@ -11,9 +11,9 @@
######*##### ## #####*###### ######*##### ## #####*######
######*## ##*###### ######*## ##*######
######*## ^^^__^^^ ##*###### ######*## ^^^__^^^ ##*######
######*## ^ ^ ##*###### ######*## ^^^^^^^^ ##*######
------* ^ ^ *------ ------* ^^^^^^^^ *------
######*## ^ ^ ##*###### ######*## ^^^^^^^^ ##*######
######*## ^^^^^^^^ ##*###### ######*## ^^^^^^^^ ##*######
######*## ##*###### ######*## ##*######
######*## ######## ##*###### ######*## ######## ##*######

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 943 B

After

Width:  |  Height:  |  Size: 943 B

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 655 B

After

Width:  |  Height:  |  Size: 655 B

View File

@ -3,6 +3,7 @@ package com.me.pacman;
import com.badlogic.gdx.assets.AssetManager; import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.FileHandleResolver; import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver; import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound; import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.BitmapFont;
@ -15,12 +16,6 @@ public class Assets {
private AssetManager manager; private AssetManager manager;
private Texture levelBackground;
private Texture levelWinBackground;
private Texture menuBackground;
private Texture highScoresBackground;
public TextureRegion[][] font;
public TextureRegion[][] level; public TextureRegion[][] level;
public TextureRegion[][] deathAnimation; public TextureRegion[][] deathAnimation;
public TextureRegion[][] ghosts; public TextureRegion[][] ghosts;
@ -29,7 +24,7 @@ public class Assets {
public TextureRegion[][] volume; public TextureRegion[][] volume;
public Sound beginning, beginning_alt; public Sound beginning, beginning_alt;
public Sound fright; public Sound chaseSound;
public Sound chomp_1, chomp_2; public Sound chomp_1, chomp_2;
public Sound deathSound; public Sound deathSound;
public Sound eat_fruit, eat_ghost; public Sound eat_fruit, eat_ghost;
@ -37,18 +32,15 @@ public class Assets {
public Sound return_base; public Sound return_base;
public Sound siren, siren_fast, siren_faster, siren_fastest; public Sound siren, siren_fast, siren_faster, siren_fastest;
public Assets() { public Assets() {
this.manager = new AssetManager(); this.manager = new AssetManager();
} }
public void loadAssets() { public void loadAssets() {
manager.load("level_background.png", Texture.class); manager.load("level_background.png", Texture.class);
manager.load("level_background_win.png", Texture.class);
manager.load("menu_background.png", Texture.class);
manager.load("high_scores_background.png", Texture.class);
manager.load("logo.png", Texture.class); manager.load("logo.png", Texture.class);
manager.load("sprites/font.png", Texture.class);
manager.load("sprites/level.png", Texture.class); manager.load("sprites/level.png", Texture.class);
manager.load("sprites/death.png", Texture.class); manager.load("sprites/death.png", Texture.class);
manager.load("sprites/ghosts.png", Texture.class); manager.load("sprites/ghosts.png", Texture.class);
@ -58,7 +50,7 @@ public class Assets {
manager.load("sounds/beginning.wav", Sound.class); manager.load("sounds/beginning.wav", Sound.class);
manager.load("sounds/beginning_alt.wav", Sound.class); manager.load("sounds/beginning_alt.wav", Sound.class);
manager.load("sounds/fright.wav", Sound.class); manager.load("sounds/chase.wav", Sound.class);
manager.load("sounds/chomp_1.wav", Sound.class); manager.load("sounds/chomp_1.wav", Sound.class);
manager.load("sounds/chomp_2.wav", Sound.class); manager.load("sounds/chomp_2.wav", Sound.class);
manager.load("sounds/death.wav", Sound.class); manager.load("sounds/death.wav", Sound.class);
@ -71,16 +63,20 @@ public class Assets {
manager.load("sounds/siren_faster.wav", Sound.class); manager.load("sounds/siren_faster.wav", Sound.class);
manager.load("sounds/siren_fastest.wav", Sound.class); manager.load("sounds/siren_fastest.wav", Sound.class);
// Yayyy! all of this to load a font
FileHandleResolver resolver = new InternalFileHandleResolver();
manager.setLoader(FreeTypeFontGenerator.class, new FreeTypeFontGeneratorLoader(resolver));
manager.setLoader(BitmapFont.class, ".ttf", new FreetypeFontLoader(resolver));
FreetypeFontLoader.FreeTypeFontLoaderParameter font = new FreetypeFontLoader.FreeTypeFontLoaderParameter();
font.fontFileName = "fonts/joystix.ttf";
font.fontParameters.size = 10;
font.fontParameters.mono = true;
manager.load("fonts/joystix.ttf", BitmapFont.class, font);
// finish loading assets from disk // finish loading assets from disk
manager.finishLoading(); manager.finishLoading();
levelBackground = manager.get("level_background.png", Texture.class);
levelWinBackground = manager.get("level_background_win.png", Texture.class);
menuBackground = manager.get("menu_background.png", Texture.class);
highScoresBackground = manager.get("high_scores_background.png", Texture.class);
// cache our texture regions // cache our texture regions
font = TextureRegion.split(manager.get("sprites/font.png", Texture.class), 8, 8);
level = TextureRegion.split(manager.get("sprites/level.png", Texture.class), 8, 8); level = TextureRegion.split(manager.get("sprites/level.png", Texture.class), 8, 8);
deathAnimation = TextureRegion.split(manager.get("sprites/death.png", Texture.class), 16, 16); deathAnimation = TextureRegion.split(manager.get("sprites/death.png", Texture.class), 16, 16);
ghosts = TextureRegion.split(manager.get("sprites/ghosts.png", Texture.class), 16, 16); ghosts = TextureRegion.split(manager.get("sprites/ghosts.png", Texture.class), 16, 16);
@ -91,7 +87,7 @@ public class Assets {
// all our sounds // all our sounds
beginning = manager.get("sounds/beginning.wav", Sound.class); beginning = manager.get("sounds/beginning.wav", Sound.class);
beginning_alt = manager.get("sounds/beginning_alt.wav", Sound.class); beginning_alt = manager.get("sounds/beginning_alt.wav", Sound.class);
fright = manager.get("sounds/fright.wav", Sound.class); chaseSound = manager.get("sounds/chase.wav", Sound.class);
chomp_1 = manager.get("sounds/chomp_1.wav", Sound.class); chomp_1 = manager.get("sounds/chomp_1.wav", Sound.class);
chomp_2 = manager.get("sounds/chomp_2.wav", Sound.class); chomp_2 = manager.get("sounds/chomp_2.wav", Sound.class);
deathSound = manager.get("sounds/death.wav", Sound.class); deathSound = manager.get("sounds/death.wav", Sound.class);
@ -106,23 +102,17 @@ public class Assets {
} }
public Texture getLevelBackground() { public Texture getLevelBackground() {
return levelBackground; return manager.get("level_background.png", Texture.class);
} }
public Texture getLevelWinBackground() {
return levelWinBackground;
}
public Texture getMenuBackground() {
return menuBackground;
}
public Texture getHighScoresBackground() { return highScoresBackground; }
public Texture getLogo() { public Texture getLogo() {
return manager.get("logo.png", Texture.class); return manager.get("logo.png", Texture.class);
} }
public BitmapFont getFont() {
return manager.get("fonts/joystix.ttf", BitmapFont.class);
}
public void dispose() { public void dispose() {
manager.dispose(); manager.dispose();
} }

View File

@ -1,44 +0,0 @@
package com.me.pacman;
import com.me.pacman.entity.Direction;
public final class Constants {
public static final String TITLE = "Pac-Dude";
public static final String VERSION = "v0.1.0";
public static final boolean DEBUG = false;
// Pixels
public static final int TILE_SIZE = 8;
// The size of the original Pac-Man game in 8x8 pixel tiles
public static final int GAME_WIDTH_TILES = 28;
public static final int GAME_HEIGHT_TILES = 36;
// The game window width and height - arbitrary units (in this case, matches up with the pixel
// counts of the original Pacman.
public static final int GAME_WIDTH = GAME_WIDTH_TILES * TILE_SIZE;
public static final int GAME_HEIGHT = GAME_HEIGHT_TILES * TILE_SIZE;
// Pac-man level rendering position offset in game coordinates
public static final int LEVEL_OFFSET_X = 0;
public static final int LEVEL_OFFSET_Y = 2 * TILE_SIZE;
// 100% speed in tiles (8 pixel / tile) per second. roughly 75 pixels/second
// 75 pixels/second aligns with the per-frame pixel movement steps in:
// https://github.com/masonicGIT/pacman/blob/master/pacman.js
// and is close to the 75.75757625 pixels/second claimed in:
// https://pacman.holenet.info/#LvlSpecs
public static final float FULL_SPEED = 9.375f;
// Ghost spawn directions, should remain constant assuming they always exit the ghost house
// from the top
public static final Direction[] GHOST_SPAWN_DIRS = {
Direction.LEFT, // Blinky
Direction.UP, // Pinky
Direction.DOWN, // Inky
Direction.DOWN, // Clyde
};
}

View File

@ -1,48 +0,0 @@
package com.me.pacman;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
public class FontRenderer {
public static final char[] CHARS = "abcdefghijklmnopqrstuvwxyz0123456789!?()[]<>$&*:#^~-_/\\".toCharArray();
public static final int[] CHAR_TO_INDEX = new int[256];
private final TextureRegion[][] sprites;
private Color color = Color.WHITE;
public FontRenderer(TextureRegion[][] sprites) {
this.sprites = sprites;
}
public void setColor(Color color) {
this.color = color;
}
public void draw(SpriteBatch batch, String str, int x, int y) {
float oldColor = batch.getPackedColor();
batch.setColor(color);
for (char c : str.toCharArray()) {
if (Character.isWhitespace(c)) {
x += 8;
continue;
}
int i = CHAR_TO_INDEX[c];
batch.draw(sprites[i / 8][i % 8], x, y);
x += 8;
}
batch.setPackedColor(oldColor);
}
static {
for (int i = 0; i < CHARS.length; i++) {
CHAR_TO_INDEX[CHARS[i]] = i;
}
}
}

View File

@ -1,67 +0,0 @@
package com.me.pacman;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.Array;
/**
* A simple class for retrieving and saving high scores to persistent storage.
*
* Scores are saved in a simple text file
*/
public class HighScoreManager {
public static final String FILE = "scores.dat";
private FileHandle file;
private Array<Score> scores = new Array<>(10);
public HighScoreManager() {
file = Gdx.files.local(FILE);
loadScores();
}
private void loadScores() {
if (!file.exists()) {
return;
}
String data = file.readString();
String[] lines = data.split("\n");
for (String line : lines) {
String[] parts = line.split("\t");
scores.add(new Score(parts[0], Integer.parseInt(parts[1])));
}
}
private void saveScores() {
for (int i = 0; i < scores.size; i++) {
Score score = scores.get(i);
boolean append = i > 0;
file.writeString(String.format("%s\t%d\n", score.scorer, score.score), append);
}
}
private void sortScores() {
scores.sort(Score.SCORE_SORTER);
}
public Score getCurrentHighScore() {
return scores.size > 0 ? scores.get(0) : null;
}
public Score[] getHighScores(int count) {
Score[] ret = new Score[Math.min(count, scores.size)];
System.arraycopy(scores.items, 0, ret, 0, ret.length);
return ret;
}
public void addScore(Score score) {
scores.add(score);
sortScores();
saveScores();
}
}

View File

@ -2,65 +2,50 @@ package com.me.pacman;
import com.badlogic.gdx.Game; import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Camera;
import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.utils.viewport.FitViewport; import com.badlogic.gdx.utils.viewport.FitViewport;
import com.badlogic.gdx.utils.viewport.Viewport; import com.badlogic.gdx.utils.viewport.Viewport;
import com.me.pacman.state.MenuState; import com.me.pacman.state.PlayState;
import com.me.pacman.state.State;
public class PacDude extends Game { public class PacDude extends Game {
public Camera cam; public static final String TITLE = "Pac-Dude";
public Viewport viewport; public static final String VERSION = "v0.0.1";
public static final int LEVEL_WIDTH = 224;
public static final int LEVEL_HEIGHT = 288;
public Assets assets; public Assets assets;
public SoundManager sound;
public SpriteBatch batch; public SpriteBatch batch;
public FontRenderer fontRenderer; public ShapeRenderer sr;
public HighScoreManager highScores; public OrthographicCamera cam;
public Viewport viewport;
private State nextState;
@Override @Override
public void create () { public void create () {
cam = new OrthographicCamera(); cam = new OrthographicCamera();
viewport = new FitViewport(Constants.GAME_WIDTH, Constants.GAME_HEIGHT, cam); viewport = new FitViewport(LEVEL_WIDTH, LEVEL_HEIGHT, cam);
viewport.apply(true); viewport.apply(true);
assets = new Assets(); assets = new Assets();
assets.loadAssets(); assets.loadAssets();
sound = new SoundManager(this);
batch = new SpriteBatch(); batch = new SpriteBatch();
fontRenderer = new FontRenderer(assets.font); sr = new ShapeRenderer();
highScores = new HighScoreManager(); setScreen(new PlayState(this));
Gdx.gl.glClearColor(0, 0, 0, 1);
setNextState(new MenuState(this));
} }
@Override @Override
public void render () { public void render () {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (nextState != null) {
Screen currScreen = getScreen();
if (currScreen != null) currScreen.dispose();
nextState.setup();
super.setScreen(nextState);
nextState = null;
}
batch.begin(); batch.begin();
if (Constants.DEBUG) {
fontRenderer.draw(batch, "fps:" + Gdx.graphics.getFramesPerSecond(), 19 * Constants.TILE_SIZE, 34 * Constants.TILE_SIZE);
}
super.render(); super.render();
batch.end(); batch.end();
} }
@ -70,6 +55,7 @@ public class PacDude extends Game {
viewport.update(width, height); viewport.update(width, height);
batch.setProjectionMatrix(cam.combined); batch.setProjectionMatrix(cam.combined);
sr.setProjectionMatrix(cam.combined);
super.resize(width, height); super.resize(width, height);
} }
@ -79,14 +65,4 @@ public class PacDude extends Game {
batch.dispose(); batch.dispose();
assets.dispose(); assets.dispose();
} }
@Override
public void setScreen(Screen screen) {
throw new IllegalStateException("Use setNextState instead.");
}
public void setNextState(State state) {
this.nextState = state;
}
} }

View File

@ -1,271 +0,0 @@
package com.me.pacman;
import java.util.Arrays;
public class RoundModifiers {
private static final RoundModifiers[] ROUND_MODIFIERS;
private float frightTime;
private float pacmanSpeed;
private float pacmanFrightSpeed;
private float ghostSpeed;
private float ghostFrightSpeed;
private float ghostTunnelSpeed;
private int elroy1DotsLeft;
private float elroy1Speed;
private int elroy2DotsLeft;
private float elroy2Speed;
private int forceLeaveSeconds;
private float[] scatterChaseSwitchTimings;
private static int cap(int round) {
return Math.min(round, 20);
}
public static float getFrightTime(int round) {
return ROUND_MODIFIERS[cap(round)].frightTime;
}
public static float getPacmanSpeed(int round) {
return Constants.FULL_SPEED * ROUND_MODIFIERS[cap(round)].pacmanSpeed;
}
public static float getPacmanFrightSpeed(int round) {
return Constants.FULL_SPEED * ROUND_MODIFIERS[cap(round)].pacmanFrightSpeed;
}
public static float getGhostSpeed(int round) {
return Constants.FULL_SPEED * ROUND_MODIFIERS[cap(round)].ghostSpeed;
}
public static float getGhostFrightSpeed(int round) {
return Constants.FULL_SPEED * ROUND_MODIFIERS[cap(round)].ghostFrightSpeed;
}
public static float getGhostTunnelSpeed(int round) {
return Constants.FULL_SPEED * ROUND_MODIFIERS[cap(round)].ghostTunnelSpeed;
}
public static int getElroy1DotsLeft(int round) {
return ROUND_MODIFIERS[cap(round)].elroy1DotsLeft;
}
public static float getElroy1Speed(int round) {
return Constants.FULL_SPEED * ROUND_MODIFIERS[cap(round)].elroy1Speed;
}
public static int getElroy2DotsLeft(int round) {
return ROUND_MODIFIERS[cap(round)].elroy2DotsLeft;
}
public static float getElroy2Speed(int round) {
return Constants.FULL_SPEED * ROUND_MODIFIERS[cap(round)].elroy2Speed;
}
public static int getForceLeaveSeconds(int round) {
return ROUND_MODIFIERS[cap(round)].forceLeaveSeconds;
}
public static float getScatterChaseTimer(int round, int scatterChaseTransition) {
return ROUND_MODIFIERS[cap(round)].scatterChaseSwitchTimings[scatterChaseTransition];
}
public RoundModifiers clone() {
RoundModifiers mod = new RoundModifiers();
mod.frightTime = frightTime;
mod.pacmanSpeed = pacmanSpeed;
mod.pacmanFrightSpeed = pacmanFrightSpeed;
mod.ghostSpeed = ghostSpeed;
mod.ghostFrightSpeed = ghostFrightSpeed;
mod.ghostTunnelSpeed = ghostTunnelSpeed;
mod.elroy1DotsLeft = elroy1DotsLeft;
mod.elroy1Speed = elroy1Speed;
mod.elroy2DotsLeft = elroy2DotsLeft;
mod.elroy2Speed = elroy2Speed;
mod.forceLeaveSeconds = forceLeaveSeconds;
mod.scatterChaseSwitchTimings = Arrays.copyOf(scatterChaseSwitchTimings, scatterChaseSwitchTimings.length);
return mod;
}
static {
ROUND_MODIFIERS = new RoundModifiers[21];
int round = 0;
// Round 1
RoundModifiers mod = new RoundModifiers();
mod.frightTime = 6f;
mod.pacmanSpeed = 0.8f;
mod.pacmanFrightSpeed = 0.9f;
mod.ghostSpeed = 0.75f;
mod.ghostFrightSpeed = 0.5f;
mod.ghostTunnelSpeed = 0.4f;
mod.elroy1DotsLeft = 20;
mod.elroy1Speed = 0.8f;
mod.elroy2DotsLeft = 10;
mod.elroy2Speed = 0.85f;
mod.forceLeaveSeconds = 4;
mod.scatterChaseSwitchTimings = new float[] {7f, 20f, 7f, 20f, 5f, 20f, 5f};
ROUND_MODIFIERS[round++] = mod;
// Round 2
mod = mod.clone();
mod.frightTime = 5f;
mod.pacmanSpeed = 0.9f;
mod.pacmanFrightSpeed = 0.95f;
mod.ghostSpeed = 0.85f;
mod.ghostFrightSpeed = 0.55f;
mod.ghostTunnelSpeed = 0.45f;
mod.elroy1DotsLeft = 30;
mod.elroy1Speed = 0.9f;
mod.elroy2DotsLeft = 14;
mod.elroy2Speed = 0.95f;
mod.scatterChaseSwitchTimings = new float[] {7f, 20f, 7f, 20f, 5f, 1033f, 1/60f};
ROUND_MODIFIERS[round++] = mod;
// Round 3
mod = mod.clone();
mod.frightTime = 4f;
mod.elroy1DotsLeft = 40;
mod.elroy1Speed = 0.9f;
mod.elroy2DotsLeft = 20;
mod.elroy2Speed = 0.95f;
ROUND_MODIFIERS[round++] = mod;
// Round 4
mod = mod.clone();
mod.frightTime = 3f;
ROUND_MODIFIERS[round++] = mod;
// Round 5
mod = mod.clone();
mod.frightTime = 2f;
mod.pacmanSpeed = 1f;
mod.pacmanFrightSpeed = 1f;
mod.ghostSpeed = 0.95f;
mod.ghostFrightSpeed = 0.6f;
mod.ghostTunnelSpeed = 0.5f;
mod.elroy1Speed = 1f;
mod.elroy2Speed = 1.05f;
mod.forceLeaveSeconds = 3;
mod.scatterChaseSwitchTimings = new float[] {7f, 20f, 7f, 20f, 5f, 1037f, 1/60f};
ROUND_MODIFIERS[round++] = mod;
// Round 6
mod = mod.clone();
mod.frightTime = 5f;
mod.elroy1DotsLeft = 50;
mod.elroy2DotsLeft = 25;
ROUND_MODIFIERS[round++] = mod;
// Round 7 & 8
mod = mod.clone();
mod.frightTime = 2f;
ROUND_MODIFIERS[round++] = mod;
ROUND_MODIFIERS[round++] = mod;
// Round 9
mod = mod.clone();
mod.frightTime = 1f;
mod.elroy1DotsLeft = 60;
mod.elroy2DotsLeft = 30;
ROUND_MODIFIERS[round++] = mod;
// Round 10
mod = mod.clone();
mod.frightTime = 5f;
ROUND_MODIFIERS[round++] = mod;
// Round 11
mod = mod.clone();
mod.frightTime = 2f;
ROUND_MODIFIERS[round++] = mod;
// Round 12 & 13
mod = mod.clone();
mod.frightTime = 1f;
mod.elroy1DotsLeft = 80;
mod.elroy2DotsLeft = 40;
ROUND_MODIFIERS[round++] = mod;
ROUND_MODIFIERS[round++] = mod;
// Round 14
mod = mod.clone();
mod.frightTime = 3f;
ROUND_MODIFIERS[round++] = mod;
// Round 15 & 16
mod = mod.clone();
mod.frightTime = 1f;
mod.elroy1DotsLeft = 100;
mod.elroy2DotsLeft = 50;
ROUND_MODIFIERS[round++] = mod;
ROUND_MODIFIERS[round++] = mod;
// Round 17
mod = mod.clone();
mod.frightTime = 0f;
ROUND_MODIFIERS[round++] = mod;
// Round 18
mod = mod.clone();
mod.frightTime = 1f;
ROUND_MODIFIERS[round++] = mod;
// Round 19 & 20
mod = mod.clone();
mod.frightTime = 0f;
mod.elroy1DotsLeft = 120;
mod.elroy2DotsLeft = 60;
ROUND_MODIFIERS[round++] = mod;
ROUND_MODIFIERS[round++] = mod;
// Round 21+
mod = mod.clone();
mod.pacmanSpeed = 0.9f;
ROUND_MODIFIERS[round++] = mod;
}
}

View File

@ -1,22 +0,0 @@
package com.me.pacman;
import java.util.Comparator;
public final class Score {
public String scorer;
public int score;
public static final Comparator<Score> SCORE_SORTER = new Comparator<Score>() {
@Override
public int compare(Score a, Score b) {
return a.score < b.score? 1 : (a.score == b.score ? 0 : -1);
}
};
public Score(String scorer, int score) {
this.scorer = scorer;
this.score = score;
}
}

View File

@ -1,167 +0,0 @@
package com.me.pacman;
import java.util.Arrays;
public class SoundManager {
public enum Effect {
BEGINNING,
BEGINNING_ALT,
FRIGHT,
CHOMP_1,
CHOMP_2,
DEATH,
EAT_FRUIT,
EAT_GHOST,
EXTRA_LIFE,
RETURN_BASE,
SIREN,
SIREN_FAST,
SIREN_FASTER,
SIREN_FASTEST
}
private float volume = 1.0f;
private boolean muted;
private PacDude game;
private long[] playing;
private long[] looping;
public SoundManager(PacDude game) {
this.game = game;
this.playing = new long[Effect.values().length];
this.looping = new long[Effect.values().length];
Arrays.fill(this.playing, -1);
Arrays.fill(this.looping, -1);
}
private float volume() {
return muted? 0f : volume;
}
public void setMuted(boolean muted) {
this.muted = muted;
}
public long play(Effect effect) {
long id = getSound(effect).play(volume());
playing[effect.ordinal()] = id;
return id;
}
public long loop(Effect effect) {
long id = getSound(effect).loop(volume());
looping[effect.ordinal()] = id;
return id;
}
public boolean isPlaying(Effect effect) {
return playing[effect.ordinal()] > -1;
}
public boolean isLooping(Effect effect) {
return looping[effect.ordinal()] > -1;
}
public void stopPlaying(Effect effect) {
com.badlogic.gdx.audio.Sound sound = getSound(effect);
long id = playing[effect.ordinal()];
if (id > -1) {
sound.stop(id);
playing[effect.ordinal()] = -1;
}
}
public void stopLooping(Effect effect) {
com.badlogic.gdx.audio.Sound sound = getSound(effect);
long id = looping[effect.ordinal()];
if (id > -1) {
sound.stop(id);
looping[effect.ordinal()] = -1;
}
}
public void stopPlays() {
for (int i = 0; i < playing.length; i++) {
if (playing[i] > -1) {
getSound(Effect.values()[i]).stop(playing[i]);
playing[i] = -1;
}
}
}
public void stopLoops() {
for (int i = 0; i < looping.length; i++) {
if (looping[i] > -1) {
getSound(Effect.values()[i]).stop(looping[i]);
looping[i] = -1;
}
}
}
public void stopAll() {
stopPlays();
stopLoops();
}
public void pauseAll() {
for (int i = 0, n = Effect.values().length; i < n; i++) {
com.badlogic.gdx.audio.Sound sound = getSound(Effect.values()[i]);
if (playing[i] > -1) {
sound.pause(playing[i]);
}
if (looping[i] > -1) {
sound.pause(looping[i]);
}
}
}
public void resumeAll() {
for (int i = 0, n = Effect.values().length; i < n; i++) {
com.badlogic.gdx.audio.Sound sound = getSound(Effect.values()[i]);
if (playing[i] > -1) {
sound.resume(playing[i]);
}
if (looping[i] > -1) {
sound.resume(looping[i]);
}
}
}
private com.badlogic.gdx.audio.Sound getSound(Effect effect) {
Assets a = game.assets;
switch (effect) {
case BEGINNING:
return a.beginning;
case BEGINNING_ALT:
return a.beginning_alt;
case FRIGHT:
return a.fright;
case CHOMP_1:
return a.chomp_1;
case CHOMP_2:
return a.chomp_2;
case DEATH:
return a.deathSound;
case EAT_FRUIT:
return a.eat_fruit;
case EAT_GHOST:
return a.eat_ghost;
case EXTRA_LIFE:
return a.extra_life;
case RETURN_BASE:
return a.return_base;
case SIREN:
return a.siren;
case SIREN_FAST:
return a.siren_fast;
case SIREN_FASTER:
return a.siren_faster;
case SIREN_FASTEST:
return a.siren_fastest;
}
return null;
}
}

View File

@ -1,42 +0,0 @@
package com.me.pacman.entity;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.entity.ai.BlinkyChaseBehaviour;
import com.me.pacman.entity.ai.StaticTargetBehaviour;
import com.me.pacman.entity.ai.Target;
import com.me.pacman.RoundModifiers;
import com.me.pacman.state.PlayState;
public class Blinky extends Ghost {
public static final Target SCATTER_TARGET = new Target((Constants.GAME_WIDTH / 8) - 2, Constants.GAME_HEIGHT / 8);
public Blinky(PlayState state, Vector2 pos, Direction direction) {
super(state, pos, direction, 0, new BlinkyChaseBehaviour(state), new StaticTargetBehaviour(state, SCATTER_TARGET), false);
}
@Override
protected float getNormalSpeed() {
int round = state.round;
if (!state.hasDied || !state.ghosts[3].inHouse) {
if (state.level.getPelletsRemaining() <= RoundModifiers.getElroy2DotsLeft(round)) {
return RoundModifiers.getElroy2Speed(round);
} else if (state.level.getPelletsRemaining() <= RoundModifiers.getElroy1DotsLeft(round)) {
return RoundModifiers.getElroy1Speed(round);
}
}
return super.getNormalSpeed();
}
@Override
public void update(float dt) {
if (inHouse) {
// Immediately leave house
leaveHouse();
}
super.update(dt);
}
}

View File

@ -1,34 +0,0 @@
package com.me.pacman.entity;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.entity.ai.ClydeChaseBehaviour;
import com.me.pacman.entity.ai.StaticTargetBehaviour;
import com.me.pacman.entity.ai.Target;
import com.me.pacman.state.PlayState;
public class Clyde extends Ghost {
public static final Target SCATTER_TARGET = new Target(1, -1);
public static final int DOT_LIMIT = 60;
public Clyde(PlayState state, Vector2 pos, Direction direction) {
super(state, pos, direction, 3, new ClydeChaseBehaviour(state), new StaticTargetBehaviour(state, SCATTER_TARGET), true);
}
@Override
public void update(float dt) {
if (inHouse) {
if (state.pelletsEatenSinceDeathCounterEnabled) {
if (state.pelletsEatenSinceDeath >= 32) {
state.pelletsEatenSinceDeathCounterEnabled = false;
}
} else if (pelletCounter >= DOT_LIMIT) {
leaveHouse();
}
}
super.update(dt);
}
}

View File

@ -1,54 +0,0 @@
package com.me.pacman.entity;
import com.badlogic.gdx.math.Vector2;
public enum Direction {
UP(new Vector2(0f, 1f)),
DOWN(new Vector2(0f, -1f)),
LEFT(new Vector2(-1f, 0f)),
RIGHT(new Vector2(1f, 0f)),
;
private Vector2 vector;
Direction(Vector2 vector) {
this.vector = vector;
}
public Direction getOpposite() {
switch (this) {
case UP:
return DOWN;
case DOWN:
return UP;
case LEFT:
return RIGHT;
case RIGHT:
return LEFT;
}
return null;
}
public boolean isOpposite(Direction dir) {
return dir == getOpposite();
}
public Vector2 getVector(float scale) {
return new Vector2(this.vector).scl(scale);
}
public Vector2 getVector() {
return this.getVector(1f);
}
public static Direction fromVector(Vector2 vector) {
for (Direction dir : values()) {
if (dir.vector.hasSameDirection(vector)) {
return dir;
}
}
return null;
}
}

View File

@ -1,55 +1,29 @@
package com.me.pacman.entity; package com.me.pacman.entity;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.entity.ai.Target;
import com.me.pacman.level.LevelTile;
import com.me.pacman.state.LevelState; import com.me.pacman.state.LevelState;
public abstract class Entity { public abstract class Entity {
public LevelState state; public LevelState state;
public Vector2 pos; public float x;
public float y;
public int age; public int age;
public Entity(LevelState state, float x, float y) { public Entity(LevelState state, float x, float y) {
this.state = state; this.state = state;
this.pos = new Vector2(x, y); this.x = x;
this.y = y;
this.age = 0; this.age = 0;
} }
public void render(SpriteBatch batch, int offsetX, int offsetY) {
batch.draw(getSprite(), (int) (x * 8) + (offsetX - 8), (y * 8) + (offsetY - 8));
}
public abstract TextureRegion getSprite(); public abstract TextureRegion getSprite();
public void render() {
TextureRegion texture = getSprite();
if (texture == null) {
return;
}
state.drawSprite(texture, pos.x, pos.y);
if (Constants.DEBUG) {
state.level.renderTile(LevelTile.DEBUG, (int) pos.x, (int) pos.y, Constants.LEVEL_OFFSET_X, Constants.LEVEL_OFFSET_Y);
}
}
public boolean onSameTile(Entity other) {
return (int) pos.x == (int) other.pos.x && (int) pos.y == (int) other.pos.y;
}
public Vector2 getTileVector(float offset) {
return new Vector2((int) pos.x + offset, (int) pos.y + offset);
}
public Vector2 getTileVector() {
return new Vector2((int) pos.x, (int) pos.y);
}
public Target getAsTarget() {
return new Target(pos.x, pos.y);
}
public void update(float dt) { public void update(float dt) {
this.age += 1; this.age += 1;
} }

View File

@ -1,277 +0,0 @@
package com.me.pacman.entity;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.entity.ai.Behaviour;
import com.me.pacman.entity.ai.ReturnToBase;
import com.me.pacman.entity.ai.Target;
import com.me.pacman.entity.path.EnterGhostHousePath;
import com.me.pacman.entity.path.ExitGhostHousePath;
import com.me.pacman.level.LevelTile;
import com.me.pacman.RoundModifiers;
import com.me.pacman.state.PlayState;
import java.util.ArrayList;
public class Ghost extends MovableEntity {
public static final float EYES_SPEED = 15f;
public static final float HOUSE_SPEED = Constants.FULL_SPEED * 0.5f;
public static final Direction[] GHOST_ORDER = { Direction.UP, Direction.LEFT, Direction.DOWN, Direction.RIGHT };
private TextureRegion[][] sprite;
private int spriteIndex;
private int counter = 0;
protected PlayState state;
public Behaviour currentBehaviour;
public Behaviour chaseBehaviour;
public Behaviour scatterBehaviour;
public Behaviour frightBehaviour;
public boolean inHouse;
public boolean caught;
public int pelletCounter;
public boolean reverse;
private int prevTileX;
private int prevTileY;
public Ghost(PlayState state, Vector2 pos, Direction direction, int spriteIndex,
Behaviour chaseBehaviour, Behaviour scatterBehaviour, boolean inHouse) {
super(state, pos.x, pos.y, RoundModifiers.getGhostSpeed(0), true, direction, 0.1f);
this.state = state;
this.spriteIndex = spriteIndex;
this.chaseBehaviour = chaseBehaviour;
this.scatterBehaviour = scatterBehaviour;
this.frightBehaviour = new FrightenedBehaviour(state);
this.inHouse = inHouse;
this.caught = false;
this.reverse = false;
sprite = state.getGame().assets.ghosts;
}
@Override
public TextureRegion getSprite() {
if (currentBehaviour instanceof ReturnToBase) {
return null;
} else if (state.frightTimer > 0 && !caught) {
if (state.frightTimer > 2) {
// Render the blue scared ghost
return sprite[0][counter % 2];
} else {
// Render the flashing white ghost
return sprite[0][(int) (state.frightTimer * 5) % 2 == 0? 2 : 0 + counter % 2];
}
} else {
return sprite[2 + (counter % 2)][spriteIndex];
}
}
@Override
public void render() {
super.render();
// draw eyes so the ghost can see
if (state.frightTimer <= 0 || caught) {
state.drawSprite(sprite[1][currDirection.ordinal()], pos.x, pos.y);
}
}
protected float getNormalSpeed() {
return RoundModifiers.getGhostSpeed(state.round);
}
@Override
public void update(float dt) {
if (age % 15 == 0) {
counter++;
}
LevelTile currentTile = state.level.getTile(pos);
if (currentTile == null || currentTile == LevelTile.TUNNEL) {
speed = RoundModifiers.getGhostTunnelSpeed(state.round);
} else if (currentBehaviour instanceof FrightenedBehaviour) {
speed = RoundModifiers.getGhostFrightSpeed(state.round);
} else if (currentBehaviour instanceof ReturnToBase) {
speed = EYES_SPEED;
} else if (inHouse) {
speed = HOUSE_SPEED;
} else {
speed = getNormalSpeed();
}
super.update(dt);
if (currentPath != null) {
if (!currentPath.finished()) {
return;
}
if (currentPath instanceof EnterGhostHousePath) {
inHouse = true;
state.updateBackgroundAudio();
}
currentPath = null;
}
int tileX = (int) pos.x;
int tileY = (int) pos.y;
if (reverse && (tileX != prevTileX || tileY != prevTileY)) {
setNextDirection(currDirection.getOpposite());
reverse = false;
}
prevTileX = tileX;
prevTileY = tileY;
if (inHouse) {
if (pos.y >= 17) {
currDirection = Direction.DOWN;
} else if (pos.y <= 16) {
currDirection = Direction.UP;
}
return;
}
if ((currentBehaviour == chaseBehaviour && state.scatter)
|| (currentBehaviour == scatterBehaviour && !state.scatter)) {
reverse = true;
updateBehaviour();
}
Direction nextDirection = getNextDirection();
if (!canMove) {
// we're stuck somewhere, let's change directions to a new valid direction
// check if we already have a nextDirection which will get us unstuck
if (nextDirection != null) {
Vector2 adjacent = nextDirection.getVector().add(pos);
LevelTile adjTile = state.level.getTile(adjacent);
if (adjTile != null && adjTile.isPassable()) {
return;
}
}
// we didn't so check other directions
for (Direction dir : GHOST_ORDER) {
if (dir.isOpposite(currDirection) || dir == currDirection) {
// don't just turn around or keep going our current direction (since we'll continue being stuck)
continue;
}
Vector2 adjacent = dir.getVector().add(pos);
LevelTile adjTile = state.level.getTile(adjacent);
if (adjTile != null && adjTile.isPassable()) {
setNextDirection(dir);
return;
}
}
}
if (nextDirection != null) {
return;
}
Target target = currentBehaviour.getTarget();
if (target == null) {
// no target, carry on current course
return;
}
if (currentBehaviour instanceof ReturnToBase && target.targetReached(pos)) {
currentPath = new EnterGhostHousePath(pos);
return;
}
// Rudimentary path finding
// Check all possible turns of the tile immediately in front of us and turn the direction that is closest
// to our target.
Vector2 ahead = new Vector2((int) pos.x, (int) pos.y).add(currDirection.getVector());
float shortest = Float.MAX_VALUE;
for (Direction dir : GHOST_ORDER) {
if (dir.isOpposite(currDirection)) {
continue;
}
Vector2 adjacent = dir.getVector().add(ahead);
LevelTile nextTile = state.level.getTile(adjacent);
if (nextTile != null && nextTile.isPassable()) {
// compute distance to target
float d = target.distance_sqr(adjacent);
if (d < shortest) {
shortest = d;
nextDirection = dir;
}
}
}
setNextDirection(nextDirection);
}
public void leaveHouse() {
currentPath = new ExitGhostHousePath(pos);
inHouse = false;
updateBehaviour();
}
public void updateBehaviour() {
if (state.frightTimer > 0 && !caught) {
currentBehaviour = frightBehaviour;
} else {
currentBehaviour = state.scatter ? scatterBehaviour : chaseBehaviour;
}
}
private class FrightenedBehaviour extends Behaviour {
private Target target;
public FrightenedBehaviour(PlayState state) {
super(state);
}
@Override
public Target getTarget() {
if (target != null && !target.targetReached(pos)) {
return target;
}
ArrayList<Vector2> adjacentTiles = new ArrayList<>(3);
ArrayList<Direction> possibleTurns = new ArrayList<>(3);
Vector2 ahead = new Vector2((int) pos.x, (int) pos.y).add(currDirection.getVector());
for (Direction dir : GHOST_ORDER) {
if (dir.isOpposite(currDirection)) {
// Don't consider going backwards
continue;
}
Vector2 adjacent = dir.getVector().add(ahead);
LevelTile nextTile = state.level.getTile(adjacent);
if (nextTile != null && nextTile.isPassable()) {
adjacentTiles.add(adjacent);
possibleTurns.add(dir);
}
}
if (possibleTurns.size() == 0) {
// No possible turns means no valid tiles ahead, which would be the tunnel.
target = null;
} else if (possibleTurns.size() == 1) {
if (possibleTurns.get(0) == currDirection) {
// can only go straight, so no target
target = null;
} else {
// turn the only possible direction
target = new Target(adjacentTiles.get(0));
}
} else {
// n-way intersection, pick random direction
int rand = state.random.nextInt(adjacentTiles.size());
target = new Target(adjacentTiles.get(rand));
}
return target;
}
}
}

View File

@ -1,35 +0,0 @@
package com.me.pacman.entity;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.entity.ai.InkyChaseBehaviour;
import com.me.pacman.entity.ai.StaticTargetBehaviour;
import com.me.pacman.entity.ai.Target;
import com.me.pacman.state.PlayState;
public class Inky extends Ghost {
public static final Target SCATTER_TARGET = new Target(Constants.GAME_WIDTH / 8, -1);
public static final int DOT_LIMIT = 30;
public Inky(PlayState state, Vector2 pos, Direction direction) {
super(state, pos, direction, 2, new InkyChaseBehaviour(state), new StaticTargetBehaviour(state, SCATTER_TARGET), true);
}
@Override
public void update(float dt) {
if (inHouse) {
if (state.pelletsEatenSinceDeathCounterEnabled) {
if (state.pelletsEatenSinceDeath >= 17) {
leaveHouse();
}
} else if (pelletCounter >= DOT_LIMIT) {
leaveHouse();
}
}
super.update(dt);
}
}

View File

@ -1,7 +1,5 @@
package com.me.pacman.entity; package com.me.pacman.entity;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.entity.path.Path;
import com.me.pacman.level.LevelTile; import com.me.pacman.level.LevelTile;
import com.me.pacman.state.LevelState; import com.me.pacman.state.LevelState;
@ -16,8 +14,6 @@ public abstract class MovableEntity extends Entity {
private Direction nextDirection = null; private Direction nextDirection = null;
public boolean canMove = true; public boolean canMove = true;
public Path currentPath = null;
public MovableEntity(LevelState state, float x, float y, float speed, boolean moving, Direction currDirection, float turnTolerance) { public MovableEntity(LevelState state, float x, float y, float speed, boolean moving, Direction currDirection, float turnTolerance) {
super(state, x, y); super(state, x, y);
this.speed = speed; this.speed = speed;
@ -33,33 +29,55 @@ public abstract class MovableEntity extends Entity {
return; return;
} }
if (currentPath != null) {
currentPath.update(dt);
currentPath.updateEntity(this);
return;
}
LevelTile nextTile = null; LevelTile nextTile = null;
if (nextDirection != null) { if (nextDirection != null) {
boolean turned = false; boolean turned = false;
nextTile = state.level.getTile(nextDirection.getVector().add(pos)); switch (nextDirection) {
if (nextTile == null) { case NORTH:
// turned around immediately after existing tunnel (or trying to turn 90 degrees after entering tunnel) nextTile = state.level.getTile(x, y + 1f);
if (nextDirection.isOpposite(currDirection)) { if (nextTile == null) {
// only allow if turning 180 degrees if (currDirection == Direction.SOUTH) {
turned = true; turned = true;
} }
} else { } else if (nextTile.isPassable() && Math.abs(x - ((int) x + 0.5f)) <= tolerance) {
if (nextTile.isPassable()) { x = ((int) x) + 0.5f;
// it's possible to turn - are we close enough to our tile's center to make the turn?
Vector2 tileCenter = getTileVector(0.5f);
if (pos.dst2(tileCenter) <= tolerance * tolerance) {
// yes, move us to the center of our tile
pos = tileCenter;
turned = true; turned = true;
} }
} break;
case EAST:
nextTile = state.level.getTile(x + 1f, y);
if (nextTile == null) {
if (currDirection == Direction.WEST) {
turned = true;
}
} else if (nextTile.isPassable() && Math.abs(y - ((int) y + 0.5f)) <= tolerance) {
y = ((int) y) + 0.5f;
turned = true;
}
break;
case SOUTH:
nextTile = state.level.getTile(x, y - 1f);
if (nextTile == null) {
if (currDirection == Direction.NORTH) {
turned = true;
}
} else if (nextTile.isPassable() && Math.abs(x - ((int) x + 0.5f)) <= tolerance) {
x = ((int) x) + 0.5f;
turned = true;
}
break;
case WEST:
nextTile = state.level.getTile(x - 1f, y);
if (nextTile == null) {
if (currDirection == Direction.EAST) {
turned = true;
}
} else if (nextTile.isPassable() && Math.abs(y - ((int) y + 0.5f)) <= tolerance) {
y = ((int) y) + 0.5f;
turned = true;
}
break;
} }
if (turned) { if (turned) {
currDirection = nextDirection; currDirection = nextDirection;
@ -67,43 +85,61 @@ public abstract class MovableEntity extends Entity {
} }
} }
LevelTile currentTile = state.level.getTile(pos); float dist = speed * dt;
Vector2 new_pos = currDirection.getVector(speed * dt).add(pos);
if (currentTile == null) { LevelTile currentTile = state.level.getTile(x, y);
// handle traveling back onto the screen nextTile = null;
switch (currDirection) {
case UP: float new_y = y;
if (pos.y >= state.level.height + 1f) { float new_x = x;
new_pos.y = -1f;
} switch (currDirection) {
break; case NORTH:
case DOWN: if (currentTile == null && y > state.level.height + 1) {
if (pos.y <= 0) { new_y = -1f;
new_pos.y = state.level.height + 1f; canMove = true;
} } else {
break; new_y += dist;
case LEFT: nextTile = state.level.getTile(new_x, new_y + 0.5f);
if (pos.x <= 0) { canMove = nextTile == null || nextTile.isPassable();
new_pos.x = state.level.width + 1f; }
} break;
break; case EAST:
case RIGHT: if (currentTile == null && x > state.level.width + 1) {
if (pos.x >= state.level.width + 1f) { new_x = -1f;
new_pos.x = -1f; canMove = true;
} } else {
break; new_x += dist;
} nextTile = state.level.getTile(new_x + 0.5f, new_y);
canMove = true; canMove = nextTile == null || nextTile.isPassable();
} else { }
nextTile = state.level.getTile(currDirection.getVector(0.5f).add(new_pos)); break;
canMove = nextTile == null || nextTile.isPassable(); case SOUTH:
if (currentTile == null && y < 0) {
new_y = state.level.height + 1;
canMove = true;
} else {
new_y -= dist;
nextTile = state.level.getTile(new_x, new_y - 0.5f);
canMove = nextTile == null || nextTile.isPassable();
}
break;
case WEST:
if (currentTile == null && x < 0) {
new_x = state.level.width + 1;
canMove = true;
} else {
new_x -= dist;
nextTile = state.level.getTile(new_x - 0.5f, new_y);
canMove = nextTile == null || nextTile.isPassable();
}
break;
} }
// if move isn't going to collide with wall, move normally. // if move isn't going to collide with wall, move normally.
// otherwise, trim would-be decimal and center entity on tile // otherwise, trim would-be decimal and center entity on tile
pos.x = canMove? new_pos.x : ((int) new_pos.x) + 0.5f; x = canMove? new_x : ((int) new_x) + 0.5f;
pos.y = canMove? new_pos.y : ((int) new_pos.y) + 0.5f; y = canMove? new_y : ((int) new_y) + 0.5f;
} }
public void setNextDirection(Direction direction) { public void setNextDirection(Direction direction) {
@ -112,8 +148,11 @@ public abstract class MovableEntity extends Entity {
} }
} }
public Direction getNextDirection() { public enum Direction {
return nextDirection; NORTH,
EAST,
SOUTH,
WEST
} }
} }

View File

@ -1,45 +1,46 @@
package com.me.pacman.entity; package com.me.pacman.entity;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.level.LevelTile; import com.me.pacman.level.LevelTile;
import com.me.pacman.RoundModifiers;
import com.me.pacman.state.PlayState; import com.me.pacman.state.PlayState;
public class Pacman extends MovableEntity { public class Pacman extends MovableEntity {
public static final Vector2 HOME = new Vector2(14, 7.5f);
private TextureRegion[][] sprite; private TextureRegion[][] sprite;
private TextureRegion[][] death;
private PlayState state; private PlayState state;
private int counter = 1; private int counter = 1;
private int freezeFrames = 0; private int freezeFrames = 0;
public boolean alive = true;
public int deathFrame = 0;
public Pacman(PlayState state, boolean moving) { public Pacman(PlayState state, boolean moving) {
super(state, HOME.x, HOME.y, Constants.FULL_SPEED * 0.8f, moving, Direction.LEFT, 0.3f); super(state, 14, 7.5f, 7.5f, moving, Direction.EAST, 0.3f);
this.state = state; this.state = state;
sprite = state.getGame().assets.pacman; sprite = state.getGame().assets.pacman;
death = state.getGame().assets.deathAnimation;
} }
@Override @Override
public TextureRegion getSprite() { public TextureRegion getSprite() {
if (!alive) {
return death[deathFrame / 4][deathFrame % 4];
}
if (!moving) { if (!moving) {
return sprite[2][0]; return sprite[2][0];
} }
return sprite[currDirection.ordinal()][canMove ? counter % 3 : 1]; int spriteDir;
switch(currDirection) {
case NORTH:
spriteDir = 0;
break;
case SOUTH:
spriteDir = 1;
break;
case WEST:
spriteDir = 2;
break;
default:
spriteDir = 3;
break;
}
return sprite[spriteDir][canMove ? counter % 3 : 1];
} }
@Override @Override
@ -49,34 +50,24 @@ public class Pacman extends MovableEntity {
return; return;
} }
if (state.frightTimer > 0) {
speed = RoundModifiers.getPacmanFrightSpeed(state.round);
} else {
speed = RoundModifiers.getPacmanSpeed(state.round);
}
super.update(dt); super.update(dt);
if (canMove && age % 4 == 0) { if (!state.paused && canMove && age % 4 == 0) {
counter += 1; counter += 1;
} }
if (!alive && age % 8 == 0) { LevelTile tile = state.level.getTile(x, y);
deathFrame++;
}
LevelTile tile = state.level.getTile(pos);
if (tile == null) { if (tile == null) {
return; return;
} }
switch (tile) { switch (tile) {
case PELLET: case PELLET:
state.eatPellet(pos.x, pos.y); state.eatPellet(x, y);
freezeFrames = 1; freezeFrames = 1;
break; break;
case POWER_PELLET: case POWER_PELLET:
state.eatPowerPellet(pos.x, pos.y); state.eatPowerPellet(x, y);
freezeFrames = 3; freezeFrames = 3;
break; break;
} }

View File

@ -1,27 +0,0 @@
package com.me.pacman.entity;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.entity.ai.PinkyChaseBehaviour;
import com.me.pacman.entity.ai.StaticTargetBehaviour;
import com.me.pacman.entity.ai.Target;
import com.me.pacman.state.PlayState;
public class Pinky extends Ghost {
public static final Target SCATTER_TARGET = new Target(2, Constants.GAME_HEIGHT / 8);
public Pinky(PlayState state, Vector2 pos, Direction direction) {
super(state, pos, direction, 1, new PinkyChaseBehaviour(state), new StaticTargetBehaviour(state, SCATTER_TARGET), true);
}
@Override
public void update(float dt) {
if (inHouse && (!state.pelletsEatenSinceDeathCounterEnabled || state.pelletsEatenSinceDeath >= 7)) {
leaveHouse();
}
super.update(dt);
}
}

View File

@ -1,15 +0,0 @@
package com.me.pacman.entity.ai;
import com.me.pacman.state.PlayState;
public abstract class Behaviour {
public PlayState state;
public Behaviour(PlayState state) {
this.state = state;
}
public abstract Target getTarget();
}

View File

@ -1,16 +0,0 @@
package com.me.pacman.entity.ai;
import com.me.pacman.state.PlayState;
public class BlinkyChaseBehaviour extends Behaviour {
public BlinkyChaseBehaviour(PlayState state) {
super(state);
}
@Override
public Target getTarget() {
return state.pacman.getAsTarget();
}
}

View File

@ -1,20 +0,0 @@
package com.me.pacman.entity.ai;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.entity.Clyde;
import com.me.pacman.state.PlayState;
public class ClydeChaseBehaviour extends Behaviour {
public ClydeChaseBehaviour(PlayState state) {
super(state);
}
@Override
public Target getTarget() {
Vector2 pacmanTile = state.pacman.getTileVector();
Vector2 clydeTile = state.ghosts[3].getTileVector();
// If clyde > 8 tiles away from pacman, target him directly - else, target Clyde's scatter tile
return pacmanTile.dst2(clydeTile) >= 8*8? new Target(pacmanTile) : Clyde.SCATTER_TARGET;
}
}

View File

@ -1,20 +0,0 @@
package com.me.pacman.entity.ai;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.state.PlayState;
public class InkyChaseBehaviour extends Behaviour {
public InkyChaseBehaviour(PlayState state) {
super(state);
}
@Override
public Target getTarget() {
Vector2 twoAheadPacman = state.pacman.currDirection.getVector(2).add(state.pacman.getTileVector());
Vector2 blinkyTile = state.ghosts[0].getTileVector();
float dst = twoAheadPacman.dst(blinkyTile); // distance between Blinky + pacman
Vector2 dir = twoAheadPacman.sub(blinkyTile); // direction from blink to pacman tile
return new Target(blinkyTile.add(dir.scl(dst * 2)));
}
}

View File

@ -1,16 +0,0 @@
package com.me.pacman.entity.ai;
import com.me.pacman.state.PlayState;
public class PinkyChaseBehaviour extends Behaviour {
public PinkyChaseBehaviour(PlayState state) {
super(state);
}
@Override
public Target getTarget() {
return new Target(state.pacman.currDirection.getVector(4).add(state.pacman.getTileVector()));
}
}

View File

@ -1,18 +0,0 @@
package com.me.pacman.entity.ai;
import com.me.pacman.state.PlayState;
public class ReturnToBase extends Behaviour {
final Target home = new Target(14, 19);
public ReturnToBase(PlayState state) {
super(state);
}
@Override
public Target getTarget() {
return home;
}
}

View File

@ -1,18 +0,0 @@
package com.me.pacman.entity.ai;
import com.me.pacman.state.PlayState;
public class StaticTargetBehaviour extends Behaviour {
private final Target target;
public StaticTargetBehaviour(PlayState state, Target target) {
super(state);
this.target = target;
}
@Override
public Target getTarget() {
return target;
}
}

View File

@ -1,39 +0,0 @@
package com.me.pacman.entity.ai;
import com.badlogic.gdx.math.Vector2;
public class Target {
public int x;
public int y;
public Target(int x, int y) {
this.x = x;
this.y = y;
}
public Target(float x, float y) {
this.x = (int) x;
this.y = (int) y;
}
public Target(Vector2 vector) {
this(vector.x, vector.y);
}
public float distance_sqr(Vector2 vec) {
float d_x = x - vec.x;
float d_y = y - vec.y;
// a^2 + b^2 = c^2. thanks pythagoras!
return d_x * d_x + d_y * d_y;
}
public boolean targetReached(float x, float y) {
return this.x == (int) x && this.y == (int) y;
}
public boolean targetReached(Vector2 vec) {
return targetReached(vec.x, vec.y);
}
}

View File

@ -1,20 +0,0 @@
package com.me.pacman.entity.path;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.entity.Ghost;
import java.util.ArrayList;
public class EnterGhostHousePath extends Path {
private static final ArrayList<Vector2> points = new ArrayList<Vector2>() {{
add(new Vector2(14, 19.5f));
add(new Vector2(14, 16.5f));
}};
public EnterGhostHousePath(Vector2 start) {
super(Ghost.EYES_SPEED, start);
addPoints(points);
}
}

View File

@ -1,20 +0,0 @@
package com.me.pacman.entity.path;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.entity.Ghost;
import java.util.ArrayList;
public class ExitGhostHousePath extends Path {
private static final ArrayList<Vector2> points = new ArrayList<Vector2>() {{
add(new Vector2(14, 16.5f));
add(new Vector2(14, 19.5f));
}};
public ExitGhostHousePath(Vector2 start) {
super(Ghost.HOUSE_SPEED, start);
addPoints(points);
}
}

View File

@ -1,89 +0,0 @@
package com.me.pacman.entity.path;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.entity.Direction;
import com.me.pacman.entity.MovableEntity;
import java.util.ArrayList;
import java.util.List;
public class Path {
private float speed; // speed in tiles/sec
private float elapsed; // seconds elapsed since beginning of path
private List<Vector2> points; // points in the path
private List<Float> segmentLengths;
private int pathSegments; // total path segments
private float pathLength; // total path length
private float progress; // distance along path
public Path(float speed, Vector2 start) {
this.speed = speed;
this.elapsed = 0;
this.points = new ArrayList<>();
this.segmentLengths = new ArrayList<>();
addPoint(start);
}
public void addPoints(List<Vector2> points) {
for (Vector2 point : points) {
addPoint(point);
}
}
protected void addPoint(Vector2 point) {
points.add(point);
if (points.size() == 1) {
return;
}
pathSegments++;
Vector2 previous = points.get(points.size()-2);
float length = previous.dst(point);
segmentLengths.add(length);
pathLength += length;
}
public void updateEntity(MovableEntity entity) {
if (finished()) {
return;
}
float tmp = progress;
for (int i = 0; i < pathSegments; i++) {
float segmentLength = segmentLengths.get(i);
if (tmp > segmentLength) {
tmp -= segmentLength;
continue;
}
Vector2 a = new Vector2(points.get(i));
Vector2 b = new Vector2(points.get(i+1));
Vector2 pathDir = b.sub(a).nor();
entity.currDirection = Direction.fromVector(pathDir);
entity.pos = a.add(pathDir.scl(tmp));
break;
}
}
public void reset() {
elapsed = 0;
progress = 0;
}
public boolean finished() {
return progress >= pathLength;
}
public void update(float dt) {
elapsed += dt;
progress = elapsed * speed;
}
}

View File

@ -1,61 +1,33 @@
package com.me.pacman.level; package com.me.pacman.level;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.PacDude; import com.me.pacman.PacDude;
public class Level { public class Level {
public static final Vector2[] GHOST_SPAWN_POINTS = {
new Vector2(14f, 19.5f),
new Vector2(14f, 16.5f),
new Vector2(12f, 16.5f),
new Vector2(16f, 16.5f),
};
private PacDude game; private PacDude game;
private TextureRegion pellet; private TextureRegion pellet;
private TextureRegion powerPellet; private TextureRegion powerPellet;
private TextureRegion debug;
// Grid of tiles, [rows][columns] // Grid of tiles, [rows][columns]
public LevelTile[][] tiles; public LevelTile[][] tiles;
// Level width and height in tiles
public int width; public int width;
public int height; public int height;
private int pellets;
private int pelletsRemaining;
public Level(PacDude game, String level) { public Level(PacDude game, String level) {
this.game = game; this.game = game;
pellet = game.assets.level[0][6]; pellet = game.assets.level[0][6];
powerPellet = game.assets.level[0][7]; powerPellet = game.assets.level[0][7];
debug = game.assets.level[1][7];
LevelLoader loader = new LevelLoader(level); LevelLoader loader = new LevelLoader(level);
tiles = loader.loadLevel(); tiles = loader.loadLevel();
height = tiles.length; height = tiles.length;
width = tiles[0].length; width = tiles[0].length;
pellets = pelletsRemaining = getTileCount(LevelTile.PELLET) + getTileCount(LevelTile.POWER_PELLET);
}
public int getPelletCount() {
return pellets;
}
public int getPelletsRemaining() {
return pelletsRemaining;
}
public int getPelletsEaten() {
return pellets - pelletsRemaining;
} }
public LevelTile getTile(int x, int y) { public LevelTile getTile(int x, int y) {
@ -66,15 +38,7 @@ public class Level {
return getTile((int) x, (int) y); return getTile((int) x, (int) y);
} }
public LevelTile getTile(Vector2 vec) {
return getTile(vec.x, vec.y);
}
public void setTile(int x, int y, LevelTile tile) { public void setTile(int x, int y, LevelTile tile) {
LevelTile exist = tiles[y][x];
if (exist.isPellet() && !tile.isPellet()) {
pelletsRemaining--;
}
tiles[y][x] = tile; tiles[y][x] = tile;
} }
@ -99,27 +63,21 @@ public class Level {
for (int i = 0; i < tiles.length; i++) { for (int i = 0; i < tiles.length; i++) {
LevelTile[] row = tiles[i]; LevelTile[] row = tiles[i];
for (int j = 0; j < row.length; j++) { for (int j = 0; j < row.length; j++) {
renderTile(row[j], j, i, offsetX, offsetY); LevelTile component = row[j];
TextureRegion sprite;
switch (component) {
case PELLET:
sprite = pellet;
break;
case POWER_PELLET:
sprite = powerPellet;
break;
default:
continue;
}
game.batch.draw(sprite, (j * 8) + offsetX, (i * 8) + offsetY);
} }
} }
} }
public void renderTile(LevelTile tile, int tileX, int tileY, int offsetX, int offsetY) {
TextureRegion sprite;
switch (tile) {
case PELLET:
sprite = pellet;
break;
case POWER_PELLET:
sprite = powerPellet;
break;
case DEBUG:
sprite = debug;
break;
default:
return;
}
game.batch.draw(sprite, (tileX * Constants.TILE_SIZE) + offsetX, (tileY * Constants.TILE_SIZE) + offsetY);
}
} }

View File

@ -9,26 +9,10 @@ public enum LevelTile {
GHOST_CHAMBER, GHOST_CHAMBER,
GHOST_GATE, GHOST_GATE,
EMPTY, EMPTY,
DEBUG,
; ;
public boolean isPassable() { public boolean isPassable() {
switch (this) { return this != WALL && this != GHOST_CHAMBER && this != GHOST_GATE;
case WALL:
case GHOST_CHAMBER:
case GHOST_GATE:
return false;
}
return true;
}
public boolean isPellet() {
switch (this) {
case PELLET:
case POWER_PELLET:
return true;
}
return false;
} }
} }

View File

@ -1,221 +0,0 @@
package com.me.pacman.state;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.FontRenderer;
import com.me.pacman.Score;
import com.me.pacman.PacDude;
import com.me.pacman.entity.Direction;
import static com.me.pacman.state.HighScoresState.BUTTON;
public class HighScoreEntryState extends State {
private Texture background;
private Score score;
private final int position;
private int currLetter;
private char[] name;
private boolean flash;
private float elapsed;
private boolean buttonPressed;
private Direction nextDirection;
public HighScoreEntryState(PacDude game, Score score, int position) {
super(game);
this.position = position;
this.score = score;
this.currLetter = 0;
this.flash = false;
this.elapsed = 0f;
this.name = score.scorer.toCharArray();
this.nextDirection = null;
}
@Override
public void setup() {
this.background = game.assets.getHighScoresBackground();
Gdx.input.setInputProcessor(this.new Controller());
}
@Override
public void render() {
game.batch.draw(background, 0, 16);
game.fontRenderer.setColor(Color.YELLOW);
game.fontRenderer.draw(game.batch, "high scores", (8 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, 25 * Constants.TILE_SIZE);
game.fontRenderer.setColor(Color.CHARTREUSE);
game.fontRenderer.draw(game.batch, "score name", (8 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, 22 * Constants.TILE_SIZE);
Score[] scores = game.highScores.getHighScores(9);
for (int n = 0; n < scores.length + 1; n++) {
int y = (18 - n) * 9;
if (n == position) {
game.fontRenderer.setColor(Color.BLUE);
game.fontRenderer.draw(game.batch, "" + score.score, (8 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, y);
for (int i = 0; i < 3; i++) {
game.fontRenderer.draw(game.batch, String.valueOf((i == currLetter && flash)? '_' : name[i]), (16 + i) * Constants.TILE_SIZE + Constants.TILE_SIZE/2, y);
}
} else {
Score score = scores[n > position? n - 1 : n];
game.fontRenderer.setColor(Color.WHITE);
game.fontRenderer.draw(game.batch, String.format("%-8d%s", score.score, score.scorer), (8 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, y);
}
}
game.fontRenderer.setColor(buttonPressed ? Color.BLUE : Color.WHITE);
game.fontRenderer.draw(game.batch, "save", 12 * Constants.TILE_SIZE, 5 * Constants.TILE_SIZE - 1);
}
@Override
public void update(float dt) {
elapsed += dt;
if (elapsed >= 1/3f) {
elapsed = 0f;
flash = !flash;
}
if (nextDirection != null) {
switch (nextDirection) {
case UP:
int nextIndex = FontRenderer.CHAR_TO_INDEX[name[currLetter]] - 1;
if (nextIndex < 0) {
nextIndex = 25;
}
flash = false;
name[currLetter] = FontRenderer.CHARS[nextIndex];
break;
case DOWN:
nextIndex = FontRenderer.CHAR_TO_INDEX[name[currLetter]] + 1;
if (nextIndex > 25) {
nextIndex = 0;
}
flash = false;
name[currLetter] = FontRenderer.CHARS[nextIndex];
break;
case LEFT:
currLetter--;
if (currLetter < 0) {
currLetter = 2;
}
flash = true;
break;
case RIGHT:
currLetter++;
if (currLetter > 2) {
currLetter = 0;
}
flash = true;
break;
}
nextDirection = null;
}
}
@Override
public void dispose() {
Gdx.input.setInputProcessor(null);
}
private void saveScoreAndReturn() {
score.scorer = String.valueOf(name);
game.highScores.addScore(score);
game.setNextState(new HighScoresState(game));
}
private final class Controller extends InputAdapter {
int downX;
int downY;
@Override
public boolean keyDown(int keycode) {
switch(keycode) {
case Input.Keys.DOWN:
nextDirection = Direction.DOWN;
break;
case Input.Keys.UP:
nextDirection = Direction.UP;
break;
case Input.Keys.RIGHT:
nextDirection = Direction.RIGHT;
break;
case Input.Keys.LEFT:
nextDirection = Direction.LEFT;
break;
case Input.Keys.ENTER:
case Input.Keys.BACK:
case Input.Keys.ESCAPE:
saveScoreAndReturn();
break;
}
elapsed = 0f;
return super.keyDown(keycode);
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector2 coords = game.viewport.unproject(new Vector2(screenX, screenY));
if (BUTTON.contains(coords)) {
buttonPressed = true;
}
downX = screenX;
downY = screenY;
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
if (buttonPressed) {
Vector2 coords = game.viewport.unproject(new Vector2(screenX, screenY));
if (BUTTON.contains(coords)) {
saveScoreAndReturn();
}
return true;
}
buttonPressed = false;
int relaX = screenX - downX;
int relaY = screenY - downY;
if (Math.abs(relaX) < 50 && Math.abs(relaY) < 50) {
// didn't move enough to consider a swipe
return true;
}
if (Math.abs(relaX) > Math.abs(relaY)) {
// x > y, so moving left-right
if (relaX > 0) {
nextDirection = Direction.RIGHT;
} else {
nextDirection = Direction.LEFT;
}
} else {
// else, moving up/down
int nextIndex;
if (relaY > 0) {
nextDirection = Direction.DOWN;
} else {
nextDirection = Direction.UP;
}
}
return true;
}
}
}

View File

@ -1,106 +0,0 @@
package com.me.pacman.state;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.PacDude;
import com.me.pacman.Score;
public class HighScoresState extends State {
public static final MenuState.BoundingBox BUTTON = new MenuState.BoundingBox(
11 * Constants.TILE_SIZE,
5 * Constants.TILE_SIZE,
17 * Constants.TILE_SIZE,
6 * Constants.TILE_SIZE);
private Texture background;
private boolean buttonPressed = false;
public HighScoresState(PacDude game) {
super(game);
}
@Override
public void setup() {
this.background = game.assets.getHighScoresBackground();
Gdx.input.setInputProcessor(this.new Controller());
}
@Override
public void render() {
game.batch.draw(background, 0, 16);
game.fontRenderer.setColor(Color.YELLOW);
game.fontRenderer.draw(game.batch, "high scores", (8 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, 25 * Constants.TILE_SIZE);
game.fontRenderer.setColor(Color.CHARTREUSE);
game.fontRenderer.draw(game.batch, "score name", (8 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, 22 * Constants.TILE_SIZE);
game.fontRenderer.setColor(Color.WHITE);
Score[] scores = game.highScores.getHighScores(10);
for (int i = 0; i < scores.length; i++) {
Score score = scores[i];
game.fontRenderer.draw(game.batch, String.format("%-8d%s", score.score, score.scorer), (8 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, (18 - i) * 9);
}
game.fontRenderer.setColor(buttonPressed ? Color.BLUE : Color.WHITE);
game.fontRenderer.draw(game.batch, "return", 11 * Constants.TILE_SIZE, 5 * Constants.TILE_SIZE - 1);
}
@Override
public void dispose() {
Gdx.input.setInputProcessor(null);
}
@Override
public void update(float dt) {}
private final class Controller extends InputAdapter {
@Override
public boolean keyDown(int keycode) {
switch(keycode) {
case Input.Keys.ENTER:
case Input.Keys.BACK:
case Input.Keys.ESCAPE:
game.setNextState(new MenuState(game));
break;
}
return super.keyDown(keycode);
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector2 coords = game.viewport.unproject(new Vector2(screenX, screenY));
if (BUTTON.contains(coords)) {
buttonPressed = true;
}
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
if (!buttonPressed) {
return true;
}
buttonPressed = false;
Vector2 coords = game.viewport.unproject(new Vector2(screenX, screenY));
if (BUTTON.contains(coords)) {
game.setNextState(new MenuState(game));
}
return true;
}
}
}

View File

@ -1,7 +1,5 @@
package com.me.pacman.state; package com.me.pacman.state;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.me.pacman.Constants;
import com.me.pacman.PacDude; import com.me.pacman.PacDude;
import com.me.pacman.level.Level; import com.me.pacman.level.Level;
@ -13,12 +11,4 @@ public abstract class LevelState extends State {
super(game); super(game);
} }
public void drawSprite(TextureRegion sprite, float x, float y) {
game.batch.draw(
sprite,
(int) (x * Constants.TILE_SIZE) + (Constants.LEVEL_OFFSET_X - Constants.TILE_SIZE),
(int) (y * Constants.TILE_SIZE) + (Constants.LEVEL_OFFSET_Y - Constants.TILE_SIZE)
);
}
} }

View File

@ -1,28 +1,15 @@
package com.me.pacman.state; package com.me.pacman.state;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.me.pacman.Constants;
import com.me.pacman.PacDude; import com.me.pacman.PacDude;
import com.me.pacman.SoundManager; import com.me.pacman.level.Level;
public class MenuState extends LevelState { public class MenuState extends LevelState {
private Texture background; private Texture levelBackground;
private Texture logo; private Texture logo;
private BitmapFont font;
public static final int NEW_GAME = 0;
public static final int HIGH_SCORES = 1;
private static final BoundingBox NEW_GAME_BOX = new BoundingBox(88, 66, 136, 84);
private static final BoundingBox HIGH_SCORE_BOX = new BoundingBox(88, 42, 136, 60);
private int selectedOption;
public MenuState(PacDude game) { public MenuState(PacDude game) {
super(game); super(game);
@ -30,118 +17,23 @@ public class MenuState extends LevelState {
@Override @Override
public void setup() { public void setup() {
background = game.assets.getMenuBackground(); levelBackground = game.assets.getLevelBackground();
logo = game.assets.getLogo(); logo = game.assets.getLogo();
logo.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear); logo.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
font = game.assets.getFont();
Gdx.input.setInputProcessor(this.new Controller()); level = new Level(game,"level");
game.sound.play(SoundManager.Effect.BEGINNING);
switch(Gdx.app.getType()) {
case Android:
case iOS:
selectedOption = -1;
break;
default:
selectedOption = 0;
break;
}
} }
@Override @Override
public void render() { public void render() {
game.batch.draw(background, 0, 16); game.batch.draw(levelBackground, 0, 16);
game.batch.draw(logo, 0, 140, 224, 120); level.render(0, 16);
game.batch.draw(logo, 0, 124, 224, 120);
game.fontRenderer.setColor(selectedOption == 0 ? Color.BLUE : Color.WHITE);
game.fontRenderer.draw(game.batch, "new", (12 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, (9 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2);
game.fontRenderer.draw(game.batch, "game", 12 * Constants.TILE_SIZE, (8 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2);
game.fontRenderer.setColor(selectedOption == 1 ? Color.BLUE : Color.WHITE);
game.fontRenderer.draw(game.batch, "high", (12 * Constants.TILE_SIZE), (6 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2);
game.fontRenderer.draw(game.batch, "scores", (11 * Constants.TILE_SIZE), (5 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2);
} }
@Override @Override
public void update(float dt) {} public void update(float dt) {
private final class Controller extends InputAdapter {
@Override
public boolean keyDown(int keycode) {
switch (keycode) {
case Input.Keys.UP:
selectedOption = selectedOption > 0 ? --selectedOption : 1;
game.sound.play(SoundManager.Effect.CHOMP_1);
break;
case Input.Keys.DOWN:
selectedOption = selectedOption < 1 ? ++selectedOption : 0;
game.sound.play(SoundManager.Effect.CHOMP_2);
break;
case Input.Keys.ENTER:
switch (selectedOption) {
case NEW_GAME:
game.setNextState(new PlayState(game));
break;
case HIGH_SCORES:
game.setNextState(new HighScoresState(game));
break;
}
break;
}
return super.keyDown(keycode);
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
Vector2 coords = game.viewport.unproject(new Vector2(screenX, screenY));
if (NEW_GAME_BOX.contains(coords)) {
selectedOption = 0;
} else if (HIGH_SCORE_BOX.contains(coords)) {
selectedOption = 1;
}
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
Vector2 coords = game.viewport.unproject(new Vector2(screenX, screenY));
if (selectedOption == 0 && NEW_GAME_BOX.contains(coords)) {
game.setNextState(new PlayState(game));
} else if (selectedOption == 1 && HIGH_SCORE_BOX.contains(coords)) {
game.setNextState(new HighScoresState(game));
} else {
selectedOption = -1;
}
return true;
}
}
@Override
public void dispose() {
Gdx.input.setInputProcessor(null);
}
public static class BoundingBox {
private int minX, minY;
private int maxX, maxY;
public BoundingBox(int minX, int minY, int maxX, int maxY) {
this.minX = minX;
this.minY = minY;
this.maxX = maxX;
this.maxY = maxY;
}
public boolean contains(int x, int y) {
return minX < x && x < maxX && minY < y && y < maxY;
}
public boolean contains(Vector2 coords) {
return contains((int) coords.x, (int) coords.y);
}
} }

View File

@ -5,357 +5,133 @@ import com.badlogic.gdx.Input;
import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import com.me.pacman.Constants;
import com.me.pacman.PacDude; import com.me.pacman.PacDude;
import com.me.pacman.Score; import com.me.pacman.entity.MovableEntity;
import com.me.pacman.SoundManager; import com.me.pacman.entity.Pacman;
import com.me.pacman.entity.*;
import com.me.pacman.entity.ai.ReturnToBase;
import com.me.pacman.entity.path.EnterGhostHousePath;
import com.me.pacman.level.Level; import com.me.pacman.level.Level;
import com.me.pacman.level.LevelTile; import com.me.pacman.level.LevelTile;
import com.me.pacman.RoundModifiers;
import java.util.Random;
public class PlayState extends LevelState { public class PlayState extends LevelState {
private Texture levelBackground, winBackground; private Texture levelBackground;
private BitmapFont font;
private int pelletCount;
private int pelletEatenCount;
private int score;
private int lives;
private int round;
public boolean paused = false;
private long sirenId;
private float waitTimer;
private TextureRegion lifeSprite; private TextureRegion lifeSprite;
public Random random; private Pacman pacman;
public int pelletsEatenSinceDeath;
public boolean pelletsEatenSinceDeathCounterEnabled;
public boolean hasDied;
private int score;
private int highScore;
private int lives;
public int round;
private boolean paused = false;
public float secondsSinceLastDot;
public boolean scatter;
private int scatterChaseTransition;
private float scatterChaseTimer;
public float frightTimer; // remaining fright time
private int ghostsCaught; // number of ghosts caught since last power pellet
private Ghost lastGhostCaptured;
public Pacman pacman;
public Ghost[] ghosts;
public enum GameState {
PRE_NEW_GAME(2.3f),
NEW_ROUND_WAIT(4.2f),
START_ROUND_WAIT(2f),
ROUND_WON_WAIT(1f),
ROUND_WON(2f),
PACMAN_CAUGHT,
PACMAN_CAUGHT_WAIT(1f),
GHOST_CAUGHT_POINTS_WAIT(1f),
GAME_OVER(2f),
PLAYING,
;
final float timer;
GameState(float timer) {
this.timer = timer;
}
GameState() {
this(0f);
}
}
private GameState state;
private float stateTimer;
public PlayState(PacDude game) { public PlayState(PacDude game) {
super(game); super(game);
} }
public void updateBackgroundAudio() {
for (Ghost ghost : ghosts) {
if ((ghost.currentBehaviour instanceof ReturnToBase
|| ghost.currentPath instanceof EnterGhostHousePath) && !ghost.inHouse) {
if (!game.sound.isLooping(SoundManager.Effect.RETURN_BASE)) {
game.sound.stopLoops();
game.sound.loop(SoundManager.Effect.RETURN_BASE);
}
return;
}
}
if (frightTimer > 0) {
if (!game.sound.isLooping(SoundManager.Effect.FRIGHT)) {
game.sound.stopLoops();
game.sound.loop(SoundManager.Effect.FRIGHT);
}
return;
}
if (!game.sound.isLooping(SoundManager.Effect.SIREN)) {
game.sound.stopLoops();
game.sound.loop(SoundManager.Effect.SIREN);
}
}
private void setGameState(GameState state) {
if (state == null) return;
this.state = state;
this.stateTimer = state.timer;
}
private void spawnGhosts() {
ghosts = new Ghost[4];
ghosts[0] = new Blinky(this, new Vector2(Level.GHOST_SPAWN_POINTS[0]), Constants.GHOST_SPAWN_DIRS[0]);
ghosts[1] = new Pinky(this, new Vector2(Level.GHOST_SPAWN_POINTS[1]), Constants.GHOST_SPAWN_DIRS[1]);
ghosts[2] = new Inky(this, new Vector2(Level.GHOST_SPAWN_POINTS[2]), Constants.GHOST_SPAWN_DIRS[2]);
ghosts[3] = new Clyde(this, new Vector2(Level.GHOST_SPAWN_POINTS[3]), Constants.GHOST_SPAWN_DIRS[3]);
}
public void resetGhosts() {
for (int i = 0; i < 4; i++) {
if (ghosts[i] == null) continue;
ghosts[i].pos = new Vector2(Level.GHOST_SPAWN_POINTS[i]);
ghosts[i].currDirection = Constants.GHOST_SPAWN_DIRS[i];
ghosts[i].currentPath = null;
ghosts[i].caught = false;
ghosts[i].inHouse = i > 0;
ghosts[i].updateBehaviour();
}
}
private void initializeLevel() {
level = new Level(game,"level");
pacman = new Pacman(this, false);
}
private void initializeRound() {
scatter = true;
scatterChaseTransition = 0;
pelletsEatenSinceDeath = 0;
pelletsEatenSinceDeathCounterEnabled = false;
hasDied = false;
spawnGhosts();
}
private void resetField() {
frightTimer = 0f;
ghostsCaught = 0;
lastGhostCaptured = null;
secondsSinceLastDot = 0;
scatterChaseTimer = RoundModifiers.getScatterChaseTimer(round, scatterChaseTransition);
random = new Random(897198256012865L);
pacman = new Pacman(this, false);
resetGhosts();
}
private void preNewGame() {
score = 0;
Score hs = game.highScores.getCurrentHighScore();
highScore = hs == null ? 0 : hs.score;
lives = 3;
round = 0;
initializeLevel();
game.sound.stopAll();
game.sound.play(SoundManager.Effect.BEGINNING);
}
private void newGame() {
lives--;
initializeRound();
resetField();
}
private void newRound() {
initializeLevel();
initializeRound();
resetField();
round++;
game.sound.stopAll();
game.sound.play(SoundManager.Effect.BEGINNING_ALT);
}
private void startGame() {
game.sound.loop(SoundManager.Effect.SIREN);
pacman.moving = true;
}
private GameState endGame() {
Score[] scores = game.highScores.getHighScores(10);
boolean addToScores = scores.length < 10;
int scorePosition = scores.length;
for (int i = 0; i < scores.length; i++) {
if (score > scores[i].score) {
addToScores = true;
scorePosition = i;
break;
}
}
if (addToScores) {
game.setNextState(new HighScoreEntryState(game, new Score("aaa", score), scorePosition));
return null;
}
return GameState.PRE_NEW_GAME;
}
@Override @Override
public void setup() { public void setup() {
levelBackground = game.assets.getLevelBackground(); levelBackground = game.assets.getLevelBackground();
winBackground = game.assets.getLevelWinBackground(); font = game.assets.getFont();
lifeSprite = game.assets.pacman[2][1]; lifeSprite = game.assets.pacman[2][1];
Gdx.input.setInputProcessor(new Controller()); Gdx.input.setInputProcessor(new Controller());
preNewGame(); pelletCount = 0;
setGameState(GameState.PRE_NEW_GAME); pelletEatenCount = 0;
} score = 0;
lives = 3;
round = 0;
public GameState stateTransition() { newGame();
switch (state) {
// The state we're transitioning /from/
case PRE_NEW_GAME:
newGame();
return GameState.START_ROUND_WAIT;
case START_ROUND_WAIT:
case NEW_ROUND_WAIT:
startGame();
return GameState.PLAYING;
case ROUND_WON_WAIT:
return GameState.ROUND_WON;
case ROUND_WON:
newRound();
return GameState.NEW_ROUND_WAIT;
case GAME_OVER:
return endGame();
case GHOST_CAUGHT_POINTS_WAIT:
updateBackgroundAudio();
return GameState.PLAYING;
case PACMAN_CAUGHT_WAIT:
game.sound.play(SoundManager.Effect.DEATH);
pacman.alive = false;
return GameState.PACMAN_CAUGHT;
}
return null;
} }
@Override @Override
public void render() { public void render() {
game.batch.draw(levelBackground, 0, 16);
level.render(0, 16);
game.fontRenderer.setColor(Color.WHITE); game.assets.getFont().setColor(Color.WHITE);
game.fontRenderer.draw(game.batch, "1up", 3 * Constants.TILE_SIZE, 35 * Constants.TILE_SIZE); game.assets.getFont().draw(game.batch, "" + score, 40, 279);
// Draw score
// Determine x position based on score size.
String s = score > 0 ? Integer.toString(score) : "00";
game.fontRenderer.draw(game.batch, s, (7 - s.length()) * Constants.TILE_SIZE, 34 * Constants.TILE_SIZE);
// Draw high score
game.fontRenderer.draw(game.batch, "high score", 9 * Constants.TILE_SIZE, 35 * Constants.TILE_SIZE);
game.fontRenderer.draw(game.batch, score >= highScore? s : Integer.toString(highScore), 12 * Constants.TILE_SIZE, 34 * Constants.TILE_SIZE);
// Draw remaining lives
for (int i = 0; i < lives; i++) { for (int i = 0; i < lives; i++) {
game.batch.draw(lifeSprite, i * 16, 0); game.batch.draw(lifeSprite, i * 16, 0);
} }
// Draw the level tiles pacman.render(game.batch, 0, 16);
level.render(Constants.LEVEL_OFFSET_X, Constants.LEVEL_OFFSET_Y);
if (state == GameState.ROUND_WON) {
// draw flashing level background
game.batch.draw((int) (stateTimer * 4) % 2 == 0? levelBackground : winBackground, Constants.LEVEL_OFFSET_X, Constants.LEVEL_OFFSET_Y);
return;
} else {
game.batch.draw(levelBackground, Constants.LEVEL_OFFSET_X, Constants.LEVEL_OFFSET_Y);
}
if (state != GameState.GHOST_CAUGHT_POINTS_WAIT) {
pacman.render();
}
switch (state) {
case PRE_NEW_GAME:
case PACMAN_CAUGHT:
case GAME_OVER:
break;
case GHOST_CAUGHT_POINTS_WAIT:
drawSprite(game.assets.points[0][ghostsCaught-1], lastGhostCaptured.pos.x, lastGhostCaptured.pos.y);
default:
for (Ghost ghost : ghosts) {
if (state == GameState.GHOST_CAUGHT_POINTS_WAIT && ghost == lastGhostCaptured) {
continue;
}
ghost.render();
}
}
if (paused) { if (paused) {
game.fontRenderer.setColor(Color.YELLOW); game.assets.getFont().setColor(Color.YELLOW);
game.fontRenderer.draw(game.batch, "paused", 11 * Constants.TILE_SIZE, 15 * Constants.TILE_SIZE); game.assets.getFont().draw(game.batch, "paused", 90, 151);
} else { return;
switch (state) { }
case PRE_NEW_GAME: }
case NEW_ROUND_WAIT:
case START_ROUND_WAIT: @Override
game.fontRenderer.setColor(Color.YELLOW); public void update(float dt) {
game.fontRenderer.draw(game.batch, "ready!", 11 * Constants.TILE_SIZE, 15 * Constants.TILE_SIZE); if (paused) {
break; return;
case GAME_OVER: }
game.fontRenderer.setColor(Color.RED);
game.fontRenderer.draw(game.batch, "game over", (9 * Constants.TILE_SIZE) + Constants.TILE_SIZE/2, 15 * Constants.TILE_SIZE); if (waitTimer > 0) {
break; waitTimer -= dt;
if (waitTimer <= 0) {
startGame();
} else {
return;
} }
} }
pacman.update(dt);
}
public void newGame() {
round++;
game.assets.siren.stop(sirenId);
level = new Level(game,"level");
pelletCount = level.getTileCount(LevelTile.PELLET);
pelletCount += level.getTileCount(LevelTile.POWER_PELLET);
if (round == 1) {
waitTimer = 4.3f;
game.assets.beginning.play(1.0f);
} else {
waitTimer = 4.2f;
game.assets.beginning_alt.play(1.0f);
}
pacman = new Pacman(this, false);
}
public void startGame() {
pacman.moving = true;
game.assets.beginning.stop();
game.assets.beginning_alt.stop();
sirenId = game.assets.siren.loop(1.0f);
if (round == 1) {
lives--;
}
} }
private void pelletEaten(float x, float y) { private void pelletEaten(float x, float y) {
level.setTile(x, y, LevelTile.EMPTY); level.setTile(x, y, LevelTile.EMPTY);
if (pelletEatenCount % 2 == 0) {
game.sound.play(level.getPelletsEaten() % 2 == 0? SoundManager.Effect.CHOMP_1 : SoundManager.Effect.CHOMP_2); game.assets.chomp_1.play(1.0f);
if (pelletsEatenSinceDeathCounterEnabled) {
// Increase global dot counter when enabled
pelletsEatenSinceDeath++;
} else { } else {
// else increment appropriate ghost's individual counter game.assets.chomp_2.play(1.0f);
for (Ghost ghost : ghosts) {
// Increment the dot counter of the first encountered ghost in the house
// Ghosts are ordered Blinky, Pinky, Inky, Clyde. Blinky's inHouse is always false,
// Increase the dotCounter of only one ghost: the first of the above list still in the house.
if (!ghost.inHouse) continue;
ghost.pelletCounter++;
break;
}
} }
pelletEatenCount++;
pelletCount--;
secondsSinceLastDot = 0; if (pelletCount == 0) {
newGame();
if (level.getPelletsRemaining() == 0) {
game.sound.stopLoops();
setGameState(GameState.ROUND_WON_WAIT);
} }
} }
@ -366,235 +142,8 @@ public class PlayState extends LevelState {
public void eatPowerPellet(float x, float y) { public void eatPowerPellet(float x, float y) {
pelletEaten(x, y); pelletEaten(x, y);
for (Ghost ghost : ghosts) { // TODO: Enable ghost chase mode
if (ghost.currentBehaviour instanceof ReturnToBase) continue;
ghost.caught = false;
ghost.currentBehaviour = ghost.frightBehaviour;
ghost.reverse = true;
}
frightTimer = RoundModifiers.getFrightTime(round);
ghostsCaught = 0;
score += 50; score += 50;
updateBackgroundAudio();
}
private void pacmanCaught() {
game.sound.stopLooping(SoundManager.Effect.SIREN);
pacman.moving = false;
pelletsEatenSinceDeath = 0;
pelletsEatenSinceDeathCounterEnabled = true;
hasDied = true;
setGameState(GameState.PACMAN_CAUGHT_WAIT);
}
private void ghostCaught(Ghost ghost) {
ghost.caught = true;
ghost.currentBehaviour = new ReturnToBase(this);
lastGhostCaptured = ghost;
ghostsCaught++;
score += ghostsCaught * 200;
game.sound.play(SoundManager.Effect.EAT_GHOST);
setGameState(GameState.GHOST_CAUGHT_POINTS_WAIT);
}
private void updateScatterTimer(float dt) {
if (scatterChaseTransition < 6) {
scatterChaseTimer -= dt;
if (scatterChaseTimer <= 0) {
scatter = !scatter;
scatterChaseTransition++;
scatterChaseTimer = RoundModifiers.getScatterChaseTimer(round, scatterChaseTransition);
}
}
}
private void updateFrightTimer(float dt) {
frightTimer -= dt;
if (frightTimer <= 0) {
for (Ghost ghost : ghosts) {
if (ghost.currentBehaviour instanceof ReturnToBase) continue;
ghost.caught = false;
ghost.currentBehaviour = ghost.chaseBehaviour;
}
updateBackgroundAudio();
}
}
private void updateSecondsSinceLastDot(float dt) {
secondsSinceLastDot += dt;
if (secondsSinceLastDot >= RoundModifiers.getForceLeaveSeconds(round)) {
// It's been 4 seconds since pacman last ate a dot, he's tryin' to avoid ghosts coming out!
// We'll get him...
for (int i = 1; i < 4; i++) {
Ghost ghost = ghosts[i];
if (ghost.inHouse) {
ghost.leaveHouse();
secondsSinceLastDot = 0;
break;
}
}
}
}
// Returns whether pacman has been caught
private boolean handleGhostCollision(Ghost ghost) {
if (ghost.onSameTile(pacman)) {
if (frightTimer > 0 && !ghost.caught) {
ghostCaught(ghost);
} else if (!(ghost.currentBehaviour instanceof ReturnToBase)) {
pacmanCaught();
}
return true;
}
return false;
}
@Override
public void update(float dt) {
if (paused) {
return;
}
if (Constants.DEBUG) {
// Fixed time step for debugger
dt = 1/60f;
}
switch (state) {
case PLAYING:
if (frightTimer <= 0) {
updateScatterTimer(dt);
} else {
updateFrightTimer(dt);
}
updateSecondsSinceLastDot(dt);
pacman.update(dt);
for (Ghost ghost : ghosts) {
ghost.update(dt);
if (handleGhostCollision(ghost)) {
return;
}
}
break;
case PACMAN_CAUGHT:
pacman.update(dt);
if (pacman.deathFrame == 13) {
if (lives > 0) {
lives--;
resetField();
setGameState(GameState.START_ROUND_WAIT);
} else {
setGameState(GameState.GAME_OVER);
}
}
break;
case GHOST_CAUGHT_POINTS_WAIT:
for (Ghost ghost : ghosts) {
if (ghost.currentBehaviour instanceof ReturnToBase && ghost != lastGhostCaptured) {
ghost.update(dt);
}
}
break;
}
if (state.timer > 0) {
stateTimer -= dt;
if (stateTimer <= 0) {
setGameState(stateTransition());
}
}
}
private final class Controller extends InputAdapter {
int downX;
int downY;
long downTime;
@Override
public boolean keyDown(int keycode) {
switch (keycode) {
case Input.Keys.UP:
case Input.Keys.W:
pacman.setNextDirection(Direction.UP);
break;
case Input.Keys.DOWN:
case Input.Keys.S:
pacman.setNextDirection(Direction.DOWN);
break;
case Input.Keys.LEFT:
case Input.Keys.A:
pacman.setNextDirection(Direction.LEFT);
break;
case Input.Keys.RIGHT:
case Input.Keys.D:
pacman.setNextDirection(Direction.RIGHT);
break;
case Input.Keys.ESCAPE:
case Input.Keys.P:
paused = !paused;
if (paused) {
game.sound.pauseAll();
} else {
game.sound.resumeAll();
}
break;
case Input.Keys.N:
preNewGame();
setGameState(GameState.PRE_NEW_GAME);
break;
}
return super.keyDown(keycode);
}
@Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
if (Math.abs(downX - screenX) < 50 && Math.abs(downY - screenY) < 50
&& System.currentTimeMillis() - downTime < 250) {
paused = !paused;
return true;
}
downX = screenX;
downY = screenY;
downTime = System.currentTimeMillis();
return true;
}
@Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
int relaX = screenX - downX;
int relaY = screenY - downY;
if (Math.abs(relaX) < 50 && Math.abs(relaY) < 50) {
// didn't move enough to consider a swipe
return true;
}
if (Math.abs(relaX) > Math.abs(relaY)) {
// x > y, so moving left-right
if (relaX > 0) {
pacman.setNextDirection(Direction.RIGHT);
} else {
pacman.setNextDirection(Direction.LEFT);
}
} else {
// else, moving up/down
if (relaY > 0) {
pacman.setNextDirection(Direction.DOWN);
} else {
pacman.setNextDirection(Direction.UP);
}
}
return true;
}
} }
@Override @Override
@ -602,4 +151,30 @@ public class PlayState extends LevelState {
Gdx.input.setInputProcessor(null); Gdx.input.setInputProcessor(null);
} }
private final class Controller extends InputAdapter {
@Override
public boolean keyDown(int keycode) {
switch (keycode) {
case Input.Keys.UP:
pacman.setNextDirection(MovableEntity.Direction.NORTH);
break;
case Input.Keys.DOWN:
pacman.setNextDirection(MovableEntity.Direction.SOUTH);
break;
case Input.Keys.LEFT:
pacman.setNextDirection(MovableEntity.Direction.WEST);
break;
case Input.Keys.RIGHT:
pacman.setNextDirection(MovableEntity.Direction.EAST);
break;
case Input.Keys.P:
paused = !paused;
break;
}
return super.keyDown(keycode);
}
}
} }

View File

@ -10,6 +10,7 @@ public abstract class State extends ScreenAdapter {
public State(PacDude game) { public State(PacDude game) {
this.game = game; this.game = game;
this.setup();
} }
@Override @Override

View File

@ -2,10 +2,10 @@ apply plugin: "java"
sourceCompatibility = 1.7 sourceCompatibility = 1.7
sourceSets.main.java.srcDirs = [ "src/" ] sourceSets.main.java.srcDirs = [ "src/" ]
sourceSets.main.resources.srcDirs = ["../android/assets"] sourceSets.main.resources.srcDirs = ["../core/assets"]
project.ext.mainClassName = "com.me.pacman.desktop.DesktopLauncher" project.ext.mainClassName = "com.me.pacman.desktop.DesktopLauncher"
project.ext.assetsDir = new File("../android/assets") project.ext.assetsDir = new File("../core/assets")
task run(dependsOn: classes, type: JavaExec) { task run(dependsOn: classes, type: JavaExec) {
main = project.mainClassName main = project.mainClassName

View File

@ -2,15 +2,14 @@ package com.me.pacman.desktop;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication; import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
import com.me.pacman.Constants;
import com.me.pacman.PacDude; import com.me.pacman.PacDude;
public class DesktopLauncher { public class DesktopLauncher {
public static void main (String[] arg) { public static void main (String[] arg) {
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.title = Constants.TITLE + " - " + Constants.VERSION; config.title = PacDude.TITLE + " - " + PacDude.VERSION;
config.width = Constants.GAME_WIDTH * 2; config.width = PacDude.LEVEL_WIDTH * 2;
config.height = Constants.GAME_HEIGHT * 2; config.height = PacDude.LEVEL_HEIGHT * 2;
config.resizable = true; config.resizable = true;
new LwjglApplication(new PacDude(), config); new LwjglApplication(new PacDude(), config);
} }

View File

@ -1,5 +1,6 @@
#Tue Dec 24 14:05:31 GET 2019
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

112
icon.svg
View File

@ -1,112 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48.026459cm"
height="48.026459cm"
viewBox="0 0 480.26459 480.26459"
version="1.1"
id="svg8"
inkscape:export-filename="/home/matt/Pacman/pacdude-512.png"
inkscape:export-xdpi="26.526085"
inkscape:export-ydpi="26.526085"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="icon.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.45254834"
inkscape:cx="1007.1956"
inkscape:cy="912.64337"
inkscape:document-units="mm"
inkscape:current-layer="layer2"
showgrid="true"
fit-margin-top="4"
fit-margin-left="4"
fit-margin-right="4"
fit-margin-bottom="4"
units="cm"
inkscape:window-width="1900"
inkscape:window-height="1039"
inkscape:window-x="10"
inkscape:window-y="31"
inkscape:window-maximized="0">
<inkscape:grid
type="xygrid"
id="grid4686"
snapvisiblegridlinesonly="false"
originx="-35.000003"
originy="-34.990077" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:groupmode="layer"
id="layer3"
inkscape:label="Background">
<rect
style="opacity:1;fill:#000000;fill-opacity:1;stroke:#0000ff;stroke-width:10;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-linecap:butt"
id="rect4736"
width="480.26459"
height="480.26459"
x="0.14373186"
y="0.11090717"
rx="50"
ry="50" />
</g>
<g
inkscape:label="Base"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-108.45938,331.50482)">
<path
style="opacity:1;fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:0.26458332;stroke-opacity:1"
id="path4538"
sodipodi:type="arc"
sodipodi:cx="348.59167"
sodipodi:cy="-91.372528"
sodipodi:rx="200"
sodipodi:ry="200"
sodipodi:start="0.6981317"
sodipodi:end="5.5850536"
d="M 501.80056,37.184994 A 200,200 0 0 1 280.18765,96.565996 200,200 0 0 1 148.59167,-91.372527 200,200 0 0 1 280.18765,-279.31105 a 200,200 0 0 1 221.61291,59.381 L 348.59167,-91.372528 Z" />
</g>
<g
inkscape:groupmode="layer"
id="layer2"
inkscape:label="Glasses"
transform="translate(-10.000003,-9.7354103)">
<rect
style="opacity:1;fill:#333333;fill-opacity:1;stroke:#ffff00;stroke-width:0.26458332;stroke-opacity:1"
id="rect4545"
width="95.61042"
height="58.758335"
x="228.37498"
y="95.285896"
rx="13.999999"
ry="7.5" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -1,87 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="108pt"
height="108pt"
viewBox="0 0 108 108"
version="1.1"
id="svg4773"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="icon_foreground.svg">
<defs
id="defs4767" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="3.1365865"
inkscape:cx="-4.5326821"
inkscape:cy="69.711018"
inkscape:document-units="pt"
inkscape:current-layer="layer1"
showgrid="true"
units="pt"
objecttolerance="1"
gridtolerance="1"
inkscape:window-width="1900"
inkscape:window-height="1039"
inkscape:window-x="10"
inkscape:window-y="31"
inkscape:window-maximized="0"
scale-x="0.35278">
<inkscape:grid
type="xygrid"
id="grid4807"
dotted="false"
snapvisiblegridlinesonly="false" />
</sodipodi:namedview>
<metadata
id="metadata4770">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-258.9)">
<path
style="opacity:1;fill:#ffff00;fill-opacity:1;stroke:#ffff00;stroke-width:0.74999523;stroke-opacity:1"
id="path4538-6"
sodipodi:type="arc"
sodipodi:cx="54.374847"
sodipodi:cy="312.52429"
sodipodi:rx="23.999851"
sodipodi:ry="23.999851"
sodipodi:start="0.6981317"
sodipodi:end="5.5850536"
d="M 72.7598,327.9511 A 23.999851,23.999851 0 0 1 46.166415,335.07678 23.999851,23.999851 0 0 1 30.374996,312.52429 23.999851,23.999851 0 0 1 46.166415,289.97181 23.999851,23.999851 0 0 1 72.7598,297.09748 L 54.374847,312.52429 Z" />
<rect
style="opacity:1;fill:#1a1a1a;fill-opacity:1;stroke:#1a1a1a;stroke-width:0;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect4828"
width="11.999926"
height="6.9999561"
x="49.499874"
y="294.65024"
rx="1.4999907"
ry="2.9999814" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1 +1 @@
include 'desktop', 'android', 'core' include 'desktop', 'core'