Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
ab3e4a177b | |||
fc912863ad | |||
5645b4bcf5 | |||
1989b4557b | |||
784bcac437 | |||
f655fe2448 | |||
b19daafc2b | |||
fb6f77b72c | |||
0a058e3d19 | |||
a9e99a363c | |||
0d69521986 | |||
5c227e53d1 | |||
e4e0d0c1b0 | |||
0e906479d1 | |||
1585407bcb | |||
82d85a926d | |||
b05cca6b3f | |||
b8fcfb4de7 | |||
c0c0a86a4c | |||
982e4ea5ad | |||
c7f352ea0c | |||
02a4c7d5ed | |||
13d1948d48 | |||
2bc78baa74 | |||
b770c2e40d | |||
fb3e26da35 | |||
31a2657c13 | |||
37944fccd9 | |||
ab94395eac | |||
389533d394 | |||
ecaf86a57a | |||
d21a4b8c1d | |||
6bfe740416 | |||
bf0d5e041b | |||
30962673d5 | |||
0501917756 | |||
20719621e2 | |||
9b06b294f4 | |||
3fe14e6e5d | |||
dd9bdac33d | |||
0d42aac28d | |||
906ef93a11 | |||
c32451a597 | |||
f6fbad0e6d | |||
8a0298b2db | |||
ac347dc6d8 | |||
d33435bf42 | |||
a8e4d2526e | |||
e38c7a2dea | |||
933f696fe8 | |||
8f13d9295c | |||
5eef40f767 | |||
cd2b968631 | |||
2a65084363 | |||
0bd4d7f042 | |||
afad1017a5 | |||
75d0c41bb5 | |||
d10148ace1 | |||
0177090ea3 | |||
1153205ddd | |||
6073c6c84a | |||
e2f601c8be |
24
android/AndroidManifest.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?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>
|
@ -11,9 +11,9 @@
|
||||
######*##### ## #####*######
|
||||
######*## ##*######
|
||||
######*## ^^^__^^^ ##*######
|
||||
######*## ^^^^^^^^ ##*######
|
||||
------* ^^^^^^^^ *------
|
||||
######*## ^^^^^^^^ ##*######
|
||||
######*## ^ ^ ##*######
|
||||
------* ^ ^ *------
|
||||
######*## ^ ^ ##*######
|
||||
######*## ^^^^^^^^ ##*######
|
||||
######*## ##*######
|
||||
######*## ######## ##*######
|
BIN
android/assets/high_scores_background.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.9 KiB |
BIN
android/assets/level_background_win.png
Normal file
After Width: | Height: | Size: 7.9 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
BIN
android/assets/menu_background.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
android/assets/sprites/font.png
Normal file
After Width: | Height: | Size: 862 B |
BIN
android/assets/sprites/font.xcf
Normal file
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
android/assets/sprites/level.png
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
android/assets/sprites/level.xcf
Normal file
Before Width: | Height: | Size: 943 B After Width: | Height: | Size: 943 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 655 B |
90
android/build.gradle
Normal file
@ -0,0 +1,90 @@
|
||||
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'
|
||||
}
|
BIN
android/ic_launcher-web.png
Normal file
After Width: | Height: | Size: 16 KiB |
45
android/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
# 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);
|
||||
}
|
9
android/project.properties
Normal file
@ -0,0 +1,9 @@
|
||||
# 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
|
6
android/res/drawable-anydpi-v26/ic_launcher.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?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>
|
21
android/res/drawable-anydpi-v26/ic_launcher_foreground.xml
Normal file
@ -0,0 +1,21 @@
|
||||
<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>
|
BIN
android/res/drawable-hdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
android/res/drawable-mdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
android/res/drawable-xhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
android/res/drawable-xxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 3.8 KiB |
BIN
android/res/drawable-xxxhdpi/ic_launcher.png
Normal file
After Width: | Height: | Size: 5.2 KiB |
4
android/res/values/color.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_background_color">#000000FF</color>
|
||||
</resources>
|
6
android/res/values/strings.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">Pac-Dude</string>
|
||||
|
||||
</resources>
|
12
android/res/values/styles.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<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>
|
17
android/src/com/me/pacman/AndroidLauncher.java
Normal file
@ -0,0 +1,17 @@
|
||||
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);
|
||||
}
|
||||
}
|
25
build.gradle
@ -10,8 +10,7 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
|
||||
|
||||
classpath 'com.android.tools.build:gradle:3.4.1'
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,6 +48,28 @@ 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") {
|
||||
apply plugin: "java-library"
|
||||
|
||||
|
Before Width: | Height: | Size: 2.1 KiB |
@ -3,7 +3,6 @@ package com.me.pacman;
|
||||
import com.badlogic.gdx.assets.AssetManager;
|
||||
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
|
||||
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
|
||||
import com.badlogic.gdx.audio.Music;
|
||||
import com.badlogic.gdx.audio.Sound;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
@ -16,6 +15,12 @@ public class Assets {
|
||||
|
||||
private AssetManager manager;
|
||||
|
||||
private Texture levelBackground;
|
||||
private Texture levelWinBackground;
|
||||
private Texture menuBackground;
|
||||
private Texture highScoresBackground;
|
||||
|
||||
public TextureRegion[][] font;
|
||||
public TextureRegion[][] level;
|
||||
public TextureRegion[][] deathAnimation;
|
||||
public TextureRegion[][] ghosts;
|
||||
@ -24,7 +29,7 @@ public class Assets {
|
||||
public TextureRegion[][] volume;
|
||||
|
||||
public Sound beginning, beginning_alt;
|
||||
public Sound chaseSound;
|
||||
public Sound fright;
|
||||
public Sound chomp_1, chomp_2;
|
||||
public Sound deathSound;
|
||||
public Sound eat_fruit, eat_ghost;
|
||||
@ -32,15 +37,18 @@ public class Assets {
|
||||
public Sound return_base;
|
||||
public Sound siren, siren_fast, siren_faster, siren_fastest;
|
||||
|
||||
|
||||
public Assets() {
|
||||
this.manager = new AssetManager();
|
||||
}
|
||||
|
||||
public void loadAssets() {
|
||||
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("sprites/font.png", Texture.class);
|
||||
manager.load("sprites/level.png", Texture.class);
|
||||
manager.load("sprites/death.png", Texture.class);
|
||||
manager.load("sprites/ghosts.png", Texture.class);
|
||||
@ -50,7 +58,7 @@ public class Assets {
|
||||
|
||||
manager.load("sounds/beginning.wav", Sound.class);
|
||||
manager.load("sounds/beginning_alt.wav", Sound.class);
|
||||
manager.load("sounds/chase.wav", Sound.class);
|
||||
manager.load("sounds/fright.wav", Sound.class);
|
||||
manager.load("sounds/chomp_1.wav", Sound.class);
|
||||
manager.load("sounds/chomp_2.wav", Sound.class);
|
||||
manager.load("sounds/death.wav", Sound.class);
|
||||
@ -63,20 +71,16 @@ public class Assets {
|
||||
manager.load("sounds/siren_faster.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
|
||||
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
|
||||
font = TextureRegion.split(manager.get("sprites/font.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);
|
||||
ghosts = TextureRegion.split(manager.get("sprites/ghosts.png", Texture.class), 16, 16);
|
||||
@ -87,7 +91,7 @@ public class Assets {
|
||||
// all our sounds
|
||||
beginning = manager.get("sounds/beginning.wav", Sound.class);
|
||||
beginning_alt = manager.get("sounds/beginning_alt.wav", Sound.class);
|
||||
chaseSound = manager.get("sounds/chase.wav", Sound.class);
|
||||
fright = manager.get("sounds/fright.wav", Sound.class);
|
||||
chomp_1 = manager.get("sounds/chomp_1.wav", Sound.class);
|
||||
chomp_2 = manager.get("sounds/chomp_2.wav", Sound.class);
|
||||
deathSound = manager.get("sounds/death.wav", Sound.class);
|
||||
@ -102,17 +106,23 @@ public class Assets {
|
||||
}
|
||||
|
||||
public Texture getLevelBackground() {
|
||||
return manager.get("level_background.png", Texture.class);
|
||||
return levelBackground;
|
||||
}
|
||||
|
||||
public Texture getLevelWinBackground() {
|
||||
return levelWinBackground;
|
||||
}
|
||||
|
||||
public Texture getMenuBackground() {
|
||||
return menuBackground;
|
||||
}
|
||||
|
||||
public Texture getHighScoresBackground() { return highScoresBackground; }
|
||||
|
||||
public Texture getLogo() {
|
||||
return manager.get("logo.png", Texture.class);
|
||||
}
|
||||
|
||||
public BitmapFont getFont() {
|
||||
return manager.get("fonts/joystix.ttf", BitmapFont.class);
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
manager.dispose();
|
||||
}
|
||||
|
44
core/src/com/me/pacman/Constants.java
Normal file
@ -0,0 +1,44 @@
|
||||
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
|
||||
};
|
||||
|
||||
}
|
48
core/src/com/me/pacman/FontRenderer.java
Normal file
@ -0,0 +1,48 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
67
core/src/com/me/pacman/HighScoreManager.java
Normal file
@ -0,0 +1,67 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@ -2,50 +2,65 @@ package com.me.pacman;
|
||||
|
||||
import com.badlogic.gdx.Game;
|
||||
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.OrthographicCamera;
|
||||
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.Viewport;
|
||||
import com.me.pacman.state.PlayState;
|
||||
import com.me.pacman.state.MenuState;
|
||||
import com.me.pacman.state.State;
|
||||
|
||||
public class PacDude extends Game {
|
||||
|
||||
public static final String TITLE = "Pac-Dude";
|
||||
public static final String VERSION = "v0.0.1";
|
||||
|
||||
public static final int LEVEL_WIDTH = 224;
|
||||
public static final int LEVEL_HEIGHT = 288;
|
||||
public Camera cam;
|
||||
public Viewport viewport;
|
||||
|
||||
public Assets assets;
|
||||
public SpriteBatch batch;
|
||||
public ShapeRenderer sr;
|
||||
public SoundManager sound;
|
||||
|
||||
public OrthographicCamera cam;
|
||||
public Viewport viewport;
|
||||
public SpriteBatch batch;
|
||||
public FontRenderer fontRenderer;
|
||||
|
||||
public HighScoreManager highScores;
|
||||
|
||||
private State nextState;
|
||||
|
||||
@Override
|
||||
public void create () {
|
||||
cam = new OrthographicCamera();
|
||||
viewport = new FitViewport(LEVEL_WIDTH, LEVEL_HEIGHT, cam);
|
||||
viewport = new FitViewport(Constants.GAME_WIDTH, Constants.GAME_HEIGHT, cam);
|
||||
viewport.apply(true);
|
||||
|
||||
assets = new Assets();
|
||||
assets.loadAssets();
|
||||
sound = new SoundManager(this);
|
||||
|
||||
batch = new SpriteBatch();
|
||||
sr = new ShapeRenderer();
|
||||
fontRenderer = new FontRenderer(assets.font);
|
||||
|
||||
setScreen(new PlayState(this));
|
||||
highScores = new HighScoreManager();
|
||||
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
setNextState(new MenuState(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render () {
|
||||
Gdx.gl.glClearColor(0, 0, 0, 1);
|
||||
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();
|
||||
if (Constants.DEBUG) {
|
||||
fontRenderer.draw(batch, "fps:" + Gdx.graphics.getFramesPerSecond(), 19 * Constants.TILE_SIZE, 34 * Constants.TILE_SIZE);
|
||||
}
|
||||
super.render();
|
||||
batch.end();
|
||||
}
|
||||
@ -55,7 +70,6 @@ public class PacDude extends Game {
|
||||
viewport.update(width, height);
|
||||
|
||||
batch.setProjectionMatrix(cam.combined);
|
||||
sr.setProjectionMatrix(cam.combined);
|
||||
|
||||
super.resize(width, height);
|
||||
}
|
||||
@ -65,4 +79,14 @@ public class PacDude extends Game {
|
||||
batch.dispose();
|
||||
assets.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScreen(Screen screen) {
|
||||
throw new IllegalStateException("Use setNextState instead.");
|
||||
}
|
||||
|
||||
public void setNextState(State state) {
|
||||
this.nextState = state;
|
||||
}
|
||||
|
||||
}
|
||||
|
271
core/src/com/me/pacman/RoundModifiers.java
Normal file
@ -0,0 +1,271 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
22
core/src/com/me/pacman/Score.java
Normal file
@ -0,0 +1,22 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
167
core/src/com/me/pacman/SoundManager.java
Normal file
@ -0,0 +1,167 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
42
core/src/com/me/pacman/entity/Blinky.java
Normal file
@ -0,0 +1,42 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
34
core/src/com/me/pacman/entity/Clyde.java
Normal file
@ -0,0 +1,34 @@
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
54
core/src/com/me/pacman/entity/Direction.java
Normal file
@ -0,0 +1,54 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -1,28 +1,54 @@
|
||||
package com.me.pacman.entity;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
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;
|
||||
|
||||
public abstract class Entity {
|
||||
|
||||
public LevelState state;
|
||||
public float x;
|
||||
public float y;
|
||||
public Vector2 pos;
|
||||
public int age;
|
||||
|
||||
public Entity(LevelState state, float x, float y) {
|
||||
this.state = state;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.pos = new Vector2(x, y);
|
||||
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 void render() {
|
||||
TextureRegion texture = getSprite();
|
||||
if (texture == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
public abstract TextureRegion getSprite();
|
||||
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) {
|
||||
this.age += 1;
|
||||
|
277
core/src/com/me/pacman/entity/Ghost.java
Normal file
@ -0,0 +1,277 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
35
core/src/com/me/pacman/entity/Inky.java
Normal file
@ -0,0 +1,35 @@
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
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.state.LevelState;
|
||||
|
||||
@ -14,6 +16,8 @@ public abstract class MovableEntity extends Entity {
|
||||
private Direction nextDirection = null;
|
||||
public boolean canMove = true;
|
||||
|
||||
public Path currentPath = null;
|
||||
|
||||
public MovableEntity(LevelState state, float x, float y, float speed, boolean moving, Direction currDirection, float turnTolerance) {
|
||||
super(state, x, y);
|
||||
this.speed = speed;
|
||||
@ -29,55 +33,33 @@ public abstract class MovableEntity extends Entity {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentPath != null) {
|
||||
currentPath.update(dt);
|
||||
currentPath.updateEntity(this);
|
||||
return;
|
||||
}
|
||||
|
||||
LevelTile nextTile = null;
|
||||
|
||||
if (nextDirection != null) {
|
||||
boolean turned = false;
|
||||
switch (nextDirection) {
|
||||
case NORTH:
|
||||
nextTile = state.level.getTile(x, y + 1f);
|
||||
nextTile = state.level.getTile(nextDirection.getVector().add(pos));
|
||||
if (nextTile == null) {
|
||||
if (currDirection == Direction.SOUTH) {
|
||||
// turned around immediately after existing tunnel (or trying to turn 90 degrees after entering tunnel)
|
||||
if (nextDirection.isOpposite(currDirection)) {
|
||||
// only allow if turning 180 degrees
|
||||
turned = true;
|
||||
}
|
||||
} else if (nextTile.isPassable() && Math.abs(x - ((int) x + 0.5f)) <= tolerance) {
|
||||
x = ((int) x) + 0.5f;
|
||||
} else {
|
||||
if (nextTile.isPassable()) {
|
||||
// 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;
|
||||
}
|
||||
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) {
|
||||
currDirection = nextDirection;
|
||||
@ -85,61 +67,43 @@ public abstract class MovableEntity extends Entity {
|
||||
}
|
||||
}
|
||||
|
||||
float dist = speed * dt;
|
||||
|
||||
LevelTile currentTile = state.level.getTile(x, y);
|
||||
nextTile = null;
|
||||
|
||||
float new_y = y;
|
||||
float new_x = x;
|
||||
LevelTile currentTile = state.level.getTile(pos);
|
||||
Vector2 new_pos = currDirection.getVector(speed * dt).add(pos);
|
||||
|
||||
if (currentTile == null) {
|
||||
// handle traveling back onto the screen
|
||||
switch (currDirection) {
|
||||
case NORTH:
|
||||
if (currentTile == null && y > state.level.height + 1) {
|
||||
new_y = -1f;
|
||||
canMove = true;
|
||||
} else {
|
||||
new_y += dist;
|
||||
nextTile = state.level.getTile(new_x, new_y + 0.5f);
|
||||
canMove = nextTile == null || nextTile.isPassable();
|
||||
case UP:
|
||||
if (pos.y >= state.level.height + 1f) {
|
||||
new_pos.y = -1f;
|
||||
}
|
||||
break;
|
||||
case EAST:
|
||||
if (currentTile == null && x > state.level.width + 1) {
|
||||
new_x = -1f;
|
||||
canMove = true;
|
||||
} else {
|
||||
new_x += dist;
|
||||
nextTile = state.level.getTile(new_x + 0.5f, new_y);
|
||||
canMove = nextTile == null || nextTile.isPassable();
|
||||
case DOWN:
|
||||
if (pos.y <= 0) {
|
||||
new_pos.y = state.level.height + 1f;
|
||||
}
|
||||
break;
|
||||
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();
|
||||
case LEFT:
|
||||
if (pos.x <= 0) {
|
||||
new_pos.x = state.level.width + 1f;
|
||||
}
|
||||
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();
|
||||
case RIGHT:
|
||||
if (pos.x >= state.level.width + 1f) {
|
||||
new_pos.x = -1f;
|
||||
}
|
||||
break;
|
||||
}
|
||||
canMove = true;
|
||||
} else {
|
||||
nextTile = state.level.getTile(currDirection.getVector(0.5f).add(new_pos));
|
||||
canMove = nextTile == null || nextTile.isPassable();
|
||||
}
|
||||
|
||||
// if move isn't going to collide with wall, move normally.
|
||||
// otherwise, trim would-be decimal and center entity on tile
|
||||
x = canMove? new_x : ((int) new_x) + 0.5f;
|
||||
y = canMove? new_y : ((int) new_y) + 0.5f;
|
||||
pos.x = canMove? new_pos.x : ((int) new_pos.x) + 0.5f;
|
||||
pos.y = canMove? new_pos.y : ((int) new_pos.y) + 0.5f;
|
||||
}
|
||||
|
||||
public void setNextDirection(Direction direction) {
|
||||
@ -148,11 +112,8 @@ public abstract class MovableEntity extends Entity {
|
||||
}
|
||||
}
|
||||
|
||||
public enum Direction {
|
||||
NORTH,
|
||||
EAST,
|
||||
SOUTH,
|
||||
WEST
|
||||
public Direction getNextDirection() {
|
||||
return nextDirection;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,46 +1,45 @@
|
||||
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.level.LevelTile;
|
||||
import com.me.pacman.RoundModifiers;
|
||||
import com.me.pacman.state.PlayState;
|
||||
|
||||
public class Pacman extends MovableEntity {
|
||||
|
||||
public static final Vector2 HOME = new Vector2(14, 7.5f);
|
||||
|
||||
private TextureRegion[][] sprite;
|
||||
private TextureRegion[][] death;
|
||||
|
||||
private PlayState state;
|
||||
private int counter = 1;
|
||||
|
||||
private int freezeFrames = 0;
|
||||
|
||||
public boolean alive = true;
|
||||
public int deathFrame = 0;
|
||||
|
||||
public Pacman(PlayState state, boolean moving) {
|
||||
super(state, 14, 7.5f, 7.5f, moving, Direction.EAST, 0.3f);
|
||||
super(state, HOME.x, HOME.y, Constants.FULL_SPEED * 0.8f, moving, Direction.LEFT, 0.3f);
|
||||
this.state = state;
|
||||
sprite = state.getGame().assets.pacman;
|
||||
death = state.getGame().assets.deathAnimation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion getSprite() {
|
||||
if (!alive) {
|
||||
return death[deathFrame / 4][deathFrame % 4];
|
||||
}
|
||||
|
||||
if (!moving) {
|
||||
return sprite[2][0];
|
||||
}
|
||||
|
||||
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];
|
||||
return sprite[currDirection.ordinal()][canMove ? counter % 3 : 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -50,24 +49,34 @@ public class Pacman extends MovableEntity {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.frightTimer > 0) {
|
||||
speed = RoundModifiers.getPacmanFrightSpeed(state.round);
|
||||
} else {
|
||||
speed = RoundModifiers.getPacmanSpeed(state.round);
|
||||
}
|
||||
|
||||
super.update(dt);
|
||||
|
||||
if (!state.paused && canMove && age % 4 == 0) {
|
||||
if (canMove && age % 4 == 0) {
|
||||
counter += 1;
|
||||
}
|
||||
|
||||
LevelTile tile = state.level.getTile(x, y);
|
||||
if (!alive && age % 8 == 0) {
|
||||
deathFrame++;
|
||||
}
|
||||
|
||||
LevelTile tile = state.level.getTile(pos);
|
||||
if (tile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (tile) {
|
||||
case PELLET:
|
||||
state.eatPellet(x, y);
|
||||
state.eatPellet(pos.x, pos.y);
|
||||
freezeFrames = 1;
|
||||
break;
|
||||
case POWER_PELLET:
|
||||
state.eatPowerPellet(x, y);
|
||||
state.eatPowerPellet(pos.x, pos.y);
|
||||
freezeFrames = 3;
|
||||
break;
|
||||
}
|
||||
|
27
core/src/com/me/pacman/entity/Pinky.java
Normal file
@ -0,0 +1,27 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
15
core/src/com/me/pacman/entity/ai/Behaviour.java
Normal file
@ -0,0 +1,15 @@
|
||||
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();
|
||||
|
||||
}
|
16
core/src/com/me/pacman/entity/ai/BlinkyChaseBehaviour.java
Normal file
@ -0,0 +1,16 @@
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
20
core/src/com/me/pacman/entity/ai/ClydeChaseBehaviour.java
Normal file
@ -0,0 +1,20 @@
|
||||
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;
|
||||
}
|
||||
}
|
20
core/src/com/me/pacman/entity/ai/InkyChaseBehaviour.java
Normal file
@ -0,0 +1,20 @@
|
||||
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)));
|
||||
}
|
||||
}
|
16
core/src/com/me/pacman/entity/ai/PinkyChaseBehaviour.java
Normal file
@ -0,0 +1,16 @@
|
||||
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()));
|
||||
}
|
||||
|
||||
}
|
18
core/src/com/me/pacman/entity/ai/ReturnToBase.java
Normal file
@ -0,0 +1,18 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
18
core/src/com/me/pacman/entity/ai/StaticTargetBehaviour.java
Normal file
@ -0,0 +1,18 @@
|
||||
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;
|
||||
}
|
||||
}
|
39
core/src/com/me/pacman/entity/ai/Target.java
Normal file
@ -0,0 +1,39 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
20
core/src/com/me/pacman/entity/path/EnterGhostHousePath.java
Normal file
@ -0,0 +1,20 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
20
core/src/com/me/pacman/entity/path/ExitGhostHousePath.java
Normal file
@ -0,0 +1,20 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
89
core/src/com/me/pacman/entity/path/Path.java
Normal file
@ -0,0 +1,89 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -1,33 +1,61 @@
|
||||
package com.me.pacman.level;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.me.pacman.Constants;
|
||||
import com.me.pacman.PacDude;
|
||||
|
||||
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 TextureRegion pellet;
|
||||
private TextureRegion powerPellet;
|
||||
private TextureRegion debug;
|
||||
|
||||
// Grid of tiles, [rows][columns]
|
||||
public LevelTile[][] tiles;
|
||||
|
||||
// Level width and height in tiles
|
||||
public int width;
|
||||
public int height;
|
||||
|
||||
private int pellets;
|
||||
private int pelletsRemaining;
|
||||
|
||||
public Level(PacDude game, String level) {
|
||||
this.game = game;
|
||||
|
||||
pellet = game.assets.level[0][6];
|
||||
powerPellet = game.assets.level[0][7];
|
||||
debug = game.assets.level[1][7];
|
||||
|
||||
LevelLoader loader = new LevelLoader(level);
|
||||
tiles = loader.loadLevel();
|
||||
|
||||
height = tiles.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) {
|
||||
@ -38,7 +66,15 @@ public class Level {
|
||||
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) {
|
||||
LevelTile exist = tiles[y][x];
|
||||
if (exist.isPellet() && !tile.isPellet()) {
|
||||
pelletsRemaining--;
|
||||
}
|
||||
tiles[y][x] = tile;
|
||||
}
|
||||
|
||||
@ -63,21 +99,27 @@ public class Level {
|
||||
for (int i = 0; i < tiles.length; i++) {
|
||||
LevelTile[] row = tiles[i];
|
||||
for (int j = 0; j < row.length; j++) {
|
||||
LevelTile component = row[j];
|
||||
renderTile(row[j], j, i, offsetX, offsetY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void renderTile(LevelTile tile, int tileX, int tileY, int offsetX, int offsetY) {
|
||||
TextureRegion sprite;
|
||||
switch (component) {
|
||||
switch (tile) {
|
||||
case PELLET:
|
||||
sprite = pellet;
|
||||
break;
|
||||
case POWER_PELLET:
|
||||
sprite = powerPellet;
|
||||
break;
|
||||
case DEBUG:
|
||||
sprite = debug;
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
game.batch.draw(sprite, (j * 8) + offsetX, (i * 8) + offsetY);
|
||||
}
|
||||
return;
|
||||
}
|
||||
game.batch.draw(sprite, (tileX * Constants.TILE_SIZE) + offsetX, (tileY * Constants.TILE_SIZE) + offsetY);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,10 +9,26 @@ public enum LevelTile {
|
||||
GHOST_CHAMBER,
|
||||
GHOST_GATE,
|
||||
EMPTY,
|
||||
DEBUG,
|
||||
;
|
||||
|
||||
public boolean isPassable() {
|
||||
return this != WALL && this != GHOST_CHAMBER && this != GHOST_GATE;
|
||||
switch (this) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
221
core/src/com/me/pacman/state/HighScoreEntryState.java
Normal file
@ -0,0 +1,221 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
106
core/src/com/me/pacman/state/HighScoresState.java
Normal file
@ -0,0 +1,106 @@
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
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.level.Level;
|
||||
|
||||
@ -11,4 +13,12 @@ public abstract class LevelState extends State {
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,28 @@
|
||||
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.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.me.pacman.Constants;
|
||||
import com.me.pacman.PacDude;
|
||||
import com.me.pacman.level.Level;
|
||||
import com.me.pacman.SoundManager;
|
||||
|
||||
public class MenuState extends LevelState {
|
||||
|
||||
private Texture levelBackground;
|
||||
private Texture background;
|
||||
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) {
|
||||
super(game);
|
||||
@ -17,23 +30,118 @@ public class MenuState extends LevelState {
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
levelBackground = game.assets.getLevelBackground();
|
||||
background = game.assets.getMenuBackground();
|
||||
logo = game.assets.getLogo();
|
||||
logo.setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear);
|
||||
font = game.assets.getFont();
|
||||
|
||||
level = new Level(game,"level");
|
||||
Gdx.input.setInputProcessor(this.new Controller());
|
||||
game.sound.play(SoundManager.Effect.BEGINNING);
|
||||
|
||||
switch(Gdx.app.getType()) {
|
||||
case Android:
|
||||
case iOS:
|
||||
selectedOption = -1;
|
||||
break;
|
||||
default:
|
||||
selectedOption = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render() {
|
||||
game.batch.draw(levelBackground, 0, 16);
|
||||
level.render(0, 16);
|
||||
game.batch.draw(logo, 0, 124, 224, 120);
|
||||
game.batch.draw(background, 0, 16);
|
||||
game.batch.draw(logo, 0, 140, 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
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,133 +5,357 @@ 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.g2d.BitmapFont;
|
||||
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.entity.MovableEntity;
|
||||
import com.me.pacman.entity.Pacman;
|
||||
import com.me.pacman.Score;
|
||||
import com.me.pacman.SoundManager;
|
||||
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.LevelTile;
|
||||
import com.me.pacman.RoundModifiers;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class PlayState extends LevelState {
|
||||
|
||||
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 Texture levelBackground, winBackground;
|
||||
private TextureRegion lifeSprite;
|
||||
|
||||
private Pacman pacman;
|
||||
public Random random;
|
||||
|
||||
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) {
|
||||
super(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setup() {
|
||||
levelBackground = game.assets.getLevelBackground();
|
||||
font = game.assets.getFont();
|
||||
lifeSprite = game.assets.pacman[2][1];
|
||||
Gdx.input.setInputProcessor(new Controller());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
pelletCount = 0;
|
||||
pelletEatenCount = 0;
|
||||
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
|
||||
public void setup() {
|
||||
levelBackground = game.assets.getLevelBackground();
|
||||
winBackground = game.assets.getLevelWinBackground();
|
||||
lifeSprite = game.assets.pacman[2][1];
|
||||
Gdx.input.setInputProcessor(new Controller());
|
||||
|
||||
preNewGame();
|
||||
setGameState(GameState.PRE_NEW_GAME);
|
||||
}
|
||||
|
||||
public GameState stateTransition() {
|
||||
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
|
||||
public void render() {
|
||||
game.batch.draw(levelBackground, 0, 16);
|
||||
level.render(0, 16);
|
||||
|
||||
game.assets.getFont().setColor(Color.WHITE);
|
||||
game.assets.getFont().draw(game.batch, "" + score, 40, 279);
|
||||
game.fontRenderer.setColor(Color.WHITE);
|
||||
game.fontRenderer.draw(game.batch, "1up", 3 * Constants.TILE_SIZE, 35 * Constants.TILE_SIZE);
|
||||
|
||||
// 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++) {
|
||||
game.batch.draw(lifeSprite, i * 16, 0);
|
||||
}
|
||||
|
||||
pacman.render(game.batch, 0, 16);
|
||||
// Draw the level tiles
|
||||
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) {
|
||||
game.assets.getFont().setColor(Color.YELLOW);
|
||||
game.assets.getFont().draw(game.batch, "paused", 90, 151);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float dt) {
|
||||
if (paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (waitTimer > 0) {
|
||||
waitTimer -= dt;
|
||||
if (waitTimer <= 0) {
|
||||
startGame();
|
||||
game.fontRenderer.setColor(Color.YELLOW);
|
||||
game.fontRenderer.draw(game.batch, "paused", 11 * Constants.TILE_SIZE, 15 * Constants.TILE_SIZE);
|
||||
} else {
|
||||
return;
|
||||
switch (state) {
|
||||
case PRE_NEW_GAME:
|
||||
case NEW_ROUND_WAIT:
|
||||
case START_ROUND_WAIT:
|
||||
game.fontRenderer.setColor(Color.YELLOW);
|
||||
game.fontRenderer.draw(game.batch, "ready!", 11 * Constants.TILE_SIZE, 15 * Constants.TILE_SIZE);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
level.setTile(x, y, LevelTile.EMPTY);
|
||||
if (pelletEatenCount % 2 == 0) {
|
||||
game.assets.chomp_1.play(1.0f);
|
||||
} else {
|
||||
game.assets.chomp_2.play(1.0f);
|
||||
}
|
||||
pelletEatenCount++;
|
||||
pelletCount--;
|
||||
|
||||
if (pelletCount == 0) {
|
||||
newGame();
|
||||
game.sound.play(level.getPelletsEaten() % 2 == 0? SoundManager.Effect.CHOMP_1 : SoundManager.Effect.CHOMP_2);
|
||||
|
||||
if (pelletsEatenSinceDeathCounterEnabled) {
|
||||
// Increase global dot counter when enabled
|
||||
pelletsEatenSinceDeath++;
|
||||
} else {
|
||||
// else increment appropriate ghost's individual counter
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
secondsSinceLastDot = 0;
|
||||
|
||||
if (level.getPelletsRemaining() == 0) {
|
||||
game.sound.stopLoops();
|
||||
setGameState(GameState.ROUND_WON_WAIT);
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,8 +366,235 @@ public class PlayState extends LevelState {
|
||||
|
||||
public void eatPowerPellet(float x, float y) {
|
||||
pelletEaten(x, y);
|
||||
// TODO: Enable ghost chase mode
|
||||
for (Ghost ghost : ghosts) {
|
||||
if (ghost.currentBehaviour instanceof ReturnToBase) continue;
|
||||
ghost.caught = false;
|
||||
ghost.currentBehaviour = ghost.frightBehaviour;
|
||||
ghost.reverse = true;
|
||||
}
|
||||
frightTimer = RoundModifiers.getFrightTime(round);
|
||||
ghostsCaught = 0;
|
||||
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
|
||||
@ -151,30 +602,4 @@ public class PlayState extends LevelState {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ public abstract class State extends ScreenAdapter {
|
||||
|
||||
public State(PacDude game) {
|
||||
this.game = game;
|
||||
this.setup();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2,10 +2,10 @@ apply plugin: "java"
|
||||
|
||||
sourceCompatibility = 1.7
|
||||
sourceSets.main.java.srcDirs = [ "src/" ]
|
||||
sourceSets.main.resources.srcDirs = ["../core/assets"]
|
||||
sourceSets.main.resources.srcDirs = ["../android/assets"]
|
||||
|
||||
project.ext.mainClassName = "com.me.pacman.desktop.DesktopLauncher"
|
||||
project.ext.assetsDir = new File("../core/assets")
|
||||
project.ext.assetsDir = new File("../android/assets")
|
||||
|
||||
task run(dependsOn: classes, type: JavaExec) {
|
||||
main = project.mainClassName
|
||||
|
@ -2,14 +2,15 @@ package com.me.pacman.desktop;
|
||||
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
import com.me.pacman.Constants;
|
||||
import com.me.pacman.PacDude;
|
||||
|
||||
public class DesktopLauncher {
|
||||
public static void main (String[] arg) {
|
||||
LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
|
||||
config.title = PacDude.TITLE + " - " + PacDude.VERSION;
|
||||
config.width = PacDude.LEVEL_WIDTH * 2;
|
||||
config.height = PacDude.LEVEL_HEIGHT * 2;
|
||||
config.title = Constants.TITLE + " - " + Constants.VERSION;
|
||||
config.width = Constants.GAME_WIDTH * 2;
|
||||
config.height = Constants.GAME_HEIGHT * 2;
|
||||
config.resizable = true;
|
||||
new LwjglApplication(new PacDude(), config);
|
||||
}
|
||||
|
5
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,5 @@
|
||||
#Tue Dec 24 14:05:31 GET 2019
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
112
icon.svg
Normal file
@ -0,0 +1,112 @@
|
||||
<?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>
|
After Width: | Height: | Size: 3.4 KiB |
87
icon_foreground.svg
Normal file
@ -0,0 +1,87 @@
|
||||
<?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>
|
After Width: | Height: | Size: 2.8 KiB |
@ -1 +1 @@
|
||||
include 'desktop', 'core'
|
||||
include 'desktop', 'android', 'core'
|
||||
|