Initial submission of Replica Island 1.1 source and assets.
git-svn-id: http://replicaisland.googlecode.com/svn/trunk@2 1dde52a2-2b62-11df-8484-2f92f965d510
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
new file mode 100644
index 0000000..f16dd96
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.replica.replicaisland" android:versionName="1.0" android:versionCode="8">
+ <application android:icon="@drawable/icon"
+ android:label="@string/app_name"
+ android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" >
+ <activity android:name=".MainMenuActivity"
+ android:label="@string/app_name"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="AndouKun" android:label="@string/app_name"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation"
+ android:launchMode="singleTask"/>
+
+ <activity android:name="LevelSelectActivity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation" />
+
+ <activity android:theme="@style/Theme.ConversationDialog"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation"
+ android:name="ConversationDialogActivity"/>
+
+ <activity android:name="DiaryActivity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation"/>
+
+
+ <activity android:name="SetPreferencesActivity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation" />
+
+ <activity android:name="AnimationPlayerActivity"
+ android:screenOrientation="landscape"
+ android:configChanges="keyboardHidden|orientation" />
+
+ </application>
+ <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission xmlns:android="http://schemas.android.com/apk/res/android"
+ android:name="android.permission.INTERNET"/>
+ <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="4"/>
+ <!-- Actually, this game works fine on small screens, but the only one out
+ right now has no 3D acceleration, so it's slow and unplayable. -->
+ <supports-screens android:largeScreens="true"
+ android:smallScreens="false"
+ android:anyDensity="true"
+ android:normalScreens="true"/>
+ <!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> -->
+</manifest>
+
diff --git a/default.properties b/default.properties
new file mode 100644
index 0000000..62bef18
--- /dev/null
+++ b/default.properties
@@ -0,0 +1,14 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system use,
+# "build.properties", and override values to adapt the script to your
+# project structure.
+
+apk-configurations=
+# Project target.
+target=android-4
+# Indicates whether an apk should be generated for each density.
+split.density=false
diff --git a/res/anim/button_flicker.xml b/res/anim/button_flicker.xml
new file mode 100644
index 0000000..d8ce192
--- /dev/null
+++ b/res/anim/button_flicker.xml
@@ -0,0 +1,9 @@
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="100"
+ android:repeatCount="7"
+ android:repeatMode="reverse"
+ android:fillAfter="true"/>
+
\ No newline at end of file
diff --git a/res/anim/button_slide.xml b/res/anim/button_slide.xml
new file mode 100644
index 0000000..3949c76
--- /dev/null
+++ b/res/anim/button_slide.xml
@@ -0,0 +1,28 @@
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <translate
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromYDelta="200"
+ android:toYDelta="-10"
+ android:duration="600"
+ android:fillAfter="false"/>
+
+
+ <translate
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromYDelta="-10"
+ android:toYDelta="10"
+ android:duration="150"
+ android:fillAfter="false"
+ android:startOffset="600"/>
+
+ <translate
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromYDelta="10"
+ android:toYDelta="0"
+ android:duration="200"
+ android:fillAfter="false"
+ android:startOffset="750"/>
+</set>
+
\ No newline at end of file
diff --git a/res/anim/fade.xml b/res/anim/fade.xml
new file mode 100644
index 0000000..b84c798
--- /dev/null
+++ b/res/anim/fade.xml
@@ -0,0 +1,6 @@
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.5"
+ android:duration="3000"
+ android:fillAfter="true"/>
\ No newline at end of file
diff --git a/res/anim/fade_out.xml b/res/anim/fade_out.xml
new file mode 100644
index 0000000..f01c2f7
--- /dev/null
+++ b/res/anim/fade_out.xml
@@ -0,0 +1,6 @@
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000"
+ android:fillAfter="true"/>
\ No newline at end of file
diff --git a/res/anim/horizontal_layer1_slide.xml b/res/anim/horizontal_layer1_slide.xml
new file mode 100644
index 0000000..7d2a61f
--- /dev/null
+++ b/res/anim/horizontal_layer1_slide.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromXDelta="0"
+ android:toXDelta="-170"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="2000"/>
\ No newline at end of file
diff --git a/res/anim/horizontal_layer2_slide.xml b/res/anim/horizontal_layer2_slide.xml
new file mode 100644
index 0000000..3a4f2cb
--- /dev/null
+++ b/res/anim/horizontal_layer2_slide.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromXDelta="0"
+ android:toXDelta="-280"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="2000"/>
\ No newline at end of file
diff --git a/res/anim/kabocha_game_over.xml b/res/anim/kabocha_game_over.xml
new file mode 100644
index 0000000..9da68ef
--- /dev/null
+++ b/res/anim/kabocha_game_over.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromXDelta="-200"
+ android:toXDelta="0"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="8000"/>
\ No newline at end of file
diff --git a/res/anim/kyle_fall.xml b/res/anim/kyle_fall.xml
new file mode 100644
index 0000000..5368349
--- /dev/null
+++ b/res/anim/kyle_fall.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:oneshot="true">
+ <item android:drawable="@drawable/anime_kyle_fall01" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall02" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall03" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall04" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall05" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall06" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall07" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall08" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall09" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall10" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall11" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall12" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall13" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall14" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall15" android:duration="83" />
+ <item android:drawable="@drawable/anime_kyle_fall16" android:duration="83" />
+</animation-list>
\ No newline at end of file
diff --git a/res/anim/rokudou_game_over.xml b/res/anim/rokudou_game_over.xml
new file mode 100644
index 0000000..4db9a79
--- /dev/null
+++ b/res/anim/rokudou_game_over.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromYDelta="-200"
+ android:toYDelta="0"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="8000"/>
\ No newline at end of file
diff --git a/res/anim/rokudou_slide_bg.xml b/res/anim/rokudou_slide_bg.xml
new file mode 100644
index 0000000..486a015
--- /dev/null
+++ b/res/anim/rokudou_slide_bg.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromYDelta="-130"
+ android:toYDelta="-50"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="2000"/>
\ No newline at end of file
diff --git a/res/anim/rokudou_slide_cliffs.xml b/res/anim/rokudou_slide_cliffs.xml
new file mode 100644
index 0000000..9179c8f
--- /dev/null
+++ b/res/anim/rokudou_slide_cliffs.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromYDelta="-158"
+ android:toYDelta="2"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="2000"/>
\ No newline at end of file
diff --git a/res/anim/rokudou_slide_rokudou.xml b/res/anim/rokudou_slide_rokudou.xml
new file mode 100644
index 0000000..db7da55
--- /dev/null
+++ b/res/anim/rokudou_slide_rokudou.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromYDelta="278"
+ android:toYDelta="-11"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="2000"/>
\ No newline at end of file
diff --git a/res/anim/rokudou_slide_sphere.xml b/res/anim/rokudou_slide_sphere.xml
new file mode 100644
index 0000000..5990d84
--- /dev/null
+++ b/res/anim/rokudou_slide_sphere.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromYDelta="-110"
+ android:toYDelta="-10"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="2000"/>
\ No newline at end of file
diff --git a/res/anim/ui_button.xml b/res/anim/ui_button.xml
new file mode 100644
index 0000000..2af91de
--- /dev/null
+++ b/res/anim/ui_button.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+ android:oneshot="false">
+ <item android:drawable="@drawable/ui_arrow_dark" android:duration="500" />
+ <item android:drawable="@drawable/ui_arrow_light" android:duration="500" />
+</animation-list>
\ No newline at end of file
diff --git a/res/anim/wait_message_fade.xml b/res/anim/wait_message_fade.xml
new file mode 100644
index 0000000..469e481
--- /dev/null
+++ b/res/anim/wait_message_fade.xml
@@ -0,0 +1,8 @@
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+ android:duration="1000"
+ android:repeatCount="-1"
+ android:repeatMode="reverse"
+ android:fillAfter="true"/>
\ No newline at end of file
diff --git a/res/anim/wanda_game_over.xml b/res/anim/wanda_game_over.xml
new file mode 100644
index 0000000..8d6ef0a
--- /dev/null
+++ b/res/anim/wanda_game_over.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:fromXDelta="200"
+ android:toXDelta="0"
+ android:duration="6000"
+ android:fillAfter="true"
+ android:startOffset="8000"/>
\ No newline at end of file
diff --git a/res/drawable-ja/title.png b/res/drawable-ja/title.png
new file mode 100644
index 0000000..492290b
--- /dev/null
+++ b/res/drawable-ja/title.png
Binary files differ
diff --git a/res/drawable-ja/titletileset.png b/res/drawable-ja/titletileset.png
new file mode 100644
index 0000000..deed9c8
--- /dev/null
+++ b/res/drawable-ja/titletileset.png
Binary files differ
diff --git a/res/drawable-normal-mdpi/level_1_9_dialog_wanda.xml b/res/drawable-normal-mdpi/level_1_9_dialog_wanda.xml
new file mode 100644
index 0000000..c892c6d
--- /dev/null
+++ b/res/drawable-normal-mdpi/level_1_9_dialog_wanda.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_surprised"
+ text = "@string/Wanda_1_9_1_1"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/drawable-normal-mdpi/title_background.png b/res/drawable-normal-mdpi/title_background.png
new file mode 100644
index 0000000..ee01f83
--- /dev/null
+++ b/res/drawable-normal-mdpi/title_background.png
Binary files differ
diff --git a/res/drawable/andou_diag01.png b/res/drawable/andou_diag01.png
new file mode 100644
index 0000000..f7ff559
--- /dev/null
+++ b/res/drawable/andou_diag01.png
Binary files differ
diff --git a/res/drawable/andou_diag02.png b/res/drawable/andou_diag02.png
new file mode 100644
index 0000000..3911a85
--- /dev/null
+++ b/res/drawable/andou_diag02.png
Binary files differ
diff --git a/res/drawable/andou_diag03.png b/res/drawable/andou_diag03.png
new file mode 100644
index 0000000..3daea11
--- /dev/null
+++ b/res/drawable/andou_diag03.png
Binary files differ
diff --git a/res/drawable/andou_diagmore01.png b/res/drawable/andou_diagmore01.png
new file mode 100644
index 0000000..34dbef4
--- /dev/null
+++ b/res/drawable/andou_diagmore01.png
Binary files differ
diff --git a/res/drawable/andou_diagmore02.png b/res/drawable/andou_diagmore02.png
new file mode 100644
index 0000000..e282395
--- /dev/null
+++ b/res/drawable/andou_diagmore02.png
Binary files differ
diff --git a/res/drawable/andou_diagmore03.png b/res/drawable/andou_diagmore03.png
new file mode 100644
index 0000000..3b0f268
--- /dev/null
+++ b/res/drawable/andou_diagmore03.png
Binary files differ
diff --git a/res/drawable/andou_die01.png b/res/drawable/andou_die01.png
new file mode 100644
index 0000000..565625b
--- /dev/null
+++ b/res/drawable/andou_die01.png
Binary files differ
diff --git a/res/drawable/andou_die02.png b/res/drawable/andou_die02.png
new file mode 100644
index 0000000..67c84ea
--- /dev/null
+++ b/res/drawable/andou_die02.png
Binary files differ
diff --git a/res/drawable/andou_explode01.png b/res/drawable/andou_explode01.png
new file mode 100644
index 0000000..140903f
--- /dev/null
+++ b/res/drawable/andou_explode01.png
Binary files differ
diff --git a/res/drawable/andou_explode02.png b/res/drawable/andou_explode02.png
new file mode 100644
index 0000000..4312953
--- /dev/null
+++ b/res/drawable/andou_explode02.png
Binary files differ
diff --git a/res/drawable/andou_explode03.png b/res/drawable/andou_explode03.png
new file mode 100644
index 0000000..18a240d
--- /dev/null
+++ b/res/drawable/andou_explode03.png
Binary files differ
diff --git a/res/drawable/andou_explode04.png b/res/drawable/andou_explode04.png
new file mode 100644
index 0000000..0a24f36
--- /dev/null
+++ b/res/drawable/andou_explode04.png
Binary files differ
diff --git a/res/drawable/andou_explode05.png b/res/drawable/andou_explode05.png
new file mode 100644
index 0000000..c884574
--- /dev/null
+++ b/res/drawable/andou_explode05.png
Binary files differ
diff --git a/res/drawable/andou_explode06.png b/res/drawable/andou_explode06.png
new file mode 100644
index 0000000..0f2fe56
--- /dev/null
+++ b/res/drawable/andou_explode06.png
Binary files differ
diff --git a/res/drawable/andou_explode07.png b/res/drawable/andou_explode07.png
new file mode 100644
index 0000000..64d6346
--- /dev/null
+++ b/res/drawable/andou_explode07.png
Binary files differ
diff --git a/res/drawable/andou_explode08.png b/res/drawable/andou_explode08.png
new file mode 100644
index 0000000..8cdd5bf
--- /dev/null
+++ b/res/drawable/andou_explode08.png
Binary files differ
diff --git a/res/drawable/andou_explode09.png b/res/drawable/andou_explode09.png
new file mode 100644
index 0000000..640c808
--- /dev/null
+++ b/res/drawable/andou_explode09.png
Binary files differ
diff --git a/res/drawable/andou_explode10.png b/res/drawable/andou_explode10.png
new file mode 100644
index 0000000..00699e2
--- /dev/null
+++ b/res/drawable/andou_explode10.png
Binary files differ
diff --git a/res/drawable/andou_explode11.png b/res/drawable/andou_explode11.png
new file mode 100644
index 0000000..1199ea4
--- /dev/null
+++ b/res/drawable/andou_explode11.png
Binary files differ
diff --git a/res/drawable/andou_explode12.png b/res/drawable/andou_explode12.png
new file mode 100644
index 0000000..de5a4ea
--- /dev/null
+++ b/res/drawable/andou_explode12.png
Binary files differ
diff --git a/res/drawable/andou_fall01.png b/res/drawable/andou_fall01.png
new file mode 100644
index 0000000..1594d71
--- /dev/null
+++ b/res/drawable/andou_fall01.png
Binary files differ
diff --git a/res/drawable/andou_fall02.png b/res/drawable/andou_fall02.png
new file mode 100644
index 0000000..15f04e4
--- /dev/null
+++ b/res/drawable/andou_fall02.png
Binary files differ
diff --git a/res/drawable/andou_fall03.png b/res/drawable/andou_fall03.png
new file mode 100644
index 0000000..1d557b5
--- /dev/null
+++ b/res/drawable/andou_fall03.png
Binary files differ
diff --git a/res/drawable/andou_fall04.png b/res/drawable/andou_fall04.png
new file mode 100644
index 0000000..a800c4a
--- /dev/null
+++ b/res/drawable/andou_fall04.png
Binary files differ
diff --git a/res/drawable/andou_flyup01.png b/res/drawable/andou_flyup01.png
new file mode 100644
index 0000000..342e3bf
--- /dev/null
+++ b/res/drawable/andou_flyup01.png
Binary files differ
diff --git a/res/drawable/andou_flyup02.png b/res/drawable/andou_flyup02.png
new file mode 100644
index 0000000..4c51be0
--- /dev/null
+++ b/res/drawable/andou_flyup02.png
Binary files differ
diff --git a/res/drawable/andou_flyup03.png b/res/drawable/andou_flyup03.png
new file mode 100644
index 0000000..5ebaccf
--- /dev/null
+++ b/res/drawable/andou_flyup03.png
Binary files differ
diff --git a/res/drawable/andou_hit.png b/res/drawable/andou_hit.png
new file mode 100644
index 0000000..9494221
--- /dev/null
+++ b/res/drawable/andou_hit.png
Binary files differ
diff --git a/res/drawable/andou_stand.png b/res/drawable/andou_stand.png
new file mode 100644
index 0000000..04484a6
--- /dev/null
+++ b/res/drawable/andou_stand.png
Binary files differ
diff --git a/res/drawable/andou_stomp01.png b/res/drawable/andou_stomp01.png
new file mode 100644
index 0000000..24db381
--- /dev/null
+++ b/res/drawable/andou_stomp01.png
Binary files differ
diff --git a/res/drawable/andou_stomp02.png b/res/drawable/andou_stomp02.png
new file mode 100644
index 0000000..c9eb196
--- /dev/null
+++ b/res/drawable/andou_stomp02.png
Binary files differ
diff --git a/res/drawable/andou_stomp03.png b/res/drawable/andou_stomp03.png
new file mode 100644
index 0000000..94fd1d1
--- /dev/null
+++ b/res/drawable/andou_stomp03.png
Binary files differ
diff --git a/res/drawable/andou_stomp04.png b/res/drawable/andou_stomp04.png
new file mode 100644
index 0000000..0143c8e
--- /dev/null
+++ b/res/drawable/andou_stomp04.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall01.png b/res/drawable/anime_kyle_fall01.png
new file mode 100644
index 0000000..af85e08
--- /dev/null
+++ b/res/drawable/anime_kyle_fall01.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall02.png b/res/drawable/anime_kyle_fall02.png
new file mode 100644
index 0000000..338574a
--- /dev/null
+++ b/res/drawable/anime_kyle_fall02.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall03.png b/res/drawable/anime_kyle_fall03.png
new file mode 100644
index 0000000..ee504ac
--- /dev/null
+++ b/res/drawable/anime_kyle_fall03.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall04.png b/res/drawable/anime_kyle_fall04.png
new file mode 100644
index 0000000..c49238a
--- /dev/null
+++ b/res/drawable/anime_kyle_fall04.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall05.png b/res/drawable/anime_kyle_fall05.png
new file mode 100644
index 0000000..5abe355
--- /dev/null
+++ b/res/drawable/anime_kyle_fall05.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall06.png b/res/drawable/anime_kyle_fall06.png
new file mode 100644
index 0000000..47ea705
--- /dev/null
+++ b/res/drawable/anime_kyle_fall06.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall07.png b/res/drawable/anime_kyle_fall07.png
new file mode 100644
index 0000000..565f8ad
--- /dev/null
+++ b/res/drawable/anime_kyle_fall07.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall08.png b/res/drawable/anime_kyle_fall08.png
new file mode 100644
index 0000000..d80591f
--- /dev/null
+++ b/res/drawable/anime_kyle_fall08.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall09.png b/res/drawable/anime_kyle_fall09.png
new file mode 100644
index 0000000..f530877
--- /dev/null
+++ b/res/drawable/anime_kyle_fall09.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall10.png b/res/drawable/anime_kyle_fall10.png
new file mode 100644
index 0000000..81b46c0
--- /dev/null
+++ b/res/drawable/anime_kyle_fall10.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall11.png b/res/drawable/anime_kyle_fall11.png
new file mode 100644
index 0000000..ac8f8a8
--- /dev/null
+++ b/res/drawable/anime_kyle_fall11.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall12.png b/res/drawable/anime_kyle_fall12.png
new file mode 100644
index 0000000..7eebe03
--- /dev/null
+++ b/res/drawable/anime_kyle_fall12.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall13.png b/res/drawable/anime_kyle_fall13.png
new file mode 100644
index 0000000..bc43e2a
--- /dev/null
+++ b/res/drawable/anime_kyle_fall13.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall14.png b/res/drawable/anime_kyle_fall14.png
new file mode 100644
index 0000000..957b10b
--- /dev/null
+++ b/res/drawable/anime_kyle_fall14.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall15.png b/res/drawable/anime_kyle_fall15.png
new file mode 100644
index 0000000..406e4dd
--- /dev/null
+++ b/res/drawable/anime_kyle_fall15.png
Binary files differ
diff --git a/res/drawable/anime_kyle_fall16.png b/res/drawable/anime_kyle_fall16.png
new file mode 100644
index 0000000..1c0f8f3
--- /dev/null
+++ b/res/drawable/anime_kyle_fall16.png
Binary files differ
diff --git a/res/drawable/background_diary.png b/res/drawable/background_diary.png
new file mode 100644
index 0000000..129a482
--- /dev/null
+++ b/res/drawable/background_diary.png
Binary files differ
diff --git a/res/drawable/background_grass.png b/res/drawable/background_grass.png
new file mode 100644
index 0000000..68f51ee
--- /dev/null
+++ b/res/drawable/background_grass.png
Binary files differ
diff --git a/res/drawable/background_grass2.png b/res/drawable/background_grass2.png
new file mode 100644
index 0000000..cb158b1
--- /dev/null
+++ b/res/drawable/background_grass2.png
Binary files differ
diff --git a/res/drawable/background_island.png b/res/drawable/background_island.png
new file mode 100644
index 0000000..80e5e34
--- /dev/null
+++ b/res/drawable/background_island.png
Binary files differ
diff --git a/res/drawable/background_island2.png b/res/drawable/background_island2.png
new file mode 100644
index 0000000..8340755
--- /dev/null
+++ b/res/drawable/background_island2.png
Binary files differ
diff --git a/res/drawable/background_lab01.png b/res/drawable/background_lab01.png
new file mode 100644
index 0000000..13899fc
--- /dev/null
+++ b/res/drawable/background_lab01.png
Binary files differ
diff --git a/res/drawable/background_sewage.png b/res/drawable/background_sewage.png
new file mode 100644
index 0000000..5d7e097
--- /dev/null
+++ b/res/drawable/background_sewage.png
Binary files differ
diff --git a/res/drawable/background_sunset.png b/res/drawable/background_sunset.png
new file mode 100644
index 0000000..68f51ee
--- /dev/null
+++ b/res/drawable/background_sunset.png
Binary files differ
diff --git a/res/drawable/background_underground.png b/res/drawable/background_underground.png
new file mode 100644
index 0000000..91469fd
--- /dev/null
+++ b/res/drawable/background_underground.png
Binary files differ
diff --git a/res/drawable/black.png b/res/drawable/black.png
new file mode 100644
index 0000000..10d896b
--- /dev/null
+++ b/res/drawable/black.png
Binary files differ
diff --git a/res/drawable/cave.png b/res/drawable/cave.png
new file mode 100644
index 0000000..7faf34c
--- /dev/null
+++ b/res/drawable/cave.png
Binary files differ
diff --git a/res/drawable/collision_map.png b/res/drawable/collision_map.png
new file mode 100644
index 0000000..5d7e11c
--- /dev/null
+++ b/res/drawable/collision_map.png
Binary files differ
diff --git a/res/drawable/custom_toast_border.xml b/res/drawable/custom_toast_border.xml
new file mode 100644
index 0000000..fbb4fd8
--- /dev/null
+++ b/res/drawable/custom_toast_border.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!-- Thanks to http://hustleplay.wordpress.com/2009/07/23/replicating-default-android-toast/ ! -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+
+<solid android:color="#99000000"/>
+<padding
+ android:left="40dp"
+ android:top="40dp"
+ android:right="40dp"
+ android:bottom="40dp" />
+<corners android:radius="10dp" />
+</shape>
\ No newline at end of file
diff --git a/res/drawable/debug_box_blue.png b/res/drawable/debug_box_blue.png
new file mode 100644
index 0000000..3bc3354
--- /dev/null
+++ b/res/drawable/debug_box_blue.png
Binary files differ
diff --git a/res/drawable/debug_box_outline.png b/res/drawable/debug_box_outline.png
new file mode 100644
index 0000000..59e3215
--- /dev/null
+++ b/res/drawable/debug_box_outline.png
Binary files differ
diff --git a/res/drawable/debug_box_red.png b/res/drawable/debug_box_red.png
new file mode 100644
index 0000000..cef3dc5
--- /dev/null
+++ b/res/drawable/debug_box_red.png
Binary files differ
diff --git a/res/drawable/debug_circle_blue.png b/res/drawable/debug_circle_blue.png
new file mode 100644
index 0000000..c469653
--- /dev/null
+++ b/res/drawable/debug_circle_blue.png
Binary files differ
diff --git a/res/drawable/debug_circle_outline.png b/res/drawable/debug_circle_outline.png
new file mode 100644
index 0000000..0bb5abb
--- /dev/null
+++ b/res/drawable/debug_circle_outline.png
Binary files differ
diff --git a/res/drawable/debug_circle_red.png b/res/drawable/debug_circle_red.png
new file mode 100644
index 0000000..c868808
--- /dev/null
+++ b/res/drawable/debug_circle_red.png
Binary files differ
diff --git a/res/drawable/dialog_box.9.png b/res/drawable/dialog_box.9.png
new file mode 100644
index 0000000..c5f2cc7
--- /dev/null
+++ b/res/drawable/dialog_box.9.png
Binary files differ
diff --git a/res/drawable/dialogue.png b/res/drawable/dialogue.png
new file mode 100644
index 0000000..f609fb3
--- /dev/null
+++ b/res/drawable/dialogue.png
Binary files differ
diff --git a/res/drawable/dust01.png b/res/drawable/dust01.png
new file mode 100644
index 0000000..1f01f28
--- /dev/null
+++ b/res/drawable/dust01.png
Binary files differ
diff --git a/res/drawable/dust02.png b/res/drawable/dust02.png
new file mode 100644
index 0000000..96e5f99
--- /dev/null
+++ b/res/drawable/dust02.png
Binary files differ
diff --git a/res/drawable/dust03.png b/res/drawable/dust03.png
new file mode 100644
index 0000000..b61986d
--- /dev/null
+++ b/res/drawable/dust03.png
Binary files differ
diff --git a/res/drawable/dust04.png b/res/drawable/dust04.png
new file mode 100644
index 0000000..682467d
--- /dev/null
+++ b/res/drawable/dust04.png
Binary files differ
diff --git a/res/drawable/dust05.png b/res/drawable/dust05.png
new file mode 100644
index 0000000..e9a6965
--- /dev/null
+++ b/res/drawable/dust05.png
Binary files differ
diff --git a/res/drawable/effect_bullet01.png b/res/drawable/effect_bullet01.png
new file mode 100644
index 0000000..ce99c45
--- /dev/null
+++ b/res/drawable/effect_bullet01.png
Binary files differ
diff --git a/res/drawable/effect_bullet02.png b/res/drawable/effect_bullet02.png
new file mode 100644
index 0000000..daea3b1
--- /dev/null
+++ b/res/drawable/effect_bullet02.png
Binary files differ
diff --git a/res/drawable/effect_crush_back01.png b/res/drawable/effect_crush_back01.png
new file mode 100644
index 0000000..350487b
--- /dev/null
+++ b/res/drawable/effect_crush_back01.png
Binary files differ
diff --git a/res/drawable/effect_crush_back02.png b/res/drawable/effect_crush_back02.png
new file mode 100644
index 0000000..4a9e249
--- /dev/null
+++ b/res/drawable/effect_crush_back02.png
Binary files differ
diff --git a/res/drawable/effect_crush_back03.png b/res/drawable/effect_crush_back03.png
new file mode 100644
index 0000000..4a9e249
--- /dev/null
+++ b/res/drawable/effect_crush_back03.png
Binary files differ
diff --git a/res/drawable/effect_crush_front01.png b/res/drawable/effect_crush_front01.png
new file mode 100644
index 0000000..aaeea33
--- /dev/null
+++ b/res/drawable/effect_crush_front01.png
Binary files differ
diff --git a/res/drawable/effect_crush_front02.png b/res/drawable/effect_crush_front02.png
new file mode 100644
index 0000000..b774948
--- /dev/null
+++ b/res/drawable/effect_crush_front02.png
Binary files differ
diff --git a/res/drawable/effect_crush_front03.png b/res/drawable/effect_crush_front03.png
new file mode 100644
index 0000000..5424031
--- /dev/null
+++ b/res/drawable/effect_crush_front03.png
Binary files differ
diff --git a/res/drawable/effect_crush_front04.png b/res/drawable/effect_crush_front04.png
new file mode 100644
index 0000000..41ad923
--- /dev/null
+++ b/res/drawable/effect_crush_front04.png
Binary files differ
diff --git a/res/drawable/effect_crush_front05.png b/res/drawable/effect_crush_front05.png
new file mode 100644
index 0000000..64c795d
--- /dev/null
+++ b/res/drawable/effect_crush_front05.png
Binary files differ
diff --git a/res/drawable/effect_crush_front06.png b/res/drawable/effect_crush_front06.png
new file mode 100644
index 0000000..656a8b5
--- /dev/null
+++ b/res/drawable/effect_crush_front06.png
Binary files differ
diff --git a/res/drawable/effect_crush_front07.png b/res/drawable/effect_crush_front07.png
new file mode 100644
index 0000000..5c64069
--- /dev/null
+++ b/res/drawable/effect_crush_front07.png
Binary files differ
diff --git a/res/drawable/effect_energyball01.png b/res/drawable/effect_energyball01.png
new file mode 100644
index 0000000..43d8910
--- /dev/null
+++ b/res/drawable/effect_energyball01.png
Binary files differ
diff --git a/res/drawable/effect_energyball02.png b/res/drawable/effect_energyball02.png
new file mode 100644
index 0000000..07d5d91
--- /dev/null
+++ b/res/drawable/effect_energyball02.png
Binary files differ
diff --git a/res/drawable/effect_energyball03.png b/res/drawable/effect_energyball03.png
new file mode 100644
index 0000000..47f9442
--- /dev/null
+++ b/res/drawable/effect_energyball03.png
Binary files differ
diff --git a/res/drawable/effect_energyball04.png b/res/drawable/effect_energyball04.png
new file mode 100644
index 0000000..f6af012
--- /dev/null
+++ b/res/drawable/effect_energyball04.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big01.png b/res/drawable/effect_explosion_big01.png
new file mode 100644
index 0000000..228ade3
--- /dev/null
+++ b/res/drawable/effect_explosion_big01.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big02.png b/res/drawable/effect_explosion_big02.png
new file mode 100644
index 0000000..5fdc2fa
--- /dev/null
+++ b/res/drawable/effect_explosion_big02.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big03.png b/res/drawable/effect_explosion_big03.png
new file mode 100644
index 0000000..84b6993
--- /dev/null
+++ b/res/drawable/effect_explosion_big03.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big04.png b/res/drawable/effect_explosion_big04.png
new file mode 100644
index 0000000..b8a8327
--- /dev/null
+++ b/res/drawable/effect_explosion_big04.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big05.png b/res/drawable/effect_explosion_big05.png
new file mode 100644
index 0000000..a6f223b
--- /dev/null
+++ b/res/drawable/effect_explosion_big05.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big06.png b/res/drawable/effect_explosion_big06.png
new file mode 100644
index 0000000..35ddde3
--- /dev/null
+++ b/res/drawable/effect_explosion_big06.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big07.png b/res/drawable/effect_explosion_big07.png
new file mode 100644
index 0000000..0705fb2
--- /dev/null
+++ b/res/drawable/effect_explosion_big07.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big08.png b/res/drawable/effect_explosion_big08.png
new file mode 100644
index 0000000..c7c20d2
--- /dev/null
+++ b/res/drawable/effect_explosion_big08.png
Binary files differ
diff --git a/res/drawable/effect_explosion_big09.png b/res/drawable/effect_explosion_big09.png
new file mode 100644
index 0000000..05935bd
--- /dev/null
+++ b/res/drawable/effect_explosion_big09.png
Binary files differ
diff --git a/res/drawable/effect_explosion_small01.png b/res/drawable/effect_explosion_small01.png
new file mode 100644
index 0000000..921bd62
--- /dev/null
+++ b/res/drawable/effect_explosion_small01.png
Binary files differ
diff --git a/res/drawable/effect_explosion_small02.png b/res/drawable/effect_explosion_small02.png
new file mode 100644
index 0000000..5a51d79
--- /dev/null
+++ b/res/drawable/effect_explosion_small02.png
Binary files differ
diff --git a/res/drawable/effect_explosion_small03.png b/res/drawable/effect_explosion_small03.png
new file mode 100644
index 0000000..06882d7
--- /dev/null
+++ b/res/drawable/effect_explosion_small03.png
Binary files differ
diff --git a/res/drawable/effect_explosion_small04.png b/res/drawable/effect_explosion_small04.png
new file mode 100644
index 0000000..d04256f
--- /dev/null
+++ b/res/drawable/effect_explosion_small04.png
Binary files differ
diff --git a/res/drawable/effect_explosion_small05.png b/res/drawable/effect_explosion_small05.png
new file mode 100644
index 0000000..6b3cd70
--- /dev/null
+++ b/res/drawable/effect_explosion_small05.png
Binary files differ
diff --git a/res/drawable/effect_explosion_small06.png b/res/drawable/effect_explosion_small06.png
new file mode 100644
index 0000000..cd9d5b7
--- /dev/null
+++ b/res/drawable/effect_explosion_small06.png
Binary files differ
diff --git a/res/drawable/effect_explosion_small07.png b/res/drawable/effect_explosion_small07.png
new file mode 100644
index 0000000..aba86b3
--- /dev/null
+++ b/res/drawable/effect_explosion_small07.png
Binary files differ
diff --git a/res/drawable/effect_glow01.png b/res/drawable/effect_glow01.png
new file mode 100644
index 0000000..79ebbe1
--- /dev/null
+++ b/res/drawable/effect_glow01.png
Binary files differ
diff --git a/res/drawable/effect_glow02.png b/res/drawable/effect_glow02.png
new file mode 100644
index 0000000..c31042c
--- /dev/null
+++ b/res/drawable/effect_glow02.png
Binary files differ
diff --git a/res/drawable/effect_glow03.png b/res/drawable/effect_glow03.png
new file mode 100644
index 0000000..d1633c5
--- /dev/null
+++ b/res/drawable/effect_glow03.png
Binary files differ
diff --git a/res/drawable/effect_smoke_big01.png b/res/drawable/effect_smoke_big01.png
new file mode 100644
index 0000000..660788e
--- /dev/null
+++ b/res/drawable/effect_smoke_big01.png
Binary files differ
diff --git a/res/drawable/effect_smoke_big02.png b/res/drawable/effect_smoke_big02.png
new file mode 100644
index 0000000..a0dcb8f
--- /dev/null
+++ b/res/drawable/effect_smoke_big02.png
Binary files differ
diff --git a/res/drawable/effect_smoke_big03.png b/res/drawable/effect_smoke_big03.png
new file mode 100644
index 0000000..2f6bcbb
--- /dev/null
+++ b/res/drawable/effect_smoke_big03.png
Binary files differ
diff --git a/res/drawable/effect_smoke_big04.png b/res/drawable/effect_smoke_big04.png
new file mode 100644
index 0000000..f7b7a18
--- /dev/null
+++ b/res/drawable/effect_smoke_big04.png
Binary files differ
diff --git a/res/drawable/effect_smoke_big05.png b/res/drawable/effect_smoke_big05.png
new file mode 100644
index 0000000..6c7d4cd
--- /dev/null
+++ b/res/drawable/effect_smoke_big05.png
Binary files differ
diff --git a/res/drawable/effect_smoke_small01.png b/res/drawable/effect_smoke_small01.png
new file mode 100644
index 0000000..b9d1c1c
--- /dev/null
+++ b/res/drawable/effect_smoke_small01.png
Binary files differ
diff --git a/res/drawable/effect_smoke_small02.png b/res/drawable/effect_smoke_small02.png
new file mode 100644
index 0000000..eb5ecef
--- /dev/null
+++ b/res/drawable/effect_smoke_small02.png
Binary files differ
diff --git a/res/drawable/effect_smoke_small03.png b/res/drawable/effect_smoke_small03.png
new file mode 100644
index 0000000..4adbba3
--- /dev/null
+++ b/res/drawable/effect_smoke_small03.png
Binary files differ
diff --git a/res/drawable/effect_smoke_small04.png b/res/drawable/effect_smoke_small04.png
new file mode 100644
index 0000000..575be64
--- /dev/null
+++ b/res/drawable/effect_smoke_small04.png
Binary files differ
diff --git a/res/drawable/effect_smoke_small05.png b/res/drawable/effect_smoke_small05.png
new file mode 100644
index 0000000..def5ab4
--- /dev/null
+++ b/res/drawable/effect_smoke_small05.png
Binary files differ
diff --git a/res/drawable/enemy_bat01.png b/res/drawable/enemy_bat01.png
new file mode 100644
index 0000000..6fc806a
--- /dev/null
+++ b/res/drawable/enemy_bat01.png
Binary files differ
diff --git a/res/drawable/enemy_bat02.png b/res/drawable/enemy_bat02.png
new file mode 100644
index 0000000..5698871
--- /dev/null
+++ b/res/drawable/enemy_bat02.png
Binary files differ
diff --git a/res/drawable/enemy_bat03.png b/res/drawable/enemy_bat03.png
new file mode 100644
index 0000000..47fe974
--- /dev/null
+++ b/res/drawable/enemy_bat03.png
Binary files differ
diff --git a/res/drawable/enemy_bat04.png b/res/drawable/enemy_bat04.png
new file mode 100644
index 0000000..7096118
--- /dev/null
+++ b/res/drawable/enemy_bat04.png
Binary files differ
diff --git a/res/drawable/enemy_brobot_idle01.png b/res/drawable/enemy_brobot_idle01.png
new file mode 100644
index 0000000..b06d8f9
--- /dev/null
+++ b/res/drawable/enemy_brobot_idle01.png
Binary files differ
diff --git a/res/drawable/enemy_brobot_idle02.png b/res/drawable/enemy_brobot_idle02.png
new file mode 100644
index 0000000..bd75506
--- /dev/null
+++ b/res/drawable/enemy_brobot_idle02.png
Binary files differ
diff --git a/res/drawable/enemy_brobot_idle03.png b/res/drawable/enemy_brobot_idle03.png
new file mode 100644
index 0000000..367ad7d
--- /dev/null
+++ b/res/drawable/enemy_brobot_idle03.png
Binary files differ
diff --git a/res/drawable/enemy_brobot_walk01.png b/res/drawable/enemy_brobot_walk01.png
new file mode 100644
index 0000000..604bea9
--- /dev/null
+++ b/res/drawable/enemy_brobot_walk01.png
Binary files differ
diff --git a/res/drawable/enemy_brobot_walk02.png b/res/drawable/enemy_brobot_walk02.png
new file mode 100644
index 0000000..0d708ab
--- /dev/null
+++ b/res/drawable/enemy_brobot_walk02.png
Binary files differ
diff --git a/res/drawable/enemy_brobot_walk03.png b/res/drawable/enemy_brobot_walk03.png
new file mode 100644
index 0000000..587aeff
--- /dev/null
+++ b/res/drawable/enemy_brobot_walk03.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_die01.png b/res/drawable/enemy_kabocha_evil_die01.png
new file mode 100644
index 0000000..df8b5ea
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_die01.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_die02.png b/res/drawable/enemy_kabocha_evil_die02.png
new file mode 100644
index 0000000..3545dd0
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_die02.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_die03.png b/res/drawable/enemy_kabocha_evil_die03.png
new file mode 100644
index 0000000..5334768
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_die03.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_die04.png b/res/drawable/enemy_kabocha_evil_die04.png
new file mode 100644
index 0000000..b218544
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_die04.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_hit01.png b/res/drawable/enemy_kabocha_evil_hit01.png
new file mode 100644
index 0000000..32d08b2
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_hit01.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_hit02.png b/res/drawable/enemy_kabocha_evil_hit02.png
new file mode 100644
index 0000000..cd86c00
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_hit02.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_stand.png b/res/drawable/enemy_kabocha_evil_stand.png
new file mode 100644
index 0000000..e797f56
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_stand.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_surprised.png b/res/drawable/enemy_kabocha_evil_surprised.png
new file mode 100644
index 0000000..e821f2d
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_surprised.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_walk01.png b/res/drawable/enemy_kabocha_evil_walk01.png
new file mode 100644
index 0000000..4dad7dd
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_walk01.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_walk02.png b/res/drawable/enemy_kabocha_evil_walk02.png
new file mode 100644
index 0000000..e615d38
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_walk02.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_walk03.png b/res/drawable/enemy_kabocha_evil_walk03.png
new file mode 100644
index 0000000..461bf1f
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_walk03.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_walk04.png b/res/drawable/enemy_kabocha_evil_walk04.png
new file mode 100644
index 0000000..2086253
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_walk04.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_walk05.png b/res/drawable/enemy_kabocha_evil_walk05.png
new file mode 100644
index 0000000..1594e6e
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_walk05.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_evil_walk06.png b/res/drawable/enemy_kabocha_evil_walk06.png
new file mode 100644
index 0000000..009d35e
--- /dev/null
+++ b/res/drawable/enemy_kabocha_evil_walk06.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_stand.png b/res/drawable/enemy_kabocha_stand.png
new file mode 100644
index 0000000..4add257
--- /dev/null
+++ b/res/drawable/enemy_kabocha_stand.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_walk01.png b/res/drawable/enemy_kabocha_walk01.png
new file mode 100644
index 0000000..d910f3a
--- /dev/null
+++ b/res/drawable/enemy_kabocha_walk01.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_walk02.png b/res/drawable/enemy_kabocha_walk02.png
new file mode 100644
index 0000000..76a0b00
--- /dev/null
+++ b/res/drawable/enemy_kabocha_walk02.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_walk03.png b/res/drawable/enemy_kabocha_walk03.png
new file mode 100644
index 0000000..b84f38c
--- /dev/null
+++ b/res/drawable/enemy_kabocha_walk03.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_walk04.png b/res/drawable/enemy_kabocha_walk04.png
new file mode 100644
index 0000000..dc3ad45
--- /dev/null
+++ b/res/drawable/enemy_kabocha_walk04.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_walk05.png b/res/drawable/enemy_kabocha_walk05.png
new file mode 100644
index 0000000..6cde53c
--- /dev/null
+++ b/res/drawable/enemy_kabocha_walk05.png
Binary files differ
diff --git a/res/drawable/enemy_kabocha_walk06.png b/res/drawable/enemy_kabocha_walk06.png
new file mode 100644
index 0000000..b84f38c
--- /dev/null
+++ b/res/drawable/enemy_kabocha_walk06.png
Binary files differ
diff --git a/res/drawable/enemy_karaguin01.png b/res/drawable/enemy_karaguin01.png
new file mode 100644
index 0000000..38640d0
--- /dev/null
+++ b/res/drawable/enemy_karaguin01.png
Binary files differ
diff --git a/res/drawable/enemy_karaguin02.png b/res/drawable/enemy_karaguin02.png
new file mode 100644
index 0000000..9841384
--- /dev/null
+++ b/res/drawable/enemy_karaguin02.png
Binary files differ
diff --git a/res/drawable/enemy_karaguin03.png b/res/drawable/enemy_karaguin03.png
new file mode 100644
index 0000000..bd7ff72
--- /dev/null
+++ b/res/drawable/enemy_karaguin03.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_crouch01.png b/res/drawable/enemy_kyle_crouch01.png
new file mode 100644
index 0000000..7c044d7
--- /dev/null
+++ b/res/drawable/enemy_kyle_crouch01.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_crouch02.png b/res/drawable/enemy_kyle_crouch02.png
new file mode 100644
index 0000000..f8a747e
--- /dev/null
+++ b/res/drawable/enemy_kyle_crouch02.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_dash01.png b/res/drawable/enemy_kyle_dash01.png
new file mode 100644
index 0000000..75e0cfd
--- /dev/null
+++ b/res/drawable/enemy_kyle_dash01.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_dash02.png b/res/drawable/enemy_kyle_dash02.png
new file mode 100644
index 0000000..6ec044d
--- /dev/null
+++ b/res/drawable/enemy_kyle_dash02.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_dead.png b/res/drawable/enemy_kyle_dead.png
new file mode 100644
index 0000000..3e49c70
--- /dev/null
+++ b/res/drawable/enemy_kyle_dead.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_jump01.png b/res/drawable/enemy_kyle_jump01.png
new file mode 100644
index 0000000..248c1ef
--- /dev/null
+++ b/res/drawable/enemy_kyle_jump01.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_jump02.png b/res/drawable/enemy_kyle_jump02.png
new file mode 100644
index 0000000..b48420c
--- /dev/null
+++ b/res/drawable/enemy_kyle_jump02.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_stand.png b/res/drawable/enemy_kyle_stand.png
new file mode 100644
index 0000000..5187356
--- /dev/null
+++ b/res/drawable/enemy_kyle_stand.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_walk01.png b/res/drawable/enemy_kyle_walk01.png
new file mode 100644
index 0000000..8777401
--- /dev/null
+++ b/res/drawable/enemy_kyle_walk01.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_walk02.png b/res/drawable/enemy_kyle_walk02.png
new file mode 100644
index 0000000..0385669
--- /dev/null
+++ b/res/drawable/enemy_kyle_walk02.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_walk03.png b/res/drawable/enemy_kyle_walk03.png
new file mode 100644
index 0000000..8910759
--- /dev/null
+++ b/res/drawable/enemy_kyle_walk03.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_walk04.png b/res/drawable/enemy_kyle_walk04.png
new file mode 100644
index 0000000..401f140
--- /dev/null
+++ b/res/drawable/enemy_kyle_walk04.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_walk05.png b/res/drawable/enemy_kyle_walk05.png
new file mode 100644
index 0000000..be29dae
--- /dev/null
+++ b/res/drawable/enemy_kyle_walk05.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_walk06.png b/res/drawable/enemy_kyle_walk06.png
new file mode 100644
index 0000000..b429acd
--- /dev/null
+++ b/res/drawable/enemy_kyle_walk06.png
Binary files differ
diff --git a/res/drawable/enemy_kyle_walk07.png b/res/drawable/enemy_kyle_walk07.png
new file mode 100644
index 0000000..ba606ff
--- /dev/null
+++ b/res/drawable/enemy_kyle_walk07.png
Binary files differ
diff --git a/res/drawable/enemy_mud_attack01.png b/res/drawable/enemy_mud_attack01.png
new file mode 100644
index 0000000..475123e
--- /dev/null
+++ b/res/drawable/enemy_mud_attack01.png
Binary files differ
diff --git a/res/drawable/enemy_mud_attack02.png b/res/drawable/enemy_mud_attack02.png
new file mode 100644
index 0000000..58ef52b
--- /dev/null
+++ b/res/drawable/enemy_mud_attack02.png
Binary files differ
diff --git a/res/drawable/enemy_mud_attack03.png b/res/drawable/enemy_mud_attack03.png
new file mode 100644
index 0000000..d87ff21
--- /dev/null
+++ b/res/drawable/enemy_mud_attack03.png
Binary files differ
diff --git a/res/drawable/enemy_mud_attack04.png b/res/drawable/enemy_mud_attack04.png
new file mode 100644
index 0000000..b0694e8
--- /dev/null
+++ b/res/drawable/enemy_mud_attack04.png
Binary files differ
diff --git a/res/drawable/enemy_mud_attack05.png b/res/drawable/enemy_mud_attack05.png
new file mode 100644
index 0000000..2124a29
--- /dev/null
+++ b/res/drawable/enemy_mud_attack05.png
Binary files differ
diff --git a/res/drawable/enemy_mud_attack06.png b/res/drawable/enemy_mud_attack06.png
new file mode 100644
index 0000000..d72a559
--- /dev/null
+++ b/res/drawable/enemy_mud_attack06.png
Binary files differ
diff --git a/res/drawable/enemy_mud_attack07.png b/res/drawable/enemy_mud_attack07.png
new file mode 100644
index 0000000..437fd03
--- /dev/null
+++ b/res/drawable/enemy_mud_attack07.png
Binary files differ
diff --git a/res/drawable/enemy_mud_idle01.png b/res/drawable/enemy_mud_idle01.png
new file mode 100644
index 0000000..3dc45a8
--- /dev/null
+++ b/res/drawable/enemy_mud_idle01.png
Binary files differ
diff --git a/res/drawable/enemy_mud_idle02.png b/res/drawable/enemy_mud_idle02.png
new file mode 100644
index 0000000..a43e98e
--- /dev/null
+++ b/res/drawable/enemy_mud_idle02.png
Binary files differ
diff --git a/res/drawable/enemy_mud_stand.png b/res/drawable/enemy_mud_stand.png
new file mode 100644
index 0000000..21a4b1a
--- /dev/null
+++ b/res/drawable/enemy_mud_stand.png
Binary files differ
diff --git a/res/drawable/enemy_mud_walk01.png b/res/drawable/enemy_mud_walk01.png
new file mode 100644
index 0000000..475123e
--- /dev/null
+++ b/res/drawable/enemy_mud_walk01.png
Binary files differ
diff --git a/res/drawable/enemy_mud_walk02.png b/res/drawable/enemy_mud_walk02.png
new file mode 100644
index 0000000..adf6df3
--- /dev/null
+++ b/res/drawable/enemy_mud_walk02.png
Binary files differ
diff --git a/res/drawable/enemy_mud_walk03.png b/res/drawable/enemy_mud_walk03.png
new file mode 100644
index 0000000..7253746
--- /dev/null
+++ b/res/drawable/enemy_mud_walk03.png
Binary files differ
diff --git a/res/drawable/enemy_mud_walk04.png b/res/drawable/enemy_mud_walk04.png
new file mode 100644
index 0000000..fefd286
--- /dev/null
+++ b/res/drawable/enemy_mud_walk04.png
Binary files differ
diff --git a/res/drawable/enemy_mud_walk05.png b/res/drawable/enemy_mud_walk05.png
new file mode 100644
index 0000000..90b2e89
--- /dev/null
+++ b/res/drawable/enemy_mud_walk05.png
Binary files differ
diff --git a/res/drawable/enemy_mud_walk06.png b/res/drawable/enemy_mud_walk06.png
new file mode 100644
index 0000000..05f1d8a
--- /dev/null
+++ b/res/drawable/enemy_mud_walk06.png
Binary files differ
diff --git a/res/drawable/enemy_onion01.png b/res/drawable/enemy_onion01.png
new file mode 100644
index 0000000..38d86aa
--- /dev/null
+++ b/res/drawable/enemy_onion01.png
Binary files differ
diff --git a/res/drawable/enemy_onion02.png b/res/drawable/enemy_onion02.png
new file mode 100644
index 0000000..c6907f6
--- /dev/null
+++ b/res/drawable/enemy_onion02.png
Binary files differ
diff --git a/res/drawable/enemy_onion03.png b/res/drawable/enemy_onion03.png
new file mode 100644
index 0000000..e7849f9
--- /dev/null
+++ b/res/drawable/enemy_onion03.png
Binary files differ
diff --git a/res/drawable/enemy_pinkdude_eyeopen.png b/res/drawable/enemy_pinkdude_eyeopen.png
new file mode 100644
index 0000000..93af644
--- /dev/null
+++ b/res/drawable/enemy_pinkdude_eyeopen.png
Binary files differ
diff --git a/res/drawable/enemy_pinkdude_jump.png b/res/drawable/enemy_pinkdude_jump.png
new file mode 100644
index 0000000..a107c20
--- /dev/null
+++ b/res/drawable/enemy_pinkdude_jump.png
Binary files differ
diff --git a/res/drawable/enemy_pinkdude_sleep01.png b/res/drawable/enemy_pinkdude_sleep01.png
new file mode 100644
index 0000000..10c43cb
--- /dev/null
+++ b/res/drawable/enemy_pinkdude_sleep01.png
Binary files differ
diff --git a/res/drawable/enemy_pinkdude_sleep02.png b/res/drawable/enemy_pinkdude_sleep02.png
new file mode 100644
index 0000000..0f53459
--- /dev/null
+++ b/res/drawable/enemy_pinkdude_sleep02.png
Binary files differ
diff --git a/res/drawable/enemy_pinkdude_stand.png b/res/drawable/enemy_pinkdude_stand.png
new file mode 100644
index 0000000..78d8563
--- /dev/null
+++ b/res/drawable/enemy_pinkdude_stand.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_die01.png b/res/drawable/enemy_rokudou_fight_die01.png
new file mode 100644
index 0000000..85c28b6
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_die01.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_die02.png b/res/drawable/enemy_rokudou_fight_die02.png
new file mode 100644
index 0000000..642060c
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_die02.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_die03.png b/res/drawable/enemy_rokudou_fight_die03.png
new file mode 100644
index 0000000..29c37b7
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_die03.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_die04.png b/res/drawable/enemy_rokudou_fight_die04.png
new file mode 100644
index 0000000..043848c
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_die04.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_fly01.png b/res/drawable/enemy_rokudou_fight_fly01.png
new file mode 100644
index 0000000..1922e84
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_fly01.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_fly02.png b/res/drawable/enemy_rokudou_fight_fly02.png
new file mode 100644
index 0000000..2dd4630
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_fly02.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_hit01.png b/res/drawable/enemy_rokudou_fight_hit01.png
new file mode 100644
index 0000000..6a1fdc2
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_hit01.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_hit02.png b/res/drawable/enemy_rokudou_fight_hit02.png
new file mode 100644
index 0000000..6845d7b
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_hit02.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_hit03.png b/res/drawable/enemy_rokudou_fight_hit03.png
new file mode 100644
index 0000000..a073401
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_hit03.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_shoot01.png b/res/drawable/enemy_rokudou_fight_shoot01.png
new file mode 100644
index 0000000..524f86d
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_shoot01.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_shoot02.png b/res/drawable/enemy_rokudou_fight_shoot02.png
new file mode 100644
index 0000000..50766c2
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_shoot02.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_stand.png b/res/drawable/enemy_rokudou_fight_stand.png
new file mode 100644
index 0000000..a4243f0
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_stand.png
Binary files differ
diff --git a/res/drawable/enemy_rokudou_fight_surprise.png b/res/drawable/enemy_rokudou_fight_surprise.png
new file mode 100644
index 0000000..9c95856
--- /dev/null
+++ b/res/drawable/enemy_rokudou_fight_surprise.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_activate01.png b/res/drawable/enemy_shadowslime_activate01.png
new file mode 100644
index 0000000..79b7cc2
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_activate01.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_activate02.png b/res/drawable/enemy_shadowslime_activate02.png
new file mode 100644
index 0000000..ad6b9a4
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_activate02.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_activate03.png b/res/drawable/enemy_shadowslime_activate03.png
new file mode 100644
index 0000000..1981a1a
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_activate03.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_activate04.png b/res/drawable/enemy_shadowslime_activate04.png
new file mode 100644
index 0000000..0ff1f6c
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_activate04.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_activate05.png b/res/drawable/enemy_shadowslime_activate05.png
new file mode 100644
index 0000000..e9510ea
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_activate05.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_activate06.png b/res/drawable/enemy_shadowslime_activate06.png
new file mode 100644
index 0000000..cc6cb71
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_activate06.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_attack01.png b/res/drawable/enemy_shadowslime_attack01.png
new file mode 100644
index 0000000..51d8bbb
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_attack01.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_attack02.png b/res/drawable/enemy_shadowslime_attack02.png
new file mode 100644
index 0000000..727e7b6
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_attack02.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_attack03.png b/res/drawable/enemy_shadowslime_attack03.png
new file mode 100644
index 0000000..96aef5e
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_attack03.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_attack04.png b/res/drawable/enemy_shadowslime_attack04.png
new file mode 100644
index 0000000..c358045
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_attack04.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_flash.png b/res/drawable/enemy_shadowslime_flash.png
new file mode 100644
index 0000000..c31a49c
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_flash.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_idle01.png b/res/drawable/enemy_shadowslime_idle01.png
new file mode 100644
index 0000000..8ba1859
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_idle01.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_idle02.png b/res/drawable/enemy_shadowslime_idle02.png
new file mode 100644
index 0000000..2b52282
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_idle02.png
Binary files differ
diff --git a/res/drawable/enemy_shadowslime_stand.png b/res/drawable/enemy_shadowslime_stand.png
new file mode 100644
index 0000000..09d5dd7
--- /dev/null
+++ b/res/drawable/enemy_shadowslime_stand.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_attack01.png b/res/drawable/enemy_skeleton_attack01.png
new file mode 100644
index 0000000..eff7a87
--- /dev/null
+++ b/res/drawable/enemy_skeleton_attack01.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_attack02.png b/res/drawable/enemy_skeleton_attack02.png
new file mode 100644
index 0000000..8171c1d
--- /dev/null
+++ b/res/drawable/enemy_skeleton_attack02.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_attack03.png b/res/drawable/enemy_skeleton_attack03.png
new file mode 100644
index 0000000..61c6ac8
--- /dev/null
+++ b/res/drawable/enemy_skeleton_attack03.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_attack04.png b/res/drawable/enemy_skeleton_attack04.png
new file mode 100644
index 0000000..bc550a3
--- /dev/null
+++ b/res/drawable/enemy_skeleton_attack04.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_stand.png b/res/drawable/enemy_skeleton_stand.png
new file mode 100644
index 0000000..2841e5c
--- /dev/null
+++ b/res/drawable/enemy_skeleton_stand.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_walk01.png b/res/drawable/enemy_skeleton_walk01.png
new file mode 100644
index 0000000..be65f1d
--- /dev/null
+++ b/res/drawable/enemy_skeleton_walk01.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_walk02.png b/res/drawable/enemy_skeleton_walk02.png
new file mode 100644
index 0000000..c6faf88
--- /dev/null
+++ b/res/drawable/enemy_skeleton_walk02.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_walk03.png b/res/drawable/enemy_skeleton_walk03.png
new file mode 100644
index 0000000..19ab440
--- /dev/null
+++ b/res/drawable/enemy_skeleton_walk03.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_walk04.png b/res/drawable/enemy_skeleton_walk04.png
new file mode 100644
index 0000000..fd42c49
--- /dev/null
+++ b/res/drawable/enemy_skeleton_walk04.png
Binary files differ
diff --git a/res/drawable/enemy_skeleton_walk05.png b/res/drawable/enemy_skeleton_walk05.png
new file mode 100644
index 0000000..dde0a9b
--- /dev/null
+++ b/res/drawable/enemy_skeleton_walk05.png
Binary files differ
diff --git a/res/drawable/enemy_source_black.png b/res/drawable/enemy_source_black.png
new file mode 100644
index 0000000..c03ccbb
--- /dev/null
+++ b/res/drawable/enemy_source_black.png
Binary files differ
diff --git a/res/drawable/enemy_source_body.png b/res/drawable/enemy_source_body.png
new file mode 100644
index 0000000..c15c69e
--- /dev/null
+++ b/res/drawable/enemy_source_body.png
Binary files differ
diff --git a/res/drawable/enemy_source_core.png b/res/drawable/enemy_source_core.png
new file mode 100644
index 0000000..7e06771
--- /dev/null
+++ b/res/drawable/enemy_source_core.png
Binary files differ
diff --git a/res/drawable/enemy_source_spikes.png b/res/drawable/enemy_source_spikes.png
new file mode 100644
index 0000000..440a944
--- /dev/null
+++ b/res/drawable/enemy_source_spikes.png
Binary files differ
diff --git a/res/drawable/enemy_source_spots.png b/res/drawable/enemy_source_spots.png
new file mode 100644
index 0000000..356eceb
--- /dev/null
+++ b/res/drawable/enemy_source_spots.png
Binary files differ
diff --git a/res/drawable/enemy_sting01.png b/res/drawable/enemy_sting01.png
new file mode 100644
index 0000000..5b2c3d1
--- /dev/null
+++ b/res/drawable/enemy_sting01.png
Binary files differ
diff --git a/res/drawable/enemy_sting02.png b/res/drawable/enemy_sting02.png
new file mode 100644
index 0000000..a7d0a45
--- /dev/null
+++ b/res/drawable/enemy_sting02.png
Binary files differ
diff --git a/res/drawable/enemy_sting03.png b/res/drawable/enemy_sting03.png
new file mode 100644
index 0000000..34a44fe
--- /dev/null
+++ b/res/drawable/enemy_sting03.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_crouch.png b/res/drawable/enemy_wanda_crouch.png
new file mode 100644
index 0000000..48b6753
--- /dev/null
+++ b/res/drawable/enemy_wanda_crouch.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_jump01.png b/res/drawable/enemy_wanda_jump01.png
new file mode 100644
index 0000000..07aa6cb
--- /dev/null
+++ b/res/drawable/enemy_wanda_jump01.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_jump02.png b/res/drawable/enemy_wanda_jump02.png
new file mode 100644
index 0000000..876178d
--- /dev/null
+++ b/res/drawable/enemy_wanda_jump02.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_run01.png b/res/drawable/enemy_wanda_run01.png
new file mode 100644
index 0000000..0b98e69
--- /dev/null
+++ b/res/drawable/enemy_wanda_run01.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_run02.png b/res/drawable/enemy_wanda_run02.png
new file mode 100644
index 0000000..b2dd6b8
--- /dev/null
+++ b/res/drawable/enemy_wanda_run02.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_run03.png b/res/drawable/enemy_wanda_run03.png
new file mode 100644
index 0000000..18e978c
--- /dev/null
+++ b/res/drawable/enemy_wanda_run03.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_run04.png b/res/drawable/enemy_wanda_run04.png
new file mode 100644
index 0000000..3eaf9cb
--- /dev/null
+++ b/res/drawable/enemy_wanda_run04.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_run05.png b/res/drawable/enemy_wanda_run05.png
new file mode 100644
index 0000000..1708487
--- /dev/null
+++ b/res/drawable/enemy_wanda_run05.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_run06.png b/res/drawable/enemy_wanda_run06.png
new file mode 100644
index 0000000..6cff048
--- /dev/null
+++ b/res/drawable/enemy_wanda_run06.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_run07.png b/res/drawable/enemy_wanda_run07.png
new file mode 100644
index 0000000..8579a1b
--- /dev/null
+++ b/res/drawable/enemy_wanda_run07.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_run08.png b/res/drawable/enemy_wanda_run08.png
new file mode 100644
index 0000000..0c78fb7
--- /dev/null
+++ b/res/drawable/enemy_wanda_run08.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot01.png b/res/drawable/enemy_wanda_shoot01.png
new file mode 100644
index 0000000..5685bf0
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot01.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot02.png b/res/drawable/enemy_wanda_shoot02.png
new file mode 100644
index 0000000..32cb529
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot02.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot03.png b/res/drawable/enemy_wanda_shoot03.png
new file mode 100644
index 0000000..13a05ed
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot03.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot04.png b/res/drawable/enemy_wanda_shoot04.png
new file mode 100644
index 0000000..9262ccc
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot04.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot05.png b/res/drawable/enemy_wanda_shoot05.png
new file mode 100644
index 0000000..e047bbe
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot05.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot06.png b/res/drawable/enemy_wanda_shoot06.png
new file mode 100644
index 0000000..8fdc822
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot06.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot07.png b/res/drawable/enemy_wanda_shoot07.png
new file mode 100644
index 0000000..1a81a66
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot07.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot08.png b/res/drawable/enemy_wanda_shoot08.png
new file mode 100644
index 0000000..8dbefd2
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot08.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_shoot09.png b/res/drawable/enemy_wanda_shoot09.png
new file mode 100644
index 0000000..7017a3a
--- /dev/null
+++ b/res/drawable/enemy_wanda_shoot09.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_stand.png b/res/drawable/enemy_wanda_stand.png
new file mode 100644
index 0000000..9f35303
--- /dev/null
+++ b/res/drawable/enemy_wanda_stand.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_walk01.png b/res/drawable/enemy_wanda_walk01.png
new file mode 100644
index 0000000..3cc54d4
--- /dev/null
+++ b/res/drawable/enemy_wanda_walk01.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_walk02.png b/res/drawable/enemy_wanda_walk02.png
new file mode 100644
index 0000000..7984035
--- /dev/null
+++ b/res/drawable/enemy_wanda_walk02.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_walk03.png b/res/drawable/enemy_wanda_walk03.png
new file mode 100644
index 0000000..f3263ff
--- /dev/null
+++ b/res/drawable/enemy_wanda_walk03.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_walk04.png b/res/drawable/enemy_wanda_walk04.png
new file mode 100644
index 0000000..ffaa874
--- /dev/null
+++ b/res/drawable/enemy_wanda_walk04.png
Binary files differ
diff --git a/res/drawable/enemy_wanda_walk05.png b/res/drawable/enemy_wanda_walk05.png
new file mode 100644
index 0000000..1eb51c8
--- /dev/null
+++ b/res/drawable/enemy_wanda_walk05.png
Binary files differ
diff --git a/res/drawable/energy_ball01.png b/res/drawable/energy_ball01.png
new file mode 100644
index 0000000..9fd0dcd
--- /dev/null
+++ b/res/drawable/energy_ball01.png
Binary files differ
diff --git a/res/drawable/energy_ball02.png b/res/drawable/energy_ball02.png
new file mode 100644
index 0000000..2bb6880
--- /dev/null
+++ b/res/drawable/energy_ball02.png
Binary files differ
diff --git a/res/drawable/energy_ball03.png b/res/drawable/energy_ball03.png
new file mode 100644
index 0000000..12224ae
--- /dev/null
+++ b/res/drawable/energy_ball03.png
Binary files differ
diff --git a/res/drawable/energy_ball04.png b/res/drawable/energy_ball04.png
new file mode 100644
index 0000000..1360c29
--- /dev/null
+++ b/res/drawable/energy_ball04.png
Binary files differ
diff --git a/res/drawable/ghost.png b/res/drawable/ghost.png
new file mode 100644
index 0000000..ea2eba8
--- /dev/null
+++ b/res/drawable/ghost.png
Binary files differ
diff --git a/res/drawable/grass.png b/res/drawable/grass.png
new file mode 100644
index 0000000..975ac64
--- /dev/null
+++ b/res/drawable/grass.png
Binary files differ
diff --git a/res/drawable/hud_box.png b/res/drawable/hud_box.png
new file mode 100644
index 0000000..bae321e
--- /dev/null
+++ b/res/drawable/hud_box.png
Binary files differ
diff --git a/res/drawable/icon.png b/res/drawable/icon.png
new file mode 100644
index 0000000..08fe090
--- /dev/null
+++ b/res/drawable/icon.png
Binary files differ
diff --git a/res/drawable/island.png b/res/drawable/island.png
new file mode 100644
index 0000000..9539e46
--- /dev/null
+++ b/res/drawable/island.png
Binary files differ
diff --git a/res/drawable/jetfire01.png b/res/drawable/jetfire01.png
new file mode 100755
index 0000000..d892d6a
--- /dev/null
+++ b/res/drawable/jetfire01.png
Binary files differ
diff --git a/res/drawable/jetfire02.png b/res/drawable/jetfire02.png
new file mode 100755
index 0000000..899db58
--- /dev/null
+++ b/res/drawable/jetfire02.png
Binary files differ
diff --git a/res/drawable/kabocha_closeup_concern.png b/res/drawable/kabocha_closeup_concern.png
new file mode 100644
index 0000000..114283c
--- /dev/null
+++ b/res/drawable/kabocha_closeup_concern.png
Binary files differ
diff --git a/res/drawable/kabocha_closeup_lunatic.png b/res/drawable/kabocha_closeup_lunatic.png
new file mode 100644
index 0000000..55ad2d7
--- /dev/null
+++ b/res/drawable/kabocha_closeup_lunatic.png
Binary files differ
diff --git a/res/drawable/kabocha_closeup_lunatic_02.png b/res/drawable/kabocha_closeup_lunatic_02.png
new file mode 100644
index 0000000..133b010
--- /dev/null
+++ b/res/drawable/kabocha_closeup_lunatic_02.png
Binary files differ
diff --git a/res/drawable/kabocha_closeup_normal.png b/res/drawable/kabocha_closeup_normal.png
new file mode 100644
index 0000000..c4b53c4
--- /dev/null
+++ b/res/drawable/kabocha_closeup_normal.png
Binary files differ
diff --git a/res/drawable/kyle_closeup_angry.png b/res/drawable/kyle_closeup_angry.png
new file mode 100644
index 0000000..ce7af38
--- /dev/null
+++ b/res/drawable/kyle_closeup_angry.png
Binary files differ
diff --git a/res/drawable/kyle_closeup_neutral.png b/res/drawable/kyle_closeup_neutral.png
new file mode 100644
index 0000000..fc8eb49
--- /dev/null
+++ b/res/drawable/kyle_closeup_neutral.png
Binary files differ
diff --git a/res/drawable/kyle_closeup_noglasses.png b/res/drawable/kyle_closeup_noglasses.png
new file mode 100644
index 0000000..7d6ae3e
--- /dev/null
+++ b/res/drawable/kyle_closeup_noglasses.png
Binary files differ
diff --git a/res/drawable/lab.png b/res/drawable/lab.png
new file mode 100644
index 0000000..83f1544
--- /dev/null
+++ b/res/drawable/lab.png
Binary files differ
diff --git a/res/drawable/lighting.png b/res/drawable/lighting.png
new file mode 100644
index 0000000..f9e8240
--- /dev/null
+++ b/res/drawable/lighting.png
Binary files differ
diff --git a/res/drawable/object_brobot_machine.png b/res/drawable/object_brobot_machine.png
new file mode 100644
index 0000000..6ec7bd7
--- /dev/null
+++ b/res/drawable/object_brobot_machine.png
Binary files differ
diff --git a/res/drawable/object_button_blue.png b/res/drawable/object_button_blue.png
new file mode 100644
index 0000000..9951411
--- /dev/null
+++ b/res/drawable/object_button_blue.png
Binary files differ
diff --git a/res/drawable/object_button_green.png b/res/drawable/object_button_green.png
new file mode 100644
index 0000000..37ee536
--- /dev/null
+++ b/res/drawable/object_button_green.png
Binary files differ
diff --git a/res/drawable/object_button_pressed_blue.png b/res/drawable/object_button_pressed_blue.png
new file mode 100644
index 0000000..2dcf45b
--- /dev/null
+++ b/res/drawable/object_button_pressed_blue.png
Binary files differ
diff --git a/res/drawable/object_button_pressed_green.png b/res/drawable/object_button_pressed_green.png
new file mode 100644
index 0000000..34244ef
--- /dev/null
+++ b/res/drawable/object_button_pressed_green.png
Binary files differ
diff --git a/res/drawable/object_button_pressed_red.png b/res/drawable/object_button_pressed_red.png
new file mode 100644
index 0000000..e510896
--- /dev/null
+++ b/res/drawable/object_button_pressed_red.png
Binary files differ
diff --git a/res/drawable/object_button_red.png b/res/drawable/object_button_red.png
new file mode 100644
index 0000000..218f6e9
--- /dev/null
+++ b/res/drawable/object_button_red.png
Binary files differ
diff --git a/res/drawable/object_cannon.png b/res/drawable/object_cannon.png
new file mode 100644
index 0000000..5213f79
--- /dev/null
+++ b/res/drawable/object_cannon.png
Binary files differ
diff --git a/res/drawable/object_coin01.png b/res/drawable/object_coin01.png
new file mode 100644
index 0000000..8c55c34
--- /dev/null
+++ b/res/drawable/object_coin01.png
Binary files differ
diff --git a/res/drawable/object_coin02.png b/res/drawable/object_coin02.png
new file mode 100644
index 0000000..971ab6d
--- /dev/null
+++ b/res/drawable/object_coin02.png
Binary files differ
diff --git a/res/drawable/object_coin03.png b/res/drawable/object_coin03.png
new file mode 100644
index 0000000..3710677
--- /dev/null
+++ b/res/drawable/object_coin03.png
Binary files differ
diff --git a/res/drawable/object_coin04.png b/res/drawable/object_coin04.png
new file mode 100644
index 0000000..036970c
--- /dev/null
+++ b/res/drawable/object_coin04.png
Binary files differ
diff --git a/res/drawable/object_coin05.png b/res/drawable/object_coin05.png
new file mode 100644
index 0000000..dca49a6
--- /dev/null
+++ b/res/drawable/object_coin05.png
Binary files differ
diff --git a/res/drawable/object_debris_block.png b/res/drawable/object_debris_block.png
new file mode 100644
index 0000000..d2603d2
--- /dev/null
+++ b/res/drawable/object_debris_block.png
Binary files differ
diff --git a/res/drawable/object_debris_piece.png b/res/drawable/object_debris_piece.png
new file mode 100644
index 0000000..a53e114
--- /dev/null
+++ b/res/drawable/object_debris_piece.png
Binary files differ
diff --git a/res/drawable/object_diary01.png b/res/drawable/object_diary01.png
new file mode 100644
index 0000000..ded5658
--- /dev/null
+++ b/res/drawable/object_diary01.png
Binary files differ
diff --git a/res/drawable/object_diary02.png b/res/drawable/object_diary02.png
new file mode 100644
index 0000000..f652c51
--- /dev/null
+++ b/res/drawable/object_diary02.png
Binary files differ
diff --git a/res/drawable/object_diary03.png b/res/drawable/object_diary03.png
new file mode 100644
index 0000000..9cce087
--- /dev/null
+++ b/res/drawable/object_diary03.png
Binary files differ
diff --git a/res/drawable/object_diary04.png b/res/drawable/object_diary04.png
new file mode 100644
index 0000000..e87585e
--- /dev/null
+++ b/res/drawable/object_diary04.png
Binary files differ
diff --git a/res/drawable/object_diary05.png b/res/drawable/object_diary05.png
new file mode 100644
index 0000000..4fb412b
--- /dev/null
+++ b/res/drawable/object_diary05.png
Binary files differ
diff --git a/res/drawable/object_diary06.png b/res/drawable/object_diary06.png
new file mode 100644
index 0000000..65cb33a
--- /dev/null
+++ b/res/drawable/object_diary06.png
Binary files differ
diff --git a/res/drawable/object_door_blue01.png b/res/drawable/object_door_blue01.png
new file mode 100644
index 0000000..4299115
--- /dev/null
+++ b/res/drawable/object_door_blue01.png
Binary files differ
diff --git a/res/drawable/object_door_blue02.png b/res/drawable/object_door_blue02.png
new file mode 100644
index 0000000..32488af
--- /dev/null
+++ b/res/drawable/object_door_blue02.png
Binary files differ
diff --git a/res/drawable/object_door_blue03.png b/res/drawable/object_door_blue03.png
new file mode 100644
index 0000000..7083079
--- /dev/null
+++ b/res/drawable/object_door_blue03.png
Binary files differ
diff --git a/res/drawable/object_door_blue04.png b/res/drawable/object_door_blue04.png
new file mode 100644
index 0000000..9ec7d6b
--- /dev/null
+++ b/res/drawable/object_door_blue04.png
Binary files differ
diff --git a/res/drawable/object_door_green01.png b/res/drawable/object_door_green01.png
new file mode 100644
index 0000000..918375f
--- /dev/null
+++ b/res/drawable/object_door_green01.png
Binary files differ
diff --git a/res/drawable/object_door_green02.png b/res/drawable/object_door_green02.png
new file mode 100644
index 0000000..8bdbce1
--- /dev/null
+++ b/res/drawable/object_door_green02.png
Binary files differ
diff --git a/res/drawable/object_door_green03.png b/res/drawable/object_door_green03.png
new file mode 100644
index 0000000..129039a
--- /dev/null
+++ b/res/drawable/object_door_green03.png
Binary files differ
diff --git a/res/drawable/object_door_green04.png b/res/drawable/object_door_green04.png
new file mode 100644
index 0000000..c6bf168
--- /dev/null
+++ b/res/drawable/object_door_green04.png
Binary files differ
diff --git a/res/drawable/object_door_red01.png b/res/drawable/object_door_red01.png
new file mode 100644
index 0000000..2a12e0d
--- /dev/null
+++ b/res/drawable/object_door_red01.png
Binary files differ
diff --git a/res/drawable/object_door_red02.png b/res/drawable/object_door_red02.png
new file mode 100644
index 0000000..afde91c
--- /dev/null
+++ b/res/drawable/object_door_red02.png
Binary files differ
diff --git a/res/drawable/object_door_red03.png b/res/drawable/object_door_red03.png
new file mode 100644
index 0000000..c539cc0
--- /dev/null
+++ b/res/drawable/object_door_red03.png
Binary files differ
diff --git a/res/drawable/object_door_red04.png b/res/drawable/object_door_red04.png
new file mode 100644
index 0000000..db97f8a
--- /dev/null
+++ b/res/drawable/object_door_red04.png
Binary files differ
diff --git a/res/drawable/object_gunturret01.png b/res/drawable/object_gunturret01.png
new file mode 100644
index 0000000..bd8614b
--- /dev/null
+++ b/res/drawable/object_gunturret01.png
Binary files differ
diff --git a/res/drawable/object_gunturret02.png b/res/drawable/object_gunturret02.png
new file mode 100644
index 0000000..c3dcb5a
--- /dev/null
+++ b/res/drawable/object_gunturret02.png
Binary files differ
diff --git a/res/drawable/object_gunturret03.png b/res/drawable/object_gunturret03.png
new file mode 100644
index 0000000..8a84f43
--- /dev/null
+++ b/res/drawable/object_gunturret03.png
Binary files differ
diff --git a/res/drawable/object_gunturret_idle.png b/res/drawable/object_gunturret_idle.png
new file mode 100644
index 0000000..998e226
--- /dev/null
+++ b/res/drawable/object_gunturret_idle.png
Binary files differ
diff --git a/res/drawable/object_ruby01.png b/res/drawable/object_ruby01.png
new file mode 100644
index 0000000..f4421c9
--- /dev/null
+++ b/res/drawable/object_ruby01.png
Binary files differ
diff --git a/res/drawable/object_ruby02.png b/res/drawable/object_ruby02.png
new file mode 100644
index 0000000..572dcf4
--- /dev/null
+++ b/res/drawable/object_ruby02.png
Binary files differ
diff --git a/res/drawable/object_ruby03.png b/res/drawable/object_ruby03.png
new file mode 100644
index 0000000..e313b51
--- /dev/null
+++ b/res/drawable/object_ruby03.png
Binary files differ
diff --git a/res/drawable/object_ruby04.png b/res/drawable/object_ruby04.png
new file mode 100644
index 0000000..35c6f01
--- /dev/null
+++ b/res/drawable/object_ruby04.png
Binary files differ
diff --git a/res/drawable/object_ruby05.png b/res/drawable/object_ruby05.png
new file mode 100644
index 0000000..62cb514
--- /dev/null
+++ b/res/drawable/object_ruby05.png
Binary files differ
diff --git a/res/drawable/object_shot01.png b/res/drawable/object_shot01.png
new file mode 100644
index 0000000..15754fc
--- /dev/null
+++ b/res/drawable/object_shot01.png
Binary files differ
diff --git a/res/drawable/object_shot02.png b/res/drawable/object_shot02.png
new file mode 100644
index 0000000..f83d545
--- /dev/null
+++ b/res/drawable/object_shot02.png
Binary files differ
diff --git a/res/drawable/object_terminal01.png b/res/drawable/object_terminal01.png
new file mode 100644
index 0000000..f40edc5
--- /dev/null
+++ b/res/drawable/object_terminal01.png
Binary files differ
diff --git a/res/drawable/object_terminal02.png b/res/drawable/object_terminal02.png
new file mode 100644
index 0000000..b12265f
--- /dev/null
+++ b/res/drawable/object_terminal02.png
Binary files differ
diff --git a/res/drawable/object_terminal03.png b/res/drawable/object_terminal03.png
new file mode 100644
index 0000000..cfc2c84
--- /dev/null
+++ b/res/drawable/object_terminal03.png
Binary files differ
diff --git a/res/drawable/object_terminal_kabocha01.png b/res/drawable/object_terminal_kabocha01.png
new file mode 100644
index 0000000..0ae4c36
--- /dev/null
+++ b/res/drawable/object_terminal_kabocha01.png
Binary files differ
diff --git a/res/drawable/object_terminal_kabocha02.png b/res/drawable/object_terminal_kabocha02.png
new file mode 100644
index 0000000..34e167c
--- /dev/null
+++ b/res/drawable/object_terminal_kabocha02.png
Binary files differ
diff --git a/res/drawable/object_terminal_kabocha03.png b/res/drawable/object_terminal_kabocha03.png
new file mode 100644
index 0000000..8fdc557
--- /dev/null
+++ b/res/drawable/object_terminal_kabocha03.png
Binary files differ
diff --git a/res/drawable/robot.png b/res/drawable/robot.png
new file mode 100644
index 0000000..8a9e698
--- /dev/null
+++ b/res/drawable/robot.png
Binary files differ
diff --git a/res/drawable/rokudou_closeup_mask.png b/res/drawable/rokudou_closeup_mask.png
new file mode 100644
index 0000000..fe4f0fd
--- /dev/null
+++ b/res/drawable/rokudou_closeup_mask.png
Binary files differ
diff --git a/res/drawable/rokudou_closeup_normal.png b/res/drawable/rokudou_closeup_normal.png
new file mode 100644
index 0000000..652ce12
--- /dev/null
+++ b/res/drawable/rokudou_closeup_normal.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_die01.png b/res/drawable/rokudou_fight_die01.png
new file mode 100644
index 0000000..85c28b6
--- /dev/null
+++ b/res/drawable/rokudou_fight_die01.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_die02.png b/res/drawable/rokudou_fight_die02.png
new file mode 100644
index 0000000..642060c
--- /dev/null
+++ b/res/drawable/rokudou_fight_die02.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_die03.png b/res/drawable/rokudou_fight_die03.png
new file mode 100644
index 0000000..29c37b7
--- /dev/null
+++ b/res/drawable/rokudou_fight_die03.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_die04.png b/res/drawable/rokudou_fight_die04.png
new file mode 100644
index 0000000..043848c
--- /dev/null
+++ b/res/drawable/rokudou_fight_die04.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_fly01.png b/res/drawable/rokudou_fight_fly01.png
new file mode 100644
index 0000000..1922e84
--- /dev/null
+++ b/res/drawable/rokudou_fight_fly01.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_fly02.png b/res/drawable/rokudou_fight_fly02.png
new file mode 100644
index 0000000..2dd4630
--- /dev/null
+++ b/res/drawable/rokudou_fight_fly02.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_hit01.png b/res/drawable/rokudou_fight_hit01.png
new file mode 100644
index 0000000..6a1fdc2
--- /dev/null
+++ b/res/drawable/rokudou_fight_hit01.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_hit02.png b/res/drawable/rokudou_fight_hit02.png
new file mode 100644
index 0000000..6845d7b
--- /dev/null
+++ b/res/drawable/rokudou_fight_hit02.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_hit03.png b/res/drawable/rokudou_fight_hit03.png
new file mode 100644
index 0000000..a073401
--- /dev/null
+++ b/res/drawable/rokudou_fight_hit03.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_shoot01.png b/res/drawable/rokudou_fight_shoot01.png
new file mode 100644
index 0000000..524f86d
--- /dev/null
+++ b/res/drawable/rokudou_fight_shoot01.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_shoot02.png b/res/drawable/rokudou_fight_shoot02.png
new file mode 100644
index 0000000..50766c2
--- /dev/null
+++ b/res/drawable/rokudou_fight_shoot02.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_stand.png b/res/drawable/rokudou_fight_stand.png
new file mode 100644
index 0000000..a4243f0
--- /dev/null
+++ b/res/drawable/rokudou_fight_stand.png
Binary files differ
diff --git a/res/drawable/rokudou_fight_surprise.png b/res/drawable/rokudou_fight_surprise.png
new file mode 100644
index 0000000..9c95856
--- /dev/null
+++ b/res/drawable/rokudou_fight_surprise.png
Binary files differ
diff --git a/res/drawable/sewage.png b/res/drawable/sewage.png
new file mode 100644
index 0000000..753fc05
--- /dev/null
+++ b/res/drawable/sewage.png
Binary files differ
diff --git a/res/drawable/sky_background.png b/res/drawable/sky_background.png
new file mode 100644
index 0000000..1c67067
--- /dev/null
+++ b/res/drawable/sky_background.png
Binary files differ
diff --git a/res/drawable/snail_bomb.png b/res/drawable/snail_bomb.png
new file mode 100644
index 0000000..39a5e41
--- /dev/null
+++ b/res/drawable/snail_bomb.png
Binary files differ
diff --git a/res/drawable/snailbomb.png b/res/drawable/snailbomb.png
new file mode 100644
index 0000000..6e13931
--- /dev/null
+++ b/res/drawable/snailbomb.png
Binary files differ
diff --git a/res/drawable/snailbomb_shoot01.png b/res/drawable/snailbomb_shoot01.png
new file mode 100644
index 0000000..7333448
--- /dev/null
+++ b/res/drawable/snailbomb_shoot01.png
Binary files differ
diff --git a/res/drawable/snailbomb_shoot02.png b/res/drawable/snailbomb_shoot02.png
new file mode 100644
index 0000000..21fb2ee
--- /dev/null
+++ b/res/drawable/snailbomb_shoot02.png
Binary files differ
diff --git a/res/drawable/snailbomb_stand.png b/res/drawable/snailbomb_stand.png
new file mode 100644
index 0000000..9c5186f
--- /dev/null
+++ b/res/drawable/snailbomb_stand.png
Binary files differ
diff --git a/res/drawable/snailbomb_walk01.png b/res/drawable/snailbomb_walk01.png
new file mode 100644
index 0000000..c73f3ca
--- /dev/null
+++ b/res/drawable/snailbomb_walk01.png
Binary files differ
diff --git a/res/drawable/snailbomb_walk02.png b/res/drawable/snailbomb_walk02.png
new file mode 100644
index 0000000..19f965e
--- /dev/null
+++ b/res/drawable/snailbomb_walk02.png
Binary files differ
diff --git a/res/drawable/spark01.png b/res/drawable/spark01.png
new file mode 100644
index 0000000..7faa229
--- /dev/null
+++ b/res/drawable/spark01.png
Binary files differ
diff --git a/res/drawable/spark02.png b/res/drawable/spark02.png
new file mode 100644
index 0000000..083b8ed
--- /dev/null
+++ b/res/drawable/spark02.png
Binary files differ
diff --git a/res/drawable/spark03.png b/res/drawable/spark03.png
new file mode 100644
index 0000000..25d47f1
--- /dev/null
+++ b/res/drawable/spark03.png
Binary files differ
diff --git a/res/drawable/title.png b/res/drawable/title.png
new file mode 100644
index 0000000..b183b8b
--- /dev/null
+++ b/res/drawable/title.png
Binary files differ
diff --git a/res/drawable/title_background.png b/res/drawable/title_background.png
new file mode 100644
index 0000000..486daa0
--- /dev/null
+++ b/res/drawable/title_background.png
Binary files differ
diff --git a/res/drawable/titletileset.png b/res/drawable/titletileset.png
new file mode 100644
index 0000000..1bea9dc
--- /dev/null
+++ b/res/drawable/titletileset.png
Binary files differ
diff --git a/res/drawable/tutorial.png b/res/drawable/tutorial.png
new file mode 100644
index 0000000..ea6f17d
--- /dev/null
+++ b/res/drawable/tutorial.png
Binary files differ
diff --git a/res/drawable/ui_0.png b/res/drawable/ui_0.png
new file mode 100644
index 0000000..1099d7a
--- /dev/null
+++ b/res/drawable/ui_0.png
Binary files differ
diff --git a/res/drawable/ui_1.png b/res/drawable/ui_1.png
new file mode 100644
index 0000000..76f755a
--- /dev/null
+++ b/res/drawable/ui_1.png
Binary files differ
diff --git a/res/drawable/ui_2.png b/res/drawable/ui_2.png
new file mode 100644
index 0000000..961977b
--- /dev/null
+++ b/res/drawable/ui_2.png
Binary files differ
diff --git a/res/drawable/ui_3.png b/res/drawable/ui_3.png
new file mode 100644
index 0000000..40fc4b4
--- /dev/null
+++ b/res/drawable/ui_3.png
Binary files differ
diff --git a/res/drawable/ui_4.png b/res/drawable/ui_4.png
new file mode 100644
index 0000000..f128843
--- /dev/null
+++ b/res/drawable/ui_4.png
Binary files differ
diff --git a/res/drawable/ui_5.png b/res/drawable/ui_5.png
new file mode 100644
index 0000000..72337c3
--- /dev/null
+++ b/res/drawable/ui_5.png
Binary files differ
diff --git a/res/drawable/ui_6.png b/res/drawable/ui_6.png
new file mode 100644
index 0000000..ec18d40
--- /dev/null
+++ b/res/drawable/ui_6.png
Binary files differ
diff --git a/res/drawable/ui_7.png b/res/drawable/ui_7.png
new file mode 100644
index 0000000..fee388f
--- /dev/null
+++ b/res/drawable/ui_7.png
Binary files differ
diff --git a/res/drawable/ui_8.png b/res/drawable/ui_8.png
new file mode 100644
index 0000000..2c9b97a
--- /dev/null
+++ b/res/drawable/ui_8.png
Binary files differ
diff --git a/res/drawable/ui_9.png b/res/drawable/ui_9.png
new file mode 100644
index 0000000..f4572a0
--- /dev/null
+++ b/res/drawable/ui_9.png
Binary files differ
diff --git a/res/drawable/ui_arrow_dark.png b/res/drawable/ui_arrow_dark.png
new file mode 100644
index 0000000..bd51557
--- /dev/null
+++ b/res/drawable/ui_arrow_dark.png
Binary files differ
diff --git a/res/drawable/ui_arrow_light.png b/res/drawable/ui_arrow_light.png
new file mode 100644
index 0000000..6fad3e4
--- /dev/null
+++ b/res/drawable/ui_arrow_light.png
Binary files differ
diff --git a/res/drawable/ui_bad_ending_rokudou_bg.png b/res/drawable/ui_bad_ending_rokudou_bg.png
new file mode 100644
index 0000000..57451e0
--- /dev/null
+++ b/res/drawable/ui_bad_ending_rokudou_bg.png
Binary files differ
diff --git a/res/drawable/ui_bad_ending_rokudou_cliffs.png b/res/drawable/ui_bad_ending_rokudou_cliffs.png
new file mode 100644
index 0000000..b0aed2d
--- /dev/null
+++ b/res/drawable/ui_bad_ending_rokudou_cliffs.png
Binary files differ
diff --git a/res/drawable/ui_bad_ending_rokudou_rokudou.png b/res/drawable/ui_bad_ending_rokudou_rokudou.png
new file mode 100644
index 0000000..2c5585f
--- /dev/null
+++ b/res/drawable/ui_bad_ending_rokudou_rokudou.png
Binary files differ
diff --git a/res/drawable/ui_bad_ending_rokudou_sphere.png b/res/drawable/ui_bad_ending_rokudou_sphere.png
new file mode 100644
index 0000000..4e15239
--- /dev/null
+++ b/res/drawable/ui_bad_ending_rokudou_sphere.png
Binary files differ
diff --git a/res/drawable/ui_bar.png b/res/drawable/ui_bar.png
new file mode 100644
index 0000000..37f5c33
--- /dev/null
+++ b/res/drawable/ui_bar.png
Binary files differ
diff --git a/res/drawable/ui_bar_bg.png b/res/drawable/ui_bar_bg.png
new file mode 100644
index 0000000..277f5bd
--- /dev/null
+++ b/res/drawable/ui_bar_bg.png
Binary files differ
diff --git a/res/drawable/ui_button_continue.png b/res/drawable/ui_button_continue.png
new file mode 100644
index 0000000..caffc6c
--- /dev/null
+++ b/res/drawable/ui_button_continue.png
Binary files differ
diff --git a/res/drawable/ui_button_fly_disabled.png b/res/drawable/ui_button_fly_disabled.png
new file mode 100644
index 0000000..f7e7499
--- /dev/null
+++ b/res/drawable/ui_button_fly_disabled.png
Binary files differ
diff --git a/res/drawable/ui_button_fly_off.png b/res/drawable/ui_button_fly_off.png
new file mode 100644
index 0000000..12752bd
--- /dev/null
+++ b/res/drawable/ui_button_fly_off.png
Binary files differ
diff --git a/res/drawable/ui_button_fly_on.png b/res/drawable/ui_button_fly_on.png
new file mode 100644
index 0000000..aede4f4
--- /dev/null
+++ b/res/drawable/ui_button_fly_on.png
Binary files differ
diff --git a/res/drawable/ui_button_options.png b/res/drawable/ui_button_options.png
new file mode 100644
index 0000000..b07f22e
--- /dev/null
+++ b/res/drawable/ui_button_options.png
Binary files differ
diff --git a/res/drawable/ui_button_start.png b/res/drawable/ui_button_start.png
new file mode 100644
index 0000000..e5e03cf
--- /dev/null
+++ b/res/drawable/ui_button_start.png
Binary files differ
diff --git a/res/drawable/ui_button_stomp_off.png b/res/drawable/ui_button_stomp_off.png
new file mode 100644
index 0000000..5618f66
--- /dev/null
+++ b/res/drawable/ui_button_stomp_off.png
Binary files differ
diff --git a/res/drawable/ui_button_stomp_on.png b/res/drawable/ui_button_stomp_on.png
new file mode 100644
index 0000000..8477a75
--- /dev/null
+++ b/res/drawable/ui_button_stomp_on.png
Binary files differ
diff --git a/res/drawable/ui_ending_bad_kabocha_background.png b/res/drawable/ui_ending_bad_kabocha_background.png
new file mode 100644
index 0000000..15a69d2
--- /dev/null
+++ b/res/drawable/ui_ending_bad_kabocha_background.png
Binary files differ
diff --git a/res/drawable/ui_ending_bad_kabocha_foreground.png b/res/drawable/ui_ending_bad_kabocha_foreground.png
new file mode 100644
index 0000000..d206323
--- /dev/null
+++ b/res/drawable/ui_ending_bad_kabocha_foreground.png
Binary files differ
diff --git a/res/drawable/ui_gem.png b/res/drawable/ui_gem.png
new file mode 100644
index 0000000..69520f4
--- /dev/null
+++ b/res/drawable/ui_gem.png
Binary files differ
diff --git a/res/drawable/ui_good_ending_background.png b/res/drawable/ui_good_ending_background.png
new file mode 100644
index 0000000..82678ca
--- /dev/null
+++ b/res/drawable/ui_good_ending_background.png
Binary files differ
diff --git a/res/drawable/ui_good_ending_foreground.png b/res/drawable/ui_good_ending_foreground.png
new file mode 100644
index 0000000..b4b9373
--- /dev/null
+++ b/res/drawable/ui_good_ending_foreground.png
Binary files differ
diff --git a/res/drawable/ui_paused.png b/res/drawable/ui_paused.png
new file mode 100644
index 0000000..da030f3
--- /dev/null
+++ b/res/drawable/ui_paused.png
Binary files differ
diff --git a/res/drawable/ui_pearl.png b/res/drawable/ui_pearl.png
new file mode 100644
index 0000000..475b824
--- /dev/null
+++ b/res/drawable/ui_pearl.png
Binary files differ
diff --git a/res/drawable/ui_rack_gray.png b/res/drawable/ui_rack_gray.png
new file mode 100644
index 0000000..980cbe0
--- /dev/null
+++ b/res/drawable/ui_rack_gray.png
Binary files differ
diff --git a/res/drawable/ui_rack_green.png b/res/drawable/ui_rack_green.png
new file mode 100644
index 0000000..d9623c2
--- /dev/null
+++ b/res/drawable/ui_rack_green.png
Binary files differ
diff --git a/res/drawable/ui_rack_red.png b/res/drawable/ui_rack_red.png
new file mode 100644
index 0000000..63649b5
--- /dev/null
+++ b/res/drawable/ui_rack_red.png
Binary files differ
diff --git a/res/drawable/ui_x.png b/res/drawable/ui_x.png
new file mode 100644
index 0000000..1bd477e
--- /dev/null
+++ b/res/drawable/ui_x.png
Binary files differ
diff --git a/res/drawable/wanda_happy.png b/res/drawable/wanda_happy.png
new file mode 100644
index 0000000..af6e371
--- /dev/null
+++ b/res/drawable/wanda_happy.png
Binary files differ
diff --git a/res/drawable/wanda_sad.png b/res/drawable/wanda_sad.png
new file mode 100644
index 0000000..df0ff8c
--- /dev/null
+++ b/res/drawable/wanda_sad.png
Binary files differ
diff --git a/res/drawable/wanda_smile.png b/res/drawable/wanda_smile.png
new file mode 100644
index 0000000..e672cce
--- /dev/null
+++ b/res/drawable/wanda_smile.png
Binary files differ
diff --git a/res/drawable/wanda_surprised.png b/res/drawable/wanda_surprised.png
new file mode 100644
index 0000000..d1f404d
--- /dev/null
+++ b/res/drawable/wanda_surprised.png
Binary files differ
diff --git a/res/layout/animation_player.xml b/res/layout/animation_player.xml
new file mode 100644
index 0000000..64bac1b
--- /dev/null
+++ b/res/layout/animation_player.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_canvas"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ >
+</ImageView>
diff --git a/res/layout/conversation_dialog.xml b/res/layout/conversation_dialog.xml
new file mode 100644
index 0000000..3528ec6
--- /dev/null
+++ b/res/layout/conversation_dialog.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TableRow>
+ <ImageView
+ android:id="@+id/speaker"
+ android:src="@drawable/wanda_smile"
+ android:adjustViewBounds="true"
+ android:gravity="center_vertical"/>
+ <TableLayout xmlns:android="http://schemas.android.com/apk/res/android">
+ <TableRow>
+ <TextView android:id="@+id/speakername"
+ android:layout_width="wrap_content"
+ android:layout_height="20dp"
+ android:text="Wanda"
+ android:textSize = "15sp"
+ android:textStyle = "bold"/>
+ </TableRow>
+ <TableRow>
+ <view xmlns:android="http://schemas.android.com/apk/res/android"
+ class = "com.replica.replicaisland.ConversationDialogActivity$TypewriterTextView"
+ android:id="@+id/typewritertext"
+ android:layout_width="300dp"
+ android:layout_height="110dp"
+
+ android:text="Blah blah blah blah blah"
+ android:textSize = "15sp"/>
+ </TableRow>
+ </TableLayout>
+ </TableRow>
+ <TableRow>
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_span = "2"
+ android:layout_gravity="right">
+ <ImageView
+ android:id="@+id/ok"
+
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="right"/>
+ </FrameLayout>
+ </TableRow>
+</TableLayout>
\ No newline at end of file
diff --git a/res/layout/custom_toast.xml b/res/layout/custom_toast.xml
new file mode 100644
index 0000000..1e6b9e3
--- /dev/null
+++ b/res/layout/custom_toast.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Thanks to http://hustleplay.wordpress.com/2009/07/23/replicating-default-android-toast/ ! -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+android:id="@+id/toast_layout_root"
+android:orientation="horizontal"
+android:layout_width="fill_parent"
+android:layout_height="fill_parent"
+android:padding="10dp"
+android:background="@drawable/custom_toast_border"
+>
+<TextView android:id="@+id/text"
+android:layout_width="wrap_content"
+android:layout_height="fill_parent"
+android:textColor="#65ff99"
+android:textSize="30sp"
+android:textStyle="bold"
+android:gravity="center"
+/>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/diary.xml b/res/layout/diary.xml
new file mode 100644
index 0000000..d213a2d
--- /dev/null
+++ b/res/layout/diary.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:background="#FFFFFF"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+
+ <ImageView
+ android:id="@+id/diarybackground"
+ android:src="@drawable/background_diary"
+ android:adjustViewBounds="true"
+ android:gravity="center_vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:clickable="false"
+ fadingEdge="0x00002000"
+ android:scaleType="fitXY"
+
+ />
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/diarytext"
+ android:layout_width="fill_parent"
+ android:textSize = "17sp"
+ android:textColor = "#000000"
+ android:singleLine="false"
+ android:scrollbars="vertical"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:text="Blah blah blah blah blah."
+ android:paddingLeft="15dp"
+ android:paddingRight="15dp"
+ android:paddingTop="15dp"/>
+ <ImageView
+ android:id="@+id/ok"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:layout_gravity="right"
+ android:layout_marginRight="30dp"
+ android:layout_marginBottom="20dp"/>
+ </LinearLayout>
+ </ScrollView>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/good_ending_animation.xml b/res/layout/good_ending_animation.xml
new file mode 100644
index 0000000..7c389cb
--- /dev/null
+++ b/res/layout/good_ending_animation.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_background"
+ android:background="@drawable/ui_good_ending_background"
+ android:layout_width="650dp"
+ android:layout_height="320dp"
+ android:adjustViewBounds="true"
+
+ >
+ </View>
+ </FrameLayout>
+
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_foreground"
+ android:background="@drawable/ui_good_ending_foreground"
+ android:layout_width="760dp"
+ android:layout_height="320dp"
+ android:adjustViewBounds="true"
+
+ >
+ </View>
+ </FrameLayout>
+
+ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent">
+ <TextView android:id="@+id/game_over"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="160dp"
+ android:layout_height="70dp"
+ android:textSize = "20sp"
+ android:textColor = "#FFFFFF"
+ android:gravity = "center"
+ android:layout_alignParentRight="true"
+ android:layout_marginTop = "20dp"
+ android:layout_marginRight = "20dp"
+ android:text="@string/thanks_for_playing"
+ android:typeface = "serif"
+ android:background="@drawable/custom_toast_border"
+ android:padding = "10dp"
+ />
+
+ </RelativeLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/kabocha_ending_animation.xml b/res/layout/kabocha_ending_animation.xml
new file mode 100644
index 0000000..e93e9af
--- /dev/null
+++ b/res/layout/kabocha_ending_animation.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_background"
+ android:background="@drawable/ui_ending_bad_kabocha_background"
+ android:layout_width="650dp"
+ android:layout_height="320dp"
+ android:adjustViewBounds="true"
+
+ >
+ </View>
+ </FrameLayout>
+
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_foreground"
+ android:background="@drawable/ui_ending_bad_kabocha_foreground"
+ android:layout_width="760dp"
+ android:layout_height="320dp"
+ android:adjustViewBounds="true"
+
+ >
+ </View>
+ </FrameLayout>
+
+ <AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/game_over"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="160dp"
+ android:layout_height="50dp"
+ android:textSize = "20sp"
+ android:textColor = "#FFFFFF"
+ android:gravity = "center"
+ android:layout_x="20dp"
+ android:layout_y="250dp"
+ android:text="@string/game_over"
+ android:typeface = "serif"
+ android:background="@drawable/custom_toast_border"
+ android:padding = "10dp"
+ />
+
+ </AbsoluteLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/level_select.xml b/res/layout/level_select.xml
new file mode 100644
index 0000000..1051fc1
--- /dev/null
+++ b/res/layout/level_select.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+ <ListView android:id="@+id/android:list"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:dividerHeight = "0dp"
+ android:footerDividersEnabled = "false"
+ android:headerDividersEnabled = "false"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/level_select_completed_row.xml b/res/layout/level_select_completed_row.xml
new file mode 100755
index 0000000..8824463
--- /dev/null
+++ b/res/layout/level_select_completed_row.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="70dp"
+ android:background = "#000000"
+ >
+ <ImageView
+ android:src="@drawable/ui_rack_gray"
+ android:adjustViewBounds="true"
+ android:gravity="center_vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_x="0dp"
+ android:layout_y="0dp"
+ android:focusable = "false"
+ android:focusableInTouchMode = "false"
+ android:scaleType="fitXY"
+
+ />
+ <TextView android:id="@+id/title" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="265dp"
+ android:layout_height="36dp"
+ android:textSize = "24sp"
+ android:textStyle = "bold"
+ android:textColor = "#066659"
+ android:gravity = "top"
+ android:layout_x="20dp"
+ android:layout_y="5dp"
+ android:text="Level 1-1"
+ />
+
+ <TextView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="60dp"
+ android:layout_height="15dp"
+ android:textSize = "12sp"
+ android:textColor = "#066659"
+ android:gravity = "top"
+ android:layout_x="290dp"
+ android:layout_y="25dp"
+ android:text="+ 0:02:031"
+ />
+
+</AbsoluteLayout>
\ No newline at end of file
diff --git a/res/layout/level_select_disabled_row.xml b/res/layout/level_select_disabled_row.xml
new file mode 100755
index 0000000..65c06ed
--- /dev/null
+++ b/res/layout/level_select_disabled_row.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="70dp"
+ android:background = "#000000"
+ >
+ <ImageView
+ android:src="@drawable/ui_rack_red"
+ android:adjustViewBounds="true"
+ android:gravity="center_vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_x="0dp"
+ android:layout_y="0dp"
+ android:focusable = "false"
+ android:focusableInTouchMode = "false"
+ android:scaleType="fitXY"
+
+ />
+ <TextView android:id="@+id/title" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="265dp"
+ android:layout_height="36dp"
+ android:textSize = "24sp"
+ android:textStyle = "bold"
+ android:textColor = "#066659"
+ android:gravity = "top"
+ android:layout_x="20dp"
+ android:layout_y="5dp"
+ android:text="Level 1-1"
+ />
+
+ <TextView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="60dp"
+ android:layout_height="15dp"
+ android:textSize = "12sp"
+ android:textColor = "#066659"
+ android:gravity = "top"
+ android:layout_x="290dp"
+ android:layout_y="25dp"
+ android:text="+ 0:02:031"
+ />
+
+</AbsoluteLayout>
\ No newline at end of file
diff --git a/res/layout/level_select_row.xml b/res/layout/level_select_row.xml
new file mode 100755
index 0000000..beb0e9b
--- /dev/null
+++ b/res/layout/level_select_row.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="70dp"
+ android:background = "#000000"
+ >
+ <ImageView
+ android:src="@drawable/ui_rack_green"
+ android:adjustViewBounds="true"
+ android:gravity="center_vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_x="0dp"
+ android:layout_y="0dp"
+ android:scaleType="fitXY"
+ />
+
+ <TextView android:id="@+id/title" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="265dp"
+ android:layout_height="36dp"
+ android:textSize = "24sp"
+ android:textStyle = "bold"
+ android:textColor = "#65ff99"
+ android:gravity = "top"
+ android:layout_x="20dp"
+ android:layout_y="5dp"
+ android:text="Level 1-1"
+ />
+
+ <TextView android:id="@+id/time" xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="60dp"
+ android:layout_height="15dp"
+ android:textSize = "12sp"
+ android:textColor = "#65ff99"
+ android:gravity = "top"
+ android:layout_x="290dp"
+ android:layout_y="25dp"
+ android:text="+ 0:02:031"
+ />
+
+</AbsoluteLayout>
\ No newline at end of file
diff --git a/res/layout/main.xml b/res/layout/main.xml
new file mode 100644
index 0000000..a56180e
--- /dev/null
+++ b/res/layout/main.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ >
+
+ <view xmlns:android="http://schemas.android.com/apk/res/android"
+ class = "com.replica.replicaisland.GLSurfaceView"
+ android:id="@+id/glsurfaceview"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ />
+ <ImageView
+ android:id="@+id/pausedMessage"
+ android:src="@drawable/ui_paused"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pleaseWaitMessage"
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="5dp"
+ android:background="@drawable/custom_toast_border"
+ android:layout_gravity="bottom|left"
+ android:visibility="gone">
+ <TextView android:id="@+id/text"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:textColor="#65ff99"
+ android:textSize="20sp"
+ android:textStyle="bold"
+ android:gravity="center"
+ android:text="@string/please_wait"
+ />
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/res/layout/mainmenu.xml b/res/layout/mainmenu.xml
new file mode 100644
index 0000000..ac27198
--- /dev/null
+++ b/res/layout/mainmenu.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="utf-8"?>
+<AbsoluteLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <ImageView
+ android:id="@+id/mainMenuBackground"
+ android:src="@drawable/title_background"
+ android:adjustViewBounds="true"
+ android:gravity="center_vertical"
+ android:scaleType="fitXY"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_x="0dp"
+ android:layout_y="0dp"
+ />
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_x="0dp"
+ android:layout_y="20dp"
+ android:gravity="center_horizontal"
+ >
+ <ImageView
+ android:id="@+id/mainMenuTitle"
+ android:src="@drawable/title"
+ android:adjustViewBounds="true"
+ android:layout_width="336dp"
+ android:layout_height="153dp"
+ android:gravity="center_horizontal"
+
+ />
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_x="0dp"
+ android:layout_y="0dp"
+ android:orientation="vertical"
+ android:gravity="bottom">
+
+
+ <ImageView
+ android:id="@+id/startButton"
+ android:src="@drawable/ui_button_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="true"
+ />
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="20dip"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ />
+
+ <ImageView
+ android:id="@+id/optionButton"
+ android:src="@drawable/ui_button_options"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:clickable="true"
+ />
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="40dip"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ />
+ </LinearLayout>
+
+</AbsoluteLayout>
\ No newline at end of file
diff --git a/res/layout/rokudou_ending_animation.xml b/res/layout/rokudou_ending_animation.xml
new file mode 100644
index 0000000..87bbdd1
--- /dev/null
+++ b/res/layout/rokudou_ending_animation.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ >
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_background"
+ android:background="@drawable/ui_bad_ending_rokudou_bg"
+ android:layout_width="480dp"
+ android:layout_height="500dp"
+ android:adjustViewBounds="true"
+
+ >
+ </View>
+ </FrameLayout>
+
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_sphere"
+ android:background="@drawable/ui_bad_ending_rokudou_sphere"
+ android:layout_width="480dp"
+ android:layout_height="500dp"
+ android:adjustViewBounds="true"
+
+ >
+ </View>
+ </FrameLayout>
+
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_cliffs"
+ android:background="@drawable/ui_bad_ending_rokudou_cliffs"
+ android:layout_width="480dp"
+ android:layout_height="500dp"
+ android:adjustViewBounds="true"
+
+ >
+ </View>
+ </FrameLayout>
+
+ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ >
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/animation_rokudou"
+ android:background="@drawable/ui_bad_ending_rokudou_rokudou"
+ android:layout_width="480dp"
+ android:layout_height="600dp"
+ android:adjustViewBounds="true"
+
+ >
+ </View>
+ </FrameLayout>
+
+ <AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/game_over"
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="160dp"
+ android:layout_height="50dp"
+ android:textSize = "20sp"
+ android:textColor = "#FFFFFF"
+ android:gravity = "center"
+ android:layout_x="20dp"
+ android:layout_y="20dp"
+ android:text="@string/game_over"
+ android:typeface = "serif"
+ android:background="@drawable/custom_toast_border"
+ android:padding = "10dp"
+ />
+
+ </AbsoluteLayout>
+ </FrameLayout>
+</RelativeLayout>
\ No newline at end of file
diff --git a/res/raw/bwv_115.mid b/res/raw/bwv_115.mid
new file mode 100644
index 0000000..4c25da9
--- /dev/null
+++ b/res/raw/bwv_115.mid
Binary files differ
diff --git a/res/raw/collision.bin b/res/raw/collision.bin
new file mode 100644
index 0000000..6f3e2d5
--- /dev/null
+++ b/res/raw/collision.bin
Binary files differ
diff --git a/res/raw/collisiontest.bin b/res/raw/collisiontest.bin
new file mode 100644
index 0000000..0406072
--- /dev/null
+++ b/res/raw/collisiontest.bin
Binary files differ
diff --git a/res/raw/deep_clang.ogg b/res/raw/deep_clang.ogg
new file mode 100644
index 0000000..9f9e22c
--- /dev/null
+++ b/res/raw/deep_clang.ogg
Binary files differ
diff --git a/res/raw/ding.ogg b/res/raw/ding.ogg
new file mode 100644
index 0000000..281b7cc
--- /dev/null
+++ b/res/raw/ding.ogg
Binary files differ
diff --git a/res/raw/dungeon01.bin b/res/raw/dungeon01.bin
new file mode 100755
index 0000000..2d126e6
--- /dev/null
+++ b/res/raw/dungeon01.bin
Binary files differ
diff --git a/res/raw/gem1.ogg b/res/raw/gem1.ogg
new file mode 100644
index 0000000..db1057a
--- /dev/null
+++ b/res/raw/gem1.ogg
Binary files differ
diff --git a/res/raw/gem2.ogg b/res/raw/gem2.ogg
new file mode 100644
index 0000000..eb7573c
--- /dev/null
+++ b/res/raw/gem2.ogg
Binary files differ
diff --git a/res/raw/gem3.ogg b/res/raw/gem3.ogg
new file mode 100644
index 0000000..ba5605a
--- /dev/null
+++ b/res/raw/gem3.ogg
Binary files differ
diff --git a/res/raw/hard_thump.ogg b/res/raw/hard_thump.ogg
new file mode 100644
index 0000000..5a092b3
--- /dev/null
+++ b/res/raw/hard_thump.ogg
Binary files differ
diff --git a/res/raw/island1.bin b/res/raw/island1.bin
new file mode 100644
index 0000000..33b1571
--- /dev/null
+++ b/res/raw/island1.bin
Binary files differ
diff --git a/res/raw/level_0_1_sewer.bin b/res/raw/level_0_1_sewer.bin
new file mode 100644
index 0000000..57087d4
--- /dev/null
+++ b/res/raw/level_0_1_sewer.bin
Binary files differ
diff --git a/res/raw/level_0_1_sewer_kyle.bin b/res/raw/level_0_1_sewer_kyle.bin
new file mode 100644
index 0000000..5ac764c
--- /dev/null
+++ b/res/raw/level_0_1_sewer_kyle.bin
Binary files differ
diff --git a/res/raw/level_0_1_sewer_wanda.bin b/res/raw/level_0_1_sewer_wanda.bin
new file mode 100644
index 0000000..558fe9e
--- /dev/null
+++ b/res/raw/level_0_1_sewer_wanda.bin
Binary files differ
diff --git a/res/raw/level_0_2_lab.bin b/res/raw/level_0_2_lab.bin
new file mode 100644
index 0000000..17f2515
--- /dev/null
+++ b/res/raw/level_0_2_lab.bin
Binary files differ
diff --git a/res/raw/level_0_3_lab.bin b/res/raw/level_0_3_lab.bin
new file mode 100644
index 0000000..fb421c8
--- /dev/null
+++ b/res/raw/level_0_3_lab.bin
Binary files differ
diff --git a/res/raw/level_1_1_island.bin b/res/raw/level_1_1_island.bin
new file mode 100644
index 0000000..78cedb7
--- /dev/null
+++ b/res/raw/level_1_1_island.bin
Binary files differ
diff --git a/res/raw/level_1_2_island.bin b/res/raw/level_1_2_island.bin
new file mode 100644
index 0000000..2f3398f
--- /dev/null
+++ b/res/raw/level_1_2_island.bin
Binary files differ
diff --git a/res/raw/level_1_3_island.bin b/res/raw/level_1_3_island.bin
new file mode 100644
index 0000000..9881a1c
--- /dev/null
+++ b/res/raw/level_1_3_island.bin
Binary files differ
diff --git a/res/raw/level_1_4_island.bin b/res/raw/level_1_4_island.bin
new file mode 100644
index 0000000..c23c5f2
--- /dev/null
+++ b/res/raw/level_1_4_island.bin
Binary files differ
diff --git a/res/raw/level_1_5_island.bin b/res/raw/level_1_5_island.bin
new file mode 100644
index 0000000..f89d834
--- /dev/null
+++ b/res/raw/level_1_5_island.bin
Binary files differ
diff --git a/res/raw/level_1_6_island.bin b/res/raw/level_1_6_island.bin
new file mode 100644
index 0000000..53a4a67
--- /dev/null
+++ b/res/raw/level_1_6_island.bin
Binary files differ
diff --git a/res/raw/level_1_8_island.bin b/res/raw/level_1_8_island.bin
new file mode 100644
index 0000000..1280834
--- /dev/null
+++ b/res/raw/level_1_8_island.bin
Binary files differ
diff --git a/res/raw/level_1_9_island.bin b/res/raw/level_1_9_island.bin
new file mode 100644
index 0000000..025c608
--- /dev/null
+++ b/res/raw/level_1_9_island.bin
Binary files differ
diff --git a/res/raw/level_2_1_grass.bin b/res/raw/level_2_1_grass.bin
new file mode 100644
index 0000000..cb64be5
--- /dev/null
+++ b/res/raw/level_2_1_grass.bin
Binary files differ
diff --git a/res/raw/level_2_2_grass.bin b/res/raw/level_2_2_grass.bin
new file mode 100644
index 0000000..cc2cf2e
--- /dev/null
+++ b/res/raw/level_2_2_grass.bin
Binary files differ
diff --git a/res/raw/level_2_3_grass.bin b/res/raw/level_2_3_grass.bin
new file mode 100644
index 0000000..3216e18
--- /dev/null
+++ b/res/raw/level_2_3_grass.bin
Binary files differ
diff --git a/res/raw/level_2_4_grass.bin b/res/raw/level_2_4_grass.bin
new file mode 100644
index 0000000..14f1cb6
--- /dev/null
+++ b/res/raw/level_2_4_grass.bin
Binary files differ
diff --git a/res/raw/level_2_5_grass.bin b/res/raw/level_2_5_grass.bin
new file mode 100644
index 0000000..6e604fd
--- /dev/null
+++ b/res/raw/level_2_5_grass.bin
Binary files differ
diff --git a/res/raw/level_2_6_grass.bin b/res/raw/level_2_6_grass.bin
new file mode 100644
index 0000000..79a4066
--- /dev/null
+++ b/res/raw/level_2_6_grass.bin
Binary files differ
diff --git a/res/raw/level_2_7_grass.bin b/res/raw/level_2_7_grass.bin
new file mode 100644
index 0000000..7365352
--- /dev/null
+++ b/res/raw/level_2_7_grass.bin
Binary files differ
diff --git a/res/raw/level_2_8_grass.bin b/res/raw/level_2_8_grass.bin
new file mode 100644
index 0000000..b700737
--- /dev/null
+++ b/res/raw/level_2_8_grass.bin
Binary files differ
diff --git a/res/raw/level_2_9_grass.bin b/res/raw/level_2_9_grass.bin
new file mode 100644
index 0000000..eb1177a
--- /dev/null
+++ b/res/raw/level_2_9_grass.bin
Binary files differ
diff --git a/res/raw/level_3_0_sewer.bin b/res/raw/level_3_0_sewer.bin
new file mode 100644
index 0000000..11b0d29
--- /dev/null
+++ b/res/raw/level_3_0_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_10_sewer.bin b/res/raw/level_3_10_sewer.bin
new file mode 100644
index 0000000..9009fbf
--- /dev/null
+++ b/res/raw/level_3_10_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_11_sewer.bin b/res/raw/level_3_11_sewer.bin
new file mode 100644
index 0000000..4f47dde
--- /dev/null
+++ b/res/raw/level_3_11_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_1_grass.bin b/res/raw/level_3_1_grass.bin
new file mode 100644
index 0000000..aeda017
--- /dev/null
+++ b/res/raw/level_3_1_grass.bin
Binary files differ
diff --git a/res/raw/level_3_2_sewer.bin b/res/raw/level_3_2_sewer.bin
new file mode 100644
index 0000000..98b80c0
--- /dev/null
+++ b/res/raw/level_3_2_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_3_sewer.bin b/res/raw/level_3_3_sewer.bin
new file mode 100644
index 0000000..650aa1a
--- /dev/null
+++ b/res/raw/level_3_3_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_4_sewer.bin b/res/raw/level_3_4_sewer.bin
new file mode 100644
index 0000000..562dfbe
--- /dev/null
+++ b/res/raw/level_3_4_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_5_sewer.bin b/res/raw/level_3_5_sewer.bin
new file mode 100644
index 0000000..fd2c690
--- /dev/null
+++ b/res/raw/level_3_5_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_6_sewer.bin b/res/raw/level_3_6_sewer.bin
new file mode 100644
index 0000000..86fb165
--- /dev/null
+++ b/res/raw/level_3_6_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_7_sewer.bin b/res/raw/level_3_7_sewer.bin
new file mode 100644
index 0000000..f93c82f
--- /dev/null
+++ b/res/raw/level_3_7_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_7_underground.bin b/res/raw/level_3_7_underground.bin
new file mode 100644
index 0000000..1f5e361
--- /dev/null
+++ b/res/raw/level_3_7_underground.bin
Binary files differ
diff --git a/res/raw/level_3_8_sewer.bin b/res/raw/level_3_8_sewer.bin
new file mode 100644
index 0000000..5ef0f2f
--- /dev/null
+++ b/res/raw/level_3_8_sewer.bin
Binary files differ
diff --git a/res/raw/level_3_9_sewer.bin b/res/raw/level_3_9_sewer.bin
new file mode 100644
index 0000000..0704d75
--- /dev/null
+++ b/res/raw/level_3_9_sewer.bin
Binary files differ
diff --git a/res/raw/level_4_1_underground.bin b/res/raw/level_4_1_underground.bin
new file mode 100644
index 0000000..5c1d192
--- /dev/null
+++ b/res/raw/level_4_1_underground.bin
Binary files differ
diff --git a/res/raw/level_4_2_underground.bin b/res/raw/level_4_2_underground.bin
new file mode 100644
index 0000000..f045bcd
--- /dev/null
+++ b/res/raw/level_4_2_underground.bin
Binary files differ
diff --git a/res/raw/level_4_3_underground.bin b/res/raw/level_4_3_underground.bin
new file mode 100644
index 0000000..dbe1c4a
--- /dev/null
+++ b/res/raw/level_4_3_underground.bin
Binary files differ
diff --git a/res/raw/level_4_4_underground.bin b/res/raw/level_4_4_underground.bin
new file mode 100644
index 0000000..cc1e555
--- /dev/null
+++ b/res/raw/level_4_4_underground.bin
Binary files differ
diff --git a/res/raw/level_4_5_underground.bin b/res/raw/level_4_5_underground.bin
new file mode 100644
index 0000000..f0634a8
--- /dev/null
+++ b/res/raw/level_4_5_underground.bin
Binary files differ
diff --git a/res/raw/level_4_7_underground.bin b/res/raw/level_4_7_underground.bin
new file mode 100644
index 0000000..d624d5b
--- /dev/null
+++ b/res/raw/level_4_7_underground.bin
Binary files differ
diff --git a/res/raw/level_4_8_underground.bin b/res/raw/level_4_8_underground.bin
new file mode 100644
index 0000000..8fcde7c
--- /dev/null
+++ b/res/raw/level_4_8_underground.bin
Binary files differ
diff --git a/res/raw/level_4_9_underground.bin b/res/raw/level_4_9_underground.bin
new file mode 100644
index 0000000..1d1b23b
--- /dev/null
+++ b/res/raw/level_4_9_underground.bin
Binary files differ
diff --git a/res/raw/level_final_boss_lab.bin b/res/raw/level_final_boss_lab.bin
new file mode 100644
index 0000000..981c801
--- /dev/null
+++ b/res/raw/level_final_boss_lab.bin
Binary files differ
diff --git a/res/raw/npc_motion_test.bin b/res/raw/npc_motion_test.bin
new file mode 100644
index 0000000..b18b873
--- /dev/null
+++ b/res/raw/npc_motion_test.bin
Binary files differ
diff --git a/res/raw/objecttestmap.bin b/res/raw/objecttestmap.bin
new file mode 100644
index 0000000..cb98392
--- /dev/null
+++ b/res/raw/objecttestmap.bin
Binary files differ
diff --git a/res/raw/openandshut.bin b/res/raw/openandshut.bin
new file mode 100644
index 0000000..0061640
--- /dev/null
+++ b/res/raw/openandshut.bin
Binary files differ
diff --git a/res/raw/performancetest.bin b/res/raw/performancetest.bin
new file mode 100644
index 0000000..4275f99
--- /dev/null
+++ b/res/raw/performancetest.bin
Binary files differ
diff --git a/res/raw/performancetest2.bin b/res/raw/performancetest2.bin
new file mode 100644
index 0000000..ddffc4e
--- /dev/null
+++ b/res/raw/performancetest2.bin
Binary files differ
diff --git a/res/raw/performancetest3.bin b/res/raw/performancetest3.bin
new file mode 100755
index 0000000..e4da302
--- /dev/null
+++ b/res/raw/performancetest3.bin
Binary files differ
diff --git a/res/raw/puzzles_test.bin b/res/raw/puzzles_test.bin
new file mode 100644
index 0000000..79511c1
--- /dev/null
+++ b/res/raw/puzzles_test.bin
Binary files differ
diff --git a/res/raw/quick_explosion.ogg b/res/raw/quick_explosion.ogg
new file mode 100644
index 0000000..5956ce6
--- /dev/null
+++ b/res/raw/quick_explosion.ogg
Binary files differ
diff --git a/res/raw/rockets.ogg b/res/raw/rockets.ogg
new file mode 100644
index 0000000..b10687e
--- /dev/null
+++ b/res/raw/rockets.ogg
Binary files differ
diff --git a/res/raw/slidetest.bin b/res/raw/slidetest.bin
new file mode 100755
index 0000000..8f10d89
--- /dev/null
+++ b/res/raw/slidetest.bin
Binary files differ
diff --git a/res/raw/slidetest2.bin b/res/raw/slidetest2.bin
new file mode 100755
index 0000000..2a19d2b
--- /dev/null
+++ b/res/raw/slidetest2.bin
Binary files differ
diff --git a/res/raw/slidetest3.bin b/res/raw/slidetest3.bin
new file mode 100755
index 0000000..6ee6828
--- /dev/null
+++ b/res/raw/slidetest3.bin
Binary files differ
diff --git a/res/raw/sound_break_block.ogg b/res/raw/sound_break_block.ogg
new file mode 100644
index 0000000..de64998
--- /dev/null
+++ b/res/raw/sound_break_block.ogg
Binary files differ
diff --git a/res/raw/sound_button.ogg b/res/raw/sound_button.ogg
new file mode 100644
index 0000000..5802f39
--- /dev/null
+++ b/res/raw/sound_button.ogg
Binary files differ
diff --git a/res/raw/sound_buzz.ogg b/res/raw/sound_buzz.ogg
new file mode 100644
index 0000000..336d817
--- /dev/null
+++ b/res/raw/sound_buzz.ogg
Binary files differ
diff --git a/res/raw/sound_cannon.ogg b/res/raw/sound_cannon.ogg
new file mode 100644
index 0000000..5641d98
--- /dev/null
+++ b/res/raw/sound_cannon.ogg
Binary files differ
diff --git a/res/raw/sound_close.ogg b/res/raw/sound_close.ogg
new file mode 100644
index 0000000..37b9f4a
--- /dev/null
+++ b/res/raw/sound_close.ogg
Binary files differ
diff --git a/res/raw/sound_explode.ogg b/res/raw/sound_explode.ogg
new file mode 100644
index 0000000..302012c
--- /dev/null
+++ b/res/raw/sound_explode.ogg
Binary files differ
diff --git a/res/raw/sound_gun.ogg b/res/raw/sound_gun.ogg
new file mode 100644
index 0000000..817e006
--- /dev/null
+++ b/res/raw/sound_gun.ogg
Binary files differ
diff --git a/res/raw/sound_kabocha_hit.ogg b/res/raw/sound_kabocha_hit.ogg
new file mode 100644
index 0000000..8aa37af
--- /dev/null
+++ b/res/raw/sound_kabocha_hit.ogg
Binary files differ
diff --git a/res/raw/sound_open.ogg b/res/raw/sound_open.ogg
new file mode 100644
index 0000000..3e0138d
--- /dev/null
+++ b/res/raw/sound_open.ogg
Binary files differ
diff --git a/res/raw/sound_poing.ogg b/res/raw/sound_poing.ogg
new file mode 100644
index 0000000..41aa6ff
--- /dev/null
+++ b/res/raw/sound_poing.ogg
Binary files differ
diff --git a/res/raw/sound_possession.ogg b/res/raw/sound_possession.ogg
new file mode 100644
index 0000000..3371b17
--- /dev/null
+++ b/res/raw/sound_possession.ogg
Binary files differ
diff --git a/res/raw/sound_rokudou_hit.ogg b/res/raw/sound_rokudou_hit.ogg
new file mode 100644
index 0000000..620e63b
--- /dev/null
+++ b/res/raw/sound_rokudou_hit.ogg
Binary files differ
diff --git a/res/raw/sound_stomp.ogg b/res/raw/sound_stomp.ogg
new file mode 100644
index 0000000..c770571
--- /dev/null
+++ b/res/raw/sound_stomp.ogg
Binary files differ
diff --git a/res/raw/testmap.bin b/res/raw/testmap.bin
new file mode 100755
index 0000000..6c4ee97
--- /dev/null
+++ b/res/raw/testmap.bin
Binary files differ
diff --git a/res/raw/thump.ogg b/res/raw/thump.ogg
new file mode 100644
index 0000000..ce3276a
--- /dev/null
+++ b/res/raw/thump.ogg
Binary files differ
diff --git a/res/values-dpad/strings.xml b/res/values-dpad/strings.xml
new file mode 100644
index 0000000..edb3bb5
--- /dev/null
+++ b/res/values-dpad/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources>
+<string name="nav_type">DPad</string>
+</resources>
\ No newline at end of file
diff --git a/res/values-en/strings.xml b/res/values-en/strings.xml
new file mode 100644
index 0000000..b33222f
--- /dev/null
+++ b/res/values-en/strings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+
+</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
new file mode 100644
index 0000000..06e44a0
--- /dev/null
+++ b/res/values-ja/strings.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">ワンダのレプリカ島</string>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<!-- Dialg Entries -->
+
+
+
+
+
+</resources>
diff --git a/res/values-nonav/strings.xml b/res/values-nonav/strings.xml
new file mode 100644
index 0000000..b094ea2
--- /dev/null
+++ b/res/values-nonav/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources>
+<string name="nav_type">None</string>
+</resources>
diff --git a/res/values-wheel/strings.xml b/res/values-wheel/strings.xml
new file mode 100644
index 0000000..106df5a
--- /dev/null
+++ b/res/values-wheel/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<resources>
+<string name="nav_type">Wheel</string>
+</resources>
\ No newline at end of file
diff --git a/res/values/kabocha.xml b/res/values/kabocha.xml
new file mode 100644
index 0000000..f77de48
--- /dev/null
+++ b/res/values/kabocha.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+<!-- Kabocha Dialog Entries -->
+
+<string name="Kabocha_0_2_1_1">
+System check looks good, unit tests are passing, boostrap is complete! Oh jolly, he’s responding!
+</string>
+
+<string name="Kabocha_0_2_1_2">
+Hello there, Android. I’m Dr. Woodrow Lichtenstein Kabochanomizu, your humble creator. You’re going to help me find something very precious that I’ve been studying for most of my life: The Source.
+</string>
+
+<string name="Kabocha_0_2_1_3">
+But first, we should go over some of the basics. Use the trackball or directional pad to <b>roll left and right</b>. You only need to make <b>very small movements</b> to move effectively. Try rolling down that hill.
+</string>
+
+<string name="Kabocha_0_2_2_1">
+Jolly good! You learn exactly as quickly as I calculated you would. Fantastique! Remember, the trackball and directional pad are just for <b>changing direction</b>. You do not need to press them constantly.
+</string>
+
+<string name="Kabocha_0_2_2_2">
+Now, rolling around is a pleasurable way to move, but to really travel in style you should fly. <b>Tap the blue button on the lower left-hand side of the screen to jump, and hold it down to fly.</b> Be careful though--your batteries can only hold you aloft for a short time before they need to recharge. Watch the meter at the top left of the screen to see your remaining flight power.
+</string>
+
+<string name="Kabocha_0_2_2_3">
+Try flying up this shaft.
+</string>
+
+<string name="Kabocha_0_2_3_1">
+Bravo! Fantastique! It’s a little scary at first, yes? Don’t worry, you’ll get the hang of it. The trick is to just make small movements left and right while in the air.
+</string>
+
+<string name="Kabocha_0_2_3_2">
+Now my friend, let us give you a more complicated test. I had my robots turn this part of the lab into something of an obstacle course. Let’s see if you can make it to the other side. I’ll be waiting for you there.
+</string>
+
+<string name="Kabocha_0_2_4_1">
+Excellent work, and in record time too! My calculations are once again correct: you are the most capable machine I have ever developed. You surely will find The Source where all others have failed.
+</string>
+
+<string name="Kabocha_0_2_4_2">
+Did you see those little pearls floating in the air along the way? You should <b>collect them</b> as you travel across the island. If you are damaged, collecting pearls will <b>help your repair system</b> and give you an <b>impressive energy shield</b> for a short time. <small>The pearls do not actually have anything to do with your operation but I really need them to help pay off this lab. The economy, you know.</small>
+</string>
+
+<string name="Kabocha_0_2_4_3">
+Jolly good. Let’s move on. Push the red button on the ground over there to open the door with the red mark.
+</string>
+
+<string name="Kabocha_0_2_5_1">
+This entire island flows from The Source. Its power affects the things that get close to it, and this island is consequently full of unique forms of life. One of them, I’m sorry to say, is my fault.
+</string>
+
+<string name="Kabocha_0_2_5_2">
+Years ago I released some small robots on the island in an attempt to pinpoint the exact location of The Source. The mistake I made was allowing them to replicate on their own. The Source twisted them such that I lost control, and since then they have multiplied at such a rate that the little pests have almost overrun the island.
+</string>
+
+<string name="Kabocha_0_2_5_3">
+I’ve given you a useful maneuver to dispose of these pesky troublemakers. While in the air, <b>press the red attack button to drop your full weight onto enemies and crush them</b>. Give it a shot on that fellow to the left.
+</string>
+
+<string name="Kabocha_0_2_6_1">
+Jolly good! Way to drop the hammer! <small>(My calculations suggest that phrase is the preferred selection given the state of contemporary language)</small> Of course, you can always just avoid these robotic cretins if you like, but you’ll be doing the island a great service if you destroy them. Remember, if you get hurt you can collect pearls to restore your health <small>and help me put a dent in my credit card bill</small>.
+</string>
+
+<string name="Kabocha_0_2_6_2">
+One more important point I almost forgot to mention. Your internal battery uses <b>red gems</b> to synthesize power from extremely low-light conditions. I did a survey of the island and concluded that every major area has enough gems to keep you going, but it is imperative that you <b>collect them whenever you see them</b>. My research suggests that there should be at least three gems in each area, and to get to the next area you must <b>collect all three</b>.
+</string>
+
+<string name="Kabocha_0_2_6_3">
+Ho ho, we can’t be sending you off into the wild without knowing things like that, can we? Right-o, ahead is an area with three gems. Find them and we can move on to more complicated topics, like what you’re doing here and how The Source is going to help me save the world.
+</string>
+
+
+<string name="Kabocha_0_3_1_1">
+Jolly ho, my little creation! You are almost ready to venture out by yourself. I need to tell you about your mission, which is of critical importance to the future of this planet. But before that, let me show you a trick.
+</string>
+
+<string name="Kabocha_0_3_1_2">
+First, make your way through this maze. Make sure you pick up the red gem on the way.
+</string>
+
+<string name="Kabocha_0_3_2_1">
+I’ve outfitted you with a special type of energy weapon that allows you to control machines. While standing on the ground, <b>hold down the attack button</b> for a few seconds to charge up the <b>Possession Orb</b>.
+</string>
+
+<string name="Kabocha_0_3_2_2">
+Once the Possession Orb is released, you can control its movement by tilting the phone. Running it into a mechanical object will allow you to possess that object. Pressing the attack button will destroy it and return control to your body.
+</string>
+
+<string name="Kabocha_0_3_2_3">
+Try possessing that robot down there and using him to break through those blue blocks. Here’s a hint: releasing a possessed robot causes it to explode.
+</string>
+
+<string name="Kabocha_0_3_3_1">
+Jolly good work! You’re already asymptotically equal to my best neural net model.
+</string>
+
+<string name="Kabocha_0_3_3_2">
+One more thing about possession: those energy orbs require a lot of power to maintain, so you can only use them for a short time if you have not collected any gems. Collecting gems will extend the amount of time the orb can survive before dissipating.
+</string>
+
+<string name="Kabocha_0_3_3_3">
+Let’s practice possession a little more. I’ll meet you up top.
+</string>
+
+<string name="Kabocha_0_3_4_1">
+We don’t actually know what The Source is or where it came from. My theory is that it’s some sort of nanotechnological machine, constantly sucking matter in from the ocean, breaking it down into its component molecules, and then putting those molecules back together however it sees fit. If that’s true, it’s probably the creation of humans, but not humans of our time. Perhaps it was sent back in time by a future civilization, I’m not sure. On that point my data is insufficient. Suffice to say it is responsible for this island and everything on it.
+</string>
+
+<string name="Kabocha_0_3_4_2">
+If I can find The Source, I can rid the world of hunger, disease, and war. With nanotechnology no problem is unsolvable, no goal out of reach. I’ve been searching for it my whole life, but now I’m old and can no longer venture out on my own. That’s why I’ve created you for this most important of tasks.
+</string>
+
+<string name="Kabocha_0_3_5_1">
+You’ve done jolly well so far, my little friend. It’s almost time to release you into the wild. But before I do that, I need to tell you about our enemies.
+</string>
+
+<string name="Kabocha_0_3_5_2">
+The island is full of danger, but be particularly careful of Rokudou operatives. The Rokudou Corporation is a huge, soulless multinational run by a maniac. I’m sure you already have enough synapse nodes to understand that The Source could be used as a horrible weapon if it fell into the wrong hands.
+</string>
+
+<string name="Kabocha_0_3_5_3">
+L. Rokudou is infamous for having built a successful business out of illegal profiteering, but few know he’s also a psychopath. I’ve seen his behavior up close, and let me tell you: that man is crazy enough to annihilate the entire world if it would make him king of the rubble.
+</string>
+
+<string name="Kabocha_0_3_5_4">
+You can be sure he’s looking for The Source, though he’s not foolish enough to do it himself. He sends agents, wet-behind-the-ears adventurers who don’t know what they’re getting into. You must avoid them at all costs, and we must find The Source before they do, or the future of the world is in great peril.
+</string>
+
+
+
+<string name = "Kabocha_4_3_1_1">
+Jolly good work, my little friend! The Source is within my grasp after all these years!
+</string>
+<string name = "Kabocha_4_3_1_2">
+Ha ha ha ha ha ha ha ha ha ha ha ha ha ha! My time has finally come. Rokudou, you shall cower at my feet! If you lick my boots well enough I may find it in my heart to kill you quickly. I was thinking of slowly replacing your bone marrow with molten lead, but that might not be painful enough.
+</string>
+<string name = "Kabocha_4_3_1_3">
+Oh, jolly day! Finally, after all my research, my blood and sweat and tears, finally The Source is mine. Nobody shall stand in my way now! Ho ha ha ha ha ha he ha ha ha ho ha he ha ho he ha ha he he ho ho ha he ho ha ha!
+</string>
+<string name = "Kabocha_4_3_1_4">
+(Ahem) As for you, my little green friend, you’ve reached the end of your usefulness. As your creator, I thank you for your service. I really should shut you down properly, but right now The Source and I have more important matters to attend to back at the lab. Tell you what: your final command is to rot down here in this cave. See to it that you follow it to the letter. <small>Ta-ta!</small>
+</string>
+
+<string name = "Kabocha_final_boss_1_1">
+Don’t waste your breath, Rokudou. This little robot is <i>my</i> creation, and he does <i>my</i> bidding.
+</string>
+<string name = "Kabocha_final_boss_1_2">
+I underestimated you, my little friend. Your capabilities have far surpassed even my most optimistic projections. Still, you are hardwired to follow my command. And besides, Rokudou is a soulless monster. What makes you think you can trust him?
+</string>
+<string name = "Kabocha_final_boss_1_3">
+Now, do me a favor and go knock his head in. A few well-placed stomps should do the trick. Jolly ho!
+</string>
+</resources>
diff --git a/res/values/kyle.xml b/res/values/kyle.xml
new file mode 100644
index 0000000..c96d45e
--- /dev/null
+++ b/res/values/kyle.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+<!-- Kyle Dialog Entries -->
+
+<string name="Kyle_2_1_1_1">
+Well, well, what have we here? Some kind of robot? Looks like Kabocha’s work. Hmph. First the maniac releases those annoying blue robots and now this? He’s too scared to venture out of his little fortified bunker so he makes these hunks of junk do his dirty work for him. What a coward.
+</string>
+
+<string name="Kyle_2_1_1_2">
+Hey! Hey you! Can you even talk? Hmph, figures. What a joke. Kabocha’s “inventions” are truly pathetic. It’s no wonder he hasn’t found The Source in 50 years--the man sets a new bar for incompetence.
+</string>
+
+<string name="Kyle_2_1_1_3">
+Tell you what, grease ball. You stay out of my way and I might not send your shiny green posterior into outer orbit. My name’s Kyle, and I am the best of the best. One false move and I’ll hit you so hard your code will run in reverse.
+</string>
+
+<string name="Kyle_3_8_1_1">
+Didn’t think I’d be seeing you again. Can’t you take a hint, you bumbling bundle of bolts? I was right about you--you’re a little Kabocha spy, aren’t you.
+</string>
+
+<string name="Kyle_3_8_1_2">
+Look, last time I didn’t really put my heart into it. I pulled my punches because frankly, I didn’t think it would take a full-strength hit to bring you down. It’s surprising that you survived, but even more inconceivable that you didn’t just turn around and go home. You’re just <i>asking</i> to be flattened, aren’t you.
+</string>
+
+<string name="Kyle_3_8_1_3">
+Hmph. Tell you what, I’ll give you a chance to prove you’re worth more than scrap metal. <b>Race me to the next gem.</b> If you get it first, I’ll let you off easy. If I get there first, I’m going to reduce you to a smoldering crater. No pulling punches this time.
+</string>
+
+<string name="Kyle_3_8_2_1">
+Not bad for amateur hour. I was going to go ahead and obliterate you anyway, but on second thought a deal is a deal. I guess I’ll let you stumble about for a while longer before I reduce you to your principal elements. More fun that way.
+</string>
+
+<string name="Kyle_2_3_1_1">
+You know what your problem is? You can’t <i>think</i>. You can’t reason. You can’t decide. All you can do is execute your little program, follow the breadcrumbs Kabocha has left for you. Your brain is a <i>routine</i>: just stimulus, response, stimulus, response. Every possible thought you could ever have has been predetermined.
+</string>
+
+<string name="Kyle_2_3_1_2">
+Which is why you’re never going to get anywhere on this island. This place is the very antithesis of determinism. It’s random, crazy. The Source doesn’t just copy things it’s touched, it combines them, mutates them, twists them. There’s no consistency, no pattern, no algorithm. It just <i>is</i>.
+</string>
+
+<string name="Kyle_2_3_1_3">
+You’re screwed. Mr. Rokudou sends people like Wanda and me to search for The Source because he knows no machine could ever survive for long on this island. We can make split-second decisions, alter our perspectives, deal with problems we’ve never seen before. All the logic boards, solid state memory, and neural networks in the world can’t help you do that. You’ve lost before you even started.
+</string>
+
+<string name="Kyle_3_9_1_1">
+You never learn, do you? It’s pointless. You cannot beat Mr. Rokudou. He has infinite resources--even the puny little array of logic gates you call a brain should be able to understand that. Face it, Kabocha is a has-been and you don’t stand a chance down here. I should just smash your central processor now and put you out of your misery.
+</string>
+
+<string name="Kyle_3_9_1_2">
+Stay out of my way. If I see you again I swear I am going to turn you inside out and set fire to your internals.
+</string>
+
+<string name="Kyle_3_4_1_1">
+You followed us down here, didn’t you? You slimy, spying little bucket of transistors. We do all the work and you just tag along, is that how it is? Mr. Rokudou takes all the risk and Kabocha just rides on his coattails, all the way to The Source? You’re even more pathetic than I thought.
+</string>
+
+<string name="Kyle_3_4_1_2">
+Look, I’ve had about enough of you and your little punk tricks. If Kabocha wants The Source he’s going to have to come down here and get it himself. No stupid little robot is going to stand in my way.
+</string>
+
+<string name="Kyle_3_10_1_1">
+I warned you, you over-complicated washing machine. I told you not to cross my path again, and yet here you are. That’s it, I’m done wasting my time with this crap.
+</string>
+
+<string name="Kyle_2_4_1_1">
+Still kicking, huh? I guess Kabocha really went all out on your design--you’re a lot more sophisticated than his earlier attempts. He’s a lunatic, you know. Mr. Rokudou says that he lost it years ago. Kabocha is obsessed with this delusional idea that Rokudou Corp. somehow snubbed him way back in the day. It’s all fiction, of course; he’s been here so long The Source has probably taken its toll on his psyche. Still, you’re proof that at least part of his senile brain is still operating.
+</string>
+
+<string name="Kyle_2_4_1_2">
+Don’t get too comfortable, though. I meant what I said before: you get in our way and I’ll take you apart. We’re here to find The Source for Mr. Rokudou, and we’re not going to fail.
+</string>
+
+
+<string name="Kyle_2_5_1_1">
+Look, sorry if I was a little harsh with you back there. I don’t trust Kabocha and I don’t trust robots. Still, you seem pretty innocent. Does Kabocha really force you to collect pearls to keep operating? I told you he was crazy.
+</string>
+
+<string name="Kyle_2_5_1_2">
+Anyway, tell you what. You stay out of my way and I’ll try to keep my cool, alright? You make me nervous and I tend to lash out when I’m nervous. I punched a teacher once for making me stand up in front of the class, you know? It’s not my favorite personality trait, but when my blood gets going there’s not much I can do to cool down. It’s probably best if you just avoid me. <small>We’re going to find The Source first anyway, ha!</small>
+</string>
+
+<string name="Kyle_2_7_1_1">
+Mr. Rokudou is a genius, you know? He built Rokudou Corp. from the ground up to find and capture The Source. He’s sent teams here periodically since the ’50s. They all failed, of course, but as technology has improved some of them actually made it back alive.
+</string>
+
+<string name="Kyle_2_7_1_2">
+And in the mean time Mr. Rokudou spread out into charity, fast food chains, and even expensive wine. Rokudou Corp. is now one of the world’s top multinational corporations.
+</string>
+
+<string name="Kyle_2_7_1_3">
+Of course, finding The Source once and for all will cement his position as the single most powerful man in the world. He’s a man with brains and morals, someone we can trust. That’s why we have to find The Source before some lunatic like Kabocha does. For all we know he wants to start the next world war.
+</string>
+
+
+<string name="Kyle_2_8_1_1">
+You’re lucky you caught up to me when I’m in a good mood. Five minutes ago I would have cracked open your top and spit on your insides, but right now I’ve got more important things to think about. Wanda just told me we’ve found the entrance.
+</string>
+
+
+<string name="Kyle_2_8_1_2">
+There’s a whole structure below us, underground. Looks man-made, though of course it’s not. We can see it on the GPR but we’ve been looking all over for a way in. Some old notebooks we found talked about an entrance to a sewer system, and that’s what the radar seems to suggest too. Now we can actually go down there and see it.
+</string>
+
+
+<string name="Kyle_2_8_1_3">
+We’re closing in on The Source! I think it’s an artifact from an ancient civilization; something based on knowledge that humanity lost a long time ago. We’ll find out soon enough. Good luck catching up to us now, robot!
+</string>
+
+
+<string name="Kyle_2_9_1_1">
+You’re pushing your luck, robot. Keep following me and I’ll start thinking you’re a spy.
+</string>
+
+<string name="Kyle_2_9_1_2">
+The sewer entrance is just ahead. We’re getting ready to make the initial descent. My advice to you: stay up here where your chances of getting smashed are slim. We don’t know what we’re going to find down there, but it’s probably not going to be pretty.
+</string>
+
+<string name="Kyle_2_9_1_3">
+Oh, pardon me, I forgot. You can’t make decisions because you’re just a box of wires and electrical tape. Well, in that case, go ahead and follow your little procedures. If I see you down there, though, I’ll smash you myself.
+</string>
+
+
+</resources>
diff --git a/res/values/rokudou.xml b/res/values/rokudou.xml
new file mode 100644
index 0000000..c7a97e5
--- /dev/null
+++ b/res/values/rokudou.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+<!-- Rokudou Dialog Entries -->
+
+<string name="Rokudou_3_9_1_1">
+Hello there, Android. My name is Rokudou.
+</string>
+
+<string name="Rokudou_3_9_1_2">
+Ever get the feeling that you are on the wrong team, Android? Ever wonder if maybe Kabochanomizu isn’t exactly the man he portrays himself to be?
+</string>
+
+<string name="Rokudou_3_9_1_3">
+You are an amazing machine. Your talents are wasted on a washed-up researcher like Kabocha. You should join my team.
+</string>
+
+<string name="Rokudou_3_9_1_4">
+I can see that you are hesitant. Not surprising; I am sure Kabocha did his best to convince you that he’s a kind-hearted scientist, acting for the greater good of the world. Make no mistake, Android. Kabocha is a lunatic--he must not gain access to The Source.
+</string>
+
+<string name="Rokudou_3_9_1_5">
+Join my team, Android. Help us find The Source and change the world. Take some time to think it over. I am a patient man.
+</string>
+
+
+<string name="Rokudou_4_1_1_1">
+Do not trouble yourself over Kyle, Android. He was a good agent but he let his success go to his head. In the final analysis, his arrogance was his biggest weakness. You, on the other hand, displayed cunning and resourcefulness. Good show!
+</string>
+
+<string name="Rokudou_4_1_1_2">
+The Source has a way of affecting people. Other teams I sent to this island in the past have vanished without a trace. The few that made it back returned with fatal health problems. That’s why an inorganic like you is so perfect for this job. Make no mistake, The Source is a natural occurrence--nature’s next logical step. I theorize that it may actually be a tiny piece of a larger whole, a body that created the entire universe! But there will be plenty of time for academics once we have obtained The Source; for now we must concentrate on finding it.
+</string>
+
+<string name="Rokudou_4_1_1_3">
+Have you considered my offer, Android? It still stands. Join my team; help me find The Source and save the world from maniacs like Kabocha.
+</string>
+
+<string name="Rokudou_4_4_1_1">
+Android. The time for pleasantries has ended. Kabocha has The Source, and it will not be long until he learns how to channel its power. We must move quickly if we are to stop him. You are our only hope now.
+</string>
+
+<string name="Rokudou_4_7_1_1">
+My sensors indicate a large disturbance field not far from here. That must be the location of Kabocha’s secret lab. He’s surely shielded it, which means the readings I am picking up are actually a tiny fraction of the power he is generating. It must be The Source! We must move quickly if we are to stop him and his insane scheme.
+</string>
+
+<string name="Rokudou_4_7_1_2">
+Keep moving, Android. The fate of the world approaches.
+</string>
+
+<string name="Rokudou_final_boss_1_1">
+Excellent work, Android. You have penetrated Kabocha’s secret laboratory. The Source is within our grasp!
+</string>
+
+<string name="Rokudou_final_boss_1_2">
+Quickly now, put Kabocha out of his misery. He may be old and frail, but as long as he lives The Source is in jeopardy.
+</string>
+
+<string name="Rokudou_final_boss_1_3">
+Android, stop wasting time! Destroy Kabocha and we shall rule the Earth!
+</string>
+</resources>
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..2aca4da
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,324 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+<string name="app_name">Replica Island</string>
+<string name="change_level">Switch Levels</string>
+<string name="test_animation">Test Animation</string>
+<string name="test_diary">Test Diary</string>
+<string name="unlock_levels">Unlock All Levels</string>
+<string name="unlock_next_level">Unlock Next Level</string>
+<string name="method_tracing">Toggle Method Tracing</string>
+
+<string name = "game_over">GAME OVER</string>
+<string name = "thanks_for_playing">THANKS FOR PLAYING!</string>
+<string name = "please_wait">PLEASE WAIT</string>
+
+
+<!-- UI -->
+<string name="preference_save_game">Game Data</string>
+<string name="preference_erase_save_game">Erase Saved Game</string>
+<string name="preference_erase_save_game_dialog">Really erase your saved game? All of your progress will be lost!</string>
+<string name="preference_erase_save_game_dialog_title">Now wait just a minute</string>
+<string name="preference_erase_save_game_dialog_ok">Erase Save</string>
+<string name="preference_erase_save_game_dialog_cancel">Cancel</string>
+
+
+<string name="preference_game_settings">Game Settings</string>
+<string name="preference_enable_sound">Enable Sound</string>
+<string name="preference_enable_sound_summary">Disabling sound may improve performance on some devices.</string>
+
+<string name="preference_configure_controls">Configure Controls</string>
+<string name="preference_enable_click_attack">Click Attack</string>
+<string name="preference_enable_click_attack_summary">Use the trackball click or directional pad center button to attack.</string>
+<string name="preference_enable_tilt_controls">Tilt Controls</string>
+<string name="preference_enable_tilt_controls_summary">EXPERIMENTAL! Tilt the phone left and right to drive the Android around. Good for devices with no trackball or d-pad.</string>
+
+
+<string name="preference_enable_statistics">Report Play Statistics</string>
+<string name="preference_enable_statistics_summary">Sends anonymous play statistics to a server to help us make this game more fun.</string>
+
+<string name="preference_about">About</string>
+<string name="preference_about_title">About Replica Island</string>
+<string name="preference_about_summary">Replica Island was made by Chris Pruett and Genki Mine, and produced by Tom Moss.</string>
+<string name="preference_visit_site">Go to the Replica Island web site</string>
+<string name="preference_thanks_title">Special Thanks</string>
+<string name="preference_thanks_summary">Special Thanks to Adrian Havill, Casey Richardson, Jason Chen, Tim Mansfield, and the Android Team for their support of this project.</string>
+
+
+<string name="saved_game_erased_notification">Saved Game Erased</string>
+
+<string name="quit_game_dialog_title">Quit Game?</string>
+<string name="quit_game_dialog_ok">Quit</string>
+<string name="quit_game_dialog_cancel">Cancel</string>
+<string name="quit_game_dialog_message">Return to main menu?</string>
+
+<string name="nav_type">Trackball</string>
+
+<string name="whats_new_dialog_title">Replica Island</string>
+<string name="whats_new_dialog_ok">Rock on!</string>
+<string name="whats_new_dialog_message">Thanks for downloading Replica Island.\n\n
+<b>New in this version:</b>\n
+\t• Control customization (Droid users, try turning Click To Attack off in the options screen)\n
+\t• Experimental Tilt controls (for devices without a trackball or dpad)\n
+\t• Various compatibility fixes (fixed drawing problems on the Cliq).
+\n
+\n
+This is an open source project. If you are interested in how it works, or would like to use it to make your
+own games, check out the source at our web site, replicaisland.net.
+\n
+\n
+Also note that this game sends anonymous play statistics back to a server. If you’d rather not
+allow that, you can turn it off in the options screen.</string>
+
+<!-- Toast Messages -->
+<string name="memory_playback_start">MEMORY PLAYBACK START</string>
+<string name="memory_playback_complete">MEMORY PLAYBACK COMPLETE</string>
+<string name = "diary_found">FOUND OLD DIARY</string>
+
+
+<!-- names -->
+<string name = "Wanda">Wanda</string>
+<string name = "Kyle">Kyle</string>
+<string name = "Kabocha">Dr. Kabochanomizu</string>
+<string name = "Rokudou">Mr. Rokudou</string>
+<string name = "Android">Android</string>
+
+<!-- level names -->
+<string name = "level_0_1_sewer">Memory #000</string>
+<string name = "level_0_2_lab">Memory #001</string>
+<string name = "level_0_3_lab">Memory #002</string>
+
+<string name = "level_1_1_island">Memory #003</string>
+<string name = "level_1_2_island">Memory #004</string>
+<string name = "level_1_3_island">Memory #005</string>
+<string name = "level_1_4_island">Memory #006</string>
+<string name = "level_1_5_island">Memory #007</string>
+<string name = "level_1_6_island">Memory #008</string>
+<string name = "level_1_7_island">Memory #009</string>
+<string name = "level_1_8_island">Memory #010</string>
+<string name = "level_1_9_island">Memory #011</string>
+
+<string name = "level_2_1_grass">Memory #012</string>
+<string name = "level_2_2_grass">Memory #013</string>
+<string name = "level_2_3_grass">Memory #014</string>
+<string name = "level_2_4_grass">Memory #015</string>
+<string name = "level_2_5_grass">Memory #016</string>
+<string name = "level_2_6_grass">Memory #017</string>
+<string name = "level_2_7_grass">Memory #018</string>
+<string name = "level_2_8_grass">Memory #019</string>
+<string name = "level_2_9_grass">Memory #020</string>
+
+<string name = "level_3_1_sewer">Memory #021</string>
+<string name = "level_3_2_sewer">Memory #022</string>
+<string name = "level_3_3_sewer">Memory #023</string>
+<string name = "level_3_4_sewer">Memory #024</string>
+<string name = "level_0_1_sewer_kyle">Memory #024.3</string>
+<string name = "level_0_1_sewer_wanda">Memory #024.7</string>
+<string name = "level_3_5_sewer">Memory #025</string> <!-- 3-5 is the first level to take place in the "present" -->
+<string name = "level_3_6_sewer">Memory #026</string>
+<string name = "level_3_7_sewer">Memory #027</string>
+<string name = "level_3_8_sewer">Memory #028</string>
+<string name = "level_3_9_sewer">Memory #029</string>
+<string name = "level_3_10_sewer">Memory #030</string>
+<string name = "level_3_11_sewer">Memory #030.5</string> <!-- Special level for Kyle fight -->
+
+<string name = "level_4_1_underground">Memory #031</string>
+<string name = "level_4_2_underground">Memory #032</string>
+<string name = "level_4_3_underground">Memory #033</string>
+<string name = "level_4_4_underground">Memory #034</string>
+<string name = "level_4_5_underground">Memory #035</string>
+<string name = "level_4_6_underground">Memory #036</string>
+<string name = "level_4_7_underground">Memory #037</string>
+<string name = "level_4_8_underground">Memory #038</string>
+<string name = "level_4_9_underground">Memory #039</string>
+
+<string name = "level_final_boss_lab">Memory #040</string>
+
+<string name = "level_puzzle_test">Puzzle Test</string>
+<string name = "level_object_test">Object Test</string>
+<string name = "level_performance_test">Performance Test</string>
+<string name = "level_performance_test2">Performance Test 2</string>
+<string name = "level_performance_test3">Performance Test 3</string>
+<string name = "level_npc_motion_test">NPC Motion Test</string>
+
+<!-- Level time codes -->
+
+<!-- level names -->
+<string name = "level_0_1_time">+ 07:12:03</string>
+<string name = "level_0_2_time">+ 00:00:00</string>
+<string name = "level_0_3_time">+ 00:06:26</string>
+
+<string name = "level_1_1_time">+ 00:34:10</string>
+<string name = "level_1_2_time">+ 00:44:46</string>
+<string name = "level_1_3_time">+ 00:52:16</string>
+<string name = "level_1_4_time">+ 00:59:01</string>
+<string name = "level_1_5_time">+ 01:07:24</string>
+<string name = "level_1_6_time">+ 01:23:38</string>
+<string name = "level_1_7_time">+ 01:38:04</string>
+<string name = "level_1_8_time">+ 01:42:00</string>
+<string name = "level_1_9_time">+ 01:56:44</string>
+
+<string name = "level_2_1_time">+ 02:25:18</string>
+<string name = "level_2_2_time">+ 02:37:20</string>
+<string name = "level_2_3_time">+ 02:40:13</string>
+<string name = "level_2_4_time">+ 02:51:09</string>
+<string name = "level_2_5_time">+ 03:01:55</string>
+<string name = "level_2_6_time">+ 03:16:28</string>
+<string name = "level_2_7_time">+ 03:33:12</string>
+<string name = "level_2_8_time">+ 03:46:07</string>
+<string name = "level_2_9_time">+ 03:54:29</string>
+
+<string name = "level_3_1_time">+ 04:07:10</string>
+<string name = "level_3_2_time">+ 04:18:42</string>
+<string name = "level_3_3_time">+ 04:45:15</string>
+<string name = "level_3_4_time">+ 04:59:06</string>
+<string name = "level_3_5_time">+ 07:19:59</string>
+<string name = "level_3_6_time">+ 07:36:17</string>
+<string name = "level_3_7_time">+ 07:51:24</string>
+<string name = "level_3_8_time">+ 08:16:55</string>
+<string name = "level_3_9_time">+ 08:35:12</string>
+<string name = "level_3_10_time">+ 08:50:36</string>
+<string name = "level_3_11_time">+ 08:55:54</string>
+
+<string name = "level_4_1_time">+ 09:10:01</string>
+<string name = "level_4_2_time">+ 09:42:40</string>
+<string name = "level_4_3_time">+ 09:58:11</string>
+<string name = "level_4_4_time">+ 10:27:36</string>
+<string name = "level_4_5_time">+ 10:52:10</string>
+<string name = "level_4_6_time">+ 11:12:35</string>
+<string name = "level_4_7_time">+ 11:39:04</string>
+<string name = "level_4_8_time">+ 11:57:22</string>
+<string name = "level_4_9_time">+ 12:13:40</string>
+<string name = "level_final_boss_time">+ 12:45:12</string>
+<string name = "level_test_time">(test level)</string>
+
+<!-- diary entries -->
+
+<string name="Diary1">
+Log Entry - Thursday - Sunny\n
+I have arrived! I borrowed a dinghy from the freighter and rowed to shore as the ship passed by the island early this morning. The captain has promised to return for me in two weeks; more than enough time to complete my mission.
+\n
+\n
+I find myself standing in the middle of a vast beach, with sand dunes stretching great distances down the coastline and the green of tropical forest visible near the horizon. An uninformed man would think this place a paradise. I know better. Still, I cannot help but marvel at the island’s wholly unnatural beauty.
+</string>
+
+<string name="Diary2">
+Log Entry - Sunday - Sunny\n
+Before leaving the mainland I met with Mr. Rokudou about my mission. He is a great man, the type of man who might actually effect change rather than just talk about it. I was happy to see that he recognized my rather unique skills. I am neither a survivalist nor an outdoorsman; I cannot even remember the last time I went camping. It was very shrewd of Mr. Rokudou to select me as the right person to visit this odd, deserted island. I shall not let him down.
+
+</string>
+
+<string name="Diary3">
+Log Entry - ??? - Dark\n
+Analysis of slime-like creature. Resembles an arthropod except instead of protective armor the beast seems to be covered in a soft, sticky substance resembling tar. Suspect genetic similarities to Holothuroidea, but the island’s influence has twisted the result into a dangerous menace. I ate a piece of the specimen and gagged.\n
+\n
+I never should have come here. Rokudou tricked me into this, and now I cannot even find my way back to the surface. The freighter has surely long since come and gone; my sense of time is quickly losing its clarity but I am sure that more than two weeks have passed since I arrived in this hellish place. I never should have come here.\n
+
+</string>
+
+<string name="Diary4">
+Log Entry - Monday - Overcast\n
+I met a group of three adventurers today. I thought that I had the island all to myself, and I was so surprised to hear human voices (with Brooklyn accents, no less!) outside my shelter that I knocked over a bamboo support and almost brought the roof down. The group is independent but they are after The Source, just like me. They were nice but I sensed a little bit of competitive tension in the air. They have been here for a month already and have barely penetrated the forest beyond the beach. I am glad to have met them but they pose no immediate threat to my mission. They are fine folks but my skills are far superior.\n
+
+</string>
+
+<string name="Diary5">
+Log Entry - Saturday - Rain\n
+Today marks three consecutive days of rain. Last week was clear--the seas were calm and my carefully constructed shelter proved unnecessary. I am sitting in a small alcove at the base of a hollow tree as the sky dumps golf ball-sized raindrops into the forest around me. The beach is long behind me now and the sound of the ocean is just a memory.\n
+\n
+This is a weird place. The oddities increase as I move closer to the center. Since entering the forest I have seen a few things I cannot explain. No matter, my mission is clear and success is guaranteed. I will sleep here tonight and then venture further towards the island’s center tomorrow.\n
+
+</string>
+
+<string name="Diary6">
+Log Entry - ??? - Damp\n
+What in the world is a sewer system doing under a tropical island anyway? The island has grown it to resemble the architecture of some old European city, but for what purpose? What little tidbit of inspiration caused this vast underground structure to manifest? Perhaps a photograph, or a child’s picture book. The island itself is too far from any country with this sort of architecture for the influence to be direct.\n
+\n
+When I get out of here I am going to find Rokudou and educate him in the error of his ways. The Source is getting closer, but I am not sure I want to find it anymore.\n
+
+</string>
+
+<string name="Diary7">
+There is no rain underground!\n
+\n
+Rain, rain, go away, come again some other day.\n
+\n
+All the king’s horses and all the king’s men couldn’t put Humpty together again.\n
+
+</string>
+
+<string name="Diary8">
+Log Entry - Wednesday - Overcast\n
+I may have found a path into the forest. The grasslands stretch out ahead in what appears to be a small glade, but I think that beyond them the border of the forest will thin. If my calculations are correct, this side of the island should be shady most of the year, so I expect that the almost impenetrable forest vegetation will yield enough to permit traversal.\n
+\n
+The adventuring party I met a few days ago has been here a month and has not yet figured this out. My intellect is truly superior--I can understand why Mr. Rokudou chose me for this mission. Once I find The Source and show him how to use it, he will surely make me his second-in-command, a position from which I shall have incredible influence. Just thinking about it makes me giddy. But I must not to get ahead of myself; the forest looms.\n
+
+</string>
+
+<string name="Diary9">
+Skin skin skin skin skin skin skin skin skin skin skin.\n
+\n
+There’s not enough skin on those pirates!\n
+</string>
+
+<string name="Diary10">
+Log Entry - Monday (?) - Rain\n
+My progress has been arrested by the insane creatures that inhabit this island. Some of them are so normal they might have migrated here after the island was formed. But the rest are clearly products of The Source running unchecked; they do not bear even the slightest resemblance to the children of Mother Nature. This island is a facsimile, a replica of bits and pieces of the world, twisted and grown from nothing, sprouting in every direction. It is power in its purest form, writhing in the sea without tether.\n
+\n
+But with a guiding hand, that power could be put to such great use! With The Source in our hands mankind could shape the world as we see fit; it would mean a true end to war and poverty. Rokudou wants that power--I want that power--so the world can be freed from governments and other parasites that seek only to oppress and destroy. Glory is almost within my grasp.\n
+
+</string>
+
+<string name="Diary11">
+Log Entry - I’ve lost track of the date - Heavy Rain\n
+\n
+The adventurers I met are dead. I found them hanging from the trees near a rock formation in the forest. The discovery was startling and sad; though we were both competing for the same goal I felt a certain camaraderie with them. They were, of course, the only other humans on the island.\n
+\n
+Though tragic, I cannot let their unfortunate end distract me from my goal. The rock formation near the bodies is an important find; it appears to be the mouth of some sort of cave. However, most exciting is that the rock itself looks as if it has been cut by man; I dare say that it resembles an entrance to a subway station or bunker.\n
+\n
+I will venture into the forest once more before entering the cave. At the adventurers’ base camp I spied batteries and other useful electronic equipment, which I can surely modify. Once I have procured those items I shall return and make my descent.\n
+
+</string>
+
+<string name="Diary12">
+Log Entry - Unknown - Underground\n
+\n
+My head hurts. Maybe it is the fumes down here; I feel like my head is filling up with pressure and soon it will burst. When the pain gets bad I sit down and focus on counting primes, an old childhood relaxation habit that has resurfaced here under the ground.\n
+\n
+When I find The Source, I am going to use it to cure myself of whatever it is that has taken hold of me. If Rokudou complains then maybe I’ll just eject him from the planet. After all, he’s not the one down here in this false sewer, with scraped knees and a pounding headache. He’s not the one who has to go days at a time without food because there isn’t any vegetation down here. I’m the one doing the work, so I’m the one who should get the reward. The Source will be mine and mine alone.\n
+\n
+2, 3, 5, 7, 11, 13, 17, 19, 23, 29\n
+
+</string>
+
+<string name="Diary13">
+He lied he lied he lied he lied. Rokudou is a liar. He seeks only to control. He says he wants The Source to save the world, to rid it of the fat cats who play the planet like a marionette. He he he HE is the fat cat, the puppeteer. He shall not have The Source. It shall be mine. Mine mine mine mine mine. 73, 79, 83, 89, 97, 101, 103, 107, 109, 113.\n
+\n
+The Source is here. It is down here, in the dark. I can feel its heat, a warmth like the sun against my cheek. But no, I mustn't go to it, not not not not yet. If I find it, Rokudou will take it from me and it will be check check check check mate. No, Kabochanomizu is cautious. Kabochanomizu is careful. I shall retreat to the surface. I know enough now to build things. I will return only when I can guarantee my success. Rokudou will grovel before me and the world will know Kabochanomizu as its savior.\n
+</string>
+
+<string name="Diary14">
+Log Entry - Saturday - Very Cloudy\n
+\n
+I have to admit that I was surprised when Rokudou selected me to find The Source. True, I have studied the island from afar as much as any other researcher in the world, but slacks and a lab coat are hardly appropriate for rugged terrain. When Rokudou offered me the job I was initially suspicious, but now I see that I am the only man with any chance of success. The island has grown unhindered for a hundred years, and its twisted derivations of reality are the type of thing that could drive a lesser man insane.\n
+\n
+Still, I can’t say that I completely trust Rokudou. He’s a smart man, but I am also sure that he selected me because he thought me easy to control. I get the feeling that he’s watching me over my shoulder. Of course, the island is free of human habitation save me and the three adventurers I met a few days ago, so the presence of a spy is unlikely. And yet, I can’t shake this feeling of being watched.\n
+
+</string>
+
+<string name="Diary15">
+Log Entry - The days have run together - Sunny\n
+\n
+There is something I must confide, even if only to this scrap of notebook paper.\n
+\n
+I killed those adventurers. I killed them in their sleep and then hung them from the trees as if to convince myself that the island had somehow done away with them. It was grisly business, but necessary. I needed their supplies, especially the batteries and flashlights they brought. More importantly, against all odds they discovered the entrance to the cave. They must have blundered across it while awkwardly stumbling through the forest--their brains are too small to have calculated its location. The Source is too important to leave to such simpletons, and I couldn’t risk the possibility, no matter how remote, that they might find it first.\n
+\n
+Am I a monster? I do not think so. I am Dr. Kabochanomizu, widely respected scientist and the world’s expert on the inverted Atlantis that is this island. My actions are logical and justified--I am no monster.\n
+\n
+If I am honest with myself, there is another reason the adventurers had to die. I wanted to do it. And I enjoyed it.
+</string>
+
+<!-- Misc Dialog -->
+
+<string name="KyleDeadNote">It’s Kyle. He’s not moving.</string>
+
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..41e1368
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="Theme.ConversationDialog" parent="android:style/Theme.Dialog">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowBackground">@drawable/dialog_box</item>
+ </style>
+
+</resources>
\ No newline at end of file
diff --git a/res/values/wanda.xml b/res/values/wanda.xml
new file mode 100644
index 0000000..e58a644
--- /dev/null
+++ b/res/values/wanda.xml
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+<!-- Wanda Dialog Entries -->
+
+<string name="Wanda_0_1_1_1">
+Oh no!
+</string>
+
+<string name="Wanda_0_1_1_2">
+Looks like he landed OK but his memory array is all smashed. Kyle has really crossed the line this time!
+</string>
+
+<string name="Wanda_0_1_1_3">
+Hmm, his self-repair mechanism is still intact. If I can just reconnect the power...
+</string>
+
+<string name="Wanda_3_5_1_1">
+Are you feeling better Mr. Android? Your memory is probably still scrambled, but don’t worry--it will return with time as your repair system brings the backup content out of storage. Frankly, I think it’s amazing that you’re still moving around after taking such a powerful hit.
+</string>
+
+<string name="Wanda_3_5_1_2">
+When your memories come back online you should access them to remind yourself what you’ve been up to until now. The repair system is going to take a while to decompress all your previous memories, so they might not come on line in chronological order. They’ll all return eventually though.
+</string>
+
+<string name="Wanda_3_5_1_3">
+Stay away from Kyle if you see him. He messed you up pretty bad before and I think he’s likely to do it again. He’s such an idiot sometimes.
+</string>
+
+<string name="Wanda_1_1_1_1">
+Why hello there! You’re not another one of this island’s crazy creations, are you? Let me guess: you must be the handiwork of Dr. Kabochanomizu. We’re supposed to be on opposing teams but I personally don’t see what all the fuss is about. If we find it first then it’s ours, if you find it first then it’s yours.
+</string>
+
+<string name="Wanda_1_1_1_2">
+Oh, I’m Wanda, by the way. Nice to meet you. Can I call you Mr. Robot? <small>Nah, that sounds like a line from an ’80s song.</small> How about Mr. Android? That’s better. It’s so nice to make your acquaintance, Mr. Android.
+</string>
+
+<string name="Wanda_1_1_1_3">
+Good luck finding The Source. My team is looking for it too, but so far no luck. Enjoy the island, but watch out: it’s pretty weird in some parts.
+</string>
+
+
+<string name="Wanda_3_7_1_1">
+Hey there, Mr. Android, looks like you’re recovering pretty quickly! Have you remembered my name yet?
+</string>
+
+<string name="Wanda_3_7_1_2">
+This sewer is the pits, huh? I mean, how can there even be a sewer here? It’s not like there are any buildings to connect it to. The things that are here, they all grew here, I guess. Mr. Rokudou says it’s just The Source doing what it does, but it gives me the creeps. I think there’s another, older structure even deeper underground, but we haven’t found an entrance yet. I almost hope we don’t.
+</string>
+
+<string name="Wanda_3_7_1_3">
+Oh, by the way, Kyle’s still on the warpath and he found out you’re still operative. You might want to watch your back.
+</string>
+
+
+<string name="Wanda_1_5_1_1">
+We meet again, Mr. Android! How’s your search going? We’ve almost finished our scan of the beach, but I’m not supposed to tell you about it. Mr. Rokudou and Dr. Kabochanomizu don’t think too highly of each other, I gather, so technically you’re a business rival. Don’t worry though, I like you.
+</string>
+
+<string name="Wanda_1_5_1_2">
+Mr. Rokudou is a pretty amazing guy. I mean, he’s like the head of this giant corporation and he sort of sounds like a robot when he talks <small>(no offense!)</small>, but he’s put all this energy into finding The Source so he can rid the world of disease, hunger, and war. That’s such a noble aspiration, particularly for a businessman, don’t you think?
+</string>
+
+<string name="Wanda_1_5_1_3">
+Kyle and I are the most recent search team, but Mr. Rokudou has been sending people here for years. I’m sure we’ll be the ones who finally find The Source.
+</string>
+
+<string name="Wanda_1_5_1_4">
+Unless, of course, you find it first!
+</string>
+
+
+<string name="Wanda_3_11_1_1">
+No, you two, stop! There’s no need to fight about this!
+</string>
+
+<string name="Wanda_3_11_2_1">
+No!
+</string>
+
+<string name="Wanda_4_1_1_1">
+Get out of my sight. You rotten, murdering, good for nothing bucket of bolts. I never want to see you again. I will never forgive you for what you did to Kyle.
+</string>
+
+
+<string name="Wanda_1_6_1_1">
+My partner, Kyle, is around here somewhere. You’ll probably run into him. I should warn you though, he’s kind of full of himself. And he’s really competitive--this isn’t just a job to him, it’s like a test of his manhood or something.
+</string>
+
+<string name="Wanda_1_6_1_2">
+But once you get to know him, he’s a pretty nice guy. Just don’t get on his bad side.
+</string>
+
+
+<string name="Wanda_4_2_1_1">
+Did you even think about what you were doing? Did you even consider that maybe, just maybe, there was some other way to resolve the situation? Or do you just calculate the shortest possible path to your objective and then iterate until it’s complete? Just follow the rules by rote, never considering whether or not your way is the best way?
+</string>
+
+<string name="Wanda_4_2_1_2">
+Kyle was my friend, but to you he was just an obstacle to overcome. You probably didn’t even think of him as a person, just another in a long line of hurdles on the way to the finish line. I can’t believe I trusted a machine.
+</string>
+
+<string name="Wanda_2_6_1_1">
+Hey Mr. Android, are you stuck? Here, I'll clear a path for you.
+</string>
+
+<string name="Wanda_2_6_2_1">
+Mr. Rokudou told us that this whole island, every little bit of it, is something that The Source manufactured. The eggheads don’t know why it makes stuff, or how it chooses to grow the things it does, but they think it has something to do with influences from outside sources. <small>Mr. Rokudou calls it “proximity influence remanifestation,” whatever that means</small>.
+</string>
+
+<string name="Wanda_2_6_2_2">
+Anyway, nobody knows exactly where The Source comes from, but it hasn’t been here for very long. This island is less than two hundred years old, and people have been visiting it for at least the last fifty. I’ve seen evidence of early explorers--old diaries and stuff. But nobody knows what The Source is doing, or who made it, or why it’s sitting out here in the middle of the ocean, growing this weirdo island.
+</string>
+
+<string name="Wanda_2_6_2_3">
+Personally, my money is on the space theory. I bet it’s some sort of alien artifact that crash-landed here a long time ago.
+</string>
+
+<string name="Wanda_4_5_1_1">
+Look, Android, I know you’re in a rough spot. Kabocha has lost it, and now he has The Source, so somebody has to stop him. I can see you’re are trying to do that.
+</string>
+
+<string name="Wanda_4_5_1_2">
+But that doesn’t make up for what you did to Kyle. Maybe you didn’t know what you were doing. Or maybe Kabocha manipulated you into thinking it was the right thing to do. I don’t know. But I will never trust you again.
+</string>
+
+<string name="Wanda_4_5_1_3">
+Let me just say this. I don’t trust Rokudou either. He could have saved Kyle but for some reason he didn’t. I’m starting to think he’s not the man I thought he was. I know he’s helping you out now but I’d watch my back if I were in your shoes.
+</string>
+
+<string name="Wanda_3_3_1_1">
+So you met Kyle, huh? I told you he can be a bit of a jerk, right? He and I grew up together, and he’s always been kind of arrogant, but getting this assignment from Mr. Rokudou really pumped up his ego. He’s actually a really nice guy under those dark glasses. Last year he wrote me a song for my birthday, if you can believe that.
+</string>
+
+<string name="Wanda_3_3_1_2">
+Kyle also got me this job. I have to admit, I was pretty hesitant to go to work for a giant company like Rokudou Corp. at first.
+</string>
+
+<string name="Wanda_3_3_1_3">
+But Mr. Rokudou’s different. I met him and even though he refuses to take off that weird mask <small>(is it zits? uncontrollable facial hair? maybe a scar?)</small> I am convinced that he’s really trying to make the world a better place.
+</string>
+
+
+
+<string name="Wanda_4_7_1_1">
+Android. I wanted to tell you about the latest information I have on Rokudou. I hacked the corporate network and had a look at Rokudou’s sensitive files. There’s still a lot I don’t have access to, but what I’ve seen is enough.
+</string>
+
+<string name="Wanda_4_7_1_2">
+He’s a criminal mastermind. The whole businessman-with-a-heart-of-gold shtick is just for show. Secretly he’s built Rokudou Corp. by investing in all sorts of illegal black markets. His main source of income seems to come from stripping natural resources from impoverished and war-torn nations.
+</string>
+
+<string name="Wanda_4_7_1_3">
+I can’t imagine what he’ll do if he gets his hands on The Source. He may actually believe he can use it to make the world a better place, but surely it will only be a better place for him--the rest of us will suffer.
+</string>
+
+<string name="Wanda_4_7_1_4">
+I know Kabocha is completely unstable and you’re trying to stop him by helping Rokudou, but either way I think the world as we know it is going to end. What should we do?
+</string>
+
+
+<string name="Wanda_1_9_1_1">
+I guess we’re actually pretty lucky nobody has found The Source so far. I mean, in the wrong hands it could probably be used as a powerful weapon. Mr. Rokudou once said that with The Source you could grow an army that would cover the earth. It’s a good thing he’s planning on using it to reduce green house gases and repopulate endangered species instead! I’m sure Dr. Kabochanomizu has a similar goal, right?
+</string>
+
+<string name="Wanda_2_9_1_1">
+Despite all of the weird bugs and stuff here, I actually kind of like this island. It’s like a theme park: just weird enough to be fun but just normal enough to pass as natural. Kyle was telling me if we find The Source, we’ll be able to control it and make it grow whatever we want.
+</string>
+
+<string name="Wanda_2_9_1_2">
+But a part of me doesn’t want to find it; I kind of like how this island has grown this way on its own, without some person directing its focus. Like, it doesn’t need to be manipulated to create something beautiful.
+</string>
+
+
+<string name="Wanda_4_9_1_1">
+Android. You have to make a choice. If you don’t stop Kabocha, he’s going to transform life on this planet in ways that can’t possibly be good. But if you help Rokudou, the world will be forced to succumb to his will. This is a lose-lose situation.
+</string>
+
+<string name="Wanda_4_9_1_2">
+You’re just a tool to these guys--they have their own personal vendettas and they are just using you as a means to an end. There must be something else you can do to prevent either of them from getting their hands on The Source.
+</string>
+
+<string name="Wanda_4_9_1_3">
+Look, I was wrong about you before. I know what happened to Kyle wasn’t your fault alone. I know you’re trying to do the right thing. But neither Rokudou nor Kabocha is fit to wield the power of The Source. You need to make a choice, Android, and you need to make it very carefully.
+</string>
+
+</resources>
diff --git a/res/xml/level_0_1_dialog_wanda.xml b/res/xml/level_0_1_dialog_wanda.xml
new file mode 100644
index 0000000..5ccb0a6
--- /dev/null
+++ b/res/xml/level_0_1_dialog_wanda.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_surprised"
+ text = "@string/Wanda_0_1_1_1"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_0_1_1_2"
+ title = "@string/Wanda"
+ />
+
+ <page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_0_1_1_3"
+ title = "@string/Wanda"
+ />
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_0_2_dialog_kabocha.xml b/res/xml/level_0_2_dialog_kabocha.xml
new file mode 100644
index 0000000..7b28bcf
--- /dev/null
+++ b/res/xml/level_0_2_dialog_kabocha.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+<page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_1_1"
+ title = "@string/Kabocha"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_1_2"
+ title = "@string/Kabocha"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_1_3"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_2_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_2_2"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_2_3"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_3_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_3_2"
+ title = "@string/Kabocha"
+/>
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_4_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_4_2"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_4_3"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_5_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_5_2"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_5_3"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_0_2_dialog_kabocha_2.xml b/res/xml/level_0_2_dialog_kabocha_2.xml
new file mode 100644
index 0000000..3ba87c2
--- /dev/null
+++ b/res/xml/level_0_2_dialog_kabocha_2.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_6_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_6_2"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_2_6_3"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+</dialog>
diff --git a/res/xml/level_0_3_dialog_kabocha.xml b/res/xml/level_0_3_dialog_kabocha.xml
new file mode 100644
index 0000000..b7571c2
--- /dev/null
+++ b/res/xml/level_0_3_dialog_kabocha.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+<page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_1_1"
+ title = "@string/Kabocha"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_1_2"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_2_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_2_2"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_2_3"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_3_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_3_2"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_3_3"
+ title = "@string/Kabocha"
+/>
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_4_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_4_2"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+<conversation>
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_5_1"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_5_2"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_5_3"
+ title = "@string/Kabocha"
+/>
+
+ <page
+ image="@drawable/kabocha_closeup_normal"
+ text = "@string/Kabocha_0_3_5_4"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_1_1_dialog_wanda.xml b/res/xml/level_1_1_dialog_wanda.xml
new file mode 100644
index 0000000..081e73b
--- /dev/null
+++ b/res/xml/level_1_1_dialog_wanda.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_surprised"
+ text = "@string/Wanda_1_1_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_1_1_1_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_1_1_1_3"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_1_5_dialog_wanda.xml b/res/xml/level_1_5_dialog_wanda.xml
new file mode 100644
index 0000000..70f5d93
--- /dev/null
+++ b/res/xml/level_1_5_dialog_wanda.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_1_5_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_1_5_1_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_1_5_1_3"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_happy"
+ text = "@string/Wanda_1_5_1_4"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_1_6_dialog_wanda.xml b/res/xml/level_1_6_dialog_wanda.xml
new file mode 100644
index 0000000..d003dd5
--- /dev/null
+++ b/res/xml/level_1_6_dialog_wanda.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+
+<conversation>
+ <page
+ image="@drawable/wanda_happy"
+ text = "@string/Wanda_1_6_1_1"
+ title = "@string/Wanda"
+ />
+
+ <page
+ image="@drawable/wanda_happy"
+ text = "@string/Wanda_1_6_1_2"
+ title = "@string/Wanda"
+ />
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_1_9_dialog_wanda.xml b/res/xml/level_1_9_dialog_wanda.xml
new file mode 100644
index 0000000..c892c6d
--- /dev/null
+++ b/res/xml/level_1_9_dialog_wanda.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_surprised"
+ text = "@string/Wanda_1_9_1_1"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_2_1_dialog_kyle.xml b/res/xml/level_2_1_dialog_kyle.xml
new file mode 100644
index 0000000..2e0a0e4
--- /dev/null
+++ b/res/xml/level_2_1_dialog_kyle.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_1_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_1_1_2"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_1_1_3"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_2_3_dialog_kyle.xml b/res/xml/level_2_3_dialog_kyle.xml
new file mode 100644
index 0000000..8eb1db0
--- /dev/null
+++ b/res/xml/level_2_3_dialog_kyle.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_angry"
+ text = "@string/Kyle_2_3_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_3_1_2"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_3_1_3"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_2_4_dialog_kyle.xml b/res/xml/level_2_4_dialog_kyle.xml
new file mode 100644
index 0000000..a205fcd
--- /dev/null
+++ b/res/xml/level_2_4_dialog_kyle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_4_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_4_1_2"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_2_5_dialog_kyle.xml b/res/xml/level_2_5_dialog_kyle.xml
new file mode 100644
index 0000000..f979f6b
--- /dev/null
+++ b/res/xml/level_2_5_dialog_kyle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_noglasses"
+ text = "@string/Kyle_2_5_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_noglasses"
+ text = "@string/Kyle_2_5_1_2"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_2_6_dialog_wanda.xml b/res/xml/level_2_6_dialog_wanda.xml
new file mode 100644
index 0000000..b64c245
--- /dev/null
+++ b/res/xml/level_2_6_dialog_wanda.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_2_6_1_1"
+ title = "@string/Wanda"
+/>
+</conversation>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_2_6_2_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_2_6_2_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_2_6_2_3"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+
+
+</dialog>
diff --git a/res/xml/level_2_7_dialog_kyle.xml b/res/xml/level_2_7_dialog_kyle.xml
new file mode 100644
index 0000000..772c8aa
--- /dev/null
+++ b/res/xml/level_2_7_dialog_kyle.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_7_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_7_1_2"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_7_1_3"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_2_8_dialog_kyle.xml b/res/xml/level_2_8_dialog_kyle.xml
new file mode 100644
index 0000000..9c1a28c
--- /dev/null
+++ b/res/xml/level_2_8_dialog_kyle.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_noglasses"
+ text = "@string/Kyle_2_8_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_noglasses"
+ text = "@string/Kyle_2_8_1_2"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_noglasses"
+ text = "@string/Kyle_2_8_1_3"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_2_9_dialog_kyle.xml b/res/xml/level_2_9_dialog_kyle.xml
new file mode 100644
index 0000000..1073f79
--- /dev/null
+++ b/res/xml/level_2_9_dialog_kyle.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_noglasses"
+ text = "@string/Kyle_2_9_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_2_9_1_2"
+ title = "@string/Kyle"
+/>
+
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_2_9_dialog_wanda.xml b/res/xml/level_2_9_dialog_wanda.xml
new file mode 100644
index 0000000..8ed2cab
--- /dev/null
+++ b/res/xml/level_2_9_dialog_wanda.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_2_9_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_happy"
+ text = "@string/Wanda_2_9_1_2"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_3_10_dialog_kyle.xml b/res/xml/level_3_10_dialog_kyle.xml
new file mode 100644
index 0000000..6acf7bc
--- /dev/null
+++ b/res/xml/level_3_10_dialog_kyle.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_angry"
+ text = "@string/Kyle_3_10_1_1"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_3_11_dialog_wanda.xml b/res/xml/level_3_11_dialog_wanda.xml
new file mode 100644
index 0000000..9b8bf32
--- /dev/null
+++ b/res/xml/level_3_11_dialog_wanda.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+<conversation>
+
+<page
+ image="@drawable/wanda_surprised"
+ text = "@string/Wanda_3_11_1_1"
+ title = "@string/Wanda"
+/>
+
+
+</conversation>
+
+<conversation>
+<page
+ image="@drawable/wanda_surprised"
+ text = "@string/Wanda_3_11_2_1"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_3_3_dialog_wanda.xml b/res/xml/level_3_3_dialog_wanda.xml
new file mode 100644
index 0000000..ff291a3
--- /dev/null
+++ b/res/xml/level_3_3_dialog_wanda.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+<conversation>
+
+<page
+ image="@drawable/wanda_happy"
+ text = "@string/Wanda_3_3_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_3_3_1_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_3_3_1_3"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_3_4_dialog_kyle.xml b/res/xml/level_3_4_dialog_kyle.xml
new file mode 100644
index 0000000..fba2a81
--- /dev/null
+++ b/res/xml/level_3_4_dialog_kyle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_angry"
+ text = "@string/Kyle_3_4_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_angry"
+ text = "@string/Kyle_3_4_1_2"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_3_5_dialog_wanda.xml b/res/xml/level_3_5_dialog_wanda.xml
new file mode 100644
index 0000000..99560a9
--- /dev/null
+++ b/res/xml/level_3_5_dialog_wanda.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+<conversation>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_3_5_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_3_5_1_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_3_5_1_3"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_3_7_dialog_wanda.xml b/res/xml/level_3_7_dialog_wanda.xml
new file mode 100644
index 0000000..bd5e9c6
--- /dev/null
+++ b/res/xml/level_3_7_dialog_wanda.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_3_7_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_3_7_1_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_smile"
+ text = "@string/Wanda_3_7_1_3"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_3_8_dialog_kyle.xml b/res/xml/level_3_8_dialog_kyle.xml
new file mode 100644
index 0000000..ed6f509
--- /dev/null
+++ b/res/xml/level_3_8_dialog_kyle.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_3_8_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_3_8_1_2"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_3_8_1_3"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_3_8_2_1"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+
+</dialog>
diff --git a/res/xml/level_3_9_dialog_kyle.xml b/res/xml/level_3_9_dialog_kyle.xml
new file mode 100644
index 0000000..d88b29f
--- /dev/null
+++ b/res/xml/level_3_9_dialog_kyle.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/kyle_closeup_neutral"
+ text = "@string/Kyle_3_9_1_1"
+ title = "@string/Kyle"
+/>
+
+<page
+ image="@drawable/kyle_closeup_angry"
+ text = "@string/Kyle_3_9_1_2"
+ title = "@string/Kyle"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_3_9_dialog_rokudou.xml b/res/xml/level_3_9_dialog_rokudou.xml
new file mode 100644
index 0000000..f029664
--- /dev/null
+++ b/res/xml/level_3_9_dialog_rokudou.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_3_9_1_1"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_3_9_1_2"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_3_9_1_3"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_3_9_1_4"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_3_9_1_5"
+ title = "@string/Rokudou"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_1_dialog_rokudou.xml b/res/xml/level_4_1_dialog_rokudou.xml
new file mode 100644
index 0000000..4c5c84a
--- /dev/null
+++ b/res/xml/level_4_1_dialog_rokudou.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ text = "@string/KyleDeadNote"
+/>
+
+</conversation>
+
+<conversation>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_4_1_1_1"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_4_1_1_2"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_4_1_1_3"
+ title = "@string/Rokudou"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_1_dialog_wanda.xml b/res/xml/level_4_1_dialog_wanda.xml
new file mode 100644
index 0000000..8d417e7
--- /dev/null
+++ b/res/xml/level_4_1_dialog_wanda.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_1_1_1"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_1_misc.xml b/res/xml/level_4_1_misc.xml
new file mode 100644
index 0000000..ce6f516
--- /dev/null
+++ b/res/xml/level_4_1_misc.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ text = "@string/KyleDeadNote"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_2_dialog_wanda.xml b/res/xml/level_4_2_dialog_wanda.xml
new file mode 100644
index 0000000..731c849
--- /dev/null
+++ b/res/xml/level_4_2_dialog_wanda.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_2_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_2_1_2"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_3_dialog_kabocha.xml b/res/xml/level_4_3_dialog_kabocha.xml
new file mode 100644
index 0000000..29ac045
--- /dev/null
+++ b/res/xml/level_4_3_dialog_kabocha.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+<page
+ image="@drawable/kabocha_closeup_concern"
+ text = "@string/Kabocha_4_3_1_1"
+ title = "@string/Kabocha"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_lunatic"
+ text = "@string/Kabocha_4_3_1_2"
+ title = "@string/Kabocha"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_lunatic_02"
+ text = "@string/Kabocha_4_3_1_3"
+ title = "@string/Kabocha"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_concern"
+ text = "@string/Kabocha_4_3_1_4"
+ title = "@string/Kabocha"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_4_dialog_rokudou.xml b/res/xml/level_4_4_dialog_rokudou.xml
new file mode 100644
index 0000000..00efe85
--- /dev/null
+++ b/res/xml/level_4_4_dialog_rokudou.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_4_4_1_1"
+ title = "@string/Rokudou"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_5_dialog_wanda.xml b/res/xml/level_4_5_dialog_wanda.xml
new file mode 100644
index 0000000..dbfb99c
--- /dev/null
+++ b/res/xml/level_4_5_dialog_wanda.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_5_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_5_1_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_5_1_3"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_7_dialog_rokudou.xml b/res/xml/level_4_7_dialog_rokudou.xml
new file mode 100644
index 0000000..3063776
--- /dev/null
+++ b/res/xml/level_4_7_dialog_rokudou.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_4_7_1_1"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/rokudou_closeup_normal"
+ text = "@string/Rokudou_4_7_1_2"
+ title = "@string/Rokudou"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_7_dialog_wanda.xml b/res/xml/level_4_7_dialog_wanda.xml
new file mode 100644
index 0000000..674d2b9
--- /dev/null
+++ b/res/xml/level_4_7_dialog_wanda.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_7_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_7_1_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_7_1_3"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_7_1_4"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_4_9_dialog_wanda.xml b/res/xml/level_4_9_dialog_wanda.xml
new file mode 100644
index 0000000..f8f2ba5
--- /dev/null
+++ b/res/xml/level_4_9_dialog_wanda.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_9_1_1"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_9_1_2"
+ title = "@string/Wanda"
+/>
+
+<page
+ image="@drawable/wanda_sad"
+ text = "@string/Wanda_4_9_1_3"
+ title = "@string/Wanda"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_final_boss_dialog.xml b/res/xml/level_final_boss_dialog.xml
new file mode 100644
index 0000000..12b2d1f
--- /dev/null
+++ b/res/xml/level_final_boss_dialog.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<dialog>
+
+<conversation>
+
+<page
+ image="@drawable/rokudou_closeup_mask"
+ text = "@string/Rokudou_final_boss_1_1"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/rokudou_closeup_mask"
+ text = "@string/Rokudou_final_boss_1_2"
+ title = "@string/Rokudou"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_lunatic"
+ text = "@string/Kabocha_final_boss_1_1"
+ title = "@string/Kabocha"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_concern"
+ text = "@string/Kabocha_final_boss_1_2"
+ title = "@string/Kabocha"
+/>
+
+<page
+ image="@drawable/kabocha_closeup_lunatic_02"
+ text = "@string/Kabocha_final_boss_1_3"
+ title = "@string/Kabocha"
+/>
+
+
+<page
+ image="@drawable/rokudou_closeup_mask"
+ text = "@string/Rokudou_final_boss_1_3"
+ title = "@string/Rokudou"
+/>
+
+</conversation>
+
+</dialog>
diff --git a/res/xml/level_tree.xml b/res/xml/level_tree.xml
new file mode 100644
index 0000000..1a5e589
--- /dev/null
+++ b/res/xml/level_tree.xml
@@ -0,0 +1,326 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<levelTree>
+
+<group>
+ <level resource="@raw/level_0_1_sewer" title = "@string/level_0_1_sewer" time = "@string/level_0_1_time" waitmessage="true">
+ <dialog>
+ <character1 resource ="@xml/level_0_1_dialog_wanda"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_0_2_lab" title = "@string/level_0_2_lab" time = "@string/level_0_2_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_0_2_dialog_kabocha"/>
+ <character2 resource ="@xml/level_0_2_dialog_kabocha_2"/>
+ </dialog>
+ </level>
+
+</group>
+
+<group>
+ <level resource="@raw/level_3_5_sewer" title = "@string/level_3_5_sewer" time = "@string/level_3_5_time">
+ <dialog>
+ <character1 resource ="@xml/level_3_5_dialog_wanda"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_0_3_lab" title = "@string/level_0_3_lab" time = "@string/level_0_3_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_0_3_dialog_kabocha"/>
+ </dialog>
+ </level>
+</group>
+
+
+<group>
+ <level resource="@raw/level_3_6_sewer" title = "@string/level_3_6_sewer" time = "@string/level_3_6_time"/>
+</group>
+
+
+<group>
+ <level resource="@raw/level_1_1_island" title = "@string/level_1_1_island" time = "@string/level_1_1_time" past="true">
+ <dialog>
+ <diary resource ="@string/Diary1"/>
+ <character1 resource ="@xml/level_1_1_dialog_wanda"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_1_2_island" title = "@string/level_1_2_island" time = "@string/level_1_2_time" past="true"/>
+ <level resource="@raw/level_1_3_island" title = "@string/level_1_3_island" time = "@string/level_1_3_time" past="true">
+ <dialog>
+ <diary resource ="@string/Diary2"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_3_7_sewer" title = "@string/level_3_7_sewer" time = "@string/level_3_7_time">
+ <dialog>
+ <character1 resource ="@xml/level_3_7_dialog_wanda"/>
+ <diary resource ="@string/Diary3"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_2_1_grass" title = "@string/level_2_1_grass" time = "@string/level_2_1_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_2_1_dialog_kyle"/>
+ </dialog>
+ </level>
+
+ <!-- <level resource="@raw/level_1_4_island" title = "@string/level_1_4_island" past="true" time = "@string/level_1_4_time"/> -->
+</group>
+
+<group>
+ <level resource="@raw/level_3_8_sewer" title = "@string/level_3_8_sewer" time = "@string/level_3_8_time">
+ <dialog>
+ <character1 resource ="@xml/level_3_8_dialog_kyle"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_1_5_island" title = "@string/level_1_5_island" past="true" time = "@string/level_1_5_time">
+ <dialog>
+ <character1 resource ="@xml/level_1_5_dialog_wanda"/>
+ <diary resource ="@string/Diary4"/>
+ </dialog>
+ </level>
+ <level resource="@raw/level_2_2_grass" title = "@string/level_2_2_grass" past="true" time = "@string/level_2_2_time"/>
+ <level resource="@raw/level_2_3_grass" title = "@string/level_2_3_grass" past="true" time = "@string/level_2_3_time">
+ <dialog>
+ <character1 resource ="@xml/level_2_3_dialog_kyle"/>
+ <diary resource ="@string/Diary5"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_3_9_sewer" title = "@string/level_3_9_sewer" time = "@string/level_3_9_time">
+ <dialog>
+ <character1 resource ="@xml/level_3_9_dialog_kyle"/>
+ <character2 resource ="@xml/level_3_9_dialog_rokudou"/>
+ <diary resource ="@string/Diary6"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_3_4_sewer" title = "@string/level_3_4_sewer" past="true" time = "@string/level_3_4_time">
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_0_1_sewer_kyle" title = "@string/level_0_1_sewer_kyle" past="true" time = "@string/level_3_4_time" restartable="false">
+ <dialog>
+ <character1 resource ="@xml/level_3_4_dialog_kyle"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_0_1_sewer_wanda" title = "@string/level_0_1_sewer_wanda" past="true" time = "@string/level_0_1_time" waitmessage="true">
+ <dialog>
+ <character1 resource ="@xml/level_0_1_dialog_wanda"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_3_10_sewer" title = "@string/level_3_10_sewer" time = "@string/level_3_10_time">
+ <dialog>
+ <character1 resource ="@xml/level_3_10_dialog_kyle"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_3_11_sewer" title = "@string/level_3_11_sewer" time = "@string/level_3_11_time">
+ <dialog>
+ <character1 resource ="@xml/level_3_11_dialog_wanda"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_4_1_underground" title = "@string/level_4_1_underground" time = "@string/level_4_1_time">
+ <dialog>
+ <character1 resource ="@xml/level_4_1_dialog_wanda"/>
+ <character2 resource ="@xml/level_4_1_dialog_rokudou"/>
+ <!-- <character2 resource ="@xml/level_4_1_misc"/> -->
+ <diary resource ="@string/Diary7"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_1_6_island" title = "@string/level_1_6_island" time = "@string/level_1_6_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_1_6_dialog_wanda"/>
+ </dialog>
+ </level>
+ <level resource="@raw/level_2_4_grass" title = "@string/level_2_4_grass" time = "@string/level_2_4_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_2_4_dialog_kyle"/>
+ </dialog>
+ </level>
+
+ <level resource="@raw/level_3_1_grass" title = "@string/level_3_1_sewer" time = "@string/level_3_1_time" past="true"/>
+</group>
+
+<group>
+ <level resource="@raw/level_4_2_underground" title = "@string/level_4_2_underground" time = "@string/level_4_2_time">
+ <dialog>
+ <character1 resource ="@xml/level_4_2_dialog_wanda"/>
+ <diary resource ="@string/Diary9"/>
+ </dialog>
+ </level>
+
+</group>
+
+
+<group>
+<!-- <level resource="@raw/level_1_7_island" title = "@string/level_1_7_island" time = "@string/level_1_7_time" past="true">
+ </level>
+-->
+ <level resource="@raw/level_2_5_grass" title = "@string/level_2_5_grass" time = "@string/level_2_5_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_2_5_dialog_kyle"/>
+ <diary resource ="@string/Diary8"/>
+ </dialog>
+ </level>
+</group>
+
+
+<group>
+ <level resource="@raw/level_4_3_underground" title = "@string/level_4_3_underground" time = "@string/level_4_3_time">
+ <dialog>
+ <character1 resource ="@xml/level_4_3_dialog_kabocha"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_2_6_grass" title = "@string/level_2_6_grass" time = "@string/level_2_6_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_2_6_dialog_wanda"/>
+ <diary resource ="@string/Diary10"/>
+ </dialog>
+ </level>
+ <level resource="@raw/level_3_2_sewer" title = "@string/level_3_2_sewer" time = "@string/level_3_2_time" past="true"/>
+</group>
+
+<group>
+ <level resource="@raw/level_4_4_underground" title = "@string/level_4_4_underground" time = "@string/level_4_4_time">
+ <dialog>
+ <character2 resource ="@xml/level_4_4_dialog_rokudou"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_2_7_grass" title = "@string/level_2_7_grass" time = "@string/level_2_7_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_2_7_dialog_kyle"/>
+ </dialog>
+ </level>
+ <level resource="@raw/level_1_8_island" title = "@string/level_1_8_island" time = "@string/level_1_8_time" past="true"/>
+</group>
+
+ <group>
+ <level resource="@raw/level_4_5_underground" title = "@string/level_4_5_underground" time = "@string/level_4_5_time">
+ <dialog>
+ <character1 resource ="@xml/level_4_5_dialog_wanda"/>
+ </dialog>
+ </level>
+</group>
+
+
+ <!-- Level 4-6 -->
+
+ <group>
+ <level resource="@raw/level_2_8_grass" title = "@string/level_2_8_grass" time = "@string/level_2_8_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_2_8_dialog_kyle"/>
+ <diary resource ="@string/Diary11"/>
+ </dialog>
+ </level>
+ <level resource="@raw/level_3_3_sewer" title = "@string/level_3_3_sewer" time = "@string/level_3_3_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_3_3_dialog_wanda"/>
+ <diary resource ="@string/Diary12"/>
+ </dialog>
+ </level>
+
+</group>
+
+<group>
+ <level resource="@raw/level_4_7_underground" title = "@string/level_4_7_underground" time = "@string/level_4_7_time">
+ <dialog>
+ <character1 resource ="@xml/level_4_7_dialog_wanda"/>
+ <character2 resource ="@xml/level_4_7_dialog_rokudou"/>
+ <diary resource ="@string/Diary13"/>
+ </dialog>
+ </level>
+</group>
+
+ <group>
+ <level resource="@raw/level_4_8_underground" title = "@string/level_4_8_underground" time = "@string/level_4_8_time"/>
+</group>
+
+<group>
+ <level resource="@raw/level_2_9_grass" title = "@string/level_2_9_grass" time = "@string/level_2_9_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_2_9_dialog_wanda"/>
+ <character2 resource ="@xml/level_2_9_dialog_kyle"/>
+ <diary resource ="@string/Diary15"/>
+ </dialog>
+ </level>
+
+ <level resource="@raw/level_1_9_island" title = "@string/level_1_9_island" time = "@string/level_1_9_time" past="true">
+ <dialog>
+ <character1 resource ="@xml/level_1_9_dialog_wanda"/>
+ <diary resource ="@string/Diary14"/>
+ </dialog>
+ </level>
+</group>
+
+<group>
+ <level resource="@raw/level_4_9_underground" title = "@string/level_4_9_underground" time = "@string/level_4_9_time">
+ <dialog>
+ <character1 resource ="@xml/level_4_9_dialog_wanda"/>
+ </dialog>
+ </level>
+
+</group>
+
+<group>
+ <level resource="@raw/level_final_boss_lab" title = "@string/level_final_boss_lab" time = "@string/level_final_boss_time">
+ <dialog>
+ <character1 resource ="@xml/level_final_boss_dialog"/>
+ </dialog>
+ </level>
+
+</group>
+
+<!--
+<group>
+ <level resource="@raw/puzzles_test" title = "@string/level_puzzle_test" time = "@string/level_test_time"/>
+ <level resource="@raw/objecttestmap" title = "@string/level_object_test" time = "@string/level_test_time"/>
+ <level resource="@raw/performancetest" title = "@string/level_performance_test" time = "@string/level_test_time"/>
+ <level resource="@raw/performancetest2" title = "@string/level_performance_test2" time = "@string/level_test_time"/>
+ <level resource="@raw/performancetest3" title = "@string/level_performance_test3" time = "@string/level_test_time"/>
+ <level resource="@raw/npc_motion_test" title = "@string/level_npc_motion_test" time = "@string/level_test_time"/>
+</group>
+ -->
+
+</levelTree>
+
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
new file mode 100644
index 0000000..ff8ea0f
--- /dev/null
+++ b/res/xml/preferences.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceCategory
+ android:title="@string/preference_game_settings">
+
+ <CheckBoxPreference
+ android:key="enableSound"
+ android:title="@string/preference_enable_sound"
+ android:summaryOn="@string/preference_enable_sound_summary"
+ android:summaryOff="@string/preference_enable_sound_summary"
+ android:defaultValue="true"
+ android:persistent="true"
+ />
+
+ <PreferenceScreen
+ android:title="@string/preference_configure_controls">
+ <CheckBoxPreference
+ android:key="enableClickAttack"
+ android:title="@string/preference_enable_click_attack"
+ android:summaryOn="@string/preference_enable_click_attack_summary"
+ android:summaryOff="@string/preference_enable_click_attack_summary"
+ android:defaultValue="true"
+ android:persistent="true"
+ />
+ <CheckBoxPreference
+ android:key="enableTiltControls"
+ android:title="@string/preference_enable_tilt_controls"
+ android:summaryOn="@string/preference_enable_tilt_controls_summary"
+ android:summaryOff="@string/preference_enable_tilt_controls_summary"
+ android:defaultValue="false"
+ android:persistent="true"
+ />
+ </PreferenceScreen>
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:title="@string/preference_save_game">
+
+ <com.replica.replicaisland.YesNoDialogPreference
+ android:key="erasegame"
+ android:title="@string/preference_erase_save_game"
+ android:dialogMessage="@string/preference_erase_save_game_dialog"
+ android:dialogTitle="@string/preference_erase_save_game_dialog_title"
+ android:negativeButtonText="@string/preference_erase_save_game_dialog_cancel"
+ android:positiveButtonText="@string/preference_erase_save_game_dialog_ok"
+ />
+
+ <CheckBoxPreference
+ android:key="enableStats"
+ android:title="@string/preference_enable_statistics"
+ android:summaryOn="@string/preference_enable_statistics_summary"
+ android:summaryOff="@string/preference_enable_statistics_summary"
+ android:defaultValue="true"
+ android:persistent="true"
+ />
+
+ </PreferenceCategory>
+
+ <PreferenceCategory
+ android:title="@string/preference_about">
+
+ <PreferenceScreen
+ android:title="@string/preference_visit_site">
+
+ <intent android:action="android.intent.action.VIEW"
+ android:data="http://replicaisland.net" />
+
+ </PreferenceScreen>
+
+ <Preference
+ android:title="@string/preference_about_title"
+ android:summary="@string/preference_about_summary"
+ android:enabled="false"
+ android:selectable="false" />
+
+ <Preference
+ android:title="@string/preference_thanks_title"
+ android:summary="@string/preference_thanks_summary"
+ android:enabled="false"
+ android:selectable="false" />
+
+ </PreferenceCategory>
+
+</PreferenceScreen>
diff --git a/src/com/replica/replicaisland/AABoxCollisionVolume.java b/src/com/replica/replicaisland/AABoxCollisionVolume.java
new file mode 100644
index 0000000..f0f33c4
--- /dev/null
+++ b/src/com/replica/replicaisland/AABoxCollisionVolume.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * An Axis-Aligned rectangular collision volume. This code treats other volumes as if they are
+ * also rectangles when calculating intersections. Therefore certain types of intersections, such
+ * as sphere vs rectangle, may not be absolutely precise (in the case of a sphere vs a rectangle,
+ * for example, a new rectangle that fits the sphere is used to perform the intersection test, so
+ * there is some potential for false-positives at the corners). However, for our purposes absolute
+ * precision isn't necessary, so this simple implementation is sufficient.
+ */
+public class AABoxCollisionVolume extends CollisionVolume {
+ private Vector2 mWidthHeight;
+ private Vector2 mBottomLeft;
+
+ public AABoxCollisionVolume(float offsetX, float offsetY, float width, float height) {
+ super();
+ mBottomLeft = new Vector2(offsetX, offsetY);
+ mWidthHeight = new Vector2(width, height);
+ }
+
+ public AABoxCollisionVolume(float offsetX, float offsetY, float width, float height,
+ int hit) {
+ super(hit);
+ mBottomLeft = new Vector2(offsetX, offsetY);
+ mWidthHeight = new Vector2(width, height);
+ }
+
+ @Override
+ public final float getMaxX() {
+ return mBottomLeft.x + mWidthHeight.x;
+ }
+
+ @Override
+ public final float getMinX() {
+ return mBottomLeft.x;
+ }
+
+ @Override
+ public final float getMaxY() {
+ return mBottomLeft.y + mWidthHeight.y;
+ }
+
+ @Override
+ public final float getMinY() {
+ return mBottomLeft.y;
+ }
+
+ /**
+ * Calculates the intersection of this volume and another, and returns true if the
+ * volumes intersect. This test treats the other volume as an AABox.
+ * @param position The world position of this volume.
+ * @param other The volume to test for intersections.
+ * @param otherPosition The world position of the other volume.
+ * @return true if the volumes overlap, false otherwise.
+ */
+ @Override
+ public boolean intersects(Vector2 position, FlipInfo flip, CollisionVolume other,
+ Vector2 otherPosition, FlipInfo otherFlip) {
+ final float left = getMinXPosition(flip) + position.x;
+ final float right = getMaxXPosition(flip) + position.x;
+ final float bottom = getMinYPosition(flip) + position.y;
+ final float top = getMaxYPosition(flip) + position.y;
+
+ final float otherLeft = other.getMinXPosition(otherFlip) + otherPosition.x;
+ final float otherRight = other.getMaxXPosition(otherFlip) + otherPosition.x;
+ final float otherBottom = other.getMinYPosition(otherFlip) + otherPosition.y;
+ final float otherTop = other.getMaxYPosition(otherFlip) + otherPosition.y;
+
+ final boolean result = boxIntersect(left, right, top, bottom,
+ otherLeft, otherRight, otherTop, otherBottom)
+ || boxIntersect(otherLeft, otherRight, otherTop, otherBottom,
+ left, right, top, bottom);
+
+ return result;
+ }
+
+ /** Tests two axis-aligned boxes for overlap. */
+ private boolean boxIntersect(float left1, float right1, float top1, float bottom1,
+ float left2, float right2, float top2, float bottom2) {
+ final boolean horizontalIntersection = left1 < right2 && left2 < right1;
+ final boolean verticalIntersection = top1 > bottom2 && top2 > bottom1;
+ final boolean intersecting = horizontalIntersection && verticalIntersection;
+ return intersecting;
+ }
+
+ /** Increases the size of this volume as necessary to fit the passed volume. */
+ public void growBy(CollisionVolume other) {
+ final float maxX;
+ final float minX;
+
+ final float maxY;
+ final float minY;
+
+ if (mWidthHeight.length2() > 0) {
+ maxX = Math.max(getMaxX(), other.getMaxX());
+ minX = Math.max(getMinX(), other.getMinX());
+ maxY = Math.max(getMaxY(), other.getMaxY());
+ minY = Math.max(getMinY(), other.getMinY());
+ } else {
+ maxX = other.getMaxX();
+ minX = other.getMinX();
+ maxY = other.getMaxY();
+ minY = other.getMinY();
+ }
+ final float horizontalDelta = maxX - minX;
+ final float verticalDelta = maxY - minY;
+ mBottomLeft.set(minX, minY);
+ mWidthHeight.set(horizontalDelta, verticalDelta);
+ }
+
+}
diff --git a/src/com/replica/replicaisland/AllocationGuard.java b/src/com/replica/replicaisland/AllocationGuard.java
new file mode 100644
index 0000000..22bd269
--- /dev/null
+++ b/src/com/replica/replicaisland/AllocationGuard.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * AllocationGuard is a utility class for tracking down memory leaks. It implements a
+ * "checkpoint" memory scheme. After the static sGuardActive flag has been set, any further
+ * allocation of AllocationGuard or its derivatives will cause an error log entry. Note
+ * that AllocationGuard requires all of its derivatives to call super() in their constructor.
+ */
+public class AllocationGuard {
+ public static boolean sGuardActive = false;
+ public AllocationGuard() {
+ if (sGuardActive) {
+ // An allocation has occurred while the guard is active! Report it.
+ DebugLog.e("AllocGuard", "An allocation of type " + this.getClass().getName()
+ + " occurred while the AllocGuard is active.");
+
+
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/AndouKun.java b/src/com/replica/replicaisland/AndouKun.java
new file mode 100644
index 0000000..c06bb8d
--- /dev/null
+++ b/src/com/replica/replicaisland/AndouKun.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Debug;
+import android.util.DisplayMetrics;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+
+/**
+ * Core activity for the game. Sets up a surface view for OpenGL, bootstraps
+ * the game engine, and manages UI events. Also manages game progression,
+ * transitioning to other activites, save game, and input events.
+ */
+public class AndouKun extends Activity implements SensorEventListener {
+ private static final int ACTIVITY_CHANGE_LEVELS = 0;
+ private static final int ACTIVITY_CONVERSATION = 1;
+ private static final int ACTIVITY_DIARY = 2;
+ private static final int ACTIVITY_ANIMATION_PLAYER = 3;
+
+ private static final int CHANGE_LEVEL_ID = Menu.FIRST;
+ private static final int TEST_ANIMATION_ID = CHANGE_LEVEL_ID + 1;
+ private static final int TEST_DIARY_ID = CHANGE_LEVEL_ID + 2;
+ private static final int METHOD_TRACING_ID = CHANGE_LEVEL_ID + 3;
+
+ private static final int ROLL_TO_FACE_BUTTON_DELAY = 400;
+
+ public static final String PREFERENCE_LEVEL_ROW = "levelRow";
+ public static final String PREFERENCE_LEVEL_INDEX = "levelIndex";
+ public static final String PREFERENCE_LEVEL_COMPLETED = "levelsCompleted";
+ public static final String PREFERENCE_SOUND_ENABLED = "enableSound";
+ public static final String PREFERENCE_SESSION_ID = "session";
+ public static final String PREFERENCE_LAST_VERSION = "lastVersion";
+ public static final String PREFERENCE_STATS_ENABLED = "enableStats";
+ public static final String PREFERENCE_CLICK_ATTACK = "enableClickAttack";
+ public static final String PREFERENCE_TILT_CONTROLS = "enableTiltControls";
+
+ public static final String PREFERENCE_NAME = "ReplicaIslandPrefs";
+
+ public static final int QUIT_GAME_DIALOG = 0;
+
+ // If the version is a negative number, debug features (logging and a debug menu)
+ // are enabled.
+ public static final int VERSION = 9;
+
+ private GLSurfaceView mGLSurfaceView;
+ private Game mGame;
+ private boolean mMethodTracing;
+ private int mLevelRow;
+ private int mLevelIndex;
+ private SensorManager mSensorManager;
+ private SharedPreferences.Editor mPrefsEditor;
+ private long mLastTouchTime = 0L;
+ private long mLastRollTime = 0L;
+ private View mPauseMessage = null;
+ private View mWaitMessage = null;
+ private Animation mWaitFadeAnimation = null;
+
+ private EventReporter mEventReporter;
+ private Thread mEventReporterThread;
+
+ private long mSessionId = 0L;
+
+ /** Called when the activity is first created. */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (VERSION < 0) {
+ DebugLog.setDebugLogging(true);
+ } else {
+ DebugLog.setDebugLogging(false);
+ }
+
+ DebugLog.d("AndouKun", "onCreate");
+
+
+ setContentView(R.layout.main);
+ mGLSurfaceView = (GLSurfaceView) findViewById(R.id.glsurfaceview);
+ mPauseMessage = findViewById(R.id.pausedMessage);
+ mWaitMessage = findViewById(R.id.pleaseWaitMessage);
+ mWaitFadeAnimation = AnimationUtils.loadAnimation(this, R.anim.wait_message_fade);
+
+
+ //mGLSurfaceView.setGLWrapper(new GLErrorLogger());
+ mGLSurfaceView.setEGLConfigChooser(false); // 16 bit, no z-buffer
+ mGame = new Game();
+ mGame.setSurfaceView(mGLSurfaceView);
+ DisplayMetrics dm = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(dm);
+
+ int defaultWidth = 480;
+ int defaultHeight = 320;
+ if (dm.widthPixels != defaultWidth) {
+ float ratio =((float)dm.widthPixels) / dm.heightPixels;
+ defaultWidth = (int)(defaultHeight * ratio);
+ }
+ mGame.bootstrap(this, dm.widthPixels, dm.heightPixels, defaultWidth, defaultHeight);
+ mGLSurfaceView.setRenderer(mGame.getRenderer());
+
+ mLevelRow = 0;
+ mLevelIndex = 0;
+
+ SharedPreferences prefs = getSharedPreferences(PREFERENCE_NAME, MODE_PRIVATE);
+ mPrefsEditor = prefs.edit();
+ mLevelRow = prefs.getInt(PREFERENCE_LEVEL_ROW, 0);
+ mLevelIndex = prefs.getInt(PREFERENCE_LEVEL_INDEX, 0);
+ int completed = prefs.getInt(PREFERENCE_LEVEL_COMPLETED, 0);
+
+ // Android activity lifecycle rules make it possible for this activity to be created
+ // and come to the foreground without the MainMenu Activity ever running, so in that
+ // case we need to make sure that this static data is valid.
+ if (!LevelTree.isLoaded()) {
+ LevelTree.loadLevelTree(R.xml.level_tree, this);
+ LevelTree.loadAllDialog(this);
+ }
+
+ if (!LevelTree.levelIsValid(mLevelRow, mLevelIndex)) {
+ // bad data? Let's try to recover.
+
+ // is the row valid?
+ if (LevelTree.rowIsValid(mLevelRow)) {
+ // In that case, just start the row over.
+ mLevelIndex = 0;
+ completed = 0;
+ } else if (LevelTree.rowIsValid(mLevelRow - 1)) {
+ // If not, try to back up a row.
+ mLevelRow--;
+ mLevelIndex = 0;
+ completed = 0;
+ }
+
+
+ if (!LevelTree.levelIsValid(mLevelRow, mLevelIndex)) {
+ // if all else fails, start the game over.
+ mLevelRow = 0;
+ mLevelIndex = 0;
+ completed = 0;
+ }
+ }
+
+ LevelTree.updateCompletedState(mLevelRow, completed);
+
+ mGame.setPendingLevel(LevelTree.get(mLevelRow, mLevelIndex));
+ if (LevelTree.get(mLevelRow, mLevelIndex).showWaitMessage) {
+ showWaitMessage();
+ } else {
+ hideWaitMessage();
+ }
+
+ mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
+
+ // This activity uses the media stream.
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+
+ mSessionId = prefs.getLong(PREFERENCE_SESSION_ID, System.currentTimeMillis());
+
+
+ mEventReporter = null;
+ mEventReporterThread = null;
+ final boolean statsEnabled = prefs.getBoolean(PREFERENCE_STATS_ENABLED, true);
+ if (statsEnabled) {
+ mEventReporter = new EventReporter();
+ mEventReporterThread = new Thread(mEventReporter);
+ mEventReporterThread.setName("EventReporter");
+ mEventReporterThread.start();
+ }
+ }
+
+
+ @Override
+ protected void onDestroy() {
+ DebugLog.d("AndouKun", "onDestroy()");
+ mGame.stop();
+ if (mEventReporterThread != null) {
+ mEventReporter.stop();
+ try {
+ mEventReporterThread.join();
+ } catch (InterruptedException e) {
+ mEventReporterThread.interrupt();
+ }
+ }
+ super.onDestroy();
+
+ }
+
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ DebugLog.d("AndouKun", "onPause");
+
+ hidePauseMessage();
+
+ mGame.onPause();
+ mGLSurfaceView.onPause();
+ mGame.getRenderer().onPause(); // hack!
+
+ if (mMethodTracing) {
+ Debug.stopMethodTracing();
+ mMethodTracing = false;
+ }
+ if (mSensorManager != null) {
+ mSensorManager.unregisterListener(this);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ DebugLog.d("AndouKun", "onResume");
+ mGLSurfaceView.onResume();
+ mGame.onResume(this, false);
+
+ // Preferences may have changed while we were paused.
+ SharedPreferences prefs = getSharedPreferences(PREFERENCE_NAME, MODE_PRIVATE);
+ final boolean soundEnabled = prefs.getBoolean(PREFERENCE_SOUND_ENABLED, true);
+ final boolean clickAttack = prefs.getBoolean(PREFERENCE_CLICK_ATTACK, true);
+ final boolean tiltControls = prefs.getBoolean(PREFERENCE_TILT_CONTROLS, false);
+
+ mGame.setSoundEnabled(soundEnabled);
+ mGame.setControlOptions(clickAttack, tiltControls);
+
+ if (mSensorManager != null) {
+ Sensor orientation = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
+ if (orientation != null) {
+ mSensorManager.registerListener(this,
+ orientation,
+ SensorManager.SENSOR_DELAY_GAME,
+ null);
+ }
+ }
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ mGame.onTrackballEvent(event);
+ final long time = System.currentTimeMillis();
+ if (time - mLastRollTime < 4) {
+ // Sleep so that the main thread doesn't get flooded with UI events.
+ try {
+ Thread.sleep(4);
+ } catch (InterruptedException e) {
+ // No big deal if this sleep is interrupted.
+ }
+ }
+ mLastRollTime = time;
+ return true;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ mGame.onTouchEvent(event);
+
+ final long time = System.currentTimeMillis();
+ if (event.getAction() == MotionEvent.ACTION_MOVE && time - mLastTouchTime < 32) {
+ // Sleep so that the main thread doesn't get flooded with UI events.
+ try {
+ Thread.sleep(32);
+ } catch (InterruptedException e) {
+ // No big deal if this sleep is interrupted.
+ }
+ mGame.getRenderer().waitDrawingComplete();
+ }
+ mLastTouchTime = time;
+ return true;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ boolean result = true;
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ final long time = System.currentTimeMillis();
+ if (time - mLastRollTime > ROLL_TO_FACE_BUTTON_DELAY) {
+ showDialog(QUIT_GAME_DIALOG);
+ result = true;
+ }
+ } else if (keyCode == KeyEvent.KEYCODE_MENU) {
+ result = true;
+ if (mGame.isPaused()) {
+ hidePauseMessage();
+ mGame.onResume(this, true);
+ } else {
+ final long time = System.currentTimeMillis();
+ if (time - mLastRollTime > ROLL_TO_FACE_BUTTON_DELAY) {
+ showPauseMessage();
+ mGame.onPause();
+ }
+ if (VERSION < 0) {
+ result = false; // Allow the debug menu to come up in debug mode.
+ }
+ }
+ } else {
+ result = mGame.onKeyDownEvent(keyCode);
+ // Sleep so that the main thread doesn't get flooded with UI events.
+ try {
+ Thread.sleep(4);
+ } catch (InterruptedException e) {
+ // No big deal if this sleep is interrupted.
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ boolean result = false;
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ result = true;
+ } else if (keyCode == KeyEvent.KEYCODE_MENU){
+ if (VERSION < 0) {
+ result = false; // Allow the debug menu to come up in debug mode.
+ }
+ } else {
+ result = mGame.onKeyUpEvent(keyCode);
+ // Sleep so that the main thread doesn't get flooded with UI events.
+ try {
+ Thread.sleep(4);
+ } catch (InterruptedException e) {
+ // No big deal if this sleep is interrupted.
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ boolean handled = false;
+ // Only allow the debug menu in development versions.
+ if (VERSION < 0) {
+ menu.add(0, CHANGE_LEVEL_ID, 0, R.string.change_level);
+ menu.add(0, TEST_ANIMATION_ID, 0, R.string.test_animation);
+ menu.add(0, TEST_DIARY_ID, 0, R.string.test_diary);
+
+ menu.add(0, METHOD_TRACING_ID, 0, R.string.method_tracing);
+ handled = true;
+ }
+
+ return handled;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ Intent i;
+ switch(item.getItemId()) {
+ case CHANGE_LEVEL_ID:
+ i = new Intent(this, LevelSelectActivity.class);
+ startActivityForResult(i, ACTIVITY_CHANGE_LEVELS);
+ return true;
+ case TEST_ANIMATION_ID:
+ i = new Intent(this, AnimationPlayerActivity.class);
+ i.putExtra("animation", AnimationPlayerActivity.ROKUDOU_ENDING);
+ startActivity(i);
+ return true;
+ case TEST_DIARY_ID:
+ i = new Intent(this, DiaryActivity.class);
+ i.putExtra("text", R.string.Diary10);
+ startActivity(i);
+ return true;
+ case METHOD_TRACING_ID:
+ if (mMethodTracing) {
+ Debug.stopMethodTracing();
+ } else {
+ Debug.startMethodTracing("andou");
+ }
+ mMethodTracing = !mMethodTracing;
+ return true;
+ }
+
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
+ super.onActivityResult(requestCode, resultCode, intent);
+
+ if (requestCode == ACTIVITY_CHANGE_LEVELS) {
+ if (resultCode == RESULT_OK) {
+ mLevelRow = intent.getExtras().getInt("row");
+ mLevelIndex = intent.getExtras().getInt("index");
+ saveGame();
+
+ mGame.setPendingLevel(LevelTree.get(mLevelRow, mLevelIndex));
+ if (LevelTree.get(mLevelRow, mLevelIndex).showWaitMessage) {
+ showWaitMessage();
+ } else {
+ hideWaitMessage();
+ }
+
+ }
+ } else if (requestCode == ACTIVITY_ANIMATION_PLAYER) {
+ // on finishing animation playback, force a level change.
+ onGameFlowEvent(GameFlowEvent.EVENT_GO_TO_NEXT_LEVEL, 0);
+ }
+ }
+
+ /*
+ * When the game thread needs to stop its own execution (to go to a new level, or restart the
+ * current level), it registers a runnable on the main thread which orders the action via this
+ * function.
+ */
+ public void onGameFlowEvent(int eventCode, int index) {
+ switch (eventCode) {
+ case GameFlowEvent.EVENT_END_GAME:
+ mGame.stop();
+ finish();
+ break;
+ case GameFlowEvent.EVENT_RESTART_LEVEL:
+ if (LevelTree.get(mLevelRow, mLevelIndex).restartable) {
+ if (mEventReporter != null) {
+ mEventReporter.addEvent(EventReporter.EVENT_DEATH,
+ mGame.getLastDeathPosition().x,
+ mGame.getLastDeathPosition().y,
+ mGame.getGameTime(),
+ LevelTree.get(mLevelRow, mLevelIndex).name,
+ VERSION,
+ mSessionId);
+ }
+ mGame.restartLevel();
+ break;
+ }
+ // else, fall through and go to the next level.
+ case GameFlowEvent.EVENT_GO_TO_NEXT_LEVEL:
+ LevelTree.get(mLevelRow, mLevelIndex).completed = true;
+ final LevelTree.LevelGroup currentGroup = LevelTree.levels.get(mLevelRow);
+ final int count = currentGroup.levels.size();
+ boolean groupCompleted = true;
+ if (mEventReporter != null) {
+ mEventReporter.addEvent(EventReporter.EVENT_BEAT_LEVEL,
+ 0,
+ 0,
+ mGame.getGameTime(),
+ LevelTree.get(mLevelRow, mLevelIndex).name,
+ VERSION,
+ mSessionId);
+ }
+ for (int x = 0; x < count; x++) {
+ if (currentGroup.levels.get(x).completed == false) {
+ // We haven't completed the group yet.
+ mLevelIndex = x;
+ groupCompleted = false;
+ break;
+ }
+ }
+
+ if (groupCompleted) {
+ mLevelIndex = 0;
+ mLevelRow++;
+ }
+
+
+ if (mLevelRow < LevelTree.levels.size()) {
+ final LevelTree.Level currentLevel = LevelTree.get(mLevelRow, mLevelIndex);
+ if (currentLevel.inThePast || LevelTree.levels.get(mLevelRow).levels.size() > 1) {
+ // go to the level select.
+ Intent i = new Intent(this, LevelSelectActivity.class);
+ startActivityForResult(i, ACTIVITY_CHANGE_LEVELS);
+ } else {
+ // go directly to the next level
+ mGame.setPendingLevel(currentLevel);
+ if (currentLevel.showWaitMessage) {
+ showWaitMessage();
+ } else {
+ hideWaitMessage();
+ }
+ mGame.requestNewLevel();
+ }
+ saveGame();
+
+ } else {
+ if (mEventReporter != null) {
+ mEventReporter.addEvent(EventReporter.EVENT_BEAT_GAME,
+ 0,
+ 0,
+ mGame.getGameTime(),
+ "end",
+ VERSION,
+ mSessionId);
+ }
+ // We beat the game!
+ mLevelRow = 0;
+ mLevelIndex = 0;
+ saveGame();
+ mGame.stop();
+ finish();
+ }
+ break;
+ case GameFlowEvent.EVENT_SHOW_DIARY:
+ Intent i = new Intent(this, DiaryActivity.class);
+ LevelTree.Level level = LevelTree.get(mLevelRow, mLevelIndex);
+ level.diaryCollected = true;
+ i.putExtra("text", level.dialogResources.diaryEntry);
+ startActivity(i);
+ break;
+
+ case GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER1:
+ i = new Intent(this, ConversationDialogActivity.class);
+ i.putExtra("levelRow", mLevelRow);
+ i.putExtra("levelIndex", mLevelIndex);
+ i.putExtra("index", index);
+ i.putExtra("character", 1);
+ startActivity(i);
+ break;
+
+ case GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER2:
+ i = new Intent(this, ConversationDialogActivity.class);
+ i.putExtra("levelRow", mLevelRow);
+ i.putExtra("levelIndex", mLevelIndex);
+ i.putExtra("index", index);
+ i.putExtra("character", 2);
+ startActivity(i);
+ break;
+ case GameFlowEvent.EVENT_SHOW_ANIMATION:
+ i = new Intent(this, AnimationPlayerActivity.class);
+ i.putExtra("animation", index);
+ startActivityForResult(i, ACTIVITY_ANIMATION_PLAYER);
+ break;
+
+ }
+ }
+
+ protected void saveGame() {
+ if (mPrefsEditor != null) {
+ final int completed = LevelTree.packCompletedLevels(mLevelRow);
+ mPrefsEditor.putInt(PREFERENCE_LEVEL_ROW, mLevelRow);
+ mPrefsEditor.putInt(PREFERENCE_LEVEL_INDEX, mLevelIndex);
+ mPrefsEditor.putInt(PREFERENCE_LEVEL_COMPLETED, completed);
+ mPrefsEditor.putLong(PREFERENCE_SESSION_ID, mSessionId);
+ mPrefsEditor.commit();
+ }
+ }
+
+ protected void showPauseMessage() {
+ if (mPauseMessage != null) {
+ mPauseMessage.setVisibility(View.VISIBLE);
+ }
+ }
+
+ protected void hidePauseMessage() {
+ if (mPauseMessage != null) {
+ mPauseMessage.setVisibility(View.GONE);
+ }
+ }
+
+ protected void showWaitMessage() {
+ if (mWaitMessage != null) {
+ mWaitMessage.setVisibility(View.VISIBLE);
+ mWaitMessage.startAnimation(mWaitFadeAnimation);
+ }
+ }
+
+ protected void hideWaitMessage() {
+ if (mWaitMessage != null) {
+ mWaitMessage.setVisibility(View.GONE);
+ mWaitMessage.clearAnimation();
+ }
+ }
+
+
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (this) {
+ if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) {
+ final float x = event.values[0];
+ final float y = event.values[1];
+ final float z = event.values[2];
+ mGame.onOrientationEvent(x, y, z);
+ }
+ }
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ Dialog dialog = null;
+ if (id == QUIT_GAME_DIALOG) {
+
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.quit_game_dialog_title)
+ .setPositiveButton(R.string.quit_game_dialog_ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ finish();
+ }
+ })
+ .setNegativeButton(R.string.quit_game_dialog_cancel, null)
+ .setMessage(R.string.quit_game_dialog_message)
+ .create();
+ }
+ return dialog;
+ }
+}
diff --git a/src/com/replica/replicaisland/AnimationComponent.java b/src/com/replica/replicaisland/AnimationComponent.java
new file mode 100644
index 0000000..778c463
--- /dev/null
+++ b/src/com/replica/replicaisland/AnimationComponent.java
@@ -0,0 +1,388 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+
+import com.replica.replicaisland.CollisionParameters.HitType;
+import com.replica.replicaisland.GameObject.ActionType;
+import com.replica.replicaisland.SoundSystem.Sound;
+
+/**
+ * Player Animation game object component. Responsible for selecting an animation to describe the
+ * player's current state. Requires the object to contain a SpriteComponent to play animations.
+ */
+public class AnimationComponent extends GameComponent {
+
+ public enum PlayerAnimations {
+ IDLE,
+ MOVE,
+ MOVE_FAST,
+ BOOST_UP,
+ BOOST_MOVE,
+ BOOST_MOVE_FAST,
+ STOMP,
+ HIT_REACT,
+ DEATH,
+ FROZEN
+ }
+
+ private static final float MIN_ROCKET_TIME = 0.0f;
+ private static final float FLICKER_INTERVAL = 0.15f;
+ private static final float FLICKER_DURATION = 3.0f;
+ private static final float LAND_THUMP_DELAY = 0.5f;
+
+ private SpriteComponent mSprite;
+ private SpriteComponent mJetSprite;
+ private SpriteComponent mSparksSprite;
+
+ private PlayerComponent mPlayer;
+ private float mLastFlickerTime;
+ private boolean mFlickerOn;
+ private float mFlickerTimeRemaining;
+
+ private GameObject.ActionType mPreviousAction;
+
+ private float mLastRocketsOnTime;
+ private boolean mExplodingDeath;
+
+ private ChangeComponentsComponent mDamageSwap;
+ private Sound mLandThump;
+ private Sound mRocketSound;
+ private Sound mExplosionSound;
+ private float mLandThumpDelay;
+ private int mRocketSoundStream;
+ private boolean mRocketSoundPaused;
+
+ private int mLastRubyCount;
+ private Sound mRubySound1;
+ private Sound mRubySound2;
+ private Sound mRubySound3;
+ private InventoryComponent mInventory;
+
+
+ public AnimationComponent() {
+ super();
+ reset();
+ setPhase(ComponentPhases.ANIMATION.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mPreviousAction = ActionType.INVALID;
+ mSprite = null;
+ mJetSprite = null;
+ mSparksSprite = null;
+ mPlayer = null;
+ mLastFlickerTime = 0.0f;
+ mFlickerOn = false;
+ mFlickerTimeRemaining = 0.0f;
+ mLastRocketsOnTime = 0.0f;
+ mExplodingDeath = false;
+ mDamageSwap = null;
+ mLandThump = null;
+ mLandThumpDelay = 0.0f;
+ mRocketSound = null;
+ mRocketSoundStream = -1;
+ mLastRubyCount = 0;
+ mInventory = null;
+ mExplosionSound = null;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mSprite != null) {
+
+ GameObject parentObject = (GameObject) parent;
+
+ final float velocityX = parentObject.getVelocity().x;
+ final float velocityY = parentObject.getVelocity().y;
+
+
+ GameObject.ActionType currentAction = parentObject.getCurrentAction();
+
+ if (mJetSprite != null) {
+ mJetSprite.setVisible(false);
+ }
+
+ if (mSparksSprite != null) {
+ mSparksSprite.setVisible(false);
+ }
+
+
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+
+ if (currentAction != ActionType.HIT_REACT && mPreviousAction == ActionType.HIT_REACT) {
+ mFlickerTimeRemaining = FLICKER_DURATION;
+ }
+
+
+ final boolean touchingGround = parentObject.touchingGround();
+
+ boolean boosting = mPlayer != null ? mPlayer.getRocketsOn() : false;
+
+ boolean visible = true;
+
+ SoundSystem sound = sSystemRegistry.soundSystem;
+
+ // It's usually not necessary to test to see if sound is enabled or not (when it's disabled,
+ // play() is just a nop), but in this case I have a stream that is maintained for the rocket
+ // sounds. So it's simpler to just avoid that code if sound is off.
+ if (sound.getSoundEnabled()) {
+ if (boosting) {
+ mLastRocketsOnTime = gameTime;
+ } else {
+ if (gameTime - mLastRocketsOnTime < MIN_ROCKET_TIME
+ && velocityY >= 0.0f) {
+ boosting = true;
+ }
+ }
+
+ if (mRocketSound != null) {
+ if (boosting) {
+ if (mRocketSoundStream == -1) {
+ mRocketSoundStream = sound.play(mRocketSound, true, SoundSystem.PRIORITY_HIGH);
+ mRocketSoundPaused = false;
+ } else if (mRocketSoundPaused) {
+ sound.resume(mRocketSoundStream);
+ mRocketSoundPaused = false;
+ }
+ } else {
+ sound.pause(mRocketSoundStream);
+ mRocketSoundPaused = true;
+ }
+ }
+ }
+
+ // Normally, for collectables like the coin, we could just tell the object to play
+ // a sound when it is collected. The gems are a special case, though, as we
+ // want to pick a different sound depending on how many have been collected.
+ if (mInventory != null && mRubySound1 != null && mRubySound2 != null && mRubySound3 != null) {
+ InventoryComponent.UpdateRecord inventory = mInventory.getRecord();
+ final int rubyCount = inventory.rubyCount;
+ if (rubyCount != mLastRubyCount) {
+ mLastRubyCount = rubyCount;
+ switch (rubyCount) {
+ case 1:
+ sound.play(mRubySound1, false, SoundSystem.PRIORITY_NORMAL);
+ break;
+ case 2:
+ sound.play(mRubySound2, false, SoundSystem.PRIORITY_NORMAL);
+ break;
+ case 3:
+ sound.play(mRubySound3, false, SoundSystem.PRIORITY_NORMAL);
+ break;
+ }
+
+ }
+ }
+
+ // Turn on visual effects (smoke, etc) when the player's life reaches 1.
+ if (mDamageSwap != null) {
+ if (parentObject.life == 1 && !mDamageSwap.getCurrentlySwapped()) {
+ mDamageSwap.activate(parentObject);
+ } else if (parentObject.life != 1 && mDamageSwap.getCurrentlySwapped()) {
+ mDamageSwap.activate(parentObject);
+ }
+ }
+
+ float opacity = 1.0f;
+
+ if (currentAction == ActionType.MOVE) {
+ InputSystem input = sSystemRegistry.inputSystem;
+ if (input.getRollDirection().x < 0.0f) {
+ parentObject.facingDirection.x = -1.0f;
+ } else if (input.getRollDirection().x > 0.0f) {
+ parentObject.facingDirection.x = 1.0f;
+ }
+
+ // TODO: get rid of these magic numbers!
+ if (touchingGround) {
+
+ if (Utils.close(velocityX, 0.0f, 30.0f)) {
+ mSprite.playAnimation(PlayerAnimations.IDLE.ordinal());
+ } else if (Math.abs(velocityX) > 300.0f) {
+ mSprite.playAnimation(PlayerAnimations.MOVE_FAST.ordinal());
+ } else {
+ mSprite.playAnimation(PlayerAnimations.MOVE.ordinal());
+ }
+
+ if (input.getClickPressed() ||
+ (input.getTouchPressed() && input.getTouchedWithinRegion(
+ ButtonConstants.STOMP_BUTTON_REGION_X,
+ ButtonConstants.STOMP_BUTTON_REGION_Y,
+ ButtonConstants.STOMP_BUTTON_REGION_WIDTH,
+ ButtonConstants.STOMP_BUTTON_REGION_HEIGHT))) {
+ // charge
+ final float pressedTime = gameTime - input.getLastClickTime();
+ final float wave = (float)Math.cos(pressedTime * (float)Math.PI * 2.0f);
+ opacity = (wave * 0.25f) + 0.75f;
+ }
+
+ } else {
+ if (boosting) {
+ if (mJetSprite != null) {
+ mJetSprite.setVisible(true);
+ }
+
+ if (Math.abs(velocityX) < 100.0f && velocityY > 10.0f) {
+ mSprite.playAnimation(PlayerAnimations.BOOST_UP.ordinal());
+ } else if (Math.abs(velocityX) > 300.0f) {
+ mSprite.playAnimation(PlayerAnimations.BOOST_MOVE_FAST.ordinal());
+ } else {
+ mSprite.playAnimation(PlayerAnimations.BOOST_MOVE.ordinal());
+ }
+ } else {
+
+ if (Utils.close(velocityX, 0.0f, 1.0f)) {
+ mSprite.playAnimation(PlayerAnimations.IDLE.ordinal());
+ } else if (Math.abs(velocityX) > 300.0f) {
+ mSprite.playAnimation(PlayerAnimations.MOVE_FAST.ordinal());
+ } else {
+ mSprite.playAnimation(PlayerAnimations.MOVE.ordinal());
+ }
+ }
+
+ }
+ } else if (currentAction == ActionType.ATTACK) {
+ mSprite.playAnimation(PlayerAnimations.STOMP.ordinal());
+ if (touchingGround && gameTime > mLandThumpDelay) {
+ if (mLandThump != null && sound != null) {
+ // modulate the sound slightly to avoid sounding too similar
+ sound.play(mLandThump, false, SoundSystem.PRIORITY_HIGH, 1.0f,
+ (float)(Math.random() * 0.5f) + 0.75f);
+ mLandThumpDelay = gameTime + LAND_THUMP_DELAY;
+ }
+ }
+ } else if (currentAction == ActionType.HIT_REACT) {
+ mSprite.playAnimation(PlayerAnimations.HIT_REACT.ordinal());
+
+ if (velocityX > 0.0f) {
+ parentObject.facingDirection.x = -1.0f;
+ } else if (velocityX < 0.0f) {
+ parentObject.facingDirection.x = 1.0f;
+ }
+
+ if (mSparksSprite != null) {
+ mSparksSprite.setVisible(true);
+ }
+ } else if (currentAction == ActionType.DEATH) {
+ if (mPreviousAction != currentAction) {
+ if (mExplosionSound != null) {
+ sound.play(mExplosionSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ // by default, explode when hit with the DEATH hit type.
+ boolean explodingDeath = parentObject.lastReceivedHitType == HitType.DEATH;
+ // or if touching a death tile.
+ HotSpotSystem hotSpot = sSystemRegistry.hotSpotSystem;
+ if (hotSpot != null) {
+ // TODO: HACK! Unify all this code.
+ if (hotSpot.getHotSpot(parentObject.getCenteredPositionX(),
+ parentObject.getPosition().y + 10.0f) == HotSpotSystem.HotSpotType.DIE) {
+ explodingDeath = true;
+ }
+ }
+ if (explodingDeath) {
+ mExplodingDeath = true;
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (factory != null && manager != null) {
+ GameObject explosion = factory.spawnEffectExplosionGiant(parentObject.getPosition().x, parentObject.getPosition().y);
+ if (explosion != null) {
+ manager.add(explosion);
+ }
+ }
+ } else {
+ mSprite.playAnimation(PlayerAnimations.DEATH.ordinal());
+ mExplodingDeath = false;
+ }
+
+ mFlickerTimeRemaining = 0.0f;
+ if (mSparksSprite != null) {
+ if (!mSprite.animationFinished()) {
+ mSparksSprite.setVisible(true);
+ }
+ }
+ }
+ if (mExplodingDeath) {
+ visible = false;
+ }
+ } else if (currentAction == ActionType.FROZEN) {
+ mSprite.playAnimation(PlayerAnimations.FROZEN.ordinal());
+ }
+
+ if (mFlickerTimeRemaining > 0.0f) {
+ mFlickerTimeRemaining -= timeDelta;
+ if (gameTime > mLastFlickerTime + FLICKER_INTERVAL) {
+ mLastFlickerTime = gameTime;
+ mFlickerOn = !mFlickerOn;
+ }
+ mSprite.setVisible(mFlickerOn);
+ if (mJetSprite != null && mJetSprite.getVisible()) {
+ mJetSprite.setVisible(mFlickerOn);
+ }
+ } else {
+ mSprite.setVisible(visible);
+ mSprite.setOpacity(opacity);
+ }
+
+ mPreviousAction = currentAction;
+ }
+ }
+
+ public void setSprite(SpriteComponent sprite) {
+ mSprite = sprite;
+ }
+
+ public void setJetSprite(SpriteComponent sprite) {
+ mJetSprite = sprite;
+ }
+
+ public void setSparksSprite(SpriteComponent sprite) {
+ mSparksSprite = sprite;
+ }
+
+ public void setPlayer(PlayerComponent player) {
+ mPlayer = player;
+ }
+
+ public final void setDamageSwap(ChangeComponentsComponent damageSwap) {
+ mDamageSwap = damageSwap;
+ }
+
+ public void setLandThump(Sound land) {
+ mLandThump = land;
+ }
+
+ public void setRocketSound(Sound sound) {
+ mRocketSound = sound;
+ }
+
+ public void setRubySounds(Sound one, Sound two, Sound three) {
+ mRubySound1 = one;
+ mRubySound2 = two;
+ mRubySound3 = three;
+ }
+
+ public void setInventory(InventoryComponent inventory) {
+ mInventory = inventory;
+ }
+
+ public void setExplosionSound(Sound sound) {
+ mExplosionSound = sound;
+ }
+}
diff --git a/src/com/replica/replicaisland/AnimationFrame.java b/src/com/replica/replicaisland/AnimationFrame.java
new file mode 100644
index 0000000..7b4e2f5
--- /dev/null
+++ b/src/com/replica/replicaisland/AnimationFrame.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A single animation frame. Frames contain a texture, a hold time, and collision volumes to
+ * use for "attacking" or "vulnerability." This allows animated sprites to cheaply interact with
+ * other objects in the game world by associating collision information with particular animation
+ * frames. Note that an animation frame may have a null texture and null collision volumes. Null
+ * collision volumes will exclude that frame from collision detection and a null texture will
+ * prevent the sprite from drawing.
+ */
+public class AnimationFrame extends AllocationGuard {
+ public Texture texture;
+ public float holdTime;
+ FixedSizeArray<CollisionVolume> attackVolumes;
+ FixedSizeArray<CollisionVolume> vulnerabilityVolumes;
+
+ public AnimationFrame(Texture textureObject, float animationHoldTime) {
+ super();
+ texture = textureObject;
+ holdTime = animationHoldTime;
+ }
+
+ public AnimationFrame(Texture textureObject, float animationHoldTime,
+ FixedSizeArray<CollisionVolume> attackVolumeList,
+ FixedSizeArray<CollisionVolume> vulnerabilityVolumeList) {
+ super();
+ texture = textureObject;
+ holdTime = animationHoldTime;
+ attackVolumes = attackVolumeList;
+ vulnerabilityVolumes = vulnerabilityVolumeList;
+ }
+}
diff --git a/src/com/replica/replicaisland/AnimationPlayerActivity.java b/src/com/replica/replicaisland/AnimationPlayerActivity.java
new file mode 100644
index 0000000..532224f
--- /dev/null
+++ b/src/com/replica/replicaisland/AnimationPlayerActivity.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.AnimationDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.TranslateAnimation;
+import android.widget.ImageView;
+
+
+public class AnimationPlayerActivity extends Activity {
+ public static final int KYLE_DEATH = 0;
+ public static final int WANDA_ENDING = 1;
+ public static final int KABOCHA_ENDING = 2;
+ public static final int ROKUDOU_ENDING = 3;
+
+ private AnimationDrawable mAnimation;
+ private int mAnimationType;
+ private long mAnimationEndTime;
+
+ private KillActivityHandler mKillActivityHandler = new KillActivityHandler();
+
+ class KillActivityHandler extends Handler {
+
+ @Override
+ public void handleMessage(Message msg) {
+ AnimationPlayerActivity.this.finish();
+ }
+
+ public void sleep(long delayMillis) {
+ this.removeMessages(0);
+ sendMessageDelayed(obtainMessage(0), delayMillis);
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent callingIntent = getIntent();
+ mAnimationType = callingIntent.getIntExtra("animation", KYLE_DEATH);
+
+ if (mAnimationType == KYLE_DEATH) {
+ setContentView(R.layout.animation_player);
+
+ ImageView canvasImage = (ImageView) findViewById(R.id.animation_canvas);
+ canvasImage.setImageResource(R.anim.kyle_fall);
+ mAnimation = (AnimationDrawable) canvasImage.getDrawable();
+ } else {
+
+
+ if (mAnimationType == WANDA_ENDING || mAnimationType == KABOCHA_ENDING) {
+ float startX = 0.0f;
+ DisplayMetrics metrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(metrics);
+ if (mAnimationType == WANDA_ENDING) {
+ setContentView(R.layout.good_ending_animation);
+ startX = 200 * metrics.density;
+
+ } else {
+ setContentView(R.layout.kabocha_ending_animation);
+ startX = -200 * metrics.density;
+ }
+
+ // HACK
+ // the TranslateAnimation system doesn't support device independent pixels.
+ // So for the Wanda ending and Kabocha endings, in which the game over text
+ // scrolls in horizontally, compute the size based on the actual density of
+ // the display and just generate the anim in code. The Rokudou animation
+ // can be safely loaded from a file.
+ Animation gameOverAnim = new TranslateAnimation(startX, 0, 0, 0);
+ gameOverAnim.setDuration(6000);
+ gameOverAnim.setFillAfter(true);
+ gameOverAnim.setFillEnabled(true);
+ gameOverAnim.setStartOffset(8000);
+
+ View background = findViewById(R.id.animation_background);
+ View foreground = findViewById(R.id.animation_foreground);
+ View gameOver = findViewById(R.id.game_over);
+
+ Animation foregroundAnim = AnimationUtils.loadAnimation(this, R.anim.horizontal_layer2_slide);
+ Animation backgroundAnim = AnimationUtils.loadAnimation(this, R.anim.horizontal_layer1_slide);
+
+ background.startAnimation(backgroundAnim);
+ foreground.startAnimation(foregroundAnim);
+ gameOver.startAnimation(gameOverAnim);
+
+ mAnimationEndTime = gameOverAnim.getDuration() + System.currentTimeMillis();
+ } else if (mAnimationType == ROKUDOU_ENDING) {
+ setContentView(R.layout.rokudou_ending_animation);
+ View background = findViewById(R.id.animation_background);
+ View sphere = findViewById(R.id.animation_sphere);
+ View cliffs = findViewById(R.id.animation_cliffs);
+ View rokudou = findViewById(R.id.animation_rokudou);
+ View gameOver = findViewById(R.id.game_over);
+
+
+ Animation backgroundAnim = AnimationUtils.loadAnimation(this, R.anim.rokudou_slide_bg);
+ Animation sphereAnim = AnimationUtils.loadAnimation(this, R.anim.rokudou_slide_sphere);
+ Animation cliffsAnim = AnimationUtils.loadAnimation(this, R.anim.rokudou_slide_cliffs);
+ Animation rokudouAnim = AnimationUtils.loadAnimation(this, R.anim.rokudou_slide_rokudou);
+ Animation gameOverAnim = AnimationUtils.loadAnimation(this, R.anim.rokudou_game_over);
+
+ background.startAnimation(backgroundAnim);
+ sphere.startAnimation(sphereAnim);
+ cliffs.startAnimation(cliffsAnim);
+ rokudou.startAnimation(rokudouAnim);
+ gameOver.startAnimation(gameOverAnim);
+ mAnimationEndTime = gameOverAnim.getDuration() + System.currentTimeMillis();
+ } else {
+ assert false;
+ }
+
+ }
+
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ long time = System.currentTimeMillis();
+ if (time > mAnimationEndTime) {
+ finish();
+ } else {
+ try {
+ Thread.sleep(32);
+ } catch (InterruptedException e) {
+ // Safe to ignore.
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ if (hasFocus && mAnimation != null) {
+ mAnimation.start();
+ mKillActivityHandler.sleep(mAnimation.getDuration(0) * mAnimation.getNumberOfFrames());
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/AttackAtDistanceComponent.java b/src/com/replica/replicaisland/AttackAtDistanceComponent.java
new file mode 100644
index 0000000..47437e2
--- /dev/null
+++ b/src/com/replica/replicaisland/AttackAtDistanceComponent.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+public class AttackAtDistanceComponent extends GameComponent {
+ private static final int DEFAULT_ATTACK_DISTANCE = 100;
+ private float mAttackDistance;
+ private float mAttackDelay;
+ private float mAttackLength;
+ private float mAttackStartTime;
+ private boolean mRequireFacing;
+ private Vector2 mDistance;
+
+ public AttackAtDistanceComponent() {
+ super();
+ setPhase(GameComponent.ComponentPhases.THINK.ordinal());
+ mDistance = new Vector2();
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mAttackDelay = 0;
+ mAttackLength = 0;
+ mAttackDistance = DEFAULT_ATTACK_DISTANCE;
+ mRequireFacing = false;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (manager != null) {
+ GameObject player = manager.getPlayer();
+ if (player != null) {
+ mDistance.set(player.getPosition());
+ mDistance.subtract(parentObject.getPosition());
+
+ TimeSystem time = sSystemRegistry.timeSystem;
+ final float currentTime = time.getGameTime();
+ final boolean facingPlayer =
+ (Utils.sign(player.getPosition().x - parentObject.getPosition().x)
+ == Utils.sign(parentObject.facingDirection.x));
+ final boolean facingDirectionCorrect = (mRequireFacing && facingPlayer)
+ || !mRequireFacing;
+ if (parentObject.getCurrentAction() == GameObject.ActionType.ATTACK) {
+ if (currentTime > mAttackStartTime + mAttackLength) {
+ parentObject.setCurrentAction(GameObject.ActionType.IDLE);
+ }
+ } else if (mDistance.length2() < (mAttackDistance * mAttackDistance)
+ && currentTime > mAttackStartTime + mAttackLength + mAttackDelay
+ && facingDirectionCorrect) {
+ mAttackStartTime = currentTime;
+ parentObject.setCurrentAction(GameObject.ActionType.ATTACK);
+ } else {
+ parentObject.setCurrentAction(GameObject.ActionType.IDLE);
+ }
+ }
+ }
+
+ }
+
+ public void setupAttack(float distance, float delay, float duration, boolean requireFacing) {
+ mAttackDistance = distance;
+ mAttackDelay = delay;
+ mAttackLength = duration;
+ mRequireFacing = requireFacing;
+ }
+
+
+}
diff --git a/src/com/replica/replicaisland/BackgroundCollisionComponent.java b/src/com/replica/replicaisland/BackgroundCollisionComponent.java
new file mode 100644
index 0000000..d3b99f1
--- /dev/null
+++ b/src/com/replica/replicaisland/BackgroundCollisionComponent.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+/**
+ * Handles collision against the background. Snaps colliding objects out of collision and reports
+ * the hit to the parent game object.
+ */
+public class BackgroundCollisionComponent extends GameComponent {
+ private Vector2 mPreviousPosition;
+ private int mWidth;
+ private int mHeight;
+ private int mHorizontalOffset;
+ private int mVerticalOffset;
+
+ // Workspace vectors. Allocated up front for speed.
+ private Vector2 mCurrentPosition;
+ private Vector2 mPreviousCenter;
+ private Vector2 mDelta;
+ private Vector2 mFilterDirection;
+ private Vector2 mHorizontalHitPoint;
+ private Vector2 mHorizontalHitNormal;
+ private Vector2 mVerticalHitPoint;
+ private Vector2 mVerticalHitNormal;
+ private Vector2 mRayStart;
+ private Vector2 mRayEnd;
+ private Vector2 mTestPointStart;
+ private Vector2 mTestPointEnd;
+ private Vector2 mMergedNormal;
+
+ /**
+ * Sets up the collision bounding box. This box may be a different size than the bounds of the
+ * sprite that this object controls.
+ * @param width The width of the collision box.
+ * @param height The height of the collision box.
+ * @param horzOffset The offset of the collision box from the object's origin in the x axis.
+ * @param vertOffset The offset of the collision box from the object's origin in the y axis.
+ */
+ public BackgroundCollisionComponent(int width, int height, int horzOffset, int vertOffset) {
+ super();
+ setPhase(ComponentPhases.COLLISION_RESPONSE.ordinal());
+ mPreviousPosition = new Vector2();
+ mWidth = width;
+ mHeight = height;
+ mHorizontalOffset = horzOffset;
+ mVerticalOffset = vertOffset;
+
+ mCurrentPosition = new Vector2();
+ mPreviousCenter = new Vector2();
+ mDelta = new Vector2();
+ mFilterDirection = new Vector2();
+ mHorizontalHitPoint = new Vector2();
+ mHorizontalHitNormal = new Vector2();
+ mVerticalHitPoint = new Vector2();
+ mVerticalHitNormal = new Vector2();
+ mRayStart = new Vector2();
+ mRayEnd = new Vector2();
+ mTestPointStart = new Vector2();
+ mTestPointEnd = new Vector2();
+ mMergedNormal = new Vector2();
+ }
+
+ public BackgroundCollisionComponent() {
+ super();
+ setPhase(ComponentPhases.COLLISION_RESPONSE.ordinal());
+ mPreviousPosition = new Vector2();
+ mCurrentPosition = new Vector2();
+ mPreviousCenter = new Vector2();
+ mDelta = new Vector2();
+ mFilterDirection = new Vector2();
+ mHorizontalHitPoint = new Vector2();
+ mHorizontalHitNormal = new Vector2();
+ mVerticalHitPoint = new Vector2();
+ mVerticalHitNormal = new Vector2();
+ mRayStart = new Vector2();
+ mRayEnd = new Vector2();
+ mTestPointStart = new Vector2();
+ mTestPointEnd = new Vector2();
+ mMergedNormal = new Vector2();
+ }
+
+ @Override
+ public void reset() {
+ mPreviousPosition.zero();
+ }
+
+ public void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ // TODO: Resize might cause new collisions.
+ }
+
+ public void setOffset(int horzOffset, int vertOffset) {
+ mHorizontalOffset = horzOffset;
+ mVerticalOffset = vertOffset;
+ }
+
+ /**
+ * This function is the meat of the collision response logic. Our collision detection and
+ * response must be capable of dealing with arbitrary surfaces and must be frame rate
+ * independent (we must sweep the space in-between frames to find collisions reliably). The
+ * following algorithm is used to keep the collision box out of the collision world.
+ * 1. Cast a ray from the center point of the box at its position last frame to the edge
+ * of the box at its current position. If the ray intersects anything, snap the box
+ * back to the point of intersection.
+ * 2. Perform Step 1 twice: once looking for surfaces opposing horizontal movement and
+ * again for surfaces opposing vertical movement. These two ray tests approximate the
+ * movement of the box between the previous frame and this one.
+ * 3. Since most collisions are collisions with the ground, more precision is required for
+ * vertical intersections. Perform another ray test, this time from the top of the
+ * box's position (after snapping in Step 2) to the bottom. Snap out of any vertical
+ * surfaces that the ray encounters. This will ensure consistent snapping behavior on
+ * incline surfaces.
+ * 4. Add the normals of the surfaces that were hit up and normalize the result to produce
+ * a direction describing the average slope of the surfaces that the box is resting on.
+ * Physics will use this value as a normal to resolve collisions with the background.
+ */
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+ parentObject.setBackgroundCollisionNormal(Vector2.ZERO);
+ if (mPreviousPosition.length2() != 0) {
+ CollisionSystem collision = sSystemRegistry.collisionSystem;
+ if (collision != null) {
+ final int left = mHorizontalOffset;
+ final int bottom = mVerticalOffset;
+ final int right = left + mWidth;
+ final int top = bottom + mHeight;
+ final float centerOffsetX = ((mWidth) / 2.0f) + left;
+ final float centerOffsetY = ((mHeight) / 2.0f) + bottom;
+
+ mCurrentPosition.set(parentObject.getPosition());
+ mDelta.set(mCurrentPosition);
+ mDelta.subtract(mPreviousPosition);
+
+ mPreviousCenter.set(centerOffsetX, centerOffsetY);
+ mPreviousCenter.add(mPreviousPosition);
+
+ boolean horizontalHit = false;
+ boolean verticalHit = false;
+
+ mVerticalHitPoint.zero();
+ mVerticalHitNormal.zero();
+ mHorizontalHitPoint.zero();
+ mHorizontalHitNormal.zero();
+
+
+ // The order in which we sweep the horizontal and vertical space can affect the
+ // final result because we perform incremental snapping mid-sweep. So it is
+ // necessary to sweep in the primary direction of movement first.
+ if (Math.abs(mDelta.x) > Math.abs(mDelta.y)) {
+ horizontalHit = sweepHorizontal(mPreviousCenter, mCurrentPosition, mDelta, left,
+ right, centerOffsetY, mHorizontalHitPoint, mHorizontalHitNormal,
+ parentObject);
+ verticalHit = sweepVertical(mPreviousCenter, mCurrentPosition, mDelta, bottom,
+ top, centerOffsetX, mVerticalHitPoint, mVerticalHitNormal,
+ parentObject);
+
+ } else {
+ verticalHit = sweepVertical(mPreviousCenter, mCurrentPosition, mDelta, bottom,
+ top, centerOffsetX, mVerticalHitPoint, mVerticalHitNormal,
+ parentObject);
+ horizontalHit = sweepHorizontal(mPreviousCenter, mCurrentPosition, mDelta, left,
+ right, centerOffsetY, mHorizontalHitPoint, mHorizontalHitNormal,
+ parentObject);
+ }
+
+ // force the collision volume to stay within the bounds of the world.
+ LevelSystem level = sSystemRegistry.levelSystem;
+ if (level != null) {
+ if (mCurrentPosition.x + left < 0.0f) {
+ mCurrentPosition.x = (-left + 1);
+ horizontalHit = true;
+ mHorizontalHitNormal.x = (mHorizontalHitNormal.x + 1.0f);
+ mHorizontalHitNormal.normalize();
+ } else if (mCurrentPosition.x + right > level.getLevelWidth()) {
+ mCurrentPosition.x = (level.getLevelWidth() - right - 1);
+ mHorizontalHitNormal.x = (mHorizontalHitNormal.x - 1.0f);
+ mHorizontalHitNormal.normalize();
+ horizontalHit = true;
+ }
+
+ /*if (mCurrentPosition.y + bottom < 0.0f) {
+ mCurrentPosition.y = (-bottom + 1);
+ verticalHit = true;
+ mVerticalHitNormal.y = (mVerticalHitNormal.y + 1.0f);
+ mVerticalHitNormal.normalize();
+ } else*/ if (mCurrentPosition.y + top > level.getLevelHeight()) {
+ mCurrentPosition.y = (level.getLevelHeight() - top - 1);
+ mVerticalHitNormal.y = (mVerticalHitNormal.y - 1.0f);
+ mVerticalHitNormal.normalize();
+ verticalHit = true;
+ }
+
+ }
+
+
+ // One more set of tests to make sure that we are aligned with the surface.
+ // This time we will just check the inside of the bounding box for intersections.
+ // The sweep tests above will keep us out of collision in most cases, but this
+ // test will ensure that we are aligned to incline surfaces correctly.
+
+ // Shoot a vertical line through the middle of the box.
+ if (mDelta.x != 0.0f && mDelta.y != 0.0f) {
+ float yStart = top;
+ float yEnd = bottom;
+
+
+ mRayStart.set(centerOffsetX, yStart);
+ mRayStart.add(mCurrentPosition);
+
+ mRayEnd.set(centerOffsetX, yEnd);
+ mRayEnd.add(mCurrentPosition);
+
+ mFilterDirection.set(mDelta);
+
+ if (collision.castRay(mRayStart, mRayEnd, mFilterDirection, mVerticalHitPoint,
+ mVerticalHitNormal, parentObject)) {
+
+ // If we found a collision, use this surface as our vertical intersection
+ // for this frame, even if the sweep above also found something.
+ verticalHit = true;
+ // snap
+ if (mVerticalHitNormal.y > 0.0f) {
+ mCurrentPosition.y = (mVerticalHitPoint.y - bottom);
+ } else if (mVerticalHitNormal.y < 0.0f) {
+ mCurrentPosition.y = (mVerticalHitPoint.y - top);
+ }
+ }
+
+
+ // Now the horizontal version of the same test
+ float xStart = left;
+ float xEnd = right;
+ if (mDelta.x < 0.0f) {
+ xStart = right;
+ xEnd = left;
+ }
+
+
+ mRayStart.set(xStart, centerOffsetY);
+ mRayStart.add(mCurrentPosition);
+
+ mRayEnd.set(xEnd, centerOffsetY);
+ mRayEnd.add(mCurrentPosition);
+
+ mFilterDirection.set(mDelta);
+
+ if (collision.castRay(mRayStart, mRayEnd, mFilterDirection, mHorizontalHitPoint,
+ mHorizontalHitNormal, parentObject)) {
+
+ // If we found a collision, use this surface as our horizontal intersection
+ // for this frame, even if the sweep above also found something.
+ horizontalHit = true;
+ // snap
+ if (mHorizontalHitNormal.x > 0.0f) {
+ mCurrentPosition.x = (mHorizontalHitPoint.x - left);
+ } else if (mHorizontalHitNormal.x < 0.0f) {
+ mCurrentPosition.x = (mHorizontalHitPoint.x - right);
+ }
+ }
+ }
+
+
+ // Record the intersection for other systems to use.
+ final TimeSystem timeSystem = sSystemRegistry.timeSystem;
+
+ if (timeSystem != null) {
+ float time = timeSystem.getGameTime();
+ if (horizontalHit) {
+ if (mHorizontalHitNormal.x > 0.0f) {
+ parentObject.setLastTouchedLeftWallTime(time);
+ } else {
+ parentObject.setLastTouchedRightWallTime(time);
+ }
+ //parentObject.setBackgroundCollisionNormal(mHorizontalHitNormal);
+ }
+
+ if (verticalHit) {
+ if (mVerticalHitNormal.y > 0.0f) {
+ parentObject.setLastTouchedFloorTime(time);
+ } else {
+ parentObject.setLastTouchedCeilingTime(time);
+ }
+ //parentObject.setBackgroundCollisionNormal(mVerticalHitNormal);
+ }
+
+
+ // If we hit multiple surfaces, merge their normals together to produce an
+ // average direction of obstruction.
+ if (true) { //(verticalHit && horizontalHit) {
+ mMergedNormal.set(mVerticalHitNormal);
+ mMergedNormal.add(mHorizontalHitNormal);
+ mMergedNormal.normalize();
+ parentObject.setBackgroundCollisionNormal(mMergedNormal);
+ }
+
+ parentObject.setPosition(mCurrentPosition);
+ }
+
+ }
+ }
+ mPreviousPosition.set(parentObject.getPosition());
+ }
+
+ /* Sweeps the space between two points looking for surfaces that oppose horizontal movement. */
+ protected boolean sweepHorizontal(Vector2 previousPosition, Vector2 currentPosition, Vector2 delta,
+ int left, int right, float centerY, Vector2 hitPoint, Vector2 hitNormal,
+ GameObject parentObject) {
+ boolean hit = false;
+ if (!Utils.close(delta.x, 0.0f)) {
+ CollisionSystem collision = sSystemRegistry.collisionSystem;
+
+ // Shoot a ray from the center of the previous frame's box to the edge (left or right,
+ // depending on the direction of movement) of the current box.
+ mTestPointStart.y = (centerY);
+ mTestPointStart.x = (left);
+ int offset = -left;
+ if (delta.x > 0.0f) {
+ mTestPointStart.x = (right);
+ offset = -right;
+ }
+
+ // Filter out surfaces that do not oppose motion in the horizontal direction, or
+ // push in the same direction as movement.
+ mFilterDirection.set(delta);
+ mFilterDirection.y = (0);
+
+ mTestPointEnd.set(currentPosition);
+ mTestPointEnd.add(mTestPointStart);
+ if (collision.castRay(previousPosition, mTestPointEnd, mFilterDirection,
+ hitPoint, hitNormal, parentObject)) {
+ // snap
+ currentPosition.x = (hitPoint.x + offset);
+ hit = true;
+ }
+ }
+ return hit;
+ }
+
+ /* Sweeps the space between two points looking for surfaces that oppose vertical movement. */
+ protected boolean sweepVertical(Vector2 previousPosition, Vector2 currentPosition, Vector2 delta,
+ int bottom, int top, float centerX, Vector2 hitPoint, Vector2 hitNormal,
+ GameObject parentObject) {
+ boolean hit = false;
+ if (!Utils.close(delta.y, 0.0f)) {
+ CollisionSystem collision = sSystemRegistry.collisionSystem;
+ // Shoot a ray from the center of the previous frame's box to the edge (top or bottom,
+ // depending on the direction of movement) of the current box.
+ mTestPointStart.x = (centerX);
+ mTestPointStart.y = (bottom);
+ int offset = -bottom;
+ if (delta.y > 0.0f) {
+ mTestPointStart.y = (top);
+ offset = -top;
+ }
+
+ mFilterDirection.set(delta);
+ mFilterDirection.x = (0);
+
+ mTestPointEnd.set(currentPosition);
+ mTestPointEnd.add(mTestPointStart);
+ if (collision.castRay(previousPosition, mTestPointEnd, mFilterDirection,
+ hitPoint, hitNormal, parentObject)) {
+ hit = true;
+ // snap
+ currentPosition.y = (hitPoint.y + offset);
+ }
+
+ }
+ return hit;
+ }
+
+ /** Comparator for hit points. */
+ private static class HitPointDistanceComparator implements Comparator<HitPoint> {
+ private Vector2 mOrigin;
+
+ public HitPointDistanceComparator() {
+ super();
+ mOrigin = new Vector2();
+ }
+
+ public final void setOrigin(Vector2 origin) {
+ mOrigin.set(origin);
+ }
+
+ public final void setOrigin(float x, float y) {
+ mOrigin.set(x, y);
+ }
+
+ public int compare(HitPoint object1, HitPoint object2) {
+ int result = 0;
+ if (object1 != null && object2 != null) {
+ final float obj1Distance = object1.hitPoint.distance2(mOrigin);
+ final float obj2Distance = object2.hitPoint.distance2(mOrigin);
+ final float distanceDelta = obj1Distance - obj2Distance;
+ result = distanceDelta < 0.0f ? -1 : 1;
+ } else if (object1 == null && object2 != null) {
+ result = 1;
+ } else if (object2 == null && object1 != null) {
+ result = -1;
+ }
+ return result;
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/BaseObject.java b/src/com/replica/replicaisland/BaseObject.java
new file mode 100644
index 0000000..4c27c14
--- /dev/null
+++ b/src/com/replica/replicaisland/BaseObject.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+/**
+ * The core object from which most other objects are derived. Anything that will be managed by
+ * an ObjectManager, and anything that requires an update per frame should be derived from
+ * BaseObject. BaseObject also defines the interface for the object-wide system registry.
+ */
+public abstract class BaseObject extends AllocationGuard {
+ static ObjectRegistry sSystemRegistry = new ObjectRegistry();
+
+ public BaseObject() {
+ super();
+ }
+
+ /**
+ * Update this object.
+ * @param timeDelta The duration since the last update (in seconds).
+ * @param parent The parent of this object (may be NULL).
+ */
+ public void update(float timeDelta, BaseObject parent) {
+ // Base class does nothing.
+ }
+
+
+ public abstract void reset();
+
+}
diff --git a/src/com/replica/replicaisland/BufferLibrary.java b/src/com/replica/replicaisland/BufferLibrary.java
new file mode 100644
index 0000000..96ccd8d
--- /dev/null
+++ b/src/com/replica/replicaisland/BufferLibrary.java
@@ -0,0 +1,58 @@
+package com.replica.replicaisland;
+
+import javax.microedition.khronos.opengles.GL10;
+
+public class BufferLibrary extends BaseObject {
+ private static final int GRID_LIST_SIZE = 256;
+ private FixedSizeArray<Grid> mGridList;
+
+ public BufferLibrary() {
+ super();
+
+ mGridList = new FixedSizeArray<Grid>(GRID_LIST_SIZE);
+ }
+
+ @Override
+ public void reset() {
+ removeAll();
+ }
+
+ public void add(Grid grid) {
+ mGridList.add(grid);
+ }
+
+ public void removeAll() {
+ mGridList.clear();
+ }
+
+ public void generateHardwareBuffers(GL10 gl) {
+ if (sSystemRegistry.contextParameters.supportsVBOs) {
+ final int count = mGridList.getCount();
+ for (int x = 0; x < count; x++) {
+ Grid grid = mGridList.get(x);
+ grid.generateHardwareBuffers(gl);
+ }
+ }
+ }
+
+ public void releaseHardwareBuffers(GL10 gl) {
+ if (sSystemRegistry.contextParameters.supportsVBOs) {
+ final int count = mGridList.getCount();
+ for (int x = 0; x < count; x++) {
+ Grid grid = mGridList.get(x);
+ grid.releaseHardwareBuffers(gl);
+ }
+ }
+ }
+
+ public void invalidateHardwareBuffers() {
+ if (sSystemRegistry.contextParameters.supportsVBOs) {
+ final int count = mGridList.getCount();
+ for (int x = 0; x < count; x++) {
+ Grid grid = mGridList.get(x);
+ grid.invalidateHardwareBuffers();
+ }
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/ButtonAnimationComponent.java b/src/com/replica/replicaisland/ButtonAnimationComponent.java
new file mode 100644
index 0000000..77758af
--- /dev/null
+++ b/src/com/replica/replicaisland/ButtonAnimationComponent.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.SoundSystem.Sound;
+
+public class ButtonAnimationComponent extends GameComponent {
+ public static final class Animation {
+ // Animations
+ public static final int UP = 0;
+ public static final int DOWN = 1;
+ }
+ private ChannelSystem.Channel mChannel;
+ private SpriteComponent mSprite;
+ private ChannelSystem.ChannelFloatValue mLastPressedTime;
+ private Sound mDepressSound;
+
+ public ButtonAnimationComponent() {
+ super();
+ setPhase(ComponentPhases.ANIMATION.ordinal());
+ mLastPressedTime = new ChannelSystem.ChannelFloatValue();
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mSprite = null;
+ mChannel = null;
+ mLastPressedTime.value = 0.0f;
+ mDepressSound = null;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mSprite != null) {
+ GameObject parentObject = (GameObject)parent;
+
+ if (parentObject.getCurrentAction() == GameObject.ActionType.HIT_REACT &&
+ parentObject.lastReceivedHitType == CollisionParameters.HitType.DEPRESS) {
+ if (mSprite.getCurrentAnimation() == Animation.UP) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.play(mDepressSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+ mSprite.playAnimation(Animation.DOWN);
+ parentObject.setCurrentAction(GameObject.ActionType.IDLE);
+ if (mChannel != null) {
+ TimeSystem time = sSystemRegistry.timeSystem;
+ mLastPressedTime.value = time.getGameTime();
+ mChannel.value = mLastPressedTime;
+ }
+ } else {
+ mSprite.playAnimation(Animation.UP);
+
+ }
+ }
+ }
+
+ public void setSprite(SpriteComponent sprite) {
+ mSprite = sprite;
+ }
+
+ public void setChannel(ChannelSystem.Channel channel) {
+ mChannel = channel;
+ }
+
+ public void setDepressSound(Sound sound) {
+ mDepressSound = sound;
+ }
+}
diff --git a/src/com/replica/replicaisland/ButtonConstants.java b/src/com/replica/replicaisland/ButtonConstants.java
new file mode 100644
index 0000000..a4e3819
--- /dev/null
+++ b/src/com/replica/replicaisland/ButtonConstants.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+public final class ButtonConstants {
+ public static final int FLY_BUTTON_REGION_X = 0;
+ public static final int FLY_BUTTON_REGION_Y = 0;
+ public static final int FLY_BUTTON_REGION_WIDTH = 90; //128 - 24;
+ public static final int FLY_BUTTON_REGION_HEIGHT = 256;
+
+ public static final int STOMP_BUTTON_REGION_X = 100;
+ public static final int STOMP_BUTTON_REGION_Y = 0;
+ public static final int STOMP_BUTTON_REGION_WIDTH = 70;
+ public static final int STOMP_BUTTON_REGION_HEIGHT = 80;
+}
diff --git a/src/com/replica/replicaisland/CameraBiasComponent.java b/src/com/replica/replicaisland/CameraBiasComponent.java
new file mode 100644
index 0000000..e3dba63
--- /dev/null
+++ b/src/com/replica/replicaisland/CameraBiasComponent.java
@@ -0,0 +1,22 @@
+package com.replica.replicaisland;
+
+public class CameraBiasComponent extends GameComponent {
+ public CameraBiasComponent() {
+ super();
+ setPhase(GameComponent.ComponentPhases.THINK.ordinal());
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject)parent;
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ if (camera != null) {
+ camera.addCameraBias(parentObject.getPosition());
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/CameraSystem.java b/src/com/replica/replicaisland/CameraSystem.java
new file mode 100644
index 0000000..6ac9694
--- /dev/null
+++ b/src/com/replica/replicaisland/CameraSystem.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Manages the position of the camera based on a target game object.
+ */
+public class CameraSystem extends BaseObject {
+ private GameObject mTarget;
+ private float mShakeTime;
+ private float mShakeMagnitude;
+ private float mShakeOffsetY;
+ private Vector2 mCurrentCameraPosition;
+ private Vector2 mFocalPosition;
+ private Vector2 mPreInterpolateCameraPosition;
+ private Vector2 mTargetPosition;
+ private Vector2 mBias;
+ private float mTargetChangedTime;
+
+ private static final float X_FOLLOW_DISTANCE = 0.0f;
+ private static final float Y_UP_FOLLOW_DISTANCE = 90.0f;
+ private static final float Y_DOWN_FOLLOW_DISTANCE = 0.0f;
+
+ private static final float MAX_INTERPOLATE_TO_TARGET_DISTANCE = 300.0f;
+ private static final float INTERPOLATE_TO_TARGET_TIME = 1.0f;
+
+ private static int SHAKE_FREQUENCY = 40;
+
+ private static float BIAS_SPEED = 400.0f;
+
+ public CameraSystem() {
+ super();
+ mCurrentCameraPosition = new Vector2();
+ mFocalPosition = new Vector2();
+ mPreInterpolateCameraPosition = new Vector2();
+ mTargetPosition = new Vector2();
+ mBias = new Vector2();
+ }
+
+ @Override
+ public void reset() {
+ mTarget = null;
+ mCurrentCameraPosition.zero();
+ mShakeTime = 0.0f;
+ mShakeMagnitude = 0.0f;
+ mFocalPosition.zero();
+ mTargetChangedTime = 0.0f;
+ mPreInterpolateCameraPosition.zero();
+ mTargetPosition.zero();
+ }
+
+ void setTarget(GameObject target) {
+ if (target != null && mTarget != target) {
+ mPreInterpolateCameraPosition.set(mCurrentCameraPosition);
+ mPreInterpolateCameraPosition.subtract(target.getPosition());
+ if (mPreInterpolateCameraPosition.length2() <
+ MAX_INTERPOLATE_TO_TARGET_DISTANCE * MAX_INTERPOLATE_TO_TARGET_DISTANCE) {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ mTargetChangedTime = time.getGameTime();
+ mPreInterpolateCameraPosition.set(mCurrentCameraPosition);
+ } else {
+ mTargetChangedTime = 0.0f;
+ mCurrentCameraPosition.set(target.getPosition());
+ }
+ }
+
+ mTarget = target;
+
+ }
+
+ public GameObject getTarget() {
+ return mTarget;
+ }
+
+ void shake(float duration, float magnitude) {
+ mShakeTime = duration;
+ mShakeMagnitude = magnitude;
+ }
+
+ public boolean shaking() {
+ return mShakeTime > 0.0f;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+
+ mShakeOffsetY = 0.0f;
+
+ if (mShakeTime > 0.0f) {
+ mShakeTime -= timeDelta;
+ mShakeOffsetY = (float) (Math.sin(mShakeTime * SHAKE_FREQUENCY) * mShakeMagnitude);
+ }
+
+ if (mTarget != null) {
+ mTargetPosition.set(mTarget.getCenteredPositionX(), mTarget.getCenteredPositionY());
+ final Vector2 targetPosition = mTargetPosition;
+
+ if (mTargetChangedTime > 0.0f) {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float delta = time.getGameTime() - mTargetChangedTime;
+
+ mCurrentCameraPosition.x = Lerp.ease(mPreInterpolateCameraPosition.x,
+ targetPosition.x, INTERPOLATE_TO_TARGET_TIME, delta);
+
+ mCurrentCameraPosition.y = Lerp.ease(mPreInterpolateCameraPosition.y,
+ targetPosition.y, INTERPOLATE_TO_TARGET_TIME, delta);
+
+ if (delta > INTERPOLATE_TO_TARGET_TIME) {
+ mTargetChangedTime = -1;
+ }
+ } else {
+
+ // Only respect the bias if the target is moving. No camera motion without
+ // player input!
+ if (mBias.length2() > 0.0f && mTarget.getVelocity().length2() > 1.0f) {
+ mBias.normalize();
+ mBias.multiply(BIAS_SPEED * timeDelta);
+ mCurrentCameraPosition.add(mBias);
+ }
+
+ final float xDelta = targetPosition.x - mCurrentCameraPosition.x;
+ if (Math.abs(xDelta) > X_FOLLOW_DISTANCE) {
+ mCurrentCameraPosition.x = targetPosition.x - (X_FOLLOW_DISTANCE * Utils.sign(xDelta));
+ }
+
+
+ final float yDelta = targetPosition.y - mCurrentCameraPosition.y;
+ if (yDelta > Y_UP_FOLLOW_DISTANCE) {
+ mCurrentCameraPosition.y = targetPosition.y - Y_UP_FOLLOW_DISTANCE;
+ } else if (yDelta < -Y_DOWN_FOLLOW_DISTANCE) {
+ mCurrentCameraPosition.y = targetPosition.y + Y_DOWN_FOLLOW_DISTANCE;
+ }
+
+ }
+
+ mBias.zero();
+
+ }
+
+ mFocalPosition.x = (float) Math.floor(mCurrentCameraPosition.x);
+ mFocalPosition.x = snapFocalPointToWorldBoundsX(mFocalPosition.x);
+
+ mFocalPosition.y = (float) Math.floor(mCurrentCameraPosition.y + mShakeOffsetY);
+ mFocalPosition.y = snapFocalPointToWorldBoundsY(mFocalPosition.y);
+ }
+
+ /** Returns the x position of the camera's look-at point. */
+ public float getFocusPositionX() {
+ return mFocalPosition.x;
+ }
+
+ /** Returns the y position of the camera's look-at point. */
+ public float getFocusPositionY() {
+ return mFocalPosition.y;
+ }
+
+ public boolean pointVisible(Vector2 point, float radius) {
+ boolean visible = false;
+ final float width = sSystemRegistry.contextParameters.gameWidth / 2.0f;
+ final float height = sSystemRegistry.contextParameters.gameHeight / 2.0f;
+ if (Math.abs(mFocalPosition.x - point.x) < (width + radius)) {
+ if (Math.abs(mFocalPosition.y - point.y) < (height + radius)) {
+ visible = true;
+ }
+ }
+ return visible;
+ }
+
+ /** Snaps a coordinate against the bounds of the world so that it may not pass out
+ * of the visible area of the world.
+ * @param worldX An x-coordinate in world units.
+ * @return An x-coordinate that is guaranteed not to expose the edges of the world.
+ */
+ public float snapFocalPointToWorldBoundsX(float worldX) {
+ float focalPositionX = worldX;
+ final float width = sSystemRegistry.contextParameters.gameWidth;
+ final LevelSystem level = sSystemRegistry.levelSystem;
+ if (level != null) {
+ final float worldPixelWidth = Math.max(level.getLevelWidth(), width);
+ final float rightEdge = focalPositionX + (width / 2.0f);
+ final float leftEdge = focalPositionX - (width / 2.0f);
+
+ if (rightEdge > worldPixelWidth) {
+ focalPositionX = worldPixelWidth - (width / 2.0f);
+ } else if (leftEdge < 0) {
+ focalPositionX = width / 2.0f;
+ }
+ }
+ return focalPositionX;
+ }
+
+ /** Snaps a coordinate against the bounds of the world so that it may not pass out
+ * of the visible area of the world.
+ * @param worldY A y-coordinate in world units.
+ * @return A y-coordinate that is guaranteed not to expose the edges of the world.
+ */
+ public float snapFocalPointToWorldBoundsY(float worldY) {
+ float focalPositionY = worldY;
+
+ final float height = sSystemRegistry.contextParameters.gameHeight;
+ final LevelSystem level = sSystemRegistry.levelSystem;
+ if (level != null) {
+ final float worldPixelHeight = Math.max(level.getLevelHeight(), sSystemRegistry.contextParameters.gameHeight);
+ final float topEdge = focalPositionY + (height / 2.0f);
+ final float bottomEdge = focalPositionY - (height / 2.0f);
+
+ if (topEdge > worldPixelHeight) {
+ focalPositionY = worldPixelHeight - (height / 2.0f);
+ } else if (bottomEdge < 0) {
+ focalPositionY = height / 2.0f;
+ }
+ }
+
+ return focalPositionY;
+ }
+
+ public void addCameraBias(Vector2 bias) {
+ final float x = bias.x - mFocalPosition.x;
+ final float y = bias.y - mFocalPosition.y;
+ final float biasX = mBias.x;
+ final float biasY = mBias.y;
+ mBias.set(x, y);
+ mBias.normalize();
+ mBias.add(biasX, biasY);
+ }
+
+
+}
diff --git a/src/com/replica/replicaisland/ChangeComponentsComponent.java b/src/com/replica/replicaisland/ChangeComponentsComponent.java
new file mode 100644
index 0000000..9127d81
--- /dev/null
+++ b/src/com/replica/replicaisland/ChangeComponentsComponent.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A game component that can swap other components in and out of its parent game object. The
+ * purpose of the ChangeComponentsComponent is to allow game objects to have different "modes"
+ * defined by different combinations of GameComponents. ChangeComponentsComponent manages the
+ * switching in and out of those modes by activating and deactivating specific game components.
+ */
+public class ChangeComponentsComponent extends GameComponent {
+ private final static int MAX_COMPONENT_SWAPS = 16;
+ private FixedSizeArray<GameComponent> mComponentsToInsert;
+ private FixedSizeArray<GameComponent> mComponentsToRemove;
+ private boolean mPingPong;
+ private boolean mActivated;
+ private boolean mCurrentlySwapped;
+ private GameObject.ActionType mSwapOnAction;
+ private GameObject.ActionType mLastAction;
+
+ public ChangeComponentsComponent() {
+ super();
+
+ mComponentsToInsert = new FixedSizeArray<GameComponent>(MAX_COMPONENT_SWAPS);
+ mComponentsToRemove = new FixedSizeArray<GameComponent>(MAX_COMPONENT_SWAPS);
+
+ reset();
+ }
+
+ @Override
+ public void reset() {
+
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ // GameComponents hanging out in the mComponentsToInsert list are not part of the object
+ // hierarchy, so we need to manually release them.
+ if (factory != null) {
+ FixedSizeArray<GameComponent> unrelasedComponents = mComponentsToInsert;
+ if (mActivated) {
+ if (!mPingPong) {
+ // if we've activated and are not set to ping pong, the contents of
+ // mComponentsToInsert have already been inserted into the object and
+ // will be cleaned up with all the other of the object's components.
+ // In that case, mComponentsToRemove contains objects that need manual
+ // clean up.
+ unrelasedComponents = mComponentsToRemove;
+ }
+ }
+ final int inactiveComponentCount = unrelasedComponents.getCount();
+ for (int x = 0; x < inactiveComponentCount; x++) {
+ GameComponent component = unrelasedComponents.get(x);
+ if (!component.shared) {
+ factory.releaseComponent(component);
+ }
+ }
+ }
+ mComponentsToInsert.clear();
+ mComponentsToRemove.clear();
+ mPingPong = false;
+ mActivated = false;
+ mCurrentlySwapped = false;
+ mSwapOnAction = GameObject.ActionType.INVALID;
+ mLastAction = GameObject.ActionType.INVALID;
+ }
+
+
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mSwapOnAction != GameObject.ActionType.INVALID) {
+ GameObject parentObject = (GameObject)parent;
+ GameObject.ActionType currentAction = parentObject.getCurrentAction();
+ if (currentAction != mLastAction) {
+ mLastAction = currentAction;
+ if (currentAction == mSwapOnAction) {
+ activate(parentObject);
+ }
+ }
+ }
+ }
+
+ public void addSwapInComponent(GameComponent component) {
+ mComponentsToInsert.add(component);
+ }
+
+ public void addSwapOutComponent(GameComponent component) {
+ mComponentsToRemove.add(component);
+ }
+
+ public void setPingPongBehavior(boolean pingPong) {
+ mPingPong = pingPong;
+ }
+
+ public void setSwapAction(GameObject.ActionType action) {
+ mSwapOnAction = action;
+ }
+
+ /** Inserts and removes components added to the swap-in and swap-out list, respectively.
+ * Unless mPingPong is set, this may only be called once.
+ * @param parent The parent object to swap components on.
+ */
+ public void activate(GameObject parent) {
+ if (!mActivated || mPingPong) {
+ final int removeCount = mComponentsToRemove.getCount();
+ for (int x = 0; x < removeCount; x++) {
+ parent.remove(mComponentsToRemove.get(x));
+ }
+
+ final int addCount = mComponentsToInsert.getCount();
+ for (int x = 0; x < addCount; x++) {
+ parent.add(mComponentsToInsert.get(x));
+ }
+
+ mActivated = true;
+ mCurrentlySwapped = !mCurrentlySwapped;
+ if (mPingPong) {
+ FixedSizeArray<GameComponent> swap = mComponentsToInsert;
+ mComponentsToInsert = mComponentsToRemove;
+ mComponentsToRemove = swap;
+ }
+ }
+ }
+
+ public boolean getCurrentlySwapped() {
+ return mCurrentlySwapped;
+ }
+}
diff --git a/src/com/replica/replicaisland/ChannelSystem.java b/src/com/replica/replicaisland/ChannelSystem.java
new file mode 100644
index 0000000..7b0d6e1
--- /dev/null
+++ b/src/com/replica/replicaisland/ChannelSystem.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+public class ChannelSystem extends BaseObject {
+ private static final int CHANNEL_COUNT = 8;
+ private static final ChannelComparator sChannelComparator = new ChannelComparator();
+ private FixedSizeArray<Channel> mChannels;
+ private Channel mSearchDummy;
+ private int mRegisteredChannelCount;
+
+ public ChannelSystem() {
+ super();
+ mChannels = new FixedSizeArray<Channel>(CHANNEL_COUNT);
+ mChannels.setComparator(sChannelComparator);
+ mSearchDummy = new Channel();
+
+ for (int x = 0; x < CHANNEL_COUNT; x++) {
+ mChannels.add(new Channel());
+ }
+
+ mRegisteredChannelCount = 0;
+ }
+
+ @Override
+ public void reset() {
+ for (int x = 0; x < CHANNEL_COUNT; x++) {
+ mChannels.get(x).name = null;
+ mChannels.get(x).value = null;
+ }
+
+ mRegisteredChannelCount = 0;
+ }
+
+ public Channel registerChannel(String name) {
+ Channel result = null;
+ mSearchDummy.name = name;
+ final int index = mChannels.find(mSearchDummy, false);
+ if (index == -1) {
+ // Add a new channel.
+ assert mRegisteredChannelCount < CHANNEL_COUNT : "Channel pool exhausted!";
+
+ if (mRegisteredChannelCount < CHANNEL_COUNT) {
+ result = mChannels.get(mRegisteredChannelCount);
+ mRegisteredChannelCount++;
+ result.name = name;
+ mChannels.sort(true);
+ }
+ } else {
+ result = mChannels.get(index);
+ }
+
+ return result;
+ }
+
+ public class Channel {
+ public String name;
+ public Object value;
+ }
+
+ public static class ChannelFloatValue {
+ public float value;
+ }
+
+ public static class ChannelBooleanValue {
+ public boolean value;
+ }
+
+ /** Comparator for channels. */
+ private final static class ChannelComparator implements Comparator<Channel> {
+ public int compare(final Channel object1, final Channel object2) {
+ int result = 0;
+ if (object1 == null && object2 != null) {
+ result = 1;
+ } else if (object1 != null && object2 == null) {
+ result = -1;
+ } else if (object1 != null && object2 != null) {
+ if (object1.name == null && object2.name != null) {
+ result = 1;
+ } else if (object1.name != null && object2.name == null) {
+ result = -1;
+ } else if (object1.name != null && object2.name != null) {
+ result = object1.name.compareTo(object2.name);
+ }
+ }
+ return result;
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/CollisionParameters.java b/src/com/replica/replicaisland/CollisionParameters.java
new file mode 100644
index 0000000..b9071fe
--- /dev/null
+++ b/src/com/replica/replicaisland/CollisionParameters.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * CollisionParamaters defines global parameters related to dynamic (object vs object) collisions.
+ */
+public final class CollisionParameters {
+ // HitType describes the type of hit that a victim object receives. Victims may choose to
+ // react differently to the intersection depending on the hit type.
+ // TODO: Make this a bit field so that objects can support multiple hit types.
+ public final class HitType {
+ public final static int INVALID = 0; // No type.
+ public final static int HIT = 1; // Standard hit type. Life is reduced by 1.
+ public final static int DEATH = 2; // Causes instant death.
+ public final static int COLLECT = 3; // Causes collectable objects to be collected by the attacker.
+ public final static int POSSESS = 4; // Causes possessable objects to become possessed.
+ public final static int DEPRESS = 5; // A hit indicating that the attacker is pressing into the victim.
+ public final static int LAUNCH = 6; // A hit indicating that the attacker will launch the victim.
+ }
+
+
+}
diff --git a/src/com/replica/replicaisland/CollisionSystem.java b/src/com/replica/replicaisland/CollisionSystem.java
new file mode 100644
index 0000000..dd6dd5a
--- /dev/null
+++ b/src/com/replica/replicaisland/CollisionSystem.java
@@ -0,0 +1,858 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.content.res.AssetManager;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Collision detection system. Provides a ray-based interface for finding surfaces in the collision
+ * world. This version is based on a collision world of line segments, organized into an array of
+ * tiles. The underlying detection algorithm isn't relevant to calling code, however, so this class
+ * may be extended to provide a completely different collision detection scheme.
+ *
+ * This class also provides a system for runtime-generated collision segments. These temporary
+ * segments are cleared each frame, and consequently must be constantly re-submitted if they are
+ * intended to persist. Temporary segments are useful for dynamic solid objects, such as moving
+ * platforms.
+ *
+ * CollisionSystem.TileVisitor is an interface for traversing individual collision tiles. Ray casts
+ * can be used to run user code over the collision world by passing different TileVisitor
+ * implementations to executeRay. Provided is TileTestVisitor, a visitor that compares the segments
+ * of each tile visited with the ray and searches for points of intersection.
+ *
+ */
+public class CollisionSystem extends BaseObject {
+ private TiledWorld mWorld;
+ private CollisionTile[] mCollisionTiles;
+ private LineSegmentPool mSegmentPool;
+ private int mTileWidth;
+ private int mTileHeight;
+ private TileTestVisitor mTileSegmentTester;
+ private FixedSizeArray<LineSegment> mTemporarySegments;
+ private FixedSizeArray<LineSegment> mPendingTemporarySegments;
+ private byte[] mWorkspaceBytes; // Included here to avoid runtime allocation during file io.
+
+ private static final int MAX_TEMPORARY_SEGMENTS = 256;
+
+ public CollisionSystem() {
+ super();
+ mTileSegmentTester = new TileTestVisitor();
+ mSegmentPool = new LineSegmentPool(MAX_TEMPORARY_SEGMENTS);
+
+ mTemporarySegments = new FixedSizeArray<LineSegment>(MAX_TEMPORARY_SEGMENTS);
+ mPendingTemporarySegments = new FixedSizeArray<LineSegment>(MAX_TEMPORARY_SEGMENTS);
+
+ mWorkspaceBytes = new byte[4];
+ }
+
+ @Override
+ public void reset() {
+ mWorld = null;
+ mCollisionTiles = null;
+
+ final int count = mTemporarySegments.getCount();
+ for (int x = 0; x < count; x++) {
+ mSegmentPool.release(mTemporarySegments.get(x));
+ mTemporarySegments.set(x, null);
+ }
+ mTemporarySegments.clear();
+
+ final int pendingCount = mPendingTemporarySegments.getCount();
+ for (int x = 0; x < pendingCount; x++) {
+ mSegmentPool.release(mPendingTemporarySegments.get(x));
+ mPendingTemporarySegments.set(x, null);
+ }
+ mPendingTemporarySegments.clear();
+ }
+
+ /* Sets the current collision world to the supplied tile world. */
+ public void initialize(TiledWorld world, int tileWidth, int tileHeight) {
+ mWorld = world;
+
+ mTileWidth = tileWidth;
+ mTileHeight = tileHeight;
+ }
+
+ /**
+ * Casts a ray into the collision world. The ray is bound by the start and end points supplied.
+ * The first intersecting segment that is found is returned; in the case where more than one
+ * segment is found, the segment closest to the start point is returned.
+ *
+ * @param startPoint The starting point for the ray in world units.
+ * @param endPoint The end point for the ray in world units.
+ * @param movementDirection If set, only segments with normals that oppose this direction will
+ * be counted as valid intersections. If null, all intersecting segments will be
+ * considered valid.
+ * @param hitPoint The point of intersection between a ray and a surface, if one is found.
+ * @param hitNormal The normal of the intersecting surface if an intersection is found.
+ * @param excludeObject If set, dynamic surfaces from this object will be ignored.
+ * @return true if a valid intersecting surface was found, false otherwise.
+ */
+ // TODO: switch to return data as a HitPoint.
+ public boolean castRay(Vector2 startPoint, Vector2 endPoint, Vector2 movementDirection,
+ Vector2 hitPoint, Vector2 hitNormal, GameObject excludeObject) {
+
+ boolean hit = false;
+
+ mTileSegmentTester.setup(movementDirection, mTileWidth, mTileHeight);
+
+ if (mCollisionTiles != null &&
+ executeRay(startPoint, endPoint, hitPoint, hitNormal, mTileSegmentTester) != -1) {
+ hit = true;
+ }
+
+ if (mTemporarySegments.getCount() > 0) {
+ VectorPool vectorPool = sSystemRegistry.vectorPool;
+ Vector2 tempHitPoint = vectorPool.allocate();
+ Vector2 tempHitNormal = vectorPool.allocate();
+
+ if (testSegmentAgainstList(mTemporarySegments, startPoint, endPoint, tempHitPoint,
+ tempHitNormal, movementDirection, excludeObject)) {
+ if (hit) {
+ // Check to see whether this collision is closer to the one we already found or
+ // not.
+ final float firstCollisionDistance = startPoint.distance2(hitPoint);
+ if (firstCollisionDistance > startPoint.distance2(tempHitPoint)) {
+ // The temporary surface is closer.
+ hitPoint.set(tempHitPoint);
+ hitNormal.set(tempHitNormal);
+ }
+ } else {
+ hit = true;
+ hitPoint.set(tempHitPoint);
+ hitNormal.set(tempHitNormal);
+ }
+ }
+
+ vectorPool.release(tempHitPoint);
+ vectorPool.release(tempHitNormal);
+ }
+
+ return hit;
+ }
+
+ public boolean testBox(float left, float right, float top, float bottom,
+ Vector2 movementDirection, FixedSizeArray<HitPoint> hitPoints,
+ GameObject excludeObject, boolean testDynamicSurfacesOnly) {
+
+ boolean foundHit = false;
+
+ // Test against the background.
+ if (!testDynamicSurfacesOnly) {
+ float startX = left;
+ float endX = right;
+ float startY = bottom;
+ float endY = top;
+ int xIncrement = 1;
+ int yIncrement = 1;
+
+ if (movementDirection != null) {
+ if (movementDirection.x < 0.0f) {
+ startX = right;
+ endX = left;
+ xIncrement = -1;
+ }
+ if (movementDirection.y < 0.0f) {
+ startY = top;
+ endY = bottom;
+ yIncrement = -1;
+ }
+ }
+ final int startTileX = Utils.clamp((int)(startX / mTileWidth), 0, mWorld.getWidth() - 1);
+ final int endTileX = Utils.clamp((int)(endX / mTileWidth), 0, mWorld.getWidth() - 1);
+ final int startTileY = Utils.clamp((int)(startY / mTileHeight), 0, mWorld.getHeight() - 1);
+ final int endTileY = Utils.clamp((int)(endY / mTileHeight), 0, mWorld.getHeight() - 1);
+
+ VectorPool vectorPool = sSystemRegistry.vectorPool;
+ Vector2 worldTileOffset = vectorPool.allocate();
+
+ final int[][] tileArray = mWorld.getTiles();
+ final int worldHeight = mWorld.getHeight() - 1;
+
+
+ for (int y = startTileY; y != endTileY + yIncrement; y += yIncrement) {
+ for (int x = startTileX; x != endTileX + xIncrement; x += xIncrement) {
+ final int tileIndex = tileArray[x][worldHeight - y];
+ if (tileIndex >= 0 && tileIndex < mCollisionTiles.length
+ && mCollisionTiles[tileIndex] != null) {
+
+ final float xOffset = x * mTileWidth;
+ final float yOffset = y * mTileHeight;
+
+ final float tileSpaceLeft = left - xOffset;
+ final float tileSpaceRight = right - xOffset;
+ final float tileSpaceTop = top - yOffset;
+ final float tileSpaceBottom = bottom - yOffset;
+
+ worldTileOffset.set(xOffset, yOffset);
+
+ boolean hit = testBoxAgainstList(mCollisionTiles[tileIndex].segments,
+ tileSpaceLeft, tileSpaceRight, tileSpaceTop, tileSpaceBottom,
+ movementDirection, excludeObject, worldTileOffset, hitPoints);
+
+ if (hit) {
+ foundHit = true;
+ }
+
+ }
+ }
+ }
+
+ vectorPool.release(worldTileOffset);
+ }
+ // temporary segments
+ boolean tempHit = testBoxAgainstList(mTemporarySegments,
+ left, right, top, bottom,
+ movementDirection, excludeObject, Vector2.ZERO, hitPoints);
+
+ if (tempHit) {
+ foundHit = true;
+ }
+
+
+
+ return foundHit;
+ }
+
+ /* Inserts a temporary surface into the collision world. It will persist for one frame. */
+ public void addTemporarySurface(Vector2 startPoint, Vector2 endPoint, Vector2 normal,
+ GameObject ownerObject) {
+ LineSegment newSegment = mSegmentPool.allocate();
+
+ newSegment.set(startPoint, endPoint, normal);
+ newSegment.setOwner(ownerObject);
+
+ mPendingTemporarySegments.add(newSegment);
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ // Clear temporary surfaces
+ final int count = mTemporarySegments.getCount();
+ if (mCollisionTiles != null && count > 0) {
+ for (int x = 0; x < count; x++) {
+ mSegmentPool.release(mTemporarySegments.get(x));
+ mTemporarySegments.set(x, null);
+ }
+ mTemporarySegments.clear();
+ }
+
+ // Temporary surfaces must persist for one frame in order to be reliable independent of
+ // frame execution order. So each frame we queue up inserted segments and then swap them
+ // into activity when this system is updated.
+ FixedSizeArray<LineSegment> swap = mTemporarySegments;
+ mTemporarySegments = mPendingTemporarySegments;
+ mPendingTemporarySegments = swap;
+ }
+
+ /**
+ * Shoots a ray through the collision world. This function is similar to executeRay() below,
+ * except that it is optimized for straight lines (either completely horizontal or completely
+ * vertical).
+ *
+ * @param startPoint The starting point for the ray, in world space.
+ * @param endPoint The ending point for the ray in world space.
+ * @param hitPoint Set to the intersection coordinates if an intersection is found.
+ * @param hitNormal Set to the normal of the intersecting surface if an intersection is found.
+ * @param visitor Class defining what work to perform at each tile step.
+ * @return The index of the tile that intersected the ray, or -1 if no intersection was found.
+ */
+ protected int executeStraigtRay(final Vector2 startPoint, final Vector2 endPoint,
+ final int startTileX, final int startTileY, final int endTileX, final int endTileY,
+ final int deltaX, final int deltaY,
+ Vector2 hitPoint, Vector2 hitNormal, TileVisitor visitor) {
+
+ int currentX = startTileX;
+ int currentY = startTileY;
+
+ int xIncrement = 0;
+ int yIncrement = 0;
+ int distance = 0;
+
+ if (deltaX != 0) {
+ distance = Math.abs(deltaX) + 1;
+ xIncrement = Utils.sign(deltaX);
+ } else if (deltaY != 0) {
+ distance = Math.abs(deltaY) + 1;
+ yIncrement = Utils.sign(deltaY);
+ }
+
+ int hitTile = -1;
+ final int worldHeight = mWorld.getHeight() - 1;
+ final int[][] tileArray = mWorld.getTiles();
+ for (int x = 0; x < distance; x++) {
+ final int tileIndex = tileArray[currentX][worldHeight - currentY];
+ if (tileIndex >= 0 && tileIndex < mCollisionTiles.length
+ && mCollisionTiles[tileIndex] != null) {
+ if (visitor.visit(mCollisionTiles[tileIndex], startPoint, endPoint,
+ hitPoint, hitNormal, currentX, currentY)) {
+ hitTile = tileIndex;
+ break;
+ }
+ }
+ currentX += xIncrement;
+ currentY += yIncrement;
+ }
+
+ return hitTile;
+ }
+
+ /**
+ * Shoots a ray through the collision world. Since the collision world is a 2D array of tiles,
+ * this algorithm traces a line in tile space and tests against each non-empty tile it visits.
+ * The Bresenham line algorithm is used for the actual traversal, but the action taken at each
+ * tile is defined by the visitor class passed to this function.
+ *
+ * @param startPoint The starting point for the ray, in world space.
+ * @param endPoint The ending point for the ray in world space.
+ * @param hitPoint Set to the intersection coordinates if an intersection is found.
+ * @param hitNormal Set to the normal of the intersecting surface if an intersection is found.
+ * @param visitor Class defining what work to perform at each tile step.
+ * @return The index of the tile that intersected the ray, or -1 if no intersection was found.
+ */
+ protected int executeRay(Vector2 startPoint, Vector2 endPoint,
+ Vector2 hitPoint, Vector2 hitNormal, TileVisitor visitor) {
+
+ final int worldHeight = mWorld.getHeight();
+ final int worldWidth = mWorld.getWidth();
+
+ final int startTileX = worldToTileColumn(startPoint.x, worldWidth);
+ final int startTileY = worldToTileRow(startPoint.y, worldHeight);
+
+ final int endTileX = worldToTileColumn(endPoint.x, worldWidth);
+ final int endTileY = worldToTileRow(endPoint.y, worldHeight);
+
+ int currentX = startTileX;
+ int currentY = startTileY;
+
+ final int deltaX = endTileX - startTileX;
+ final int deltaY = endTileY - startTileY;
+
+ int hitTile = -1;
+
+ if (deltaX == 0 || deltaY == 0) {
+ hitTile = executeStraigtRay(startPoint, endPoint, startTileX, startTileY,
+ endTileX, endTileY, deltaX, deltaY, hitPoint, hitNormal, visitor);
+ } else {
+
+ final int xIncrement = deltaX != 0 ? Utils.sign(deltaX) : 0;
+ final int yIncrement = deltaY != 0 ? Utils.sign(deltaY) : 0;
+
+ // Note: I'm deviating from the Bresenham algorithm here by adding one to force the end
+ // tile to be visited.
+ final int lateralDelta = Math.abs(deltaX) + 1;
+ final int verticalDelta = Math.abs(deltaY) + 1;
+
+ final int deltaX2 = lateralDelta * 2;
+ final int deltaY2 = verticalDelta * 2;
+
+ final int worldHeightMinusOne = worldHeight - 1;
+ final int[][]tileArray = mWorld.getTiles();
+
+ // Bresenham line algorithm in tile space.
+ if (lateralDelta >= verticalDelta) {
+ int error = deltaY2 - lateralDelta;
+ for (int i = 0; i < lateralDelta; i++) {
+ final int tileIndex = tileArray[currentX][worldHeightMinusOne - currentY];
+ if (tileIndex >= 0 && tileIndex < mCollisionTiles.length
+ && mCollisionTiles[tileIndex] != null) {
+ if (visitor.visit(mCollisionTiles[tileIndex], startPoint, endPoint,
+ hitPoint, hitNormal, currentX, currentY)) {
+ hitTile = tileIndex;
+ break;
+ }
+ }
+
+ if (error > 0) {
+ currentY += yIncrement;
+ error -= deltaX2;
+ }
+
+ error += deltaY2;
+ currentX += xIncrement;
+ }
+ }
+ else if (verticalDelta >= lateralDelta) {
+ int error = deltaX2 - verticalDelta;
+
+ for (int i = 0; i < verticalDelta; i++) {
+ final int tileIndex = tileArray[currentX][worldHeightMinusOne - currentY];
+ if (tileIndex >= 0 && tileIndex < mCollisionTiles.length
+ && mCollisionTiles[tileIndex] != null) {
+ if (visitor.visit(mCollisionTiles[tileIndex], startPoint, endPoint,
+ hitPoint, hitNormal, currentX, currentY)) {
+ hitTile = tileIndex;
+ break;
+ }
+ }
+
+ if (error > 0) {
+ currentX += xIncrement;
+ error -= deltaY2;
+ }
+
+ error += deltaX2;
+ currentY += yIncrement;
+ }
+ }
+ }
+ return hitTile;
+ }
+
+ protected final int worldToTileColumn(final float x, final int width) {
+ return Utils.clamp((int)Math.floor(x / mTileWidth), 0, width - 1);
+ }
+
+ protected final int worldToTileRow(float y, final int height) {
+ return Utils.clamp((int)Math.floor(y / mTileHeight), 0, height - 1);
+ }
+
+ /*
+ * Given a list of segments and a ray, this function performs an intersection search and
+ * returns the closest intersecting segment, if any exists.
+ */
+ protected static boolean testSegmentAgainstList(FixedSizeArray<LineSegment> segments,
+ Vector2 startPoint, Vector2 endPoint, Vector2 hitPoint, Vector2 hitNormal,
+ Vector2 movementDirection, GameObject excludeObject) {
+ boolean foundHit = false;
+ float closestDistance = -1;
+ float hitX = 0;
+ float hitY = 0;
+ float normalX = 0;
+ float normalY = 0;
+ final int count = segments.getCount();
+ final Object[] segmentArray = segments.getArray();
+ for (int x = 0; x < count; x++) {
+ LineSegment segment = (LineSegment)segmentArray[x];
+ // If a movement direction has been passed, filter out invalid surfaces by ignoring
+ // those that do not oppose movement. If no direction has been passed, accept all
+ // surfaces.
+ final float dot = movementDirection.length2() > 0.0f ?
+ movementDirection.dot(segment.mNormal) : -1.0f;
+
+ if (dot < 0.0f &&
+ (excludeObject == null || segment.owner != excludeObject) &&
+ segment.calculateIntersection(startPoint, endPoint, hitPoint)) {
+ final float distance = hitPoint.distance2(startPoint);
+
+ if (!foundHit || closestDistance > distance) {
+ closestDistance = distance;
+ foundHit = true;
+ normalX = segment.mNormal.x;
+ normalY = segment.mNormal.y;
+ // Store the components on their own so we don't have to allocate a vector
+ // in this loop.
+ hitX = hitPoint.x;
+ hitY = hitPoint.y;
+ }
+ }
+ }
+
+ if (foundHit) {
+ hitPoint.set(hitX, hitY);
+ hitNormal.set(normalX, normalY);
+ }
+ return foundHit;
+ }
+
+ protected static boolean testBoxAgainstList(FixedSizeArray<LineSegment> segments,
+ float left, float right, float top, float bottom,
+ Vector2 movementDirection, GameObject excludeObject, Vector2 outputOffset,
+ FixedSizeArray<HitPoint> outputHitPoints) {
+ int hitCount = 0;
+ final int maxSegments = outputHitPoints.getCapacity() - outputHitPoints.getCount();
+ final int count = segments.getCount();
+ final Object[] segmentArray = segments.getArray();
+
+ VectorPool vectorPool = sSystemRegistry.vectorPool;
+ HitPointPool hitPool = sSystemRegistry.hitPointPool;
+
+ Vector2 tempHitPoint = vectorPool.allocate();
+
+ for (int x = 0; x < count && hitCount < maxSegments; x++) {
+ LineSegment segment = (LineSegment)segmentArray[x];
+ // If a movement direction has been passed, filter out invalid surfaces by ignoring
+ // those that do not oppose movement. If no direction has been passed, accept all
+ // surfaces.
+ final float dot = movementDirection.length2() > 0.0f ?
+ movementDirection.dot(segment.mNormal) : -1.0f;
+
+ if (dot < 0.0f &&
+ (excludeObject == null || segment.owner != excludeObject) &&
+ segment.calculateIntersectionBox(left, right, top, bottom, tempHitPoint)) {
+
+ Vector2 hitPoint = vectorPool.allocate(tempHitPoint);
+ Vector2 hitNormal = vectorPool.allocate(segment.mNormal);
+
+ hitPoint.add(outputOffset);
+ HitPoint hit = hitPool.allocate();
+
+ hit.hitPoint = hitPoint;
+ hit.hitNormal = hitNormal;
+
+ outputHitPoints.add(hit);
+
+ hitCount++;
+ }
+ }
+
+ vectorPool.release(tempHitPoint);
+
+ return hitCount > 0;
+ }
+
+ /*
+ * Loads line segments from a binary file and builds the tiled collision database
+ * accordingly.
+ */
+ public boolean loadCollisionTiles(InputStream stream) {
+ boolean success = false;
+ AssetManager.AssetInputStream byteStream = (AssetManager.AssetInputStream) stream;
+ int signature;
+
+ // TODO: this is a hack. I really should only allocate an array that is the size of the
+ // tileset, but at this point I don't actually know that size, so I allocate a buffer that's
+ // probably large enough.
+ mCollisionTiles = new CollisionTile[256];
+ try {
+ signature = (byte)byteStream.read();
+ if (signature == 52) {
+ // This file has the following deserialization format:
+ // read the number of tiles
+ // for each tile
+ // read the tile id
+ // read the number of segments
+ // for each segment
+ // read startx, starty, endx, endy, normalx, normaly
+ final int tileCount = byteStream.read();
+ final int size = (1 + 1 + 4 + 4 + 4 + 4 + 4 + 4) * tileCount;
+ if (byteStream.available() >= size) {
+ for (int x = 0; x < tileCount; x++) {
+ final int tileIndex = byteStream.read();
+ final int segmentCount = byteStream.read();
+
+ if (mCollisionTiles[tileIndex] == null && segmentCount > 0) {
+ mCollisionTiles[tileIndex] = new CollisionTile(segmentCount);
+ }
+
+ for (int y = 0; y < segmentCount; y++) {
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final float startX = Utils.byteArrayToFloat(mWorkspaceBytes);
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final float startY = Utils.byteArrayToFloat(mWorkspaceBytes);
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final float endX = Utils.byteArrayToFloat(mWorkspaceBytes);
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final float endY = Utils.byteArrayToFloat(mWorkspaceBytes);
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final float normalX = Utils.byteArrayToFloat(mWorkspaceBytes);
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final float normalY = Utils.byteArrayToFloat(mWorkspaceBytes);
+
+ // TODO: it might be wise to pool line segments. I don't think that
+ // this data will be loaded very often though, so this is ok for now.
+ LineSegment newSegment = new LineSegment();
+ newSegment.mStartPoint.set(startX, startY);
+ newSegment.mEndPoint.set(endX, endY);
+ newSegment.mNormal.set(normalX, normalY);
+
+ mCollisionTiles[tileIndex].addSegment(newSegment);
+ }
+ }
+ }
+ }
+ } catch (IOException e) {
+ //TODO: figure out the best way to deal with this. Assert?
+ }
+
+ return success;
+ }
+
+
+ /**
+ * An interface for visiting tiles during a ray cast. Implementations of TileVisitor
+ * can be passed to executeRay(); the visit() function will be invoked for each tile touched by
+ * the ray until the traversal is completed or visit() returns false.
+ */
+ public abstract class TileVisitor extends AllocationGuard {
+ public TileVisitor() {
+ super();
+ }
+
+ // If true is returned, tile scanning continues. Otherwise it stops.
+ public abstract boolean visit(CollisionTile tile, Vector2 startPoint, Vector2 endPoint,
+ Vector2 hitPoint, Vector2 hitNormal, int tileX, int tileY);
+ }
+
+ /**
+ * TileTestVisitor tests the ray against a list of segments assigned to each tile. If any
+ * segment in any tile of the ray's path is found to be intersecting with the ray, traversal
+ * stops and intersection information is recorded.
+ */
+ protected class TileTestVisitor extends TileVisitor {
+ // These vectors are all temporary storage variables allocated as class members to avoid
+ // runtime allocation.
+ private Vector2 mDelta;
+ private Vector2 mTileSpaceStart;
+ private Vector2 mTileSpaceEnd;
+ private Vector2 mTileSpaceOffset;
+ private int mTileHeight;
+ private int mTileWidth;
+
+ public TileTestVisitor() {
+ super();
+ mDelta = new Vector2();
+ mTileSpaceStart = new Vector2();
+ mTileSpaceEnd = new Vector2();
+ mTileSpaceOffset = new Vector2();
+ }
+
+ /**
+ * Sets the visitor up for a ray test. Initializes the size of the tiles and the direction
+ * of movement by which intersections should be filtered.
+ */
+ public void setup(Vector2 movementDirection, int tileWidth, int tileHeight) {
+ if (movementDirection != null) {
+ mDelta.set(movementDirection);
+ mDelta.normalize();
+ } else {
+ mDelta.zero();
+ }
+ mTileWidth = tileWidth;
+ mTileHeight = tileHeight;
+ }
+
+ /**
+ * Converts the ray into tile space and then compares it to the segments
+ * stored in the current tile.
+ */
+ @Override
+ public boolean visit(CollisionTile tile, Vector2 startPoint, Vector2 endPoint,
+ Vector2 hitPoint, Vector2 hitNormal, int tileX, int tileY) {
+ mTileSpaceOffset.set(tileX * mTileWidth, tileY * mTileHeight);
+ mTileSpaceStart.set(startPoint);
+ mTileSpaceStart.subtract(mTileSpaceOffset);
+ mTileSpaceEnd.set(endPoint);
+ mTileSpaceEnd.subtract(mTileSpaceOffset);
+ // find all the hits in the tile and pick the closest to the start point.
+ boolean foundHit = testSegmentAgainstList(tile.segments, mTileSpaceStart, mTileSpaceEnd,
+ hitPoint, hitNormal, mDelta, null);
+
+ if (foundHit) {
+ // The hitPoint is in tile space, so convert it back to world space.
+ hitPoint.add(mTileSpaceOffset);
+ }
+
+ return foundHit;
+ }
+ }
+
+ /**
+ * A class describing a single surface in the collision world. Surfaces are stored as a line
+ * segment and a normal. The normal must be normalized (its length must be 1.0) and should
+ * describe the direction that the segment "pushes against" in a collision.
+ */
+ protected class LineSegment extends AllocationGuard {
+ private Vector2 mStartPoint;
+ private Vector2 mEndPoint;
+ public Vector2 mNormal;
+ public GameObject owner;
+
+ public LineSegment() {
+ super();
+ mStartPoint = new Vector2();
+ mEndPoint = new Vector2();
+ mNormal = new Vector2();
+ }
+
+ /* Sets up the line segment. Values are copied to local storage. */
+ public void set(Vector2 start, Vector2 end, Vector2 norm) {
+ mStartPoint.set(start);
+ mEndPoint.set(end);
+ mNormal.set(norm);
+ }
+
+ public void setOwner(GameObject ownerObject) {
+ owner = ownerObject;
+ }
+ /**
+ * Checks to see if these lines intersect by projecting one onto the other and then
+ * assuring that the collision point is within the range of each segment.
+ */
+ public boolean calculateIntersection(Vector2 otherStart, Vector2 otherEnd,
+ Vector2 hitPoint) {
+ boolean intersecting = false;
+
+ // Reference: http://local.wasp.uwa.edu.au/~pbourke/geometry/lineline2d/
+ final float x1 = mStartPoint.x;
+ final float x2 = mEndPoint.x;
+ final float x3 = otherStart.x;
+ final float x4 = otherEnd.x;
+ final float y1 = mStartPoint.y;
+ final float y2 = mEndPoint.y;
+ final float y3 = otherStart.y;
+ final float y4 = otherEnd.y;
+
+ final float denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
+ if (denom != 0) {
+ final float uA = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
+ final float uB = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denom;
+
+ if (uA >= 0.0f && uA <= 1.0f && uB >= 0.0f && uB <= 1.0f) {
+ final float hitX = x1 + (uA * (x2 - x1));
+ final float hitY = y1 + (uA * (y2 - y1));
+ hitPoint.set(hitX, hitY);
+ intersecting = true;
+ }
+ }
+ return intersecting;
+ }
+
+ // Based on http://www.garagegames.com/community/resources/view/309
+ public boolean calculateIntersectionBox(float left, float right, float top, float bottom,
+ Vector2 hitPoint) {
+
+ final float x1 = mStartPoint.x;
+ final float x2 = mEndPoint.x;
+ final float y1 = mStartPoint.y;
+ final float y2 = mEndPoint.y;
+
+ float startIntersect;
+ float endIntersect;
+ float intersectTimeStart = 0.0f;
+ float intersectTimeEnd = 1.0f;
+
+ if (x1 < x2) {
+ if (x1 > right || x2 < left) {
+ return false;
+ }
+ final float deltaX = x2 - x1;
+ startIntersect = (x1 < left) ? (left - x1) / deltaX : 0.0f;
+ endIntersect = (x2 > right) ? (right - x1) / deltaX : 1.0f;
+ } else {
+ if (x2 > right || x1 < left) {
+ return false;
+ }
+ final float deltaX = x2 - x1;
+ startIntersect = (x1 > right) ? (right - x1) / deltaX : 0.0f;
+ endIntersect = (x2 < left) ? (left - x1) / deltaX : 1.0f;
+ }
+
+ if (startIntersect > intersectTimeStart) {
+ intersectTimeStart = startIntersect;
+ }
+ if (endIntersect < intersectTimeEnd) {
+ intersectTimeEnd = endIntersect;
+ }
+ if (intersectTimeEnd < intersectTimeStart) {
+ return false;
+ }
+
+ // y
+ if (y1 < y2) {
+ if (y1 > top || y2 < bottom) {
+ return false;
+ }
+ final float deltaY = y2 - y1;
+ startIntersect = (y1 < bottom) ? (bottom - y1) / deltaY : 0.0f;
+ endIntersect = (y2 > top) ? (top - y1) / deltaY : 1.0f;
+ } else {
+ if (y2 > top || y1 < bottom) {
+ return false;
+ }
+ final float deltaY = y2 - y1;
+ startIntersect = (y1 > top) ? (top - y1) / deltaY : 0.0f;
+ endIntersect = (y2 < bottom) ? (bottom - y1) / deltaY : 1.0f;
+ }
+
+ if (startIntersect > intersectTimeStart) {
+ intersectTimeStart = startIntersect;
+ }
+ if (endIntersect < intersectTimeEnd) {
+ intersectTimeEnd = endIntersect;
+ }
+ if (intersectTimeEnd < intersectTimeStart) {
+ return false;
+ }
+
+ hitPoint.set(mEndPoint);
+ hitPoint.subtract(mStartPoint);
+ hitPoint.multiply(intersectTimeStart);
+ hitPoint.add(mStartPoint);
+
+ return true;
+ }
+
+ }
+
+ /**
+ * A pool of line segments.
+ */
+ protected class LineSegmentPool extends TObjectPool<LineSegment> {
+ public LineSegmentPool() {
+ super();
+ }
+
+ public LineSegmentPool(int count) {
+ super(count);
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ @Override
+ protected void fill() {
+ for (int x = 0; x < getSize(); x++) {
+ getAvailable().add(new LineSegment());
+ }
+ }
+
+ @Override
+ public void release(Object entry) {
+ ((LineSegment)entry).owner = null;
+ super.release(entry);
+ }
+
+
+ }
+
+ /**
+ * A single collision tile. Manages a list of line segments.
+ */
+ protected class CollisionTile extends AllocationGuard {
+ public FixedSizeArray<LineSegment> segments;
+
+ public CollisionTile(int maxSegments) {
+ super();
+ segments = new FixedSizeArray<LineSegment>(maxSegments);
+ }
+
+ public boolean addSegment(LineSegment segment) {
+ boolean success = false;
+ if (segments.getCount() < segments.getCapacity()) {
+ success = true;
+ }
+ segments.add(segment);
+ return success;
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/CollisionVolume.java b/src/com/replica/replicaisland/CollisionVolume.java
new file mode 100644
index 0000000..122bbd6
--- /dev/null
+++ b/src/com/replica/replicaisland/CollisionVolume.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.CollisionParameters.HitType;
+
+/**
+ * CollisionVolume describes a volume (rectangle, sphere, etc) used for dynamic collision detection.
+ * Volumes can be tested for intersection against other volumes, and can be grown to contain a set
+ * of other volumes. The volume itself is stored in object-relative space (in terms of offsets from
+ * some origin); when used with game objects the position of the parent object must be passed to
+ * a parameter of the intersection test. This means that a single instance of a CollisionVolume and
+ * its derivatives is safe to share amongst many game object instances.
+ */
+public abstract class CollisionVolume extends AllocationGuard {
+ // TODO: does this really belong here?
+ // When used as an attack volume, mHitType specifies the type of hit that the volume deals.
+ // When used as a vulnerability volume, it specifies which type the volume is vulernable to
+ // (invalid = all types).
+ public int mHitType;
+
+ public CollisionVolume() {
+ super();
+ mHitType = HitType.INVALID;
+ }
+
+ public CollisionVolume(int type) {
+ super();
+ mHitType = type;
+ }
+
+ public void setHitType(int type) {
+ mHitType = type;
+ }
+
+ public int getHitType() {
+ return mHitType;
+ }
+
+
+ public abstract boolean intersects(Vector2 position, FlipInfo flip, CollisionVolume other,
+ Vector2 otherPosition, FlipInfo otherFlip);
+
+ public float getMinXPosition(FlipInfo flip) {
+ float value = 0;
+ if (flip != null && flip.flipX) {
+ final float maxX = getMaxX();
+ value = flip.parentWidth - maxX;
+ } else {
+ value = getMinX();
+ }
+ return value;
+ }
+
+ public float getMaxXPosition(FlipInfo flip) {
+ float value = 0;
+ if (flip != null && flip.flipX) {
+ final float minX = getMinX();
+ value = flip.parentWidth - minX;
+ } else {
+ value = getMaxX();
+ }
+ return value;
+ }
+
+ public float getMinYPosition(FlipInfo flip) {
+ float value = 0;
+ if (flip != null && flip.flipY) {
+ final float maxY = getMaxY();
+ value = flip.parentHeight - maxY;
+ } else {
+ value = getMinY();
+ }
+ return value;
+ }
+
+ public float getMaxYPosition(FlipInfo flip) {
+ float value = 0;
+ if (flip != null && flip.flipY) {
+ final float minY = getMinY();
+ value = flip.parentHeight - minY;
+ } else {
+ value = getMaxY();
+ }
+ return value;
+ }
+
+ protected abstract float getMinX();
+ protected abstract float getMaxX();
+ protected abstract float getMinY();
+ protected abstract float getMaxY();
+
+
+ public static class FlipInfo {
+ public boolean flipX;
+ public boolean flipY;
+ public float parentWidth;
+ public float parentHeight;
+ }
+}
diff --git a/src/com/replica/replicaisland/ContextParameters.java b/src/com/replica/replicaisland/ContextParameters.java
new file mode 100644
index 0000000..4d20c23
--- /dev/null
+++ b/src/com/replica/replicaisland/ContextParameters.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.content.Context;
+
+/** Contains global (but typically constant) parameters about the current operating context */
+public class ContextParameters extends BaseObject {
+ public int viewWidth;
+ public int viewHeight;
+ public Context context;
+ public int gameWidth;
+ public int gameHeight;
+ public float viewScaleX;
+ public float viewScaleY;
+ public boolean supportsDrawTexture;
+ public boolean supportsVBOs;
+
+ public ContextParameters() {
+ super();
+ }
+
+ @Override
+ public void reset() {
+
+ }
+}
diff --git a/src/com/replica/replicaisland/ConversationDialogActivity.java b/src/com/replica/replicaisland/ConversationDialogActivity.java
new file mode 100644
index 0000000..0bb3975
--- /dev/null
+++ b/src/com/replica/replicaisland/ConversationDialogActivity.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.drawable.AnimationDrawable;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.replica.replicaisland.ConversationUtils.Conversation;
+import com.replica.replicaisland.ConversationUtils.ConversationPage;
+
+public class ConversationDialogActivity extends Activity {
+
+ private final static float TEXT_CHARACTER_DELAY = 0.1f;
+ private final static int TEXT_CHARACTER_DELAY_MS = (int)(TEXT_CHARACTER_DELAY * 1000);
+ private ConversationUtils.Conversation mConversation;
+ private ArrayList<ConversationUtils.ConversationPage> mPages;
+ private int mCurrentPage;
+
+ private ImageView mOkArrow;
+ private AnimationDrawable mOkAnimation;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.conversation_dialog);
+
+ mOkArrow = (ImageView)findViewById(R.id.ok);
+ mOkArrow.setBackgroundResource(R.anim.ui_button);
+ mOkAnimation = (AnimationDrawable) mOkArrow.getBackground();
+ mOkArrow.setVisibility(View.INVISIBLE);
+
+ final Intent callingIntent = getIntent();
+ final int levelRow = callingIntent.getIntExtra("levelRow", -1);
+ final int levelIndex = callingIntent.getIntExtra("levelIndex", -1);
+ final int index = callingIntent.getIntExtra("index", -1);
+ final int character = callingIntent.getIntExtra("character", 1);
+
+ mPages = null;
+
+ // LevelTree.get(mLevelRow, mLevelIndex).dialogResources.character2Entry.get(index)
+ if (levelRow != -1 && levelIndex != -1 && index != -1) {
+ if (character == 1) {
+ mConversation = LevelTree.get(levelRow, levelIndex).dialogResources.character1Conversations.get(index);
+ } else {
+ mConversation = LevelTree.get(levelRow, levelIndex).dialogResources.character2Conversations.get(index);
+ }
+ TypewriterTextView tv = (TypewriterTextView)findViewById(R.id.typewritertext);
+ tv.setParentActivity(this);
+
+ } else {
+ // bail
+ finish();
+ }
+ }
+
+ private void formatPages(Conversation conversation, TextView textView) {
+ Paint paint = new Paint();
+ final int maxWidth = textView.getWidth();
+ final int maxHeight = textView.getHeight();
+ paint.setTextSize(textView.getTextSize());
+ paint.setTypeface(textView.getTypeface());
+
+ for (int page = conversation.pages.size() - 1; page >= 0 ; page--) {
+ ConversationUtils.ConversationPage currentPage = conversation.pages.get(page);
+ CharSequence text = currentPage.text;
+ // Iterate line by line through the text. Add \n if it gets too wide,
+ // and split into a new page if it gets too long.
+ int currentOffset = 0;
+ int textLength = text.length();
+ SpannableStringBuilder spannedText = new SpannableStringBuilder(text);
+ int lineCount = 0;
+ final float fontHeight = -paint.ascent() + paint.descent();
+ final int maxLinesPerPage = (int)(maxHeight / fontHeight);
+ CharSequence newline = "\n";
+ int addedPages = 0;
+ int lastPageStart = 0;
+ do {
+ int fittingChars = paint.breakText(text, currentOffset, textLength, true, maxWidth, null);
+
+ if (currentOffset + fittingChars < textLength) {
+ fittingChars -= 2;
+ // Text doesn't fit on the line. Insert a return after the last space.
+ int lastSpace = TextUtils.lastIndexOf(text, ' ', currentOffset + fittingChars - 1);
+ if (lastSpace == -1) {
+ // No spaces, just split at the last character.
+ lastSpace = currentOffset + fittingChars - 1;
+ }
+ spannedText.replace(lastSpace, lastSpace + 1, newline, 0, 1);
+ lineCount++;
+ currentOffset = lastSpace + 1;
+ } else {
+ lineCount++;
+ currentOffset = textLength;
+ }
+
+ if (lineCount >= maxLinesPerPage || currentOffset >= textLength) {
+ lineCount = 0;
+ if (addedPages == 0) {
+ // overwrite the original page
+ currentPage.text = spannedText.subSequence(lastPageStart, currentOffset);
+ } else {
+ // split into a new page
+ ConversationPage newPage = new ConversationPage();
+ newPage.imageResource = currentPage.imageResource;
+ newPage.text = spannedText.subSequence(lastPageStart, currentOffset);
+ newPage.title = currentPage.title;
+ conversation.pages.add(page + addedPages, newPage);
+ }
+ lastPageStart = currentOffset;
+ addedPages++;
+ }
+ } while (currentOffset < textLength);
+
+
+ }
+
+ // Holy crap we did a lot of allocation there.
+ Runtime.getRuntime().gc();
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ TypewriterTextView tv = (TypewriterTextView)findViewById(R.id.typewritertext);
+
+ if (tv.getRemainingTime() > 0) {
+ tv.snapToEnd();
+ } else {
+ mCurrentPage++;
+ if (mCurrentPage < mPages.size()) {
+ showPage(mPages.get(mCurrentPage));
+ } else {
+ finish();
+ }
+ }
+ }
+ // Sleep so that the main thread doesn't get flooded with UI events.
+ try {
+ Thread.sleep(32);
+ } catch (InterruptedException e) {
+ // No big deal if this sleep is interrupted.
+ }
+ return true;
+ }
+
+ protected void showPage(ConversationUtils.ConversationPage page) {
+ TypewriterTextView tv = (TypewriterTextView)findViewById(R.id.typewritertext);
+ tv.setTypewriterText(page.text);
+
+ mOkArrow.setVisibility(View.INVISIBLE);
+ mOkAnimation.start();
+
+ tv.setOkArrow(mOkArrow);
+
+ ImageView image = (ImageView)findViewById(R.id.speaker);
+ if (page.imageResource != 0) {
+ image.setImageResource(page.imageResource);
+ image.setVisibility(View.VISIBLE);
+ } else {
+ image.setVisibility(View.GONE);
+ }
+
+ TextView title = (TextView)findViewById(R.id.speakername);
+ if (page.title != null) {
+ title.setText(page.title);
+ title.setVisibility(View.VISIBLE);
+ } else {
+ title.setVisibility(View.GONE);
+ }
+
+ }
+
+ public void processText() {
+ if (!mConversation.splittingComplete) {
+ TextView textView = (TextView)findViewById(R.id.typewritertext);
+ formatPages(mConversation, textView);
+ mConversation.splittingComplete = true;
+ }
+
+ if (mPages == null) {
+ mPages = mConversation.pages;
+ showPage(mPages.get(0));
+
+ mCurrentPage = 0;
+ }
+ }
+
+
+ public static class TypewriterTextView extends TextView {
+ private int mCurrentCharacter;
+ private long mLastTime;
+ private CharSequence mText;
+ private View mOkArrow;
+ private ConversationDialogActivity mParentActivity; // This really sucks.
+
+ public TypewriterTextView(Context context) {
+ super(context);
+ }
+
+ public TypewriterTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public TypewriterTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public void setParentActivity(ConversationDialogActivity parent) {
+ mParentActivity = parent;
+ }
+
+ public void setTypewriterText(CharSequence text) {
+ mText = text;
+ mCurrentCharacter = 0;
+ mLastTime = 0;
+ postInvalidate();
+ }
+
+ public long getRemainingTime() {
+ return (mText.length() - mCurrentCharacter) * TEXT_CHARACTER_DELAY_MS;
+ }
+
+ public void snapToEnd() {
+ mCurrentCharacter = mText.length() - 1;
+ }
+
+ public void setOkArrow(View arrow) {
+ mOkArrow = arrow;
+ }
+
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ // We need to wait until layout has occurred before we can setup the
+ // text page. Ugh. Bidirectional dependency!
+ if (mParentActivity != null) {
+ mParentActivity.processText();
+ }
+ super.onSizeChanged(w, h, oldw, oldh);
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ final long time = SystemClock.uptimeMillis();
+ final long delta = time - mLastTime;
+ if (delta > TEXT_CHARACTER_DELAY_MS) {
+ if (mText != null) {
+ if (mCurrentCharacter <= mText.length()) {
+ CharSequence subtext = mText.subSequence(0, mCurrentCharacter);
+ setText(subtext, TextView.BufferType.SPANNABLE);
+ mCurrentCharacter++;
+ postInvalidateDelayed(TEXT_CHARACTER_DELAY_MS);
+ } else {
+ if (mOkArrow != null) {
+ mOkArrow.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+ }
+ super.onDraw(canvas);
+ }
+ }
+
+
+
+}
diff --git a/src/com/replica/replicaisland/ConversationUtils.java b/src/com/replica/replicaisland/ConversationUtils.java
new file mode 100644
index 0000000..9cfe9e2
--- /dev/null
+++ b/src/com/replica/replicaisland/ConversationUtils.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+import java.util.ArrayList;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+
+public final class ConversationUtils {
+ private static final int MAX_CHARACTERS_PER_PAGE = 250;
+
+ public static class ConversationPage {
+ public int imageResource;
+ public CharSequence text;
+ public String title;
+ }
+
+ public static class Conversation {
+ public ArrayList<ConversationPage> pages = new ArrayList<ConversationPage>();
+ public boolean splittingComplete;
+ }
+
+ public final static ArrayList<Conversation> loadDialog(int resource, Context context) {
+ XmlResourceParser parser = context.getResources().getXml(resource);
+
+ ArrayList<Conversation> dialog = null;
+ Conversation currentConversation = null;
+
+ try {
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals("conversation")) {
+ if (dialog == null) {
+ dialog = new ArrayList<Conversation>();
+ }
+ currentConversation = new Conversation();
+ currentConversation.splittingComplete = false;
+ dialog.add(currentConversation);
+ } else if (parser.getName().equals("page")) {
+ ConversationPage page = new ConversationPage();
+ for (int i=0; i < parser.getAttributeCount(); i++) {
+ final int value = parser.getAttributeResourceValue(i, -1);
+ if (value != -1) {
+ if (parser.getAttributeName(i).equals("image")) {
+ page.imageResource = value;
+ }
+ if (parser.getAttributeName(i).equals("text")) {
+ page.text = context.getText(value);
+ }
+ if (parser.getAttributeName(i).equals("title")) {
+ page.title = context.getString(value);
+ }
+ }
+ }
+ currentConversation.pages.add(page);
+ }
+ }
+ eventType = parser.next();
+ }
+ } catch(Exception e) {
+ DebugLog.e("LoadDialog", e.getStackTrace().toString());
+ } finally {
+ parser.close();
+ }
+
+ return dialog;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/CustomToastSystem.java b/src/com/replica/replicaisland/CustomToastSystem.java
new file mode 100644
index 0000000..1959434
--- /dev/null
+++ b/src/com/replica/replicaisland/CustomToastSystem.java
@@ -0,0 +1,40 @@
+package com.replica.replicaisland;
+
+import android.content.Context;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class CustomToastSystem extends BaseObject {
+ private View mView;
+ private TextView mText;
+ private Toast mToast;
+
+ public CustomToastSystem(Context context) {
+ LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ mView = inflater.inflate(R.layout.custom_toast, null);
+
+ mText = (TextView) mView.findViewById(R.id.text);
+ mToast = new Toast(context);
+ mToast.setView(mView);
+
+ }
+
+
+ @Override
+ public void reset() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void toast(String text, int length) {
+ mText.setText(text);
+
+ mToast.setGravity(Gravity.CENTER, 0, 0);
+ mToast.setDuration(length);
+ mToast.show();
+ }
+
+}
diff --git a/src/com/replica/replicaisland/DebugLog.java b/src/com/replica/replicaisland/DebugLog.java
new file mode 100644
index 0000000..0cb1477
--- /dev/null
+++ b/src/com/replica/replicaisland/DebugLog.java
@@ -0,0 +1,103 @@
+package com.replica.replicaisland;
+
+import android.util.Log;
+
+public final class DebugLog {
+ private static boolean mLoggingEnabled = true;
+
+ private DebugLog() {
+
+ }
+
+ public static void setDebugLogging(boolean enabled) {
+ mLoggingEnabled = enabled;
+ }
+
+ public static int v(String tag, String msg) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.v(tag, msg);
+ }
+ return result;
+ }
+
+ public static int v(String tag, String msg, Throwable tr) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.v(tag, msg, tr);
+ }
+ return result;
+ }
+
+ public static int d(String tag, String msg) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.d(tag, msg);
+ }
+ return result;
+ }
+
+ public static int d(String tag, String msg, Throwable tr) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.d(tag, msg, tr);
+ }
+ return result;
+ }
+
+ public static int i(String tag, String msg) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.i(tag, msg);
+ }
+ return result;
+ }
+
+ public static int i(String tag, String msg, Throwable tr) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.i(tag, msg, tr);
+ }
+ return result;
+ }
+
+ public static int w(String tag, String msg) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.w(tag, msg);
+ }
+ return result;
+ }
+
+ public static int w(String tag, String msg, Throwable tr) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.w(tag, msg, tr);
+ }
+ return result;
+ }
+
+ public static int w(String tag, Throwable tr) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.w(tag, tr);
+ }
+ return result;
+ }
+
+ public static int e(String tag, String msg) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.e(tag, msg);
+ }
+ return result;
+ }
+
+ public static int e(String tag, String msg, Throwable tr) {
+ int result = 0;
+ if (mLoggingEnabled) {
+ result = Log.e(tag, msg, tr);
+ }
+ return result;
+ }
+}
diff --git a/src/com/replica/replicaisland/DebugSystem.java b/src/com/replica/replicaisland/DebugSystem.java
new file mode 100644
index 0000000..e53af79
--- /dev/null
+++ b/src/com/replica/replicaisland/DebugSystem.java
@@ -0,0 +1,97 @@
+package com.replica.replicaisland;
+
+public final class DebugSystem extends BaseObject {
+ public static final int COLOR_RED = 0;
+ public static final int COLOR_BLUE = 1;
+ public static final int COLOR_OUTLINE = 2;
+ public static final int SHAPE_BOX = 0;
+ public static final int SHAPE_CIRCLE = 1;
+
+ private Texture mRedBoxTexture;
+ private Texture mBlueBoxTexture;
+ private Texture mOutlineBoxTexture;
+ private Texture mRedCircleTexture;
+ private Texture mBlueCircleTexture;
+ private Texture mOutlineCircleTexture;
+
+ private Vector2 mWorkVector;
+
+ public DebugSystem(TextureLibrary library) {
+ super();
+ if (library != null) {
+ mRedBoxTexture = library.allocateTexture(R.drawable.debug_box_red);
+ mBlueBoxTexture = library.allocateTexture(R.drawable.debug_box_blue);
+ mOutlineBoxTexture = library.allocateTexture(R.drawable.debug_box_outline);
+ mRedCircleTexture = library.allocateTexture(R.drawable.debug_circle_red);
+ mBlueCircleTexture = library.allocateTexture(R.drawable.debug_circle_blue);
+ mOutlineCircleTexture = library.allocateTexture(R.drawable.debug_circle_outline);
+
+ }
+
+ mWorkVector = new Vector2();
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ public void drawShape(float x, float y, float width, float height, int shapeType, int colorType) {
+ final RenderSystem render = sSystemRegistry.renderSystem;
+ final DrawableFactory factory = sSystemRegistry.drawableFactory;
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ ContextParameters params = sSystemRegistry.contextParameters;
+ mWorkVector.set(x, y);
+ mWorkVector.x = (mWorkVector.x - camera.getFocusPositionX()
+ + (params.gameWidth / 2));
+ mWorkVector.y = (mWorkVector.y - camera.getFocusPositionY()
+ + (params.gameHeight / 2));
+
+ if (mWorkVector.x + width >= 0.0f && mWorkVector.x < params.gameWidth
+ && mWorkVector.y + height >= 0.0f && mWorkVector.y < params.gameHeight) {
+ DrawableBitmap bitmap = factory.allocateDrawableBitmap();
+ if (bitmap != null) {
+ Texture texture = getTexture(shapeType, colorType);
+ bitmap.resize((int)texture.width, (int)texture.height);
+ // TODO: scale stretch hack. fix!
+ bitmap.setWidth((int)width);
+ bitmap.setHeight((int)height);
+ bitmap.setTexture(texture);
+ mWorkVector.set(x, y);
+
+ render.scheduleForDraw(bitmap, mWorkVector, SortConstants.HUD, true);
+ }
+ }
+ }
+
+ private final Texture getTexture(int shapeType, int colorType) {
+ Texture result = null;
+ if (shapeType == SHAPE_BOX) {
+ switch (colorType) {
+ case COLOR_RED:
+ result = mRedBoxTexture;
+ break;
+ case COLOR_BLUE:
+ result = mBlueBoxTexture;
+ break;
+ case COLOR_OUTLINE:
+ result = mOutlineBoxTexture;
+ break;
+ }
+ } else if (shapeType == SHAPE_CIRCLE) {
+ switch (colorType) {
+ case COLOR_RED:
+ result = mRedCircleTexture;
+ break;
+ case COLOR_BLUE:
+ result = mBlueCircleTexture;
+ break;
+ case COLOR_OUTLINE:
+ result = mOutlineCircleTexture;
+ break;
+ }
+ }
+ return result;
+ }
+
+
+}
diff --git a/src/com/replica/replicaisland/DiaryActivity.java b/src/com/replica/replicaisland/DiaryActivity.java
new file mode 100644
index 0000000..7de529c
--- /dev/null
+++ b/src/com/replica/replicaisland/DiaryActivity.java
@@ -0,0 +1,51 @@
+package com.replica.replicaisland;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.AnimationDrawable;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class DiaryActivity extends Activity {
+
+ private OnClickListener mKillDiaryListener = new OnClickListener() {
+ public void onClick(View arg0) {
+ finish();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.diary);
+
+ TextView text = (TextView)findViewById(R.id.diarytext);
+
+ ImageView image = (ImageView)findViewById(R.id.diarybackground);
+ image.startAnimation(AnimationUtils.loadAnimation(this, R.anim.fade));
+ final Intent callingIntent = getIntent();
+ final int textResource = callingIntent.getIntExtra("text", -1);
+
+ if (textResource != -1) {
+ text.setText(textResource);
+ }
+
+ ImageView okArrow = (ImageView)findViewById(R.id.ok);
+ okArrow.setOnClickListener(mKillDiaryListener);
+ okArrow.setBackgroundResource(R.anim.ui_button);
+ AnimationDrawable anim = (AnimationDrawable) okArrow.getBackground();
+ anim.start();
+
+ BaseObject.sSystemRegistry.customToastSystem.toast(getString(R.string.diary_found), Toast.LENGTH_SHORT);
+
+ }
+
+
+
+
+}
diff --git a/src/com/replica/replicaisland/DoorAnimationComponent.java b/src/com/replica/replicaisland/DoorAnimationComponent.java
new file mode 100644
index 0000000..2f8c8b9
--- /dev/null
+++ b/src/com/replica/replicaisland/DoorAnimationComponent.java
@@ -0,0 +1,183 @@
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.SoundSystem.Sound;
+
+public class DoorAnimationComponent extends GameComponent {
+
+ public static final class Animation {
+ // Animations
+ public static final int CLOSED = 0;
+ public static final int OPEN = 1;
+ public static final int CLOSING = 2;
+ public static final int OPENING = 3;
+ }
+
+ // State
+ protected static final int STATE_CLOSED = 0;
+ protected static final int STATE_OPEN = 1;
+ protected static final int STATE_CLOSING = 2;
+ protected static final int STATE_OPENING = 3;
+
+ protected final static float DEFAULT_STAY_OPEN_TIME = 5.0f;
+
+ private SpriteComponent mSprite;
+ private int mState;
+ private ChannelSystem.Channel mChannel;
+ private SolidSurfaceComponent mSolidSurface;
+ private float mStayOpenTime;
+ private Sound mCloseSound;
+ private Sound mOpenSound;
+
+ public DoorAnimationComponent() {
+ super();
+ setPhase(ComponentPhases.ANIMATION.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mSprite = null;
+ mState = STATE_CLOSED;
+ mChannel = null;
+ mSolidSurface = null;
+ mStayOpenTime = DEFAULT_STAY_OPEN_TIME;
+ mCloseSound = null;
+ mOpenSound = null;
+ }
+
+ private void open(float timeSinceTriggered, GameObject parentObject) {
+ if (mSprite != null) {
+ final float openAnimationLength = mSprite.findAnimation(Animation.OPENING).getLength();
+
+
+
+ if (timeSinceTriggered > openAnimationLength) {
+ // snap open.
+ mSprite.playAnimation(Animation.OPEN);
+ mState = STATE_OPEN;
+ if (mSolidSurface != null) {
+ parentObject.remove(mSolidSurface);
+ }
+ } else {
+ float timeOffset = timeSinceTriggered;
+ if (mState == STATE_CLOSING) {
+ // opening and closing animations are the same length.
+ // if we're in the middle of one and need to go to the other,
+ // we can start the new one mid-way through so that the door appears to
+ // simply reverse direction.
+ timeOffset = openAnimationLength - mSprite.getCurrentAnimationTime();
+ } else {
+ if (mSolidSurface != null) {
+ parentObject.remove(mSolidSurface);
+ }
+ }
+
+ mState = STATE_OPENING;
+ mSprite.playAnimation(Animation.OPENING);
+ mSprite.setCurrentAnimationTime(timeOffset);
+
+ if (mOpenSound != null) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.play(mOpenSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+ }
+ }
+ }
+
+ private void close(float timeSinceTriggered, GameObject parentObject) {
+ if (mSprite != null) {
+ final float closeAnimationLength = mSprite.findAnimation(Animation.CLOSING).getLength();
+
+ if (timeSinceTriggered > mStayOpenTime + closeAnimationLength) {
+ // snap open.
+ mSprite.playAnimation(Animation.CLOSED);
+ mState = STATE_CLOSED;
+ if (mSolidSurface != null) {
+ parentObject.add(mSolidSurface);
+ }
+ } else {
+ float timeOffset = timeSinceTriggered - mStayOpenTime;
+ if (mState == STATE_OPENING) {
+ timeOffset = closeAnimationLength - mSprite.getCurrentAnimationTime();
+ }
+
+ mState = STATE_CLOSING;
+ mSprite.playAnimation(Animation.CLOSING);
+ mSprite.setCurrentAnimationTime(timeOffset);
+ if (mCloseSound != null) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.play(mCloseSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+ }
+ }
+ }
+
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mChannel != null) {
+ if (mChannel.value != null && mChannel.value instanceof ChannelSystem.ChannelFloatValue) {
+ final float lastPressedTime = ((ChannelSystem.ChannelFloatValue)mChannel.value).value;
+ TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+ final float delta = gameTime - lastPressedTime;
+ if (delta < mStayOpenTime
+ && (mState == STATE_CLOSED || mState == STATE_CLOSING)) {
+ open(delta, (GameObject)parent);
+
+ } else if (delta > mStayOpenTime
+ && (mState == STATE_OPEN || mState == STATE_OPENING)) {
+ close(delta, (GameObject)parent);
+ }
+ }
+ }
+ if (mSprite != null) {
+
+
+ if (mState == STATE_OPENING && mSprite.animationFinished()) {
+ mSprite.playAnimation(Animation.OPEN);
+ mState = STATE_OPEN;
+ } else if (mState == STATE_CLOSING && mSprite.animationFinished()) {
+ mSprite.playAnimation(Animation.CLOSED);
+ mState = STATE_CLOSED;
+ if (mSolidSurface != null) {
+ ((GameObject)parent).add(mSolidSurface);
+ }
+ }
+
+ // Deal with the case where the animation and state are out of sync
+ // (side-effect of possession).
+ // TODO: figure out a better way to do this.
+ if (mSprite.getCurrentAnimation() == Animation.OPENING && mState == STATE_CLOSED) {
+ mSprite.playAnimation(Animation.CLOSING);
+ mState = STATE_CLOSING;
+ }
+
+ }
+ }
+
+ public void setSprite(SpriteComponent sprite) {
+ mSprite = sprite;
+ }
+
+ public void setChannel(ChannelSystem.Channel channel) {
+ mChannel = channel;
+ }
+
+ public void setSolidSurface(SolidSurfaceComponent surface) {
+ mSolidSurface = surface;
+ }
+
+ public void setStayOpenTime(float time) {
+ mStayOpenTime = time;
+ }
+
+ public void setSounds(Sound openSound, Sound closeSound) {
+ mOpenSound = openSound;
+ mCloseSound = closeSound;
+ }
+}
diff --git a/src/com/replica/replicaisland/DrawableBitmap.java b/src/com/replica/replicaisland/DrawableBitmap.java
new file mode 100644
index 0000000..1c951d4
--- /dev/null
+++ b/src/com/replica/replicaisland/DrawableBitmap.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+/**
+ * Draws a screen-aligned bitmap to the screen.
+ */
+public class DrawableBitmap extends DrawableObject {
+
+ private Texture mTexture;
+ private int mWidth;
+ private int mHeight;
+ private int mCrop[];
+ private int mViewWidth;
+ private int mViewHeight;
+ private float mOpacity;
+
+ DrawableBitmap(Texture texture, int width, int height) {
+ super();
+ mTexture = texture;
+ mWidth = width;
+ mHeight = height;
+ mCrop = new int[4];
+ mViewWidth = 0;
+ mViewHeight = 0;
+ mOpacity = 1.0f;
+ setCrop(0, height, width, height);
+ }
+
+ public void reset() {
+ mTexture = null;
+ mViewWidth = 0;
+ mViewHeight = 0;
+ mOpacity = 1.0f;
+
+ }
+
+ public void setViewSize(int width, int height) {
+ mViewHeight = height;
+ mViewWidth = width;
+ }
+
+ public void setOpacity(float opacity) {
+ mOpacity = opacity;
+ }
+
+ /**
+ * Begins drawing bitmaps. Sets the OpenGL state for rapid drawing.
+ *
+ * @param gl A pointer to the OpenGL context.
+ * @param viewWidth The width of the screen.
+ * @param viewHeight The height of the screen.
+ */
+ public static void beginDrawing(GL10 gl, float viewWidth, float viewHeight) {
+ gl.glShadeModel(GL10.GL_FLAT);
+ gl.glEnable(GL10.GL_BLEND);
+ gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
+ gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
+
+ gl.glMatrixMode(GL10.GL_PROJECTION);
+ gl.glPushMatrix();
+ gl.glLoadIdentity();
+ gl.glOrthof(0.0f, viewWidth, 0.0f, viewHeight, 0.0f, 1.0f);
+ gl.glMatrixMode(GL10.GL_MODELVIEW);
+ gl.glPushMatrix();
+ gl.glLoadIdentity();
+
+ gl.glEnable(GL10.GL_TEXTURE_2D);
+
+ }
+
+ /**
+ * Draw the bitmap at a given x,y position, expressed in pixels, with the
+ * lower-left-hand-corner of the view being (0,0).
+ *
+ * @param gl A pointer to the OpenGL context
+ * @param x The number of pixels to offset this drawable's origin in the x-axis.
+ * @param y The number of pixels to offset this drawable's origin in the y-axis
+ * @param scaleX The horizontal scale factor between the bitmap resolution and the display resolution.
+ * @param scaleY The vertical scale factor between the bitmap resolution and the display resolution.
+ */
+ @Override
+ public void draw(float x, float y, float scaleX, float scaleY) {
+ GL10 gl = OpenGLSystem.getGL();
+ final Texture texture = mTexture;
+
+ if (gl != null && texture != null) {
+ assert texture.loaded;
+
+ final float snappedX = (int) x;
+ final float snappedY = (int) y;
+
+ final float opacity = mOpacity;
+ final float width = mWidth;
+ final float height = mHeight;
+ final float viewWidth = mViewWidth;
+ final float viewHeight = mViewHeight;
+
+ boolean cull = false;
+ if (viewWidth > 0) {
+ if (snappedX + width < 0.0f
+ || snappedX > viewWidth
+ || snappedY + height < 0.0f
+ || snappedY > viewHeight
+ || opacity == 0.0f
+ || !texture.loaded) {
+ cull = true;
+ }
+ }
+ if (!cull) {
+ OpenGLSystem.bindTexture(GL10.GL_TEXTURE_2D, texture.name);
+
+ // This is necessary because we could be drawing the same texture with different
+ // crop (say, flipped horizontally) on the same frame.
+ OpenGLSystem.setTextureCrop(mCrop);
+
+ if (opacity < 1.0f) {
+ gl.glColor4f(opacity, opacity, opacity, opacity);
+ }
+
+ ((GL11Ext) gl).glDrawTexfOES(snappedX * scaleX, snappedY * scaleY,
+ getPriority(), width * scaleX, height * scaleY);
+
+ if (opacity < 1.0f) {
+ gl.glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
+ }
+ }
+ }
+ }
+
+ /**
+ * Ends the drawing and restores the OpenGL state.
+ *
+ * @param gl A pointer to the OpenGL context.
+ */
+ public static void endDrawing(GL10 gl) {
+ gl.glDisable(GL10.GL_BLEND);
+ gl.glMatrixMode(GL10.GL_PROJECTION);
+ gl.glPopMatrix();
+ gl.glMatrixMode(GL10.GL_MODELVIEW);
+ gl.glPopMatrix();
+ }
+
+ public void resize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ setCrop(0, height, width, height);
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public void setWidth(int width) {
+ mWidth = width;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public void setHeight(int height) {
+ mHeight = height;
+ }
+
+ /**
+ * Changes the crop parameters of this bitmap. Note that the underlying OpenGL texture's
+ * parameters are not changed immediately The crop is updated on the
+ * next call to draw(). Note that the image may be flipped by providing a negative width or
+ * height.
+ *
+ * @param left
+ * @param bottom
+ * @param width
+ * @param height
+ */
+ public void setCrop(int left, int bottom, int width, int height) {
+ // Negative width and height values will flip the image.
+ mCrop[0] = left;
+ mCrop[1] = bottom;
+ mCrop[2] = width;
+ mCrop[3] = -height;
+ }
+
+ public int[] getCrop() {
+ return mCrop;
+ }
+
+ public void setTexture(Texture texture) {
+ mTexture = texture;
+ }
+
+ @Override
+ public Texture getTexture() {
+ return mTexture;
+ }
+
+ @Override
+ public boolean visibleAtPosition(Vector2 position) {
+ boolean cull = false;
+ if (mViewWidth > 0) {
+ if (position.x + mWidth < 0 || position.x > mViewWidth
+ || position.y + mHeight < 0 || position.y > mViewHeight) {
+ cull = true;
+ }
+ }
+ return !cull;
+ }
+
+ protected final void setFlip(boolean horzFlip, boolean vertFlip) {
+ setCrop(horzFlip ? mWidth : 0,
+ vertFlip ? 0 : mHeight,
+ horzFlip ? -mWidth : mWidth,
+ vertFlip ? -mHeight : mHeight);
+ }
+}
diff --git a/src/com/replica/replicaisland/DrawableFactory.java b/src/com/replica/replicaisland/DrawableFactory.java
new file mode 100644
index 0000000..b9a33dc
--- /dev/null
+++ b/src/com/replica/replicaisland/DrawableFactory.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+/**
+ * This class manages drawable objects that have short lifetimes (one or two frames). It provides
+ * type-specific allocator functions and a type-insensitive release function. This class manages
+ * pools of objects so no actual allocations occur after bootstrap.
+ */
+public class DrawableFactory extends BaseObject {
+ private final static int BITMAP_POOL_SIZE = 128;
+
+ private DrawableBitmapPool mBitmapPool;
+ private ScrollableBitmapPool mScrollableBitmapPool;
+ private TiledBackgroundVertexGridPool mTiledBackgroundVertexGridPool;
+
+ // This class wraps several object pools and provides a type-sensitive release function.
+ public DrawableFactory() {
+ super();
+ mBitmapPool = new DrawableBitmapPool(BITMAP_POOL_SIZE);
+ mTiledBackgroundVertexGridPool = new TiledBackgroundVertexGridPool();
+ mScrollableBitmapPool = new ScrollableBitmapPool();
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ public DrawableBitmap allocateDrawableBitmap() {
+ return mBitmapPool.allocate();
+ }
+
+ public TiledBackgroundVertexGrid allocateTiledBackgroundVertexGrid() {
+ return mTiledBackgroundVertexGridPool.allocate();
+ }
+
+ public ScrollableBitmap allocateScrollableBitmap() {
+ return mScrollableBitmapPool.allocate();
+ }
+
+ public void release(DrawableObject object) {
+ ObjectPool pool = object.getParentPool();
+ if (pool != null) {
+ pool.release(object);
+ }
+ // Objects with no pool weren't created by this factory. Ignore them.
+ }
+
+ private class DrawableBitmapPool extends TObjectPool<DrawableBitmap> {
+
+ public DrawableBitmapPool(int size) {
+ super(size);
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ protected void fill() {
+ int size = getSize();
+ for (int x = 0; x < size; x++) {
+ DrawableBitmap entry = new DrawableBitmap(null, 0, 0);
+ entry.setParentPool(this);
+ getAvailable().add(entry);
+ }
+ }
+
+ @Override
+ public void release(Object entry) {
+ ((DrawableBitmap)entry).reset();
+ super.release(entry);
+ }
+
+ @Override
+ public DrawableBitmap allocate() {
+ DrawableBitmap result = super.allocate();
+ ContextParameters params = sSystemRegistry.contextParameters;
+ if (result != null && params != null) {
+ result.setViewSize(params.gameWidth, params.gameHeight);
+ }
+ return result;
+ }
+ }
+
+ private class ScrollableBitmapPool extends TObjectPool<ScrollableBitmap> {
+
+ public ScrollableBitmapPool() {
+ super();
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ protected void fill() {
+ int size = getSize();
+ for (int x = 0; x < size; x++) {
+ ScrollableBitmap entry = new ScrollableBitmap(null, 0, 0);
+ entry.setParentPool(this);
+ getAvailable().add(entry);
+ }
+ }
+
+ @Override
+ public void release(Object entry) {
+ ((ScrollableBitmap)entry).reset();
+ super.release(entry);
+ }
+
+
+ }
+
+ private class TiledBackgroundVertexGridPool extends TObjectPool<TiledBackgroundVertexGrid> {
+
+ public TiledBackgroundVertexGridPool() {
+ super();
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ @Override
+ protected void fill() {
+ int size = getSize();
+ for (int x = 0; x < size; x++) {
+ TiledBackgroundVertexGrid entry = new TiledBackgroundVertexGrid();
+ entry.setParentPool(this);
+ getAvailable().add(entry);
+ }
+ }
+
+ @Override
+ public void release(Object entry) {
+ TiledBackgroundVertexGrid bg = (TiledBackgroundVertexGrid)entry;
+ bg.reset();
+ super.release(entry);
+ }
+
+ }
+}
diff --git a/src/com/replica/replicaisland/DrawableObject.java b/src/com/replica/replicaisland/DrawableObject.java
new file mode 100644
index 0000000..1cb4af4
--- /dev/null
+++ b/src/com/replica/replicaisland/DrawableObject.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * DrawableObject is the base object interface for objects that can be rendered to the screen.
+ * Note that objects derived from DrawableObject are passed between threads, and that care must be
+ * taken when modifying drawable parameters to avoid side-effects (for example, the DrawableFactory
+ * class can be used to generate fire-and-forget drawables).
+ */
+public abstract class DrawableObject extends AllocationGuard {
+ private float mPriority;
+ private ObjectPool mParentPool;
+
+ public abstract void draw(float x, float y, float scaleX, float scaleY);
+
+ public DrawableObject() {
+ super();
+ }
+
+ public void setPriority(float f) {
+ mPriority = f;
+ }
+
+ public float getPriority() {
+ return mPriority;
+ }
+
+ public void setParentPool(ObjectPool pool) {
+ mParentPool = pool;
+ }
+
+ public ObjectPool getParentPool() {
+ return mParentPool;
+ }
+
+ // Override to allow drawables to be sorted by texture.
+ public Texture getTexture() {
+ return null;
+ }
+
+ // Function to allow drawables to specify culling rules.
+ public boolean visibleAtPosition(Vector2 position) {
+ return true;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/DynamicCollisionComponent.java b/src/com/replica/replicaisland/DynamicCollisionComponent.java
new file mode 100644
index 0000000..b82fd79
--- /dev/null
+++ b/src/com/replica/replicaisland/DynamicCollisionComponent.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+/**
+ * A component to include dynamic collision volumes (such as those produced every frame from
+ * animating sprites) in the dynamic collision world. Given a set of "attack" volumes and
+ * "vulnerability" volumes (organized such that only attack vs vulnerability intersections result
+ * in valid "hits"), this component creates a bounding volume that encompasses the set and submits
+ * it to the dynamic collision system. Including this component in a game object will allow it to
+ * send and receive hits to other game objects.
+ */
+public class DynamicCollisionComponent extends GameComponent {
+ private FixedSizeArray<CollisionVolume> mAttackVolumes;
+ private FixedSizeArray<CollisionVolume> mVulnerabilityVolumes;
+ private SphereCollisionVolume mBoundingVolume;
+ private HitReactionComponent mHitReactionComponent;
+
+ public DynamicCollisionComponent() {
+ super();
+ mBoundingVolume = new SphereCollisionVolume(0.0f, 0.0f, 0.0f);
+ setPhase(ComponentPhases.FRAME_END.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mAttackVolumes = null;
+ mVulnerabilityVolumes = null;
+ mBoundingVolume.setCenter(Vector2.ZERO);
+ mBoundingVolume.setRadius(0.0f);
+ mHitReactionComponent = null;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObjectCollisionSystem collision = sSystemRegistry.gameObjectCollisionSystem;
+ if (collision != null && mBoundingVolume.getRadius() > 0.0f) {
+ collision.registerForCollisions((GameObject)parent, mHitReactionComponent, mBoundingVolume,
+ mAttackVolumes, mVulnerabilityVolumes);
+ }
+ }
+
+ public void setHitReactionComponent(HitReactionComponent component) {
+ mHitReactionComponent = component;
+ }
+
+ public void setCollisionVolumes(FixedSizeArray<CollisionVolume> attackVolumes,
+ FixedSizeArray<CollisionVolume> vulnerableVolumes) {
+ if (mVulnerabilityVolumes != vulnerableVolumes || mAttackVolumes != attackVolumes) {
+ mAttackVolumes = attackVolumes;
+ mVulnerabilityVolumes = vulnerableVolumes;
+ mBoundingVolume.reset();
+ if (mAttackVolumes != null) {
+ final int count = mAttackVolumes.getCount();
+ for (int x = 0; x < count; x++) {
+ mBoundingVolume.growBy(mAttackVolumes.get(x));
+ }
+ }
+
+ if (mVulnerabilityVolumes != null) {
+ final int count = mVulnerabilityVolumes.getCount();
+ for (int x = 0; x < count; x++) {
+ mBoundingVolume.growBy(mVulnerabilityVolumes.get(x));
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/EnemyAnimationComponent.java b/src/com/replica/replicaisland/EnemyAnimationComponent.java
new file mode 100644
index 0000000..340b2ea
--- /dev/null
+++ b/src/com/replica/replicaisland/EnemyAnimationComponent.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A general-purpose animation selection system for animating enemy characters. Most enemy
+ * characters behave similarly, so this code tries to decide which animation bets fits their current
+ * state. Other code (such as enemy AI) may move these characters around and change the current
+ * ActionType, which will result in this code figuring out which sequence of animations is best to
+ * play.
+ */
+public class EnemyAnimationComponent extends GameComponent {
+
+ public enum EnemyAnimations {
+ IDLE,
+ MOVE,
+ ATTACK,
+ HIDDEN,
+ APPEAR,
+ }
+
+ private enum AnimationState {
+ IDLING,
+ MOVING,
+ HIDING,
+ APPEARING,
+ ATTACKING
+ }
+
+ private SpriteComponent mSprite;
+ private AnimationState mState;
+ private boolean mFacePlayer;
+
+ public EnemyAnimationComponent() {
+ super();
+ setPhase(ComponentPhases.ANIMATION.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mState = AnimationState.IDLING;
+ mFacePlayer = false;
+ mSprite = null;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mSprite != null) {
+ GameObject parentObject = (GameObject) parent;
+ final float velocityX = parentObject.getVelocity().x;
+
+ GameObject.ActionType currentAction = parentObject.getCurrentAction();
+
+ switch(mState) {
+ case IDLING:
+ mSprite.playAnimation(EnemyAnimations.IDLE.ordinal());
+ if (mFacePlayer) {
+ facePlayer(parentObject);
+ }
+
+ if (currentAction == GameObject.ActionType.ATTACK) {
+ mState = AnimationState.ATTACKING;
+ } else if (currentAction == GameObject.ActionType.HIDE) {
+ mState = AnimationState.HIDING;
+ } else if (Math.abs(velocityX) > 0.0f) {
+ mState = AnimationState.MOVING;
+ }
+
+ break;
+
+ case MOVING:
+ mSprite.playAnimation(EnemyAnimations.MOVE.ordinal());
+ final float targetVelocityX = parentObject.getTargetVelocity().x;
+
+ if (!Utils.close(velocityX, 0.0f)) {
+ if (velocityX < 0.0f && targetVelocityX < 0.0f) {
+ parentObject.facingDirection.x = -1.0f;
+ } else if (velocityX > 0.0f && targetVelocityX > 0.0f) {
+ parentObject.facingDirection.x = 1.0f;
+ }
+ }
+ if (currentAction == GameObject.ActionType.ATTACK) {
+ mState = AnimationState.ATTACKING;
+ } else if (currentAction == GameObject.ActionType.HIDE) {
+ mState = AnimationState.HIDING;
+ } else if (Math.abs(velocityX) == 0.0f) {
+ mState = AnimationState.IDLING;
+ }
+ break;
+ case ATTACKING:
+ mSprite.playAnimation(EnemyAnimations.ATTACK.ordinal());
+ if (currentAction != GameObject.ActionType.ATTACK
+ && mSprite.animationFinished()) {
+ mState = AnimationState.IDLING;
+ }
+ break;
+ case HIDING:
+ mSprite.playAnimation(EnemyAnimations.HIDDEN.ordinal());
+ if (currentAction != GameObject.ActionType.HIDE) {
+ mState = AnimationState.APPEARING;
+ }
+ break;
+ case APPEARING:
+ if (mFacePlayer) {
+ facePlayer(parentObject);
+ }
+
+ mSprite.playAnimation(EnemyAnimations.APPEAR.ordinal());
+ if (mSprite.animationFinished()) {
+ mState = AnimationState.IDLING;
+ }
+ break;
+
+ }
+
+
+ }
+ }
+
+ private void facePlayer(GameObject parentObject) {
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (manager != null) {
+ GameObject player = manager.getPlayer();
+ if (player != null) {
+ if (player.getPosition().x < parentObject.getPosition().x) {
+ parentObject.facingDirection.x = -1.0f;
+ } else {
+ parentObject.facingDirection.x = 1.0f;
+ }
+ }
+ }
+ }
+
+ public void setSprite(SpriteComponent sprite) {
+ mSprite = sprite;
+ }
+
+ public void setFacePlayer(boolean facePlayer) {
+ mFacePlayer = facePlayer;
+ }
+}
diff --git a/src/com/replica/replicaisland/EventRecorder.java b/src/com/replica/replicaisland/EventRecorder.java
new file mode 100644
index 0000000..4f2bec6
--- /dev/null
+++ b/src/com/replica/replicaisland/EventRecorder.java
@@ -0,0 +1,20 @@
+package com.replica.replicaisland;
+
+public class EventRecorder extends BaseObject {
+ private Vector2 mLastDeathPosition = new Vector2();
+
+ @Override
+ public void reset() {
+ // TODO Auto-generated method stub
+
+ }
+
+ synchronized void setLastDeathPosition(Vector2 position) {
+ mLastDeathPosition.set(position);
+ }
+
+ synchronized Vector2 getLastDeathPosition() {
+ return mLastDeathPosition;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/EventReporter.java b/src/com/replica/replicaisland/EventReporter.java
new file mode 100644
index 0000000..1165c67
--- /dev/null
+++ b/src/com/replica/replicaisland/EventReporter.java
@@ -0,0 +1,129 @@
+package com.replica.replicaisland;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+
+public class EventReporter implements Runnable {
+ public final static int EVENT_DEATH = 0;
+ public final static int EVENT_BEAT_LEVEL = 1;
+ public final static int EVENT_BEAT_GAME = 2;
+
+ public final static String REPORT_SERVER = "http://www.replicaisland.net/reporting/event.php";
+
+ private class Event {
+ public String eventType;
+ public float x;
+ public float y;
+ public float time;
+ public String level;
+ public int version;
+ public long session;
+ }
+
+ private Object mLock = new Object();
+ private ArrayList<Event> mEvents = new ArrayList<Event>();
+ private ArrayList<Event> mProcessedEvents = new ArrayList<Event>();
+ private boolean mDone = false;
+
+ public void run() {
+ while (!mDone) {
+ synchronized(mLock) {
+ if (mEvents.isEmpty()) {
+ while (mEvents.isEmpty() && !mDone) {
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ mProcessedEvents.addAll(mEvents);
+ mEvents.clear();
+ }
+
+ final int count = mProcessedEvents.size();
+ for (int x = 0; x < count; x++) {
+ recordEvent(mProcessedEvents.get(x));
+ }
+
+ mProcessedEvents.clear();
+ }
+ }
+
+ public void addEvent(int eventType, float x, float y, float time, String level, int version, long session) {
+ Event event = new Event();
+ event.x = x;
+ event.y = y;
+ event.time = time;
+ event.level = level;
+ event.version = version;
+ event.session = session;
+ switch (eventType) {
+ case EVENT_DEATH:
+ event.eventType = "death";
+ break;
+ case EVENT_BEAT_LEVEL:
+ event.eventType = "beatLevel";
+ break;
+ case EVENT_BEAT_GAME:
+ event.eventType = "beatGame";
+ break;
+ }
+ synchronized(mLock) {
+ mEvents.add(event);
+ mLock.notifyAll();
+ }
+ }
+
+ public void recordEvent(Event event) {
+ URL serverAddress = null;
+ HttpURLConnection connection = null;
+
+ try {
+ serverAddress = new URL(REPORT_SERVER + "?"
+ + "event=" + event.eventType
+ + "&x=" + event.x
+ + "&y=" + event.y
+ + "&time=" + event.time
+ + "&level=" + URLEncoder.encode(event.level, "UTF-8")
+ + "&version=" + event.version
+ + "&session=" + event.session);
+ //set up out communications stuff
+ connection = null;
+
+ //Set up the initial connection
+ connection = (HttpURLConnection)serverAddress.openConnection();
+
+ connection.setRequestMethod("GET");
+ connection.setDoOutput(true);
+ connection.setReadTimeout(0);
+
+ connection.connect();
+ final int response = connection.getResponseCode();
+ DebugLog.d("Report Event", event.eventType + " " + response + ":" + connection.getURL().toString());
+
+
+
+ } catch (Exception e) {
+ // This code can silently fail.
+ //e.printStackTrace();
+ }
+ finally
+ {
+ //close the connection
+ connection.disconnect();
+ connection = null;
+ }
+
+ }
+
+ public void stop() {
+ synchronized(mLock) {
+ mDone = true;
+ mLock.notifyAll();
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/FadeDrawableComponent.java b/src/com/replica/replicaisland/FadeDrawableComponent.java
new file mode 100644
index 0000000..2b35dc6
--- /dev/null
+++ b/src/com/replica/replicaisland/FadeDrawableComponent.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+public class FadeDrawableComponent extends GameComponent {
+ public static final int LOOP_TYPE_NONE = 0;
+ public static final int LOOP_TYPE_LOOP = 1;
+ public static final int LOOP_TYPE_PING_PONG = 2;
+
+ public static final int FADE_LINEAR = 0;
+ public static final int FADE_EASE = 1;
+
+ private Texture mTexture;
+ private RenderComponent mRenderComponent;
+ private float mInitialOpacity;
+ private float mTargetOpacity;
+ private float mStartTime;
+ private float mDuration;
+ private int mLoopType;
+ private int mFunction;
+ private float mInitialDelay;
+ private float mInitialDelayTimer;
+ private float mActivateTime;
+ private float mPhaseDuration;
+
+ public FadeDrawableComponent() {
+ super();
+ setPhase(ComponentPhases.PRE_DRAW.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mTexture = null;
+ mRenderComponent = null;
+ mInitialOpacity = 0.0f;
+ mTargetOpacity = 0.0f;
+ mDuration = 0.0f;
+ mLoopType = LOOP_TYPE_NONE;
+ mFunction = FADE_LINEAR;
+ mStartTime = 0.0f;
+ mInitialDelay = 0.0f;
+ mActivateTime = 0.0f;
+ mPhaseDuration = 0.0f;
+ mInitialDelayTimer = 0.0f;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mRenderComponent != null) {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float currentTime = time.getGameTime();
+
+ // Support repeating "phases" on top of the looping fade itself.
+ // Complexity++, but it lets this component handle several
+ // different use cases.
+ if (mActivateTime == 0.0f) {
+ mActivateTime = currentTime;
+ mInitialDelayTimer = mInitialDelay;
+ } else if (mPhaseDuration > 0.0f && currentTime - mActivateTime > mPhaseDuration) {
+ mActivateTime = currentTime;
+ mInitialDelayTimer = mInitialDelay;
+ mStartTime = 0.0f;
+ }
+
+ if (mInitialDelayTimer > 0.0f) {
+ mInitialDelayTimer -= timeDelta;
+ } else {
+ if (mStartTime == 0) {
+ mStartTime = currentTime;
+ }
+ float elapsed = currentTime - mStartTime;
+ float opacity = mInitialOpacity;
+ if (mLoopType != LOOP_TYPE_NONE && elapsed > mDuration) {
+ final float endTime = mStartTime + mDuration;
+ elapsed = endTime - currentTime;
+ mStartTime = endTime;
+ if (mLoopType == LOOP_TYPE_PING_PONG) {
+ float temp = mInitialOpacity;
+ mInitialOpacity = mTargetOpacity;
+ mTargetOpacity = temp;
+ }
+ }
+
+ if (elapsed > mDuration) {
+ opacity = mTargetOpacity;
+ } else if (elapsed != 0.0f) {
+ if (mFunction == FADE_LINEAR) {
+ opacity = Lerp.lerp(mInitialOpacity, mTargetOpacity, mDuration, elapsed);
+ } else if (mFunction == FADE_EASE) {
+ opacity = Lerp.ease(mInitialOpacity, mTargetOpacity, mDuration, elapsed);
+ }
+ }
+
+ if (mTexture != null) {
+ // If a texture is set then we supply a drawable to the render component.
+ // If not, we take whatever drawable the renderer already has.
+ final DrawableFactory factory = sSystemRegistry.drawableFactory;
+ if (factory != null) {
+ GameObject parentObject = ((GameObject)parent);
+ DrawableBitmap bitmap = factory.allocateDrawableBitmap();
+ bitmap.resize((int)mTexture.width, (int)mTexture.height);
+ //TODO: Super tricky scale. fix this!
+ bitmap.setWidth((int)parentObject.width);
+ bitmap.setHeight((int)parentObject.height);
+ bitmap.setOpacity(opacity);
+ bitmap.setTexture(mTexture);
+ mRenderComponent.setDrawable(bitmap);
+ }
+ } else {
+ DrawableObject drawable = mRenderComponent.getDrawable();
+ // TODO: ack, instanceof! Fix this!
+ if (drawable != null && drawable instanceof DrawableBitmap) {
+ ((DrawableBitmap)drawable).setOpacity(opacity);
+ }
+ }
+ }
+ }
+ }
+
+ public void setupFade(float startOpacity, float endOpacity, float duration, int loopType, int function, float initialDelay) {
+ mInitialOpacity = startOpacity;
+ mTargetOpacity = endOpacity;
+ mDuration = duration;
+ mLoopType = loopType;
+ mFunction = function;
+ mInitialDelay = initialDelay;
+ }
+
+ /** Enables phases; the initial delay will be re-started when the phase ends. **/
+ public void setPhaseDuration(float duration) {
+ mPhaseDuration = duration;
+ }
+
+ /** If set to something non-null, this component will overwrite the drawable on the target render component. **/
+ public void setTexture(Texture texture) {
+ mTexture = texture;
+ }
+
+ public void setRenderComponent(RenderComponent component) {
+ mRenderComponent = component;
+ }
+}
diff --git a/src/com/replica/replicaisland/FixedAnimationComponent.java b/src/com/replica/replicaisland/FixedAnimationComponent.java
new file mode 100644
index 0000000..ccc46c4
--- /dev/null
+++ b/src/com/replica/replicaisland/FixedAnimationComponent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+public class FixedAnimationComponent extends GameComponent {
+ private int mAnimationIndex;
+
+ public FixedAnimationComponent() {
+ super();
+ setPhase(ComponentPhases.ANIMATION.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mAnimationIndex = 0;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ // We look up the sprite component each frame so that this component can be shared.
+ GameObject parentObject = (GameObject)parent;
+ SpriteComponent sprite = parentObject.findByClass(SpriteComponent.class);
+ if (sprite != null) {
+ sprite.playAnimation(mAnimationIndex);
+ }
+ }
+
+ public void setAnimation(int index) {
+ mAnimationIndex = index;
+ }
+}
diff --git a/src/com/replica/replicaisland/FixedSizeArray.java b/src/com/replica/replicaisland/FixedSizeArray.java
new file mode 100644
index 0000000..c1cb4bf
--- /dev/null
+++ b/src/com/replica/replicaisland/FixedSizeArray.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * FixedSizeArray is an alternative to a standard Java collection like ArrayList. It is designed
+ * to provide a contiguous array of fixed length which can be accessed, sorted, and searched without
+ * requiring any runtime allocation. This implementation makes a distinction between the "capacity"
+ * of an array (the maximum number of objects it can contain) and the "count" of an array
+ * (the current number of objects inserted into the array). Operations such as set() and remove()
+ * can only operate on objects that have been explicitly add()-ed to the array; that is, indexes
+ * larger than getCount() but smaller than getCapacity() can't be used on their own.
+ * @param <T> The type of object that this array contains.
+ */
+public class FixedSizeArray<T> extends AllocationGuard {
+ private final static int LINEAR_SEARCH_CUTOFF = 16;
+ private final T[] mContents;
+ private int mCount;
+ private Comparator<T> mComparator;
+ private boolean mSorted;
+ private Sorter<T> mSorter;
+
+ public FixedSizeArray(int size) {
+ super();
+ assert size > 0;
+ // Ugh! No generic array construction in Java.
+ mContents = (T[])new Object[size];
+ mCount = 0;
+ mSorted = false;
+
+ mSorter = new StandardSorter<T>();
+ }
+
+ public FixedSizeArray(int size, Comparator<T> comparator) {
+ super();
+ assert size > 0;
+ mContents = (T[])new Object[size];
+ mCount = 0;
+ mComparator = comparator;
+ mSorted = false;
+
+ mSorter = new StandardSorter<T>();
+ }
+
+ /**
+ * Inserts a new object into the array. If the array is full, an assert is thrown and the
+ * object is ignored.
+ */
+ public final void add(T object) {
+ assert mCount < mContents.length : "Array exhausted!";
+ if (mCount < mContents.length) {
+ mContents[mCount] = object;
+ mSorted = false;
+ mCount++;
+ }
+ }
+
+ /**
+ * Searches for an object and removes it from the array if it is found. Other indexes in the
+ * array are shifted up to fill the space left by the removed object. Note that if
+ * ignoreComparator is set to true, a linear search of object references will be performed.
+ * Otherwise, the comparator set on this array (if any) will be used to find the object.
+ */
+ public void remove(T object, boolean ignoreComparator) {
+ final int index = find(object, ignoreComparator);
+ if (index != -1) {
+ remove(index);
+ }
+ }
+
+ /**
+ * Removes the specified index from the array. Subsequent entries in the array are shifted up
+ * to fill the space.
+ */
+ public void remove(int index) {
+ assert index < mCount;
+ // ugh
+ if (index < mCount) {
+ for (int x = index; x < mCount; x++) {
+ if (x + 1 < mContents.length && x + 1 < mCount) {
+ mContents[x] = mContents[x + 1];
+ } else {
+ mContents[x] = null;
+ }
+ }
+ mCount--;
+ }
+ }
+
+ /**
+ * Removes the last element in the array and returns it. This method is faster than calling
+ * remove(count -1);
+ * @return The contents of the last element in the array.
+ */
+ public T removeLast() {
+ T object = null;
+ if (mCount > 0) {
+ object = mContents[mCount - 1];
+ mContents[mCount - 1] = null;
+ mCount--;
+ }
+ return object;
+ }
+
+ /**
+ * Swaps the element at the passed index with the element at the end of the array. When
+ * followed by removeLast(), this is useful for quickly removing array elements.
+ */
+ public void swapWithLast(int index) {
+ if (mCount > 0 && index < mCount - 1) {
+ T object = mContents[mCount - 1];
+ mContents[mCount - 1] = mContents[index];
+ mContents[index] = object;
+ mSorted = false;
+ }
+ }
+
+ /**
+ * Sets the value of a specific index in the array. An object must have already been added to
+ * the array at that index for this command to complete.
+ */
+ public void set(int index, T object) {
+ assert index < mCount;
+ if (index < mCount) {
+ mContents[index] = object;
+ }
+ }
+
+ /**
+ * Clears the contents of the array, releasing all references to objects it contains and
+ * setting its count to zero.
+ */
+ public void clear() {
+ for (int x = 0; x < mCount; x++) {
+ mContents[x] = null;
+ }
+ mCount = 0;
+ mSorted = false;
+ }
+
+ /**
+ * Returns an entry from the array at the specified index.
+ */
+ public T get(int index) {
+ assert index < mCount;
+ T result = null;
+ if (index < mCount && index >= 0) {
+ result = mContents[index];
+ }
+ return result;
+ }
+
+ /**
+ * Returns the raw internal array. Exposed here so that tight loops can cache this array
+ * and walk it without the overhead of repeated function calls. Beware that changing this array
+ * can leave FixedSizeArray in an undefined state, so this function is potentially dangerous
+ * and should be used in read-only cases.
+ * @return The internal storage array.
+ */
+ public final Object[] getArray() {
+ return mContents;
+ }
+
+
+ /**
+ * Searches the array for the specified object. If the array has been sorted with sort(),
+ * and if no other order-changing events have occurred since the sort (e.g. add()), a
+ * binary search will be performed. If a comparator has been specified with setComparator(),
+ * it will be used to perform the search. If not, the default comparator for the object type
+ * will be used. If the array is unsorted, a linear search is performed.
+ * Note that if ignoreComparator is set to true, a linear search of object references will be
+ * performed. Otherwise, the comparator set on this array (if any) will be used to find the
+ * object.
+ * @param object The object to search for.
+ * @return The index of the object in the array, or -1 if the object is not found.
+ */
+ public int find(T object, boolean ignoreComparator) {
+ int index = -1;
+ final int count = mCount;
+ final boolean sorted = mSorted;
+ final Comparator comparator = mComparator;
+ final T[] contents = mContents;
+ if (sorted && !ignoreComparator && count > LINEAR_SEARCH_CUTOFF) {
+ if (comparator != null) {
+ index = Arrays.binarySearch(contents, object, comparator);
+ } else {
+ index = Arrays.binarySearch(contents, object);
+ }
+ // Arrays.binarySearch() returns a negative insertion index if the object isn't found,
+ // but we just want a boolean.
+ if (index < 0) {
+ index = -1;
+ }
+ } else {
+ // unsorted, linear search
+
+ if (comparator != null && !ignoreComparator) {
+ for (int x = 0; x < count; x++) {
+ final int result = comparator.compare(contents[x], object);
+ if (result == 0) {
+ index = x;
+ break;
+ } else if (result > 0 && sorted) {
+ // we've passed the object, early out
+ break;
+ }
+ }
+ } else {
+ for (int x = 0; x < count; x++) {
+ if (contents[x] == object) {
+ index = x;
+ break;
+ }
+ }
+ }
+ }
+ return index;
+ }
+
+ /**
+ * Sorts the array. If the array is already sorted, no work will be performed unless
+ * the forceResort parameter is set to true. If a comparator has been specified with
+ * setComparator(), it will be used for the sort; otherwise the object's natural ordering will
+ * be used.
+ * @param forceResort If set to true, the array will be resorted even if the order of the
+ * objects in the array has not changed since the last sort.
+ */
+ public void sort(boolean forceResort) {
+ if (!mSorted || forceResort) {
+ if (mComparator != null) {
+ mSorter.sort(mContents, mCount, mComparator);
+ } else {
+ DebugLog.d("FixedSizeArray", "No comparator specified for this type, using Arrays.sort().");
+
+ Arrays.sort(mContents, 0, mCount);
+ }
+ mSorted = true;
+ }
+ }
+
+ /** Returns the number of objects in the array. */
+ public int getCount() {
+ return mCount;
+ }
+
+ /** Returns the maximum number of objects that can be inserted inot this array. */
+ public int getCapacity() {
+ return mContents.length;
+ }
+
+ /** Sets a comparator to use for sorting and searching. */
+ public void setComparator(Comparator<T> comparator) {
+ mComparator = comparator;
+ mSorted = false;
+ }
+
+ public void setSorter(Sorter<T> sorter) {
+ mSorter = sorter;
+ }
+}
diff --git a/src/com/replica/replicaisland/GLErrorLogger.java b/src/com/replica/replicaisland/GLErrorLogger.java
new file mode 100644
index 0000000..7d84d47
--- /dev/null
+++ b/src/com/replica/replicaisland/GLErrorLogger.java
@@ -0,0 +1,1184 @@
+package com.replica.replicaisland;
+
+import java.nio.Buffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+import java.nio.ShortBuffer;
+
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL10Ext;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+import android.opengl.GLU;
+
+public class GLErrorLogger implements GLSurfaceView.GLWrapper {
+
+ public GL wrap(GL gl) {
+ return new ErrorLoggingGL(gl);
+ }
+
+ static class ErrorLoggingGL implements GL, GL10, GL10Ext, GL11, GL11Ext {
+ private GL mGL;
+
+ public ErrorLoggingGL(GL gl) {
+ mGL = gl;
+ }
+
+ public void checkError() {
+
+ int error = ((GL10)mGL).glGetError();
+ if (error != GL10.GL_NO_ERROR) {
+ String method = Thread.currentThread().getStackTrace()[3].getMethodName();
+ DebugLog.d("GL ERROR", "Error: " + error + " (" + GLU.gluErrorString(error) + "): " + method);
+ }
+
+ assert error == GL10.GL_NO_ERROR;
+ }
+
+ public void glActiveTexture(int texture) {
+ ((GL10) mGL).glActiveTexture(texture);
+ checkError();
+ }
+
+ public void glAlphaFunc(int func, float ref) {
+ ((GL10)mGL).glAlphaFunc(func, ref);
+ checkError();
+ }
+
+ public void glAlphaFuncx(int func, int ref) {
+ ((GL10)mGL).glAlphaFuncx(func, ref);
+ checkError();
+ }
+
+ public void glBindTexture(int target, int texture) {
+ ((GL10)mGL).glBindTexture(target, texture);
+ checkError();
+ }
+
+ public void glBlendFunc(int sfactor, int dfactor) {
+ ((GL10)mGL).glBlendFunc(sfactor, dfactor);
+ checkError();
+ }
+
+ public void glClear(int mask) {
+ ((GL10)mGL).glClear(mask);
+ checkError();
+ }
+
+ public void glClearColor(float red, float green, float blue, float alpha) {
+ ((GL10)mGL).glClearColor(red, green, blue, alpha);
+ checkError();
+ }
+
+ public void glClearColorx(int red, int green, int blue, int alpha) {
+ ((GL10)mGL).glClearColorx(red, green, blue, alpha);
+ checkError();
+ }
+
+ public void glClearDepthf(float depth) {
+ ((GL10)mGL).glClearDepthf(depth);
+ checkError();
+ }
+
+ public void glClearDepthx(int depth) {
+ ((GL10)mGL).glClearDepthx(depth);
+ checkError();
+ }
+
+ public void glClearStencil(int s) {
+ ((GL10)mGL).glClearStencil(s);
+ checkError();
+ }
+
+ public void glClientActiveTexture(int texture) {
+ ((GL10)mGL).glClientActiveTexture(texture);
+ checkError();
+ }
+
+ public void glColor4f(float red, float green, float blue, float alpha) {
+ ((GL10)mGL).glColor4f(red, green, blue, alpha);
+ checkError();
+ }
+
+ public void glColor4x(int red, int green, int blue, int alpha) {
+ ((GL10)mGL).glColor4x(red, green, blue, alpha);
+ checkError();
+ }
+
+ public void glColorMask(boolean red, boolean green, boolean blue,
+ boolean alpha) {
+ ((GL10)mGL).glColorMask(red, green, blue, alpha);
+ checkError();
+ }
+
+ public void glColorPointer(int size, int type, int stride,
+ Buffer pointer) {
+ ((GL10)mGL).glColorPointer(size, type, stride, pointer);
+ checkError();
+ }
+
+ public void glCompressedTexImage2D(int target, int level,
+ int internalformat, int width, int height, int border,
+ int imageSize, Buffer data) {
+ ((GL10)mGL).glCompressedTexImage2D(target, level,
+ internalformat, width, height, border, imageSize, data);
+ checkError();
+ }
+
+ public void glCompressedTexSubImage2D(int target, int level,
+ int xoffset, int yoffset, int width, int height, int format,
+ int imageSize, Buffer data) {
+ ((GL10)mGL).glCompressedTexSubImage2D( target, level,
+ xoffset, yoffset, width, height, format,
+ imageSize, data);
+ checkError();
+ }
+
+ public void glCopyTexImage2D(int target, int level, int internalformat,
+ int x, int y, int width, int height, int border) {
+ ((GL10)mGL).glCopyTexImage2D( target, level, internalformat,
+ x, y, width, height, border);
+ checkError();
+
+ }
+
+ public void glCopyTexSubImage2D(int target, int level, int xoffset,
+ int yoffset, int x, int y, int width, int height) {
+ ((GL10)mGL).glCopyTexSubImage2D( target, level, xoffset,
+ yoffset, x, y, width, height);
+ checkError();
+ }
+
+ public void glCullFace(int mode) {
+ ((GL10)mGL).glCullFace(mode);
+ checkError();
+ }
+
+ public void glDeleteTextures(int n, IntBuffer textures) {
+ ((GL10)mGL).glDeleteTextures(n, textures);
+ checkError();
+ }
+
+ public void glDeleteTextures(int n, int[] textures, int offset) {
+ ((GL10)mGL).glDeleteTextures(n, textures, offset);
+ checkError();
+ }
+
+ public void glDepthFunc(int func) {
+ ((GL10)mGL).glDepthFunc(func);
+ checkError();
+ }
+
+ public void glDepthMask(boolean flag) {
+ ((GL10)mGL).glDepthMask(flag);
+ checkError();
+ }
+
+ public void glDepthRangef(float zNear, float zFar) {
+ ((GL10)mGL).glDepthRangef(zNear, zFar);
+ checkError();
+ }
+
+ public void glDepthRangex(int zNear, int zFar) {
+ ((GL10)mGL).glDepthRangex(zNear, zFar);
+ checkError();
+ }
+
+ public void glDisable(int cap) {
+ ((GL10)mGL).glDisable(cap);
+ checkError();
+ }
+
+ public void glDisableClientState(int array) {
+ ((GL10)mGL).glDisableClientState(array);
+ checkError();
+ }
+
+ public void glDrawArrays(int mode, int first, int count) {
+ ((GL10)mGL).glDrawArrays(mode, first, count);
+ checkError();
+ }
+
+ public void glDrawElements(int mode, int count, int type, Buffer indices) {
+ ((GL10)mGL).glDrawElements(mode, count, type, indices);
+ checkError();
+ }
+
+ public void glEnable(int cap) {
+ ((GL10)mGL).glEnable(cap);
+ checkError();
+ }
+
+ public void glEnableClientState(int array) {
+ ((GL10)mGL).glEnableClientState(array);
+ checkError();
+ }
+
+ public void glFinish() {
+ ((GL10)mGL).glFinish();
+ checkError();
+ }
+
+ public void glFlush() {
+ ((GL10)mGL).glFlush();
+ checkError();
+ }
+
+ public void glFogf(int pname, float param) {
+ ((GL10)mGL).glFogf(pname, param);
+ checkError();
+ }
+
+ public void glFogfv(int pname, FloatBuffer params) {
+ ((GL10)mGL).glFogfv(pname, params);
+ checkError();
+ }
+
+ public void glFogfv(int pname, float[] params, int offset) {
+ ((GL10)mGL).glFogfv(pname, params, offset);
+ checkError();
+ }
+
+ public void glFogx(int pname, int param) {
+ ((GL10)mGL).glFogx(pname, param);
+ checkError();
+ }
+
+ public void glFogxv(int pname, IntBuffer params) {
+ ((GL10)mGL).glFogxv(pname, params);
+ checkError();
+ }
+
+ public void glFogxv(int pname, int[] params, int offset) {
+ ((GL10)mGL).glFogxv(pname, params, offset);
+ checkError();
+ }
+
+ public void glFrontFace(int mode) {
+ ((GL10)mGL).glFrontFace(mode);
+ checkError();
+ }
+
+ public void glFrustumf(float left, float right, float bottom,
+ float top, float zNear, float zFar) {
+ ((GL10)mGL).glFrustumf( left, right, bottom,
+ top, zNear, zFar);
+ checkError();
+ }
+
+ public void glFrustumx(int left, int right, int bottom, int top,
+ int zNear, int zFar) {
+ ((GL10)mGL).glFrustumx( left, right, bottom, top,
+ zNear, zFar);
+ checkError();
+ }
+
+ public void glGenTextures(int n, IntBuffer textures) {
+ ((GL10)mGL).glGenTextures(n, textures);
+ checkError();
+ }
+
+ public void glGenTextures(int n, int[] textures, int offset) {
+ ((GL10)mGL).glGenTextures(n, textures, offset);
+ checkError();
+ }
+
+ public int glGetError() {
+ return ((GL10)mGL).glGetError();
+ }
+
+ public void glGetIntegerv(int pname, IntBuffer params) {
+ ((GL10)mGL).glGetIntegerv(pname, params);
+ checkError();
+ }
+
+ public void glGetIntegerv(int pname, int[] params, int offset) {
+ ((GL10)mGL).glGetIntegerv(pname, params, offset);
+ checkError();
+ }
+
+ public String glGetString(int name) {
+ String result = ((GL10)mGL).glGetString(name);
+ checkError();
+ return result;
+ }
+
+ public void glHint(int target, int mode) {
+ ((GL10)mGL).glHint(target, mode);
+ checkError();
+ }
+
+ public void glLightModelf(int pname, float param) {
+ ((GL10)mGL).glLightModelf(pname, param);
+ checkError();
+ }
+
+ public void glLightModelfv(int pname, FloatBuffer params) {
+ ((GL10)mGL).glLightModelfv(pname, params);
+ checkError();
+ }
+
+ public void glLightModelfv(int pname, float[] params, int offset) {
+ ((GL10)mGL).glLightModelfv(pname, params, offset);
+ checkError();
+ }
+
+ public void glLightModelx(int pname, int param) {
+ ((GL10)mGL).glLightModelx(pname, param);
+ checkError();
+ }
+
+ public void glLightModelxv(int pname, IntBuffer params) {
+ ((GL10)mGL).glLightModelxv(pname, params);
+ checkError();
+ }
+
+ public void glLightModelxv(int pname, int[] params, int offset) {
+ ((GL10)mGL).glLightModelxv(pname, params, offset);
+ checkError();
+ }
+
+ public void glLightf(int light, int pname, float param) {
+ ((GL10)mGL).glLightf(light, pname, param);
+ checkError();
+ }
+
+ public void glLightfv(int light, int pname, FloatBuffer params) {
+ ((GL10)mGL).glLightfv(light, pname, params);
+ checkError();
+ }
+
+ public void glLightfv(int light, int pname, float[] params, int offset) {
+ ((GL10)mGL).glLightfv(light, pname, params, offset);
+ checkError();
+ }
+
+ public void glLightx(int light, int pname, int param) {
+ ((GL10)mGL).glLightx(light, pname, param);
+ checkError();
+ }
+
+ public void glLightxv(int light, int pname, IntBuffer params) {
+ ((GL10)mGL).glLightxv(light, pname, params);
+ checkError();
+ }
+
+ public void glLightxv(int light, int pname, int[] params, int offset) {
+ ((GL10)mGL).glLightxv(light, pname, params, offset);
+ checkError();
+ }
+
+ public void glLineWidth(float width) {
+ ((GL10)mGL).glLineWidth(width);
+ checkError();
+ }
+
+ public void glLineWidthx(int width) {
+ ((GL10)mGL).glLineWidthx(width);
+ checkError();
+ }
+
+ public void glLoadIdentity() {
+ ((GL10)mGL).glLoadIdentity();
+ checkError();
+ }
+
+ public void glLoadMatrixf(FloatBuffer m) {
+ ((GL10)mGL).glLoadMatrixf(m);
+ checkError();
+ }
+
+ public void glLoadMatrixf(float[] m, int offset) {
+ ((GL10)mGL).glLoadMatrixf(m, offset);
+ checkError();
+ }
+
+ public void glLoadMatrixx(IntBuffer m) {
+ ((GL10)mGL).glLoadMatrixx(m);
+ checkError();
+ }
+
+ public void glLoadMatrixx(int[] m, int offset) {
+ ((GL10)mGL).glLoadMatrixx(m, offset);
+ checkError();
+ }
+
+ public void glLogicOp(int opcode) {
+ ((GL10)mGL).glLogicOp(opcode);
+ checkError();
+ }
+
+ public void glMaterialf(int face, int pname, float param) {
+ ((GL10)mGL).glMaterialf(face, pname, param);
+ checkError();
+ }
+
+ public void glMaterialfv(int face, int pname, FloatBuffer params) {
+ ((GL10)mGL).glMaterialfv(face, pname, params);
+ checkError();
+ }
+
+ public void glMaterialfv(int face, int pname, float[] params, int offset) {
+ ((GL10)mGL).glMaterialfv(face, pname, params, offset);
+ checkError();
+ }
+
+ public void glMaterialx(int face, int pname, int param) {
+ ((GL10)mGL).glMaterialx(face, pname, param);
+ checkError();
+ }
+
+ public void glMaterialxv(int face, int pname, IntBuffer params) {
+ ((GL10)mGL).glMaterialxv(face, pname, params);
+ checkError();
+ }
+
+ public void glMaterialxv(int face, int pname, int[] params, int offset) {
+ ((GL10)mGL).glMaterialxv(face, pname, params, offset);
+ checkError();
+ }
+
+ public void glMatrixMode(int mode) {
+ ((GL10)mGL).glMatrixMode(mode);
+ checkError();
+ }
+
+ public void glMultMatrixf(FloatBuffer m) {
+ ((GL10)mGL).glMultMatrixf(m);
+ checkError();
+ }
+
+ public void glMultMatrixf(float[] m, int offset) {
+ ((GL10)mGL).glMultMatrixf(m, offset);
+ checkError();
+ }
+
+ public void glMultMatrixx(IntBuffer m) {
+ ((GL10)mGL).glMultMatrixx(m);
+ checkError();
+ }
+
+ public void glMultMatrixx(int[] m, int offset) {
+ ((GL10)mGL).glMultMatrixx(m, offset);
+ checkError();
+ }
+
+ public void glMultiTexCoord4f(int target, float s, float t, float r,
+ float q) {
+ ((GL10)mGL).glMultiTexCoord4f( target, s, t, r, q);
+ checkError();
+ }
+
+ public void glMultiTexCoord4x(int target, int s, int t, int r, int q) {
+ ((GL10)mGL).glMultiTexCoord4x( target, s, t, r, q);
+ checkError();
+ }
+
+ public void glNormal3f(float nx, float ny, float nz) {
+ ((GL10)mGL).glNormal3f(nx, ny, nz);
+ checkError();
+ }
+
+ public void glNormal3x(int nx, int ny, int nz) {
+ ((GL10)mGL).glNormal3x(nx, ny, nz);
+ checkError();
+ }
+
+ public void glNormalPointer(int type, int stride, Buffer pointer) {
+ ((GL10)mGL).glNormalPointer(type, stride, pointer);
+ checkError();
+ }
+
+ public void glOrthof(float left, float right, float bottom, float top,
+ float zNear, float zFar) {
+ ((GL10)mGL).glOrthof( left, right, bottom, top,
+ zNear, zFar);
+ checkError();
+ }
+
+ public void glOrthox(int left, int right, int bottom, int top,
+ int zNear, int zFar) {
+ ((GL10)mGL).glOrthox( left, right, bottom, top,
+ zNear, zFar);
+ checkError();
+ }
+
+ public void glPixelStorei(int pname, int param) {
+ ((GL10)mGL).glPixelStorei(pname, param);
+ checkError();
+ }
+
+ public void glPointSize(float size) {
+ ((GL10)mGL).glPointSize(size);
+ checkError();
+ }
+
+ public void glPointSizex(int size) {
+ ((GL10)mGL).glPointSizex(size);
+ checkError();
+ }
+
+ public void glPolygonOffset(float factor, float units) {
+ ((GL10)mGL).glPolygonOffset(factor, units);
+ checkError();
+ }
+
+ public void glPolygonOffsetx(int factor, int units) {
+ ((GL10)mGL).glPolygonOffsetx(factor, units);
+ checkError();
+ }
+
+ public void glPopMatrix() {
+ ((GL10)mGL).glPopMatrix();
+ checkError();
+ }
+
+ public void glPushMatrix() {
+ ((GL10)mGL).glPushMatrix();
+ checkError();
+ }
+
+ public void glReadPixels(int x, int y, int width, int height,
+ int format, int type, Buffer pixels) {
+ ((GL10)mGL).glReadPixels( x, y, width, height,
+ format, type, pixels);
+ checkError();
+ }
+
+ public void glRotatef(float angle, float x, float y, float z) {
+ ((GL10)mGL).glRotatef(angle, x, y, z);
+ checkError();
+ }
+
+ public void glRotatex(int angle, int x, int y, int z) {
+ ((GL10)mGL).glRotatex(angle, x, y, z);
+ checkError();
+ }
+
+ public void glSampleCoverage(float value, boolean invert) {
+ ((GL10)mGL).glSampleCoverage(value, invert);
+ checkError();
+ }
+
+ public void glSampleCoveragex(int value, boolean invert) {
+ ((GL10)mGL).glSampleCoveragex(value, invert);
+ checkError();
+ }
+
+ public void glScalef(float x, float y, float z) {
+ ((GL10)mGL).glScalef(x, y, z);
+ checkError();
+ }
+
+ public void glScalex(int x, int y, int z) {
+ ((GL10)mGL).glScalex(x, y, z);
+ checkError();
+ }
+
+ public void glScissor(int x, int y, int width, int height) {
+ ((GL10)mGL).glScissor(x, y, width, height);
+ checkError();
+ }
+
+ public void glShadeModel(int mode) {
+ ((GL10)mGL).glShadeModel(mode);
+ checkError();
+ }
+
+ public void glStencilFunc(int func, int ref, int mask) {
+ ((GL10)mGL).glStencilFunc(func, ref, mask);
+ checkError();
+ }
+
+ public void glStencilMask(int mask) {
+ ((GL10)mGL).glStencilMask(mask);
+ checkError();
+ }
+
+ public void glStencilOp(int fail, int zfail, int zpass) {
+ ((GL10)mGL).glStencilOp(fail, zfail, zpass);
+ checkError();
+ }
+
+ public void glTexCoordPointer(int size, int type, int stride,
+ Buffer pointer) {
+ ((GL10)mGL).glTexCoordPointer( size, type, stride,
+ pointer);
+ checkError();
+ }
+
+ public void glTexEnvf(int target, int pname, float param) {
+ ((GL10)mGL).glTexEnvf(target, pname, param);
+ checkError();
+ }
+
+ public void glTexEnvfv(int target, int pname, FloatBuffer params) {
+ ((GL10)mGL).glTexEnvfv(target, pname, params);
+ checkError();
+ }
+
+ public void glTexEnvfv(int target, int pname, float[] params, int offset) {
+ ((GL10)mGL).glTexEnvfv(target, pname, params, offset);
+ checkError();
+ }
+
+ public void glTexEnvx(int target, int pname, int param) {
+ ((GL10)mGL).glTexEnvx(target, pname, param);
+ checkError();
+ }
+
+ public void glTexEnvxv(int target, int pname, IntBuffer params) {
+ ((GL10)mGL).glTexEnvxv(target, pname, params);
+ checkError();
+ }
+
+ public void glTexEnvxv(int target, int pname, int[] params, int offset) {
+ ((GL10)mGL).glTexEnvxv(target, pname, params, offset);
+ checkError();
+ }
+
+ public void glTexImage2D(int target, int level, int internalformat,
+ int width, int height, int border, int format, int type,
+ Buffer pixels) {
+ ((GL10)mGL).glTexImage2D( target, level, internalformat,
+ width, height, border, format, type,
+ pixels);
+ checkError();
+ }
+
+ public void glTexParameterf(int target, int pname, float param) {
+ ((GL10)mGL).glTexParameterf(target, pname, param);
+ checkError();
+ }
+
+ public void glTexParameterx(int target, int pname, int param) {
+ ((GL10)mGL).glTexParameterx(target, pname, param);
+ checkError();
+ }
+
+ public void glTexSubImage2D(int target, int level, int xoffset,
+ int yoffset, int width, int height, int format, int type,
+ Buffer pixels) {
+ ((GL10)mGL).glTexSubImage2D( target, level, xoffset,
+ yoffset, width, height, format, type,
+ pixels);
+ checkError();
+ }
+
+ public void glTranslatef(float x, float y, float z) {
+ ((GL10)mGL).glTranslatef(x, y, z);
+ checkError();
+ }
+
+ public void glTranslatex(int x, int y, int z) {
+ ((GL10)mGL).glTranslatex(x, y, z);
+ checkError();
+ }
+
+ public void glVertexPointer(int size, int type, int stride,
+ Buffer pointer) {
+ ((GL10)mGL).glVertexPointer( size, type, stride,
+ pointer);
+ checkError();
+ }
+
+ public void glViewport(int x, int y, int width, int height) {
+ ((GL10)mGL).glViewport(x, y, width, height);
+ checkError();
+ }
+
+ public void glBindBuffer(int arg0, int arg1) {
+ ((GL11)mGL).glBindBuffer(arg0, arg1);
+ checkError();
+ }
+
+ public void glBufferData(int arg0, int arg1, Buffer arg2, int arg3) {
+ ((GL11)mGL).glBufferData(arg0, arg1, arg2, arg3);
+ checkError();
+ }
+
+ public void glBufferSubData(int arg0, int arg1, int arg2, Buffer arg3) {
+ ((GL11)mGL).glBufferSubData(arg0, arg1, arg2, arg3);
+ checkError();
+ }
+
+ public void glClipPlanef(int arg0, FloatBuffer arg1) {
+ ((GL11)mGL).glClipPlanef(arg0, arg1);
+ checkError();
+ }
+
+ public void glClipPlanef(int arg0, float[] arg1, int arg2) {
+ ((GL11)mGL).glClipPlanef(arg0, arg1, arg2);
+ checkError();
+ }
+
+ public void glClipPlanex(int arg0, IntBuffer arg1) {
+ ((GL11)mGL).glClipPlanex(arg0, arg1);
+ checkError();
+ }
+
+ public void glClipPlanex(int arg0, int[] arg1, int arg2) {
+ ((GL11)mGL).glClipPlanex(arg0, arg1, arg2);
+ checkError();
+ }
+
+ public void glColor4ub(byte arg0, byte arg1, byte arg2, byte arg3) {
+ ((GL11)mGL).glColor4ub(arg0, arg1, arg2, arg3);
+ checkError();
+ }
+
+ public void glColorPointer(int arg0, int arg1, int arg2, int arg3) {
+ ((GL11)mGL).glColorPointer(arg0, arg1, arg2, arg3);
+ checkError();
+ }
+
+ public void glDeleteBuffers(int n, IntBuffer buffers) {
+ ((GL11)mGL).glDeleteBuffers(n, buffers);
+ checkError();
+ }
+
+ public void glDeleteBuffers(int n, int[] buffers, int offset) {
+ ((GL11)mGL).glDeleteBuffers(n, buffers, offset);
+ checkError();
+ }
+
+ public void glDrawElements(int mode, int count, int type, int offset) {
+ ((GL11)mGL).glDrawElements(mode, count, type, offset);
+ checkError();
+ }
+
+ public void glGenBuffers(int n, IntBuffer buffers) {
+ ((GL11)mGL).glGenBuffers(n, buffers);
+ checkError();
+ }
+
+ public void glGenBuffers(int n, int[] buffers, int offset) {
+ ((GL11)mGL).glGenBuffers(n, buffers, offset);
+ checkError();
+ }
+
+ public void glGetBooleanv(int pname, IntBuffer params) {
+ ((GL11)mGL).glGetBooleanv(pname, params);
+ checkError();
+ }
+
+ public void glGetBooleanv(int pname, boolean[] params, int offset) {
+ ((GL11)mGL).glGetBooleanv(pname, params, offset);
+ checkError();
+ }
+
+ public void glGetBufferParameteriv(int target, int pname,
+ IntBuffer params) {
+ ((GL11)mGL).glGetBufferParameteriv( target, pname,
+ params);
+ checkError();
+ }
+
+ public void glGetBufferParameteriv(int target, int pname, int[] params,
+ int offset) {
+ ((GL11)mGL).glGetBufferParameteriv( target, pname, params,
+ offset);
+ checkError();
+ }
+
+ public void glGetClipPlanef(int pname, FloatBuffer eqn) {
+ ((GL11)mGL).glGetClipPlanef(pname, eqn);
+ checkError();
+ }
+
+ public void glGetClipPlanef(int pname, float[] eqn, int offset) {
+ ((GL11)mGL).glGetClipPlanef(pname, eqn, offset);
+ checkError();
+ }
+
+ public void glGetClipPlanex(int pname, IntBuffer eqn) {
+ ((GL11)mGL).glGetClipPlanex(pname, eqn);
+ checkError();
+ }
+
+ public void glGetClipPlanex(int pname, int[] eqn, int offset) {
+ ((GL11)mGL).glGetClipPlanex(pname, eqn, offset);
+ checkError();
+ }
+
+ public void glGetFixedv(int pname, IntBuffer params) {
+ ((GL11)mGL).glGetFixedv(pname, params);
+ checkError();
+ }
+
+ public void glGetFixedv(int pname, int[] params, int offset) {
+ ((GL11)mGL).glGetFixedv(pname, params, offset);
+ checkError();
+ }
+
+ public void glGetFloatv(int pname, FloatBuffer params) {
+ ((GL11)mGL).glGetFloatv(pname, params);
+ checkError();
+ }
+
+ public void glGetFloatv(int pname, float[] params, int offset) {
+ ((GL11)mGL).glGetFloatv(pname, params, offset);
+ checkError();
+ }
+
+ public void glGetLightfv(int light, int pname, FloatBuffer params) {
+ ((GL11)mGL).glGetLightfv(light, pname, params);
+ checkError();
+ }
+
+ public void glGetLightfv(int light, int pname, float[] params,
+ int offset) {
+ ((GL11)mGL).glGetLightfv( light, pname, params,
+ offset);
+ checkError();
+ }
+
+ public void glGetLightxv(int light, int pname, IntBuffer params) {
+ ((GL11)mGL).glGetLightxv(light, pname, params);
+ checkError();
+ }
+
+ public void glGetLightxv(int light, int pname, int[] params, int offset) {
+ ((GL11)mGL).glGetLightxv(light, pname, params, offset);
+ checkError();
+ }
+
+ public void glGetMaterialfv(int face, int pname, FloatBuffer params) {
+ ((GL11)mGL).glGetMaterialfv(face, pname, params);
+ checkError();
+ }
+
+ public void glGetMaterialfv(int face, int pname, float[] params,
+ int offset) {
+ ((GL11)mGL).glGetMaterialfv( face, pname, params,
+ offset);
+ checkError();
+ }
+
+ public void glGetMaterialxv(int face, int pname, IntBuffer params) {
+ ((GL11)mGL).glGetMaterialxv(face, pname, params);
+ checkError();
+ }
+
+ public void glGetMaterialxv(int face, int pname, int[] params,
+ int offset) {
+ ((GL11)mGL).glGetMaterialxv( face, pname, params,
+ offset);
+ checkError();
+ }
+
+ public void glGetPointerv(int pname, Buffer[] params) {
+ ((GL11)mGL).glGetPointerv(pname, params);
+ checkError();
+ }
+
+ public void glGetTexEnviv(int env, int pname, IntBuffer params) {
+ ((GL11)mGL).glGetTexEnviv(env, pname, params);
+ checkError();
+ }
+
+ public void glGetTexEnviv(int env, int pname, int[] params, int offset) {
+ ((GL11)mGL).glGetTexEnviv(env, pname, params, offset);
+ checkError();
+ }
+
+ public void glGetTexEnvxv(int env, int pname, IntBuffer params) {
+ ((GL11)mGL).glGetTexEnvxv(env, pname, params);
+ checkError();
+ }
+
+ public void glGetTexEnvxv(int env, int pname, int[] params, int offset) {
+ ((GL11)mGL).glGetTexEnvxv(env, pname, params, offset);
+ checkError();
+ }
+
+ public void glGetTexParameterfv(int target, int pname,
+ FloatBuffer params) {
+ ((GL11)mGL).glGetTexParameterfv( target, pname,
+ params);
+ checkError();
+ }
+
+ public void glGetTexParameterfv(int target, int pname, float[] params,
+ int offset) {
+ ((GL11)mGL).glGetTexParameterfv( target, pname, params,
+ offset);
+ checkError();
+ }
+
+ public void glGetTexParameteriv(int target, int pname, IntBuffer params) {
+ ((GL11)mGL).glGetTexParameteriv(target, pname, params);
+ checkError();
+ }
+
+ public void glGetTexParameteriv(int target, int pname, int[] params,
+ int offset) {
+ ((GL11)mGL).glGetTexParameteriv( target, pname, params,
+ offset);
+ checkError();
+ }
+
+ public void glGetTexParameterxv(int target, int pname, IntBuffer params) {
+ ((GL11)mGL).glGetTexParameterxv(target, pname, params);
+ checkError();
+ }
+
+ public void glGetTexParameterxv(int target, int pname, int[] params,
+ int offset) {
+ ((GL11)mGL).glGetTexParameterxv( target, pname, params,
+ offset);
+ checkError();
+ }
+
+ public boolean glIsBuffer(int buffer) {
+ boolean result = ((GL11)mGL).glIsBuffer( buffer);
+ checkError();
+ return result;
+ }
+
+ public boolean glIsEnabled(int cap) {
+ boolean result = ((GL11)mGL).glIsEnabled(cap);
+ checkError();
+ return result;
+ }
+
+ public boolean glIsTexture(int texture) {
+ boolean result = ((GL11)mGL).glIsTexture( texture);
+ checkError();
+ return result;
+ }
+
+ public void glNormalPointer(int type, int stride, int offset) {
+ ((GL11)mGL).glNormalPointer(type, stride, offset);
+ checkError();
+ }
+
+ public void glPointParameterf(int pname, float param) {
+ ((GL11)mGL).glPointParameterf(pname, param);
+ checkError();
+ }
+
+ public void glPointParameterfv(int pname, FloatBuffer params) {
+ ((GL11)mGL).glPointParameterfv(pname, params);
+ checkError();
+ }
+
+ public void glPointParameterfv(int pname, float[] params, int offset) {
+ ((GL11)mGL).glPointParameterfv(pname, params, offset);
+ checkError();
+ }
+
+ public void glPointParameterx(int pname, int param) {
+ ((GL11)mGL).glPointParameterx(pname, param);
+ checkError();
+ }
+
+ public void glPointParameterxv(int pname, IntBuffer params) {
+ ((GL11)mGL).glPointParameterxv(pname, params);
+ checkError();
+ }
+
+ public void glPointParameterxv(int pname, int[] params, int offset) {
+ ((GL11)mGL).glPointParameterxv(pname, params, offset);
+ checkError();
+ }
+
+ public void glPointSizePointerOES(int type, int stride, Buffer pointer) {
+ ((GL11)mGL).glPointSizePointerOES(type, stride, pointer);
+ checkError();
+ }
+
+ public void glTexCoordPointer(int size, int type, int stride, int offset) {
+ ((GL11)mGL).glTexCoordPointer(size, type, stride, offset);
+ checkError();
+ }
+
+ public void glTexEnvi(int target, int pname, int param) {
+ ((GL11)mGL).glTexEnvi(target, pname, param);
+ checkError();
+ }
+
+ public void glTexEnviv(int target, int pname, IntBuffer params) {
+ ((GL11)mGL).glTexEnviv(target, pname, params);
+ checkError();
+ }
+
+ public void glTexEnviv(int target, int pname, int[] params, int offset) {
+ ((GL11)mGL).glTexEnviv(target, pname, params, offset);
+ checkError();
+ }
+
+ public void glTexParameterfv(int target, int pname, FloatBuffer params) {
+ ((GL11)mGL).glTexParameterfv(target, pname, params);
+ checkError();
+ }
+
+ public void glTexParameterfv(int target, int pname, float[] params,
+ int offset) {
+ ((GL11)mGL).glTexParameterfv( target, pname, params,
+ offset);
+ checkError();
+ }
+
+ public void glTexParameteri(int target, int pname, int param) {
+ ((GL11)mGL).glTexParameteri(target, pname, param);
+ checkError();
+ }
+
+ public void glTexParameteriv(int target, int pname, IntBuffer params) {
+ ((GL11)mGL).glTexParameteriv(target, pname, params);
+ checkError();
+ }
+
+ public void glTexParameteriv(int target, int pname, int[] params,
+ int offset) { }
+
+ public void glTexParameterxv(int target, int pname, IntBuffer params) {
+ ((GL11)mGL).glTexParameterxv(target, pname, params);
+ checkError();
+ }
+
+ public void glTexParameterxv(int target, int pname, int[] params,
+ int offset) {
+ ((GL11)mGL).glTexParameterxv( target, pname, params,
+ offset);
+ checkError();
+ }
+
+ public void glVertexPointer(int size, int type, int stride, int offset) {
+ ((GL11)mGL).glVertexPointer(size, type, stride, offset);
+ checkError();
+ }
+
+ public void glCurrentPaletteMatrixOES(int matrixpaletteindex) {
+ ((GL11Ext)mGL).glCurrentPaletteMatrixOES(matrixpaletteindex);
+ checkError();
+ }
+
+ public void glDrawTexfOES(float x, float y, float z, float width,
+ float height) {
+ ((GL11Ext)mGL).glDrawTexfOES( x, y, z, width,
+ height);
+ checkError();
+ }
+
+ public void glDrawTexfvOES(FloatBuffer coords) {
+ ((GL11Ext)mGL).glDrawTexfvOES(coords);
+ checkError();
+ }
+
+ public void glDrawTexfvOES(float[] coords, int offset) {
+ ((GL11Ext)mGL).glDrawTexfvOES(coords, offset);
+ checkError();
+ }
+
+ public void glDrawTexiOES(int x, int y, int z, int width, int height) {
+ ((GL11Ext)mGL).glDrawTexiOES( x, y, z, width, height);
+ checkError();
+ }
+
+ public void glDrawTexivOES(IntBuffer coords) {
+ ((GL11Ext)mGL).glDrawTexivOES(coords);
+ checkError();
+
+
+ }
+
+ public void glDrawTexivOES(int[] coords, int offset) {
+ ((GL11Ext)mGL).glDrawTexivOES(coords, offset);
+ checkError();
+
+
+ }
+
+ public void glDrawTexsOES(short x, short y, short z, short width,
+ short height) {
+ ((GL11Ext)mGL).glDrawTexsOES( x, y, z, width,
+ height);
+ checkError();
+ }
+
+ public void glDrawTexsvOES(ShortBuffer coords) {
+ ((GL11Ext)mGL).glDrawTexsvOES(coords);
+ checkError();
+
+
+ }
+
+ public void glDrawTexsvOES(short[] coords, int offset) {
+ ((GL11Ext)mGL).glDrawTexsvOES(coords, offset);
+ checkError();
+
+
+ }
+
+ public void glDrawTexxOES(int x, int y, int z, int width, int height) {
+ ((GL11Ext)mGL).glDrawTexxOES( x, y, z, width, height);
+ checkError();
+ }
+
+ public void glDrawTexxvOES(IntBuffer coords) {
+ ((GL11Ext)mGL).glDrawTexxvOES(coords);
+ checkError();
+
+
+ }
+
+ public void glDrawTexxvOES(int[] coords, int offset) {
+ ((GL11Ext)mGL).glDrawTexxvOES(coords, offset);
+ checkError();
+
+
+ }
+
+ public void glLoadPaletteFromModelViewMatrixOES() {
+ ((GL11Ext)mGL).glLoadPaletteFromModelViewMatrixOES();
+ checkError();
+
+
+ }
+
+ public void glMatrixIndexPointerOES(int size, int type, int stride,
+ Buffer pointer) {
+ ((GL11Ext)mGL).glMatrixIndexPointerOES( size, type, stride,
+ pointer);
+ checkError();
+
+ }
+
+ public void glMatrixIndexPointerOES(int size, int type, int stride,
+ int offset) {
+ ((GL11Ext)mGL).glMatrixIndexPointerOES( size, type, stride,
+ offset);
+ checkError();
+ }
+
+ public void glWeightPointerOES(int size, int type, int stride,
+ Buffer pointer) {
+ ((GL11Ext)mGL).glWeightPointerOES( size, type, stride,
+ pointer);
+ checkError();
+ }
+
+ public void glWeightPointerOES(int size, int type, int stride,
+ int offset) {
+ ((GL11Ext)mGL).glWeightPointerOES( size, type, stride,
+ offset);
+ checkError();
+ }
+
+ public int glQueryMatrixxOES(IntBuffer arg0, IntBuffer arg1) {
+ int result = ((GL10Ext)mGL).glQueryMatrixxOES( arg0, arg1);
+ checkError();
+ return result;
+ }
+
+ public int glQueryMatrixxOES(int[] arg0, int arg1, int[] arg2, int arg3) {
+ int result = ((GL10Ext)mGL).glQueryMatrixxOES(arg0, arg1,arg2, arg3);
+ checkError();
+ return result;
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/GLSurfaceView.java b/src/com/replica/replicaisland/GLSurfaceView.java
new file mode 100644
index 0000000..4430ae2
--- /dev/null
+++ b/src/com/replica/replicaisland/GLSurfaceView.java
@@ -0,0 +1,1631 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.io.Writer;
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGL11;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+import javax.microedition.khronos.opengles.GL;
+import javax.microedition.khronos.opengles.GL10;
+
+import android.content.Context;
+import android.content.pm.ConfigurationInfo;
+import android.opengl.GLDebugHelper;
+import android.util.AttributeSet;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+
+/**
+ * An implementation of SurfaceView that uses the dedicated surface for
+ * displaying OpenGL rendering.
+ * <p>
+ * A GLSurfaceView provides the following features:
+ * <p>
+ * <ul>
+ * <li>Manages a surface, which is a special piece of memory that can be
+ * composited into the Android view system.
+ * <li>Manages an EGL display, which enables OpenGL to render into a surface.
+ * <li>Accepts a user-provided Renderer object that does the actual rendering.
+ * <li>Renders on a dedicated thread to decouple rendering performance from the
+ * UI thread.
+ * <li>Supports both on-demand and continuous rendering.
+ * <li>Optionally wraps, traces, and/or error-checks the renderer's OpenGL calls.
+ * </ul>
+ *
+ * <h3>Using GLSurfaceView</h3>
+ * <p>
+ * Typically you use GLSurfaceView by subclassing it and overriding one or more of the
+ * View system input event methods. If your application does not need to override event
+ * methods then GLSurfaceView can be used as-is. For the most part
+ * GLSurfaceView behavior is customized by calling "set" methods rather than by subclassing.
+ * For example, unlike a regular View, drawing is delegated to a separate Renderer object which
+ * is registered with the GLSurfaceView
+ * using the {@link #setRenderer(Renderer)} call.
+ * <p>
+ * <h3>Initializing GLSurfaceView</h3>
+ * All you have to do to initialize a GLSurfaceView is call {@link #setRenderer(Renderer)}.
+ * However, if desired, you can modify the default behavior of GLSurfaceView by calling one or
+ * more of these methods before calling setRenderer:
+ * <ul>
+ * <li>{@link #setDebugFlags(int)}
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * <li>{@link #setGLWrapper(GLWrapper)}
+ * </ul>
+ * <p>
+ * <h4>Choosing an EGL Configuration</h4>
+ * A given Android device may support multiple possible types of drawing surfaces.
+ * The available surfaces may differ in how may channels of data are present, as
+ * well as how many bits are allocated to each channel. Therefore, the first thing
+ * GLSurfaceView has to do when starting to render is choose what type of surface to use.
+ * <p>
+ * By default GLSurfaceView chooses an available surface that's closest to a 16-bit R5G6B5 surface
+ * with a 16-bit depth buffer and no stencil. If you would prefer a different surface (for example,
+ * if you do not need a depth buffer) you can override the default behavior by calling one of the
+ * setEGLConfigChooser methods.
+ * <p>
+ * <h4>Debug Behavior</h4>
+ * You can optionally modify the behavior of GLSurfaceView by calling
+ * one or more of the debugging methods {@link #setDebugFlags(int)},
+ * and {@link #setGLWrapper}. These methods may be called before and/or after setRenderer, but
+ * typically they are called before setRenderer so that they take effect immediately.
+ * <p>
+ * <h4>Setting a Renderer</h4>
+ * Finally, you must call {@link #setRenderer} to register a {@link Renderer}.
+ * The renderer is
+ * responsible for doing the actual OpenGL rendering.
+ * <p>
+ * <h3>Rendering Mode</h3>
+ * Once the renderer is set, you can control whether the renderer draws
+ * continuously or on-demand by calling
+ * {@link #setRenderMode}. The default is continuous rendering.
+ * <p>
+ * <h3>Activity Life-cycle</h3>
+ * A GLSurfaceView must be notified when the activity is paused and resumed. GLSurfaceView clients
+ * are required to call {@link #onPause()} when the activity pauses and
+ * {@link #onResume()} when the activity resumes. These calls allow GLSurfaceView to
+ * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate
+ * the OpenGL display.
+ * <p>
+ * <h3>Handling events</h3>
+ * <p>
+ * To handle an event you will typically subclass GLSurfaceView and override the
+ * appropriate method, just as you would with any other View. However, when handling
+ * the event, you may need to communicate with the Renderer object
+ * that's running in the rendering thread. You can do this using any
+ * standard Java cross-thread communication mechanism. In addition,
+ * one relatively easy way to communicate with your renderer is
+ * to call
+ * {@link #queueEvent(Runnable)}. For example:
+ * <pre class="prettyprint">
+ * class MyGLSurfaceView extends GLSurfaceView {
+ *
+ * private MyRenderer mMyRenderer;
+ *
+ * public void start() {
+ * mMyRenderer = ...;
+ * setRenderer(mMyRenderer);
+ * }
+ *
+ * public boolean onKeyDown(int keyCode, KeyEvent event) {
+ * if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+ * queueEvent(new Runnable() {
+ * // This method will be called on the rendering
+ * // thread:
+ * public void run() {
+ * mMyRenderer.handleDpadCenter();
+ * }});
+ * return true;
+ * }
+ * return super.onKeyDown(keyCode, event);
+ * }
+ * }
+ * </pre>
+ *
+ */
+public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback {
+ private final static boolean LOG_THREADS = false;
+ private final static boolean LOG_SURFACE = true;
+ private final static boolean LOG_RENDERER = false;
+ // Work-around for bug 2263168
+ private final static boolean DRAW_TWICE_AFTER_SIZE_CHANGED = true;
+ /**
+ * The renderer only renders
+ * when the surface is created, or when {@link #requestRender} is called.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ */
+ public final static int RENDERMODE_WHEN_DIRTY = 0;
+ /**
+ * The renderer is called
+ * continuously to re-render the scene.
+ *
+ * @see #getRenderMode()
+ * @see #setRenderMode(int)
+ * @see #requestRender()
+ */
+ public final static int RENDERMODE_CONTINUOUSLY = 1;
+
+ /**
+ * Check glError() after every GL call and throw an exception if glError indicates
+ * that an error has occurred. This can be used to help track down which OpenGL ES call
+ * is causing an error.
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_CHECK_GL_ERROR = 1;
+
+ /**
+ * Log GL calls to the system log at "verbose" level with tag "GLSurfaceView".
+ *
+ * @see #getDebugFlags
+ * @see #setDebugFlags
+ */
+ public final static int DEBUG_LOG_GL_CALLS = 2;
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Standard View constructor. In order to render something, you
+ * must call {@link #setRenderer} to register a renderer.
+ */
+ public GLSurfaceView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ // Install a SurfaceHolder.Callback so we get notified when the
+ // underlying surface is created and destroyed
+ SurfaceHolder holder = getHolder();
+ holder.addCallback(this);
+ holder.setType(SurfaceHolder.SURFACE_TYPE_GPU);
+
+ }
+
+ /**
+ * Set the glWrapper. If the glWrapper is not null, its
+ * {@link GLWrapper#wrap(GL)} method is called
+ * whenever a surface is created. A GLWrapper can be used to wrap
+ * the GL object that's passed to the renderer. Wrapping a GL
+ * object enables examining and modifying the behavior of the
+ * GL calls made by the renderer.
+ * <p>
+ * Wrapping is typically used for debugging purposes.
+ * <p>
+ * The default value is null.
+ * @param glWrapper the new GLWrapper
+ */
+ public void setGLWrapper(GLWrapper glWrapper) {
+ mGLWrapper = glWrapper;
+ }
+
+ /**
+ * Set the debug flags to a new value. The value is
+ * constructed by OR-together zero or more
+ * of the DEBUG_CHECK_* constants. The debug flags take effect
+ * whenever a surface is created. The default value is zero.
+ * @param debugFlags the new debug flags
+ * @see #DEBUG_CHECK_GL_ERROR
+ * @see #DEBUG_LOG_GL_CALLS
+ */
+ public void setDebugFlags(int debugFlags) {
+ mDebugFlags = debugFlags;
+ }
+
+ /**
+ * Get the current value of the debug flags.
+ * @return the current value of the debug flags.
+ */
+ public int getDebugFlags() {
+ return mDebugFlags;
+ }
+
+ /**
+ * Set the renderer associated with this view. Also starts the thread that
+ * will call the renderer, which in turn causes the rendering to start.
+ * <p>This method should be called once and only once in the life-cycle of
+ * a GLSurfaceView.
+ * <p>The following GLSurfaceView methods can only be called <em>before</em>
+ * setRenderer is called:
+ * <ul>
+ * <li>{@link #setEGLConfigChooser(boolean)}
+ * <li>{@link #setEGLConfigChooser(EGLConfigChooser)}
+ * <li>{@link #setEGLConfigChooser(int, int, int, int, int, int)}
+ * </ul>
+ * <p>
+ * The following GLSurfaceView methods can only be called <em>after</em>
+ * setRenderer is called:
+ * <ul>
+ * <li>{@link #getRenderMode()}
+ * <li>{@link #onPause()}
+ * <li>{@link #onResume()}
+ * <li>{@link #queueEvent(Runnable)}
+ * <li>{@link #requestRender()}
+ * <li>{@link #setRenderMode(int)}
+ * </ul>
+ *
+ * @param renderer the renderer to use to perform OpenGL drawing.
+ */
+ public void setRenderer(Renderer renderer) {
+ checkRenderThreadState();
+ if (mEGLConfigChooser == null) {
+ mEGLConfigChooser = new SimpleEGLConfigChooser(true);
+ }
+ if (mEGLContextFactory == null) {
+ mEGLContextFactory = new DefaultContextFactory();
+ }
+ if (mEGLWindowSurfaceFactory == null) {
+ mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory();
+ }
+ mGLThread = new GLThread(renderer);
+ mGLThread.start();
+ }
+
+ /**
+ * Install a custom EGLContextFactory.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If this method is not called, then by default
+ * a context will be created with no shared context and
+ * with a null attribute list.
+ */
+ public void setEGLContextFactory(EGLContextFactory factory) {
+ checkRenderThreadState();
+ mEGLContextFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLWindowSurfaceFactory.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If this method is not called, then by default
+ * a window surface will be created with a null attribute list.
+ */
+ public void setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory factory) {
+ checkRenderThreadState();
+ mEGLWindowSurfaceFactory = factory;
+ }
+
+ /**
+ * Install a custom EGLConfigChooser.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ * @param configChooser
+ */
+ public void setEGLConfigChooser(EGLConfigChooser configChooser) {
+ checkRenderThreadState();
+ mEGLConfigChooser = configChooser;
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * as close to 16-bit RGB as possible, with or without an optional depth
+ * buffer as close to 16-bits as possible.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ *
+ * @param needDepth
+ */
+ public void setEGLConfigChooser(boolean needDepth) {
+ setEGLConfigChooser(new SimpleEGLConfigChooser(needDepth));
+ }
+
+ /**
+ * Install a config chooser which will choose a config
+ * with at least the specified component sizes, and as close
+ * to the specified component sizes as possible.
+ * <p>If this method is
+ * called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>
+ * If no setEGLConfigChooser method is called, then by default the
+ * view will choose a config as close to 16-bit RGB as possible, with
+ * a depth buffer as close to 16 bits as possible.
+ *
+ */
+ public void setEGLConfigChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ setEGLConfigChooser(new ComponentSizeChooser(redSize, greenSize,
+ blueSize, alphaSize, depthSize, stencilSize));
+ }
+
+ /**
+ * Inform the default EGLContextFactory and default EGLConfigChooser
+ * which EGLContext client version to pick.
+ * <p>Use this method to create an OpenGL ES 2.0-compatible context.
+ * Example:
+ * <pre class="prettyprint">
+ * public MyView(Context context) {
+ * super(context);
+ * setEGLContextClientVersion(2); // Pick an OpenGL ES 2.0 context.
+ * setRenderer(new MyRenderer());
+ * }
+ * </pre>
+ * <p>Note: Activities which require OpenGL ES 2.0 should indicate this by
+ * setting @lt;uses-feature android:glEsVersion="0x00020000" /> in the activity's
+ * AndroidManifest.xml file.
+ * <p>If this method is called, it must be called before {@link #setRenderer(Renderer)}
+ * is called.
+ * <p>This method only affects the behavior of the default EGLContexFactory and the
+ * default EGLConfigChooser. If
+ * {@link #setEGLContextFactory(EGLContextFactory)} has been called, then the supplied
+ * EGLContextFactory is responsible for creating an OpenGL ES 2.0-compatible context.
+ * If
+ * {@link #setEGLConfigChooser(EGLConfigChooser)} has been called, then the supplied
+ * EGLConfigChooser is responsible for choosing an OpenGL ES 2.0-compatible config.
+ * @param version The EGLContext client version to choose. Use 2 for OpenGL ES 2.0
+ */
+ public void setEGLContextClientVersion(int version) {
+ checkRenderThreadState();
+ mEGLContextClientVersion = version;
+ }
+
+ /**
+ * Set the rendering mode. When renderMode is
+ * RENDERMODE_CONTINUOUSLY, the renderer is called
+ * repeatedly to re-render the scene. When renderMode
+ * is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
+ * is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
+ * <p>
+ * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance
+ * by allowing the GPU and CPU to idle when the view does not need to be updated.
+ * <p>
+ * This method can only be called after {@link #setRenderer(Renderer)}
+ *
+ * @param renderMode one of the RENDERMODE_X constants
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public void setRenderMode(int renderMode) {
+ mGLThread.setRenderMode(renderMode);
+ }
+
+ /**
+ * Get the current rendering mode. May be called
+ * from any thread. Must not be called before a renderer has been set.
+ * @return the current rendering mode.
+ * @see #RENDERMODE_CONTINUOUSLY
+ * @see #RENDERMODE_WHEN_DIRTY
+ */
+ public int getRenderMode() {
+ return mGLThread.getRenderMode();
+ }
+
+ /**
+ * Request that the renderer render a frame.
+ * This method is typically used when the render mode has been set to
+ * {@link #RENDERMODE_WHEN_DIRTY}, so that frames are only rendered on demand.
+ * May be called
+ * from any thread. Must not be called before a renderer has been set.
+ */
+ public void requestRender() {
+ mGLThread.requestRender();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceCreated(SurfaceHolder holder) {
+ mGLThread.surfaceCreated();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ // Surface will be destroyed when we return
+ mGLThread.surfaceDestroyed();
+ }
+
+ /**
+ * This method is part of the SurfaceHolder.Callback interface, and is
+ * not normally called or subclassed by clients of GLSurfaceView.
+ */
+ public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
+ mGLThread.onWindowResize(w, h);
+ }
+
+ /**
+ * Inform the view that the activity is paused. The owner of this view must
+ * call this method when the activity is paused. Calling this method will
+ * pause the rendering thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onPause() {
+ mGLThread.onPause();
+ }
+
+ /**
+ * Inform the view that the activity is resumed. The owner of this view must
+ * call this method when the activity is resumed. Calling this method will
+ * recreate the OpenGL display and resume the rendering
+ * thread.
+ * Must not be called before a renderer has been set.
+ */
+ public void onResume() {
+ mGLThread.onResume();
+ }
+
+ public void flushTextures(TextureLibrary library) {
+ mGLThread.flushTextures(library);
+ }
+
+ public void loadTextures(TextureLibrary library) {
+ mGLThread.loadTextures(library);
+ }
+
+ public void flushBuffers(BufferLibrary library) {
+ mGLThread.flushBuffers(library);
+ }
+
+ public void loadBuffers(BufferLibrary library) {
+ mGLThread.loadBuffers(library);
+ }
+
+ /**
+ * Queue a runnable to be run on the GL rendering thread. This can be used
+ * to communicate with the Renderer on the rendering thread.
+ * Must not be called before a renderer has been set.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ mGLThread.queueEvent(r);
+ }
+
+ /**
+ * Inform the view that the window focus has changed.
+ */
+ @Override
+ public void onWindowFocusChanged(boolean hasFocus) {
+ super.onWindowFocusChanged(hasFocus);
+ mGLThread.onWindowFocusChanged(hasFocus);
+ }
+
+ /**
+ * This method is used as part of the View class and is not normally
+ * called or subclassed by clients of GLSurfaceView.
+ * Must not be called before a renderer has been set.
+ */
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mGLThread.requestExitAndWait();
+ }
+
+ // ----------------------------------------------------------------------
+
+ /**
+ * An interface used to wrap a GL interface.
+ * <p>Typically
+ * used for implementing debugging and tracing on top of the default
+ * GL interface. You would typically use this by creating your own class
+ * that implemented all the GL methods by delegating to another GL instance.
+ * Then you could add your own behavior before or after calling the
+ * delegate. All the GLWrapper would do was instantiate and return the
+ * wrapper GL instance:
+ * <pre class="prettyprint">
+ * class MyGLWrapper implements GLWrapper {
+ * GL wrap(GL gl) {
+ * return new MyGLImplementation(gl);
+ * }
+ * static class MyGLImplementation implements GL,GL10,GL11,... {
+ * ...
+ * }
+ * }
+ * </pre>
+ * @see #setGLWrapper(GLWrapper)
+ */
+ public interface GLWrapper {
+ /**
+ * Wraps a gl interface in another gl interface.
+ * @param gl a GL interface that is to be wrapped.
+ * @return either the input argument or another GL object that wraps the input argument.
+ */
+ GL wrap(GL gl);
+ }
+
+ /**
+ * A generic renderer interface.
+ * <p>
+ * The renderer is responsible for making OpenGL calls to render a frame.
+ * <p>
+ * GLSurfaceView clients typically create their own classes that implement
+ * this interface, and then call {@link GLSurfaceView#setRenderer} to
+ * register the renderer with the GLSurfaceView.
+ * <p>
+ * <h3>Threading</h3>
+ * The renderer will be called on a separate thread, so that rendering
+ * performance is decoupled from the UI thread. Clients typically need to
+ * communicate with the renderer from the UI thread, because that's where
+ * input events are received. Clients can communicate using any of the
+ * standard Java techniques for cross-thread communication, or they can
+ * use the {@link GLSurfaceView#queueEvent(Runnable)} convenience method.
+ * <p>
+ * <h3>EGL Context Lost</h3>
+ * There are situations where the EGL rendering context will be lost. This
+ * typically happens when device wakes up after going to sleep. When
+ * the EGL context is lost, all OpenGL resources (such as textures) that are
+ * associated with that context will be automatically deleted. In order to
+ * keep rendering correctly, a renderer must recreate any lost resources
+ * that it still needs. The {@link #onSurfaceCreated(GL10, EGLConfig)} method
+ * is a convenient place to do this.
+ *
+ *
+ * @see #setRenderer(Renderer)
+ */
+ public interface Renderer {
+ /**
+ * Called when the surface is created or recreated.
+ * <p>
+ * Called when the rendering thread
+ * starts and whenever the EGL context is lost. The context will typically
+ * be lost when the Android device awakes after going to sleep.
+ * <p>
+ * Since this method is called at the beginning of rendering, as well as
+ * every time the EGL context is lost, this method is a convenient place to put
+ * code to create resources that need to be created when the rendering
+ * starts, and that need to be recreated when the EGL context is lost.
+ * Textures are an example of a resource that you might want to create
+ * here.
+ * <p>
+ * Note that when the EGL context is lost, all OpenGL resources associated
+ * with that context will be automatically deleted. You do not need to call
+ * the corresponding "glDelete" methods such as glDeleteTextures to
+ * manually delete these lost resources.
+ * <p>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param config the EGLConfig of the created surface. Can be used
+ * to create matching pbuffers.
+ */
+ void onSurfaceCreated(GL10 gl, EGLConfig config);
+
+ /**
+ * Called when the surface changed size.
+ * <p>
+ * Called after the surface is created and whenever
+ * the OpenGL ES surface size changes.
+ * <p>
+ * Typically you will set your viewport here. If your camera
+ * is fixed then you could also set your projection matrix here:
+ * <pre class="prettyprint">
+ * void onSurfaceChanged(GL10 gl, int width, int height) {
+ * gl.glViewport(0, 0, width, height);
+ * // for a fixed camera, set the projection too
+ * float ratio = (float) width / height;
+ * gl.glMatrixMode(GL10.GL_PROJECTION);
+ * gl.glLoadIdentity();
+ * gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ * }
+ * </pre>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ * @param width
+ * @param height
+ */
+ void onSurfaceChanged(GL10 gl, int width, int height);
+
+ /**
+ * Called when the OpenGL context has been lost is about
+ * to be recreated. onSurfaceCreated() will be called after
+ * onSurfaceLost().
+ * */
+ void onSurfaceLost();
+
+ /**
+ * Called to draw the current frame.
+ * <p>
+ * This method is responsible for drawing the current frame.
+ * <p>
+ * The implementation of this method typically looks like this:
+ * <pre class="prettyprint">
+ * void onDrawFrame(GL10 gl) {
+ * gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ * //... other gl calls to render the scene ...
+ * }
+ * </pre>
+ * @param gl the GL interface. Use <code>instanceof</code> to
+ * test if the interface supports GL11 or higher interfaces.
+ */
+ void onDrawFrame(GL10 gl);
+
+ void loadTextures(GL10 gl, TextureLibrary library);
+ void flushTextures(GL10 gl, TextureLibrary library);
+ void loadBuffers(GL10 gl, BufferLibrary library);
+ void flushBuffers(GL10 gl, BufferLibrary library);
+
+
+
+ }
+
+ /**
+ * An interface for customizing the eglCreateContext and eglDestroyContext calls.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLContextFactory(EGLContextFactory)}
+ */
+ public interface EGLContextFactory {
+ EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig);
+ void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context);
+ }
+
+ private class DefaultContextFactory implements EGLContextFactory {
+ private int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
+
+ public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig config) {
+ int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, mEGLContextClientVersion,
+ EGL10.EGL_NONE };
+
+ return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT,
+ mEGLContextClientVersion != 0 ? attrib_list : null);
+ }
+
+ public void destroyContext(EGL10 egl, EGLDisplay display,
+ EGLContext context) {
+ egl.eglDestroyContext(display, context);
+ }
+ }
+
+ /**
+ * An interface for customizing the eglCreateWindowSurface and eglDestroySurface calls.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLWindowSurfaceFactory(EGLWindowSurfaceFactory)}
+ */
+ public interface EGLWindowSurfaceFactory {
+ EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display, EGLConfig config,
+ Object nativeWindow);
+ void destroySurface(EGL10 egl, EGLDisplay display, EGLSurface surface);
+ }
+
+ private static class DefaultWindowSurfaceFactory implements EGLWindowSurfaceFactory {
+
+ public EGLSurface createWindowSurface(EGL10 egl, EGLDisplay display,
+ EGLConfig config, Object nativeWindow) {
+ return egl.eglCreateWindowSurface(display, config, nativeWindow, null);
+ }
+
+ public void destroySurface(EGL10 egl, EGLDisplay display,
+ EGLSurface surface) {
+ egl.eglDestroySurface(display, surface);
+ }
+ }
+
+ /**
+ * An interface for choosing an EGLConfig configuration from a list of
+ * potential configurations.
+ * <p>
+ * This interface must be implemented by clients wishing to call
+ * {@link GLSurfaceView#setEGLConfigChooser(EGLConfigChooser)}
+ */
+ public interface EGLConfigChooser {
+ /**
+ * Choose a configuration from the list. Implementors typically
+ * implement this method by calling
+ * {@link EGL10#eglChooseConfig} and iterating through the results. Please consult the
+ * EGL specification available from The Khronos Group to learn how to call eglChooseConfig.
+ * @param egl the EGL10 for the current display.
+ * @param display the current display.
+ * @return the chosen configuration.
+ */
+ EGLConfig chooseConfig(EGL10 egl, EGLDisplay display);
+ }
+
+ private abstract class BaseConfigChooser
+ implements EGLConfigChooser {
+ public BaseConfigChooser(int[] configSpec) {
+ mConfigSpec = filterConfigSpec(configSpec);
+ }
+
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
+ int[] num_config = new int[1];
+ if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig failed");
+ }
+
+ int numConfigs = num_config[0];
+
+ if (numConfigs <= 0) {
+ throw new IllegalArgumentException(
+ "No configs match configSpec");
+ }
+
+ EGLConfig[] configs = new EGLConfig[numConfigs];
+ if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
+ num_config)) {
+ throw new IllegalArgumentException("eglChooseConfig#2 failed");
+ }
+ EGLConfig config = chooseConfig(egl, display, configs);
+ if (config == null) {
+ throw new IllegalArgumentException("No config chosen");
+ }
+ return config;
+ }
+
+ abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs);
+
+ protected int[] mConfigSpec;
+
+ private int[] filterConfigSpec(int[] configSpec) {
+ if (mEGLContextClientVersion != 2) {
+ return configSpec;
+ }
+ /* We know none of the subclasses define EGL_RENDERABLE_TYPE.
+ * And we know the configSpec is well formed.
+ */
+ int len = configSpec.length;
+ int[] newConfigSpec = new int[len + 2];
+ System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
+ newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
+ newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
+ newConfigSpec[len+1] = EGL10.EGL_NONE;
+ return newConfigSpec;
+ }
+ }
+
+ private class ComponentSizeChooser extends BaseConfigChooser {
+ public ComponentSizeChooser(int redSize, int greenSize, int blueSize,
+ int alphaSize, int depthSize, int stencilSize) {
+ super(new int[] {
+ EGL10.EGL_RED_SIZE, redSize,
+ EGL10.EGL_GREEN_SIZE, greenSize,
+ EGL10.EGL_BLUE_SIZE, blueSize,
+ EGL10.EGL_ALPHA_SIZE, alphaSize,
+ EGL10.EGL_DEPTH_SIZE, depthSize,
+ EGL10.EGL_STENCIL_SIZE, stencilSize,
+ EGL10.EGL_NONE});
+ mValue = new int[1];
+ mRedSize = redSize;
+ mGreenSize = greenSize;
+ mBlueSize = blueSize;
+ mAlphaSize = alphaSize;
+ mDepthSize = depthSize;
+ mStencilSize = stencilSize;
+ }
+
+ @Override
+ public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
+ EGLConfig[] configs) {
+ EGLConfig closestConfig = null;
+ int closestDistance = 1000;
+ for(EGLConfig config : configs) {
+ int d = findConfigAttrib(egl, display, config,
+ EGL10.EGL_DEPTH_SIZE, 0);
+ int s = findConfigAttrib(egl, display, config,
+ EGL10.EGL_STENCIL_SIZE, 0);
+ if (d >= mDepthSize && s>= mStencilSize) {
+ int r = findConfigAttrib(egl, display, config,
+ EGL10.EGL_RED_SIZE, 0);
+ int g = findConfigAttrib(egl, display, config,
+ EGL10.EGL_GREEN_SIZE, 0);
+ int b = findConfigAttrib(egl, display, config,
+ EGL10.EGL_BLUE_SIZE, 0);
+ int a = findConfigAttrib(egl, display, config,
+ EGL10.EGL_ALPHA_SIZE, 0);
+ int distance = Math.abs(r - mRedSize)
+ + Math.abs(g - mGreenSize)
+ + Math.abs(b - mBlueSize)
+ + Math.abs(a - mAlphaSize);
+ if (distance < closestDistance) {
+ closestDistance = distance;
+ closestConfig = config;
+ }
+ }
+ }
+ return closestConfig;
+ }
+
+ private int findConfigAttrib(EGL10 egl, EGLDisplay display,
+ EGLConfig config, int attribute, int defaultValue) {
+
+ if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
+ return mValue[0];
+ }
+ return defaultValue;
+ }
+
+ private int[] mValue;
+ // Subclasses can adjust these values:
+ protected int mRedSize;
+ protected int mGreenSize;
+ protected int mBlueSize;
+ protected int mAlphaSize;
+ protected int mDepthSize;
+ protected int mStencilSize;
+ }
+
+ /**
+ * This class will choose a supported surface as close to
+ * RGB565 as possible, with or without a depth buffer.
+ *
+ */
+ private class SimpleEGLConfigChooser extends ComponentSizeChooser {
+ public SimpleEGLConfigChooser(boolean withDepthBuffer) {
+ super(4, 4, 4, 0, withDepthBuffer ? 16 : 0, 0);
+ // Adjust target values. This way we'll accept a 4444 or
+ // 555 buffer if there's no 565 buffer available.
+ mRedSize = 5;
+ mGreenSize = 6;
+ mBlueSize = 5;
+ }
+ }
+
+ /**
+ * An EGL helper class.
+ */
+
+ private class EglHelper {
+ public EglHelper() {
+
+ }
+
+ /**
+ * Initialize EGL for a given configuration spec.
+ * @param configSpec
+ */
+ public void start(){
+ /*
+ * Get an EGL instance
+ */
+ mEgl = (EGL10) EGLContext.getEGL();
+
+ /*
+ * Get to the default display.
+ */
+ mEglDisplay = mEgl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+
+ if (mEglDisplay == EGL10.EGL_NO_DISPLAY) {
+ throw new RuntimeException("eglGetDisplay failed");
+ }
+
+ /*
+ * We can now initialize EGL for that display
+ */
+ int[] version = new int[2];
+ if(!mEgl.eglInitialize(mEglDisplay, version)) {
+ throw new RuntimeException("eglInitialize failed");
+ }
+ mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay);
+
+ /*
+ * Create an OpenGL ES context. This must be done only once, an
+ * OpenGL context is a somewhat heavy object.
+ */
+ mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig);
+ if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) {
+ throwEglException("createContext");
+ }
+
+ mEglSurface = null;
+ }
+
+ /*
+ * React to the creation of a new surface by creating and returning an
+ * OpenGL interface that renders to that surface.
+ */
+ public GL createSurface(SurfaceHolder holder) {
+ /*
+ * The window size has changed, so we need to create a new
+ * surface.
+ */
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+
+ /*
+ * Unbind and destroy the old EGL surface, if
+ * there is one.
+ */
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ }
+
+ /*
+ * Create an EGL surface we can render into.
+ */
+ mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl,
+ mEglDisplay, mEglConfig, holder);
+
+ if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) {
+ throwEglException("createWindowSurface");
+ }
+
+ /*
+ * Before we can issue GL commands, we need to make sure
+ * the context is current and bound to a surface.
+ */
+ if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
+ throwEglException("eglMakeCurrent");
+ }
+
+ GL gl = mEglContext.getGL();
+ if (mGLWrapper != null) {
+ gl = mGLWrapper.wrap(gl);
+ }
+
+ if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) {
+ int configFlags = 0;
+ Writer log = null;
+ if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) {
+ configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR;
+ }
+ if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) {
+ log = new LogWriter();
+ }
+ gl = GLDebugHelper.wrap(gl, configFlags, log);
+ }
+ return gl;
+ }
+
+ /**
+ * Display the current render surface.
+ * @return false if the context has been lost.
+ */
+ public boolean swap() {
+ mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+
+ /*
+ * Always check for EGL_CONTEXT_LOST, which means the context
+ * and all associated data were lost (For instance because
+ * the device went to sleep). We need to sleep until we
+ * get a new surface.
+ */
+ return mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
+ }
+
+ public void destroySurface() {
+ if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) {
+ mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_SURFACE,
+ EGL10.EGL_NO_CONTEXT);
+ mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface);
+ mEglSurface = null;
+ }
+ }
+
+ public void finish() {
+ if (mEglContext != null) {
+ mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext);
+ mEglContext = null;
+ }
+ if (mEglDisplay != null) {
+ mEgl.eglTerminate(mEglDisplay);
+ mEglDisplay = null;
+ }
+ }
+
+ private void throwEglException(String function) {
+ throw new RuntimeException(function + " failed: " + mEgl.eglGetError());
+ }
+
+ /** Checks to see if the current context is valid. **/
+ public boolean verifyContext() {
+ EGLContext currentContext = mEgl.eglGetCurrentContext();
+ return currentContext != EGL10.EGL_NO_CONTEXT && mEgl.eglGetError() != EGL11.EGL_CONTEXT_LOST;
+ }
+
+ EGL10 mEgl;
+ EGLDisplay mEglDisplay;
+ EGLSurface mEglSurface;
+ EGLConfig mEglConfig;
+ EGLContext mEglContext;
+
+ }
+
+ /**
+ * A generic GL Thread. Takes care of initializing EGL and GL. Delegates
+ * to a Renderer instance to do the actual drawing. Can be configured to
+ * render continuously or on request.
+ *
+ * All potentially blocking synchronization is done through the
+ * sGLThreadManager object. This avoids multiple-lock ordering issues.
+ *
+ */
+ private class GLThread extends Thread {
+ public GLThread(Renderer renderer) {
+ super();
+ mWidth = 0;
+ mHeight = 0;
+ mRequestRender = true;
+ mRenderMode = RENDERMODE_CONTINUOUSLY;
+ mRenderer = renderer;
+ }
+
+
+ @Override
+ public void run() {
+ setName("GLThread " + getId());
+ if (LOG_THREADS) {
+ DebugLog.i("GLThread", "starting tid=" + getId());
+ }
+
+ try {
+ guardedRun();
+ } catch (InterruptedException e) {
+ // fall thru and exit normally
+ } finally {
+ sGLThreadManager.threadExiting(this);
+ }
+ }
+
+ /*
+ * This private method should only be called inside a
+ * synchronized(sGLThreadManager) block.
+ */
+ private void stopEglLocked() {
+ if (mHaveEglSurface) {
+ mHaveEglSurface = false;
+ mEglHelper.destroySurface();
+ sGLThreadManager.releaseEglSurfaceLocked(this);
+ }
+ }
+
+ private void guardedRun() throws InterruptedException {
+ mEglHelper = new EglHelper();
+ mHaveEglContext = false;
+ mHaveEglSurface = false;
+ try {
+ GL10 gl = null;
+ boolean createEglSurface = false;
+ boolean sizeChanged = false;
+ boolean wantRenderNotification = false;
+ boolean doRenderNotification = false;
+ int w = 0;
+ int h = 0;
+ Runnable event = null;
+
+ while (true) {
+ synchronized (sGLThreadManager) {
+ while (true) {
+ if (mShouldExit) {
+ return;
+ }
+
+ if (! mEventQueue.isEmpty()) {
+ event = mEventQueue.remove(0);
+ break;
+ }
+
+ // Do we need to release the EGL surface?
+ if (mHaveEglSurface && mPaused) {
+ if (LOG_SURFACE) {
+ DebugLog.i("GLThread", "releasing EGL surface because paused tid=" + getId());
+ }
+ stopEglLocked();
+ }
+
+ // Have we lost the surface view surface?
+ if ((! mHasSurface) && (! mWaitingForSurface)) {
+ if (LOG_SURFACE) {
+ DebugLog.i("GLThread", "noticed surfaceView surface lost tid=" + getId());
+ }
+ if (mHaveEglSurface) {
+ stopEglLocked();
+ }
+ mWaitingForSurface = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ // Have we acquired the surface view surface?
+ if (mHasSurface && mWaitingForSurface) {
+ if (LOG_SURFACE) {
+ DebugLog.i("GLThread", "noticed surfaceView surface acquired tid=" + getId());
+ }
+ mWaitingForSurface = false;
+ sGLThreadManager.notifyAll();
+ }
+
+ if (doRenderNotification) {
+ wantRenderNotification = false;
+ doRenderNotification = false;
+ mRenderComplete = true;
+ sGLThreadManager.notifyAll();
+ }
+
+ // Ready to draw?
+ if ((!mPaused) && mHasSurface
+ && (mWidth > 0) && (mHeight > 0)
+ && (mRequestRender || (mRenderMode == RENDERMODE_CONTINUOUSLY))) {
+
+ if (mHaveEglContext && !mHaveEglSurface) {
+ // Let's make sure the context hasn't been lost.
+ if (!mEglHelper.verifyContext()) {
+ mEglHelper.finish();
+ mRenderer.onSurfaceLost();
+ mHaveEglContext = false;
+ }
+ }
+ // If we don't have an egl surface, try to acquire one.
+ if ((! mHaveEglContext) && sGLThreadManager.tryAcquireEglSurfaceLocked(this)) {
+ mHaveEglContext = true;
+ mEglHelper.start();
+
+ sGLThreadManager.notifyAll();
+ }
+
+ if (mHaveEglContext && !mHaveEglSurface) {
+ mHaveEglSurface = true;
+ createEglSurface = true;
+ sizeChanged = true;
+ }
+
+ if (mHaveEglSurface) {
+ if (mSizeChanged) {
+ sizeChanged = true;
+ w = mWidth;
+ h = mHeight;
+ wantRenderNotification = true;
+
+ if (DRAW_TWICE_AFTER_SIZE_CHANGED) {
+ // We keep mRequestRender true so that we draw twice after the size changes.
+ // (Once because of mSizeChanged, the second time because of mRequestRender.)
+ // This forces the updated graphics onto the screen.
+ } else {
+ mRequestRender = false;
+ }
+ mSizeChanged = false;
+ } else {
+ mRequestRender = false;
+ }
+ sGLThreadManager.notifyAll();
+ break;
+ }
+ }
+
+ // By design, this is the only place in a GLThread thread where we wait().
+ if (LOG_THREADS) {
+ DebugLog.i("GLThread", "waiting tid=" + getId());
+ }
+ sGLThreadManager.wait();
+ }
+ } // end of synchronized(sGLThreadManager)
+
+ if (event != null) {
+ event.run();
+ event = null;
+ continue;
+ }
+
+ if (mHasFocus) {
+ if (createEglSurface) {
+ gl = (GL10) mEglHelper.createSurface(getHolder());
+ sGLThreadManager.checkGLDriver(gl);
+ if (LOG_RENDERER) {
+ DebugLog.w("GLThread", "onSurfaceCreated");
+ }
+ mGL = gl;
+ mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig);
+ createEglSurface = false;
+ }
+
+
+ if (sizeChanged) {
+ if (LOG_RENDERER) {
+ DebugLog.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")");
+ }
+ mRenderer.onSurfaceChanged(gl, w, h);
+ sizeChanged = false;
+ }
+
+
+
+ if (LOG_RENDERER) {
+ DebugLog.w("GLThread", "onDrawFrame");
+ }
+ mRenderer.onDrawFrame(gl);
+ if(!mEglHelper.swap()) {
+ if (LOG_SURFACE) {
+ DebugLog.i("GLThread", "egl surface lost tid=" + getId());
+ }
+ }
+
+ }
+ if (wantRenderNotification) {
+ doRenderNotification = true;
+ }
+ }
+
+ } finally {
+ mGL = null;
+ /*
+ * clean-up everything...
+ */
+ synchronized (sGLThreadManager) {
+ stopEglLocked();
+ mEglHelper.finish();
+ }
+ }
+ }
+
+ public void setRenderMode(int renderMode) {
+ if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
+ throw new IllegalArgumentException("renderMode");
+ }
+ synchronized(sGLThreadManager) {
+ mRenderMode = renderMode;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public int getRenderMode() {
+ synchronized(sGLThreadManager) {
+ return mRenderMode;
+ }
+ }
+
+ public void requestRender() {
+ synchronized(sGLThreadManager) {
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void surfaceCreated() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ DebugLog.i("GLThread", "surfaceCreated tid=" + getId());
+ }
+ mHasSurface = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void surfaceDestroyed() {
+ synchronized(sGLThreadManager) {
+ if (LOG_THREADS) {
+ DebugLog.i("GLThread", "surfaceDestroyed tid=" + getId());
+ }
+ mHasSurface = false;
+ sGLThreadManager.notifyAll();
+ while((!mWaitingForSurface) && (!mExited)) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void onPause() {
+ synchronized (sGLThreadManager) {
+ mPaused = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void onResume() {
+ synchronized (sGLThreadManager) {
+ mPaused = false;
+ mRequestRender = true;
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ public void onWindowResize(int w, int h) {
+ synchronized (sGLThreadManager) {
+ mWidth = w;
+ mHeight = h;
+ mSizeChanged = true;
+ mRequestRender = true;
+ mRenderComplete = false;
+ sGLThreadManager.notifyAll();
+
+ // Wait for thread to react to resize and render a frame
+ while (! mExited && !mPaused && !mRenderComplete ) {
+ if (LOG_SURFACE) {
+ DebugLog.i("Main thread", "onWindowResize waiting for render complete.");
+ }
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ public void loadTextures(TextureLibrary library) {
+ synchronized (this) {
+ assert mGL != null;
+ if (mGL != null && mHasSurface) {
+ mRenderer.loadTextures(mGL, library);
+ }
+ }
+ }
+
+ public void flushTextures(TextureLibrary library) {
+ synchronized (this) {
+ assert mGL != null;
+ if (mGL != null) {
+ mRenderer.flushTextures(mGL, library);
+ }
+ }
+ }
+
+ public void loadBuffers(BufferLibrary library) {
+ synchronized (this) {
+ assert mGL != null;
+ if (mGL != null) {
+ mRenderer.loadBuffers(mGL, library);
+ }
+ }
+ }
+
+ public void flushBuffers(BufferLibrary library) {
+ synchronized (this) {
+ assert mGL != null;
+ if (mGL != null) {
+ mRenderer.flushBuffers(mGL, library);
+ }
+ }
+ }
+
+ // On some Qualcomm devices (such as the HTC Magic running Android 1.6),
+ // there's a bug in the graphics driver that will cause glViewport() to
+ // do the wrong thing in a very specific situation. When the screen is
+ // rotated, if a surface is created in one layout (say, portrait view)
+ // and then rotated to another, subsequent calls to glViewport are clipped.
+ // So, if the window is, say, 320x480 when the surface is created, and
+ // then the rotation occurs and glViewport() is called with the new
+ // size of 480x320, devices with the buggy driver will clip the viewport
+ // to the old width (which means 320x320...ugh!). This is fixed in
+ // Android 2.1 Qualcomm devices (like Nexus One) and doesn't affect
+ // non-Qualcomm devices (like the Motorola DROID).
+ //
+ // Unfortunately, under Android 1.6 this exact case occurs when the
+ // screen is put to sleep and then wakes up again. The lock screen
+ // comes up in portrait mode, but at the same time the window surface
+ // is also created in the backgrounded game. When the lock screen is closed
+ // and the game comes forward, the window is fixed to the correct size
+ // which causes the bug to occur.
+
+ // The solution used here is to simply never render when the window surface
+ // does not have the focus. When the lock screen (or menu) is up, rendering
+ // will stop. This resolves the driver bug (as the egl surface won't be created
+ // until after the screen size has been fixed), and is generally good practice
+ // since you don't want to be doing a lot of CPU intensive work when the lock
+ // screen is up (to preserve battery life).
+
+ public void onWindowFocusChanged(boolean hasFocus) {
+ synchronized(sGLThreadManager) {
+ mHasFocus = hasFocus;
+ sGLThreadManager.notifyAll();
+ }
+ if (LOG_SURFACE) {
+ DebugLog.i("Main thread", "Focus " + (mHasFocus ? "gained" : "lost"));
+ }
+
+ }
+
+ public void requestExitAndWait() {
+ // don't call this from GLThread thread or it is a guaranteed
+ // deadlock!
+ synchronized(sGLThreadManager) {
+ mShouldExit = true;
+ sGLThreadManager.notifyAll();
+ while (! mExited) {
+ try {
+ sGLThreadManager.wait();
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+ }
+
+ /**
+ * Queue an "event" to be run on the GL rendering thread.
+ * @param r the runnable to be run on the GL rendering thread.
+ */
+ public void queueEvent(Runnable r) {
+ if (r == null) {
+ throw new IllegalArgumentException("r must not be null");
+ }
+ synchronized(sGLThreadManager) {
+ mEventQueue.add(r);
+ sGLThreadManager.notifyAll();
+ }
+ }
+
+ // Once the thread is started, all accesses to the following member
+ // variables are protected by the sGLThreadManager monitor
+ private boolean mShouldExit;
+ private boolean mExited;
+ private boolean mPaused;
+ private boolean mHasSurface;
+ private boolean mWaitingForSurface;
+ private boolean mHaveEglContext;
+ private boolean mHaveEglSurface;
+ private int mWidth;
+ private int mHeight;
+ private int mRenderMode;
+ private boolean mRequestRender;
+ private boolean mRenderComplete;
+ private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>();
+ private GL10 mGL;
+ private boolean mHasFocus;
+
+ // End of member variables protected by the sGLThreadManager monitor.
+
+ private Renderer mRenderer;
+ private EglHelper mEglHelper;
+ }
+
+ static class LogWriter extends Writer {
+
+ @Override public void close() {
+ flushBuilder();
+ }
+
+ @Override public void flush() {
+ flushBuilder();
+ }
+
+ @Override public void write(char[] buf, int offset, int count) {
+ for(int i = 0; i < count; i++) {
+ char c = buf[offset + i];
+ if ( c == '\n') {
+ flushBuilder();
+ }
+ else {
+ mBuilder.append(c);
+ }
+ }
+ }
+
+ private void flushBuilder() {
+ if (mBuilder.length() > 0) {
+ DebugLog.v("GLSurfaceView", mBuilder.toString());
+ mBuilder.delete(0, mBuilder.length());
+ }
+ }
+
+ private StringBuilder mBuilder = new StringBuilder();
+ }
+
+
+ private void checkRenderThreadState() {
+ if (mGLThread != null) {
+ throw new IllegalStateException(
+ "setRenderer has already been called for this instance.");
+ }
+ }
+
+ private static class GLThreadManager {
+
+ public synchronized void threadExiting(GLThread thread) {
+ if (LOG_THREADS) {
+ DebugLog.i("GLThread", "exiting tid=" + thread.getId());
+ }
+ thread.mExited = true;
+ if (mEglOwner == thread) {
+ mEglOwner = null;
+ }
+ notifyAll();
+ }
+
+ /*
+ * Tries once to acquire the right to use an EGL
+ * surface. Does not block. Requires that we are already
+ * in the sGLThreadManager monitor when this is called.
+ *
+ * @return true if the right to use an EGL surface was acquired.
+ */
+ public boolean tryAcquireEglSurfaceLocked(GLThread thread) {
+ if (mEglOwner == thread || mEglOwner == null) {
+ mEglOwner = thread;
+ notifyAll();
+ return true;
+ }
+ checkGLESVersion();
+ if (mMultipleGLESContextsAllowed) {
+ return true;
+ }
+ return false;
+ }
+ /*
+ * Releases the EGL surface. Requires that we are already in the
+ * sGLThreadManager monitor when this is called.
+ */
+ public void releaseEglSurfaceLocked(GLThread thread) {
+ if (mEglOwner == thread) {
+ mEglOwner = null;
+ }
+ notifyAll();
+ }
+
+ public synchronized void checkGLDriver(GL10 gl) {
+ if (! mGLESDriverCheckComplete) {
+ checkGLESVersion();
+ if (mGLESVersion < kGLES_20) {
+ String renderer = gl.glGetString(GL10.GL_RENDERER);
+ mMultipleGLESContextsAllowed = false;
+ notifyAll();
+ }
+ mGLESDriverCheckComplete = true;
+ }
+ }
+
+ private void checkGLESVersion() {
+ if (! mGLESVersionCheckComplete) {
+ mGLESVersion = ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
+ if (mGLESVersion >= kGLES_20) {
+ mMultipleGLESContextsAllowed = true;
+ }
+ mGLESVersionCheckComplete = true;
+ }
+
+ }
+
+ private boolean mGLESVersionCheckComplete;
+ private int mGLESVersion;
+ private boolean mGLESDriverCheckComplete;
+ private boolean mMultipleGLESContextsAllowed;
+ private int mGLContextCount;
+ private static final int kGLES_20 = 0x20000;
+ private GLThread mEglOwner;
+
+ }
+
+ private static final GLThreadManager sGLThreadManager = new GLThreadManager();
+ private boolean mSizeChanged = true;
+
+ private GLThread mGLThread;
+ private EGLConfigChooser mEGLConfigChooser;
+ private EGLContextFactory mEGLContextFactory;
+ private EGLWindowSurfaceFactory mEGLWindowSurfaceFactory;
+ private GLWrapper mGLWrapper;
+ private int mDebugFlags;
+ private int mEGLContextClientVersion;
+
+
+}
diff --git a/src/com/replica/replicaisland/Game.java b/src/com/replica/replicaisland/Game.java
new file mode 100644
index 0000000..a6cbf44
--- /dev/null
+++ b/src/com/replica/replicaisland/Game.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.widget.Toast;
+
+/**
+ * High-level setup object for the AndouKun game engine.
+ * This class sets up the core game engine objects and threads. It also passes events to the
+ * game thread from the main UI thread.
+ */
+public class Game extends AllocationGuard {
+ private GameThread mGameThread;
+ private Thread mGame;
+ private ObjectManager mGameRoot;
+
+ private GameRenderer mRenderer;
+ private GLSurfaceView mSurfaceView;
+ private boolean mRunning;
+ private boolean mBootstrapComplete;
+ private LevelTree.Level mPendingLevel;
+ private LevelTree.Level mCurrentLevel;
+ private LevelTree.Level mLastLevel;
+ private boolean mGLDataLoaded;
+ private ContextParameters mContextParameters;
+
+ public Game() {
+ super();
+ mRunning = false;
+ mBootstrapComplete = false;
+ mGLDataLoaded = false;
+ mContextParameters = new ContextParameters();
+ }
+
+ /**
+ * Creates core game objects and constructs the game engine object graph. Note that the
+ * game does not actually begin running after this function is called (see start() below).
+ * Also note that textures are not loaded from the resource pack by this function, as OpenGl
+ * isn't yet available.
+ * @param context
+ */
+ public void bootstrap(Context context, int viewWidth, int viewHeight, int gameWidth, int gameHeight) {
+ if (!mBootstrapComplete) {
+ mRenderer = new GameRenderer(context, this, gameWidth, gameHeight);
+
+ // Create core systems
+ BaseObject.sSystemRegistry.openGLSystem = new OpenGLSystem(null);
+
+ BaseObject.sSystemRegistry.customToastSystem = new CustomToastSystem(context);
+
+ ContextParameters params = mContextParameters;
+ params.viewWidth = viewWidth;
+ params.viewHeight = viewHeight;
+ params.gameWidth = gameWidth;
+ params.gameHeight = gameHeight;
+ params.viewScaleX = (float)viewWidth / gameWidth;
+ params.viewScaleY = (float)viewHeight / gameHeight;
+ params.context = context;
+ BaseObject.sSystemRegistry.contextParameters = params;
+
+ // Short-term textures are cleared between levels.
+ TextureLibrary shortTermTextureLibrary = new TextureLibrary();
+ BaseObject.sSystemRegistry.shortTermTextureLibrary = shortTermTextureLibrary;
+
+ // Long-term textures persist between levels.
+ TextureLibrary longTermTextureLibrary = new TextureLibrary();
+ BaseObject.sSystemRegistry.longTermTextureLibrary = longTermTextureLibrary;
+
+ // The buffer library manages hardware VBOs.
+ BaseObject.sSystemRegistry.bufferLibrary = new BufferLibrary();
+
+
+
+ BaseObject.sSystemRegistry.soundSystem = new SoundSystem();
+
+ // The root of the game graph.
+ MainLoop gameRoot = new MainLoop();
+
+ InputSystem input = new InputSystem();
+ gameRoot.add(input);
+ BaseObject.sSystemRegistry.inputSystem = input;
+
+
+ LevelSystem level = new LevelSystem();
+ BaseObject.sSystemRegistry.levelSystem = level;
+
+ CollisionSystem collision = new CollisionSystem();
+ BaseObject.sSystemRegistry.collisionSystem = collision;
+ BaseObject.sSystemRegistry.hitPointPool = new HitPointPool();
+
+ GameObjectManager gameManager = new GameObjectManager(params.viewWidth * 2);
+ BaseObject.sSystemRegistry.gameObjectManager = gameManager;
+
+ GameObjectFactory objectFactory = new GameObjectFactory();
+ BaseObject.sSystemRegistry.gameObjectFactory = objectFactory;
+
+ BaseObject.sSystemRegistry.hotSpotSystem = new HotSpotSystem();
+
+ BaseObject.sSystemRegistry.levelBuilder = new LevelBuilder();
+
+ BaseObject.sSystemRegistry.channelSystem = new ChannelSystem();
+ BaseObject.sSystemRegistry.registerForReset(BaseObject.sSystemRegistry.channelSystem);
+
+ CameraSystem camera = new CameraSystem();
+
+
+ BaseObject.sSystemRegistry.cameraSystem = camera;
+ BaseObject.sSystemRegistry.registerForReset(camera);
+
+ collision.loadCollisionTiles(context.getResources().openRawResource(R.raw.collision));
+
+ gameRoot.add(gameManager);
+
+ // Camera must come after the game manager so that the camera target moves before the camera
+ // centers.
+
+ gameRoot.add(camera);
+
+
+ // More basic systems.
+
+ GameObjectCollisionSystem dynamicCollision = new GameObjectCollisionSystem();
+ gameRoot.add(dynamicCollision);
+ BaseObject.sSystemRegistry.gameObjectCollisionSystem = dynamicCollision;
+
+
+ RenderSystem renderer = new RenderSystem();
+ BaseObject.sSystemRegistry.renderSystem = renderer;
+ BaseObject.sSystemRegistry.vectorPool = new VectorPool();
+ BaseObject.sSystemRegistry.drawableFactory = new DrawableFactory();
+
+ HudSystem hud = new HudSystem();
+ hud.setFuelDrawable(
+ new DrawableBitmap(longTermTextureLibrary.allocateTexture(
+ R.drawable.ui_bar), 0, 0),
+ new DrawableBitmap(longTermTextureLibrary.allocateTexture(
+ R.drawable.ui_bar_bg), 0, 0));
+ hud.setFadeTexture(longTermTextureLibrary.allocateTexture(R.drawable.black));
+ hud.setButtonDrawables(
+ new DrawableBitmap(longTermTextureLibrary.allocateTexture(
+ R.drawable.ui_button_fly_disabled), 0, 0),
+ new DrawableBitmap(longTermTextureLibrary.allocateTexture(
+ R.drawable.ui_button_fly_off), 0, 0),
+ new DrawableBitmap(longTermTextureLibrary.allocateTexture(
+ R.drawable.ui_button_fly_on), 0, 0),
+ new DrawableBitmap(longTermTextureLibrary.allocateTexture(
+ R.drawable.ui_button_stomp_off), 0, 0),
+ new DrawableBitmap(longTermTextureLibrary.allocateTexture(
+ R.drawable.ui_button_stomp_on), 0, 0));
+ Texture[] digitTextures = {
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_0),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_1),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_2),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_3),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_4),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_5),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_6),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_7),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_8),
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_9)
+ };
+ DrawableBitmap[] digits = {
+ new DrawableBitmap(digitTextures[0], 0, 0),
+ new DrawableBitmap(digitTextures[1], 0, 0),
+ new DrawableBitmap(digitTextures[2], 0, 0),
+ new DrawableBitmap(digitTextures[3], 0, 0),
+ new DrawableBitmap(digitTextures[4], 0, 0),
+ new DrawableBitmap(digitTextures[5], 0, 0),
+ new DrawableBitmap(digitTextures[6], 0, 0),
+ new DrawableBitmap(digitTextures[7], 0, 0),
+ new DrawableBitmap(digitTextures[8], 0, 0),
+ new DrawableBitmap(digitTextures[9], 0, 0)
+ };
+ DrawableBitmap xDrawable = new DrawableBitmap(
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_x), 0, 0);
+
+ hud.setDigitDrawables(digits, xDrawable);
+ hud.setCollectableDrawables(
+ new DrawableBitmap(
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_pearl), 0, 0),
+ new DrawableBitmap(
+ longTermTextureLibrary.allocateTexture(R.drawable.ui_gem), 0, 0));
+
+ BaseObject.sSystemRegistry.hudSystem = hud;
+ gameRoot.add(hud);
+
+ BaseObject.sSystemRegistry.vibrationSystem = new VibrationSystem();
+
+ BaseObject.sSystemRegistry.eventRecorder = new EventRecorder();
+
+ gameRoot.add(collision);
+
+ // debug systems
+ //BaseObject.sSystemRegistry.debugSystem = new DebugSystem(longTermTextureLibrary);
+ //dynamicCollision.setDebugPrefs(false, true);
+
+
+ objectFactory.preloadEffects();
+
+ mGameRoot = gameRoot;
+
+ mGameThread = new GameThread(mRenderer);
+ mGameThread.setGameRoot(mGameRoot);
+
+
+ mCurrentLevel = null;
+
+ mBootstrapComplete = true;
+ }
+ }
+
+
+ protected synchronized void stopLevel() {
+ stop();
+ GameObjectManager manager = BaseObject.sSystemRegistry.gameObjectManager;
+ manager.destroyAll();
+ manager.commitUpdates();
+
+ //TODO: it's not strictly necessary to clear the static data here, but if I don't do it
+ // then two things happen: first, the static data will refer to junk Texture objects, and
+ // second, memory that may not be needed for the next level will hang around. One solution
+ // would be to break up the texture library into static and non-static things, and
+ // then selectively clear static game components based on their usefulness next level,
+ // but this is way simpler.
+ GameObjectFactory factory = BaseObject.sSystemRegistry.gameObjectFactory;
+ factory.clearStaticData();
+ factory.sanityCheckPools();
+
+ // Reset the level
+ BaseObject.sSystemRegistry.levelSystem.reset();
+
+ // Reset systems that need it.
+ BaseObject.sSystemRegistry.reset();
+
+ // Dump the short-term texture objects only.
+ mSurfaceView.flushTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary);
+ BaseObject.sSystemRegistry.shortTermTextureLibrary.removeAll();
+ mSurfaceView.flushBuffers(BaseObject.sSystemRegistry.bufferLibrary);
+ BaseObject.sSystemRegistry.bufferLibrary.removeAll();
+ }
+
+ public synchronized void requestNewLevel() {
+ // tell the Renderer to call us back when the
+ // render thread is ready to manage some texture memory.
+ mRenderer.requestCallback();
+ }
+
+ public synchronized void restartLevel() {
+ DebugLog.d("AndouKun", "Restarting...");
+ final LevelTree.Level level = mCurrentLevel;
+ stop();
+
+ // Destroy all game objects and respawn them. No need to destroy other systems.
+ GameObjectManager manager = BaseObject.sSystemRegistry.gameObjectManager;
+ manager.destroyAll();
+ manager.commitUpdates();
+
+ // Reset systems that need it.
+ BaseObject.sSystemRegistry.reset();
+
+ LevelSystem levelSystem = BaseObject.sSystemRegistry.levelSystem;
+ levelSystem.incrementAttemptsCount();
+ levelSystem.spawnObjects();
+
+ BaseObject.sSystemRegistry.hudSystem.startFade(true, 0.2f);
+
+ mCurrentLevel = level;
+ mPendingLevel = null;
+ start();
+ }
+
+ protected synchronized void goToLevel(LevelTree.Level level) {
+
+ ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
+ BaseObject.sSystemRegistry.levelSystem.loadLevel(level,
+ params.context.getResources().openRawResource(level.resource), mGameRoot);
+
+ Context context = params.context;
+ mRenderer.setContext(context);
+ mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary);
+ mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary);
+ mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary);
+
+ mGLDataLoaded = true;
+
+
+ mCurrentLevel = level;
+ mPendingLevel = null;
+
+ TimeSystem time = BaseObject.sSystemRegistry.timeSystem;
+ time.reset();
+
+ HudSystem hud = BaseObject.sSystemRegistry.hudSystem;
+ if (hud != null) {
+ hud.startFade(true, 1.0f);
+ }
+
+ CustomToastSystem toast = BaseObject.sSystemRegistry.customToastSystem;
+ if (toast != null) {
+ if (level.inThePast) {
+ toast.toast(context.getString(R.string.memory_playback_start), Toast.LENGTH_LONG);
+ } else {
+ if (mLastLevel != null && mLastLevel.inThePast) {
+ toast.toast(context.getString(R.string.memory_playback_complete), Toast.LENGTH_LONG);
+ }
+ }
+ }
+
+ mLastLevel = level;
+
+ start();
+ }
+
+ /** Starts the game running. */
+ public void start() {
+ if (!mRunning) {
+ assert mGame == null;
+ // Now's a good time to run the GC.
+ Runtime r = Runtime.getRuntime();
+ r.gc();
+ DebugLog.d("AndouKun", "Start!");
+ mGame = new Thread(mGameThread);
+ mGame.setName("Game");
+ mGame.start();
+ mRunning = true;
+ AllocationGuard.sGuardActive = false;
+ } else {
+ mGameThread.resumeGame();
+ }
+ }
+
+ public void stop() {
+ if (mRunning) {
+ DebugLog.d("AndouKun", "Stop!");
+ if (mGameThread.getPaused()) {
+ mGameThread.resumeGame();
+ }
+ mGameThread.stopGame();
+ try {
+ mGame.join();
+ } catch (InterruptedException e) {
+ mGame.interrupt();
+ }
+ mGame = null;
+ mRunning = false;
+ mCurrentLevel = null;
+ AllocationGuard.sGuardActive = false;
+ }
+ }
+
+ public boolean onTrackballEvent(MotionEvent event) {
+ if (mRunning) {
+ mGameThread.rollEvent(event.getRawX(), event.getRawY());
+ boolean clickDown = event.getAction() == MotionEvent.ACTION_DOWN;
+ if ((clickDown && event.getPressure() > 0)
+ || event.getAction() == MotionEvent.ACTION_UP){
+ mGameThread.clickEvent(clickDown);
+ }
+ }
+ return true;
+ }
+
+ public boolean onOrientationEvent(float x, float y, float z) {
+ if (mRunning) {
+ mGameThread.orientationEvent(x, y, z);
+ }
+ return true;
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ if (mRunning) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ mGameThread.touchUpEvent(event.getRawX() * (1.0f / mContextParameters.viewScaleX),
+ event.getRawY() * (1.0f / mContextParameters.viewScaleY));
+ } else {
+ mGameThread.touchDownEvent(event.getRawX() * (1.0f / mContextParameters.viewScaleX),
+ event.getRawY() * (1.0f / mContextParameters.viewScaleY));
+ }
+
+ }
+ return true;
+ }
+
+ public boolean onKeyDownEvent(int keyCode) {
+ boolean result = false;
+ if (mRunning) {
+ result = mGameThread.keydownEvent(keyCode);
+ }
+ return result;
+ }
+
+ public boolean onKeyUpEvent(int keyCode) {
+ boolean result = false;
+ if (mRunning) {
+ result = mGameThread.keyupEvent(keyCode);
+ }
+ return result;
+ }
+
+ public GameRenderer getRenderer() {
+ return mRenderer;
+ }
+
+ public void onPause() {
+ if (mRunning) {
+ mGameThread.pauseGame();
+ }
+ }
+
+ public void onResume(Context context, boolean force) {
+ if (force && mRunning) {
+ mGameThread.resumeGame();
+ } else {
+ mRenderer.setContext(context);
+ // Don't explicitly resume the game here. We'll do that in
+ // the SurfaceReady() callback, which will prevent the game
+ // starting before the render thread is ready to go.
+ BaseObject.sSystemRegistry.contextParameters.context = context;
+ }
+ }
+
+ public void onSurfaceReady() {
+ DebugLog.d("AndouKun", "Surface Ready");
+
+ if (mPendingLevel != null && mPendingLevel != mCurrentLevel) {
+ if (mRunning) {
+ stopLevel();
+ }
+ goToLevel(mPendingLevel);
+ } else if (mGameThread.getPaused() && mRunning) {
+ mGameThread.resumeGame();
+ }
+ }
+
+ public void setSurfaceView(GLSurfaceView view) {
+ mSurfaceView = view;
+ }
+
+ public void onSurfaceLost() {
+ DebugLog.d("AndouKun", "Surface Lost");
+
+ BaseObject.sSystemRegistry.shortTermTextureLibrary.invalidateAll();
+ BaseObject.sSystemRegistry.longTermTextureLibrary.invalidateAll();
+ BaseObject.sSystemRegistry.bufferLibrary.invalidateHardwareBuffers();
+
+ mGLDataLoaded = false;
+ }
+
+ public void onSurfaceCreated() {
+ DebugLog.d("AndouKun", "Surface Created");
+
+ // TODO: this is dumb. SurfaceView doesn't need to control everything here.
+ // GL should just be passed to this function and then set up directly.
+
+ if (!mGLDataLoaded && mGameThread.getPaused() && mRunning && mPendingLevel == null) {
+
+ mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary);
+ mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary);
+ mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary);
+ mGLDataLoaded = true;
+ }
+ }
+
+ public void setPendingLevel(LevelTree.Level level) {
+ mPendingLevel = level;
+ }
+
+ public void setSoundEnabled(boolean soundEnabled) {
+ BaseObject.sSystemRegistry.soundSystem.setSoundEnabled(soundEnabled);
+ }
+
+ public void setControlOptions(boolean clickAttack, boolean tiltControls) {
+ BaseObject.sSystemRegistry.inputSystem.setClickActive(clickAttack);
+ BaseObject.sSystemRegistry.inputSystem.setUseOrientationForRoll(tiltControls);
+ }
+
+ public float getGameTime() {
+ return BaseObject.sSystemRegistry.timeSystem.getGameTime();
+ }
+
+ public Vector2 getLastDeathPosition() {
+ return BaseObject.sSystemRegistry.eventRecorder.getLastDeathPosition();
+ }
+
+ public boolean isPaused() {
+ return (mRunning && mGameThread != null && mGameThread.getPaused());
+ }
+
+}
diff --git a/src/com/replica/replicaisland/GameComponent.java b/src/com/replica/replicaisland/GameComponent.java
new file mode 100644
index 0000000..099c04f
--- /dev/null
+++ b/src/com/replica/replicaisland/GameComponent.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A game component implements a single feature of a game object. Components are run once per frame
+ * when their parent object is active. Updating a game object is equivalent to updating all of its
+ * components. Note that a game object may contain more than one instance of the same type of
+ * component.
+ */
+public abstract class GameComponent extends PhasedObject {
+ // Defines high-level buckets within which components may choose to run.
+ public enum ComponentPhases {
+ THINK, // decisions are made
+ PHYSICS, // impulse velocities are summed
+ POST_PHYSICS, // inertia, friction, and bounce
+ MOVEMENT, // position is updated
+ COLLISION_DETECTION, // intersections are detected
+ COLLISION_RESPONSE, // intersections are resolved
+ POST_COLLISION, // position is now final for the frame
+ ANIMATION, // animations are selected
+ PRE_DRAW, // drawing state is initialized
+ DRAW, // drawing commands are scheduled.
+ FRAME_END, // final cleanup before the next update
+ }
+
+ public boolean shared;
+
+ public GameComponent() {
+ super();
+ shared = false;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/GameComponentPool.java b/src/com/replica/replicaisland/GameComponentPool.java
new file mode 100644
index 0000000..6270570
--- /dev/null
+++ b/src/com/replica/replicaisland/GameComponentPool.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+public class GameComponentPool extends TObjectPool<GameComponent> {
+ public Class<?> objectClass;
+
+ public GameComponentPool(Class<?> type) {
+ super();
+ objectClass = type;
+ fill();
+ }
+
+ public GameComponentPool(Class<?> type, int size) {
+ super(size);
+ objectClass = type;
+ fill();
+ }
+
+ @Override
+ protected void fill() {
+ if (objectClass != null) {
+ for (int x = 0; x < getSize(); x++) {
+ try {
+ getAvailable().add(objectClass.newInstance());
+ } catch (IllegalAccessException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InstantiationException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+}
+
+
+
diff --git a/src/com/replica/replicaisland/GameFlowEvent.java b/src/com/replica/replicaisland/GameFlowEvent.java
new file mode 100644
index 0000000..a7acf8f
--- /dev/null
+++ b/src/com/replica/replicaisland/GameFlowEvent.java
@@ -0,0 +1,48 @@
+package com.replica.replicaisland;
+
+import android.content.Context;
+
+public class GameFlowEvent implements Runnable {
+ public static final int EVENT_INVALID = -1;
+ public static final int EVENT_RESTART_LEVEL = 0;
+ public static final int EVENT_END_GAME = 1;
+ public static final int EVENT_GO_TO_NEXT_LEVEL = 2;
+
+ public static final int EVENT_SHOW_DIARY = 3;
+ public static final int EVENT_SHOW_DIALOG_CHARACTER1 = 4;
+ public static final int EVENT_SHOW_DIALOG_CHARACTER2 = 5;
+ public static final int EVENT_SHOW_ANIMATION = 6;
+
+ private int mEventCode;
+ private int mDataIndex;
+ private AndouKun mMainActivity;
+
+ public void post(int event, int index, Context context) {
+ if (context instanceof AndouKun) {
+ DebugLog.d("GameFlowEvent", "Post Game Flow Event: " + event + ", " + index);
+ mEventCode = event;
+ mDataIndex = index;
+ mMainActivity = (AndouKun)context;
+ mMainActivity.runOnUiThread(this);
+ }
+ }
+
+ public void postImmediate(int event, int index, Context context) {
+ if (context instanceof AndouKun) {
+ DebugLog.d("GameFlowEvent", "Execute Immediate Game Flow Event: " + event + ", " + index);
+ mEventCode = event;
+ mDataIndex = index;
+ mMainActivity = (AndouKun)context;
+ mMainActivity.onGameFlowEvent(mEventCode, mDataIndex);
+ }
+ }
+
+ public void run() {
+ if (mMainActivity != null) {
+ DebugLog.d("GameFlowEvent", "Execute Game Flow Event: " + mEventCode + ", " + mDataIndex);
+ mMainActivity.onGameFlowEvent(mEventCode, mDataIndex);
+ mMainActivity = null;
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/GameObject.java b/src/com/replica/replicaisland/GameObject.java
new file mode 100644
index 0000000..392486a
--- /dev/null
+++ b/src/com/replica/replicaisland/GameObject.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.CollisionParameters.HitType;
+
+/**
+ * GameObject defines any object that resides in the game world (character, background, special
+ * effect, enemy, etc). It is a collection of GameComponents which implement its behavior;
+ * GameObjects themselves have no intrinsic behavior. GameObjects are also "bags of data" that
+ * components can use to share state (direct component-to-component communication is discouraged).
+ */
+public class GameObject extends PhasedObjectManager {
+ private final static float COLLISION_SURFACE_DECAY_TIME = 0.3f;
+ // These fields are managed by components.
+ private Vector2 mPosition;
+ private Vector2 mVelocity;
+ private Vector2 mTargetVelocity;
+ private Vector2 mAcceleration;
+ private Vector2 mImpulse;
+
+ private Vector2 mBackgroundCollisionNormal;
+
+ private float mLastTouchedFloorTime;
+ private float mLastTouchedCeilingTime;
+ private float mLastTouchedLeftWallTime;
+ private float mLastTouchedRightWallTime;
+
+ public boolean positionLocked;
+
+ public float activationRadius;
+ public boolean destroyOnDeactivation;
+
+ public int life;
+
+ public int lastReceivedHitType;
+
+ public Vector2 facingDirection;
+ public float width;
+ public float height;
+
+ private static final int DEFAULT_LIFE = 1;
+
+ public enum ActionType {
+ INVALID,
+ IDLE,
+ MOVE,
+ ATTACK,
+ HIT_REACT,
+ DEATH,
+ HIDE,
+ FROZEN
+ }
+
+ private ActionType mCurrentAction;
+
+ public enum Team {
+ NONE,
+ PLAYER,
+ ENEMY
+ }
+
+ public Team team;
+
+ public GameObject() {
+ super();
+
+ mPosition = new Vector2();
+ mVelocity = new Vector2();
+ mTargetVelocity = new Vector2();
+ mAcceleration = new Vector2();
+ mImpulse = new Vector2();
+ mBackgroundCollisionNormal = new Vector2();
+
+ facingDirection = new Vector2(1, 0);
+
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ removeAll();
+ commitUpdates();
+
+ mPosition.zero();
+ mVelocity.zero();
+ mTargetVelocity.zero();
+ mAcceleration.zero();
+ mImpulse.zero();
+ mBackgroundCollisionNormal.zero();
+ facingDirection.set(1.0f, 1.0f);
+
+ mCurrentAction = ActionType.INVALID;
+ positionLocked = false;
+ activationRadius = 0;
+ destroyOnDeactivation = false;
+ life = DEFAULT_LIFE;
+ team = Team.NONE;
+ width = 0.0f;
+ height = 0.0f;
+
+ lastReceivedHitType = HitType.INVALID;
+ }
+
+ // Utility functions
+ public final boolean touchingGround() {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+ final boolean touching = gameTime > 0.1f &&
+ Utils.close(mLastTouchedFloorTime, time.getGameTime(), COLLISION_SURFACE_DECAY_TIME);
+ return touching;
+ }
+
+ public final boolean touchingCeiling() {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+ final boolean touching = gameTime > 0.1f &&
+ Utils.close(mLastTouchedCeilingTime, time.getGameTime(), COLLISION_SURFACE_DECAY_TIME);
+ return touching;
+ }
+
+ public final boolean touchingLeftWall() {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+ final boolean touching = gameTime > 0.1f &&
+ Utils.close(mLastTouchedLeftWallTime, time.getGameTime(), COLLISION_SURFACE_DECAY_TIME);
+ return touching;
+ }
+
+ public final boolean touchingRightWall() {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+ final boolean touching = gameTime > 0.1f &&
+ Utils.close(mLastTouchedRightWallTime, time.getGameTime(), COLLISION_SURFACE_DECAY_TIME);
+ return touching;
+ }
+
+ public final Vector2 getPosition() {
+ return mPosition;
+ }
+
+ public final void setPosition(Vector2 position) {
+ mPosition.set(position);
+ }
+
+ public final float getCenteredPositionX() {
+ return mPosition.x + (width / 2.0f);
+ }
+
+ public final float getCenteredPositionY() {
+ return mPosition.y + (height / 2.0f);
+ }
+
+ public final Vector2 getVelocity() {
+ return mVelocity;
+ }
+
+ public final void setVelocity(Vector2 velocity) {
+ mVelocity.set(velocity);
+ }
+
+ public final Vector2 getTargetVelocity() {
+ return mTargetVelocity;
+ }
+
+ public final void setTargetVelocity(Vector2 targetVelocity) {
+ mTargetVelocity.set(targetVelocity);
+ }
+
+ public final Vector2 getAcceleration() {
+ return mAcceleration;
+ }
+
+ public final void setAcceleration(Vector2 acceleration) {
+ mAcceleration.set(acceleration);
+ }
+
+ public final Vector2 getImpulse() {
+ return mImpulse;
+ }
+
+ public final void setImpulse(Vector2 impulse) {
+ mImpulse.set(impulse);
+ }
+
+ public final Vector2 getBackgroundCollisionNormal() {
+ return mBackgroundCollisionNormal;
+ }
+
+ public final void setBackgroundCollisionNormal(Vector2 normal) {
+ mBackgroundCollisionNormal.set(normal);
+ }
+
+ public final float getLastTouchedFloorTime() {
+ return mLastTouchedFloorTime;
+ }
+
+ public final void setLastTouchedFloorTime(float lastTouchedFloorTime) {
+ mLastTouchedFloorTime = lastTouchedFloorTime;
+ }
+
+ public final float getLastTouchedCeilingTime() {
+ return mLastTouchedCeilingTime;
+ }
+
+ public final void setLastTouchedCeilingTime(float lastTouchedCeilingTime) {
+ mLastTouchedCeilingTime = lastTouchedCeilingTime;
+ }
+
+ public final float getLastTouchedLeftWallTime() {
+ return mLastTouchedLeftWallTime;
+ }
+
+ public final void setLastTouchedLeftWallTime(float lastTouchedLeftWallTime) {
+ mLastTouchedLeftWallTime = lastTouchedLeftWallTime;
+ }
+
+ public final float getLastTouchedRightWallTime() {
+ return mLastTouchedRightWallTime;
+ }
+
+ public final void setLastTouchedRightWallTime(float lastTouchedRightWallTime) {
+ mLastTouchedRightWallTime = lastTouchedRightWallTime;
+ }
+
+ public final ActionType getCurrentAction() {
+ return mCurrentAction;
+ }
+
+ public final void setCurrentAction(ActionType type) {
+ mCurrentAction = type;
+ }
+}
diff --git a/src/com/replica/replicaisland/GameObjectCollisionSystem.java b/src/com/replica/replicaisland/GameObjectCollisionSystem.java
new file mode 100644
index 0000000..aa5f9a1
--- /dev/null
+++ b/src/com/replica/replicaisland/GameObjectCollisionSystem.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+import com.replica.replicaisland.CollisionParameters.HitType;
+
+/**
+ * A system for calculating collisions between moving game objects. This system accepts collision
+ * volumes from game objects each frame and performs a series of tests to see which of them
+ * overlap. Collisions are only considered between offending "attack" volumes and receiving
+ * "vulnerability" volumes. This implementation works by using a sweep-and-prune algorithm:
+ * objects to be considered are sorted in the x axis and then compared in one dimension for
+ * overlaps. A bounding volume that encompasses all attack and vulnerability volumes is used for
+ * this test, and when an intersection is found the actual offending and receiving volumes are
+ * compared. If an intersection is detected both objects receive notification via a
+ * HitReactionComponent, if one has been specified.
+ */
+public class GameObjectCollisionSystem extends BaseObject {
+ private static final int MAX_COLLIDING_OBJECTS = 64;
+ private static final int COLLISION_RECORD_POOL_SIZE = 64;
+ private static final CollisionVolumeComparator sCollisionVolumeComparator
+ = new CollisionVolumeComparator();
+ private static CollisionVolume.FlipInfo sFlip = new CollisionVolume.FlipInfo();
+ private static CollisionVolume.FlipInfo sOtherFlip = new CollisionVolume.FlipInfo();
+
+ FixedSizeArray<CollisionVolumeRecord> mObjects;
+ CollisionVolumeRecordPool mRecordPool;
+ private boolean mDrawDebugBoundingVolume = false;
+ private boolean mDrawDebugCollisionVolumes = false;
+
+
+ public GameObjectCollisionSystem() {
+ super();
+ mObjects = new FixedSizeArray<CollisionVolumeRecord>(MAX_COLLIDING_OBJECTS);
+ mObjects.setComparator(sCollisionVolumeComparator);
+ //mObjects.setSorter(new ShellSorter<CollisionVolumeRecord>());
+ mRecordPool = new CollisionVolumeRecordPool(COLLISION_RECORD_POOL_SIZE);
+ }
+
+ @Override
+ public void reset() {
+ final int count = mObjects.getCount();
+
+ for (int x = 0; x < count; x++) {
+ mRecordPool.release(mObjects.get(x));
+ }
+ mObjects.clear();
+
+ mDrawDebugBoundingVolume = false;
+ mDrawDebugCollisionVolumes = false;
+ }
+
+ /**
+ * Adds a game object, and its related volumes, to the dynamic collision world for one frame.
+ * Once registered for collisions the object may damage other objects via attack volumes or
+ * receive damage from other volumes via vulnerability volumes.
+ * @param object The object to consider for collision.
+ * @param reactionComponent A HitReactionComponent to notify when an intersection is calculated.
+ * If null, the intersection will still occur and no notification will be sent.
+ * @param boundingVolume A volume that describes the game object in space. It should encompass
+ * all of the attack and vulnerability volumes.
+ * @param attackVolumes A list of volumes that can hit other game objects. May be null.
+ * @param vulnerabilityVolumes A list of volumes that can receive hits from other game objects.
+ * May be null.
+ */
+ public void registerForCollisions(GameObject object,
+ HitReactionComponent reactionComponent,
+ CollisionVolume boundingVolume,
+ FixedSizeArray<CollisionVolume> attackVolumes,
+ FixedSizeArray<CollisionVolume> vulnerabilityVolumes) {
+ CollisionVolumeRecord record = mRecordPool.allocate();
+ if (record != null && object != null && boundingVolume != null
+ && (attackVolumes != null || vulnerabilityVolumes != null)) {
+ record.object = object;
+ record.boundingVolume = boundingVolume;
+ record.attackVolumes = attackVolumes;
+ record.vulnerabilityVolumes = vulnerabilityVolumes;
+ record.reactionComponent = reactionComponent;
+ mObjects.add(record);
+ }
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ // Sort the objects by their x position.
+ mObjects.sort(true);
+
+ final int count = mObjects.getCount();
+ for (int x = 0; x < count; x++) {
+ final CollisionVolumeRecord record = mObjects.get(x);
+ final Vector2 position = record.object.getPosition();
+ sFlip.flipX = (record.object.facingDirection.x < 0.0f);
+ sFlip.flipY = (record.object.facingDirection.y < 0.0f);
+ sFlip.parentWidth = record.object.width;
+ sFlip.parentHeight = record.object.height;
+
+ if (sSystemRegistry.debugSystem != null) {
+ drawDebugVolumes(record);
+ }
+
+ final float maxX = record.boundingVolume.getMaxXPosition(sFlip) + position.x;
+ for (int y = x + 1; y < count; y++) {
+ final CollisionVolumeRecord other = mObjects.get(y);
+ final Vector2 otherPosition = other.object.getPosition();
+ sOtherFlip.flipX = (other.object.facingDirection.x < 0.0f);
+ sOtherFlip.flipY = (other.object.facingDirection.y < 0.0f);
+ sOtherFlip.parentWidth = other.object.width;
+ sOtherFlip.parentHeight = other.object.height;
+
+ if (otherPosition.x + other.boundingVolume.getMinXPosition(sOtherFlip) > maxX) {
+ // These objects can't possibly be colliding. And since the list is sorted,
+ // there are no potentially colliding objects after this object
+ // either, so we're done!
+ break;
+ } else {
+ final boolean testRequired = (record.attackVolumes != null && other.vulnerabilityVolumes != null) ||
+ (record.vulnerabilityVolumes != null && other.attackVolumes != null);
+ if (testRequired && record.boundingVolume.intersects(position, sFlip,
+ other.boundingVolume, otherPosition, sOtherFlip)) {
+ // These two objects are potentially colliding.
+ // Now we must test all attack vs vulnerability boxes.
+ final int hit = testAttackAgainstVulnerability(
+ record.attackVolumes,
+ other.vulnerabilityVolumes,
+ position,
+ otherPosition,
+ sFlip,
+ sOtherFlip);
+ if (hit != HitType.INVALID) {
+ boolean hitAccepted = false;
+ if (other.reactionComponent != null) {
+ hitAccepted = other.reactionComponent.receivedHit(
+ other.object, record.object, hit);
+ }
+ if (record.reactionComponent != null) {
+ record.reactionComponent.hitVictim(
+ record.object, other.object, hit, hitAccepted);
+ }
+
+ }
+
+ final int hit2 = testAttackAgainstVulnerability(
+ other.attackVolumes,
+ record.vulnerabilityVolumes,
+ otherPosition,
+ position,
+ sOtherFlip,
+ sFlip);
+ if (hit2 != HitType.INVALID) {
+ boolean hitAccepted = false;
+ if (record.reactionComponent != null) {
+ hitAccepted = record.reactionComponent.receivedHit(
+ record.object, other.object, hit2);
+ }
+ if (other.reactionComponent != null) {
+ other.reactionComponent.hitVictim(
+ other.object, record.object, hit2, hitAccepted);
+ }
+
+ }
+ }
+ }
+ }
+ // This is a little tricky. Since we always sweep forward in the list it's safe
+ // to invalidate the current record after we've tested it. This way we don't have to
+ // iterate over the object list twice.
+ mRecordPool.release(record);
+ }
+
+ mObjects.clear();
+ }
+
+ /** Compares the passed list of attack volumes against the passed list of vulnerability volumes
+ * and returns a hit type if an intersection is found.
+ * @param attackVolumes Offensive collision volumes.
+ * @param vulnerabilityVolumes Receiving collision volumes.
+ * @param attackPosition The world position of the attacking object.
+ * @param vulnerabilityPosition The world position of the receiving object.
+ * @return The hit type of the first attacking volume that intersects a vulnerability volume,
+ * or HitType.INVALID if no intersections are found.
+ */
+ private int testAttackAgainstVulnerability(
+ FixedSizeArray<CollisionVolume> attackVolumes,
+ FixedSizeArray<CollisionVolume> vulnerabilityVolumes,
+ Vector2 attackPosition,
+ Vector2 vulnerabilityPosition,
+ CollisionVolume.FlipInfo attackFlip,
+ CollisionVolume.FlipInfo vulnerabilityFlip) {
+ int intersectionType = HitType.INVALID;
+ if (attackVolumes != null && vulnerabilityVolumes != null) {
+ final int attackCount = attackVolumes.getCount();
+ for (int x = 0; x < attackCount && intersectionType == HitType.INVALID; x++) {
+ final CollisionVolume attackVolume = attackVolumes.get(x);
+ final int hitType = attackVolume.getHitType();
+ if (hitType != HitType.INVALID) {
+ final int vulnerabilityCount = vulnerabilityVolumes.getCount();
+ for (int y = 0; y < vulnerabilityCount; y++) {
+ final CollisionVolume vulnerabilityVolume = vulnerabilityVolumes.get(y);
+ final int vulnerableType = vulnerabilityVolume.getHitType();
+ if (vulnerableType == HitType.INVALID || vulnerableType == hitType) {
+ if (attackVolume.intersects(attackPosition, attackFlip,
+ vulnerabilityVolume, vulnerabilityPosition,
+ vulnerabilityFlip)) {
+ intersectionType = hitType;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return intersectionType;
+ }
+
+ private final void drawDebugVolumes(CollisionVolumeRecord record) {
+ final Vector2 position = record.object.getPosition();
+ if (mDrawDebugBoundingVolume) {
+ final CollisionVolume boundingVolume = record.boundingVolume;
+ sSystemRegistry.debugSystem.drawShape(
+ position.x + boundingVolume.getMinXPosition(sFlip), position.y + boundingVolume.getMinYPosition(sFlip),
+ boundingVolume.getMaxX() - boundingVolume.getMinX(),
+ boundingVolume.getMaxY() - boundingVolume.getMinY(),
+ DebugSystem.SHAPE_CIRCLE,
+ DebugSystem.COLOR_OUTLINE);
+ }
+ if (mDrawDebugCollisionVolumes) {
+ if (record.attackVolumes != null) {
+ final int attackVolumeCount = record.attackVolumes.getCount();
+ for (int y = 0; y < attackVolumeCount; y++) {
+ CollisionVolume volume = record.attackVolumes.get(y);
+ sSystemRegistry.debugSystem.drawShape(
+ position.x + volume.getMinXPosition(sFlip), position.y + volume.getMinYPosition(sFlip),
+ volume.getMaxX() - volume.getMinX(),
+ volume.getMaxY() - volume.getMinY(),
+ volume.getClass() == AABoxCollisionVolume.class ? DebugSystem.SHAPE_BOX : DebugSystem.SHAPE_CIRCLE,
+ DebugSystem.COLOR_RED);
+ }
+ }
+
+ if (record.vulnerabilityVolumes != null) {
+ final int vulnVolumeCount = record.vulnerabilityVolumes.getCount();
+ for (int y = 0; y < vulnVolumeCount; y++) {
+ CollisionVolume volume = record.vulnerabilityVolumes.get(y);
+ sSystemRegistry.debugSystem.drawShape(
+ position.x + volume.getMinXPosition(sFlip), position.y + volume.getMinYPosition(sFlip),
+ volume.getMaxX() - volume.getMinX(),
+ volume.getMaxY() - volume.getMinY(),
+ volume.getClass() == AABoxCollisionVolume.class ? DebugSystem.SHAPE_BOX : DebugSystem.SHAPE_CIRCLE,
+ DebugSystem.COLOR_BLUE);
+ }
+ }
+ }
+ }
+
+ public void setDebugPrefs(boolean drawBoundingVolumes, boolean drawCollisionVolumes) {
+ mDrawDebugBoundingVolume = drawBoundingVolumes;
+ mDrawDebugCollisionVolumes = drawCollisionVolumes;
+ }
+
+ /** A record of a single game object and its associated collision info. */
+ private class CollisionVolumeRecord extends AllocationGuard {
+ public GameObject object;
+ public HitReactionComponent reactionComponent;
+ public CollisionVolume boundingVolume;
+ public FixedSizeArray<CollisionVolume> attackVolumes;
+ public FixedSizeArray<CollisionVolume> vulnerabilityVolumes;
+
+ public void reset() {
+ object = null;
+ attackVolumes = null;
+ vulnerabilityVolumes = null;
+ boundingVolume = null;
+ reactionComponent = null;
+ }
+ }
+
+ /** A pool of collision volume records. */
+ private class CollisionVolumeRecordPool extends TObjectPool<CollisionVolumeRecord> {
+
+ public CollisionVolumeRecordPool(int count) {
+ super(count);
+ }
+
+ @Override
+ protected void fill() {
+ for (int x = 0; x < getSize(); x++) {
+ getAvailable().add(new CollisionVolumeRecord());
+ }
+ }
+
+ @Override
+ public void release(Object entry) {
+ ((CollisionVolumeRecord)entry).reset();
+ super.release(entry);
+ }
+
+ }
+
+ /**
+ * Comparator for game objects that considers the world position of the object's bounding
+ * volume and sorts objects from left to right on the x axis. */
+ public final static class CollisionVolumeComparator implements Comparator<CollisionVolumeRecord> {
+ private static CollisionVolume.FlipInfo sCompareFlip = new CollisionVolume.FlipInfo();
+ public int compare(CollisionVolumeRecord object1, CollisionVolumeRecord object2) {
+ int result = 0;
+ if (object1 == null && object2 != null) {
+ result = 1;
+ } else if (object1 != null && object2 == null) {
+ result = -1;
+ } else if (object1 != null && object2 != null) {
+ sCompareFlip.flipX = (object1.object.facingDirection.x < 0.0f);
+ sCompareFlip.flipY = (object1.object.facingDirection.y < 0.0f);
+ sCompareFlip.parentWidth = object1.object.width;
+ sCompareFlip.parentHeight = object1.object.height;
+
+ final float minX1 = object1.object.getPosition().x
+ + object1.boundingVolume.getMinXPosition(sCompareFlip);
+
+ sCompareFlip.flipX = (object2.object.facingDirection.x < 0.0f);
+ sCompareFlip.flipY = (object2.object.facingDirection.y < 0.0f);
+ sCompareFlip.parentWidth = object2.object.width;
+ sCompareFlip.parentHeight = object2.object.height;
+
+ final float minX2 = object2.object.getPosition().x
+ + object2.boundingVolume.getMinXPosition(sCompareFlip);
+
+ final float delta = minX1 - minX2;
+ if (delta < 0.0f) {
+ result = -1;
+ } else if (delta > 0.0f) {
+ result = 1;
+ }
+ }
+ return result;
+ }
+ }
+
+
+
+}
diff --git a/src/com/replica/replicaisland/GameObjectFactory.java b/src/com/replica/replicaisland/GameObjectFactory.java
new file mode 100644
index 0000000..6dd8d1a
--- /dev/null
+++ b/src/com/replica/replicaisland/GameObjectFactory.java
@@ -0,0 +1,6504 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+import com.replica.replicaisland.AnimationComponent.PlayerAnimations;
+import com.replica.replicaisland.CollisionParameters.HitType;
+import com.replica.replicaisland.EnemyAnimationComponent.EnemyAnimations;
+import com.replica.replicaisland.GameObject.ActionType;
+import com.replica.replicaisland.GameObject.Team;
+
+/** A class for generating game objects at runtime.
+ * This should really be replaced with something that is data-driven, but it is hard to do data
+ * parsing quickly at runtime. For the moment this class is full of large functions that just
+ * patch pointers between objects, but in the future those functions should either be
+ * a) generated from data at compile time, or b) described by data at runtime.
+ */
+public class GameObjectFactory extends BaseObject {
+ private final static int MAX_GAME_OBJECTS = 256;
+ private final static ComponentPoolComparator sComponentPoolComparator = new ComponentPoolComparator();
+ private FixedSizeArray<FixedSizeArray<BaseObject>> mStaticData;
+ private FixedSizeArray<GameComponentPool> mComponentPools;
+ private GameComponentPool mPoolSearchDummy;
+ private GameObjectPool mGameObjectPool;
+
+ private float mTightActivationRadius;
+ private float mNormalActivationRadius;
+ private float mWideActivationRadius;
+ private float mAlwaysActive;
+
+ private final static String sRedButtonChannel = "RED BUTTON";
+ private final static String sBlueButtonChannel = "BLUE BUTTON";
+ private final static String sGreenButtonChannel = "GREEN BUTTON";
+ private final static String sSurprisedNPCChannel = "SURPRISED";
+
+
+ // A list of game objects that can be spawned at runtime. Note that the indicies of these
+ // objects must match the order of the object tileset in the level editor in order for the
+ // level content to make sense.
+ public enum GameObjectType {
+ INVALID(-1),
+
+ PLAYER (0),
+
+ // Collectables
+ COIN (1),
+ RUBY (2),
+ DIARY (3),
+
+ // Characters
+ WANDA (10),
+ KYLE (11),
+ KYLE_DEAD (12),
+ ANDOU_DEAD (13),
+ KABOCHA (26),
+ ROKUDOU_TERMINAL (27),
+ KABOCHA_TERMINAL (28),
+ EVIL_KABOCHA (29),
+ ROKUDOU (30),
+
+ // AI
+ BROBOT (16),
+ SNAILBOMB (17),
+ SHADOWSLIME (18),
+ MUDMAN (19),
+ SKELETON (20),
+ KARAGUIN (21),
+ PINK_NAMAZU (22),
+ TURRET (23),
+ TURRET_LEFT (24),
+ BAT (6),
+ STING(7),
+ ONION(8),
+
+ // Objects
+ DOOR_RED (32),
+ DOOR_BLUE (33),
+ DOOR_GREEN (34),
+ BUTTON_RED (35),
+ BUTTON_BLUE (36),
+ BUTTON_GREEN (37),
+ CANNON (38),
+ BROBOT_SPAWNER (39),
+ BROBOT_SPAWNER_LEFT (40),
+ BREAKABLE_BLOCK(41),
+ THE_SOURCE(42),
+
+ // Effects
+ DUST(48),
+ EXPLOSION_SMALL(49),
+ EXPLOSION_LARGE(50),
+ EXPLOSION_GIANT(51),
+
+
+ // Special Spawnable
+ DOOR_RED_NONBLOCKING (52),
+ DOOR_BLUE_NONBLOCKING (53),
+ DOOR_GREEN_NONBLOCKING (54),
+
+ GHOST_NPC(55),
+
+ CAMERA_BIAS(56),
+
+ SMOKE_BIG(57),
+ SMOKE_SMALL(58),
+
+ CRUSH_FLASH(59),
+ FLASH(60),
+
+ // Projectiles
+ ENERGY_BALL(68),
+ CANNON_BALL(65),
+ TURRET_BULLET(66),
+ BROBOT_BULLET(67),
+ BREAKABLE_BLOCK_PIECE(68),
+ BREAKABLE_BLOCK_PIECE_SPAWNER(69),
+ WANDA_SHOT(70),
+
+ // Special Objects -- Not spawnable normally
+ PLAYER_JETS(-1),
+ PLAYER_SPARKS(-1),
+ PLAYER_GLOW(-1),
+ ENEMY_SPARKS(-1),
+ GHOST(-1),
+ SMOKE_POOF(-1),
+ GEM_EFFECT(-1),
+ GEM_EFFECT_SPAWNER(-1),
+
+
+ // End
+ OBJECT_COUNT(-1);
+
+ private final int mIndex;
+ GameObjectType(int index) {
+ this.mIndex = index;
+ }
+
+ public int index() {
+ return mIndex;
+ }
+
+ // TODO: Is there any better way to do this?
+ public static GameObjectType indexToType(int index) {
+ final GameObjectType[] valuesArray = values();
+ GameObjectType foundType = INVALID;
+ for (int x = 0; x < valuesArray.length; x++) {
+ GameObjectType type = valuesArray[x];
+ if (type.mIndex == index) {
+ foundType = type;
+ break;
+ }
+ }
+ return foundType;
+ }
+
+ }
+
+ public GameObjectFactory() {
+ super();
+
+ mGameObjectPool = new GameObjectPool(MAX_GAME_OBJECTS);
+
+ final int objectTypeCount = GameObjectType.OBJECT_COUNT.ordinal();
+ mStaticData = new FixedSizeArray<FixedSizeArray<BaseObject>>(objectTypeCount);
+
+ for (int x = 0; x < objectTypeCount; x++) {
+ mStaticData.add(null);
+ }
+
+ final ContextParameters context = sSystemRegistry.contextParameters;
+ final float halfHeight2 = (context.gameHeight * 0.5f) * (context.gameHeight * 0.5f);
+ final float halfWidth2 = (context.gameWidth * 0.5f) * (context.gameWidth * 0.5f);
+ final float screenSizeRadius = (float)Math.sqrt(halfHeight2 + halfWidth2);
+ mTightActivationRadius = screenSizeRadius + 128.0f;
+ mNormalActivationRadius = screenSizeRadius * 1.25f;
+ mWideActivationRadius = screenSizeRadius * 2.0f;
+ mAlwaysActive = -1.0f;
+
+ // TODO: I wish there was a way to do this automatically, but the ClassLoader doesn't seem
+ // to provide access to the currently loaded class list. There's some discussion of walking
+ // the actual class file objects and using forName() to instantiate them, but that sounds
+ // really heavy-weight. For now I'll rely on (sucky) manual enumeration.
+ class ComponentClass {
+ public Class<?> type;
+ public int poolSize;
+ public ComponentClass(Class<?> classType, int size) {
+ type = classType;
+ poolSize = size;
+ }
+ }
+ ComponentClass[] componentTypes = {
+ new ComponentClass(AnimationComponent.class, 1),
+ new ComponentClass(AttackAtDistanceComponent.class, 16),
+ new ComponentClass(BackgroundCollisionComponent.class, 128),
+ new ComponentClass(ButtonAnimationComponent.class, 32),
+ new ComponentClass(CameraBiasComponent.class, 8),
+ new ComponentClass(ChangeComponentsComponent.class, 128),
+ new ComponentClass(DoorAnimationComponent.class, 256), //!
+ new ComponentClass(DynamicCollisionComponent.class, 256),
+ new ComponentClass(EnemyAnimationComponent.class, 128),
+ new ComponentClass(FadeDrawableComponent.class, 32),
+ new ComponentClass(FixedAnimationComponent.class, 8),
+ new ComponentClass(GenericAnimationComponent.class, 32),
+ new ComponentClass(GhostComponent.class, 64),
+ new ComponentClass(GravityComponent.class, 128),
+ new ComponentClass(HitPlayerComponent.class, 256),
+ new ComponentClass(HitReactionComponent.class, 256),
+ new ComponentClass(InventoryComponent.class, 128),
+ new ComponentClass(LauncherComponent.class, 16),
+ new ComponentClass(LaunchProjectileComponent.class, 128),
+ new ComponentClass(LifetimeComponent.class, 256),
+ new ComponentClass(MotionBlurComponent.class, 1),
+ new ComponentClass(MovementComponent.class, 128),
+ new ComponentClass(NPCAnimationComponent.class, 8),
+ new ComponentClass(NPCComponent.class, 8),
+ new ComponentClass(OrbitalMagnetComponent.class, 1),
+ new ComponentClass(PatrolComponent.class, 128),
+ new ComponentClass(PhysicsComponent.class, 8),
+ new ComponentClass(PlayerComponent.class, 1),
+ new ComponentClass(PlaySingleSoundComponent.class, 32),
+ new ComponentClass(PopOutComponent.class, 32),
+ new ComponentClass(RenderComponent.class, 256),
+ new ComponentClass(ScrollerComponent.class, 8),
+ new ComponentClass(SelectDialogComponent.class, 8),
+ new ComponentClass(SimpleCollisionComponent.class, 32),
+ new ComponentClass(SimplePhysicsComponent.class, 128),
+ new ComponentClass(SleeperComponent.class, 32),
+ new ComponentClass(SolidSurfaceComponent.class, 16),
+ new ComponentClass(SpriteComponent.class, 256),
+ new ComponentClass(TheSourceComponent.class, 1),
+
+
+ };
+
+ mComponentPools = new FixedSizeArray<GameComponentPool>(componentTypes.length, sComponentPoolComparator);
+ for (int x = 0; x < componentTypes.length; x++) {
+ ComponentClass component = componentTypes[x];
+ mComponentPools.add(new GameComponentPool(component.type, component.poolSize));
+ }
+ mComponentPools.sort(true);
+
+ mPoolSearchDummy = new GameComponentPool(Object.class, 1);
+
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ protected GameComponentPool getComponentPool(Class<?> componentType) {
+ GameComponentPool pool = null;
+ mPoolSearchDummy.objectClass = componentType;
+ final int index = mComponentPools.find(mPoolSearchDummy, false);
+ if (index != -1) {
+ pool = mComponentPools.get(index);
+ }
+ return pool;
+ }
+
+ protected GameComponent allocateComponent(Class<?> componentType) {
+ GameComponentPool pool = getComponentPool(componentType);
+ assert pool != null;
+ GameComponent component = null;
+ if (pool != null) {
+ component = pool.allocate();
+ }
+ return component;
+ }
+
+ protected void releaseComponent(GameComponent component) {
+ GameComponentPool pool = getComponentPool(component.getClass());
+ assert pool != null;
+ if (pool != null) {
+ component.reset();
+ component.shared = false;
+ pool.release(component);
+ }
+ }
+
+ public void preloadEffects() {
+ // These textures appear in every level, so they are long-term.
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+ textureLibrary.allocateTexture(R.drawable.dust01);
+ textureLibrary.allocateTexture(R.drawable.dust02);
+ textureLibrary.allocateTexture(R.drawable.dust03);
+ textureLibrary.allocateTexture(R.drawable.dust04);
+ textureLibrary.allocateTexture(R.drawable.dust05);
+
+ textureLibrary.allocateTexture(R.drawable.effect_energyball01);
+ textureLibrary.allocateTexture(R.drawable.effect_energyball02);
+ textureLibrary.allocateTexture(R.drawable.effect_energyball03);
+ textureLibrary.allocateTexture(R.drawable.effect_energyball04);
+
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_small01);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_small02);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_small03);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_small04);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_small05);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_small06);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_small07);
+
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big01);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big02);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big03);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big04);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big05);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big06);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big07);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big08);
+ textureLibrary.allocateTexture(R.drawable.effect_explosion_big09);
+
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_big01);
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_big02);
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_big03);
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_big04);
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_big05);
+
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_small01);
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_small02);
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_small03);
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_small04);
+ textureLibrary.allocateTexture(R.drawable.effect_smoke_small05);
+
+ textureLibrary.allocateTexture(R.drawable.effect_crush_back01);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_back02);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_back03);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_front01);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_front02);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_front03);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_front04);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_front05);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_front06);
+ textureLibrary.allocateTexture(R.drawable.effect_crush_front07);
+ }
+
+ public void destroy(GameObject object) {
+ object.commitUpdates();
+ final int componentCount = object.getCount();
+ for (int x = 0; x < componentCount; x++) {
+ GameComponent component = (GameComponent)object.get(x);
+ if (!component.shared) {
+ releaseComponent(component);
+ }
+ }
+ object.removeAll();
+ object.commitUpdates();
+ mGameObjectPool.release(object);
+ }
+
+ public GameObject spawn(GameObjectType type, float x, float y, boolean horzFlip) {
+ GameObject newObject = null;
+ switch(type) {
+ case PLAYER:
+ newObject = spawnPlayer(x, y);
+ break;
+ case COIN:
+ newObject = spawnCoin(x, y);
+ break;
+ case RUBY:
+ newObject = spawnRuby(x, y);
+ break;
+ case DIARY:
+ newObject = spawnDiary(x, y);
+ break;
+ case WANDA:
+ newObject = spawnEnemyWanda(x, y, true);
+ break;
+ case KYLE:
+ newObject = spawnEnemyKyle(x, y, true);
+ break;
+ case KYLE_DEAD:
+ newObject = spawnEnemyKyleDead(x, y);
+ break;
+ case ANDOU_DEAD:
+ newObject = spawnEnemyAndouDead(x, y);
+ break;
+ case KABOCHA:
+ newObject = spawnEnemyKabocha(x, y, true);
+ break;
+ case ROKUDOU_TERMINAL:
+ newObject = spawnRokudouTerminal(x, y);
+ break;
+ case KABOCHA_TERMINAL:
+ newObject = spawnKabochaTerminal(x, y);
+ break;
+ case EVIL_KABOCHA:
+ newObject = spawnEnemyEvilKabocha(x, y, true);
+ break;
+ case ROKUDOU:
+ newObject = spawnEnemyRokudou(x, y, true);
+ break;
+ case BROBOT:
+ newObject = spawnEnemyBrobot(x, y, horzFlip);
+ break;
+ case SNAILBOMB:
+ newObject = spawnEnemySnailBomb(x, y, horzFlip);
+ break;
+ case SHADOWSLIME:
+ newObject = spawnEnemyShadowSlime(x, y, horzFlip);
+ break;
+ case MUDMAN:
+ newObject = spawnEnemyMudman(x, y, horzFlip);
+ break;
+ case SKELETON:
+ newObject = spawnEnemySkeleton(x, y, horzFlip);
+ break;
+ case KARAGUIN:
+ newObject = spawnEnemyKaraguin(x, y, horzFlip);
+ break;
+ case PINK_NAMAZU:
+ newObject = spawnEnemyPinkNamazu(x, y, horzFlip);
+ break;
+ case BAT:
+ newObject = spawnEnemyBat(x, y, horzFlip);
+ break;
+ case STING:
+ newObject = spawnEnemySting(x, y, horzFlip);
+ break;
+ case ONION:
+ newObject = spawnEnemyOnion(x, y, horzFlip);
+ break;
+ case TURRET:
+ case TURRET_LEFT:
+ newObject = spawnObjectTurret(x, y, (type == GameObjectType.TURRET_LEFT));
+ break;
+ case DOOR_RED:
+ case DOOR_RED_NONBLOCKING:
+ newObject = spawnObjectDoor(x, y, GameObjectType.DOOR_RED, (type == GameObjectType.DOOR_RED));
+ break;
+ case DOOR_BLUE:
+ case DOOR_BLUE_NONBLOCKING:
+ newObject = spawnObjectDoor(x, y, GameObjectType.DOOR_BLUE, (type == GameObjectType.DOOR_BLUE));
+ break;
+ case DOOR_GREEN:
+ case DOOR_GREEN_NONBLOCKING:
+ newObject = spawnObjectDoor(x, y, GameObjectType.DOOR_GREEN, (type == GameObjectType.DOOR_GREEN));
+ break;
+ case BUTTON_RED:
+ newObject = spawnObjectButton(x, y, GameObjectType.BUTTON_RED);
+ break;
+ case BUTTON_BLUE:
+ newObject = spawnObjectButton(x, y, GameObjectType.BUTTON_BLUE);
+ break;
+ case BUTTON_GREEN:
+ newObject = spawnObjectButton(x, y, GameObjectType.BUTTON_GREEN);
+ break;
+ case CANNON:
+ newObject = spawnObjectCannon(x, y);
+ break;
+ case BROBOT_SPAWNER:
+ case BROBOT_SPAWNER_LEFT:
+ newObject = spawnObjectBrobotSpawner(x, y, (type == GameObjectType.BROBOT_SPAWNER_LEFT));
+ break;
+ case BREAKABLE_BLOCK:
+ newObject = spawnObjectBreakableBlock(x, y);
+ break;
+ case THE_SOURCE:
+ newObject = spawnObjectTheSource(x, y);
+ break;
+ case DUST:
+ newObject = spawnDust(x, y, horzFlip);
+ break;
+ case EXPLOSION_SMALL:
+ newObject = spawnEffectExplosionSmall(x, y);
+ break;
+ case EXPLOSION_LARGE:
+ newObject = spawnEffectExplosionLarge(x, y);
+ break;
+ case EXPLOSION_GIANT:
+ newObject = spawnEffectExplosionGiant(x, y);
+ break;
+ case GHOST_NPC:
+ newObject = spawnGhostNPC(x, y);
+ break;
+ case CAMERA_BIAS:
+ newObject = spawnCameraBias(x, y);
+ break;
+ case SMOKE_BIG:
+ newObject = spawnEffectSmokeBig(x, y);
+ break;
+ case SMOKE_SMALL:
+ newObject = spawnEffectSmokeSmall(x, y);
+ break;
+ case CRUSH_FLASH:
+ newObject = spawnEffectCrushFlash(x, y);
+ break;
+ case FLASH:
+ newObject = spawnEffectFlash(x, y);
+ break;
+ case ENERGY_BALL:
+ newObject = spawnEnergyBall(x, y, horzFlip);
+ break;
+ case CANNON_BALL:
+ newObject = spawnCannonBall(x, y, horzFlip);
+ break;
+ case TURRET_BULLET:
+ newObject = spawnTurretBullet(x, y, horzFlip);
+ break;
+ case BROBOT_BULLET:
+ newObject = spawnBrobotBullet(x, y, horzFlip);
+ break;
+ case BREAKABLE_BLOCK_PIECE:
+ newObject = spawnBreakableBlockPiece(x, y);
+ break;
+ case BREAKABLE_BLOCK_PIECE_SPAWNER:
+ newObject = spawnBreakableBlockPieceSpawner(x, y);
+ break;
+ case WANDA_SHOT:
+ newObject = spawnWandaShot(x, y, horzFlip);
+ break;
+ case SMOKE_POOF:
+ newObject = spawnSmokePoof(x, y);
+ break;
+ case GEM_EFFECT:
+ newObject = spawnGemEffect(x, y);
+ break;
+ case GEM_EFFECT_SPAWNER:
+ newObject = spawnGemEffectSpawner(x, y);
+ break;
+ }
+
+ return newObject;
+ }
+
+
+
+ public void spawnFromWorld(TiledWorld world, int tileWidth, int tileHeight) {
+ // Walk the world and spawn objects based on tile indexes.
+ final float worldHeight = world.getHeight() * tileHeight;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (manager != null) {
+ for (int y = 0; y < world.getHeight(); y++) {
+ for (int x = 0; x < world.getWidth(); x++) {
+ int index = world.getTile(x, y);
+ if (index != -1) {
+ GameObjectType type = GameObjectType.indexToType(index);
+ if (type != GameObjectType.INVALID) {
+ final float worldX = x * tileWidth;
+ final float worldY = worldHeight - ((y + 1) * tileHeight);
+ GameObject object = spawn(type, worldX, worldY, false);
+ if (object != null) {
+ if (object.height < tileHeight) {
+ // make sure small objects are vertically centered in their
+ // tile.
+ object.getPosition().y += (tileHeight - object.height) / 2.0f;
+ }
+ if (object.width < tileWidth) {
+ object.getPosition().x += (tileWidth - object.width) / 2.0f;
+ } else if (object.width > tileWidth) {
+ object.getPosition().x -= (object.width - tileWidth) / 2.0f;
+ }
+ manager.add(object);
+ if (type == GameObjectType.PLAYER) {
+ manager.setPlayer(object);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ private FixedSizeArray<BaseObject> getStaticData(GameObjectType type) {
+ return mStaticData.get(type.ordinal());
+ }
+
+ private void setStaticData(GameObjectType type, FixedSizeArray<BaseObject> data) {
+ int index = type.ordinal();
+ assert mStaticData.get(index) == null;
+
+ final int staticDataCount = data.getCount();
+
+ for (int x = 0; x < staticDataCount; x++) {
+ BaseObject entry = data.get(x);
+ if (entry instanceof GameComponent) {
+ ((GameComponent) entry).shared = true;
+ }
+ }
+
+ mStaticData.set(index, data);
+ }
+
+ private void addStaticData(GameObjectType type, GameObject object, SpriteComponent sprite) {
+ FixedSizeArray<BaseObject> staticData = getStaticData(type);
+ assert staticData != null;
+
+ if (staticData != null) {
+ final int staticDataCount = staticData.getCount();
+
+ for (int x = 0; x < staticDataCount; x++) {
+ BaseObject entry = staticData.get(x);
+ if (entry instanceof GameComponent && object != null) {
+ object.add((GameComponent)entry);
+ } else if (entry instanceof SpriteAnimation && sprite != null) {
+ sprite.addAnimation((SpriteAnimation)entry);
+ }
+ }
+ }
+ }
+
+ public void clearStaticData() {
+ final int typeCount = mStaticData.getCount();
+ for (int x = 0; x < typeCount; x++) {
+ FixedSizeArray<BaseObject> staticData = mStaticData.get(x);
+ if (staticData != null) {
+ final int count = staticData.getCount();
+ for (int y = 0; y < count; y++) {
+ BaseObject entry = staticData.get(y);
+ if (entry != null) {
+ if (entry instanceof GameComponent) {
+ releaseComponent((GameComponent)entry);
+ }
+ }
+ }
+ staticData.clear();
+ mStaticData.set(x, null);
+ }
+ }
+ }
+
+ public void sanityCheckPools() {
+ final int outstandingObjects = mGameObjectPool.getAllocatedCount();
+ if (outstandingObjects != 0) {
+ DebugLog.d("Sanity Check", "Outstanding game object allocations! ("
+ + outstandingObjects + ")");
+ assert false;
+ }
+
+ final int componentPoolCount = mComponentPools.getCount();
+ for (int x = 0; x < componentPoolCount; x++) {
+ final int outstandingComponents = mComponentPools.get(x).getAllocatedCount();
+
+ if (outstandingComponents != 0) {
+ DebugLog.d("Sanity Check", "Outstanding "
+ + mComponentPools.get(x).objectClass.getSimpleName()
+ + " allocations! (" + outstandingComponents + ")");
+ //assert false;
+ }
+ }
+ }
+
+ public GameObject spawnPlayer(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.PLAYER);
+
+ if (staticData == null) {
+ final int staticObjectCount = 13;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+ PhysicsComponent physics = (PhysicsComponent)allocateComponent(PhysicsComponent.class);
+
+ physics.setMass(9.1f); // ~90kg w/ earth gravity
+ physics.setDynamicFrictionCoeffecient(0.2f);
+ physics.setStaticFrictionCoeffecient(0.01f);
+
+ // Animation Data
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 32, 32));
+
+ FixedSizeArray<CollisionVolume> pressAndCollectVolume =
+ new FixedSizeArray<CollisionVolume>(2);
+ AABoxCollisionVolume collectionVolume = new AABoxCollisionVolume(16, 0, 32, 48);
+ collectionVolume.setHitType(HitType.COLLECT);
+ pressAndCollectVolume.add(collectionVolume);
+ AABoxCollisionVolume pressCollisionVolume = new AABoxCollisionVolume(16, 0, 32, 16);
+ pressCollisionVolume.setHitType(HitType.DEPRESS);
+ pressAndCollectVolume.add(pressCollisionVolume);
+
+ SpriteAnimation idle = new SpriteAnimation(PlayerAnimations.IDLE.ordinal(), 1);
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_stand),
+ 1.0f, pressAndCollectVolume, basicVulnerabilityVolume));
+
+ SpriteAnimation angle = new SpriteAnimation(PlayerAnimations.MOVE.ordinal(), 1);
+ angle.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_diag01),
+ 0.0416f, pressAndCollectVolume, basicVulnerabilityVolume));
+
+ SpriteAnimation extremeAngle = new SpriteAnimation(
+ PlayerAnimations.MOVE_FAST.ordinal(), 1);
+ extremeAngle.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_diagmore01),
+ 0.0416f, pressAndCollectVolume, basicVulnerabilityVolume));
+
+ SpriteAnimation up = new SpriteAnimation(PlayerAnimations.BOOST_UP.ordinal(), 2);
+ up.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_flyup02),
+ Utils.framesToTime(24, 1), pressAndCollectVolume, basicVulnerabilityVolume));
+ up.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_flyup03),
+ Utils.framesToTime(24, 1), pressAndCollectVolume, basicVulnerabilityVolume));
+ up.setLoop(true);
+
+ SpriteAnimation boostAngle = new SpriteAnimation(PlayerAnimations.BOOST_MOVE.ordinal(), 2);
+ boostAngle.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_diag02),
+ Utils.framesToTime(24, 1), pressAndCollectVolume, basicVulnerabilityVolume));
+ boostAngle.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_diag03),
+ Utils.framesToTime(24, 1), pressAndCollectVolume, basicVulnerabilityVolume));
+ boostAngle.setLoop(true);
+
+ SpriteAnimation boostExtremeAngle = new SpriteAnimation(
+ PlayerAnimations.BOOST_MOVE_FAST.ordinal(), 2);
+ boostExtremeAngle.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_diagmore02),
+ Utils.framesToTime(24, 1), pressAndCollectVolume, basicVulnerabilityVolume));
+ boostExtremeAngle.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_diagmore03),
+ Utils.framesToTime(24, 1), pressAndCollectVolume, basicVulnerabilityVolume));
+ boostExtremeAngle.setLoop(true);
+
+ FixedSizeArray<CollisionVolume> stompAttackVolume =
+ new FixedSizeArray<CollisionVolume>(3);
+ stompAttackVolume.add(new AABoxCollisionVolume(16, -5.0f, 32, 37, HitType.HIT));
+ stompAttackVolume.add(pressCollisionVolume);
+ stompAttackVolume.add(collectionVolume);
+
+ SpriteAnimation stomp = new SpriteAnimation(PlayerAnimations.STOMP.ordinal(), 4);
+ stomp.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_stomp01),
+ Utils.framesToTime(24, 1), stompAttackVolume, null));
+ stomp.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_stomp02),
+ Utils.framesToTime(24, 1), stompAttackVolume, null));
+ stomp.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_stomp03),
+ Utils.framesToTime(24, 1), stompAttackVolume, null));
+ stomp.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_stomp04),
+ Utils.framesToTime(24, 1), stompAttackVolume, null));
+
+ SpriteAnimation hitReactAnim = new SpriteAnimation(PlayerAnimations.HIT_REACT.ordinal(), 1);
+ hitReactAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_hit),
+ 0.1f, pressAndCollectVolume, null));
+
+ SpriteAnimation deathAnim = new SpriteAnimation(PlayerAnimations.DEATH.ordinal(), 16);
+ AnimationFrame death1 =
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_die01),
+ Utils.framesToTime(24, 1), null, null);
+ AnimationFrame death2 =
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_die02),
+ Utils.framesToTime(24, 1), null, null);
+ deathAnim.addFrame(death1);
+ deathAnim.addFrame(death2);
+ deathAnim.addFrame(death1);
+ deathAnim.addFrame(death2);
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode01),
+ Utils.framesToTime(24, 1), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode02),
+ Utils.framesToTime(24, 1), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode03),
+ Utils.framesToTime(24, 1), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode04),
+ Utils.framesToTime(24, 1), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode05),
+ Utils.framesToTime(24, 2), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode06),
+ Utils.framesToTime(24, 2), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode07),
+ Utils.framesToTime(24, 2), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode08),
+ Utils.framesToTime(24, 2), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode09),
+ Utils.framesToTime(24, 2), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode10),
+ Utils.framesToTime(24, 2), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode11),
+ Utils.framesToTime(24, 2), null, null));
+ deathAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode12),
+ Utils.framesToTime(24, 2), null, null));
+
+
+ SpriteAnimation frozenAnim = new SpriteAnimation(PlayerAnimations.FROZEN.ordinal(), 1);
+ // Frozen has no frames!
+
+
+ // Save static data
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+
+
+ staticData.add(idle);
+ staticData.add(angle);
+ staticData.add(extremeAngle);
+ staticData.add(up);
+ staticData.add(boostAngle);
+ staticData.add(boostExtremeAngle);
+ staticData.add(stomp);
+ staticData.add(hitReactAnim);
+ staticData.add(deathAnim);
+ staticData.add(frozenAnim);
+
+ setStaticData(GameObjectType.PLAYER, staticData);
+ }
+
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.PLAYER);
+ BackgroundCollisionComponent bgcollision
+ = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(32, 48);
+ bgcollision.setOffset(16, 0);
+ PlayerComponent player = (PlayerComponent)allocateComponent(PlayerComponent.class);
+ AnimationComponent animation =
+ (AnimationComponent)allocateComponent(AnimationComponent.class);
+
+ animation.setPlayer(player);
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ animation.setLandThump(sound.load(R.raw.thump));
+ animation.setRocketSound(sound.load(R.raw.rockets));
+ animation.setRubySounds(sound.load(R.raw.gem1), sound.load(R.raw.gem2), sound.load(R.raw.gem3));
+ animation.setExplosionSound(sound.load(R.raw.sound_explode));
+ }
+
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+ animation.setSprite(sprite);
+
+
+
+ DynamicCollisionComponent dynamicCollision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setBounceOnHit(true);
+ hitReact.setPauseOnAttack(true);
+ hitReact.setInvincibleTime(3.0f);
+ hitReact.setSpawnOnDealHit(HitType.HIT, GameObjectType.CRUSH_FLASH, false, true);
+
+ if (sound != null) {
+ hitReact.setTakeHitSound(HitType.HIT, sound.load(R.raw.deep_clang));
+ }
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ player.setHitReactionComponent(hitReact);
+
+ InventoryComponent inventory = (InventoryComponent)allocateComponent(InventoryComponent.class);
+
+ player.setInventory(inventory);
+ animation.setInventory(inventory);
+
+ ChangeComponentsComponent damageSwap = (ChangeComponentsComponent)allocateComponent(ChangeComponentsComponent.class);
+ animation.setDamageSwap(damageSwap);
+
+ LaunchProjectileComponent smokeGun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ smokeGun.setDelayBetweenShots(0.25f);
+ smokeGun.setObjectTypeToSpawn(GameObjectType.SMOKE_BIG);
+ smokeGun.setOffsetX(32);
+ smokeGun.setOffsetY(15);
+ smokeGun.setVelocityX(-150.0f);
+ smokeGun.setVelocityY(100.0f);
+ smokeGun.setThetaError(0.1f);
+
+ LaunchProjectileComponent smokeGun2
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ smokeGun2.setDelayBetweenShots(0.35f);
+ smokeGun2.setObjectTypeToSpawn(GameObjectType.SMOKE_SMALL);
+ smokeGun2.setOffsetX(16);
+ smokeGun2.setOffsetY(15);
+ smokeGun2.setVelocityX(-150.0f);
+ smokeGun2.setVelocityY(150.0f);
+ smokeGun2.setThetaError(0.1f);
+
+ damageSwap.addSwapInComponent(smokeGun);
+ damageSwap.addSwapInComponent(smokeGun2);
+ damageSwap.setPingPongBehavior(true);
+
+ ChangeComponentsComponent invincibleSwap = (ChangeComponentsComponent)allocateComponent(ChangeComponentsComponent.class);
+ invincibleSwap.setPingPongBehavior(true);
+ player.setInvincibleSwap(invincibleSwap);
+
+ object.life = PlayerComponent.MAX_PLAYER_LIFE;
+ object.team = Team.PLAYER;
+
+ // Very very basic DDA. Make the game easier if we've died on this level too much.
+ LevelSystem level = sSystemRegistry.levelSystem;
+ if (level != null) {
+ player.adjustDifficulty(object, level.getAttemptsCount());
+ }
+
+
+ object.add(player);
+ object.add(inventory);
+ object.add(bgcollision);
+ object.add(render);
+ object.add(animation);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+ object.add(damageSwap);
+ object.add(invincibleSwap);
+
+ addStaticData(GameObjectType.PLAYER, object, sprite);
+
+
+
+ sprite.playAnimation(PlayerAnimations.IDLE.ordinal());
+
+
+ // Jets
+ {
+ FixedSizeArray<BaseObject> jetStaticData = getStaticData(GameObjectType.PLAYER_JETS);
+ if (jetStaticData == null) {
+ jetStaticData = new FixedSizeArray<BaseObject>(1);
+
+ SpriteAnimation jetAnim = new SpriteAnimation(0, 2);
+ jetAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.jetfire01),
+ Utils.framesToTime(24, 1)));
+ jetAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.jetfire02),
+ Utils.framesToTime(24, 1)));
+ jetAnim.setLoop(true);
+
+ jetStaticData.add(jetAnim);
+
+ setStaticData(GameObjectType.PLAYER_JETS, jetStaticData);
+ }
+
+ RenderComponent jetRender = (RenderComponent)allocateComponent(RenderComponent.class);
+ jetRender.setPriority(SortConstants.PLAYER - 1);
+ jetRender.setDrawOffset(0.0f, -16.0f);
+ SpriteComponent jetSprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ jetSprite.setSize(64, 64);
+ jetSprite.setRenderComponent(jetRender);
+
+ object.add(jetRender);
+ object.add(jetSprite);
+
+ addStaticData(GameObjectType.PLAYER_JETS, object, jetSprite);
+
+ jetSprite.playAnimation(0);
+
+ animation.setJetSprite(jetSprite);
+ }
+ // Sparks
+ {
+ FixedSizeArray<BaseObject> sparksStaticData = getStaticData(GameObjectType.PLAYER_SPARKS);
+
+ if (sparksStaticData == null) {
+ sparksStaticData = new FixedSizeArray<BaseObject>(1);
+
+ SpriteAnimation sparksAnim = new SpriteAnimation(0, 3);
+ sparksAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.spark01),
+ Utils.framesToTime(24, 1)));
+ sparksAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.spark02),
+ Utils.framesToTime(24, 1)));
+ sparksAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.spark03),
+ Utils.framesToTime(24, 1)));
+ sparksAnim.setLoop(true);
+
+ sparksStaticData.add(sparksAnim);
+
+ setStaticData(GameObjectType.PLAYER_SPARKS, sparksStaticData);
+ }
+
+ RenderComponent sparksRender = (RenderComponent)allocateComponent(RenderComponent.class);
+ sparksRender.setPriority(SortConstants.PLAYER + 1);
+ SpriteComponent sparksSprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sparksSprite.setSize(64, 64);
+ sparksSprite.setRenderComponent(sparksRender);
+
+ object.add(sparksRender);
+ object.add(sparksSprite);
+
+ addStaticData(GameObjectType.PLAYER_SPARKS, object, sparksSprite);
+
+ sparksSprite.playAnimation(0);
+
+ animation.setSparksSprite(sparksSprite);
+ }
+
+ // Glow
+ {
+ FixedSizeArray<BaseObject> glowStaticData = getStaticData(GameObjectType.PLAYER_GLOW);
+ if (glowStaticData == null) {
+ glowStaticData = new FixedSizeArray<BaseObject>(1);
+
+ FixedSizeArray<CollisionVolume> glowAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ glowAttackVolume.add(new SphereCollisionVolume(40, 40, 40, HitType.HIT));
+
+ SpriteAnimation glowAnim = new SpriteAnimation(0, 3);
+ glowAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.effect_glow01),
+ Utils.framesToTime(24, 1), glowAttackVolume, null));
+ glowAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.effect_glow02),
+ Utils.framesToTime(24, 1), glowAttackVolume, null));
+ glowAnim.addFrame(
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.effect_glow03),
+ Utils.framesToTime(24, 1), glowAttackVolume, null));
+ glowAnim.setLoop(true);
+
+ glowStaticData.add(glowAnim);
+
+ setStaticData(GameObjectType.PLAYER_GLOW, glowStaticData);
+ }
+
+ RenderComponent glowRender = (RenderComponent)allocateComponent(RenderComponent.class);
+ glowRender.setPriority(SortConstants.PLAYER + 1);
+ glowRender.setDrawOffset(0, -5.0f);
+ SpriteComponent glowSprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ glowSprite.setSize(64, 64);
+ glowSprite.setRenderComponent(glowRender);
+
+ DynamicCollisionComponent glowCollision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ glowSprite.setCollisionComponent(glowCollision);
+
+ FadeDrawableComponent glowFade = (FadeDrawableComponent)allocateComponent(FadeDrawableComponent.class);
+ glowFade.setupFade(1.0f, 0.0f, 0.15f,
+ FadeDrawableComponent.LOOP_TYPE_PING_PONG,
+ FadeDrawableComponent.FADE_EASE,
+ PlayerComponent.GLOW_DURATION - 4.0f); // 4 seconds before the glow ends, start flashing
+ glowFade.setPhaseDuration(PlayerComponent.GLOW_DURATION);
+ glowFade.setRenderComponent(glowRender);
+
+ invincibleSwap.addSwapInComponent(glowRender);
+ invincibleSwap.addSwapInComponent(glowSprite);
+ invincibleSwap.addSwapInComponent(glowCollision);
+ invincibleSwap.addSwapInComponent(glowFade);
+
+ addStaticData(GameObjectType.PLAYER_GLOW, object, glowSprite);
+
+ glowSprite.playAnimation(0);
+
+ }
+
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ if (camera != null) {
+ camera.setTarget(object);
+ }
+
+ return object;
+ }
+
+
+ // Sparks are used by more than one enemy type, so the setup for them is abstracted.
+ private void setupEnemySparks() {
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.ENEMY_SPARKS);
+ if (staticData == null) {
+ staticData = new FixedSizeArray<BaseObject>(1);
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ SpriteAnimation sparksAnim = new SpriteAnimation(0, 13);
+ AnimationFrame frame1 =
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.spark01),
+ Utils.framesToTime(24, 1));
+ AnimationFrame frame2 =
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.spark02),
+ Utils.framesToTime(24, 1));
+ AnimationFrame frame3 =
+ new AnimationFrame(textureLibrary.allocateTexture(R.drawable.spark03),
+ Utils.framesToTime(24, 1));
+ sparksAnim.addFrame(frame1);
+ sparksAnim.addFrame(frame2);
+ sparksAnim.addFrame(frame3);
+ sparksAnim.addFrame(frame1);
+ sparksAnim.addFrame(frame2);
+ sparksAnim.addFrame(frame3);
+ sparksAnim.addFrame(frame1);
+ sparksAnim.addFrame(frame2);
+ sparksAnim.addFrame(frame3);
+ sparksAnim.addFrame(frame1);
+ sparksAnim.addFrame(frame2);
+ sparksAnim.addFrame(frame3);
+ sparksAnim.addFrame(new AnimationFrame(null, 3.0f));
+ sparksAnim.setLoop(true);
+
+ staticData.add(sparksAnim);
+ setStaticData(GameObjectType.ENEMY_SPARKS, staticData);
+ }
+
+ }
+
+ public GameObject spawnEnemyBrobot(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.BROBOT);
+ if (staticData == null) {
+ final int staticObjectCount = 5;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.4f);
+
+
+ // Animations
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 32, 32));
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(2);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 32, 32, HitType.HIT));
+ basicAttackVolume.add(new AABoxCollisionVolume(16, 0, 32, 16, HitType.DEPRESS));
+
+ SpriteAnimation idle = new SpriteAnimation(EnemyAnimations.IDLE.ordinal(), 4);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_idle01),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_idle02),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_idle03),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_idle02),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume));
+
+ idle.setLoop(true);
+
+ SpriteAnimation walk = new SpriteAnimation(EnemyAnimations.MOVE.ordinal(), 3);
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk01),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk02),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk03),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ walk.setLoop(true);
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(idle);
+ staticData.add(walk);
+
+ setStaticData(GameObjectType.BROBOT, staticData);
+
+ }
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+ BackgroundCollisionComponent bgcollision
+ = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(32, 48);
+ bgcollision.setOffset(16, 0);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ EnemyAnimationComponent animation
+ = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ PatrolComponent patrol = (PatrolComponent)allocateComponent(PatrolComponent.class);
+ patrol.setMovementSpeed(50.0f, 1000.0f);
+
+ DynamicCollisionComponent collision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact
+ = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.EXPLOSION_GIANT);
+ lifetime.setVulnerableToDeathTiles(true);
+
+ GhostComponent ghost = (GhostComponent)allocateComponent(GhostComponent.class);
+ ghost.setMovementSpeed(500.0f);
+ ghost.setAcceleration(1000.0f);
+ ghost.setJumpImpulse(300.0f);
+ ghost.setKillOnRelease(true);
+ ghost.setDelayOnRelease(1.5f);
+
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ ghost.setAmbientSound(sound.load(R.raw.sound_possession));
+ }
+
+ ChangeComponentsComponent ghostSwap
+ = (ChangeComponentsComponent)allocateComponent(ChangeComponentsComponent.class);
+ ghostSwap.addSwapInComponent(ghost);
+ ghostSwap.addSwapOutComponent(patrol);
+
+ SimplePhysicsComponent ghostPhysics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ ghostPhysics.setBounciness(0.0f);
+
+ object.add(render);
+ object.add(sprite);
+
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+ object.add(ghostSwap);
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ addStaticData(GameObjectType.BROBOT, object, sprite);
+
+ object.commitUpdates();
+
+ SimplePhysicsComponent normalPhysics = object.findByClass(SimplePhysicsComponent.class);
+ if (normalPhysics != null) {
+ ghostSwap.addSwapOutComponent(normalPhysics);
+ }
+
+ ghostSwap.addSwapInComponent(ghostPhysics);
+
+ sprite.playAnimation(0);
+
+ // Sparks
+ setupEnemySparks();
+
+ RenderComponent sparksRender = (RenderComponent)allocateComponent(RenderComponent.class);
+ sparksRender.setPriority(render.getPriority() + 1);
+ SpriteComponent sparksSprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sparksSprite.setSize(64, 64);
+ sparksSprite.setRenderComponent(sparksRender);
+
+ addStaticData(GameObjectType.ENEMY_SPARKS, object, sparksSprite);
+
+ sparksSprite.playAnimation(0);
+
+ ghostSwap.addSwapInComponent(sparksSprite);
+ ghostSwap.addSwapInComponent(sparksRender);
+
+
+ hitReact.setPossessionComponent(ghostSwap);
+
+ return object;
+ }
+
+ public GameObject spawnEnemySnailBomb(float positionX, float positionY, boolean flipHorizontal) {
+
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ // Make sure related textures are loaded.
+ textureLibrary.allocateTexture(R.drawable.snail_bomb);
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.SNAILBOMB);
+ if (staticData == null) {
+ final int staticObjectCount = 6;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+ GameComponent physics = allocateComponent(SimplePhysicsComponent.class);
+
+ // Animations
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(12, 5, 42, 27, HitType.HIT));
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new AABoxCollisionVolume(12, 5, 42, 27, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(EnemyAnimations.IDLE.ordinal(), 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.snailbomb_stand),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume));
+
+ SpriteAnimation walk = new SpriteAnimation(EnemyAnimations.MOVE.ordinal(), 5);
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.snailbomb_stand),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.snailbomb_walk01),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.snailbomb_walk02),
+ Utils.framesToTime(24, 6), basicAttackVolume, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.snailbomb_walk01),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.snailbomb_stand),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume));
+ walk.setLoop(true);
+
+ SpriteAnimation attack = new SpriteAnimation(EnemyAnimations.ATTACK.ordinal(), 2);
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.snailbomb_shoot01),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.snailbomb_shoot02),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume));
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(idle);
+ staticData.add(walk);
+ staticData.add(attack);
+
+
+ setStaticData(GameObjectType.SNAILBOMB, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+ BackgroundCollisionComponent bgcollision
+ = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(32, 48);
+ bgcollision.setOffset(16, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ EnemyAnimationComponent animation
+ = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ PatrolComponent patrol = (PatrolComponent)allocateComponent(PatrolComponent.class);
+ patrol.setMovementSpeed(20.0f, 1000.0f);
+ patrol.setupAttack(300, 1.0f, 4.0f, true);
+
+ DynamicCollisionComponent collision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact
+ = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setVulnerableToDeathTiles(true);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.SMOKE_POOF);
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ lifetime.setDeathSound(sound.load(R.raw.sound_stomp));
+ }
+
+ LaunchProjectileComponent gun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ gun.setSetsPerActivation(1);
+ gun.setShotsPerSet(3);
+ gun.setDelayBeforeFirstSet(1.0f);
+ gun.setDelayBetweenShots(0.25f);
+ gun.setObjectTypeToSpawn(GameObjectType.CANNON_BALL);
+ gun.setOffsetX(55);
+ gun.setOffsetY(21);
+ gun.setRequiredAction(GameObject.ActionType.ATTACK);
+ gun.setVelocityX(100.0f);
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+ object.add(gun);
+
+ addStaticData(GameObjectType.SNAILBOMB, object, sprite);
+
+ final SpriteAnimation attack = sprite.findAnimation(EnemyAnimations.ATTACK.ordinal());
+ if (attack != null) {
+ gun.setDelayBeforeFirstSet(attack.getLength());
+ }
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyShadowSlime(float positionX, float positionY, boolean flipHorizontal) {
+
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ // Make sure related textures are loaded.
+ textureLibrary.allocateTexture(R.drawable.energy_ball01);
+ textureLibrary.allocateTexture(R.drawable.energy_ball02);
+ textureLibrary.allocateTexture(R.drawable.energy_ball03);
+ textureLibrary.allocateTexture(R.drawable.energy_ball04);
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.SHADOWSLIME);
+ if (staticData == null) {
+ final int staticObjectCount = 5;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ PopOutComponent popOut = (PopOutComponent)allocateComponent(PopOutComponent.class);
+ popOut.setAppearDistance(150);
+ popOut.setHideDistance(190);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume = new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 32, 32));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.HIT);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume = new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 32, 32, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(EnemyAnimations.IDLE.ordinal(), 2);
+ AnimationFrame idle1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_idle01),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame idle2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_idle02),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume);
+ idle.addFrame(idle1);
+ idle.addFrame(idle2);
+ idle.setLoop(true);
+
+
+ SpriteAnimation appear = new SpriteAnimation(EnemyAnimations.APPEAR.ordinal(), 6);
+ AnimationFrame appear1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_activate01),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume);
+
+ AnimationFrame appear2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_activate02),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame appear3 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_activate03),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame appear4 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_activate04),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame appear5 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_activate05),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame appear6 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_activate06),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume);
+
+ appear.addFrame(appear1);
+ appear.addFrame(appear2);
+ appear.addFrame(appear3);
+ appear.addFrame(appear4);
+ appear.addFrame(appear5);
+ appear.addFrame(appear6);
+
+ SpriteAnimation hidden = new SpriteAnimation(EnemyAnimations.HIDDEN.ordinal(), 6);
+ hidden.addFrame(appear6);
+ hidden.addFrame(appear5);
+ hidden.addFrame(appear4);
+ hidden.addFrame(appear3);
+ hidden.addFrame(appear2);
+ hidden.addFrame(appear1);
+ /*hidden.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_stand),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume));*/
+
+
+ SpriteAnimation attack = new SpriteAnimation(EnemyAnimations.ATTACK.ordinal(), 10);
+ AnimationFrame attack1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_attack01),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame attack2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_attack02),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame attack3 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_attack03),
+ Utils.framesToTime(24, 2), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame attack4 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_attack04),
+ Utils.framesToTime(24, 6), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame attackFlash = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_flash),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume);
+
+ AnimationFrame attack5 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_attack03),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume);
+ AnimationFrame attack6 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_attack02),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume);
+
+ AnimationFrame attack7 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_shadowslime_attack04),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume);
+
+ attack.addFrame(attack1);
+ attack.addFrame(attack2);
+ attack.addFrame(attack3);
+ attack.addFrame(attack4);
+ attack.addFrame(attackFlash);
+ attack.addFrame(attack7);
+ attack.addFrame(attackFlash);
+ attack.addFrame(attack5);
+ attack.addFrame(attack6);
+ attack.addFrame(attack1);
+
+ popOut.setupAttack(200, 2.0f, attack.getLength());
+
+
+ staticData.add(popOut);
+ staticData.add(idle);
+ staticData.add(hidden);
+ staticData.add(appear);
+ staticData.add(attack);
+
+ setStaticData(GameObjectType.SHADOWSLIME, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+ BackgroundCollisionComponent bgcollision
+ = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(32, 48);
+ bgcollision.setOffset(16, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+
+ sprite.playAnimation(0);
+
+ EnemyAnimationComponent animation
+ = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+ animation.setFacePlayer(true);
+
+
+ DynamicCollisionComponent collision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact
+ = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.SMOKE_POOF);
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ lifetime.setDeathSound(sound.load(R.raw.sound_stomp));
+ }
+
+ LaunchProjectileComponent gun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+
+
+ gun.setShotsPerSet(1);
+ gun.setSetsPerActivation(1);
+ gun.setObjectTypeToSpawn(GameObjectType.ENERGY_BALL);
+ gun.setOffsetX(44);
+ gun.setOffsetY(22);
+ gun.setRequiredAction(GameObject.ActionType.ATTACK);
+ gun.setVelocityX(30.0f);
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ // Hack. Adjusting position lets us avoid giving this character gravity, physics, and
+ // collision.
+
+ object.getPosition().y -= 5;
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+ object.add(gun);
+
+ addStaticData(GameObjectType.SHADOWSLIME, object, sprite);
+
+ final SpriteAnimation attack = sprite.findAnimation(EnemyAnimations.ATTACK.ordinal());
+ final SpriteAnimation appear = sprite.findAnimation(EnemyAnimations.APPEAR.ordinal());
+ if (attack != null && appear != null) {
+ gun.setDelayBeforeFirstSet(attack.getLength() / 2.0f);
+ } else {
+ gun.setDelayBeforeFirstSet(Utils.framesToTime(24, 12));
+ }
+
+ return object;
+ }
+
+ public GameObject spawnEnemyMudman(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 128;
+ object.height = 128;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.MUDMAN);
+ if (staticData == null) {
+ final int staticObjectCount = 7;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ GameComponent physics = allocateComponent(SimplePhysicsComponent.class);
+
+ SolidSurfaceComponent solidSurface
+ = (SolidSurfaceComponent)allocateComponent(SolidSurfaceComponent.class);
+ solidSurface.inititalize(4);
+ // house shape:
+ // / \ 1/ \2
+ // | | 3| |4
+ Vector2 surface1Start = new Vector2(32, 64);
+ Vector2 surface1End = new Vector2(64, 96);
+ Vector2 surface1Normal = new Vector2(-0.707f, 0.707f);
+ surface1Normal.normalize();
+
+ Vector2 surface2Start = new Vector2(64, 96);
+ Vector2 surface2End = new Vector2(75, 64);
+ Vector2 surface2Normal = new Vector2(0.9456f, 0.3250f);
+ surface2Normal.normalize();
+
+ Vector2 surface3Start = new Vector2(32, 0);
+ Vector2 surface3End = new Vector2(32, 64);
+ Vector2 surface3Normal = new Vector2(-1, 0);
+
+ Vector2 surface4Start = new Vector2(75, 0);
+ Vector2 surface4End = new Vector2(75, 64);
+ Vector2 surface4Normal = new Vector2(1, 0);
+
+ solidSurface.addSurface(surface1Start, surface1End, surface1Normal);
+ solidSurface.addSurface(surface2Start, surface2End, surface2Normal);
+ solidSurface.addSurface(surface3Start, surface3End, surface3Normal);
+ solidSurface.addSurface(surface4Start, surface4End, surface4Normal);
+
+ SpriteAnimation idle = new SpriteAnimation(EnemyAnimations.IDLE.ordinal(), 4);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_stand),
+ Utils.framesToTime(24, 12), null, null));
+ AnimationFrame idle1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_idle01),
+ Utils.framesToTime(24, 2), null, null);
+ AnimationFrame idle2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_idle01),
+ Utils.framesToTime(24, 7), null, null);
+ idle.addFrame(idle1);
+ idle.addFrame(idle2);
+ idle.addFrame(idle1);
+ idle.setLoop(true);
+
+
+ SpriteAnimation walk = new SpriteAnimation(EnemyAnimations.MOVE.ordinal(), 6);
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_walk01),
+ Utils.framesToTime(24, 4), null, null));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_walk02),
+ Utils.framesToTime(24, 4), null, null));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_walk03),
+ Utils.framesToTime(24, 5), null, null));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_walk04),
+ Utils.framesToTime(24, 4), null, null));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_walk05),
+ Utils.framesToTime(24, 4), null, null));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_walk06),
+ Utils.framesToTime(24, 5), null, null));
+ walk.setLoop(true);
+
+ FixedSizeArray<CollisionVolume> crushAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ crushAttackVolume.add(new AABoxCollisionVolume(64, 0, 64, 96, HitType.HIT));
+
+ SpriteAnimation attack = new SpriteAnimation(EnemyAnimations.ATTACK.ordinal(), 8);
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_stand),
+ Utils.framesToTime(24, 2), null, null));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_attack01),
+ Utils.framesToTime(24, 2), null, null));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_attack02),
+ Utils.framesToTime(24, 2), null, null));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_attack03),
+ Utils.framesToTime(24, 2), null, null));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_attack04),
+ Utils.framesToTime(24, 1), crushAttackVolume, null));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_attack05),
+ Utils.framesToTime(24, 1), crushAttackVolume, null));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_attack06),
+ Utils.framesToTime(24, 8), crushAttackVolume, null));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_mud_attack07),
+ Utils.framesToTime(24, 5), null, null));
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(solidSurface);
+ staticData.add(idle);
+ staticData.add(walk);
+ staticData.add(attack);
+
+ setStaticData(GameObjectType.MUDMAN, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(80, 90);
+ bgcollision.setOffset(32, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+
+ sprite.playAnimation(0);
+
+ EnemyAnimationComponent animation = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ PatrolComponent patrol = (PatrolComponent)allocateComponent(PatrolComponent.class);
+ patrol.setMovementSpeed(20.0f, 400.0f);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+
+ object.team = Team.ENEMY;
+ object.life = 1;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+
+ addStaticData(GameObjectType.MUDMAN, object, sprite);
+
+ final SpriteAnimation attack = sprite.findAnimation(EnemyAnimations.ATTACK.ordinal());
+ if (attack != null) {
+ patrol.setupAttack(70.0f, attack.getLength(), 0.0f, true);
+ }
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemySkeleton(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.SKELETON);
+ if (staticData == null) {
+ final int staticObjectCount = 7;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ GameComponent physics = allocateComponent(SimplePhysicsComponent.class);
+
+ SolidSurfaceComponent solidSurface = (SolidSurfaceComponent)allocateComponent(SolidSurfaceComponent.class);
+ solidSurface.inititalize(4);
+
+ Vector2 surface1Start = new Vector2(25, 0);
+ Vector2 surface1End = new Vector2(25, 64);
+ Vector2 surface1Normal = new Vector2(-1, 0);
+
+ Vector2 surface2Start = new Vector2(40, 0);
+ Vector2 surface2End = new Vector2(40, 64);
+ Vector2 surface2Normal = new Vector2(1, 0);
+
+ solidSurface.addSurface(surface1Start, surface1End, surface1Normal);
+ solidSurface.addSurface(surface2Start, surface2End, surface2Normal);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 32, 32));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.HIT);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 48, 32, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(EnemyAnimations.IDLE.ordinal(), 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_stand),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+
+ SpriteAnimation walk = new SpriteAnimation(EnemyAnimations.MOVE.ordinal(), 6);
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_walk01),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_walk02),
+ Utils.framesToTime(24, 4), null, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_walk03),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_walk04),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_walk05),
+ Utils.framesToTime(24, 4), null, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_walk03),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume));
+
+ walk.setLoop(true);
+
+
+ SpriteAnimation attack = new SpriteAnimation(EnemyAnimations.ATTACK.ordinal(), 3);
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_attack01),
+ Utils.framesToTime(24, 5), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_attack03),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_skeleton_attack04),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(solidSurface);
+ staticData.add(idle);
+ staticData.add(walk);
+ staticData.add(attack);
+
+ setStaticData(GameObjectType.SKELETON, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(32, 48);
+ bgcollision.setOffset(16, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ EnemyAnimationComponent animation = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ PatrolComponent patrol = (PatrolComponent)allocateComponent(PatrolComponent.class);
+ patrol.setMovementSpeed(20.0f, 1000.0f);
+ patrol.setTurnToFacePlayer(true);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setVulnerableToDeathTiles(true);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.SMOKE_POOF);
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ lifetime.setDeathSound(sound.load(R.raw.sound_stomp));
+ }
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+
+ addStaticData(GameObjectType.SKELETON, object, sprite);
+
+ final SpriteAnimation attack = sprite.findAnimation(EnemyAnimations.ATTACK.ordinal());
+ if (attack != null) {
+ patrol.setupAttack(75.0f, attack.getLength(), 2.0f, true);
+ }
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+
+ public GameObject spawnEnemyKaraguin(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.KARAGUIN);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(8, 16, 16));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.HIT);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(8, 16, 16, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 3);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_karaguin01),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_karaguin02),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_karaguin03),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.setLoop(true);
+
+ staticData.add(movement);
+ staticData.add(idle);
+
+
+ setStaticData(GameObjectType.KARAGUIN, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ PatrolComponent patrol = (PatrolComponent)allocateComponent(PatrolComponent.class);
+ patrol.setMovementSpeed(50.0f, 1000.0f);
+ patrol.setTurnToFacePlayer(false);
+ patrol.setFlying(true);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.SMOKE_POOF);
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ lifetime.setDeathSound(sound.load(R.raw.sound_stomp));
+ }
+
+ EnemyAnimationComponent animation = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+ // HACK. These guys originally moved on their own, so let's keep them that way.
+ object.getVelocity().x = 50.0f * object.facingDirection.x;
+ object.getTargetVelocity().x = 50.0f * object.facingDirection.x;
+
+ object.add(render);
+ object.add(animation);
+ object.add(sprite);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+
+ addStaticData(GameObjectType.KARAGUIN, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyPinkNamazu(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 128;
+ object.height = 128;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.PINK_NAMAZU);
+ if (staticData == null) {
+ final int staticObjectCount = 7;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ GameComponent physics = allocateComponent(SimplePhysicsComponent.class);
+
+ SolidSurfaceComponent solidSurface
+ = (SolidSurfaceComponent)allocateComponent(SolidSurfaceComponent.class);
+ solidSurface.inititalize(5);
+ // circle shape:
+ // __ __3
+ // / \ 2/ \4
+ // | | 1| |5
+ /*
+ 0:12,6:22,52:0.98058067569092,-0.19611613513818
+ 0:22,52:50,75:-0.62580046626293,0.77998318983495
+ 0:50,75:81,75:0,1
+ 0:81,75:104,49:0.74038072228541,0.67218776102228
+ 0:104,49:104,6:-0.99997086544204,-0.00763336538505
+ */
+ Vector2 surface1Start = new Vector2(12, 3);
+ Vector2 surface1End = new Vector2(22, 52);
+ Vector2 surface1Normal = new Vector2(-0.98058067569092f, -0.19611613513818f);
+ surface1Normal.normalize();
+
+ Vector2 surface2Start = new Vector2(22, 52);
+ Vector2 surface2End = new Vector2(50, 75);
+ Vector2 surface2Normal = new Vector2(-0.62580046626293f, 0.77998318983495f);
+ surface2Normal.normalize();
+
+ Vector2 surface3Start = new Vector2(50, 75);
+ Vector2 surface3End = new Vector2(81, 75);
+ Vector2 surface3Normal = new Vector2(0, 1);
+
+ Vector2 surface4Start = new Vector2(81, 75);
+ Vector2 surface4End = new Vector2(104,49);
+ Vector2 surface4Normal = new Vector2(0.74038072228541f, 0.67218776102228f);
+
+ Vector2 surface5Start = new Vector2(104,49);
+ Vector2 surface5End = new Vector2(104, 3);
+ Vector2 surface5Normal = new Vector2(1.0f, 0.0f);
+
+ solidSurface.addSurface(surface1Start, surface1End, surface1Normal);
+ solidSurface.addSurface(surface2Start, surface2End, surface2Normal);
+ solidSurface.addSurface(surface3Start, surface3End, surface3Normal);
+ solidSurface.addSurface(surface4Start, surface4End, surface4Normal);
+ solidSurface.addSurface(surface5Start, surface5End, surface5Normal);
+
+
+ SpriteAnimation idle = new SpriteAnimation(GenericAnimationComponent.Animation.IDLE, 4);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_pinkdude_stand),
+ Utils.framesToTime(24, 8), null, null));
+ AnimationFrame idle1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_pinkdude_sleep01),
+ Utils.framesToTime(24, 3), null, null);
+ AnimationFrame idle2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_pinkdude_sleep02),
+ Utils.framesToTime(24, 8), null, null);
+ idle.addFrame(idle1);
+ idle.addFrame(idle2);
+ idle.addFrame(idle1);
+ idle.setLoop(true);
+
+
+ SpriteAnimation wake = new SpriteAnimation(GenericAnimationComponent.Animation.MOVE, 4);
+ AnimationFrame wake1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_pinkdude_eyeopen),
+ Utils.framesToTime(24, 3), null, null);
+ AnimationFrame wake2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_pinkdude_stand),
+ Utils.framesToTime(24, 3), null, null);
+ wake.addFrame(wake1);
+ wake.addFrame(wake2);
+ wake.addFrame(wake1);
+ wake.addFrame(wake2);
+
+ FixedSizeArray<CollisionVolume> crushAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ crushAttackVolume.add(new AABoxCollisionVolume(32, 0, 64, 32, HitType.HIT));
+
+ SpriteAnimation attack = new SpriteAnimation(GenericAnimationComponent.Animation.ATTACK, 1);
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_pinkdude_jump),
+ Utils.framesToTime(24, 2), crushAttackVolume, null));
+
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(solidSurface);
+ staticData.add(idle);
+ staticData.add(wake);
+ staticData.add(attack);
+
+ setStaticData(GameObjectType.PINK_NAMAZU, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(100, 75);
+ bgcollision.setOffset(12, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ GenericAnimationComponent animation =
+ (GenericAnimationComponent)allocateComponent(GenericAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ SleeperComponent sleeper = (SleeperComponent)allocateComponent(SleeperComponent.class);
+ sleeper.setAttackImpulse(100.0f, 170.0f);
+ sleeper.setSlam(0.3f, 25.0f);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+
+ object.team = Team.ENEMY;
+ object.life = 1;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(sleeper);
+
+
+ addStaticData(GameObjectType.PINK_NAMAZU, object, sprite);
+
+ final SpriteAnimation wakeUp = sprite.findAnimation(GenericAnimationComponent.Animation.MOVE);
+ if (wakeUp != null) {
+ sleeper.setWakeUpDuration(wakeUp.getLength() + 1.0f);
+ }
+
+ sprite.playAnimation(GenericAnimationComponent.Animation.IDLE);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyBat(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 64;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.BAT);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 32, 16));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.HIT);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 32, 16, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 4);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_bat01),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_bat02),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_bat03),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_bat04),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.setLoop(true);
+
+ staticData.add(movement);
+ staticData.add(idle);
+
+
+ setStaticData(GameObjectType.BAT, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ PatrolComponent patrol = (PatrolComponent)allocateComponent(PatrolComponent.class);
+ patrol.setMovementSpeed(75.0f, 1000.0f);
+ patrol.setTurnToFacePlayer(false);
+ patrol.setFlying(true);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.SMOKE_POOF);
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ lifetime.setDeathSound(sound.load(R.raw.sound_stomp));
+ }
+
+ EnemyAnimationComponent animation = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ // HACK. These guys originally moved on their own, so let's keep them that way.
+ object.getVelocity().x = 75.0f * object.facingDirection.x;
+ object.getTargetVelocity().x = 75.0f * object.facingDirection.x;
+
+ object.add(render);
+ object.add(animation);
+ object.add(sprite);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+
+ addStaticData(GameObjectType.BAT, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemySting(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.STING);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 32, 16));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.HIT);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 32, 16, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 3);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_sting01),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_sting02),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_sting03),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+
+ idle.setLoop(true);
+
+ staticData.add(movement);
+ staticData.add(idle);
+
+
+ setStaticData(GameObjectType.STING, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ PatrolComponent patrol = (PatrolComponent)allocateComponent(PatrolComponent.class);
+ patrol.setMovementSpeed(75.0f, 1000.0f);
+ patrol.setTurnToFacePlayer(false);
+ patrol.setFlying(true);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.SMOKE_POOF);
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ lifetime.setDeathSound(sound.load(R.raw.sound_stomp));
+ }
+
+ EnemyAnimationComponent animation = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ // HACK. These guys originally moved on their own, so let's keep them that way.
+ object.getVelocity().x = 25.0f * object.facingDirection.x;
+ object.getTargetVelocity().x = 25.0f * object.facingDirection.x;
+
+ object.add(render);
+ object.add(animation);
+ object.add(sprite);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+
+ addStaticData(GameObjectType.STING, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyOnion(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.ONION);
+ if (staticData == null) {
+ final int staticObjectCount = 5;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.2f);
+
+
+ // Animations
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 32, 32));
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 32, 32, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(EnemyAnimations.IDLE.ordinal(), 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_onion01),
+ Utils.framesToTime(24, 3), basicAttackVolume, basicVulnerabilityVolume));
+
+ idle.setLoop(true);
+
+ SpriteAnimation walk = new SpriteAnimation(EnemyAnimations.MOVE.ordinal(), 3);
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_onion01),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_onion02),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ walk.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_onion03),
+ Utils.framesToTime(24, 1), basicAttackVolume, basicVulnerabilityVolume));
+ walk.setLoop(true);
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(idle);
+ staticData.add(walk);
+
+ setStaticData(GameObjectType.ONION, staticData);
+
+ }
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_ENEMY);
+
+ BackgroundCollisionComponent bgcollision
+ = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(32, 48);
+ bgcollision.setOffset(16, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ EnemyAnimationComponent animation
+ = (EnemyAnimationComponent)allocateComponent(EnemyAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ PatrolComponent patrol = (PatrolComponent)allocateComponent(PatrolComponent.class);
+ patrol.setMovementSpeed(50.0f, 1000.0f);
+
+ DynamicCollisionComponent collision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact
+ = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setVulnerableToDeathTiles(true);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.SMOKE_POOF);
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ lifetime.setDeathSound(sound.load(R.raw.sound_stomp));
+ }
+
+ object.add(render);
+ object.add(sprite);
+
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+
+ object.team = Team.ENEMY;
+
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ addStaticData(GameObjectType.ONION, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyWanda(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ // Make sure related textures are loaded.
+ textureLibrary.allocateTexture(R.drawable.energy_ball01);
+ textureLibrary.allocateTexture(R.drawable.energy_ball02);
+ textureLibrary.allocateTexture(R.drawable.energy_ball03);
+ textureLibrary.allocateTexture(R.drawable.energy_ball04);
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 128;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.WANDA);
+ if (staticData == null) {
+ final int staticObjectCount = 9;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.0f);
+
+
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(20, 5, 26, 80, HitType.COLLECT));
+
+ SpriteAnimation idle = new SpriteAnimation(NPCAnimationComponent.IDLE, 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_stand),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+
+
+
+ AnimationFrame walkFrame1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_walk01),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_walk02),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame3 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_walk03),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame4 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_walk04),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame5 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_walk05),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ SpriteAnimation walk = new SpriteAnimation(NPCAnimationComponent.WALK, 8);
+ walk.addFrame(walkFrame1);
+ walk.addFrame(walkFrame2);
+ walk.addFrame(walkFrame3);
+ walk.addFrame(walkFrame4);
+ walk.addFrame(walkFrame5);
+ walk.addFrame(walkFrame4);
+ walk.addFrame(walkFrame3);
+ walk.addFrame(walkFrame2);
+ walk.setLoop(true);
+
+
+ AnimationFrame runFrame4 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run04),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+
+ SpriteAnimation run = new SpriteAnimation(NPCAnimationComponent.RUN, 9);
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run02),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run03),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ run.addFrame(runFrame4);
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run05),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run06),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run07),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ run.addFrame(runFrame4);
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run08),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ run.setLoop(true);
+
+ SpriteAnimation jumpStart = new SpriteAnimation(NPCAnimationComponent.JUMP_START, 4);
+ AnimationFrame jump1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_jump01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame jump2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_jump01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ jumpStart.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_run04),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ jumpStart.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_crouch),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ jumpStart.addFrame(jump1);
+ jumpStart.addFrame(jump2);
+
+ SpriteAnimation jumpAir = new SpriteAnimation(NPCAnimationComponent.JUMP_AIR, 2);
+ jumpAir.addFrame(jump1);
+ jumpAir.addFrame(jump2);
+ jumpAir.setLoop(true);
+
+ SpriteAnimation attack = new SpriteAnimation(NPCAnimationComponent.SHOOT, 11);
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot01),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot02),
+ Utils.framesToTime(24, 8), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot03),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot04),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot05),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot06),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot07),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot08),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot09),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot02),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_wanda_shoot01),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume));
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(idle);
+ staticData.add(walk);
+ staticData.add(run);
+ staticData.add(jumpStart);
+ staticData.add(jumpAir);
+ staticData.add(attack);
+
+ setStaticData(GameObjectType.WANDA, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.NPC);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(32, 82);
+ bgcollision.setOffset(20, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ NPCAnimationComponent animation = (NPCAnimationComponent)allocateComponent(NPCAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ NPCComponent patrol = (NPCComponent)allocateComponent(NPCComponent.class);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ patrol.setHitReactionComponent(hitReact);
+
+ SoundSystem sound = sSystemRegistry.soundSystem;
+
+ LaunchProjectileComponent gun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ gun.setShotsPerSet(1);
+ gun.setSetsPerActivation(1);
+ gun.setDelayBeforeFirstSet(Utils.framesToTime(24, 11));
+ gun.setObjectTypeToSpawn(GameObjectType.WANDA_SHOT);
+ gun.setOffsetX(45);
+ gun.setOffsetY(42);
+ gun.setRequiredAction(GameObject.ActionType.ATTACK);
+ gun.setVelocityX(300.0f);
+ gun.setShootSound(sound.load(R.raw.sound_poing));
+
+ object.team = Team.ENEMY;
+ object.life = 1;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(gun);
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.WANDA, object, sprite);
+
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+
+ public GameObject spawnEnemyKyle(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 128;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.KYLE);
+ if (staticData == null) {
+ final int staticObjectCount = 9;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.0f);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(20, 5, 26, 80, HitType.COLLECT));
+
+ SpriteAnimation idle = new SpriteAnimation(NPCAnimationComponent.IDLE, 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_stand),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+
+ AnimationFrame walkFrame1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_walk01),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_walk02),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame3 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_walk03),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame4 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_walk04),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame5 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_walk05),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame6 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_walk06),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame7 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_walk07),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ SpriteAnimation walk = new SpriteAnimation(NPCAnimationComponent.WALK, 12);
+ walk.addFrame(walkFrame1);
+ walk.addFrame(walkFrame2);
+ walk.addFrame(walkFrame3);
+ walk.addFrame(walkFrame4);
+ walk.addFrame(walkFrame3);
+ walk.addFrame(walkFrame2);
+ walk.addFrame(walkFrame1);
+ walk.addFrame(walkFrame5);
+ walk.addFrame(walkFrame6);
+ walk.addFrame(walkFrame7);
+ walk.addFrame(walkFrame6);
+ walk.addFrame(walkFrame5);
+
+ walk.setLoop(true);
+
+ AnimationFrame crouch1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_crouch01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame crouch2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_crouch02),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+
+ SpriteAnimation runStart = new SpriteAnimation(NPCAnimationComponent.RUN_START, 2);
+ runStart.addFrame(crouch1);
+ runStart.addFrame(crouch2);
+
+ FixedSizeArray<CollisionVolume> attackVolume =
+ new FixedSizeArray<CollisionVolume>(2);
+ attackVolume.add(new AABoxCollisionVolume(32, 32, 50, 32, HitType.HIT));
+ attackVolume.add(new AABoxCollisionVolume(32, 32, 50, 32, HitType.COLLECT));
+
+ SpriteAnimation run = new SpriteAnimation(NPCAnimationComponent.RUN, 2);
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_dash01),
+ Utils.framesToTime(24, 1), attackVolume, basicVulnerabilityVolume));
+ run.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_dash02),
+ Utils.framesToTime(24, 1), attackVolume, basicVulnerabilityVolume));
+ run.setLoop(true);
+
+ SpriteAnimation jumpStart = new SpriteAnimation(NPCAnimationComponent.JUMP_START, 2);
+ jumpStart.addFrame(crouch1);
+ jumpStart.addFrame(crouch2);
+
+ SpriteAnimation jumpAir = new SpriteAnimation(NPCAnimationComponent.JUMP_AIR, 2);
+ AnimationFrame jump1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_jump01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame jump2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kyle_jump01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ jumpAir.addFrame(jump1);
+ jumpAir.addFrame(jump2);
+ jumpAir.setLoop(true);
+
+
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(idle);
+ staticData.add(walk);
+ staticData.add(runStart);
+ staticData.add(run);
+ staticData.add(jumpStart);
+ staticData.add(jumpAir);
+
+ setStaticData(GameObjectType.KYLE, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.NPC);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(32, 90);
+ bgcollision.setOffset(20, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ NPCAnimationComponent animation = (NPCAnimationComponent)allocateComponent(NPCAnimationComponent.class);
+ animation.setSprite(sprite);
+ animation.setStopAtWalls(false); // Kyle can run through walls
+
+ NPCComponent patrol = (NPCComponent)allocateComponent(NPCComponent.class);
+ patrol.setSpeeds(350.0f, 50.0f, 400.0f, -10.0f, 400.0f);
+ patrol.setGameEvent(GameFlowEvent.EVENT_SHOW_ANIMATION, AnimationPlayerActivity.KYLE_DEATH, false);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ patrol.setHitReactionComponent(hitReact);
+
+ MotionBlurComponent motionBlur = (MotionBlurComponent)allocateComponent(MotionBlurComponent.class);
+ motionBlur.setTarget(render);
+
+ LauncherComponent launcher = (LauncherComponent)allocateComponent(LauncherComponent.class);
+ launcher.setup((float)(Math.PI * 0.45f), 1000.0f, 0.0f, 0.0f, false);
+ launcher.setLaunchEffect(GameObjectType.FLASH, 70.0f, 50.0f);
+ hitReact.setLauncherComponent(launcher, HitType.HIT);
+
+ object.team = Team.NONE;
+ object.life = 1;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(motionBlur);
+ object.add(launcher);
+
+ addStaticData(GameObjectType.KYLE, object, sprite);
+
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyKyleDead(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 128;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.KYLE_DEAD);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(32, 5, 64, 32, HitType.COLLECT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 1);
+ AnimationFrame frame1 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.enemy_kyle_dead),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+
+ idle.addFrame(frame1);
+
+ idle.setLoop(true);
+
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.KYLE_DEAD, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ dynamicCollision.setHitReactionComponent(hitReact);
+ hitReact.setSpawnGameEventOnHit(HitType.COLLECT, GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER2, 0);
+
+ SelectDialogComponent dialogSelect = (SelectDialogComponent)allocateComponent(SelectDialogComponent.class);
+ dialogSelect.setHitReact(hitReact);
+
+ // Since this object doesn't have gravity or background collision, adjust down to simulate the position
+ // at which a bounding volume would rest.
+
+ object.getPosition().y -= 5.0f;
+
+ object.add(dialogSelect);
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.KYLE_DEAD, object, sprite);
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyAndouDead(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.ANDOU_DEAD);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 1);
+ AnimationFrame frame1 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.andou_explode12),
+ Utils.framesToTime(24, 1), null, null);
+
+ idle.addFrame(frame1);
+
+ idle.setLoop(true);
+
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.ANDOU_DEAD, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ LaunchProjectileComponent smokeGun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ smokeGun.setDelayBetweenShots(0.25f);
+ smokeGun.setObjectTypeToSpawn(GameObjectType.SMOKE_BIG);
+ smokeGun.setOffsetX(32);
+ smokeGun.setOffsetY(15);
+ smokeGun.setVelocityX(-150.0f);
+ smokeGun.setVelocityY(100.0f);
+ smokeGun.setThetaError(0.1f);
+
+ LaunchProjectileComponent smokeGun2
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ smokeGun2.setDelayBetweenShots(0.35f);
+ smokeGun2.setObjectTypeToSpawn(GameObjectType.SMOKE_SMALL);
+ smokeGun2.setOffsetX(16);
+ smokeGun2.setOffsetY(15);
+ smokeGun2.setVelocityX(-150.0f);
+ smokeGun2.setVelocityY(150.0f);
+ smokeGun2.setThetaError(0.1f);
+
+ object.add(render);
+ object.add(sprite);
+ object.add(smokeGun);
+ object.add(smokeGun2);
+
+ object.facingDirection.x = -1.0f;
+
+ addStaticData(GameObjectType.ANDOU_DEAD, object, sprite);
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyKabocha(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 128;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.KABOCHA);
+ if (staticData == null) {
+ final int staticObjectCount = 5;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.0f);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(20, 5, 26, 80, HitType.COLLECT));
+
+ SpriteAnimation idle = new SpriteAnimation(NPCAnimationComponent.IDLE, 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_stand),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+
+ AnimationFrame walkFrame1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_walk01),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_walk02),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame3 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_walk03),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame4 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_walk04),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame5 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_walk05),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame6 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_walk06),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+
+ SpriteAnimation walk = new SpriteAnimation(NPCAnimationComponent.WALK, 6);
+ walk.addFrame(walkFrame1);
+ walk.addFrame(walkFrame2);
+ walk.addFrame(walkFrame3);
+ walk.addFrame(walkFrame4);
+ walk.addFrame(walkFrame5);
+ walk.addFrame(walkFrame6);
+
+
+ walk.setLoop(true);
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(idle);
+ staticData.add(walk);
+
+ setStaticData(GameObjectType.KABOCHA, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.NPC);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(38, 82);
+ bgcollision.setOffset(16, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ NPCAnimationComponent animation = (NPCAnimationComponent)allocateComponent(NPCAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ NPCComponent patrol = (NPCComponent)allocateComponent(NPCComponent.class);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ patrol.setHitReactionComponent(hitReact);
+
+ object.team = Team.ENEMY;
+ object.life = 1;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.KABOCHA, object, sprite);
+
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnRokudouTerminal(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.ROKUDOU_TERMINAL);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume = new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(0, 0, 64, 64));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.COLLECT);
+
+
+ AnimationFrame frame1 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame frame2 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal02),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame frame3 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal03),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame frame4 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal01),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame frame5 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal02),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame frame6 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal01),
+ 1.0f, null, basicVulnerabilityVolume);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 12);
+ idle.addFrame(frame1);
+ idle.addFrame(frame5);
+ idle.addFrame(frame4);
+ idle.addFrame(frame3);
+ idle.addFrame(frame2);
+ idle.addFrame(frame6);
+ idle.addFrame(frame6);
+ idle.addFrame(frame3);
+ idle.addFrame(frame2);
+ idle.addFrame(frame1);
+ idle.addFrame(frame2);
+ idle.addFrame(frame6);
+
+ idle.setLoop(true);
+
+
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.ROKUDOU_TERMINAL, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setSpawnGameEventOnHit(HitType.COLLECT, GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER2, 0);
+
+ SelectDialogComponent dialogSelect = (SelectDialogComponent)allocateComponent(SelectDialogComponent.class);
+ dialogSelect.setHitReact(hitReact);
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ object.add(dialogSelect);
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.ROKUDOU_TERMINAL, object, sprite);
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+
+ public GameObject spawnKabochaTerminal(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.KABOCHA_TERMINAL);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume = new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(0, 0, 64, 64));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.COLLECT);
+
+
+ AnimationFrame frame1 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal_kabocha01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame frame2 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal_kabocha02),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame frame3 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal_kabocha03),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume);
+ AnimationFrame frame4 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal_kabocha01),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame frame5 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal_kabocha02),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame frame6 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_terminal_kabocha01),
+ 1.0f, null, basicVulnerabilityVolume);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 12);
+ idle.addFrame(frame1);
+ idle.addFrame(frame5);
+ idle.addFrame(frame4);
+ idle.addFrame(frame3);
+ idle.addFrame(frame2);
+ idle.addFrame(frame6);
+ idle.addFrame(frame6);
+ idle.addFrame(frame3);
+ idle.addFrame(frame2);
+ idle.addFrame(frame1);
+ idle.addFrame(frame2);
+ idle.addFrame(frame6);
+
+ idle.setLoop(true);
+
+
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.KABOCHA_TERMINAL, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setSpawnGameEventOnHit(HitType.COLLECT, GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER2, 0);
+
+ SelectDialogComponent dialogSelect = (SelectDialogComponent)allocateComponent(SelectDialogComponent.class);
+ dialogSelect.setHitReact(hitReact);
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ object.add(dialogSelect);
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.KABOCHA_TERMINAL, object, sprite);
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyEvilKabocha(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 128;
+ object.height = 128;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.EVIL_KABOCHA);
+ if (staticData == null) {
+ final int staticObjectCount = 8;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.0f);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(52, 5, 26, 80, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(NPCAnimationComponent.IDLE, 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_stand),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+
+ AnimationFrame walkFrame1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_walk01),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_walk02),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame3 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_walk03),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame4 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_walk04),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame5 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_walk05),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+ AnimationFrame walkFrame6 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_walk06),
+ Utils.framesToTime(24, 3), null, basicVulnerabilityVolume);
+
+ SpriteAnimation walk = new SpriteAnimation(NPCAnimationComponent.WALK, 6);
+ walk.addFrame(walkFrame1);
+ walk.addFrame(walkFrame2);
+ walk.addFrame(walkFrame3);
+ walk.addFrame(walkFrame4);
+ walk.addFrame(walkFrame5);
+ walk.addFrame(walkFrame6);
+
+ walk.setLoop(true);
+
+
+ SpriteAnimation surprised = new SpriteAnimation(NPCAnimationComponent.SURPRISED, 1);
+ surprised.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_surprised),
+ 4.0f, null, null));
+
+
+ SpriteAnimation hit = new SpriteAnimation(NPCAnimationComponent.TAKE_HIT, 2);
+ hit.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_hit01),
+ Utils.framesToTime(24, 1), null, null));
+ hit.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_hit02),
+ Utils.framesToTime(24, 10), null, null));
+
+ SpriteAnimation die = new SpriteAnimation(NPCAnimationComponent.DEATH, 5);
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_die01),
+ Utils.framesToTime(24, 6), null, null));
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_stand),
+ Utils.framesToTime(24, 2), null, null));
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_die02),
+ Utils.framesToTime(24, 2), null, null));
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_die03),
+ Utils.framesToTime(24, 2), null, null));
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_kabocha_evil_die04),
+ Utils.framesToTime(24, 6), null, null));
+
+ staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(idle);
+ staticData.add(walk);
+ staticData.add(surprised);
+ staticData.add(hit);
+ staticData.add(die);
+
+ setStaticData(GameObjectType.EVIL_KABOCHA, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.NPC);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(38, 82);
+ bgcollision.setOffset(45, 5);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ NPCAnimationComponent animation = (NPCAnimationComponent)allocateComponent(NPCAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ ChannelSystem.Channel surpriseChannel = null;
+ ChannelSystem channelSystem = BaseObject.sSystemRegistry.channelSystem;
+ surpriseChannel = channelSystem.registerChannel(sSurprisedNPCChannel);
+ animation.setChannel(surpriseChannel);
+ animation.setChannelTrigger(NPCAnimationComponent.SURPRISED);
+
+ NPCComponent patrol = (NPCComponent)allocateComponent(NPCComponent.class);
+ patrol.setSpeeds(50.0f, 50.0f, 0.0f, -10.0f, 200.0f);
+ patrol.setReactToHits(true);
+ patrol.setGameEvent(GameFlowEvent.EVENT_SHOW_ANIMATION, AnimationPlayerActivity.ROKUDOU_ENDING, true);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ hitReact.setTakeHitSound(HitType.HIT, sound.load(R.raw.sound_kabocha_hit));
+ }
+
+ patrol.setHitReactionComponent(hitReact);
+
+ object.team = Team.ENEMY;
+ object.life = 3;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.EVIL_KABOCHA, object, sprite);
+
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnemyRokudou(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ // Make sure related textures are loaded.
+ textureLibrary.allocateTexture(R.drawable.energy_ball01);
+ textureLibrary.allocateTexture(R.drawable.energy_ball02);
+ textureLibrary.allocateTexture(R.drawable.energy_ball03);
+ textureLibrary.allocateTexture(R.drawable.energy_ball04);
+
+ textureLibrary.allocateTexture(R.drawable.effect_bullet01);
+ textureLibrary.allocateTexture(R.drawable.effect_bullet02);
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mNormalActivationRadius;
+ object.width = 128;
+ object.height = 128;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.ROKUDOU);
+ if (staticData == null) {
+ final int staticObjectCount = 8;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.0f);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(45, 23, 42, 75, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(NPCAnimationComponent.IDLE, 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_stand),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+
+ SpriteAnimation fly = new SpriteAnimation(NPCAnimationComponent.WALK, 2);
+ fly.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_fly01),
+ 1.0f, null, basicVulnerabilityVolume));
+ fly.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_fly02),
+ 1.0f, null, basicVulnerabilityVolume));
+ fly.setLoop(true);
+
+ SpriteAnimation shoot = new SpriteAnimation(NPCAnimationComponent.SHOOT, 2);
+ shoot.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_shoot01),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ shoot.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_shoot02),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ shoot.setLoop(true);
+
+
+ SpriteAnimation surprised = new SpriteAnimation(NPCAnimationComponent.SURPRISED, 1);
+ surprised.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_surprise),
+ 4.0f, null, null));
+
+
+ SpriteAnimation hit = new SpriteAnimation(NPCAnimationComponent.TAKE_HIT, 7);
+ AnimationFrame hitFrame1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_hit01),
+ Utils.framesToTime(24, 2), null, null);
+ AnimationFrame hitFrame2 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_hit02),
+ Utils.framesToTime(24, 1), null, null);
+ AnimationFrame hitFrame3 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_hit03),
+ Utils.framesToTime(24, 1), null, null);
+
+ hit.addFrame(hitFrame1);
+ hit.addFrame(hitFrame2);
+ hit.addFrame(hitFrame3);
+ hit.addFrame(hitFrame2);
+ hit.addFrame(hitFrame3);
+ hit.addFrame(hitFrame2);
+ hit.addFrame(hitFrame3);
+
+ SpriteAnimation die = new SpriteAnimation(NPCAnimationComponent.DEATH, 5);
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_stand),
+ Utils.framesToTime(24, 6), null, null));
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_die01),
+ Utils.framesToTime(24, 2), null, null));
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_die02),
+ Utils.framesToTime(24, 4), null, null));
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_die03),
+ Utils.framesToTime(24, 6), null, null));
+ die.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_rokudou_fight_die04),
+ Utils.framesToTime(24, 6), null, null));
+
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(idle);
+ staticData.add(fly);
+ staticData.add(surprised);
+ staticData.add(hit);
+ staticData.add(die);
+ staticData.add(shoot);
+
+ setStaticData(GameObjectType.ROKUDOU, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.NPC);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(45, 75);
+ bgcollision.setOffset(45, 23);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+
+
+ NPCAnimationComponent animation = (NPCAnimationComponent)allocateComponent(NPCAnimationComponent.class);
+ animation.setSprite(sprite);
+ animation.setFlying(true);
+
+ ChannelSystem.Channel surpriseChannel = null;
+ ChannelSystem channelSystem = BaseObject.sSystemRegistry.channelSystem;
+ surpriseChannel = channelSystem.registerChannel(sSurprisedNPCChannel);
+ animation.setChannel(surpriseChannel);
+ animation.setChannelTrigger(NPCAnimationComponent.SURPRISED);
+
+ NPCComponent patrol = (NPCComponent)allocateComponent(NPCComponent.class);
+ patrol.setSpeeds(500.0f, 100.0f, 100.0f, -100.0f, 400.0f);
+ patrol.setFlying(true);
+ patrol.setReactToHits(true);
+ patrol.setGameEvent(GameFlowEvent.EVENT_SHOW_ANIMATION, AnimationPlayerActivity.KABOCHA_ENDING, true);
+ patrol.setPauseOnAttack(false);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ hitReact.setTakeHitSound(HitType.HIT, sound.load(R.raw.sound_rokudou_hit));
+ }
+
+ patrol.setHitReactionComponent(hitReact);
+
+ ChangeComponentsComponent deathSwap = (ChangeComponentsComponent)allocateComponent(ChangeComponentsComponent.class);
+ deathSwap.addSwapInComponent(allocateComponent(GravityComponent.class));
+ deathSwap.setSwapAction(ActionType.DEATH);
+
+ LaunchProjectileComponent gun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ gun.setShotsPerSet(1);
+ gun.setSetsPerActivation(-1);
+ gun.setDelayBetweenSets(1.5f);
+ gun.setObjectTypeToSpawn(GameObjectType.ENERGY_BALL);
+ gun.setOffsetX(75);
+ gun.setOffsetY(42);
+ gun.setRequiredAction(GameObject.ActionType.ATTACK);
+ gun.setVelocityX(300.0f);
+ gun.setVelocityY(-300.0f);
+ gun.setShootSound(sound.load(R.raw.sound_poing));
+
+
+ LaunchProjectileComponent gun2
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ gun2.setShotsPerSet(5);
+ gun2.setDelayBetweenShots(0.1f);
+ gun2.setSetsPerActivation(-1);
+ gun2.setDelayBetweenSets(2.5f);
+ gun2.setObjectTypeToSpawn(GameObjectType.TURRET_BULLET);
+ gun2.setOffsetX(75);
+ gun2.setOffsetY(42);
+ gun2.setRequiredAction(GameObject.ActionType.ATTACK);
+ gun2.setVelocityX(300.0f);
+ gun2.setVelocityY(-300.0f);
+ gun.setShootSound(sound.load(R.raw.sound_gun));
+
+
+ object.team = Team.ENEMY;
+ object.life = 3;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ // HACK! Since there's no gravity and this is a big character, align him to the floor
+ // manually.
+ object.getPosition().y -= 23;
+
+ object.add(render);
+ object.add(sprite);
+ object.add(bgcollision);
+ object.add(animation);
+ object.add(patrol);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(deathSwap);
+ object.add(gun);
+ object.add(gun2);
+
+ addStaticData(GameObjectType.ROKUDOU, object, sprite);
+
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+
+ public GameObject spawnPlayerGhost(float positionX, float positionY, GameObject player, float lifeTime) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.GHOST);
+ if (staticData == null) {
+ final int staticObjectCount = 4;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ //GravityComponent gravity = (GravityComponent)allocateComponent(GravityComponent.class);
+ //gravity.setGravityMultiplier(0.1f);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.6f);
+
+ GhostComponent ghost = (GhostComponent)allocateComponent(GhostComponent.class);
+ ghost.setMovementSpeed(2000.0f);
+ ghost.setAcceleration(300.0f);
+ ghost.setUseOrientationSensor(true);
+ ghost.setKillOnRelease(true);
+
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ ghost.setAmbientSound(sound.load(R.raw.sound_possession));
+ }
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(32, 32, 32, HitType.POSSESS));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 4);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_energyball01),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_energyball02),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_energyball03),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_energyball04),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.setLoop(true);
+
+ //staticData.add(gravity);
+ staticData.add(movement);
+ staticData.add(physics);
+ staticData.add(ghost);
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.GHOST, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.PROJECTILE);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(64, 64);
+ bgcollision.setOffset(0, 0);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setDieOnAttack(true);
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+ LifetimeComponent life = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ // when the ghost dies it either releases itself or passes control to another object, so we
+ // don't want control to return to the player.
+
+ life.setReleaseGhostOnDeath(false);
+
+
+ object.life = 1;
+
+ object.add(bgcollision);
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+ object.add(life);
+
+ addStaticData(GameObjectType.GHOST, object, sprite);
+
+ object.commitUpdates();
+
+ GhostComponent ghost = object.findByClass(GhostComponent.class);
+ if (ghost != null) {
+ ghost.setLifeTime(lifeTime);
+ }
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEnergyBall(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.ENERGY_BALL);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 16, 16, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 4);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.energy_ball01),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.energy_ball02),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.energy_ball03),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.energy_ball04),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.setLoop(true);
+
+ staticData.add(movement);
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.ENERGY_BALL, staticData);
+
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.PROJECTILE);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(5.0f);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setDieOnAttack(true);
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ object.life = 1;
+ object.team = Team.ENEMY;
+ object.destroyOnDeactivation = true;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.ENERGY_BALL, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnWandaShot(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.WANDA_SHOT);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 16, 16, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 4);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.energy_ball01),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.energy_ball02),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.energy_ball03),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.energy_ball04),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.setLoop(true);
+
+ staticData.add(movement);
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.WANDA_SHOT, staticData);
+
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.PROJECTILE);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(5.0f);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ //hitReact.setDieOnAttack(true);
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ object.life = 1;
+ object.team = Team.NONE;
+ object.destroyOnDeactivation = true;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.WANDA_SHOT, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnCannonBall(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.CANNON_BALL);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(8, 16, 16, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 1);
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.snail_bomb),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+
+ staticData.add(movement);
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.CANNON_BALL, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.PROJECTILE);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(3.0f);
+ lifetime.setDieOnHitBackground(true);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setDieOnAttack(true);
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ SimpleCollisionComponent collision = (SimpleCollisionComponent)allocateComponent(SimpleCollisionComponent.class);
+
+
+ object.life = 1;
+ object.team = Team.ENEMY;
+ object.destroyOnDeactivation = true;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+ object.add(collision);
+
+ addStaticData(GameObjectType.CANNON_BALL, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnTurretBullet(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 16;
+ object.height = 16;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.TURRET_BULLET);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(8, 8, 8, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 2);
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.effect_bullet01),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.effect_bullet02),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.setLoop(true);
+
+ staticData.add(movement);
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.TURRET_BULLET, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.PROJECTILE);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(3.0f);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setDieOnAttack(true);
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+
+ object.life = 1;
+ object.team = Team.ENEMY;
+ object.destroyOnDeactivation = true;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+
+ addStaticData(GameObjectType.TURRET_BULLET, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnBrobotBullet(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.BROBOT_BULLET);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 3);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk01),
+ Utils.framesToTime(24, 1), null, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk02),
+ Utils.framesToTime(24, 1), null, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk03),
+ Utils.framesToTime(24, 1), null, null));
+ idle.setLoop(true);
+
+ staticData.add(movement);
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.BROBOT_BULLET, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.PROJECTILE);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(3.0f);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ object.life = 1;
+ object.team = Team.ENEMY;
+ object.destroyOnDeactivation = true;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+
+
+ addStaticData(GameObjectType.BROBOT_BULLET, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnCoin(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 16;
+ object.height = 16;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.COIN);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume = null; /*new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(8, 8, 8));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.COLLECT);*/
+
+ SpriteAnimation idle = new SpriteAnimation(0, 5);
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_coin01),
+ Utils.framesToTime(24, 30), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_coin02),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_coin03),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_coin04),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_coin05),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ idle.setLoop(true);
+
+ InventoryComponent.UpdateRecord addCoin = new InventoryComponent.UpdateRecord();
+ addCoin.coinCount = 1;
+
+ staticData.add(addCoin);
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.COIN, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ //DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ //sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setDieWhenCollected(true);
+ hitReact.setInvincible(true);
+
+ HitPlayerComponent hitPlayer = (HitPlayerComponent)allocateComponent(HitPlayerComponent.class);
+ hitPlayer.setup(32, hitReact, HitType.COLLECT, false);
+
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ hitReact.setTakeHitSound(HitType.COLLECT, sound.load(R.raw.ding));
+ }
+
+ // TODO: this is pretty dumb. The static data binding needs to be made generic.
+ final int staticDataSize = staticData.getCount();
+ for (int x = 0; x < staticDataSize; x++) {
+ final BaseObject entry = staticData.get(x);
+ if (entry instanceof InventoryComponent.UpdateRecord) {
+ hitReact.setInventoryUpdate((InventoryComponent.UpdateRecord)entry);
+ break;
+ }
+ }
+
+ //dynamicCollision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent life = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+
+ object.life = 1;
+
+ object.add(render);
+ object.add(sprite);
+ //object.add(dynamicCollision);
+ object.add(hitPlayer);
+ object.add(hitReact);
+ object.add(life);
+
+ addStaticData(GameObjectType.COIN, object, sprite);
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnRuby(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.RUBY);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume = new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 16, 16));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.COLLECT);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 5);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_ruby01),
+ 2.0f, null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_ruby02),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_ruby03),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_ruby04),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_ruby05),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ idle.setLoop(true);
+
+ InventoryComponent.UpdateRecord addRuby = new InventoryComponent.UpdateRecord();
+ addRuby.rubyCount = 1;
+
+ staticData.add(addRuby);
+
+ staticData.add(idle);
+ setStaticData(GameObjectType.RUBY, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setDieWhenCollected(true);
+ hitReact.setInvincible(true);
+ // TODO: this is pretty dumb. The static data binding needs to be made generic.
+ final int staticDataSize = staticData.getCount();
+ for (int x = 0; x < staticDataSize; x++) {
+ final BaseObject entry = staticData.get(x);
+ if (entry instanceof InventoryComponent.UpdateRecord) {
+ hitReact.setInventoryUpdate((InventoryComponent.UpdateRecord)entry);
+ break;
+ }
+ }
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent life = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ life.setObjectToSpawnOnDeath(GameObjectType.GEM_EFFECT_SPAWNER);
+
+ object.life = 1;
+
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+ object.add(life);
+
+ addStaticData(GameObjectType.RUBY, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnDiary(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ LevelSystem level = sSystemRegistry.levelSystem;
+ if (level != null) {
+ final LevelTree.Level currentLevel = level.getCurrentLevel();
+ if (currentLevel != null && currentLevel.diaryCollected) {
+ return null;
+ }
+ }
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.DIARY);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume = new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(16, 16, 16));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.COLLECT);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 8);
+ AnimationFrame frame1 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_diary01),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+ AnimationFrame frame2 = new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_diary02),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume);
+
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_diary01),
+ 1.0f, null, basicVulnerabilityVolume));
+ idle.addFrame(frame2);
+ idle.addFrame(frame1);
+ idle.addFrame(frame2);
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_diary03),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_diary04),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_diary05),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(textureLibrary.allocateTexture(R.drawable.object_diary06),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+
+ idle.setLoop(true);
+
+ InventoryComponent.UpdateRecord addDiary = new InventoryComponent.UpdateRecord();
+ addDiary.diaryCount = 1;
+
+ staticData.add(addDiary);
+
+ staticData.add(idle);
+
+ setStaticData(GameObjectType.DIARY, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setDieWhenCollected(true);
+ hitReact.setInvincible(true);
+ hitReact.setSpawnGameEventOnHit(CollisionParameters.HitType.COLLECT,
+ GameFlowEvent.EVENT_SHOW_DIARY, 0);
+ // TODO: this is pretty dumb. The static data binding needs to be made generic.
+ final int staticDataSize = staticData.getCount();
+ for (int x = 0; x < staticDataSize; x++) {
+ final BaseObject entry = staticData.get(x);
+ if (entry instanceof InventoryComponent.UpdateRecord) {
+ hitReact.setInventoryUpdate((InventoryComponent.UpdateRecord)entry);
+ break;
+ }
+ }
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent life = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+
+ object.life = 1;
+
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+ object.add(life);
+
+ addStaticData(GameObjectType.DIARY, object, sprite);
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnObjectDoor(float positionX, float positionY, GameObjectType type, boolean solid) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(type);
+ if (staticData == null) {
+ final int staticObjectCount = 5;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ final int red_frames[] = {
+ R.drawable.object_door_red01,
+ R.drawable.object_door_red02,
+ R.drawable.object_door_red03,
+ R.drawable.object_door_red04,
+ };
+
+ final int blue_frames[] = {
+ R.drawable.object_door_blue01,
+ R.drawable.object_door_blue02,
+ R.drawable.object_door_blue03,
+ R.drawable.object_door_blue04,
+ };
+
+ final int green_frames[] = {
+ R.drawable.object_door_green01,
+ R.drawable.object_door_green02,
+ R.drawable.object_door_green03,
+ R.drawable.object_door_green04,
+ };
+
+ int frames[] = red_frames;
+
+ if (type == GameObjectType.DOOR_GREEN) {
+ frames = green_frames;
+ } else if (type == GameObjectType.DOOR_BLUE) {
+ frames = blue_frames;
+ }
+
+ FixedSizeArray<CollisionVolume> vulnerabilityVolume = null;
+
+ AnimationFrame frame1 = new AnimationFrame(textureLibrary.allocateTexture(frames[0]),
+ Utils.framesToTime(24, 1), null, vulnerabilityVolume);
+ AnimationFrame frame2 = new AnimationFrame(textureLibrary.allocateTexture(frames[1]),
+ Utils.framesToTime(24, 2));
+ AnimationFrame frame3 = new AnimationFrame(textureLibrary.allocateTexture(frames[2]),
+ Utils.framesToTime(24, 2));
+ AnimationFrame frame4 = new AnimationFrame(textureLibrary.allocateTexture(frames[3]),
+ Utils.framesToTime(24, 1));
+
+ // one frame of closing is deadly
+
+ FixedSizeArray<CollisionVolume> attackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ attackVolume.add(new AABoxCollisionVolume(12, 8, 8, 56));
+ attackVolume.get(0).setHitType(HitType.DEATH);
+
+
+
+ AnimationFrame closeFrame2 = new AnimationFrame(textureLibrary.allocateTexture(frames[1]),
+ Utils.framesToTime(24, 2), attackVolume, vulnerabilityVolume);
+
+ SpriteAnimation idle_closed = new SpriteAnimation(DoorAnimationComponent.Animation.CLOSED, 1);
+ idle_closed.addFrame(frame1);
+
+ SpriteAnimation idle_open = new SpriteAnimation(DoorAnimationComponent.Animation.OPEN, 1);
+ idle_open.addFrame(frame4);
+
+ SpriteAnimation open = new SpriteAnimation(DoorAnimationComponent.Animation.OPENING, 2);
+ open.addFrame(frame2);
+ open.addFrame(frame3);
+
+ SpriteAnimation close = new SpriteAnimation(DoorAnimationComponent.Animation.CLOSING, 2);
+ close.addFrame(frame3);
+ close.addFrame(closeFrame2);
+
+ SolidSurfaceComponent solidSurface
+ = (SolidSurfaceComponent)allocateComponent(SolidSurfaceComponent.class);
+ solidSurface.inititalize(4);
+ // box shape:
+ // ___ ___1
+ // | | 2| |3
+ // --- ---4
+ Vector2 surface1Start = new Vector2(0, object.height);
+ Vector2 surface1End = new Vector2(object.width, object.height);
+ Vector2 surface1Normal = new Vector2(0.0f, -1.0f);
+ surface1Normal.normalize();
+
+ Vector2 surface2Start = new Vector2(0, object.height);
+ Vector2 surface2End = new Vector2(0, 0);
+ Vector2 surface2Normal = new Vector2(-1.0f, 0.0f);
+ surface2Normal.normalize();
+
+ Vector2 surface3Start = new Vector2(object.width, object.height);
+ Vector2 surface3End = new Vector2(object.width, 0);
+ Vector2 surface3Normal = new Vector2(1.0f, 0);
+
+ Vector2 surface4Start = new Vector2(0, 0);
+ Vector2 surface4End = new Vector2(object.width, 0);
+ Vector2 surface4Normal = new Vector2(0, 1.0f);
+
+ solidSurface.addSurface(surface1Start, surface1End, surface1Normal);
+ solidSurface.addSurface(surface2Start, surface2End, surface2Normal);
+ solidSurface.addSurface(surface3Start, surface3End, surface3Normal);
+ solidSurface.addSurface(surface4Start, surface4End, surface4Normal);
+
+ staticData.add(idle_open);
+ staticData.add(idle_closed);
+ staticData.add(open);
+ staticData.add(close);
+ staticData.add(solidSurface);
+ setStaticData(type, staticData);
+ }
+
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.FOREGROUND_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DoorAnimationComponent doorAnim = (DoorAnimationComponent)allocateComponent(DoorAnimationComponent.class);
+ doorAnim.setSprite(sprite);
+
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ doorAnim.setSounds(sound.load(R.raw.sound_open), sound.load(R.raw.sound_close));
+ }
+
+ ChannelSystem.Channel doorChannel = null;
+ ChannelSystem channelSystem = BaseObject.sSystemRegistry.channelSystem;
+ switch (type) {
+ case DOOR_RED:
+ doorChannel = channelSystem.registerChannel(sRedButtonChannel);
+ break;
+ case DOOR_BLUE:
+ doorChannel = channelSystem.registerChannel(sBlueButtonChannel);
+ break;
+ case DOOR_GREEN:
+ doorChannel = channelSystem.registerChannel(sGreenButtonChannel);
+ break;
+ }
+ doorAnim.setChannel(doorChannel);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact
+ = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+
+
+ object.add(render);
+ object.add(sprite);
+ object.add(doorAnim);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+ addStaticData(type, object, sprite);
+
+ object.commitUpdates();
+
+ SolidSurfaceComponent solidSurface = object.findByClass(SolidSurfaceComponent.class);
+ if (solid) {
+ doorAnim.setSolidSurface(solidSurface);
+ } else {
+ object.remove(solidSurface);
+ object.commitUpdates();
+ }
+
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnObjectButton(float positionX, float positionY, GameObjectType type) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(type);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ final int red_frames[] = {
+ R.drawable.object_button_red,
+ R.drawable.object_button_pressed_red,
+ };
+
+ final int blue_frames[] = {
+ R.drawable.object_button_blue,
+ R.drawable.object_button_pressed_blue,
+ };
+
+ final int green_frames[] = {
+ R.drawable.object_button_green,
+ R.drawable.object_button_pressed_green,
+ };
+
+ int frames[] = red_frames;
+
+ if (type == GameObjectType.BUTTON_GREEN) {
+ frames = green_frames;
+ } else if (type == GameObjectType.BUTTON_BLUE) {
+ frames = blue_frames;
+ }
+
+ FixedSizeArray<CollisionVolume> vulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ vulnerabilityVolume.add(new AABoxCollisionVolume(0, 0, 32, 16));
+ vulnerabilityVolume.get(0).setHitType(HitType.DEPRESS);
+
+ AnimationFrame frame1 = new AnimationFrame(textureLibrary.allocateTexture(frames[0]),
+ Utils.framesToTime(24, 1), null, vulnerabilityVolume);
+ AnimationFrame frame2 = new AnimationFrame(textureLibrary.allocateTexture(frames[1]),
+ Utils.framesToTime(24, 1), null, vulnerabilityVolume);
+
+ SpriteAnimation idle = new SpriteAnimation(ButtonAnimationComponent.Animation.UP, 1);
+ idle.addFrame(frame1);
+
+ SpriteAnimation pressed = new SpriteAnimation(ButtonAnimationComponent.Animation.DOWN, 1);
+ pressed.addFrame(frame2);
+
+ staticData.add(idle);
+ staticData.add(pressed);
+
+ setStaticData(type, staticData);
+ }
+
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ ButtonAnimationComponent button = (ButtonAnimationComponent)allocateComponent(ButtonAnimationComponent.class);
+ button.setSprite(sprite);
+
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ button.setDepressSound(sound.load(R.raw.sound_button));
+ }
+
+ ChannelSystem.Channel buttonChannel = null;
+ ChannelSystem channelSystem = BaseObject.sSystemRegistry.channelSystem;
+ switch (type) {
+ case BUTTON_RED:
+ buttonChannel = channelSystem.registerChannel(sRedButtonChannel);
+ break;
+ case BUTTON_BLUE:
+ buttonChannel = channelSystem.registerChannel(sBlueButtonChannel);
+ break;
+ case BUTTON_GREEN:
+ buttonChannel = channelSystem.registerChannel(sGreenButtonChannel);
+ break;
+ }
+ button.setChannel(buttonChannel);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setInvincible(false);
+
+
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ object.team = Team.NONE;
+
+ object.add(render);
+ object.add(sprite);
+ object.add(button);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+
+ addStaticData(type, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnObjectCannon(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 64;
+ object.height = 128;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.CANNON);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> attackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ attackVolume.add(new AABoxCollisionVolume(16, 16, 32, 80));
+ attackVolume.get(0).setHitType(HitType.LAUNCH);
+
+ AnimationFrame frame1 = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_cannon),
+ 1.0f, attackVolume, null);
+
+ SpriteAnimation idle = new SpriteAnimation(GenericAnimationComponent.Animation.IDLE, 1);
+ idle.addFrame(frame1);
+
+ AnimationFrame frame1NoAttack = new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_cannon),
+ 1.0f, null, null);
+
+ SpriteAnimation shoot = new SpriteAnimation(GenericAnimationComponent.Animation.ATTACK, 1);
+ shoot.addFrame(frame1NoAttack);
+
+ staticData.add(idle);
+ staticData.add(shoot);
+
+ setStaticData(GameObjectType.CANNON, staticData);
+ }
+
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.FOREGROUND_OBJECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ LauncherComponent launcher = (LauncherComponent)allocateComponent(LauncherComponent.class);
+ launcher.setLaunchEffect(GameObjectType.SMOKE_POOF, 32.0f, 85.0f);
+
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ launcher.setLaunchSound(sound.load(R.raw.sound_cannon));
+ }
+
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ hitReact.setInvincible(false);
+ hitReact.setLauncherComponent(launcher, HitType.LAUNCH);
+
+ dynamicCollision.setHitReactionComponent(hitReact);
+
+ GenericAnimationComponent anim = (GenericAnimationComponent)allocateComponent(GenericAnimationComponent.class);
+ anim.setSprite(sprite);
+
+ object.team = Team.NONE;
+
+ object.add(render);
+ object.add(sprite);
+ object.add(dynamicCollision);
+ object.add(hitReact);
+ object.add(launcher);
+ object.add(anim);
+
+ addStaticData(GameObjectType.CANNON, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnObjectBrobotSpawner(float positionX, float positionY, boolean flipHorizontal) {
+
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ // This is pretty heavy-handed.
+ // TODO: figure out a general solution for objects that depend on other objects.
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_idle01);
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_idle02);
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_idle03);
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk01);
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk02);
+ textureLibrary.allocateTexture(R.drawable.enemy_brobot_walk03);
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.BROBOT_SPAWNER);
+ if (staticData == null) {
+ final int staticObjectCount = 3;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(32, 32, 32));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.POSSESS);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_brobot_machine),
+ 1.0f, null, basicVulnerabilityVolume));
+
+ SolidSurfaceComponent solidSurface
+ = (SolidSurfaceComponent)allocateComponent(SolidSurfaceComponent.class);
+ solidSurface.inititalize(3);
+ /*
+ 0:2,0:8,59:-0.99532399996093,0.09659262446878
+ 0:8,59:61,33:0.44551558813576,0.89527418187282
+ 0:61,33:61,-1:1,0
+
+ */
+ // trapezoid shape:
+ // |\ |\2
+ // | | 1| |3
+
+ Vector2 surface1Start = new Vector2(0, 0);
+ Vector2 surface1End = new Vector2(8.0f, 59.0f);
+ Vector2 surface1Normal = new Vector2(-0.9953f, 0.0965f);
+ surface1Normal.normalize();
+
+ Vector2 surface2Start = new Vector2(8.0f, 59.0f);
+ Vector2 surface2End = new Vector2(61.0f, 33.0f);
+ Vector2 surface2Normal = new Vector2(0.445515f, 0.89527f);
+ surface2Normal.normalize();
+
+ Vector2 surface3Start = new Vector2(61.0f, 33.0f);
+ Vector2 surface3End = new Vector2(61.0f, 0.0f);
+ Vector2 surface3Normal = new Vector2(1.0f, 0.0f);
+
+ solidSurface.addSurface(surface1Start, surface1End, surface1Normal);
+ solidSurface.addSurface(surface2Start, surface2End, surface2Normal);
+ solidSurface.addSurface(surface3Start, surface3End, surface3Normal);
+
+ GhostComponent ghost = (GhostComponent)allocateComponent(GhostComponent.class);
+ ghost.setTargetAction(ActionType.IDLE);
+ ghost.changeActionOnButton(ActionType.ATTACK);
+
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ ghost.setAmbientSound(sound.load(R.raw.sound_possession));
+ }
+
+ staticData.add(ghost);
+ staticData.add(solidSurface);
+ staticData.add(idle);
+
+
+ setStaticData(GameObjectType.BROBOT_SPAWNER, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent collision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact
+ = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LaunchProjectileComponent gun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ gun.setDelayBeforeFirstSet(3.0f);
+ gun.setObjectTypeToSpawn(GameObjectType.BROBOT);
+ gun.setOffsetX(36);
+ gun.setOffsetY(50);
+ gun.setVelocityX(100.0f);
+ gun.setVelocityY(300.0f);
+ gun.enableProjectileTracking(1);
+
+ LaunchProjectileComponent possessedGun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ possessedGun.setRequiredAction(ActionType.ATTACK);
+ possessedGun.setDelayBeforeFirstSet(0.0f);
+ possessedGun.setObjectTypeToSpawn(GameObjectType.BROBOT_BULLET);
+ possessedGun.setOffsetX(36);
+ possessedGun.setOffsetY(50);
+ possessedGun.setVelocityX(600.0f);
+ possessedGun.setVelocityY(600.0f);
+ possessedGun.setThetaError(0.3f);
+
+ ChangeComponentsComponent componentSwap = (ChangeComponentsComponent)allocateComponent(ChangeComponentsComponent.class);
+ componentSwap.addSwapOutComponent(gun);
+ componentSwap.addSwapInComponent(possessedGun);
+ componentSwap.setPingPongBehavior(true);
+
+ hitReact.setPossessionComponent(componentSwap);
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ } else {
+ object.facingDirection.x = 1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(gun);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(componentSwap);
+
+
+ addStaticData(GameObjectType.BROBOT_SPAWNER, object, sprite);
+
+ object.commitUpdates();
+
+ GhostComponent possessedGhost = object.findByClass(GhostComponent.class);
+ if (possessedGhost != null) {
+ object.remove(possessedGhost); // Not supposed to be added yet.
+ componentSwap.addSwapInComponent(possessedGhost);
+ }
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+public GameObject spawnObjectBreakableBlock(float positionX, float positionY) {
+
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ // Preload block piece texture.
+ textureLibrary.allocateTexture(R.drawable.object_debris_piece);
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.BREAKABLE_BLOCK);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new AABoxCollisionVolume(7, 0, 32 - 7, 42, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 1);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_debris_block),
+ 1.0f, null, basicVulnerabilityVolume));
+
+ SolidSurfaceComponent solidSurface
+ = (SolidSurfaceComponent)allocateComponent(SolidSurfaceComponent.class);
+ solidSurface.inititalize(4);
+
+ // box shape:
+ // ___ ___2
+ // | | 1| |3
+ // --- ---4
+
+ Vector2 surface1Start = new Vector2(0.0f, 0.0f);
+ Vector2 surface1End = new Vector2(0.0f, 32.0f);
+ Vector2 surface1Normal = new Vector2(-1.0f, 0.0f);
+ surface1Normal.normalize();
+
+ Vector2 surface2Start = new Vector2(0.0f, 32.0f);
+ Vector2 surface2End = new Vector2(32.0f, 32.0f);
+ Vector2 surface2Normal = new Vector2(0.0f, 1.0f);
+ surface2Normal.normalize();
+
+ Vector2 surface3Start = new Vector2(32.0f, 32.0f);
+ Vector2 surface3End = new Vector2(32.0f, 0.0f);
+ Vector2 surface3Normal = new Vector2(1.0f, 0.0f);
+
+ Vector2 surface4Start = new Vector2(32.0f, 0.0f);
+ Vector2 surface4End = new Vector2(0.0f, 0.0f);
+ Vector2 surface4Normal = new Vector2(0.0f, -1.0f);
+
+ solidSurface.addSurface(surface1Start, surface1End, surface1Normal);
+ solidSurface.addSurface(surface2Start, surface2End, surface2Normal);
+ solidSurface.addSurface(surface3Start, surface3End, surface3Normal);
+ solidSurface.addSurface(surface4Start, surface4End, surface4Normal);
+
+ staticData.add(solidSurface);
+ staticData.add(idle);
+
+
+ setStaticData(GameObjectType.BREAKABLE_BLOCK, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ DynamicCollisionComponent collision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact
+ = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.BREAKABLE_BLOCK_PIECE_SPAWNER);
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ lifetime.setDeathSound(sound.load(R.raw.sound_break_block));
+ }
+
+ object.life = 1;
+ object.team = Team.ENEMY;
+
+ object.add(render);
+ object.add(sprite);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+
+
+ addStaticData(GameObjectType.BREAKABLE_BLOCK, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnObjectTheSource(float positionX, float positionY) {
+
+ final TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.activationRadius = mAlwaysActive;
+ object.width = 512;
+ object.height = 512;
+ object.getPosition().set(positionX, positionY);
+
+ RenderComponent layer1Render = (RenderComponent)allocateComponent(RenderComponent.class);
+ layer1Render.setPriority(SortConstants.THE_SOURCE_START);
+ FadeDrawableComponent layer1Fade = (FadeDrawableComponent)allocateComponent(FadeDrawableComponent.class);
+ layer1Fade.setRenderComponent(layer1Render);
+ layer1Fade.setTexture(textureLibrary.allocateTexture(R.drawable.enemy_source_spikes));
+ layer1Fade.setupFade(1.0f, 0.2f, 1.9f, FadeDrawableComponent.LOOP_TYPE_PING_PONG, FadeDrawableComponent.FADE_EASE, 0.0f);
+
+ RenderComponent layer2Render = (RenderComponent)allocateComponent(RenderComponent.class);
+ layer2Render.setPriority(SortConstants.THE_SOURCE_START + 1);
+ FadeDrawableComponent layer2Fade = (FadeDrawableComponent)allocateComponent(FadeDrawableComponent.class);
+ layer2Fade.setRenderComponent(layer2Render);
+ layer2Fade.setTexture(textureLibrary.allocateTexture(R.drawable.enemy_source_body));
+ layer2Fade.setupFade(1.0f, 0.8f, 5.0f, FadeDrawableComponent.LOOP_TYPE_PING_PONG, FadeDrawableComponent.FADE_EASE, 0.0f);
+
+ RenderComponent layer3Render = (RenderComponent)allocateComponent(RenderComponent.class);
+ layer3Render.setPriority(SortConstants.THE_SOURCE_START + 2);
+ FadeDrawableComponent layer3Fade = (FadeDrawableComponent)allocateComponent(FadeDrawableComponent.class);
+ layer3Fade.setRenderComponent(layer3Render);
+ layer3Fade.setTexture(textureLibrary.allocateTexture(R.drawable.enemy_source_black));
+ layer3Fade.setupFade(0.0f, 1.0f, 6.0f, FadeDrawableComponent.LOOP_TYPE_PING_PONG, FadeDrawableComponent.FADE_LINEAR, 0.0f);
+
+ RenderComponent layer4Render = (RenderComponent)allocateComponent(RenderComponent.class);
+ layer4Render.setPriority(SortConstants.THE_SOURCE_START + 3);
+ FadeDrawableComponent layer4Fade = (FadeDrawableComponent)allocateComponent(FadeDrawableComponent.class);
+ layer4Fade.setRenderComponent(layer4Render);
+ layer4Fade.setTexture(textureLibrary.allocateTexture(R.drawable.enemy_source_spots));
+ layer4Fade.setupFade(0.0f, 1.0f, 2.3f, FadeDrawableComponent.LOOP_TYPE_PING_PONG, FadeDrawableComponent.FADE_EASE, 0.0f);
+
+ RenderComponent layer5Render = (RenderComponent)allocateComponent(RenderComponent.class);
+ layer5Render.setPriority(SortConstants.THE_SOURCE_START + 4);
+ FadeDrawableComponent layer5Fade = (FadeDrawableComponent)allocateComponent(FadeDrawableComponent.class);
+ layer5Fade.setRenderComponent(layer5Render);
+ layer5Fade.setTexture(textureLibrary.allocateTexture(R.drawable.enemy_source_core));
+ layer5Fade.setupFade(0.2f, 1.0f, 1.2f, FadeDrawableComponent.LOOP_TYPE_PING_PONG, FadeDrawableComponent.FADE_EASE, 0.0f);
+
+
+ OrbitalMagnetComponent orbit = (OrbitalMagnetComponent)allocateComponent(OrbitalMagnetComponent.class);
+ orbit.setup(320.0f, 220.0f);
+
+ DynamicCollisionComponent collision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ FixedSizeArray<CollisionVolume> vulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ vulnerabilityVolume.add(new SphereCollisionVolume(256, 256, 256, HitType.HIT));
+ FixedSizeArray<CollisionVolume> attackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ attackVolume.add(new SphereCollisionVolume(256, 256, 256, HitType.HIT));
+ collision.setCollisionVolumes(attackVolume, vulnerabilityVolume);
+
+ HitReactionComponent hitReact = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+ hitReact.setInvincibleTime(TheSourceComponent.SHAKE_TIME);
+
+ TheSourceComponent theSource = (TheSourceComponent)allocateComponent(TheSourceComponent.class);
+ ChannelSystem.Channel surpriseChannel = null;
+ ChannelSystem channelSystem = BaseObject.sSystemRegistry.channelSystem;
+ surpriseChannel = channelSystem.registerChannel(sSurprisedNPCChannel);
+ theSource.setChannel(surpriseChannel);
+ theSource.setGameEvent(GameFlowEvent.EVENT_SHOW_ANIMATION, AnimationPlayerActivity.WANDA_ENDING);
+
+
+ object.life = 3;
+ object.team = Team.PLAYER;
+
+ object.add(layer1Render);
+ object.add(layer2Render);
+ object.add(layer3Render);
+ object.add(layer4Render);
+ object.add(layer5Render);
+
+ object.add(layer1Fade);
+ object.add(layer2Fade);
+ object.add(layer3Fade);
+ object.add(layer4Fade);
+ object.add(layer5Fade);
+
+ object.add(orbit);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(theSource);
+
+ return object;
+ }
+
+ public GameObject spawnObjectTurret(float positionX, float positionY, boolean flipHorizontal) {
+
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ // Make sure related textures are loaded.
+ textureLibrary.allocateTexture(R.drawable.effect_bullet01);
+ textureLibrary.allocateTexture(R.drawable.effect_bullet02);
+
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.TURRET);
+ if (staticData == null) {
+ final int staticObjectCount = 3;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ // Animations
+ FixedSizeArray<CollisionVolume> basicVulnerabilityVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicVulnerabilityVolume.add(new SphereCollisionVolume(32, 32, 32));
+ basicVulnerabilityVolume.get(0).setHitType(HitType.POSSESS);
+
+ SpriteAnimation idle = new SpriteAnimation(EnemyAnimations.IDLE.ordinal(), 2);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_gunturret01),
+ 1.0f, null, basicVulnerabilityVolume));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_gunturret_idle),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ idle.setLoop(true);
+
+ SpriteAnimation attack = new SpriteAnimation(EnemyAnimations.ATTACK.ordinal(), 4);
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_gunturret02),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_gunturret01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_gunturret03),
+ Utils.framesToTime(24, 2), null, basicVulnerabilityVolume));
+ attack.addFrame(new AnimationFrame(
+ textureLibrary.allocateTexture(R.drawable.object_gunturret01),
+ Utils.framesToTime(24, 1), null, basicVulnerabilityVolume));
+ attack.setLoop(true);
+
+ GhostComponent ghost = (GhostComponent)allocateComponent(GhostComponent.class);
+ ghost.setTargetAction(ActionType.IDLE);
+ ghost.changeActionOnButton(ActionType.ATTACK);
+
+ staticData.add(idle);
+ staticData.add(attack);
+ staticData.add(ghost);
+
+
+ setStaticData(GameObjectType.TURRET, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ GenericAnimationComponent animation
+ = (GenericAnimationComponent)allocateComponent(GenericAnimationComponent.class);
+ animation.setSprite(sprite);
+
+ AttackAtDistanceComponent attack = (AttackAtDistanceComponent)
+ allocateComponent(AttackAtDistanceComponent.class);
+ attack.setupAttack(300, 0.0f, 1.0f, true);
+
+
+ DynamicCollisionComponent collision
+ = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(collision);
+
+ HitReactionComponent hitReact
+ = (HitReactionComponent)allocateComponent(HitReactionComponent.class);
+ collision.setHitReactionComponent(hitReact);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setObjectToSpawnOnDeath(GameObjectType.EXPLOSION_LARGE);
+
+ SoundSystem sound = sSystemRegistry.soundSystem;
+
+ LaunchProjectileComponent gun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ gun.setShotsPerSet(1);
+ gun.setDelayBetweenShots(0.0f);
+ gun.setDelayBetweenSets(0.3f);
+ gun.setObjectTypeToSpawn(GameObjectType.TURRET_BULLET);
+ gun.setOffsetX(54);
+ gun.setOffsetY(13);
+ gun.setRequiredAction(GameObject.ActionType.ATTACK);
+ gun.setVelocityX(300.0f);
+ gun.setVelocityY(-300.0f);
+ gun.setShootSound(sound.load(R.raw.sound_gun));
+
+ // Components for possession
+
+ ChangeComponentsComponent componentSwap = (ChangeComponentsComponent)allocateComponent(ChangeComponentsComponent.class);
+ componentSwap.addSwapOutComponent(attack);
+ componentSwap.setPingPongBehavior(true);
+
+ hitReact.setPossessionComponent(componentSwap);
+
+ object.team = Team.ENEMY;
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ } else {
+ object.facingDirection.x = 1.0f;
+ }
+
+ object.add(render);
+ object.add(sprite);
+ object.add(animation);
+ object.add(attack);
+ object.add(collision);
+ object.add(hitReact);
+ object.add(lifetime);
+ object.add(gun);
+ object.add(componentSwap);
+
+ addStaticData(GameObjectType.TURRET, object, sprite);
+
+ object.commitUpdates();
+
+ GhostComponent possessedGhost = object.findByClass(GhostComponent.class);
+ if (possessedGhost != null) {
+ object.remove(possessedGhost); // Not supposed to be added yet.
+ componentSwap.addSwapInComponent(possessedGhost);
+ }
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnDust(float positionX, float positionY, boolean flipHorizontal) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.DUST);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 5);
+ idle.addFrame(new AnimationFrame(textureLibrary.getTextureByResource(R.drawable.dust01),
+ Utils.framesToTime(24, 1)));
+ idle.addFrame(new AnimationFrame(textureLibrary.getTextureByResource(R.drawable.dust02),
+ Utils.framesToTime(24, 1)));
+ idle.addFrame(new AnimationFrame(textureLibrary.getTextureByResource(R.drawable.dust03),
+ Utils.framesToTime(24, 1)));
+ idle.addFrame(new AnimationFrame(textureLibrary.getTextureByResource(R.drawable.dust04),
+ Utils.framesToTime(24, 1)));
+ idle.addFrame(new AnimationFrame(textureLibrary.getTextureByResource(R.drawable.dust05),
+ Utils.framesToTime(24, 1)));
+
+ staticData.add(idle);
+ setStaticData(GameObjectType.DUST, staticData);
+ }
+
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(0.30f);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+
+ if (flipHorizontal) {
+ object.facingDirection.x = -1.0f;
+ }
+ object.destroyOnDeactivation = true;
+
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+
+ addStaticData(GameObjectType.DUST, object, sprite);
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEffectExplosionSmall(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.EXPLOSION_SMALL);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(16, 16, 16, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 7);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small01),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small02),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small03),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small04),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small05),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small06),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small07),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+
+ staticData.add(idle);
+ setStaticData(GameObjectType.EXPLOSION_SMALL, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+
+ DynamicCollisionComponent dynamicCollision = (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ object.add(dynamicCollision);
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+
+ addStaticData(GameObjectType.EXPLOSION_SMALL, object, sprite);
+
+ final SpriteAnimation idle = sprite.findAnimation(0);
+ if (idle != null) {
+ lifetime.setTimeUntilDeath(idle.getLength());
+ }
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEffectExplosionLarge(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.EXPLOSION_LARGE);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume =
+ new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(32, 32, 32, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 9);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big01),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big02),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big03),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big04),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big05),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big06),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big07),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big08),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big09),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+
+
+ staticData.add(idle);
+ setStaticData(GameObjectType.EXPLOSION_LARGE, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+
+ DynamicCollisionComponent dynamicCollision =
+ (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ PlaySingleSoundComponent soundEffect = (PlaySingleSoundComponent)allocateComponent(PlaySingleSoundComponent.class);
+ soundEffect.setSound(sSystemRegistry.soundSystem.load(R.raw.quick_explosion));
+
+
+ object.add(soundEffect);
+ object.add(dynamicCollision);
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+
+ addStaticData(GameObjectType.EXPLOSION_LARGE, object, sprite);
+
+ final SpriteAnimation idle = sprite.findAnimation(0);
+ if (idle != null) {
+ lifetime.setTimeUntilDeath(idle.getLength());
+ }
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEffectExplosionGiant(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.EXPLOSION_GIANT);
+ if (staticData == null) {
+ final int staticObjectCount = 4;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ FixedSizeArray<CollisionVolume> basicAttackVolume = new FixedSizeArray<CollisionVolume>(1);
+ basicAttackVolume.add(new SphereCollisionVolume(64, 32, 32, HitType.HIT));
+
+ SpriteAnimation idle = new SpriteAnimation(0, 9);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big01),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big02),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big03),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big04),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big05),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big06),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big07),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big08),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_big09),
+ Utils.framesToTime(24, 1), basicAttackVolume, null));
+
+
+ AnimationFrame smallFrame1 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small01),
+ Utils.framesToTime(24, 1), null, null);
+ AnimationFrame smallFrame2 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small02),
+ Utils.framesToTime(24, 1), null, null);
+ AnimationFrame smallFrame3 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small03),
+ Utils.framesToTime(24, 1), null, null);
+ AnimationFrame smallFrame4 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small04),
+ Utils.framesToTime(24, 1), null, null);
+ AnimationFrame smallFrame5 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small05),
+ Utils.framesToTime(24, 1), null, null);
+ AnimationFrame smallFrame6 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_explosion_small06),
+ Utils.framesToTime(24, 1), null, null);
+ AnimationFrame smallFrame7 = new AnimationFrame
+ (textureLibrary.getTextureByResource(R.drawable.effect_explosion_small07),
+ Utils.framesToTime(24, 1), null, null);
+
+ SpriteAnimation smallBlast1 = new SpriteAnimation(0, 7);
+ smallBlast1.addFrame(smallFrame1);
+ smallBlast1.addFrame(smallFrame2);
+ smallBlast1.addFrame(smallFrame3);
+ smallBlast1.addFrame(smallFrame4);
+ smallBlast1.addFrame(smallFrame5);
+ smallBlast1.addFrame(smallFrame6);
+ smallBlast1.addFrame(smallFrame7);
+
+ SpriteAnimation smallBlast2 = new SpriteAnimation(0, 8);
+ smallBlast2.addFrame(new AnimationFrame(null, Utils.framesToTime(24, 4), null, null));
+ smallBlast2.addFrame(smallFrame1);
+ smallBlast2.addFrame(smallFrame2);
+ smallBlast2.addFrame(smallFrame3);
+ smallBlast2.addFrame(smallFrame4);
+ smallBlast2.addFrame(smallFrame5);
+ smallBlast2.addFrame(smallFrame6);
+ smallBlast2.addFrame(smallFrame7);
+
+ SpriteAnimation smallBlast3 = new SpriteAnimation(0, 8);
+ smallBlast3.addFrame(new AnimationFrame(null, Utils.framesToTime(24, 8), null, null));
+ smallBlast3.addFrame(smallFrame1);
+ smallBlast3.addFrame(smallFrame2);
+ smallBlast3.addFrame(smallFrame3);
+ smallBlast3.addFrame(smallFrame4);
+ smallBlast3.addFrame(smallFrame5);
+ smallBlast3.addFrame(smallFrame6);
+ smallBlast3.addFrame(smallFrame7);
+
+
+ staticData.add(idle);
+ staticData.add(smallBlast1);
+ staticData.add(smallBlast2);
+ staticData.add(smallBlast3);
+
+ setStaticData(GameObjectType.EXPLOSION_GIANT, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ // Hack. Use static data differently for this object so we can share three animations
+ // amongst three separate sprites.
+
+ final SpriteAnimation idle = (SpriteAnimation)staticData.get(0);
+ final SpriteAnimation smallBlast1 = (SpriteAnimation)staticData.get(1);
+ final SpriteAnimation smallBlast2 = (SpriteAnimation)staticData.get(2);
+ final SpriteAnimation smallBlast3 = (SpriteAnimation)staticData.get(3);
+
+ sprite.addAnimation(idle);
+ sprite.playAnimation(0);
+
+ RenderComponent blast1Render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+ SpriteComponent blast1Sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ blast1Sprite.setSize(32, 32);
+ blast1Sprite.setRenderComponent(blast1Render);
+ blast1Render.setDrawOffset(40, 50);
+ blast1Sprite.addAnimation(smallBlast1);
+ blast1Sprite.playAnimation(0);
+
+ RenderComponent blast2Render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+ SpriteComponent blast2Sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ blast2Sprite.setSize(32, 32);
+ blast2Sprite.setRenderComponent(blast2Render);
+ blast2Render.setDrawOffset(-10, 0);
+ blast2Sprite.addAnimation(smallBlast2);
+ blast2Sprite.playAnimation(0);
+
+ RenderComponent blast3Render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+ SpriteComponent blast3Sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ blast3Sprite.setSize(32, 32);
+ blast3Sprite.setRenderComponent(blast3Render);
+ blast3Render.setDrawOffset(0, 32);
+ blast3Sprite.addAnimation(smallBlast3);
+ blast3Sprite.playAnimation(0);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(Math.max(
+ Math.max(
+ Math.max(idle.getLength(), smallBlast1.getLength()),
+ smallBlast2.getLength()),
+ smallBlast3.getLength()));
+
+ DynamicCollisionComponent dynamicCollision =
+ (DynamicCollisionComponent)allocateComponent(DynamicCollisionComponent.class);
+ sprite.setCollisionComponent(dynamicCollision);
+
+ PlaySingleSoundComponent soundEffect = (PlaySingleSoundComponent)allocateComponent(PlaySingleSoundComponent.class);
+ soundEffect.setSound(sSystemRegistry.soundSystem.load(R.raw.quick_explosion));
+
+
+
+ object.team = Team.PLAYER; // Maybe this should be an argument to this function.
+
+ object.add(dynamicCollision);
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+ object.add(soundEffect);
+
+
+ object.add(blast1Render);
+ object.add(blast1Sprite);
+
+ object.add(blast2Render);
+ object.add(blast2Sprite);
+
+ object.add(blast3Render);
+ object.add(blast3Sprite);
+
+
+ return object;
+ }
+
+
+ public GameObject spawnGhostNPC(float positionX, float positionY) {
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.GHOST_NPC);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ staticData.add(gravity);
+ staticData.add(movement);
+
+
+ setStaticData(GameObjectType.GHOST_NPC, staticData);
+ }
+
+ NPCComponent patrol = (NPCComponent)allocateComponent(NPCComponent.class);
+ LifetimeComponent life = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+
+ object.team = Team.NONE;
+ object.life = 1;
+
+ object.add(patrol);
+ object.add(life);
+
+ addStaticData(GameObjectType.GHOST_NPC, object, null);
+ return object;
+ }
+
+ private GameObject spawnCameraBias(float positionX, float positionY) {
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.CAMERA_BIAS);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent bias = allocateComponent(CameraBiasComponent.class);
+
+ staticData.add(bias);
+
+ setStaticData(GameObjectType.CAMERA_BIAS, staticData);
+ }
+
+ addStaticData(GameObjectType.CAMERA_BIAS, object, null);
+ return object;
+ }
+
+ public GameObject spawnEffectSmokeBig(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.SMOKE_BIG);
+ if (staticData == null) {
+ final int staticObjectCount = 6;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+
+ AnimationFrame frame2 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big02),
+ Utils.framesToTime(24, 1), null, null);
+
+ AnimationFrame frame3 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big03),
+ Utils.framesToTime(24, 1), null, null);
+
+ AnimationFrame frame4 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big04),
+ Utils.framesToTime(24, 1), null, null);
+
+ AnimationFrame frame5 = new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big05),
+ Utils.framesToTime(24, 1), null, null);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 5);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big01),
+ Utils.framesToTime(24, 10), null, null));
+ idle.addFrame(frame2);
+ idle.addFrame(frame3);
+ idle.addFrame(frame4);
+ idle.addFrame(frame5);
+
+ SpriteAnimation idle2 = new SpriteAnimation(1, 5);
+ idle2.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big01),
+ Utils.framesToTime(24, 13), null, null));
+ idle2.addFrame(frame2);
+ idle2.addFrame(frame3);
+ idle2.addFrame(frame4);
+ idle2.addFrame(frame5);
+
+ SpriteAnimation idle3 = new SpriteAnimation(2, 5);
+ idle3.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big01),
+ Utils.framesToTime(24, 8), null, null));
+ idle3.addFrame(frame2);
+ idle3.addFrame(frame3);
+ idle3.addFrame(frame4);
+ idle3.addFrame(frame5);
+
+ SpriteAnimation idle4 = new SpriteAnimation(3, 5);
+ idle4.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big01),
+ Utils.framesToTime(24, 5), null, null));
+ idle4.addFrame(frame2);
+ idle4.addFrame(frame3);
+ idle4.addFrame(frame4);
+ idle4.addFrame(frame5);
+
+ SpriteAnimation idle5 = new SpriteAnimation(4, 5);
+ idle5.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_big01),
+ Utils.framesToTime(24, 15), null, null));
+ idle5.addFrame(frame2);
+ idle5.addFrame(frame3);
+ idle5.addFrame(frame4);
+ idle5.addFrame(frame5);
+
+ staticData.add(idle);
+ staticData.add(idle2);
+ staticData.add(idle3);
+ staticData.add(idle4);
+ staticData.add(idle5);
+ staticData.add(movement);
+ setStaticData(GameObjectType.SMOKE_BIG, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setDieWhenInvisible(true);
+
+ object.destroyOnDeactivation = true;
+
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+
+ addStaticData(GameObjectType.SMOKE_BIG, object, sprite);
+
+ final int animIndex = (int)(Math.random() * sprite.getAnimationCount());
+ final SpriteAnimation idle = sprite.findAnimation(animIndex);
+ if (idle != null) {
+ lifetime.setTimeUntilDeath(idle.getLength());
+ sprite.playAnimation(animIndex);
+ }
+
+
+
+ return object;
+ }
+
+ public GameObject spawnEffectSmokeSmall(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 16;
+ object.height = 16;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.SMOKE_SMALL);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ SpriteAnimation idle = new SpriteAnimation(0, 5);
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_small01),
+ Utils.framesToTime(24, 10), null, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_small02),
+ Utils.framesToTime(24, 1), null, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_small03),
+ Utils.framesToTime(24, 1), null, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_small04),
+ Utils.framesToTime(24, 1), null, null));
+ idle.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_smoke_small05),
+ Utils.framesToTime(24, 1), null, null));
+
+ staticData.add(idle);
+ staticData.add(movement);
+ setStaticData(GameObjectType.SMOKE_SMALL, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+
+ SpriteComponent sprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ sprite.setSize((int)object.width, (int)object.height);
+ sprite.setRenderComponent(render);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setDieWhenInvisible(true);
+
+ object.destroyOnDeactivation = true;
+
+ object.add(lifetime);
+ object.add(render);
+ object.add(sprite);
+
+ addStaticData(GameObjectType.SMOKE_SMALL, object, sprite);
+
+ final SpriteAnimation idle = sprite.findAnimation(0);
+ if (idle != null) {
+ lifetime.setTimeUntilDeath(idle.getLength());
+ }
+
+ sprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnEffectCrushFlash(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.CRUSH_FLASH);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ SpriteAnimation back = new SpriteAnimation(0, 3);
+ back.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_back01),
+ Utils.framesToTime(24, 1), null, null));
+ back.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_back02),
+ Utils.framesToTime(24, 1), null, null));
+ back.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_back03),
+ Utils.framesToTime(24, 1), null, null));
+
+ SpriteAnimation front = new SpriteAnimation(1, 7);
+ front.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_front01),
+ Utils.framesToTime(24, 1), null, null));
+ front.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_front02),
+ Utils.framesToTime(24, 1), null, null));
+ front.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_front03),
+ Utils.framesToTime(24, 1), null, null));
+ front.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_front04),
+ Utils.framesToTime(24, 1), null, null));
+ front.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_front05),
+ Utils.framesToTime(24, 1), null, null));
+ front.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_front06),
+ Utils.framesToTime(24, 1), null, null));
+ front.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_front07),
+ Utils.framesToTime(24, 1), null, null));
+
+
+ staticData.add(back);
+ staticData.add(front);
+ setStaticData(GameObjectType.CRUSH_FLASH, staticData);
+ }
+
+
+ RenderComponent backRender = (RenderComponent)allocateComponent(RenderComponent.class);
+ backRender.setPriority(SortConstants.EFFECT);
+
+ SpriteComponent backSprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ backSprite.setSize((int)object.width, (int)object.height);
+ backSprite.setRenderComponent(backRender);
+
+ RenderComponent foreRender = (RenderComponent)allocateComponent(RenderComponent.class);
+ foreRender.setPriority(SortConstants.FOREGROUND_EFFECT);
+
+ SpriteComponent foreSprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ foreSprite.setSize((int)object.width, (int)object.height);
+ foreSprite.setRenderComponent(foreRender);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+
+
+ object.add(lifetime);
+ object.add(backRender);
+ object.add(foreRender);
+ object.add(foreSprite);
+ object.add(backSprite);
+
+ addStaticData(GameObjectType.CRUSH_FLASH, object, backSprite);
+ addStaticData(GameObjectType.CRUSH_FLASH, null, foreSprite);
+
+
+ final SpriteAnimation idle = foreSprite.findAnimation(1);
+ if (idle != null) {
+ lifetime.setTimeUntilDeath(idle.getLength());
+ }
+
+ backSprite.playAnimation(0);
+ foreSprite.playAnimation(1);
+
+ return object;
+ }
+
+ public GameObject spawnEffectFlash(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.longTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mAlwaysActive;
+ object.width = 64;
+ object.height = 64;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.FLASH);
+ if (staticData == null) {
+ final int staticObjectCount = 1;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ SpriteAnimation back = new SpriteAnimation(0, 3);
+ back.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_back01),
+ Utils.framesToTime(24, 1), null, null));
+ back.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_back02),
+ Utils.framesToTime(24, 1), null, null));
+ back.addFrame(new AnimationFrame(
+ textureLibrary.getTextureByResource(R.drawable.effect_crush_back03),
+ Utils.framesToTime(24, 1), null, null));
+
+
+ staticData.add(back);
+ setStaticData(GameObjectType.FLASH, staticData);
+ }
+
+
+ RenderComponent backRender = (RenderComponent)allocateComponent(RenderComponent.class);
+ backRender.setPriority(SortConstants.EFFECT);
+
+ SpriteComponent backSprite = (SpriteComponent)allocateComponent(SpriteComponent.class);
+ backSprite.setSize((int)object.width, (int)object.height);
+ backSprite.setRenderComponent(backRender);
+
+
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+
+
+ object.add(lifetime);
+ object.add(backRender);
+ object.add(backSprite);
+
+ addStaticData(GameObjectType.FLASH, object, backSprite);
+
+
+ final SpriteAnimation idle = backSprite.findAnimation(0);
+ if (idle != null) {
+ lifetime.setTimeUntilDeath(idle.getLength());
+ }
+
+ backSprite.playAnimation(0);
+
+ return object;
+ }
+
+ public GameObject spawnBreakableBlockPiece(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 16;
+ object.height = 16;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.BREAKABLE_BLOCK_PIECE);
+ if (staticData == null) {
+ final int staticObjectCount = 4;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent gravity = allocateComponent(GravityComponent.class);
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ SimplePhysicsComponent physics = (SimplePhysicsComponent)allocateComponent(SimplePhysicsComponent.class);
+ physics.setBounciness(0.3f);
+
+ DrawableBitmap piece = new DrawableBitmap(
+ textureLibrary.getTextureByResource(R.drawable.object_debris_piece),
+ (int)object.width,
+ (int)object.height);
+
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.GENERAL_OBJECT);
+ render.setDrawable(piece);
+
+ staticData.add(render);
+ staticData.add(movement);
+ staticData.add(gravity);
+ staticData.add(physics);
+ setStaticData(GameObjectType.BREAKABLE_BLOCK_PIECE, staticData);
+ }
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(3.0f);
+
+ BackgroundCollisionComponent bgcollision = (BackgroundCollisionComponent)allocateComponent(BackgroundCollisionComponent.class);
+ bgcollision.setSize(12, 12);
+ bgcollision.setOffset(2, 2);
+
+
+ object.destroyOnDeactivation = true;
+
+ object.add(lifetime);
+ object.add(bgcollision);
+
+ addStaticData(GameObjectType.BREAKABLE_BLOCK_PIECE, object, null);
+
+ return object;
+ }
+
+ public GameObject spawnBreakableBlockPieceSpawner(float positionX, float positionY) {
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 1;
+ object.height = 1;
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(0.5f);
+
+ LaunchProjectileComponent pieceSpawner
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ pieceSpawner.setObjectTypeToSpawn(GameObjectType.BREAKABLE_BLOCK_PIECE);
+ pieceSpawner.setDelayBeforeFirstSet(0.0f);
+ pieceSpawner.setSetsPerActivation(1);
+ pieceSpawner.setShotsPerSet(3);
+ pieceSpawner.setDelayBetweenShots(0.0f);
+ pieceSpawner.setOffsetX(16);
+ pieceSpawner.setOffsetY(16);
+ pieceSpawner.setVelocityX(600.0f);
+ pieceSpawner.setVelocityY(-1000.0f);
+ pieceSpawner.setThetaError(1.0f);
+
+ object.life = 1;
+ object.destroyOnDeactivation = true;
+
+ object.add(lifetime);
+ object.add(pieceSpawner);
+
+ return object;
+ }
+
+ public GameObject spawnSmokePoof(float positionX, float positionY) {
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 1;
+ object.height = 1;
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(0.5f);
+
+ LaunchProjectileComponent smokeGun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ smokeGun.setSetsPerActivation(1);
+ smokeGun.setShotsPerSet(3);
+ smokeGun.setDelayBetweenShots(0.0f);
+ smokeGun.setObjectTypeToSpawn(GameObjectType.SMOKE_BIG);
+ smokeGun.setVelocityX(200.0f);
+ smokeGun.setVelocityY(200.0f);
+ smokeGun.setOffsetX(16);
+ smokeGun.setOffsetY(16);
+ smokeGun.setThetaError(1.0f);
+
+ LaunchProjectileComponent smokeGun2
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ smokeGun2.setSetsPerActivation(1);
+ smokeGun2.setShotsPerSet(3);
+ smokeGun2.setDelayBetweenShots(0.0f);
+ smokeGun2.setObjectTypeToSpawn(GameObjectType.SMOKE_SMALL);
+ smokeGun2.setVelocityX(200.0f);
+ smokeGun2.setVelocityY(200.0f);
+ smokeGun2.setThetaError(1.0f);
+ smokeGun2.setOffsetX(16);
+ smokeGun2.setOffsetY(16);
+
+ object.life = 1;
+ object.destroyOnDeactivation = true;
+
+ object.add(lifetime);
+ object.add(smokeGun);
+ object.add(smokeGun2);
+
+ return object;
+ }
+
+ public GameObject spawnGemEffect(float positionX, float positionY) {
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 32;
+ object.height = 32;
+
+ FixedSizeArray<BaseObject> staticData = getStaticData(GameObjectType.GEM_EFFECT);
+ if (staticData == null) {
+ final int staticObjectCount = 2;
+ staticData = new FixedSizeArray<BaseObject>(staticObjectCount);
+
+ GameComponent movement = allocateComponent(MovementComponent.class);
+
+ staticData.add(movement);
+
+ setStaticData(GameObjectType.GEM_EFFECT, staticData);
+ }
+
+ RenderComponent render = (RenderComponent)allocateComponent(RenderComponent.class);
+ render.setPriority(SortConstants.EFFECT);
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(0.5f);
+
+ FadeDrawableComponent fadeOut = (FadeDrawableComponent)allocateComponent(FadeDrawableComponent.class);
+ fadeOut.setupFade(1.0f, 0.0f, 0.5f, FadeDrawableComponent.LOOP_TYPE_NONE, FadeDrawableComponent.FADE_LINEAR, 0.0f);
+ fadeOut.setTexture(textureLibrary.allocateTexture(R.drawable.object_ruby01));
+ fadeOut.setRenderComponent(render);
+
+ object.destroyOnDeactivation = true;
+
+ object.add(lifetime);
+ object.add(fadeOut);
+ object.add(render);
+
+ addStaticData(GameObjectType.GEM_EFFECT, object, null);
+
+ return object;
+ }
+
+ public GameObject spawnGemEffectSpawner(float positionX, float positionY) {
+
+ GameObject object = mGameObjectPool.allocate();
+ object.getPosition().set(positionX, positionY);
+ object.activationRadius = mTightActivationRadius;
+ object.width = 1;
+ object.height = 1;
+
+ LifetimeComponent lifetime = (LifetimeComponent)allocateComponent(LifetimeComponent.class);
+ lifetime.setTimeUntilDeath(0.5f);
+
+ final int gems = 6;
+ final float angleIncrement = (float)(2.0f * Math.PI) / gems;
+ for (int x = 0; x < gems; x++) {
+ LaunchProjectileComponent gemGun
+ = (LaunchProjectileComponent)allocateComponent(LaunchProjectileComponent.class);
+ gemGun.setSetsPerActivation(1);
+ gemGun.setShotsPerSet(1);
+ gemGun.setDelayBetweenShots(0.0f);
+ gemGun.setObjectTypeToSpawn(GameObjectType.GEM_EFFECT);
+ gemGun.setVelocityX((float)Math.sin(angleIncrement * x) * 150.0f);
+ gemGun.setVelocityY((float)Math.cos(angleIncrement * x) * 150.0f);
+ gemGun.setOffsetX(16);
+ gemGun.setOffsetY(16);
+
+ object.add(gemGun);
+ }
+
+
+
+ object.life = 1;
+ object.destroyOnDeactivation = true;
+
+ object.add(lifetime);
+
+
+ return object;
+ }
+
+ /** Comparator for game objects objects. */
+ private final static class ComponentPoolComparator implements Comparator<GameComponentPool> {
+ public int compare(final GameComponentPool object1, final GameComponentPool object2) {
+ int result = 0;
+ if (object1 == null && object2 != null) {
+ result = 1;
+ } else if (object1 != null && object2 == null) {
+ result = -1;
+ } else if (object1 != null && object2 != null) {
+ result = object1.objectClass.hashCode() - object2.objectClass.hashCode();
+ }
+ return result;
+ }
+ }
+
+ public class GameObjectPool extends TObjectPool<GameObject> {
+
+ public GameObjectPool() {
+ super();
+ }
+
+ public GameObjectPool(int size) {
+ super(size);
+ }
+
+ @Override
+ protected void fill() {
+ for (int x = 0; x < getSize(); x++) {
+ getAvailable().add(new GameObject());
+ }
+ }
+
+ @Override
+ public void release(Object entry) {
+ ((GameObject)entry).reset();
+ super.release(entry);
+ }
+
+ }
+}
+
diff --git a/src/com/replica/replicaisland/GameObjectManager.java b/src/com/replica/replicaisland/GameObjectManager.java
new file mode 100644
index 0000000..bea2861
--- /dev/null
+++ b/src/com/replica/replicaisland/GameObjectManager.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+/**
+ * A node in the game graph that manages the activation status of its children. The
+ * GameObjectManager moves the objects it manages in and out of the active list (that is,
+ * in and out of the game tree, causing them to be updated or ignored, respectively) each frame
+ * based on the distance of that object to the camera. Objects may specify an "activation radius"
+ * to define an area around themselves so that the position of the camera can be used to determine
+ * which objects should receive processing time and which should be ignored. Objects that do not
+ * move should have an activation radius that defines a sphere similar to the size of the screen;
+ * they only need processing when they are visible. Objects that move around will probably need
+ * larger regions so that they can leave the visible area of the game world and not be immediately
+ * deactivated.
+ */
+public class GameObjectManager extends ObjectManager {
+ private static final int MAX_GAME_OBJECTS = 256;
+ private float mMaxActivationRadius;
+ private final static HorizontalPositionComparator sGameObjectComparator
+ = new HorizontalPositionComparator();
+ private FixedSizeArray<BaseObject> mInactiveObjects;
+ private FixedSizeArray<GameObject> mMarkedForDeathObjects;
+ private GameObject mPlayer;
+ private boolean mVisitingGraph;
+ private Vector2 mCameraFocus;
+
+
+ public GameObjectManager(float maxActivationRadius) {
+ super(MAX_GAME_OBJECTS);
+ mMaxActivationRadius = maxActivationRadius;
+
+ mInactiveObjects = new FixedSizeArray<BaseObject>(MAX_GAME_OBJECTS);
+ mInactiveObjects.setComparator(sGameObjectComparator);
+
+ mMarkedForDeathObjects = new FixedSizeArray<GameObject>(MAX_GAME_OBJECTS);
+ mVisitingGraph = false;
+
+ mCameraFocus = new Vector2();
+
+ }
+
+ @Override
+ public void commitUpdates() {
+ super.commitUpdates();
+
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ final int objectsToKillCount = mMarkedForDeathObjects.getCount();
+ if (factory != null && objectsToKillCount > 0) {
+ final Object[] deathArray = mMarkedForDeathObjects.getArray();
+ for (int x = 0; x < objectsToKillCount; x++) {
+ factory.destroy((GameObject)deathArray[x]);
+ }
+ mMarkedForDeathObjects.clear();
+ }
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ commitUpdates();
+
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+
+ mCameraFocus.set(camera.getFocusPositionX(), camera.getFocusPositionY());
+ mVisitingGraph = true;
+ FixedSizeArray<BaseObject> objects = getObjects();
+ final int count = objects.getCount();
+
+ if (count > 0) {
+ final Object[] objectArray = objects.getArray();
+ for (int i = count - 1; i >= 0; i--) {
+ GameObject gameObject = (GameObject)objectArray[i];
+ final float distance2 = mCameraFocus.distance2(gameObject.getPosition());
+ if (distance2 < (gameObject.activationRadius * gameObject.activationRadius)
+ || gameObject.activationRadius == -1) {
+ gameObject.update(timeDelta, this);
+ } else {
+ // Remove the object from the list.
+ // It's safe to just swap the current object with the last
+ // object because this list is being iterated backwards, so
+ // the last object in the list has already been processed.
+ objects.swapWithLast(i);
+ objects.removeLast();
+ if (gameObject.destroyOnDeactivation) {
+ mMarkedForDeathObjects.add(gameObject);
+ } else {
+ mInactiveObjects.add((BaseObject)gameObject);
+ }
+ }
+ }
+ }
+
+ mInactiveObjects.sort(false);
+ final int inactiveCount = mInactiveObjects.getCount();
+ if (inactiveCount > 0) {
+ final Object[] inactiveArray = mInactiveObjects.getArray();
+ for (int i = inactiveCount - 1; i >= 0; i--) {
+ GameObject gameObject = (GameObject)inactiveArray[i];
+
+ final Vector2 position = gameObject.getPosition();
+ final float distance2 = mCameraFocus.distance2(position);
+ final float xDistance = position.x - mCameraFocus.x;
+ if (distance2 < (gameObject.activationRadius * gameObject.activationRadius)
+ || gameObject.activationRadius == -1) {
+ gameObject.update(timeDelta, this);
+ mInactiveObjects.swapWithLast(i);
+ mInactiveObjects.removeLast();
+ objects.add(gameObject);
+ } else if (xDistance < -mMaxActivationRadius) {
+ // We've passed the focus, we can stop processing now
+ break;
+ }
+ }
+ }
+ mVisitingGraph = false;
+ }
+
+
+ @Override
+ public void add(BaseObject object) {
+ if (object instanceof GameObject) {
+ super.add(object);
+ }
+ }
+
+ @Override
+ public void remove(BaseObject object) {
+ super.remove(object);
+ if (object == mPlayer) {
+ mPlayer = null;
+ }
+ }
+
+ public void destroy(GameObject object) {
+ mMarkedForDeathObjects.add(object);
+ remove(object);
+ }
+
+ public void destroyAll() {
+ assert mVisitingGraph == false;
+ commitUpdates();
+
+ FixedSizeArray<BaseObject> objects = getObjects();
+ final int count = objects.getCount();
+ for (int i = count - 1; i >= 0; i--) {
+ mMarkedForDeathObjects.add((GameObject)objects.get(i));
+ objects.remove(i);
+ }
+
+ final int inactiveObjectCount = mInactiveObjects.getCount();
+ for (int j = inactiveObjectCount - 1; j >= 0; j--) {
+ mMarkedForDeathObjects.add((GameObject)mInactiveObjects.get(j));
+ mInactiveObjects.remove(j);
+ }
+
+ mPlayer = null;
+ }
+
+ public void setPlayer(GameObject player) {
+ mPlayer = player;
+ }
+
+ public GameObject getPlayer() {
+ return mPlayer;
+ }
+
+ /** Comparator for game objects objects. */
+ private final static class HorizontalPositionComparator implements Comparator<BaseObject> {
+ public int compare(BaseObject object1, BaseObject object2) {
+ int result = 0;
+ if (object1 == null && object2 != null) {
+ result = 1;
+ } else if (object1 != null && object2 == null) {
+ result = -1;
+ } else if (object1 != null && object2 != null) {
+ float delta = ((GameObject) object1).getPosition().x
+ - ((GameObject) object2).getPosition().x;
+ if (delta < 0) {
+ result = -1;
+ } else if (delta > 0) {
+ result = 1;
+ }
+ }
+ return result;
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/GameRenderer.java b/src/com/replica/replicaisland/GameRenderer.java
new file mode 100644
index 0000000..3acd198
--- /dev/null
+++ b/src/com/replica/replicaisland/GameRenderer.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.SystemClock;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+import com.replica.replicaisland.RenderSystem.RenderElement;
+
+/**
+ * GameRenderer the top-level rendering interface for the game engine. It is called by
+ * GLSurfaceView and is responsible for submitting commands to OpenGL. GameRenderer receives a
+ * queue of renderable objects from the thread and uses that to draw the scene every frame. If
+ * no queue is available then no drawing is performed. If the queue is not changed from frame to
+ * frame, the same scene will be redrawn every frame.
+ * The GameRenderer also invokes texture loads when it is activated.
+ */
+public class GameRenderer implements GLSurfaceView.Renderer {
+ private static final int PROFILE_REPORT_DELAY = 3 * 1000;
+
+ private int mWidth;
+ private int mHeight;
+ private int mHalfWidth;
+ private int mHalfHeight;
+
+ private float mScaleX;
+ private float mScaleY;
+ private Context mContext;
+ private long mLastTime;
+ private int mProfileFrames;
+ private long mProfileWaitTime;
+ private long mProfileFrameTime;
+ private long mProfileSubmitTime;
+ private int mProfileObjectCount;
+
+ private ObjectManager mDrawQueue;
+ private boolean mDrawQueueChanged;
+ private Game mGame;
+ private Object mDrawLock;
+
+ float mCameraX;
+ float mCameraY;
+
+ boolean mCallbackRequested;
+
+ public GameRenderer(Context context, Game game, int gameWidth, int gameHeight) {
+ mContext = context;
+ mGame = game;
+ mWidth = gameWidth;
+ mHeight = gameHeight;
+ mHalfWidth = gameWidth / 2;
+ mHalfHeight = gameHeight / 2;
+ mScaleX = 1.0f;
+ mScaleY = 1.0f;
+ mDrawQueueChanged = false;
+ mDrawLock = new Object();
+ mCameraX = 0.0f;
+ mCameraY = 0.0f;
+ mCallbackRequested = false;
+ }
+
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ /*
+ * Some one-time OpenGL initialization can be made here probably based
+ * on features of this particular context
+ */
+ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
+
+ gl.glClearColor(0.0f, 0.0f, 0.0f, 1);
+ gl.glShadeModel(GL10.GL_FLAT);
+ gl.glDisable(GL10.GL_DEPTH_TEST);
+ gl.glEnable(GL10.GL_TEXTURE_2D);
+ /*
+ * By default, OpenGL enables features that improve quality but reduce
+ * performance. One might want to tweak that especially on software
+ * renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+ gl.glDisable(GL10.GL_LIGHTING);
+
+ gl.glTexEnvx(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
+
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+ String extensions = gl.glGetString(GL10.GL_EXTENSIONS);
+ String version = gl.glGetString(GL10.GL_VERSION);
+ boolean isOpenGL10 = version.contains("1.0");
+ boolean supportsDrawTexture = extensions.contains("draw_texture");
+ // VBOs are standard in GLES1.1
+ boolean supportsVBOs = !isOpenGL10 || extensions.contains("vertex_buffer_object");
+ ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
+ params.supportsDrawTexture = supportsDrawTexture;
+ params.supportsVBOs = supportsVBOs;
+
+ hackBrokenDevices();
+
+ DebugLog.i("Graphics Support", version + ": " +(supportsDrawTexture ? "draw texture," : "") + (supportsVBOs ? "vbos" : ""));
+
+ mGame.onSurfaceCreated();
+
+ }
+
+ private void hackBrokenDevices() {
+ // Some devices are broken. Fix them here. This is pretty much the only
+ // device-specific code in the whole project. Ugh.
+ ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
+
+
+ if (Build.PRODUCT.contains("morrison")) {
+ // This is the Motorola Cliq. This device LIES and says it supports
+ // VBOs, which it actually does not (or, more likely, the extensions string
+ // is correct and the GL JNI glue is broken).
+ params.supportsVBOs = false;
+ // TODO: if Motorola fixes this, I should switch to using the fingerprint
+ // (blur/morrison/morrison/morrison:1.5/CUPCAKE/091007:user/ota-rel-keys,release-keys)
+ // instead of the product name so that newer versions use VBOs.
+ }
+ }
+
+ public void loadTextures(GL10 gl, TextureLibrary library) {
+ if (gl != null) {
+ library.loadAll(mContext, gl);
+ DebugLog.d("AndouKun", "Textures Loaded.");
+ }
+ }
+
+ public void flushTextures(GL10 gl, TextureLibrary library) {
+ if (gl != null) {
+ library.deleteAll(gl);
+ DebugLog.d("AndouKun", "Textures Unloaded.");
+ }
+ }
+
+ public void loadBuffers(GL10 gl, BufferLibrary library) {
+ if (gl != null) {
+ library.generateHardwareBuffers(gl);
+ DebugLog.d("AndouKun", "Buffers Created.");
+ }
+ }
+
+ public void flushBuffers(GL10 gl, BufferLibrary library) {
+ if (gl != null) {
+ library.releaseHardwareBuffers(gl);
+ DebugLog.d("AndouKun", "Buffers Released.");
+ }
+ }
+
+ public void onSurfaceLost() {
+ mGame.onSurfaceLost();
+ }
+
+ public void requestCallback() {
+ mCallbackRequested = true;
+ }
+
+ /** Draws the scene. Note that the draw queue is locked for the duration of this function. */
+ public void onDrawFrame(GL10 gl) {
+
+ long time = SystemClock.uptimeMillis();
+ long time_delta = (time - mLastTime);
+
+ synchronized(mDrawLock) {
+ if (!mDrawQueueChanged) {
+ while (!mDrawQueueChanged) {
+ try {
+ mDrawLock.wait();
+ } catch (InterruptedException e) {
+ // No big deal if this wait is interrupted.
+ }
+ }
+ }
+ mDrawQueueChanged = false;
+ }
+
+ final long wait = SystemClock.uptimeMillis();
+
+ if (mCallbackRequested) {
+ mGame.onSurfaceReady();
+ mCallbackRequested = false;
+ }
+
+ DrawableBitmap.beginDrawing(gl, mWidth, mHeight);
+
+ synchronized (this) {
+ if (mDrawQueue != null && mDrawQueue.getObjects().getCount() > 0) {
+ OpenGLSystem.setGL(gl);
+ FixedSizeArray<BaseObject> objects = mDrawQueue.getObjects();
+ Object[] objectArray = objects.getArray();
+ final int count = objects.getCount();
+ final float scaleX = mScaleX;
+ final float scaleY = mScaleY;
+ final float halfWidth = mHalfWidth;
+ final float halfHeight = mHalfHeight;
+ mProfileObjectCount += count;
+ for (int i = 0; i < count; i++) {
+ RenderElement element = (RenderElement)objectArray[i];
+ float x = element.x;
+ float y = element.y;
+ if (element.cameraRelative) {
+ x = (x - mCameraX) + halfWidth;
+ y = (y - mCameraY) + halfHeight;
+ }
+ element.mDrawable.draw(x, y, scaleX, scaleY);
+ }
+ OpenGLSystem.setGL(null);
+ } else if (mDrawQueue == null) {
+ // If we have no draw queue, clear the screen. If we have a draw queue that
+ // is empty, we'll leave the frame buffer alone.
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+ }
+ }
+
+ DrawableBitmap.endDrawing(gl);
+
+ long time2 = SystemClock.uptimeMillis();
+ mLastTime = time2;
+
+ mProfileFrameTime += time_delta;
+ mProfileSubmitTime += time2 - time;
+ mProfileWaitTime += wait - time;
+
+ mProfileFrames++;
+ if (mProfileFrameTime > PROFILE_REPORT_DELAY) {
+ final int validFrames = mProfileFrames;
+ final long averageFrameTime = mProfileFrameTime / validFrames;
+ final long averageSubmitTime = mProfileSubmitTime / validFrames;
+ final float averageObjectsPerFrame = (float)mProfileObjectCount / validFrames;
+ final long averageWaitTime = mProfileWaitTime / validFrames;
+
+ DebugLog.d("Render Profile",
+ "Average Submit: " + averageSubmitTime
+ + " Average Draw: " + averageFrameTime
+ + " Objects/Frame: " + averageObjectsPerFrame
+ + " Wait Time: " + averageWaitTime);
+
+ mProfileFrameTime = 0;
+ mProfileSubmitTime = 0;
+ mProfileFrames = 0;
+ mProfileObjectCount = 0;
+ }
+
+ }
+
+ public void onSurfaceChanged(GL10 gl, int w, int h) {
+ DebugLog.d("AndouKun", "Surface Size Change: " + w + ", " + h);
+
+ //mWidth = w;0
+ //mHeight = h;
+ // ensure the same aspect ratio as the game
+ float scaleX = (float)w / mWidth;
+ float scaleY = (float)h / mHeight;
+ final int viewportWidth = (int)(mWidth * scaleX);
+ final int viewportHeight = (int)(mHeight * scaleY);
+ gl.glViewport(0, 0, viewportWidth, viewportHeight);
+ mScaleX = scaleX;
+ mScaleY = scaleY;
+
+
+ /*
+ * Set our projection matrix. This doesn't have to be done each time we
+ * draw, but usually a new projection needs to be set when the viewport
+ * is resized.
+ */
+ float ratio = (float) mWidth / mHeight;
+ gl.glMatrixMode(GL10.GL_PROJECTION);
+ gl.glLoadIdentity();
+ gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+
+
+ mGame.onSurfaceReady();
+ }
+
+ public synchronized void setDrawQueue(ObjectManager queue, float cameraX, float cameraY) {
+ mDrawQueue = queue;
+ mCameraX = cameraX;
+ mCameraY = cameraY;
+ synchronized(mDrawLock) {
+ mDrawQueueChanged = true;
+ mDrawLock.notify();
+ }
+ }
+
+ public synchronized void onPause() {
+ // Stop waiting to avoid deadlock.
+ // TODO: this is a hack. Probably this renderer
+ // should just use GLSurfaceView's non-continuious render
+ // mode.
+ synchronized(mDrawLock) {
+ mDrawQueueChanged = true;
+ mDrawLock.notify();
+ }
+ }
+
+ /**
+ * This function blocks while drawFrame() is in progress, and may be used by other threads to
+ * determine when drawing is occurring.
+ */
+
+ public synchronized void waitDrawingComplete() {
+ }
+
+ public void setContext(Context newContext) {
+ mContext = newContext;
+ }
+
+
+}
diff --git a/src/com/replica/replicaisland/GameThread.java b/src/com/replica/replicaisland/GameThread.java
new file mode 100644
index 0000000..d8e75aa
--- /dev/null
+++ b/src/com/replica/replicaisland/GameThread.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.os.SystemClock;
+import android.view.KeyEvent;
+
+
+/**
+ * The GameThread contains the main loop for the game engine logic. It invokes the game graph,
+ * manages synchronization of input events, and handles the draw queue swap with the rendering
+ * thread.
+ */
+public class GameThread implements Runnable {
+ private long mLastTime;
+ private float mLastMotionX;
+ private float mLastMotionY;
+ private int mLastTouchX;
+ private int mLastTouchY;
+ private boolean mTouchReleased;
+ private boolean mClickUp;
+ private boolean mClickDown;
+ private float mOrientationX;
+ private float mOrientationY;
+ private float mOrientationZ;
+ private boolean mOrientationChanged;
+ private boolean mKeyLeft;
+ private boolean mKeyRight;
+ private boolean mKeyDown;
+ private boolean mKeyUp;
+ private boolean mKeyTouch;
+ private boolean mKeyClick;
+ private boolean mKeyInputReceived;
+ private boolean mKeyLeftUp;
+ private boolean mKeyRightUp;
+ private boolean mKeyDownUp;
+ private boolean mKeyUpUp;
+ private boolean mKeyTouchUp;
+ private boolean mKeyClickUp;
+
+ private ObjectManager mGameRoot;
+ private GameRenderer mRenderer;
+ private Object mInputLock;
+ private Object mPauseLock;
+ private boolean mFinished;
+ private boolean mPaused = false;
+ private int mProfileFrames;
+ private long mProfileTime;
+ private static final float PROFILE_REPORT_DELAY = 3.0f;
+
+ public GameThread(GameRenderer renderer) {
+ mLastTime = SystemClock.uptimeMillis();
+ mRenderer = renderer;
+ mInputLock = new Object();
+ mPauseLock = new Object();
+ mFinished = false;
+ mPaused = false;
+ }
+
+ public void run() {
+ mLastTime = SystemClock.uptimeMillis();
+ mFinished = false;
+ while (!mFinished) {
+ if (mGameRoot != null) {
+ mRenderer.waitDrawingComplete();
+
+ final long time = SystemClock.uptimeMillis();
+ final long timeDelta = time - mLastTime;
+ long finalDelta = timeDelta;
+ if (timeDelta > 12) {
+ float secondsDelta = (time - mLastTime) * 0.001f;
+ if (secondsDelta > 0.1f) {
+ secondsDelta = 0.1f;
+ }
+ mLastTime = time;
+
+ synchronized (mInputLock) {
+
+
+ if (mKeyInputReceived) {
+ BaseObject.sSystemRegistry.inputSystem.keyUp(
+ mKeyLeftUp,
+ mKeyRightUp,
+ mKeyUpUp,
+ mKeyDownUp,
+ mKeyTouchUp,
+ mKeyClickUp);
+ BaseObject.sSystemRegistry.inputSystem.keyDown(
+ mKeyLeft,
+ mKeyRight,
+ mKeyUp,
+ mKeyDown,
+ mKeyTouch,
+ mKeyClick);
+ mKeyInputReceived = false;
+ mKeyLeft = false;
+ mKeyRight = false;
+ mKeyUp = false;
+ mKeyDown = false;
+ mKeyTouch = false;
+ mKeyClick = false;
+ mKeyLeftUp = false;
+ mKeyRightUp = false;
+ mKeyUpUp = false;
+ mKeyDownUp = false;
+ mKeyTouchUp = false;
+ mKeyClickUp = false;
+ }
+
+ if (mLastMotionX != 0 || mLastMotionY != 0) {
+ BaseObject.sSystemRegistry.inputSystem
+ .roll(mLastMotionX, mLastMotionY);
+ mLastMotionX = 0;
+ mLastMotionY = 0;
+ }
+ if (mLastTouchX != 0 || mLastTouchY != 0) {
+ BaseObject.sSystemRegistry.inputSystem
+ .touch(mLastTouchX, mLastTouchY, mTouchReleased);
+ mLastTouchX = 0;
+ mLastTouchY = 0;
+ mTouchReleased = false;
+ }
+
+ if (mClickDown) {
+ BaseObject.sSystemRegistry.inputSystem.clickDown();
+ mClickDown = false;
+ }
+ if (mClickUp) {
+ BaseObject.sSystemRegistry.inputSystem.clickUp();
+ mClickUp = false;
+ }
+
+ if (mOrientationChanged) {
+ BaseObject.sSystemRegistry.inputSystem.setOrientation(
+ mOrientationX,
+ mOrientationY,
+ mOrientationZ);
+ mOrientationChanged = false;
+ }
+
+
+ }
+
+ mGameRoot.update(secondsDelta, null);
+
+ CameraSystem camera = mGameRoot.sSystemRegistry.cameraSystem;
+ float x = 0.0f;
+ float y = 0.0f;
+ if (camera != null) {
+ x = camera.getFocusPositionX();
+ y = camera.getFocusPositionY();
+ }
+ BaseObject.sSystemRegistry.renderSystem.swap(mRenderer, x, y);
+
+ final long endTime = SystemClock.uptimeMillis();
+
+ finalDelta = endTime - time;
+
+ mProfileTime += finalDelta;
+ mProfileFrames++;
+ if (mProfileTime > PROFILE_REPORT_DELAY * 1000) {
+ final long averageFrameTime = mProfileTime / mProfileFrames;
+ DebugLog.d("Game Profile", "Average: " + averageFrameTime);
+ mProfileTime = 0;
+ mProfileFrames = 0;
+ }
+ }
+ // If the game logic completed in less than 16ms, that means it's running
+ // faster than 60fps, which is our target frame rate. In that case we should
+ // yield to the rendering thread, at least for the remaining frame.
+
+ if (finalDelta < 16) {
+ try {
+ Thread.sleep(16 - finalDelta);
+ } catch (InterruptedException e) {
+ // Interruptions here are no big deal.
+ }
+ }
+
+ synchronized(mPauseLock) {
+ if (mPaused) {
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.pauseAll();
+ BaseObject.sSystemRegistry.inputSystem.releaseAllKeys();
+ }
+ while (mPaused) {
+ try {
+ mPauseLock.wait();
+ } catch (InterruptedException e) {
+ // No big deal if this wait is interrupted.
+ }
+ }
+ }
+ }
+ }
+ }
+ // Make sure our dependence on the render system is cleaned up.
+ BaseObject.sSystemRegistry.renderSystem.emptyQueues(mRenderer);
+ }
+
+ public void stopGame() {
+ synchronized (mPauseLock) {
+ mPaused = false;
+ mFinished = true;
+ mPauseLock.notifyAll();
+ }
+ }
+
+ public void pauseGame() {
+ synchronized (mPauseLock) {
+ mPaused = true;
+ }
+ }
+
+ public void resumeGame() {
+ synchronized (mPauseLock) {
+ mPaused = false;
+ mPauseLock.notifyAll();
+ }
+ }
+
+ public boolean getPaused() {
+ return mPaused;
+ }
+
+ public void setGameRoot(ObjectManager gameRoot) {
+ mGameRoot = gameRoot;
+ }
+
+ public void rollEvent(float f, float g) {
+ synchronized (mInputLock) {
+ mLastMotionX += f;
+ mLastMotionY += g;
+ }
+ }
+
+ public void clickEvent(boolean down) {
+ synchronized (mInputLock) {
+ if (down) {
+ mClickDown = true;
+ } else {
+ mClickUp = true;
+ }
+ }
+ }
+
+ public void orientationEvent(float x, float y, float z) {
+ synchronized (mInputLock) {
+ mOrientationX = x;
+ mOrientationY = y;
+ mOrientationZ = z;
+ mOrientationChanged = true;
+ }
+ }
+
+ public void touchDownEvent(float x, float y) {
+ synchronized (mInputLock) {
+ mTouchReleased = false;
+ mLastTouchX = (int)x;
+ mLastTouchY = (int)y;
+ }
+ }
+
+ public void touchUpEvent(float x, float y) {
+ synchronized (mInputLock) {
+ mTouchReleased = true;
+ mLastTouchX = (int)x;
+ mLastTouchY = (int)y;
+ }
+ }
+
+ public boolean keydownEvent(int keycode) {
+ boolean ateKey = true;
+ synchronized (mInputLock) {
+ switch (keycode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ mKeyLeft = true;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ mKeyRight = true;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ mKeyUp = true;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ mKeyDown = true;
+ break;
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ mKeyClick = true;
+ break;
+ case KeyEvent.KEYCODE_SPACE:
+ mKeyTouch = true;
+ break;
+ case KeyEvent.KEYCODE_B:
+ mKeyClick = true;
+ break;
+ case KeyEvent.KEYCODE_A:
+ mKeyTouch = true;
+ break;
+ default:
+ ateKey = false;
+ }
+
+ mKeyInputReceived = ateKey;
+ }
+ return ateKey;
+ }
+
+ public boolean keyupEvent(int keycode) {
+ boolean ateKey = true;
+ synchronized (mInputLock) {
+ switch (keycode) {
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ mKeyLeftUp = true;
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ mKeyRightUp = true;
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ mKeyUpUp = true;
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ mKeyDownUp = true;
+ break;
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ mKeyClickUp = true;
+ break;
+ case KeyEvent.KEYCODE_SPACE:
+ mKeyTouchUp = true;
+ break;
+ case KeyEvent.KEYCODE_B:
+ mKeyClickUp = true;
+ break;
+ case KeyEvent.KEYCODE_A:
+ mKeyTouchUp = true;
+ break;
+ default:
+ ateKey = false;
+ }
+ mKeyInputReceived = ateKey;
+ }
+ return ateKey;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/GenericAnimationComponent.java b/src/com/replica/replicaisland/GenericAnimationComponent.java
new file mode 100644
index 0000000..cbebee4
--- /dev/null
+++ b/src/com/replica/replicaisland/GenericAnimationComponent.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+public class GenericAnimationComponent extends GameComponent {
+ private SpriteComponent mSprite;
+
+ public GenericAnimationComponent() {
+ super();
+ setPhase(ComponentPhases.ANIMATION.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mSprite = null;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mSprite != null) {
+ GameObject parentObject = (GameObject) parent;
+ if (parentObject.facingDirection.x != 0.0f && parentObject.getVelocity().x != 0.0f) {
+ parentObject.facingDirection.x = Utils.sign(parentObject.getVelocity().x);
+ }
+ switch(parentObject.getCurrentAction()) {
+
+ case IDLE:
+ mSprite.playAnimation(Animation.IDLE);
+ break;
+ case MOVE:
+ mSprite.playAnimation(Animation.MOVE);
+ break;
+ case ATTACK:
+ mSprite.playAnimation(Animation.ATTACK);
+ break;
+ case HIT_REACT:
+ mSprite.playAnimation(Animation.HIT_REACT);
+ break;
+ case DEATH:
+ mSprite.playAnimation(Animation.DEATH);
+ break;
+ case HIDE:
+ mSprite.playAnimation(Animation.HIDE);
+ break;
+ case FROZEN:
+ mSprite.playAnimation(Animation.FROZEN);
+ break;
+ case INVALID:
+ default:
+ mSprite.playAnimation(-1);
+ break;
+ }
+ }
+ }
+
+ public void setSprite(SpriteComponent sprite) {
+ mSprite = sprite;
+ }
+
+
+ public static final class Animation {
+ public static final int IDLE = 0;
+ public static final int MOVE = 1;
+ public static final int ATTACK = 2;
+ public static final int HIT_REACT = 3;
+ public static final int DEATH = 4;
+ public static final int HIDE = 5;
+ public static final int FROZEN = 6;
+ }
+}
diff --git a/src/com/replica/replicaisland/GhostComponent.java b/src/com/replica/replicaisland/GhostComponent.java
new file mode 100644
index 0000000..8be11c3
--- /dev/null
+++ b/src/com/replica/replicaisland/GhostComponent.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.SoundSystem.Sound;
+
+public class GhostComponent extends GameComponent {
+ private float mMovementSpeed;
+ private float mJumpImpulse;
+ private float mAcceleration;
+ private boolean mUseOrientationSensor;
+ private float mDelayOnRelease;
+ private boolean mKillOnRelease;
+ private GameObject.ActionType mTargetAction;
+ private float mLifeTime;
+ private boolean mChangeActionOnButton;
+ private GameObject.ActionType mButtonPressedAction;
+ private Sound mAmbientSound;
+ private int mAmbientSoundStream;
+
+ public GhostComponent() {
+ super();
+ setPhase(GameComponent.ComponentPhases.THINK.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mMovementSpeed = 0.0f;
+ mJumpImpulse = 0.0f;
+ mAcceleration = 0.0f;
+ mUseOrientationSensor = false;
+ mDelayOnRelease = 0.0f;
+ mKillOnRelease = false;
+ mTargetAction = GameObject.ActionType.MOVE;
+ mLifeTime = 0.0f;
+ mChangeActionOnButton = false;
+ mButtonPressedAction = GameObject.ActionType.INVALID;
+ mAmbientSound = null;
+ mAmbientSoundStream = -1;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+ boolean timeToRelease = false;
+ final InputSystem input = sSystemRegistry.inputSystem;
+ final CameraSystem camera = sSystemRegistry.cameraSystem;
+
+ if (parentObject.life > 0) {
+
+ if (mLifeTime > 0.0f) {
+ mLifeTime -= timeDelta;
+ if (mLifeTime <= 0.0f) {
+ timeToRelease = true;
+ } else if (mLifeTime < 1.0f) {
+ // Do we have a sprite we can fade out?
+ SpriteComponent sprite = parentObject.findByClass(SpriteComponent.class);
+ if (sprite != null) {
+ sprite.setOpacity(mLifeTime);
+ }
+ }
+ }
+
+ if (parentObject.getPosition().y < -parentObject.height) {
+ // we fell off the bottom of the screen, die.
+ parentObject.life = 0;
+ timeToRelease = true;
+ }
+
+ parentObject.setCurrentAction(mTargetAction);
+ if (camera != null) {
+ camera.setTarget(parentObject);
+ }
+
+ if (input != null) {
+
+ if (mUseOrientationSensor) {
+ parentObject.getTargetVelocity().x =
+ input.getPitch() * mMovementSpeed;
+
+ parentObject.getTargetVelocity().y =
+ input.getRoll() * mMovementSpeed;
+
+ parentObject.getAcceleration().x = mAcceleration;
+ parentObject.getAcceleration().y = mAcceleration;
+ } else {
+ final Vector2 rollDirection = input.getRollDirection();
+ parentObject.getTargetVelocity().x =
+ rollDirection.x * mMovementSpeed;
+
+ parentObject.getAcceleration().x = mAcceleration;
+ }
+
+ final boolean buttonPressed = input.getTouchPressed()
+ && input.getTouchedWithinRegion(
+ ButtonConstants.FLY_BUTTON_REGION_X,
+ ButtonConstants.FLY_BUTTON_REGION_Y,
+ ButtonConstants.FLY_BUTTON_REGION_WIDTH,
+ ButtonConstants.FLY_BUTTON_REGION_HEIGHT);
+
+ if (buttonPressed
+ && input.getTouchTriggered()
+ && parentObject.touchingGround()
+ && !mChangeActionOnButton) {
+ parentObject.getImpulse().y += mJumpImpulse;// * timeDelta;
+ } else if (mChangeActionOnButton && buttonPressed) {
+ parentObject.setCurrentAction(mButtonPressedAction);
+ }
+
+ if (input.getClickTriggered() ||
+ (input.getTouchTriggered() && input.getTouchedWithinRegion(
+ ButtonConstants.STOMP_BUTTON_REGION_X,
+ ButtonConstants.STOMP_BUTTON_REGION_Y,
+ ButtonConstants.STOMP_BUTTON_REGION_WIDTH,
+ ButtonConstants.STOMP_BUTTON_REGION_HEIGHT))) {
+ timeToRelease = true;
+ }
+ }
+
+ if (!timeToRelease && mAmbientSound != null && mAmbientSoundStream == -1) {
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ mAmbientSoundStream = sound.play(mAmbientSound, true, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+ }
+
+ if (parentObject.life == 0) {
+ if (mAmbientSoundStream > -1) {
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.stop(mAmbientSoundStream);
+ mAmbientSoundStream = -1;
+ }
+ }
+ }
+
+ if (timeToRelease) {
+ releaseControl(parentObject);
+ }
+
+ }
+
+ public final void releaseControl(GameObject parentObject) {
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ GameObject player = null;
+ if (manager != null) {
+ player = manager.getPlayer();
+ }
+
+ final CameraSystem camera = sSystemRegistry.cameraSystem;
+ if (camera != null) {
+ camera.setTarget(null);
+ }
+
+ if (player != null) {
+
+ if (mKillOnRelease) {
+ parentObject.life = 0;
+ } else {
+ // See if there's a component swap we can run.
+ ChangeComponentsComponent swap = parentObject.findByClass(ChangeComponentsComponent.class);
+ if (swap != null) {
+ swap.activate(parentObject);
+ }
+ }
+
+ PlayerComponent control = player.findByClass(PlayerComponent.class);
+ if (camera.pointVisible(player.getPosition(), player.width)) {
+ control.deactivateGhost(0.0f);
+ } else {
+ control.deactivateGhost(mDelayOnRelease);
+ }
+ final InputSystem input = sSystemRegistry.inputSystem;
+ if (input != null) {
+ input.clearClickTriggered();
+ }
+ }
+
+ if (mAmbientSoundStream > -1) {
+ SoundSystem sound = BaseObject.sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.stop(mAmbientSoundStream);
+ mAmbientSoundStream = -1;
+ }
+ }
+ }
+
+ public final void setMovementSpeed(float movementSpeed) {
+ mMovementSpeed = movementSpeed;
+ }
+
+ public final void setJumpImpulse(float jumpImpulse) {
+ mJumpImpulse = jumpImpulse;
+ }
+
+ public final void setAcceleration(float accceleration) {
+ mAcceleration = accceleration;
+ }
+
+ public final void setUseOrientationSensor(boolean useSensor) {
+ mUseOrientationSensor = useSensor;
+ }
+
+ public final void setDelayOnRelease(float delayOnRelease) {
+ mDelayOnRelease = delayOnRelease;
+ }
+
+ public final void setKillOnRelease(boolean killOnRelease) {
+ mKillOnRelease = killOnRelease;
+ }
+
+ public final void setTargetAction(GameObject.ActionType action) {
+ mTargetAction = action;
+ }
+
+ public final void setLifeTime(float lifeTime) {
+ mLifeTime = lifeTime;
+ }
+
+ public final void changeActionOnButton(GameObject.ActionType pressedAction) {
+ mButtonPressedAction = pressedAction;
+ mChangeActionOnButton = true;
+ }
+
+ public final void setAmbientSound(Sound sound) {
+ mAmbientSound = sound;
+ }
+}
diff --git a/src/com/replica/replicaisland/GravityComponent.java b/src/com/replica/replicaisland/GravityComponent.java
new file mode 100644
index 0000000..b8545fb
--- /dev/null
+++ b/src/com/replica/replicaisland/GravityComponent.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A game component that implements gravity. Adding this component to a game object will cause
+ * it to be pulled down towards the ground.
+ */
+public class GravityComponent extends GameComponent {
+ private Vector2 mGravity;
+ private Vector2 mScaledGravity;
+ private static final Vector2 sDefaultGravity = new Vector2(0.0f, -400.0f);
+
+ public GravityComponent() {
+ super();
+ mGravity = new Vector2(sDefaultGravity);
+ mScaledGravity = new Vector2();
+ setPhase(ComponentPhases.PHYSICS.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mGravity.set(sDefaultGravity);
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ mScaledGravity.set(mGravity);
+ mScaledGravity.multiply(timeDelta);
+ ((GameObject) parent).getVelocity().add(mScaledGravity);
+ }
+
+ public Vector2 getGravity() {
+ return mGravity;
+ }
+
+ public void setGravityMultiplier(float multiplier) {
+ mGravity.set(sDefaultGravity);
+ mGravity.multiply(multiplier);
+ }
+}
diff --git a/src/com/replica/replicaisland/Grid.java b/src/com/replica/replicaisland/Grid.java
new file mode 100644
index 0000000..0a94c7f
--- /dev/null
+++ b/src/com/replica/replicaisland/Grid.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.CharBuffer;
+import java.nio.FloatBuffer;
+import java.nio.IntBuffer;
+
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+
+/**
+ * A 2D rectangular mesh. Can be drawn textured or untextured.
+ * This version is modified from the original Grid.java (found in
+ * the SpriteText package in the APIDemos Android sample) to support hardware
+ * vertex buffers and to insert edges between grid squares for tiling.
+ */
+class Grid {
+ private static final int FLOAT_SIZE = 4;
+ private static final int FIXED_SIZE = 4;
+ private static final int CHAR_SIZE = 2;
+
+ private FloatBuffer mFloatVertexBuffer;
+ private FloatBuffer mFloatTexCoordBuffer;
+ private IntBuffer mFixedVertexBuffer;
+ private IntBuffer mFixedTexCoordBuffer;
+ private CharBuffer mIndexBuffer;
+
+ private Buffer mVertexBuffer;
+ private Buffer mTexCoordBuffer;
+ private int mCoordinateSize;
+ private int mCoordinateType;
+
+ private int mVertsAcross;
+ private int mVertsDown;
+ private int mIndexCount;
+ private boolean mUseHardwareBuffers;
+ private int mVertBufferIndex;
+ private int mIndexBufferIndex;
+ private int mTextureCoordBufferIndex;
+
+ public Grid(int quadsAcross, int quadsDown, boolean useFixedPoint) {
+ final int vertsAcross = quadsAcross * 2;
+ final int vertsDown = quadsDown * 2;
+ if (vertsAcross < 0 || vertsAcross >= 65536) {
+ throw new IllegalArgumentException("quadsAcross");
+ }
+ if (vertsDown < 0 || vertsDown >= 65536) {
+ throw new IllegalArgumentException("quadsDown");
+ }
+ if (vertsAcross * vertsDown >= 65536) {
+ throw new IllegalArgumentException("quadsAcross * quadsDown >= 32768");
+ }
+
+ mUseHardwareBuffers = false;
+
+ mVertsAcross = vertsAcross;
+ mVertsDown = vertsDown;
+ int size = vertsAcross * vertsDown;
+
+
+ if (useFixedPoint) {
+ mFixedVertexBuffer = ByteBuffer.allocateDirect(FIXED_SIZE * size * 3)
+ .order(ByteOrder.nativeOrder()).asIntBuffer();
+ mFixedTexCoordBuffer = ByteBuffer.allocateDirect(FIXED_SIZE * size * 2)
+ .order(ByteOrder.nativeOrder()).asIntBuffer();
+
+ mVertexBuffer = mFixedVertexBuffer;
+ mTexCoordBuffer = mFixedTexCoordBuffer;
+ mCoordinateSize = FIXED_SIZE;
+ mCoordinateType = GL10.GL_FIXED;
+
+ } else {
+ mFloatVertexBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 3)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+ mFloatTexCoordBuffer = ByteBuffer.allocateDirect(FLOAT_SIZE * size * 2)
+ .order(ByteOrder.nativeOrder()).asFloatBuffer();
+
+ mVertexBuffer = mFloatVertexBuffer;
+ mTexCoordBuffer = mFloatTexCoordBuffer;
+ mCoordinateSize = FLOAT_SIZE;
+ mCoordinateType = GL10.GL_FLOAT;
+ }
+
+
+
+
+ int quadCount = quadsAcross * quadsDown;
+ int indexCount = quadCount * 6;
+ mIndexCount = indexCount;
+ mIndexBuffer = ByteBuffer.allocateDirect(CHAR_SIZE * indexCount)
+ .order(ByteOrder.nativeOrder()).asCharBuffer();
+
+ /*
+ * Initialize triangle list mesh.
+ *
+ * [0]------[1] [2]------[3] ...
+ * | / | | / |
+ * | / | | / |
+ * | / | | / |
+ * [w]-----[w+1] [w+2]----[w+3]...
+ * | |
+ *
+ */
+
+ {
+ int i = 0;
+ for (int y = 0; y < quadsDown; y++) {
+ final int indexY = y * 2;
+ for (int x = 0; x < quadsAcross; x++) {
+ final int indexX = x * 2;
+ char a = (char) (indexY * mVertsAcross + indexX);
+ char b = (char) (indexY * mVertsAcross + indexX + 1);
+ char c = (char) ((indexY + 1) * mVertsAcross + indexX);
+ char d = (char) ((indexY + 1) * mVertsAcross + indexX + 1);
+
+ mIndexBuffer.put(i++, a);
+ mIndexBuffer.put(i++, b);
+ mIndexBuffer.put(i++, c);
+
+ mIndexBuffer.put(i++, b);
+ mIndexBuffer.put(i++, c);
+ mIndexBuffer.put(i++, d);
+ }
+ }
+ }
+
+ mVertBufferIndex = 0;
+ }
+
+ public void set(int quadX, int quadY, float[][] positions, float[][] uvs) {
+ if (quadX < 0 || quadX * 2 >= mVertsAcross) {
+ throw new IllegalArgumentException("quadX");
+ }
+ if (quadY < 0 || quadY * 2 >= mVertsDown) {
+ throw new IllegalArgumentException("quadY");
+ }
+ if (positions.length < 4) {
+ throw new IllegalArgumentException("positions");
+ }
+ if (uvs.length < 4) {
+ throw new IllegalArgumentException("quadY");
+ }
+
+ int i = quadX * 2;
+ int j = quadY * 2;
+
+ setVertex(i, j, positions[0][0], positions[0][1], positions[0][2], uvs[0][0], uvs[0][1]);
+ setVertex(i + 1, j, positions[1][0], positions[1][1], positions[1][2], uvs[1][0], uvs[1][1]);
+ setVertex(i, j + 1, positions[2][0], positions[2][1], positions[2][2], uvs[2][0], uvs[2][1]);
+ setVertex(i + 1, j + 1, positions[3][0], positions[3][1], positions[3][2], uvs[3][0], uvs[3][1]);
+ }
+
+
+ private void setVertex(int i, int j, float x, float y, float z, float u, float v) {
+ if (i < 0 || i >= mVertsAcross) {
+ throw new IllegalArgumentException("i");
+ }
+ if (j < 0 || j >= mVertsDown) {
+ throw new IllegalArgumentException("j");
+ }
+
+ final int index = mVertsAcross * j + i;
+
+ final int posIndex = index * 3;
+ final int texIndex = index * 2;
+
+
+ if (mCoordinateType == GL10.GL_FLOAT) {
+ mFloatVertexBuffer.put(posIndex, x);
+ mFloatVertexBuffer.put(posIndex + 1, y);
+ mFloatVertexBuffer.put(posIndex + 2, z);
+
+ mFloatTexCoordBuffer.put(texIndex, u);
+ mFloatTexCoordBuffer.put(texIndex + 1, v);
+ } else {
+ mFixedVertexBuffer.put(posIndex, (int)(x * (1 << 16)));
+ mFixedVertexBuffer.put(posIndex + 1, (int)(y * (1 << 16)));
+ mFixedVertexBuffer.put(posIndex + 2, (int)(z * (1 << 16)));
+
+ mFixedTexCoordBuffer.put(texIndex, (int)(u * (1 << 16)));
+ mFixedTexCoordBuffer.put(texIndex + 1, (int)(v * (1 << 16)));
+ }
+ }
+
+ public static void beginDrawing(GL10 gl, boolean useTexture) {
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+
+ if (useTexture) {
+ gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+ gl.glEnable(GL10.GL_TEXTURE_2D);
+ } else {
+ gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
+ gl.glDisable(GL10.GL_TEXTURE_2D);
+ }
+ }
+
+ public void beginDrawingStrips(GL10 gl, boolean useTexture) {
+ beginDrawing(gl, useTexture);
+ if (!mUseHardwareBuffers) {
+ gl.glVertexPointer(3, mCoordinateType, 0, mVertexBuffer);
+
+ if (useTexture) {
+ gl.glTexCoordPointer(2, mCoordinateType, 0, mTexCoordBuffer);
+ }
+
+ } else {
+ GL11 gl11 = (GL11)gl;
+ // draw using hardware buffers
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
+ gl11.glVertexPointer(3, mCoordinateType, 0, 0);
+
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mTextureCoordBufferIndex);
+ gl11.glTexCoordPointer(2, mCoordinateType, 0, 0);
+
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex);
+ }
+ }
+
+ // Assumes beginDrawingStrips() has been called before this.
+ public void drawStrip(GL10 gl, boolean useTexture, int startIndex, int indexCount) {
+ int count = indexCount;
+ if (startIndex + indexCount >= mIndexCount) {
+ count = mIndexCount - startIndex;
+ }
+ if (!mUseHardwareBuffers) {
+ gl.glDrawElements(GL10.GL_TRIANGLES, count,
+ GL10.GL_UNSIGNED_SHORT, mIndexBuffer.position(startIndex));
+ } else {
+ GL11 gl11 = (GL11)gl;
+ gl11.glDrawElements(GL11.GL_TRIANGLES, count,
+ GL11.GL_UNSIGNED_SHORT, startIndex * CHAR_SIZE);
+
+ }
+ }
+
+ public void draw(GL10 gl, boolean useTexture) {
+ if (!mUseHardwareBuffers) {
+ gl.glVertexPointer(3, mCoordinateType, 0, mVertexBuffer);
+
+ if (useTexture) {
+ gl.glTexCoordPointer(2, mCoordinateType, 0, mTexCoordBuffer);
+ }
+
+ gl.glDrawElements(GL10.GL_TRIANGLES, mIndexCount,
+ GL10.GL_UNSIGNED_SHORT, mIndexBuffer);
+ } else {
+ GL11 gl11 = (GL11)gl;
+ // draw using hardware buffers
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
+ gl11.glVertexPointer(3, mCoordinateType, 0, 0);
+
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mTextureCoordBufferIndex);
+ gl11.glTexCoordPointer(2, mCoordinateType, 0, 0);
+
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, mIndexBufferIndex);
+ gl11.glDrawElements(GL11.GL_TRIANGLES, mIndexCount,
+ GL11.GL_UNSIGNED_SHORT, 0);
+
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
+
+
+ }
+ }
+
+ public static void endDrawing(GL10 gl) {
+ gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
+ }
+
+ public boolean usingHardwareBuffers() {
+ return mUseHardwareBuffers;
+ }
+
+ /**
+ * When the OpenGL ES device is lost, GL handles become invalidated.
+ * In that case, we just want to "forget" the old handles (without
+ * explicitly deleting them) and make new ones.
+ */
+ public void invalidateHardwareBuffers() {
+ mVertBufferIndex = 0;
+ mIndexBufferIndex = 0;
+ mTextureCoordBufferIndex = 0;
+ mUseHardwareBuffers = false;
+ }
+
+ /**
+ * Deletes the hardware buffers allocated by this object (if any).
+ */
+ public void releaseHardwareBuffers(GL10 gl) {
+ if (mUseHardwareBuffers) {
+ if (gl instanceof GL11) {
+ GL11 gl11 = (GL11)gl;
+ int[] buffer = new int[1];
+ buffer[0] = mVertBufferIndex;
+ gl11.glDeleteBuffers(1, buffer, 0);
+
+ buffer[0] = mTextureCoordBufferIndex;
+ gl11.glDeleteBuffers(1, buffer, 0);
+
+ buffer[0] = mIndexBufferIndex;
+ gl11.glDeleteBuffers(1, buffer, 0);
+ }
+
+ invalidateHardwareBuffers();
+ }
+ }
+
+ /**
+ * Allocates hardware buffers on the graphics card and fills them with
+ * data if a buffer has not already been previously allocated. Note that
+ * this function uses the GL_OES_vertex_buffer_object extension, which is
+ * not guaranteed to be supported on every device.
+ * @param gl A pointer to the OpenGL ES context.
+ */
+ public void generateHardwareBuffers(GL10 gl) {
+ if (!mUseHardwareBuffers) {
+ DebugLog.i("Grid", "Using Hardware Buffers");
+ if (gl instanceof GL11) {
+ GL11 gl11 = (GL11)gl;
+ int[] buffer = new int[1];
+
+ // Allocate and fill the vertex buffer.
+ gl11.glGenBuffers(1, buffer, 0);
+ mVertBufferIndex = buffer[0];
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, mVertBufferIndex);
+ final int vertexSize = mVertexBuffer.capacity() * mCoordinateSize;
+ gl11.glBufferData(GL11.GL_ARRAY_BUFFER, vertexSize,
+ mVertexBuffer, GL11.GL_STATIC_DRAW);
+
+ // Allocate and fill the texture coordinate buffer.
+ gl11.glGenBuffers(1, buffer, 0);
+ mTextureCoordBufferIndex = buffer[0];
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER,
+ mTextureCoordBufferIndex);
+ final int texCoordSize =
+ mTexCoordBuffer.capacity() * mCoordinateSize;
+ gl11.glBufferData(GL11.GL_ARRAY_BUFFER, texCoordSize,
+ mTexCoordBuffer, GL11.GL_STATIC_DRAW);
+
+ // Unbind the array buffer.
+ gl11.glBindBuffer(GL11.GL_ARRAY_BUFFER, 0);
+
+ // Allocate and fill the index buffer.
+ gl11.glGenBuffers(1, buffer, 0);
+ mIndexBufferIndex = buffer[0];
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER,
+ mIndexBufferIndex);
+ // A char is 2 bytes.
+ final int indexSize = mIndexBuffer.capacity() * 2;
+ gl11.glBufferData(GL11.GL_ELEMENT_ARRAY_BUFFER, indexSize, mIndexBuffer,
+ GL11.GL_STATIC_DRAW);
+
+ // Unbind the element array buffer.
+ gl11.glBindBuffer(GL11.GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ mUseHardwareBuffers = true;
+
+ assert mVertBufferIndex != 0;
+ assert mTextureCoordBufferIndex != 0;
+ assert mIndexBufferIndex != 0;
+ assert gl11.glGetError() == 0;
+
+
+ }
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/HitPlayerComponent.java b/src/com/replica/replicaisland/HitPlayerComponent.java
new file mode 100644
index 0000000..7e51689
--- /dev/null
+++ b/src/com/replica/replicaisland/HitPlayerComponent.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+public class HitPlayerComponent extends GameComponent {
+ float mDistance2;
+ Vector2 mPlayerPosition;
+ Vector2 mMyPosition;
+ HitReactionComponent mHitReact;
+ int mHitType;
+ boolean mHitDirection;
+
+ public HitPlayerComponent() {
+ super();
+ mPlayerPosition = new Vector2();
+ mMyPosition = new Vector2();
+ reset();
+ setPhase(ComponentPhases.THINK.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mDistance2 = 0.0f;
+ mPlayerPosition.zero();
+ mMyPosition.zero();
+ mHitReact = null;
+ mHitType = CollisionParameters.HitType.INVALID;
+ mHitDirection = false; // by default, hit myself
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (manager != null && mHitReact != null) {
+ GameObject player = manager.getPlayer();
+ if (player != null && player.life > 0) {
+ mPlayerPosition.set(player.getCenteredPositionX(), player.getCenteredPositionY());
+ GameObject parentObject = (GameObject)parent;
+ mMyPosition.set(parentObject.getCenteredPositionX(), parentObject.getCenteredPositionY());
+ if (mMyPosition.distance2(mPlayerPosition) <= mDistance2) {
+ HitReactionComponent playerHitReact = player.findByClass(HitReactionComponent.class);
+ if (playerHitReact != null) {
+ if (!mHitDirection) {
+ // hit myself
+ boolean accepted = mHitReact.receivedHit(parentObject, player, mHitType);
+ playerHitReact.hitVictim(player, parentObject, mHitType, accepted);
+ } else {
+ // hit the player
+ boolean accepted = playerHitReact.receivedHit(player, parentObject, mHitType);
+ mHitReact.hitVictim(parentObject, player, mHitType, accepted);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public void setup(float distance, HitReactionComponent hitReact, int hitType, boolean hitPlayer) {
+ mDistance2 = distance * distance;
+ mHitReact = hitReact;
+ mHitType = hitType;
+ mHitDirection = hitPlayer;
+ }
+}
diff --git a/src/com/replica/replicaisland/HitPoint.java b/src/com/replica/replicaisland/HitPoint.java
new file mode 100644
index 0000000..e44f86e
--- /dev/null
+++ b/src/com/replica/replicaisland/HitPoint.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+public class HitPoint extends AllocationGuard {
+ public Vector2 hitPoint;
+ public Vector2 hitNormal;
+
+ public HitPoint() {
+ super();
+ }
+
+ public final void reset() {
+ hitPoint = null;
+ hitNormal = null;
+ }
+}
diff --git a/src/com/replica/replicaisland/HitPointPool.java b/src/com/replica/replicaisland/HitPointPool.java
new file mode 100644
index 0000000..98470fa
--- /dev/null
+++ b/src/com/replica/replicaisland/HitPointPool.java
@@ -0,0 +1,19 @@
+package com.replica.replicaisland;
+
+public class HitPointPool extends TObjectPool<HitPoint> {
+
+ @Override
+ protected void fill() {
+ final int size = getSize();
+ for (int x = 0; x < size; x++) {
+ getAvailable().add(new HitPoint());
+ }
+ }
+
+ @Override
+ public void release(Object entry) {
+ ((HitPoint)entry).reset();
+ super.release(entry);
+ }
+
+}
diff --git a/src/com/replica/replicaisland/HitReactionComponent.java b/src/com/replica/replicaisland/HitReactionComponent.java
new file mode 100644
index 0000000..a56dff0
--- /dev/null
+++ b/src/com/replica/replicaisland/HitReactionComponent.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.CollisionParameters.HitType;
+import com.replica.replicaisland.GameObject.ActionType;
+import com.replica.replicaisland.GameObject.Team;
+import com.replica.replicaisland.GameObjectFactory.GameObjectType;
+
+/**
+ * A general-purpose component that responds to dynamic collision notifications. This component
+ * may be configured to produce common responses to hit (taking damage, being knocked back, etc), or
+ * it can be derived for entirely different responses. This component must exist on an object for
+ * that object to respond to dynamic collisions.
+ */
+public class HitReactionComponent extends GameComponent {
+ private static final float ATTACK_PAUSE_DELAY = (1.0f / 60) * 4;
+ private final static float DEFAULT_BOUNCE_MAGNITUDE = 200.0f;
+ private final static float EVENT_SEND_DELAY = 5.0f;
+
+ private boolean mPauseOnAttack;
+ private float mPauseOnAttackTime;
+ private boolean mBounceOnHit;
+ private float mBounceMagnitude;
+ private float mInvincibleAfterHitTime;
+ private float mLastHitTime;
+ private boolean mInvincible;
+ private boolean mDieOnCollect;
+ private boolean mDieOnAttack;
+ private ChangeComponentsComponent mPossessionComponent;
+ private InventoryComponent.UpdateRecord mInventoryUpdate;
+ private LauncherComponent mLauncherComponent;
+ private int mLauncherHitType;
+ private float mInvincibleTime;
+ private int mGameEventHitType;
+ private int mGameEventOnHit;
+ private int mGameEventIndexData;
+ private float mLastGameEventTime;
+ private boolean mForceInvincibility;
+ private SoundSystem.Sound mTakeHitSound;
+ private SoundSystem.Sound mDealHitSound;
+ private int mDealHitSoundHitType;
+ private int mTakeHitSoundHitType;
+
+ private GameObjectFactory.GameObjectType mSpawnOnDealHitObjectType;
+ private int mSpawnOnDealHitHitType;
+ private boolean mAlignDealHitObjectToVictimX;
+ private boolean mAlignDealHitObjectToVictimY;
+
+
+ public HitReactionComponent() {
+ super();
+ reset();
+ setPhase(ComponentPhases.PRE_DRAW.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mPauseOnAttack = false;
+ mPauseOnAttackTime = ATTACK_PAUSE_DELAY;
+ mBounceOnHit = false;
+ mBounceMagnitude = DEFAULT_BOUNCE_MAGNITUDE;
+ mInvincibleAfterHitTime = 0.0f;
+ mInvincible = false;
+ mDieOnCollect = false;
+ mDieOnAttack = false;
+ mPossessionComponent = null;
+ mInventoryUpdate = null;
+ mLauncherComponent = null;
+ mLauncherHitType = HitType.LAUNCH;
+ mInvincibleTime = 0.0f;
+ mGameEventOnHit = -1;
+ mGameEventIndexData = 0;
+ mLastGameEventTime = -1.0f;
+ mGameEventHitType = CollisionParameters.HitType.INVALID;
+ mForceInvincibility = false;
+ mTakeHitSound = null;
+ mDealHitSound = null;
+ mSpawnOnDealHitObjectType = GameObjectType.INVALID;
+ mSpawnOnDealHitHitType = CollisionParameters.HitType.INVALID;
+ mDealHitSoundHitType = CollisionParameters.HitType.INVALID;
+ mAlignDealHitObjectToVictimX = false;
+ mAlignDealHitObjectToVictimY = false;
+ }
+
+ /** Called when this object attacks another object. */
+ public void hitVictim(GameObject parent, GameObject victim, int hitType,
+ boolean hitAccepted) {
+ if (hitAccepted) {
+ if (mPauseOnAttack && hitType == CollisionParameters.HitType.HIT) {
+ TimeSystem time = sSystemRegistry.timeSystem;
+ time.freeze(mPauseOnAttackTime);
+ }
+
+ if (mDieOnAttack) {
+ parent.life = 0;
+ }
+
+ if (hitType == mLauncherHitType && mLauncherComponent != null) {
+ mLauncherComponent.prepareToLaunch(victim, parent);
+ }
+
+ if (mDealHitSound != null &&
+ (hitType == mDealHitSoundHitType ||
+ mDealHitSoundHitType == CollisionParameters.HitType.INVALID)) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.play(mDealHitSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+
+ if (mSpawnOnDealHitObjectType != GameObjectType.INVALID &&
+ hitType == mSpawnOnDealHitHitType) {
+ final float x = mAlignDealHitObjectToVictimX ?
+ victim.getPosition().x : parent.getPosition().x;
+ final float y = mAlignDealHitObjectToVictimY ?
+ victim.getPosition().y : parent.getPosition().y;
+
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+
+ if (factory != null) {
+ GameObject object = factory.spawn(mSpawnOnDealHitObjectType, x,
+ y, parent.facingDirection.x < 0.0f);
+
+ if (object != null && manager != null) {
+ manager.add(object);
+ }
+ }
+ }
+ }
+ }
+
+ /** Called when this object is hit by another object. */
+ public boolean receivedHit(GameObject parent, GameObject attacker, int hitType) {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+
+ if (mGameEventHitType == hitType &&
+ mGameEventHitType != CollisionParameters.HitType.INVALID ) {
+ if (mLastGameEventTime < 0.0f || gameTime > mLastGameEventTime + EVENT_SEND_DELAY) {
+ LevelSystem level = sSystemRegistry.levelSystem;
+ level.sendGameEvent(mGameEventOnHit, mGameEventIndexData, true);
+ } else {
+ // special case. If we're waiting for a hit type to spawn an event and
+ // another event has just happened, eat this hit so we don't miss
+ // the chance to send the event.
+ hitType = CollisionParameters.HitType.INVALID;
+ }
+ mLastGameEventTime = gameTime;
+ }
+
+ switch(hitType) {
+ case CollisionParameters.HitType.INVALID:
+ break;
+
+ case CollisionParameters.HitType.HIT:
+ // don't hit our friends, if we have friends.
+ final boolean sameTeam = (parent.team == attacker.team && parent.team != Team.NONE);
+ if (!mForceInvincibility && !mInvincible && parent.life > 0 && !sameTeam) {
+ parent.life -= 1;
+
+ if (mBounceOnHit && parent.life > 0) {
+ VectorPool pool = sSystemRegistry.vectorPool;
+ Vector2 newVelocity = pool.allocate(parent.getPosition());
+ newVelocity.subtract(attacker.getPosition());
+ newVelocity.set(0.5f * Utils.sign(newVelocity.x),
+ 0.5f * Utils.sign(newVelocity.y));
+ newVelocity.multiply(mBounceMagnitude);
+ parent.setVelocity(newVelocity);
+ parent.getTargetVelocity().zero();
+ pool.release(newVelocity);
+ }
+
+ if (mInvincibleAfterHitTime > 0.0f) {
+ mInvincible = true;
+ mInvincibleTime = mInvincibleAfterHitTime;
+ }
+
+ } else {
+ // Ignore this hit.
+ hitType = CollisionParameters.HitType.INVALID;
+ }
+ break;
+ case CollisionParameters.HitType.DEATH:
+ // respect teams?
+ parent.life = 0;
+ break;
+ case CollisionParameters.HitType.COLLECT:
+ if (mInventoryUpdate != null && parent.life > 0) {
+ InventoryComponent attackerInventory = attacker.findByClass(InventoryComponent.class);
+ if (attackerInventory != null) {
+ attackerInventory.applyUpdate(mInventoryUpdate);
+ }
+ }
+ if (mDieOnCollect && parent.life > 0) {
+ parent.life = 0;
+ }
+ break;
+ case CollisionParameters.HitType.POSSESS:
+ if (mPossessionComponent != null && parent.life > 0 && attacker.life > 0) {
+ mPossessionComponent.activate(parent);
+ } else {
+ hitType = CollisionParameters.HitType.INVALID;
+ }
+ break;
+ case CollisionParameters.HitType.LAUNCH:
+ break;
+
+ default:
+ break;
+ }
+
+
+ if (hitType != CollisionParameters.HitType.INVALID) {
+ if (mTakeHitSound != null && hitType == mTakeHitSoundHitType) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.play(mTakeHitSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+ mLastHitTime = gameTime;
+ parent.setCurrentAction(ActionType.HIT_REACT);
+ parent.lastReceivedHitType = hitType;
+
+ }
+
+ return hitType != CollisionParameters.HitType.INVALID;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject)parent;
+ TimeSystem time = sSystemRegistry.timeSystem;
+
+ final float gameTime = time.getGameTime();
+
+ if (mInvincible && mInvincibleTime > 0) {
+ if (time.getGameTime() > mLastHitTime + mInvincibleTime) {
+ mInvincible = false;
+ }
+ }
+
+ // This means that the lastReceivedHitType will persist for two frames, giving all systems
+ // a chance to react.
+ if (gameTime - mLastHitTime > timeDelta) {
+ parentObject.lastReceivedHitType = CollisionParameters.HitType.INVALID;
+ }
+ }
+
+ public void setPauseOnAttack(boolean pause) {
+ mPauseOnAttack = pause;
+ }
+
+ public void setPauseOnAttackTime(float seconds) {
+ mPauseOnAttackTime = seconds;
+ }
+
+ public void setBounceOnHit(boolean bounce) {
+ mBounceOnHit = bounce;
+ }
+
+ public void setBounceMagnitude(float magnitude) {
+ mBounceMagnitude = magnitude;
+ }
+
+ public void setInvincibleTime(float time) {
+ mInvincibleAfterHitTime = time;
+ }
+
+ public void setDieWhenCollected(boolean die) {
+ mDieOnCollect = true;
+ }
+
+ public void setDieOnAttack(boolean die) {
+ mDieOnAttack = die;
+ }
+
+ public void setInvincible(boolean invincible) {
+ mInvincible = invincible;
+ }
+
+ public void setPossessionComponent(ChangeComponentsComponent component) {
+ mPossessionComponent = component;
+ }
+
+ public void setInventoryUpdate(InventoryComponent.UpdateRecord update) {
+ mInventoryUpdate = update;
+ }
+
+ public void setLauncherComponent(LauncherComponent component, int launchHitType) {
+ mLauncherComponent = component;
+ mLauncherHitType = launchHitType;
+ }
+
+ public void setSpawnGameEventOnHit(int hitType, int gameFlowEventType, int indexData) {
+ mGameEventHitType = hitType;
+ mGameEventOnHit = gameFlowEventType;
+ mGameEventIndexData = indexData;
+ if (hitType == HitType.INVALID) {
+ // The game event has been cleared, so reset the timer blocking a
+ // subsequent event.
+ mLastGameEventTime = -1.0f;
+ }
+ }
+
+ public final void setForceInvincible(boolean force) {
+ mForceInvincibility = force;
+ }
+
+ public final void setTakeHitSound(int hitType, SoundSystem.Sound sound) {
+ mTakeHitSoundHitType = hitType;
+ mTakeHitSound = sound;
+ }
+
+ public final void setDealHitSound(int hitType, SoundSystem.Sound sound) {
+ mDealHitSound = sound;
+ mDealHitSoundHitType = hitType;
+ }
+
+ public final void setSpawnOnDealHit(int hitType, GameObjectType objectType, boolean alignToVictimX,
+ boolean alignToVicitmY) {
+ mSpawnOnDealHitObjectType = objectType;
+ mSpawnOnDealHitHitType = hitType;
+ mAlignDealHitObjectToVictimX = alignToVictimX;
+ mAlignDealHitObjectToVictimY = alignToVicitmY;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/HotSpotSystem.java b/src/com/replica/replicaisland/HotSpotSystem.java
new file mode 100644
index 0000000..247fbe6
--- /dev/null
+++ b/src/com/replica/replicaisland/HotSpotSystem.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A system for testing positions against "hot spots" embedded in the level tile map data.
+ * A level may contain a layer of "hot spots," tiles that provide a hint to the game objects about
+ * how to act in that particular area of the game world. Hot spots are commonly used to direct AI
+ * characters, or to define areas where special collision rules apply (e.g. regions that cause
+ * instant death when entered).
+ */
+public class HotSpotSystem extends BaseObject {
+ TiledWorld mWorld;
+
+ public class HotSpotType {
+ public static final int NONE = -1;
+ public static final int GO_RIGHT = 0;
+ public static final int GO_LEFT = 1;
+ public static final int GO_UP = 2;
+ public static final int GO_DOWN = 3;
+
+ public static final int WAIT_SHORT = 4;
+ public static final int WAIT_MEDIUM = 5;
+ public static final int WAIT_LONG = 6;
+
+ public static final int ATTACK = 7;
+ public static final int TALK = 8;
+ public static final int DIE = 9;
+ public static final int WALK_AND_TALK = 10;
+ public static final int TAKE_CAMERA_FOCUS = 11;
+ public static final int RELEASE_CAMERA_FOCUS = 12;
+ public static final int END_LEVEL = 13;
+ public static final int GAME_EVENT = 14;
+ public static final int NPC_RUN_QUEUED_COMMANDS = 15;
+
+ public static final int NPC_GO_RIGHT = 16;
+ public static final int NPC_GO_LEFT = 17;
+ public static final int NPC_GO_UP = 18;
+ public static final int NPC_GO_DOWN = 19;
+ public static final int NPC_GO_UP_RIGHT = 20;
+ public static final int NPC_GO_UP_LEFT = 21;
+ public static final int NPC_GO_DOWN_LEFT = 22;
+ public static final int NPC_GO_DOWN_RIGHT = 23;
+ public static final int NPC_GO_TOWARDS_PLAYER = 24;
+ public static final int NPC_GO_RANDOM = 25;
+ public static final int NPC_GO_UP_FROM_GROUND = 26;
+ public static final int NPC_GO_DOWN_FROM_CEILING = 27;
+ public static final int NPC_STOP = 28;
+ public static final int NPC_SLOW = 29;
+
+
+ public static final int NPC_SELECT_DIALOG_1_1 = 32;
+ public static final int NPC_SELECT_DIALOG_1_2 = 33;
+ public static final int NPC_SELECT_DIALOG_1_3 = 34;
+ public static final int NPC_SELECT_DIALOG_1_4 = 35;
+ public static final int NPC_SELECT_DIALOG_1_5 = 36;
+
+ public static final int NPC_SELECT_DIALOG_2_1 = 38;
+ public static final int NPC_SELECT_DIALOG_2_2 = 39;
+ public static final int NPC_SELECT_DIALOG_2_3 = 40;
+ public static final int NPC_SELECT_DIALOG_2_4 = 41;
+ public static final int NPC_SELECT_DIALOG_2_5 = 42;
+
+
+
+ }
+
+ public HotSpotSystem() {
+ super();
+ }
+
+ @Override
+ public void reset() {
+ mWorld = null;
+ }
+
+ public final void setWorld(TiledWorld world) {
+ mWorld = world;
+ }
+
+ public int getHotSpot(float worldX, float worldY) {
+ //TOOD: take a region? how do we deal with multiple hot spot intersections?
+ int result = HotSpotType.NONE;
+ if (mWorld != null) {
+
+ final int xTile = getHitTileX(worldX);
+ final int yTile = getHitTileY(worldY);
+
+ result = mWorld.getTile(xTile, yTile);
+ }
+
+ return result;
+ }
+
+ public int getHotSpotByTile(int tileX, int tileY) {
+ //TOOD: take a region? how do we deal with multiple hot spot intersections?
+ int result = HotSpotType.NONE;
+ if (mWorld != null) {
+ result = mWorld.getTile(tileX, tileY);
+ }
+
+ return result;
+ }
+
+ public final int getHitTileX(float worldX) {
+ int xTile = 0;
+ LevelSystem level = sSystemRegistry.levelSystem;
+ if (mWorld != null && level != null) {
+ final float worldPixelWidth = level.getLevelWidth();
+ xTile = (int)Math.floor(((worldX) / worldPixelWidth) * mWorld.getWidth());
+ }
+ return xTile;
+ }
+
+ public final int getHitTileY(float worldY) {
+ int yTile = 0;
+ LevelSystem level = sSystemRegistry.levelSystem;
+ if (mWorld != null && level != null) {
+ final float worldPixelHeight = level.getLevelHeight();
+ // TODO: it is stupid to keep doing this space conversion all over the code. Fix this
+ // in the TiledWorld code!
+ final float flippedY = worldPixelHeight - (worldY);
+ yTile = (int)Math.floor((flippedY / worldPixelHeight) * mWorld.getHeight());
+ }
+ return yTile;
+ }
+
+ public final float getTileCenterWorldPositionX(int tileX) {
+ float worldX = 0.0f;
+ LevelSystem level = sSystemRegistry.levelSystem;
+ if (mWorld != null && level != null) {
+ final float tileWidth = level.getLevelWidth() / mWorld.getWidth();
+ worldX = (tileX * tileWidth) + (tileWidth / 2.0f);
+ }
+ return worldX;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/HudSystem.java b/src/com/replica/replicaisland/HudSystem.java
new file mode 100644
index 0000000..e122d18
--- /dev/null
+++ b/src/com/replica/replicaisland/HudSystem.java
@@ -0,0 +1,429 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A very simple manager for orthographic in-game UI elements.
+ * TODO: This should probably manage a number of hud objects in keeping with the component-centric
+ * architecture of this engine. The current code is monolithic and should be refactored.
+ */
+public class HudSystem extends BaseObject {
+ private static final int FUEL_BAR_EDGE_PADDING = 15;
+ private static final float FUEL_DECREASE_BAR_SPEED = 0.75f;
+ private static final float FUEL_INCREASE_BAR_SPEED = 2.0f;
+ private static final float FLY_BUTTON_X = -12.0f;
+ private static final float FLY_BUTTON_Y = -5.0f;
+ private static final float STOMP_BUTTON_X = 85.0f;
+ private static final float STOMP_BUTTON_Y = -10.0f;
+ private static final float STOMP_BUTTON_SCALE = 0.65f;
+ private static final int COLLECTABLE_EDGE_PADDING = 8;
+ private static final int MAX_DIGITS = 4;
+
+ private DrawableBitmap mFuelDrawable;
+ private DrawableBitmap mFuelBackgroundDrawable;
+ private float mFuelPercent;
+ private float mFuelTargetPercent;
+
+ private Texture mFadeTexture;
+ private float mFadeStartTime;
+ private float mFadeDuration;
+ private boolean mFadeIn;
+ private boolean mFading;
+ private int mFadePendingEventType;
+ private int mFadePendingEventIndex;
+
+ private DrawableBitmap mFlyButtonEnabledDrawable;
+ private DrawableBitmap mFlyButtonDisabledDrawable;
+ private DrawableBitmap mFlyButtonDepressedDrawable;
+
+ private DrawableBitmap mStompButtonEnabledDrawable;
+ private DrawableBitmap mStompButtonDepressedDrawable;
+
+ private Vector2 mFlyButtonLocation;
+ private boolean mFlyButtonActive;
+ private boolean mFlyButtonPressed;
+
+ private Vector2 mStompButtonLocation;
+ private boolean mStompButtonPressed;
+
+ private DrawableBitmap mRubyDrawable;
+ private DrawableBitmap mCoinDrawable;
+
+ private int mCoinCount;
+ private int mRubyCount;
+ private Vector2 mCoinLocation;
+ private Vector2 mRubyLocation;
+ private int[] mCoinDigits;
+ private int[] mRubyDigits;
+ private boolean mCoinDigitsChanged;
+ private boolean mRubyDigitsChanged;
+
+ private DrawableBitmap[] mDigitDrawables;
+ private DrawableBitmap mXDrawable;
+
+
+ public HudSystem() {
+ super();
+ mFlyButtonLocation = new Vector2();
+ mStompButtonLocation = new Vector2();
+ mCoinLocation = new Vector2();
+ mRubyLocation = new Vector2();
+ mDigitDrawables = new DrawableBitmap[10];
+ mCoinDigits = new int[MAX_DIGITS];
+ mRubyDigits = new int[MAX_DIGITS];
+
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mFuelDrawable = null;
+ mFadeTexture = null;
+ mFuelPercent = 1.0f;
+ mFuelTargetPercent = 1.0f;
+ mFading = false;
+ mFlyButtonDisabledDrawable = null;
+ mFlyButtonEnabledDrawable = null;
+ mFlyButtonDepressedDrawable = null;
+ mFlyButtonLocation.set(FLY_BUTTON_X, FLY_BUTTON_Y);
+ mFlyButtonActive = true;
+ mFlyButtonPressed = false;
+ mStompButtonEnabledDrawable = null;
+ mStompButtonDepressedDrawable = null;
+ mStompButtonLocation.set(STOMP_BUTTON_X, STOMP_BUTTON_Y);
+ mStompButtonPressed = false;
+ mCoinCount = 0;
+ mRubyCount = 0;
+ mCoinDigits[0] = 0;
+ mCoinDigits[1] = -1;
+ mRubyDigits[0] = 0;
+ mRubyDigits[1] = -1;
+ mCoinDigitsChanged = true;
+ mRubyDigitsChanged = true;
+ for (int x = 0; x < mDigitDrawables.length; x++) {
+ mDigitDrawables[x] = null;
+ }
+ mXDrawable = null;
+ mFadePendingEventType = GameFlowEvent.EVENT_INVALID;
+ mFadePendingEventIndex = 0;
+ }
+
+ public void setFuelPercent(float percent) {
+ mFuelTargetPercent = percent;
+ }
+
+ public void setFuelDrawable(DrawableBitmap fuel, DrawableBitmap background) {
+ mFuelDrawable = fuel;
+ mFuelBackgroundDrawable = background;
+ }
+
+ public void setFadeTexture(Texture texture) {
+ mFadeTexture = texture;
+ }
+
+ public void setButtonDrawables(DrawableBitmap disabled, DrawableBitmap enabled,
+ DrawableBitmap depressed, DrawableBitmap stompEnabled, DrawableBitmap stompDepressed) {
+ mFlyButtonDisabledDrawable = disabled;
+ mFlyButtonEnabledDrawable = enabled;
+ mFlyButtonDepressedDrawable = depressed;
+ mStompButtonEnabledDrawable = stompEnabled;
+ mStompButtonDepressedDrawable = stompDepressed;
+ }
+
+ public void setDigitDrawables(DrawableBitmap[] digits, DrawableBitmap xMark) {
+ mXDrawable = xMark;
+ for (int x = 0; x < mDigitDrawables.length && x < digits.length; x++) {
+ mDigitDrawables[x] = digits[x];
+ }
+ }
+
+ public void setCollectableDrawables(DrawableBitmap coin, DrawableBitmap ruby) {
+ mCoinDrawable = coin;
+ mRubyDrawable = ruby;
+ }
+
+ public void setButtonState(boolean pressed, boolean attackPressed) {
+ mFlyButtonPressed = pressed;
+ mStompButtonPressed = attackPressed;
+ }
+
+ public void startFade(boolean in, float duration) {
+ mFadeStartTime = sSystemRegistry.timeSystem.getRealTime();
+ mFadeDuration = duration;
+ mFadeIn = in;
+ mFading = true;
+ }
+
+ public void clearFade() {
+ mFading = false;
+ }
+
+ public boolean isFading() {
+ return mFading;
+ }
+
+ public void updateInventory(InventoryComponent.UpdateRecord newInventory) {
+ mCoinDigitsChanged = (mCoinCount != newInventory.coinCount);
+ mRubyDigitsChanged = (mRubyCount != newInventory.rubyCount);
+
+ mCoinCount = newInventory.coinCount;
+ mRubyCount = newInventory.rubyCount;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ final RenderSystem render = sSystemRegistry.renderSystem;
+ final VectorPool pool = sSystemRegistry.vectorPool;
+ final ContextParameters params = sSystemRegistry.contextParameters;
+ final DrawableFactory factory = sSystemRegistry.drawableFactory;
+
+ final GameObjectManager manager = sSystemRegistry.gameObjectManager;
+
+ if (manager != null && manager.getPlayer() != null) {
+ // Only draw player-specific HUD elements when there's a player.
+ if (mFuelDrawable != null && mFuelBackgroundDrawable != null
+ && render != null && pool != null && factory != null && params != null) {
+ if (mFuelPercent < mFuelTargetPercent) {
+ mFuelPercent += (FUEL_INCREASE_BAR_SPEED * timeDelta);
+ if (mFuelPercent > mFuelTargetPercent) {
+ mFuelPercent = mFuelTargetPercent;
+ }
+ } else if (mFuelPercent > mFuelTargetPercent) {
+ mFuelPercent -= (FUEL_DECREASE_BAR_SPEED * timeDelta);
+ if (mFuelPercent < mFuelTargetPercent) {
+ mFuelPercent = mFuelTargetPercent;
+ }
+ }
+
+ if (mFuelBackgroundDrawable.getWidth() == 0) {
+ // first time init
+ Texture tex = mFuelDrawable.getTexture();
+ mFuelDrawable.resize(tex.width, tex.height);
+ Texture backgroundTex = mFuelBackgroundDrawable.getTexture();
+ mFuelBackgroundDrawable.resize(backgroundTex.width, backgroundTex.height);
+ }
+
+ final int height = mFuelDrawable.getHeight();
+
+
+ Vector2 location = pool.allocate();
+ location.set(FUEL_BAR_EDGE_PADDING,
+ params.gameHeight - height - FUEL_BAR_EDGE_PADDING);
+ render.scheduleForDraw(mFuelBackgroundDrawable, location, SortConstants.HUD, false);
+ location.x += 2;
+ location.y += 2;
+ final int barWidth = (int)((100 - 4) * mFuelPercent);
+ if (barWidth >= 1) {
+ DrawableBitmap bitmap = factory.allocateDrawableBitmap();
+ if (bitmap != null) {
+ bitmap.resize(barWidth, mFuelDrawable.getHeight());
+ bitmap.setTexture(mFuelDrawable.getTexture());
+ render.scheduleForDraw(bitmap, location, SortConstants.HUD + 1, false);
+ }
+ }
+
+ pool.release(location);
+ }
+
+ if (mFlyButtonDisabledDrawable != null && mFlyButtonEnabledDrawable != null
+ && mFlyButtonDepressedDrawable != null) {
+
+ DrawableBitmap bitmap = mFlyButtonEnabledDrawable;
+ if (mFlyButtonActive && mFlyButtonPressed) {
+ bitmap = mFlyButtonDepressedDrawable;
+ } else if (!mFlyButtonActive) {
+ bitmap = mFlyButtonDisabledDrawable;
+ }
+
+ if (bitmap.getWidth() == 0) {
+ // first time init
+ Texture tex = bitmap.getTexture();
+ bitmap.resize(tex.width, tex.height);
+ }
+
+ render.scheduleForDraw(bitmap, mFlyButtonLocation, SortConstants.HUD, false);
+ }
+
+ if (mStompButtonEnabledDrawable != null && mStompButtonDepressedDrawable != null) {
+
+ DrawableBitmap bitmap = mStompButtonEnabledDrawable;
+ if (mStompButtonPressed) {
+ bitmap = mStompButtonDepressedDrawable;
+ }
+
+ if (bitmap.getWidth() == 0) {
+ // first time init
+ Texture tex = bitmap.getTexture();
+ bitmap.resize(tex.width, tex.height);
+ bitmap.setWidth((int)(tex.width * STOMP_BUTTON_SCALE));
+ bitmap.setHeight((int)(tex.height * STOMP_BUTTON_SCALE));
+ }
+
+ render.scheduleForDraw(bitmap, mStompButtonLocation, SortConstants.HUD, false);
+ }
+
+ if (mCoinDrawable != null) {
+ if (mCoinDrawable.getWidth() == 0) {
+ // first time init
+ Texture tex = mCoinDrawable.getTexture();
+ mCoinDrawable.resize(tex.width, tex.height);
+ mCoinLocation.x = (params.gameWidth / 2.0f) - tex.width / 2.0f;
+ mCoinLocation.y = params.gameHeight - tex.height - COLLECTABLE_EDGE_PADDING;
+ }
+
+ render.scheduleForDraw(mCoinDrawable, mCoinLocation, SortConstants.HUD, false);
+ if (mCoinDigitsChanged) {
+ intToDigitArray(mCoinCount, mCoinDigits);
+ mCoinDigitsChanged = false;
+ }
+ final float offset = mCoinDrawable.getWidth() * 0.75f;
+ mCoinLocation.x += offset;
+ drawNumber(mCoinLocation, mCoinDigits);
+ mCoinLocation.x -= offset;
+ }
+
+ if (mRubyDrawable != null) {
+ if (mRubyDrawable.getWidth() == 0) {
+ // first time init
+ Texture tex = mRubyDrawable.getTexture();
+ mRubyDrawable.resize(tex.width, tex.height);
+ mRubyLocation.x = (params.gameWidth / 2.0f) + 100.0f;
+ mRubyLocation.y = params.gameHeight - tex.height - COLLECTABLE_EDGE_PADDING;
+ }
+ render.scheduleForDraw(mRubyDrawable, mRubyLocation, SortConstants.HUD, false);
+ if (mRubyDigitsChanged) {
+ intToDigitArray(mRubyCount, mRubyDigits);
+ mRubyDigitsChanged = false;
+ }
+ final float offset = mRubyDrawable.getWidth() * 0.75f;
+ mRubyLocation.x += offset;
+ drawNumber(mRubyLocation, mRubyDigits);
+ mRubyLocation.x -= offset;
+ }
+ }
+
+ if (mFading && factory != null) {
+
+ final float time = sSystemRegistry.timeSystem.getRealTime();
+ final float fadeDelta = (time - mFadeStartTime);
+
+ float percentComplete = 1.0f;
+ if (fadeDelta < mFadeDuration) {
+ percentComplete = fadeDelta / mFadeDuration;
+ } else if (mFadeIn) {
+ // We've faded in. Turn fading off.
+ mFading = false;
+ }
+
+ if (percentComplete < 1.0f || !mFadeIn) {
+ float opacityValue = percentComplete;
+ if (mFadeIn) {
+ opacityValue = 1.0f - percentComplete;
+ }
+
+ DrawableBitmap bitmap = factory.allocateDrawableBitmap();
+ if (bitmap != null) {
+ bitmap.setWidth(params.gameWidth);
+ bitmap.setHeight(params.gameHeight);
+ bitmap.setTexture(mFadeTexture);
+ bitmap.setCrop(0, mFadeTexture.height, mFadeTexture.width, mFadeTexture.height);
+ bitmap.setOpacity(opacityValue);
+ render.scheduleForDraw(bitmap, Vector2.ZERO, SortConstants.FADE, false);
+ }
+ }
+
+ if (percentComplete >= 1.0f && mFadePendingEventType != GameFlowEvent.EVENT_INVALID) {
+ LevelSystem level = sSystemRegistry.levelSystem;
+ if (level != null) {
+ level.sendGameEvent(mFadePendingEventType, mFadePendingEventIndex, false);
+ mFadePendingEventType = GameFlowEvent.EVENT_INVALID;
+ mFadePendingEventIndex = 0;
+ }
+ }
+ }
+ }
+
+ private void drawNumber(Vector2 location, int[] digits) {
+ final RenderSystem render = sSystemRegistry.renderSystem;
+
+ if (mDigitDrawables[0].getWidth() == 0) {
+ // first time init
+ for (int x = 0; x < mDigitDrawables.length; x++) {
+ Texture tex = mDigitDrawables[x].getTexture();
+ mDigitDrawables[x].resize(tex.width, tex.height);
+ }
+ }
+
+ if (mXDrawable.getWidth() == 0) {
+ // first time init
+ Texture tex = mXDrawable.getTexture();
+ mXDrawable.resize(tex.width, tex.height);
+ }
+
+ final float characterWidth = mDigitDrawables[0].getWidth() / 2.0f;
+ float offset = 0.0f;
+
+ if (mXDrawable != null) {
+ render.scheduleForDraw(mXDrawable, location, SortConstants.HUD, false);
+ }
+
+ for (int x = 0; x < digits.length && digits[x] != -1; x++) {
+ int index = digits[x];
+ DrawableBitmap digit = mDigitDrawables[index];
+ if (digit != null) {
+ location.x += characterWidth;
+ offset += characterWidth;
+ render.scheduleForDraw(digit, location, SortConstants.HUD, false);
+ }
+ }
+
+ location.x -= offset;
+
+
+ }
+
+ public int intToDigitArray(int value, int[] digits) {
+ int characterCount = 1;
+ if (value >= 1000) {
+ characterCount = 4;
+ } else if (value >= 100) {
+ characterCount = 3;
+ } else if (value >= 10) {
+ characterCount = 2;
+ }
+
+ int remainingValue = value;
+ int count = 0;
+ do {
+ int index = remainingValue != 0 ? remainingValue % 10 : 0;
+ remainingValue /= 10;
+ digits[characterCount - 1 - count] = index;
+ count++;
+ } while (remainingValue > 0 && count < digits.length);
+
+ if (count < digits.length) {
+ digits[count] = -1;
+ }
+ return count;
+ }
+ public void sendGameEventOnFadeComplete(int eventType, int eventIndex) {
+ mFadePendingEventType = eventType;
+ mFadePendingEventIndex = eventIndex;
+ }
+
+
+}
diff --git a/src/com/replica/replicaisland/InputSystem.java b/src/com/replica/replicaisland/InputSystem.java
new file mode 100644
index 0000000..025871e
--- /dev/null
+++ b/src/com/replica/replicaisland/InputSystem.java
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Manages input from a roller wheel and touch screen. Reduces frequent UI messages to
+ * an average direction over a short period of time.
+ */
+public class InputSystem extends BaseObject {
+
+ private class InputButton {
+ private static final float BUTTON_LIFETIME = -1.0f; //60.0f * 10.0f;
+
+ private float mTimeout = BUTTON_LIFETIME;
+ private boolean mDown;
+ private boolean mReleased;
+ private int mDownFrames;
+ private float mLastPressedTime;
+ private float mDownTime;
+ private float mMagnitude;
+ private boolean mActive = true; // if false, does all necessary tracking but returns false to queries.
+
+ public void press(float currentTime, float magnitude) {
+ if (!mDown) {
+ mDown = true;
+ mDownFrames = 0;
+ mDownTime = currentTime;
+ }
+ mMagnitude = magnitude;
+ mLastPressedTime = currentTime;
+ mReleased = false;
+ }
+
+ public void release() {
+ mReleased = true;
+ }
+
+ public void update(float currentTime) {
+ if (mDown) {
+ mDownFrames++;
+ if (mDownFrames > 1) {
+ if (mReleased || (mTimeout > 0.0f && (currentTime - mLastPressedTime) > mTimeout)) {
+ mDown = false;
+ }
+ }
+ }
+ }
+
+ public final boolean getPressed() {
+ return mActive ? mDown : false;
+ }
+
+ public final boolean getTriggered() {
+ return mActive ? (mDown && mDownFrames <= 1) : false;
+ }
+
+ public final float getPressedDuration(float currentTime) {
+ return mActive ? (currentTime - mDownTime) : 0.0f;
+ }
+
+ public final float getLastPressedTime() {
+ return mLastPressedTime;
+ }
+
+ public final float getMagnitude() {
+ float magnitude = 0.0f;
+ if (mActive && mDown) {
+ magnitude = mMagnitude;
+ }
+ return magnitude;
+ }
+
+ public final void setTimeout(float timeout) {
+ mTimeout = timeout;
+ }
+
+ public void setActive(boolean active) {
+ mActive = active;
+ }
+ }
+
+ private class InputXY {
+ private InputButton mXAxis = new InputButton();
+ private InputButton mYAxis = new InputButton();
+
+ public final void press(float currentTime, float x, float y) {
+ mXAxis.press(currentTime, x);
+ mYAxis.press(currentTime, y);
+ }
+
+ public final void release() {
+ mXAxis.release();
+ mYAxis.release();
+ }
+
+ public final void update(float currentTime) {
+ mXAxis.update(currentTime);
+ mYAxis.update(currentTime);
+ }
+
+ public final boolean getTriggered() {
+ return mXAxis.getTriggered() || mYAxis.getTriggered();
+ }
+
+ public final boolean getPressed() {
+ return mXAxis.getPressed() || mYAxis.getPressed();
+ }
+
+ public final void setVector(Vector2 vector) {
+ vector.x = mXAxis.getMagnitude();
+ vector.y = mYAxis.getMagnitude();
+ }
+
+ public final float getX() {
+ return mXAxis.getMagnitude();
+ }
+
+ public final float getY() {
+ return mYAxis.getMagnitude();
+ }
+
+ public final float getLastPressedTime() {
+ return Math.max(mXAxis.getLastPressedTime(), mYAxis.getLastPressedTime());
+ }
+
+ public final void releaseX() {
+ mXAxis.release();
+ }
+
+ public final void releaseY() {
+ mYAxis.release();
+ }
+
+ public final void setTimeout(float timeout) {
+ mXAxis.setTimeout(timeout);
+ mYAxis.setTimeout(timeout);
+ }
+ }
+
+ private final static float KEY_ROLL_SPEED = 0.25f;
+ private final static float TILT_ROLL_SPEED = 5.0f;
+ private final static float DPAD_TIMEOUT = -1.0f; //2.0f;
+ private final static float ROLL_TIMEOUT = 0.1f;
+ private final static int ROLL_HISTORY_SIZE = 10;
+ // Raw trackball input is filtered by this value. Increasing it will
+ // make the control more twitchy, while decreasing it will make the control more precise.
+ private final static float ROLL_FILTER = 0.4f;
+
+ private InputXY mTouchScreen = new InputXY();
+ private InputXY mOrientationSensor = new InputXY();
+ private InputXY mDirectionalPad = new InputXY();
+ private InputButton mClick = new InputButton();
+
+ private Vector2 mTempDirection = new Vector2();
+
+ // Roll averaging
+ private FixedSizeArray<Vector2> mRecentRollInput = new FixedSizeArray<Vector2>(ROLL_HISTORY_SIZE);
+ private int mCurrentInputSlot;
+ private Vector2 mCurrentRollDirection = new Vector2();
+
+ private boolean mUseOrientationForRoll = false; // If true, pipes tilt into the directional pad.
+
+
+ public InputSystem() {
+ super();
+ for (int x = 0; x < ROLL_HISTORY_SIZE; x++) {
+ mRecentRollInput.add(new Vector2());
+ }
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mCurrentInputSlot = 0;
+ mCurrentRollDirection.zero();
+ }
+
+ public void roll(float x, float y) {
+ if (!mDirectionalPad.getPressed()) {
+ for (int index = 0; index < ROLL_HISTORY_SIZE; index++) {
+ mRecentRollInput.get(index).zero();
+ }
+ mCurrentRollDirection.set(x * ROLL_FILTER, y * ROLL_FILTER);
+ mRecentRollInput.get(0).set(x * ROLL_FILTER, y * ROLL_FILTER);
+ mCurrentInputSlot = 1;
+ } else {
+ // recalculate accumulated direction
+ mCurrentRollDirection.subtract(mRecentRollInput.get(mCurrentInputSlot));
+ mRecentRollInput.get(mCurrentInputSlot).set(x * ROLL_FILTER, y * ROLL_FILTER);
+ mCurrentRollDirection.add(mRecentRollInput.get(mCurrentInputSlot));
+
+ mCurrentInputSlot = (mCurrentInputSlot + 1) % ROLL_HISTORY_SIZE;
+
+ // Clamp to 1.0
+ if (Math.abs(mCurrentRollDirection.x) > 1.0f) {
+ if (mCurrentRollDirection.x < 0.0f) {
+ mCurrentRollDirection.x = (-1.0f);
+ } else {
+ mCurrentRollDirection.x = (1.0f);
+ }
+ }
+
+ if (Math.abs(mCurrentRollDirection.y) > 1.0f) {
+ if (mCurrentRollDirection.y < 0.0f) {
+ mCurrentRollDirection.y = (-1.0f);
+ } else {
+ mCurrentRollDirection.y = (1.0f);
+ }
+ }
+ }
+ TimeSystem time = sSystemRegistry.timeSystem;
+ mDirectionalPad.setTimeout(ROLL_TIMEOUT);
+ mDirectionalPad.press(time.getGameTime(), mCurrentRollDirection.x, mCurrentRollDirection.y);
+ }
+
+ public void touch(int x, int y, boolean released) {
+ if (released) {
+ mTouchScreen.release();
+ } else {
+ ContextParameters params = sSystemRegistry.contextParameters;
+ TimeSystem time = sSystemRegistry.timeSystem;
+ // Change the origin of the touch location from the top-left to the bottom-left to match
+ // OpenGL space.
+ // TODO: UNIFY THIS SHIT
+ mTouchScreen.press(time.getGameTime(), x, params.gameHeight - y);
+ }
+ }
+
+ public void clickDown() {
+ TimeSystem time = sSystemRegistry.timeSystem;
+ mClick.press(time.getGameTime(), 1.0f);
+ }
+
+ public void clickUp() {
+ mClick.release();
+ }
+
+ public void setOrientation(float azimuth, float pitch, float roll) {
+ //DebugLog.d("Orientation", "Pitch: " + pitch + " Roll: " + roll);
+ final float correctedPitch = -pitch / 180.0f;
+ final float correctedRoll = -roll / 90.0f;
+ //DebugLog.d("Orientation", "Pitch: " + correctedPitch + " Roll: " + correctedRoll);
+
+ TimeSystem time = sSystemRegistry.timeSystem;
+ mOrientationSensor.press(time.getGameTime(), correctedPitch, correctedRoll);
+ if (mUseOrientationForRoll) {
+ float smoothedPitch = correctedPitch;
+ float smoothedRoll = correctedRoll;
+ if (Math.abs(correctedPitch) < 0.03f) {
+ smoothedPitch = 0.0f; // dead zone
+ } else if (Math.abs(correctedPitch) < 0.1f) {
+ smoothedPitch *= 0.75f;
+ }
+ if (Math.abs(smoothedRoll) < 0.03f) {
+ smoothedRoll = 0.0f; // dead zone
+ } else if (Math.abs(smoothedRoll) < 0.1f) {
+ smoothedRoll *= 0.75f;
+ }
+
+ //roll(smoothedPitch * TILT_ROLL_SPEED, smoothedRoll * TILT_ROLL_SPEED);
+ mDirectionalPad.press(time.getGameTime(), smoothedPitch * TILT_ROLL_SPEED, smoothedRoll * TILT_ROLL_SPEED);
+ }
+ }
+
+ public void keyDown(boolean left, boolean right, boolean up, boolean down,
+ boolean touch, boolean click) {
+ float x = 0.0f;
+ float y = 0.0f;
+ TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+ if (left) {
+ x = -KEY_ROLL_SPEED;
+ } else if (right) {
+ x = KEY_ROLL_SPEED;
+ }
+ if (up) {
+ y = KEY_ROLL_SPEED;
+ } else if (down) {
+ y = -KEY_ROLL_SPEED;
+ }
+ if (x != 0.0f || y != 0.0f) {
+ mDirectionalPad.setTimeout(DPAD_TIMEOUT);
+ mDirectionalPad.press(gameTime, x, y);
+ }
+
+ if (touch) {
+ mTouchScreen.press(gameTime, 0, 0);
+ }
+
+ if (click) {
+ mClick.press(gameTime, 1.0f);
+ }
+ }
+
+ public void keyUp(boolean left, boolean right, boolean up, boolean down,
+ boolean touch, boolean click) {
+ if (left || right) {
+ mDirectionalPad.releaseX();
+ }
+
+ if (up || down) {
+ mDirectionalPad.releaseY();
+ }
+
+ if (touch) {
+ mTouchScreen.release();
+ }
+
+ if (click) {
+ mClick.release();
+ }
+ }
+
+ public void releaseAllKeys() {
+ mDirectionalPad.releaseX();
+ mDirectionalPad.releaseY();
+ mTouchScreen.release();
+ mClick.release();
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+
+ mTouchScreen.update(gameTime);
+ mDirectionalPad.update(gameTime);
+ mOrientationSensor.update(gameTime);
+ mClick.update(gameTime);
+ }
+
+ public final boolean getRollTriggered() {
+ return mDirectionalPad.getPressed();
+ }
+
+ public final Vector2 getRollDirection() {
+ mDirectionalPad.setVector(mTempDirection);
+ return mTempDirection;
+ }
+
+ public final boolean getTouchTriggered() {
+ return mTouchScreen.getTriggered();
+ }
+
+ public final boolean getTouchPressed() {
+ return mTouchScreen.getPressed();
+ }
+
+ public final Vector2 getTouchPosition() {
+ mTouchScreen.setVector(mTempDirection);
+ return mTempDirection;
+ }
+
+ public final boolean getTouchedWithinRegion(int x, int y, int width, int height) {
+ mTouchScreen.setVector(mTempDirection);
+
+ return (mTempDirection.x >= x &&
+ mTempDirection.y >= y &&
+ mTempDirection.x <= x + width &&
+ mTempDirection.y <= y + height);
+ }
+
+ public final boolean getClickTriggered() {
+ return mClick.getTriggered();
+ }
+
+ public final void clearClickTriggered() {
+ mClick.release();
+ }
+
+ public final boolean getClickPressed() {
+ return mClick.getPressed();
+ }
+
+ public final float getLastRollTime() {
+ return mDirectionalPad.getLastPressedTime();
+ }
+
+ public final float getLastTouchTime() {
+ return mTouchScreen.getLastPressedTime();
+ }
+
+ public final float getLastClickTime() {
+ return mClick.getLastPressedTime();
+ }
+
+ public final float getPitch() {
+ return mOrientationSensor.getX();
+ }
+
+ public final float getRoll() {
+ return mOrientationSensor.getY();
+ }
+
+ public void setClickActive(boolean active) {
+ mClick.setActive(active);
+ }
+
+ public void setUseOrientationForRoll(boolean rollWithOrientation) {
+ mUseOrientationForRoll = rollWithOrientation;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/Interpolator.java b/src/com/replica/replicaisland/Interpolator.java
new file mode 100644
index 0000000..4e5fc3c
--- /dev/null
+++ b/src/com/replica/replicaisland/Interpolator.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Helper class for interpolating velocity over time given a target velocity and acceleration.
+ * The current velocity will be accelerated towards the target until the target is reached.
+ * Note that acceleration is effectively an absolute value--it always points in the direction of
+ * the target velocity.
+ */
+public class Interpolator extends AllocationGuard {
+
+ private float mCurrent;
+ private float mTarget;
+ private float mAcceleration;
+
+ public Interpolator() {
+ super();
+ }
+
+ // Rather than simply interpolating acceleration and velocity for each time step
+ // (as in, position += (velocity * time); velocity += (acceleration * time);),
+ // we actually perform the work needed to calculate the integral of velocity with respect to
+ // time.
+ //
+ // The integral of velocity is:
+ //
+ // integral[(v + aT)dT]
+ //
+ // Simplified to:
+ //
+ // vT + 1/2 * aT^2
+ //
+ // Thus:
+ // change in position = velocity * time + (0.5 * acceleration * (time^2))
+ // change in velocity = acceleration * time
+
+ public void set(float current, float target, float acceleration) {
+ mCurrent = current;
+ mTarget = target;
+ mAcceleration = acceleration;
+ }
+
+ // While this function writes directly to velocity, it doesn't affect
+ // position. Instead, the position offset is returned so that it can be blended.
+ public float interpolate(float secondsDelta) {
+ float oldVelocity = mCurrent;
+
+ // point the acceleration at the target, or zero it if we are already
+ // there
+ float directionalAcceleration = calculateAcceleration(oldVelocity, mAcceleration, mTarget);
+
+ // calculate scaled acceleration (0.5 * acceleration * (time^2))
+ float scaledAcceleration;
+ scaledAcceleration = scaleAcceleration(directionalAcceleration, secondsDelta);
+
+ // calculate the change in position
+ float positionOffset = (oldVelocity * secondsDelta) + scaledAcceleration;
+
+ // change in velocity = v + aT
+ float newVelocity = oldVelocity + (directionalAcceleration * secondsDelta);
+
+ // check to see if we've passed our target velocity since the last time
+ // step. If so, clamp to the target
+ if (passedTarget(oldVelocity, newVelocity, mTarget)) {
+ newVelocity = mTarget;
+ }
+
+ mCurrent = newVelocity;
+
+ return positionOffset;
+ }
+
+ public float getCurrent() {
+ return mCurrent;
+ }
+
+ private boolean passedTarget(float oldVelocity, float newVelocity, float targetVelocity) {
+ boolean result = false;
+
+ if (oldVelocity < targetVelocity && newVelocity > targetVelocity) {
+ result = true;
+ } else if (oldVelocity > targetVelocity && newVelocity < targetVelocity) {
+ result = true;
+ }
+
+ return result;
+ }
+
+ // find the magnitude and direction of acceleration.
+ // in this system, acceleration always points toward target velocity
+ private float calculateAcceleration(float velocity, float acceleration, float target) {
+ if (Math.abs(velocity - target) < 0.0001f) {
+ // no accel needed
+ acceleration = 0.0f;
+ } else if (velocity > target) {
+ // accel must be negative
+ acceleration *= -1.0f;
+ }
+
+ return acceleration;
+ }
+
+ // calculates 1/2 aT^2
+ private float scaleAcceleration(float acceleration, float secondsDelta) {
+ float timeSquared = (secondsDelta * secondsDelta);
+ float scaledAccel = acceleration * timeSquared;
+ scaledAccel *= 0.5f;
+
+ return scaledAccel;
+ }
+}
diff --git a/src/com/replica/replicaisland/InventoryComponent.java b/src/com/replica/replicaisland/InventoryComponent.java
new file mode 100644
index 0000000..ae23b7c
--- /dev/null
+++ b/src/com/replica/replicaisland/InventoryComponent.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+public class InventoryComponent extends GameComponent {
+ private UpdateRecord mInventory;
+ private boolean mInventoryChanged;
+
+ public InventoryComponent() {
+ super();
+ mInventory = new UpdateRecord();
+ reset();
+ setPhase(ComponentPhases.FRAME_END.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mInventoryChanged = true;
+ mInventory.reset();
+ }
+
+ public void applyUpdate(UpdateRecord record) {
+ mInventory.add(record);
+ mInventoryChanged = true;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mInventoryChanged) {
+ HudSystem hud = sSystemRegistry.hudSystem;
+ if (hud != null) {
+ hud.updateInventory(mInventory);
+ }
+ mInventoryChanged = false;
+ }
+ }
+
+ public UpdateRecord getRecord() {
+ return mInventory;
+ }
+
+ public void setChanged() {
+ mInventoryChanged = true;
+ }
+
+ public static class UpdateRecord extends BaseObject {
+ public int rubyCount;
+ public int coinCount;
+ public int diaryCount;
+
+ public UpdateRecord() {
+ super();
+ }
+
+ public void reset() {
+ rubyCount = 0;
+ coinCount = 0;
+ diaryCount = 0;
+ }
+
+ public void add(UpdateRecord other) {
+ rubyCount += other.rubyCount;
+ coinCount += other.coinCount;
+ diaryCount += other.diaryCount;
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/LaunchProjectileComponent.java b/src/com/replica/replicaisland/LaunchProjectileComponent.java
new file mode 100644
index 0000000..ac8de8e
--- /dev/null
+++ b/src/com/replica/replicaisland/LaunchProjectileComponent.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.GameObject.ActionType;
+
+/**
+ * A component that allows an object to spawn other objects and apply velocity to them at
+ * specific intervals. Can be used to launch projectiles, particle effects, or any other type
+ * of game object.
+ */
+public class LaunchProjectileComponent extends GameComponent {
+ private GameObjectFactory.GameObjectType mObjectTypeToSpawn;
+ private float mOffsetX;
+ private float mOffsetY;
+ private float mVelocityX;
+ private float mVelocityY;
+ private float mThetaError;
+ private GameObject.ActionType mRequiredAction;
+ private float mDelayBetweenShots;
+ private int mProjectilesInSet;
+ private float mDelayBetweenSets;
+ private int mSetsPerActivation;
+ private float mDelayBeforeFirstSet;
+
+ private float mLastProjectileTime;
+ private float mSetStartedTime;
+ private int mLaunchedCount;
+ private int mSetCount;
+
+ private boolean mTrackProjectiles;
+ private int mMaxTrackedProjectiles;
+ private int mTrackedProjectileCount;
+
+ private Vector2 mWorkingVector;
+
+ private SoundSystem.Sound mShootSound;
+
+
+ public LaunchProjectileComponent() {
+ super();
+ setPhase(ComponentPhases.POST_COLLISION.ordinal());
+ mWorkingVector = new Vector2();
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mRequiredAction = ActionType.INVALID;
+ mObjectTypeToSpawn = GameObjectFactory.GameObjectType.INVALID;
+ mOffsetX = 0.0f;
+ mOffsetY = 0.0f;
+ mVelocityX = 0.0f;
+ mVelocityY = 0.0f;
+ mDelayBetweenShots = 0.0f;
+ mProjectilesInSet = 0;
+ mDelayBetweenSets = 0.0f;
+ mLastProjectileTime = 0.0f;
+ mSetStartedTime = -1.0f;
+ mLaunchedCount = 0;
+ mSetCount = 0;
+ mSetsPerActivation = -1;
+ mProjectilesInSet = 0;
+ mDelayBeforeFirstSet = 0.0f;
+ mTrackProjectiles = false;
+ mMaxTrackedProjectiles = 0;
+ mTrackedProjectileCount = 0;
+ mThetaError = 0.0f;
+ mShootSound = null;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+
+ if (mTrackedProjectileCount < mMaxTrackedProjectiles || !mTrackProjectiles) {
+ if (parentObject.getCurrentAction() == mRequiredAction
+ || mRequiredAction == ActionType.INVALID) {
+
+ if (mSetStartedTime == -1.0f) {
+ mLaunchedCount = 0;
+ mLastProjectileTime = 0.0f;
+ mSetStartedTime = gameTime;
+ }
+
+ final float setDelay = mSetCount > 0 ? mDelayBetweenSets : mDelayBeforeFirstSet;
+
+ if (gameTime - mSetStartedTime >= setDelay &&
+ (mSetCount < mSetsPerActivation || mSetsPerActivation == -1)) {
+ // We can start shooting.
+ final float timeSinceLastShot = gameTime - mLastProjectileTime;
+
+ if (timeSinceLastShot >= mDelayBetweenShots) {
+
+ launch(parentObject);
+ mLastProjectileTime = gameTime;
+
+ if (mLaunchedCount >= mProjectilesInSet && mProjectilesInSet > 0) {
+ mSetStartedTime = -1.0f;
+ mSetCount++;
+ }
+ }
+ }
+ } else {
+ // Force the timer to start counting when the right action is activated.
+ mSetStartedTime = -1.0f;
+ mSetCount = 0;
+ }
+ }
+ }
+
+ private void launch(GameObject parentObject) {
+ mLaunchedCount++;
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (factory != null && manager != null) {
+ float offsetX = mOffsetX;
+ float offsetY = mOffsetY;
+ boolean flip = false;
+ if (parentObject.facingDirection.x < 0.0f) {
+ offsetX = parentObject.width - mOffsetX;
+ flip = true;
+ }
+
+ if (parentObject.facingDirection.y < 0.0f) {
+ offsetY = parentObject.height - mOffsetY;
+ }
+
+ final float x = parentObject.getPosition().x + offsetX;
+ final float y = parentObject.getPosition().y + offsetY;
+ GameObject object = factory.spawn(mObjectTypeToSpawn, x, y, flip);
+ mWorkingVector.set(1.0f, 1.0f);
+ if (mThetaError > 0.0f) {
+ final float angle = (float)(Math.random() * mThetaError * Math.PI * 2.0f);
+ mWorkingVector.x = (float)Math.sin(angle);
+ mWorkingVector.y = (float)Math.cos(angle);
+ if (Utils.close(mWorkingVector.length2(), 0.0f)) {
+ mWorkingVector.set(1.0f, 1.0f);
+ }
+ }
+ mWorkingVector.x *= flip ? -mVelocityX : mVelocityX;
+ mWorkingVector.y *= mVelocityY;
+
+ object.getVelocity().set(mWorkingVector);
+ object.getTargetVelocity().set(mWorkingVector);
+ // Center the projectile on the spawn point.
+ object.getPosition().x -= object.width / 2.0f;
+ object.getPosition().y -= object.height / 2.0f;
+
+
+ if (mTrackProjectiles) {
+ object.commitUpdates();
+ LifetimeComponent projectileLife = object.findByClass(LifetimeComponent.class);
+ if (projectileLife != null) {
+ projectileLife.setTrackingSpawner(this);
+ mTrackedProjectileCount++;
+ }
+ }
+ manager.add(object);
+
+ if (mShootSound != null) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.play(mShootSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+ }
+
+
+ }
+
+ public final void setObjectTypeToSpawn(GameObjectFactory.GameObjectType objectTypeToSpawn) {
+ mObjectTypeToSpawn = objectTypeToSpawn;
+ }
+
+ public final void setOffsetX(float offsetX) {
+ mOffsetX = offsetX;
+ }
+
+ public final void setOffsetY(float offsetY) {
+ mOffsetY = offsetY;
+ }
+
+ public final void setVelocityX(float velocityX) {
+ mVelocityX = velocityX;
+ }
+
+ public final void setVelocityY(float velocityY) {
+ mVelocityY = velocityY;
+ }
+
+ public final void setRequiredAction(GameObject.ActionType requiredAction) {
+ mRequiredAction = requiredAction;
+ }
+
+ public final void setDelayBetweenShots(float launchDelay) {
+ mDelayBetweenShots = launchDelay;
+ }
+
+ public final void setDelayBetweenSets(float delayBetweenSets) {
+ mDelayBetweenSets = delayBetweenSets;
+ }
+
+ public final void setDelayBeforeFirstSet(float delayBeforeFirstSet) {
+ mDelayBeforeFirstSet = delayBeforeFirstSet;
+ }
+
+ public final void setShotsPerSet(int shotCount) {
+ mProjectilesInSet = shotCount;
+ }
+
+ public final void setSetsPerActivation(int setCount) {
+ mSetsPerActivation = setCount;
+ }
+
+ public final void enableProjectileTracking(int max) {
+ mMaxTrackedProjectiles = max;
+ mTrackProjectiles = true;
+ }
+
+ public final void trackedProjectileDestroyed() {
+ assert mTrackProjectiles;
+ if (mTrackedProjectileCount == mMaxTrackedProjectiles) {
+ // Let's restart the set.
+ mSetStartedTime = -1.0f;
+ mSetCount = 0;
+ }
+ mTrackedProjectileCount--;
+ }
+
+ public final void setThetaError(float error) {
+ mThetaError = error;
+ }
+
+ public final void setShootSound(SoundSystem.Sound shoot) {
+ mShootSound = shoot;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/LauncherComponent.java b/src/com/replica/replicaisland/LauncherComponent.java
new file mode 100644
index 0000000..79497ec
--- /dev/null
+++ b/src/com/replica/replicaisland/LauncherComponent.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.GameObject.ActionType;
+import com.replica.replicaisland.SoundSystem.Sound;
+
+public class LauncherComponent extends GameComponent {
+ private final static float DEFAULT_LAUNCH_DELAY = 2.0f;
+ private final static float DEFAULT_LAUNCH_MAGNITUDE = 2000.0f;
+ private final static float DEFAULT_POST_LAUNCH_DELAY = 1.0f;
+ private GameObject mShot;
+ private float mLaunchTime;
+ private float mAngle;
+ private float mLaunchDelay;
+ private Vector2 mLaunchDirection;
+ private float mLaunchMagnitude;
+ private float mPostLaunchDelay;
+ private boolean mDriveActions;
+ private GameObjectFactory.GameObjectType mLaunchEffect;
+ private float mLaunchEffectOffsetX;
+ private float mLaunchEffectOffsetY;
+ private Sound mLaunchSound;
+
+ public LauncherComponent() {
+ super();
+ mLaunchDirection = new Vector2();
+ reset();
+ setPhase(ComponentPhases.THINK.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mShot = null;
+ mLaunchTime = 0.0f;
+ mAngle = 0.0f;
+ mLaunchDelay = DEFAULT_LAUNCH_DELAY;
+ mLaunchMagnitude = DEFAULT_LAUNCH_MAGNITUDE;
+ mPostLaunchDelay = DEFAULT_POST_LAUNCH_DELAY;
+ mDriveActions = true;
+ mLaunchEffect = GameObjectFactory.GameObjectType.INVALID;
+ mLaunchEffectOffsetX = 0.0f;
+ mLaunchEffectOffsetY = 0.0f;
+ mLaunchSound = null;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+ GameObject parentObject = (GameObject)parent;
+
+ if (mShot != null) {
+ if (mShot.life <= 0) {
+ // Looks like the shot is dead. Let's forget about it.
+ // TODO: this is unreliable. We should have a "notify on death" event or something.
+ mShot = null;
+ } else {
+ if (gameTime > mLaunchTime) {
+ fire(mShot, parentObject, mAngle);
+ mShot = null;
+ if (mDriveActions) {
+ parentObject.setCurrentAction(ActionType.ATTACK);
+ }
+ } else {
+ mShot.setPosition(parentObject.getPosition());
+ }
+ }
+ } else if (gameTime > mLaunchTime + mPostLaunchDelay) {
+ if (mDriveActions) {
+ parentObject.setCurrentAction(ActionType.IDLE);
+ }
+ }
+ }
+
+ public void prepareToLaunch(GameObject object, GameObject parentObject) {
+ if (mShot != object) {
+ if (mShot != null) {
+ // We already have a shot loaded and we are asked to shoot something else.
+ // Shoot the current shot off and then load the new one.
+ fire(mShot, parentObject, mAngle);
+ }
+ final TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+ mShot = object;
+ mLaunchTime = gameTime + mLaunchDelay;
+ }
+ }
+
+ private void fire(GameObject object, GameObject parentObject, float mAngle) {
+ if (mDriveActions) {
+ object.setCurrentAction(ActionType.MOVE);
+ }
+ mLaunchDirection.set((float)Math.sin(mAngle), (float)Math.cos(mAngle));
+ mLaunchDirection.multiply(parentObject.facingDirection);
+ mLaunchDirection.multiply(mLaunchMagnitude);
+ object.setVelocity(mLaunchDirection);
+
+ if (mLaunchSound != null) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.play(mLaunchSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+
+ if (mLaunchEffect != GameObjectFactory.GameObjectType.INVALID) {
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (factory != null && manager != null) {
+ final Vector2 position = parentObject.getPosition();
+
+ GameObject effect = factory.spawn(mLaunchEffect,
+ position.x + (mLaunchEffectOffsetX * parentObject.facingDirection.x),
+ position.y + (mLaunchEffectOffsetY * parentObject.facingDirection.y),
+ false);
+
+ manager.add(effect);
+ }
+ }
+ }
+
+ public void setup(float angle, float magnitude, float launchDelay, float postLaunchDelay, boolean driveActions) {
+ mAngle = angle;
+ mLaunchMagnitude = magnitude;
+ mLaunchDelay = launchDelay;
+ mPostLaunchDelay = postLaunchDelay;
+ mDriveActions = driveActions;
+ }
+
+ public void setLaunchEffect(GameObjectFactory.GameObjectType effectType, float offsetX, float offsetY) {
+ mLaunchEffect = effectType;
+ mLaunchEffectOffsetX = offsetX;
+ mLaunchEffectOffsetY = offsetY;
+ }
+
+ public void setLaunchSound(Sound sound) {
+ mLaunchSound = sound;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/Lerp.java b/src/com/replica/replicaisland/Lerp.java
new file mode 100644
index 0000000..c6f1ae4
--- /dev/null
+++ b/src/com/replica/replicaisland/Lerp.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+public final class Lerp {
+
+ public static float lerp(float start, float target, float duration, float timeSinceStart)
+ {
+ float value = start;
+ if (timeSinceStart > 0.0f && timeSinceStart < duration)
+ {
+ final float range = target - start;
+ final float percent = timeSinceStart / duration;
+ value = start + (range * percent);
+ }
+ else if (timeSinceStart >= duration)
+ {
+ value = target;
+ }
+ return value;
+ }
+
+ public static float ease(float start, float target, float duration, float timeSinceStart)
+ {
+ float value = start;
+ if (timeSinceStart > 0.0f && timeSinceStart < duration)
+ {
+ final float range = target - start;
+ final float percent = timeSinceStart / (duration / 2.0f);
+ if (percent < 1.0f)
+ {
+ value = start + ((range / 2.0f) * percent * percent * percent);
+ }
+ else
+ {
+ final float shiftedPercent = percent - 2.0f;
+ value = start + ((range / 2.0f) *
+ ((shiftedPercent * shiftedPercent * shiftedPercent) + 2.0f));
+ }
+ }
+ else if (timeSinceStart >= duration)
+ {
+ value = target;
+ }
+ return value;
+ }
+}
diff --git a/src/com/replica/replicaisland/LevelBuilder.java b/src/com/replica/replicaisland/LevelBuilder.java
new file mode 100644
index 0000000..08f2248
--- /dev/null
+++ b/src/com/replica/replicaisland/LevelBuilder.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+public class LevelBuilder extends BaseObject {
+ private final static int THEME_GRASS = 0;
+ private final static int THEME_ISLAND = 1;
+ private final static int THEME_SEWER = 2;
+ private final static int THEME_UNDERGROUND = 3;
+ private final static int THEME_LAB = 4;
+ private final static int THEME_LIGHTING = 5;
+ private final static int THEME_TUTORIAL = 6;
+
+
+ private final static int BACKGROUND_SUNSET = 0;
+ private final static int BACKGROUND_ISLAND = 1;
+ private final static int BACKGROUND_SEWER = 2;
+ private final static int BACKGROUND_UNDERGROUND = 3;
+ private final static int BACKGROUND_FOREST = 4;
+ private final static int BACKGROUND_ISLAND2 = 5;
+ private final static int BACKGROUND_LAB = 6;
+
+ public LevelBuilder() {
+ super();
+ }
+
+ @Override
+ public void reset() {
+ }
+
+
+ public GameObject buildBackground(int backgroundImage, int levelWidth, int levelHeight) {
+ // Generate the scrolling background.
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+
+ GameObject background = new GameObject();
+
+ if (textureLibrary != null) {
+
+ int backgroundResource = -1;
+
+ switch (backgroundImage) {
+ case BACKGROUND_SUNSET:
+ backgroundResource = R.drawable.background_sunset;
+ break;
+ case BACKGROUND_ISLAND:
+ backgroundResource = R.drawable.background_island;
+ break;
+ case BACKGROUND_SEWER:
+ backgroundResource = R.drawable.background_sewage;
+ break;
+ case BACKGROUND_UNDERGROUND:
+ backgroundResource = R.drawable.background_underground;
+ break;
+ case BACKGROUND_FOREST:
+ backgroundResource = R.drawable.background_grass2;
+ break;
+ case BACKGROUND_ISLAND2:
+ backgroundResource = R.drawable.background_island2;
+ break;
+ case BACKGROUND_LAB:
+ backgroundResource = R.drawable.background_lab01;
+ break;
+ default:
+ assert false;
+ }
+
+ if (backgroundResource > -1) {
+
+ // Background Layer //
+ RenderComponent backgroundRender = new RenderComponent();
+ backgroundRender.setPriority(SortConstants.BACKGROUND_START);
+
+ ContextParameters params = sSystemRegistry.contextParameters;
+ // The background image is ideally 1.5 times the size of the largest screen axis
+ // (normally the width, but just in case, let's calculate it).
+ final int idealSize = (int)Math.max(params.gameWidth * 1.5f, params.gameHeight * 1.5f);
+ int width = idealSize;
+ int height = idealSize;
+
+ ScrollerComponent scroller3 =
+ new ScrollerComponent(0.0f, 0.0f, width, height,
+ textureLibrary.allocateTexture(backgroundResource));
+ scroller3.setRenderComponent(backgroundRender);
+
+ // Scroll speeds such that the background will evenly match the beginning
+ // and end of the level. Don't allow speeds > 1.0, though; that would be faster than
+ // the foreground, which is disorienting and looks like rotation.
+ final float scrollSpeedX = Math.min((float)(width - params.gameWidth) / (levelWidth - params.gameWidth), 1.0f);
+ final float scrollSpeedY = Math.min((float)(height - params.gameHeight) / (levelHeight - params.gameHeight), 1.0f);
+
+
+ scroller3.setScrollSpeed(scrollSpeedX, scrollSpeedY);
+
+ backgroundRender.setCameraRelative(false);
+
+ background.add(scroller3);
+ background.add(backgroundRender);
+ }
+ }
+ return background;
+ }
+
+ public void addTileMapLayer(GameObject background, int priority, float scrollSpeed,
+ int width, int height, int tileWidth, int tileHeight, TiledWorld world,
+ int theme) {
+
+ int tileMapIndex = 0;
+ switch(theme) {
+ case THEME_GRASS:
+ tileMapIndex = R.drawable.grass;
+ break;
+ case THEME_ISLAND:
+ tileMapIndex = R.drawable.island;
+ break;
+ case THEME_SEWER:
+ tileMapIndex = R.drawable.sewage;
+ break;
+ case THEME_UNDERGROUND:
+ tileMapIndex = R.drawable.cave;
+ break;
+ case THEME_LAB:
+ tileMapIndex = R.drawable.lab;
+ break;
+ case THEME_LIGHTING:
+ tileMapIndex = R.drawable.titletileset;
+ priority = SortConstants.OVERLAY; //hack!
+ break;
+ case THEME_TUTORIAL:
+ tileMapIndex = R.drawable.tutorial;
+ break;
+ default:
+ assert false;
+ }
+
+ RenderComponent backgroundRender = new RenderComponent();
+ backgroundRender.setPriority(priority);
+
+ //Vertex Buffer Code
+ TextureLibrary textureLibrary = sSystemRegistry.shortTermTextureLibrary;
+ TiledVertexGrid bg = new TiledVertexGrid(textureLibrary.allocateTexture(tileMapIndex),
+ width, height, tileWidth, tileHeight);
+ bg.setWorld(world);
+
+ //TODO: The map format should really just output independent speeds for x and y,
+ // but as a short term solution we can assume parallax layers lock in the smaller
+ // direction of movement.
+ float xScrollSpeed = 1.0f;
+ float yScrollSpeed = 1.0f;
+
+ if (world.getWidth() > world.getHeight()) {
+ xScrollSpeed = scrollSpeed;
+ } else {
+ yScrollSpeed = scrollSpeed;
+ }
+
+ ScrollerComponent scroller = new ScrollerComponent(xScrollSpeed, yScrollSpeed,
+ width, height, bg);
+ scroller.setRenderComponent(backgroundRender);
+
+ background.add(scroller);
+ background.add(backgroundRender);
+ backgroundRender.setCameraRelative(false);
+ }
+
+ // This method is a HACK to workaround the stupid map file format.
+ // We want the foreground layer to be render priority FOREGROUND, but
+ // we don't know which is the foreground layer until we've added them all.
+ // So now that we've added them all, find foreground layer and make sure
+ // its render priority is set.
+ public void promoteForegroundLayer(GameObject backgroundObject) {
+ backgroundObject.commitUpdates(); // Make sure layers are sorted.
+ final int componentCount = backgroundObject.getCount();
+ for (int x = componentCount - 1; x >= 0; x--) {
+ GameComponent component = (GameComponent)backgroundObject.get(x);
+ if (component instanceof RenderComponent) {
+ RenderComponent render = (RenderComponent)component;
+ if (render.getPriority() != SortConstants.OVERLAY) {
+ // found it.
+ render.setPriority(SortConstants.FOREGROUND);
+ break;
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/LevelSelectActivity.java b/src/com/replica/replicaisland/LevelSelectActivity.java
new file mode 100644
index 0000000..ba0c76a
--- /dev/null
+++ b/src/com/replica/replicaisland/LevelSelectActivity.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.app.ListActivity;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+public class LevelSelectActivity extends ListActivity {
+ private final static int UNLOCK_ALL_LEVELS_ID = 0;
+ private final static int UNLOCK_NEXT_LEVEL_ID = 1;
+ private final static LevelDataComparator sLevelComparator = new LevelDataComparator();
+ private ArrayList<LevelMetaData> mLevelData;
+ private Animation mButtonFlickerAnimation;
+ private boolean mLevelSelected;
+
+ private class LevelMetaData {
+ public LevelTree.Level level;
+ public int x;
+ public int y;
+ boolean enabled;
+
+ @Override
+ public String toString() {
+ return level.name;
+ }
+ }
+
+ private class DisableItemArrayAdapter<T> extends ArrayAdapter<T> {
+ private static final int TYPE_ENABLED = 0;
+ private static final int TYPE_DISABLED = 1;
+ private static final int TYPE_COMPLETED = 2;
+ private static final int TYPE_COUNT = 3;
+
+ private int mRowResource;
+ private int mDisabledRowResource;
+ private int mCompletedRowResource;
+ private Context mContext;
+ private int mTextViewResource;
+ private int mTextViewResource2;
+
+ public DisableItemArrayAdapter(Context context, int resource, int disabledResource, int completedResource,
+ int textViewResourceId, int textViewResourceId2, List<T> objects) {
+ super(context, resource, textViewResourceId, objects);
+ mRowResource = resource;
+ mDisabledRowResource = disabledResource;
+ mCompletedRowResource = completedResource;
+ mContext = context;
+ mTextViewResource = textViewResourceId;
+ mTextViewResource2 = textViewResourceId2;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ // TODO: do we have separators in this list?
+ return mLevelData.get(position).enabled;
+ }
+
+
+ @Override
+ public boolean areAllItemsEnabled() {
+ return false;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ int type = TYPE_ENABLED;
+ LevelMetaData level = mLevelData.get(position);
+ if (level != null) {
+ if (!level.enabled) {
+ if (level.level.completed) {
+ type = TYPE_COMPLETED;
+ } else {
+ type = TYPE_DISABLED;
+ }
+ }
+ }
+ return type;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return TYPE_COUNT;
+ }
+
+ @Override
+ public boolean hasStableIds() {
+ return true;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return mLevelData.size() > 0;
+ }
+
+ @Override
+ public View getView (int position, View convertView, ViewGroup parent) {
+ View sourceView = null;
+ if (mLevelData.get(position).enabled) {
+ if (convertView != null && convertView.getId() == mRowResource) {
+ sourceView = convertView;
+ } else {
+ sourceView = LayoutInflater.from(mContext).inflate(
+ mRowResource, parent, false);
+ }
+ } else if (mLevelData.get(position).level.completed) {
+ if (convertView != null && convertView.getId() == mCompletedRowResource) {
+ sourceView = convertView;
+ } else {
+ sourceView = LayoutInflater.from(mContext).inflate(
+ mCompletedRowResource, parent, false);
+ }
+ } else {
+ if (convertView != null && convertView.getId() == mDisabledRowResource) {
+ sourceView = convertView;
+ } else {
+ sourceView = LayoutInflater.from(mContext).inflate(
+ mDisabledRowResource, parent, false);
+ }
+ }
+ TextView view = (TextView)sourceView.findViewById(mTextViewResource);
+ if (view != null) {
+ view.setText(mLevelData.get(position).level.name);
+ }
+
+ TextView view2 = (TextView)sourceView.findViewById(mTextViewResource2);
+ if (view2 != null) {
+ view2.setText(mLevelData.get(position).level.timeStamp);
+ }
+ return sourceView;
+ }
+
+ }
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.level_select);
+ mLevelData = new ArrayList<LevelMetaData>();
+
+ generateLevelList(true);
+
+ DisableItemArrayAdapter<LevelMetaData> adapter = new DisableItemArrayAdapter<LevelMetaData>(
+ this, R.layout.level_select_row, R.layout.level_select_disabled_row, R.layout.level_select_completed_row,
+ R.id.title, R.id.time, mLevelData);
+
+ adapter.sort(sLevelComparator);
+
+ setListAdapter(adapter);
+
+ mButtonFlickerAnimation = AnimationUtils.loadAnimation(this, R.anim.button_flicker);
+
+ mLevelSelected = false;
+
+ // Keep the volume control type consistent across all activities.
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ }
+
+ protected void generateLevelList(boolean onlyAllowThePast) {
+ final int count = LevelTree.levels.size();
+ boolean oneBranchUnlocked = false;
+ for (int x = 0; x < count; x++) {
+ boolean anyUnlocksThisBranch = false;
+ final LevelTree.LevelGroup group = LevelTree.levels.get(x);
+ for (int y = 0; y < group.levels.size(); y++) {
+ LevelTree.Level level = group.levels.get(y);
+ boolean enabled = false;
+ if (!level.completed && !oneBranchUnlocked) {
+ enabled = true;
+ anyUnlocksThisBranch = true;
+ }
+ if (enabled || level.completed || !onlyAllowThePast || (onlyAllowThePast && level.inThePast)) {
+ addItem(level, x, y, enabled);
+ }
+ }
+ if (anyUnlocksThisBranch) {
+ oneBranchUnlocked = true;
+ }
+ }
+ }
+
+ protected void unlockNext() {
+ final int count = LevelTree.levels.size();
+ for (int x = 0; x < count; x++) {
+ final LevelTree.LevelGroup group = LevelTree.levels.get(x);
+ for (int y = 0; y < group.levels.size(); y++) {
+ LevelTree.Level level = group.levels.get(y);
+ if (!level.completed) {
+ level.completed = true;
+ return;
+ }
+ }
+
+ }
+ }
+
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id) {
+ if (!mLevelSelected) {
+ super.onListItemClick(l, v, position, id);
+ LevelMetaData selectedLevel = mLevelData.get(position);
+ if (selectedLevel.enabled) {
+ mLevelSelected = true;
+ Intent intent = new Intent();
+
+ intent.putExtra("resource", selectedLevel.level.resource);
+ intent.putExtra("row", selectedLevel.x);
+ intent.putExtra("index", selectedLevel.y);
+ TextView text = (TextView)v.findViewById(R.id.title);
+ if (text != null) {
+ text.startAnimation(mButtonFlickerAnimation);
+ mButtonFlickerAnimation.setAnimationListener(new EndActivityAfterAnimation(intent));
+ } else {
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+ }
+ }
+ }
+
+ private void addItem(LevelTree.Level level, int x, int y, boolean enabled) {
+ LevelMetaData data = new LevelMetaData();
+ data.level = level;
+ data.x = x;
+ data.y = y;
+ data.enabled = enabled;
+ mLevelData.add(data);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ boolean handled = false;
+ if (AndouKun.VERSION < 0) {
+ menu.add(0, UNLOCK_NEXT_LEVEL_ID, 0, R.string.unlock_next_level);
+ menu.add(0, UNLOCK_ALL_LEVELS_ID, 0, R.string.unlock_levels);
+
+ handled = true;
+ }
+ return handled;
+ }
+
+ @Override
+ public boolean onMenuItemSelected(int featureId, MenuItem item) {
+ switch(item.getItemId()) {
+ case UNLOCK_NEXT_LEVEL_ID:
+ unlockNext();
+ mLevelData.clear();
+ generateLevelList(false);
+ ((ArrayAdapter)getListAdapter()).sort(sLevelComparator);
+ ((ArrayAdapter)getListAdapter()).notifyDataSetChanged();
+
+ return true;
+
+ case UNLOCK_ALL_LEVELS_ID:
+ // Regenerate the level list to remove the past-only filter.
+ mLevelData.clear();
+ generateLevelList(false);
+ for (LevelMetaData level : mLevelData) {
+ level.enabled = true;
+ }
+
+ ((ArrayAdapter)getListAdapter()).sort(sLevelComparator);
+ ((ArrayAdapter)getListAdapter()).notifyDataSetChanged();
+ return true;
+ }
+
+ return super.onMenuItemSelected(featureId, item);
+ }
+
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ boolean result = false;
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ result = true;
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ boolean result = false;
+ if (keyCode == KeyEvent.KEYCODE_BACK) {
+ result = true;
+ }
+ return result;
+ }
+
+
+ /** Comparator for level meta data. */
+ private final static class LevelDataComparator implements Comparator<LevelMetaData> {
+ public int compare(final LevelMetaData object1, final LevelMetaData object2) {
+ int result = 0;
+ if (object1 == null && object2 != null) {
+ result = 1;
+ } else if (object1 != null && object2 == null) {
+ result = -1;
+ } else if (object1 != null && object2 != null) {
+ result = object1.level.timeStamp.compareTo(object2.level.timeStamp);
+ }
+ return result;
+ }
+ }
+
+ protected class EndActivityAfterAnimation implements Animation.AnimationListener {
+ private Intent mIntent;
+
+ EndActivityAfterAnimation(Intent intent) {
+ mIntent = intent;
+ }
+
+
+ public void onAnimationEnd(Animation animation) {
+ setResult(RESULT_OK, mIntent);
+ finish();
+ }
+
+ public void onAnimationRepeat(Animation animation) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void onAnimationStart(Animation animation) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+}
+
diff --git a/src/com/replica/replicaisland/LevelSystem.java b/src/com/replica/replicaisland/LevelSystem.java
new file mode 100644
index 0000000..d3be014
--- /dev/null
+++ b/src/com/replica/replicaisland/LevelSystem.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.content.res.AssetManager;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Manages information about the current level, including setup, deserialization, and tear-down.
+ */
+public class LevelSystem extends BaseObject {
+
+ public int mWidthInTiles;
+ public int mHeightInTiles;
+ public int mTileWidth;
+ public int mTileHeight;
+ public GameObject mBackgroundObject;
+ public ObjectManager mRoot;
+ private byte[] mWorkspaceBytes;
+ private TiledWorld mSpawnLocations;
+ private GameFlowEvent mGameFlowEvent;
+ private int mAttempts;
+ private LevelTree.Level mCurrentLevel;
+
+ public LevelSystem() {
+ super();
+ mWorkspaceBytes = new byte[4];
+ mGameFlowEvent = new GameFlowEvent();
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ if (mBackgroundObject != null && mRoot != null) {
+ mBackgroundObject.removeAll();
+ mBackgroundObject.commitUpdates();
+ mRoot.remove(mBackgroundObject);
+ mBackgroundObject = null;
+ mRoot = null;
+ }
+ mSpawnLocations = null;
+ mAttempts = 0;
+ mCurrentLevel = null;
+ }
+
+ public float getLevelWidth() {
+ return mWidthInTiles * mTileWidth;
+ }
+
+ public float getLevelHeight() {
+ return mHeightInTiles * mTileHeight;
+ }
+
+ public void sendRestartEvent() {
+ mGameFlowEvent.post(GameFlowEvent.EVENT_RESTART_LEVEL, 0,
+ sSystemRegistry.contextParameters.context);
+ }
+
+ public void sendNextLevelEvent() {
+ mGameFlowEvent.post(GameFlowEvent.EVENT_GO_TO_NEXT_LEVEL, 0,
+ sSystemRegistry.contextParameters.context);
+ }
+
+ public void sendGameEvent(int type, int index, boolean immediate) {
+ if (immediate) {
+ mGameFlowEvent.postImmediate(type, index,
+ sSystemRegistry.contextParameters.context);
+ } else {
+ mGameFlowEvent.post(type, index,
+ sSystemRegistry.contextParameters.context);
+ }
+ }
+
+ /**
+ * Loads a level from a binary file. The file consists of several layers, including background
+ * tile layers and at most one collision layer. Each layer is used to bootstrap related systems
+ * and provide them with layer data.
+ * @param stream The input stream for the level file resource.
+ * @param tiles A tile library to use when constructing tiled background layers.
+ * @param background An object to assign background layer rendering components to.
+ * @return
+ */
+ public boolean loadLevel(LevelTree.Level level, InputStream stream, ObjectManager root) {
+ boolean success = false;
+ mCurrentLevel = level;
+ AssetManager.AssetInputStream byteStream = (AssetManager.AssetInputStream) stream;
+ int signature;
+ try {
+ signature = (byte)byteStream.read();
+ if (signature == 96) {
+ final int layerCount = (byte)byteStream.read();
+ final int backgroundIndex = (byte)byteStream.read();
+
+ mRoot = root;
+ mTileWidth = 32;
+ mTileHeight = 32;
+
+ ContextParameters params = sSystemRegistry.contextParameters;
+ int currentPriority = SortConstants.BACKGROUND_START + 1;
+ for (int x = 0; x < layerCount; x++) {
+ final int type = (byte)byteStream.read();
+ final int tileIndex = (byte)byteStream.read();
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final float scrollSpeed = Utils.byteArrayToFloat(mWorkspaceBytes);
+
+ // TODO: use a pool here? Seems pointless.
+ TiledWorld world = new TiledWorld(byteStream);
+
+ if (type == 0) { // it's a background layer
+ assert mWidthInTiles != 0;
+ assert mTileWidth != 0;
+
+ // We require a collision layer to set up the tile sizes before we load.
+ // TODO: this really sucks. there's no reason each layer can't have its
+ // own tile widths and heights. Refactor this crap.
+ if (mWidthInTiles > 0 && mTileWidth > 0) {
+
+ LevelBuilder builder = sSystemRegistry.levelBuilder;
+
+ if (mBackgroundObject == null) {
+ mBackgroundObject =
+ builder.buildBackground(
+ backgroundIndex,
+ mWidthInTiles * mTileWidth,
+ mHeightInTiles * mTileHeight);
+ root.add(mBackgroundObject);
+ }
+
+
+ builder.addTileMapLayer(mBackgroundObject, currentPriority,
+ scrollSpeed, params.gameWidth, params.gameHeight,
+ mTileWidth, mTileHeight, world, tileIndex);
+
+
+ currentPriority++;
+ }
+
+ } else if (type == 1) { // collision
+ // Collision always defines the world boundaries.
+ mWidthInTiles = world.getWidth();
+ mHeightInTiles = world.getHeight();
+
+
+ CollisionSystem collision = sSystemRegistry.collisionSystem;
+ if (collision != null) {
+ collision.initialize(world, mTileWidth, mTileHeight);
+ }
+ } else if (type == 2) { // objects
+ mSpawnLocations = world;
+ spawnObjects();
+ } else if (type == 3) { // hot spots
+ HotSpotSystem hotSpots = sSystemRegistry.hotSpotSystem;
+ if (hotSpots != null) {
+ hotSpots.setWorld(world);
+ }
+
+ }
+ }
+
+ // hack!
+ sSystemRegistry.levelBuilder.promoteForegroundLayer(mBackgroundObject);
+
+ }
+
+ } catch (IOException e) {
+ //TODO: figure out the best way to deal with this. Assert?
+ }
+
+ return success;
+ }
+
+ public void spawnObjects() {
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ if (factory != null && mSpawnLocations != null) {
+ DebugLog.d("LevelSystem", "Spawning Objects!");
+
+ factory.spawnFromWorld(mSpawnLocations, mTileWidth, mTileHeight);
+ }
+ }
+
+ public void incrementAttemptsCount() {
+ mAttempts++;
+ }
+
+ public int getAttemptsCount() {
+ return mAttempts;
+ }
+
+ public LevelTree.Level getCurrentLevel() {
+ return mCurrentLevel;
+ }
+}
diff --git a/src/com/replica/replicaisland/LevelTree.java b/src/com/replica/replicaisland/LevelTree.java
new file mode 100644
index 0000000..00df0d7
--- /dev/null
+++ b/src/com/replica/replicaisland/LevelTree.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.ArrayList;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+
+public final class LevelTree {
+ public static class LevelGroup {
+ public ArrayList<Level> levels = new ArrayList<Level>();
+ }
+
+ public static class Level {
+ public int resource;
+ public DialogEntry dialogResources;
+ public String name;
+ public String timeStamp;
+ public boolean completed;
+ public boolean inThePast;
+ public boolean diaryCollected;
+ public boolean restartable;
+ public boolean showWaitMessage;
+
+ public Level(int level, DialogEntry dialogs, String title, String time,
+ boolean pastEvent, boolean restartOnDeath, boolean waitMessage) {
+ resource = level;
+ dialogResources = dialogs;
+ name = title;
+ timeStamp = time;
+ completed = false;
+ inThePast = pastEvent;
+ diaryCollected = false;
+ restartable = restartOnDeath;
+ showWaitMessage = waitMessage;
+ }
+
+ }
+
+ public static class DialogEntry {
+ public int diaryEntry = 0;
+ public int character1Entry = 0;
+ public int character2Entry = 0;
+ public ArrayList<ConversationUtils.Conversation> character1Conversations;
+ public ArrayList<ConversationUtils.Conversation> character2Conversations;
+ }
+ public final static ArrayList<LevelGroup> levels = new ArrayList<LevelGroup>();
+ private static boolean mLoaded = false;
+
+ public static final Level get(int row, int index) {
+ return levels.get(row).levels.get(index);
+ }
+
+ public static final boolean isLoaded() {
+ return mLoaded;
+ }
+
+ public static final void loadLevelTree(int resource, Context context) {
+ if (levels.size() > 0) {
+ // already loaded
+ return;
+ }
+
+ XmlResourceParser parser = context.getResources().getXml(resource);
+
+ levels.clear();
+
+ LevelGroup currentGroup = null;
+ Level currentLevel = null;
+ DialogEntry currentDialog = null;
+
+ try {
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if(eventType == XmlPullParser.START_TAG) {
+ if (parser.getName().equals("group")) {
+ currentGroup = new LevelGroup();
+ levels.add(currentGroup);
+ currentLevel = null;
+ currentDialog = null;
+ }
+
+ if (parser.getName().equals("level") && currentGroup != null) {
+ int levelResource = 0;
+ String titleString = null;
+ String timeStamp = null;
+ boolean inThePast = false;
+ boolean restartable = true;
+ boolean showWaitMessage = false;
+ for(int i=0; i < parser.getAttributeCount(); i++) {
+ if (parser.getAttributeName(i).equals("past")) {
+ if (parser.getAttributeValue(i).equals("true")) {
+ inThePast = true;
+ }
+ } else if (parser.getAttributeName(i).equals("restartable")) {
+ if (parser.getAttributeValue(i).equals("false")) {
+ restartable = false;
+ }
+ } else if (parser.getAttributeName(i).equals("waitmessage")) {
+ if (parser.getAttributeValue(i).equals("true")) {
+ showWaitMessage = true;
+ }
+ } else {
+ final int value = parser.getAttributeResourceValue(i, -1);
+ if (value != -1) {
+ if (parser.getAttributeName(i).equals("resource")) {
+ levelResource = value;
+ }
+ if (parser.getAttributeName(i).equals("title")) {
+ titleString = context.getString(value);
+ } else if (parser.getAttributeName(i).equals("time")) {
+ timeStamp = context.getString(value);
+ }
+ }
+ }
+
+ }
+ currentDialog = null;
+ currentLevel = new Level(levelResource, null, titleString, timeStamp, inThePast, restartable, showWaitMessage);
+ currentGroup.levels.add(currentLevel);
+ }
+
+ if (parser.getName().equals("dialog") && currentLevel != null) {
+ currentDialog = new DialogEntry();
+ currentLevel.dialogResources = currentDialog;
+ }
+
+ if (parser.getName().equals("diary") && currentDialog != null) {
+
+ for(int i=0; i < parser.getAttributeCount(); i++) {
+ final int value = parser.getAttributeResourceValue(i, -1);
+ if (value != -1) {
+ if (parser.getAttributeName(i).equals("resource")) {
+ currentDialog.diaryEntry = value;
+ }
+
+ }
+ }
+ }
+
+ if (parser.getName().equals("character1") && currentDialog != null) {
+ for(int i=0; i < parser.getAttributeCount(); i++) {
+ final int value = parser.getAttributeResourceValue(i, -1);
+ if (value != -1) {
+ if (parser.getAttributeName(i).equals("resource")) {
+ currentDialog.character1Entry = value;
+ }
+
+ }
+ }
+ }
+
+ if (parser.getName().equals("character2") && currentDialog != null) {
+
+ for(int i=0; i < parser.getAttributeCount(); i++) {
+ final int value = parser.getAttributeResourceValue(i, -1);
+ if (value != -1) {
+ if (parser.getAttributeName(i).equals("resource")) {
+ currentDialog.character2Entry = value;
+ }
+
+ }
+ }
+ }
+
+ }
+ eventType = parser.next();
+ }
+ } catch(Exception e) {
+ DebugLog.e("LevelTree", e.getStackTrace().toString());
+ } finally {
+ parser.close();
+ }
+ mLoaded = true;
+ }
+
+ public final static void loadAllDialog(Context context) {
+ final int levelGroupCount = levels.size();
+ for (int x = 0; x < levelGroupCount; x++) {
+ final ArrayList<Level> row = levels.get(x).levels;
+ final int levelCount = row.size();
+ for (int y = 0; y < levelCount; y++) {
+ final Level level = row.get(y);
+ if (level != null && level.dialogResources != null) {
+ DialogEntry dialog = level.dialogResources;
+ if (dialog.character1Entry != 0) {
+ dialog.character1Conversations = ConversationUtils.loadDialog(dialog.character1Entry, context);
+ }
+
+ if (dialog.character2Entry != 0) {
+ dialog.character2Conversations = ConversationUtils.loadDialog(dialog.character2Entry, context);
+ }
+ }
+ }
+
+ }
+ }
+
+ public final static void updateCompletedState(int levelRow, int completedLevels) {
+ final int rowCount = levels.size();
+ for (int x = 0; x < rowCount; x++) {
+ final LevelGroup group = levels.get(x);
+ final int levelCount = group.levels.size();
+ for (int y = 0; y < levelCount; y++) {
+ final Level level = group.levels.get(y);
+ if (x < levelRow) {
+ level.completed = true;
+ } else if (x == levelRow) {
+ if ((completedLevels & (1 << y)) != 0) {
+ level.completed = true;
+ }
+ } else {
+ level.completed = false;
+ }
+ }
+ }
+
+ }
+
+ public final static int packCompletedLevels(int levelRow) {
+ int completed = 0;
+ final LevelGroup group = levels.get(levelRow);
+ final int levelCount = group.levels.size();
+ for (int y = 0; y < levelCount; y++) {
+ final Level level = group.levels.get(y);
+ if (level.completed) {
+ completed |= 1 << y;
+ }
+ }
+ return completed;
+ }
+
+ public static boolean levelIsValid(int row, int index) {
+ boolean valid = false;
+ if (row >= 0 && row < levels.size()) {
+ final LevelGroup group = levels.get(row);
+ if (index >=0 && index < group.levels.size()) {
+ valid = true;
+ }
+ }
+
+ return valid;
+ }
+
+ public static boolean rowIsValid(int row) {
+ boolean valid = false;
+ if (row >= 0 && row < levels.size()) {
+ valid = true;
+ }
+
+ return valid;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/LifetimeComponent.java b/src/com/replica/replicaisland/LifetimeComponent.java
new file mode 100644
index 0000000..72f9aa0
--- /dev/null
+++ b/src/com/replica/replicaisland/LifetimeComponent.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.SoundSystem.Sound;
+
+/**
+ * This component allows objects to die and be deleted when their life is reduced to zero or they
+ * meet other configurable criteria.
+ */
+public class LifetimeComponent extends GameComponent {
+ private boolean mDieWhenInvisible;
+ private float mTimeUntilDeath;
+ private GameObjectFactory.GameObjectType mSpawnOnDeathType;
+ private LaunchProjectileComponent mTrackingSpawner;
+ private Vector2 mHotSpotTestPoint;
+ private boolean mReleaseGhostOnDeath;
+ private boolean mVulnerableToDeathTiles;
+ private boolean mDieOnHitBackground;
+ private Sound mDeathSound;
+
+ public LifetimeComponent() {
+ super();
+ mHotSpotTestPoint = new Vector2();
+ reset();
+ setPhase(ComponentPhases.THINK.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mDieWhenInvisible = false;
+ mTimeUntilDeath = -1;
+ mSpawnOnDeathType = GameObjectFactory.GameObjectType.INVALID;
+ mTrackingSpawner = null;
+ mHotSpotTestPoint.zero();
+ mReleaseGhostOnDeath = true;
+ mVulnerableToDeathTiles = false;
+ mDieOnHitBackground = false;
+ mDeathSound = null;
+ }
+
+ public void setDieWhenInvisible(boolean die) {
+ mDieWhenInvisible = die;
+ }
+
+ public void setTimeUntilDeath(float time) {
+ mTimeUntilDeath = time;
+ }
+
+ public void setObjectToSpawnOnDeath(GameObjectFactory.GameObjectType type) {
+ mSpawnOnDeathType = type;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject)parent;
+ if (mTimeUntilDeath > 0) {
+ mTimeUntilDeath -= timeDelta;
+ if (mTimeUntilDeath <= 0) {
+ die(parentObject);
+ return;
+ }
+ }
+
+ if (mDieWhenInvisible) {
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ ContextParameters context = sSystemRegistry.contextParameters;
+ final float dx =
+ Math.abs(parentObject.getPosition().x - camera.getFocusPositionX());
+ final float dy =
+ Math.abs(parentObject.getPosition().y - camera.getFocusPositionY());
+ if (dx > context.gameWidth || dy > context.gameHeight) {
+ // the position of this object is off the screen, destroy!
+ // TODO: this is a pretty dumb test. We should have a bounding volume instead.
+ die(parentObject);
+ return;
+ }
+ }
+
+ if (parentObject.life > 0 && mVulnerableToDeathTiles) {
+ HotSpotSystem hotSpot = sSystemRegistry.hotSpotSystem;
+ if (hotSpot != null) {
+ // TODO: HACK! Unify all this code.
+ if (hotSpot.getHotSpot(parentObject.getCenteredPositionX(),
+ parentObject.getPosition().y + 10.0f) == HotSpotSystem.HotSpotType.DIE) {
+ parentObject.life = 0;
+ }
+ }
+ }
+
+ if (parentObject.life > 0 && mDieOnHitBackground) {
+ if (parentObject.getBackgroundCollisionNormal().length2() > 0.0f) {
+ parentObject.life = 0;
+ }
+ }
+
+ if (parentObject.life <= 0) {
+ die(parentObject);
+ return;
+ }
+ }
+
+ private void die(GameObject parentObject) {
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+
+ if (mReleaseGhostOnDeath) {
+ // TODO: This is sort of a hack. Find a better way to do this without introducing a
+ // dependency between these two. Generic on-death event or something.
+ GhostComponent ghost = parentObject.findByClass(GhostComponent.class);
+ if (ghost != null) {
+ ghost.releaseControl(parentObject);
+ }
+ }
+
+ if (mSpawnOnDeathType != GameObjectFactory.GameObjectType.INVALID) {
+ GameObject object = factory.spawn(mSpawnOnDeathType, parentObject.getPosition().x,
+ parentObject.getPosition().y, parentObject.facingDirection.x < 0.0f);
+
+ if (object != null && manager != null) {
+ manager.add(object);
+ }
+
+ }
+
+ if (mTrackingSpawner != null) {
+ mTrackingSpawner.trackedProjectileDestroyed();
+ }
+
+ if (manager != null) {
+ manager.destroy(parentObject);
+ }
+
+ if (mDeathSound != null) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ if (sound != null) {
+ sound.play(mDeathSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+
+ }
+
+ public final void setTrackingSpawner(LaunchProjectileComponent spawner) {
+ mTrackingSpawner = spawner;
+ }
+
+ public final void setReleaseGhostOnDeath(boolean release) {
+ mReleaseGhostOnDeath = release;
+ }
+
+ public final void setVulnerableToDeathTiles(boolean vulnerable) {
+ mVulnerableToDeathTiles = vulnerable;
+ }
+
+ public final void setDieOnHitBackground(boolean die) {
+ mDieOnHitBackground = die;
+ }
+
+ public final void setDeathSound(Sound deathSound) {
+ mDeathSound = deathSound;
+ }
+}
diff --git a/src/com/replica/replicaisland/MainLoop.java b/src/com/replica/replicaisland/MainLoop.java
new file mode 100644
index 0000000..ad31b5c
--- /dev/null
+++ b/src/com/replica/replicaisland/MainLoop.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Main game loop. Updates the time system and passes the result down to the rest of the game
+ * graph. This object is effectively the root of the game graph.
+ */
+public class MainLoop extends ObjectManager {
+
+ // Ensures that time updates before everything else.
+ public MainLoop() {
+ super();
+ mTimeSystem = new TimeSystem();
+ sSystemRegistry.timeSystem = mTimeSystem;
+ sSystemRegistry.registerForReset(mTimeSystem);
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ mTimeSystem.update(timeDelta, parent);
+ final float newTimeDelta = mTimeSystem.getFrameDelta(); // The time system may warp time.
+ super.update(newTimeDelta, parent);
+ }
+
+ private TimeSystem mTimeSystem;
+}
diff --git a/src/com/replica/replicaisland/MainMenuActivity.java b/src/com/replica/replicaisland/MainMenuActivity.java
new file mode 100644
index 0000000..6ef6289
--- /dev/null
+++ b/src/com/replica/replicaisland/MainMenuActivity.java
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.ImageView;
+
+public class MainMenuActivity extends Activity {
+ private boolean mPaused;
+ private View mStartButton;
+ private View mOptionsButton;
+ private View mBackground;
+ private Animation mButtonFlickerAnimation;
+ private Animation mFadeOutAnimation;
+ private Animation mAlternateFadeOutAnimation;
+
+ private final static int WHATS_NEW_DIALOG = 0;
+
+ // Create an anonymous implementation of OnClickListener
+ private View.OnClickListener sStartButtonListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ if (!mPaused) {
+ Intent i = new Intent(getBaseContext(), AndouKun.class);
+
+ v.startAnimation(mButtonFlickerAnimation);
+ mFadeOutAnimation.setAnimationListener(new StartActivityAfterAnimation(i));
+ mBackground.startAnimation(mFadeOutAnimation);
+ mOptionsButton.startAnimation(mAlternateFadeOutAnimation);
+ mPaused = true;
+ }
+ }
+ };
+
+ private View.OnClickListener sOptionButtonListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ if (!mPaused) {
+ Intent i = new Intent(getBaseContext(), SetPreferencesActivity.class);
+
+ v.startAnimation(mButtonFlickerAnimation);
+ mFadeOutAnimation.setAnimationListener(new StartActivityAfterAnimation(i));
+ mBackground.startAnimation(mFadeOutAnimation);
+ mStartButton.startAnimation(mAlternateFadeOutAnimation);
+ mPaused = true;
+ }
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.mainmenu);
+ mPaused = true;
+
+ mStartButton = findViewById(R.id.startButton);
+ mOptionsButton = findViewById(R.id.optionButton);
+ mBackground = findViewById(R.id.mainMenuBackground);
+
+ if (mStartButton != null) {
+ mStartButton.setOnClickListener(sStartButtonListener);
+ }
+
+ if (mOptionsButton != null) {
+ mOptionsButton.setOnClickListener(sOptionButtonListener);
+ }
+
+
+ mButtonFlickerAnimation = AnimationUtils.loadAnimation(this, R.anim.button_flicker);
+ mFadeOutAnimation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
+ mAlternateFadeOutAnimation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
+
+ if (!LevelTree.isLoaded()) {
+ LevelTree.loadLevelTree(R.xml.level_tree, this);
+ LevelTree.loadAllDialog(this);
+ }
+
+ // Keep the volume control type consistent across all activities.
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+ //MediaPlayer mp = MediaPlayer.create(this, R.raw.bwv_115);
+ //mp.start();
+
+ }
+
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mPaused = true;
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mPaused = false;
+
+
+ if (mStartButton != null) {
+ mStartButton.setVisibility(View.VISIBLE);
+ mStartButton.clearAnimation();
+ mStartButton.startAnimation(AnimationUtils.loadAnimation(this, R.anim.button_slide));
+
+ // Change "start" to "continue" if there's a saved game.
+ SharedPreferences prefs = getSharedPreferences(AndouKun.PREFERENCE_NAME, MODE_PRIVATE);
+ final int row = prefs.getInt(AndouKun.PREFERENCE_LEVEL_ROW, 0);
+ final int index = prefs.getInt(AndouKun.PREFERENCE_LEVEL_INDEX, 0);
+ if (row != 0 || index != 0) {
+ ((ImageView)mStartButton).setImageDrawable(getResources().getDrawable(R.drawable.ui_button_continue));
+ } else {
+ ((ImageView)mStartButton).setImageDrawable(getResources().getDrawable(R.drawable.ui_button_start));
+ }
+
+ final int lastVersion = prefs.getInt(AndouKun.PREFERENCE_LAST_VERSION, 0);
+ if (lastVersion == 0) {
+ // This is the first time the game has been run.
+ // Pre-configure the control options to match the device.
+ // The resource system can tell us what this device has.
+ // TODO: is there a better way to do this? Seems like a kind of neat
+ // way to do custom device profiles.
+ final String navType = getString(R.string.nav_type);
+ if (navType != null) {
+ if (navType.equalsIgnoreCase("DPad")) {
+ // Turn off the click-to-attack pref on devices that have a dpad.
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(AndouKun.PREFERENCE_CLICK_ATTACK, false);
+ editor.commit();
+ } else if (navType.equalsIgnoreCase("None")) {
+ // Turn on tilt controls if there's nothing else.
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(AndouKun.PREFERENCE_TILT_CONTROLS, true);
+ editor.commit();
+ }
+ }
+ }
+ if (Math.abs(lastVersion) < Math.abs(AndouKun.VERSION)) {
+ // show what's new message
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(AndouKun.PREFERENCE_LAST_VERSION, AndouKun.VERSION);
+ editor.commit();
+
+ showDialog(WHATS_NEW_DIALOG);
+
+ }
+
+ }
+
+ if (mOptionsButton != null) {
+ mOptionsButton.setVisibility(View.VISIBLE);
+ mOptionsButton.clearAnimation();
+ Animation anim = AnimationUtils.loadAnimation(this, R.anim.button_slide);
+ anim.setStartOffset(200L);
+ mOptionsButton.startAnimation(anim);
+ }
+
+ if (mBackground != null) {
+ mBackground.clearAnimation();
+ }
+
+
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ Dialog dialog;
+ if (id == WHATS_NEW_DIALOG) {
+ dialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.whats_new_dialog_title)
+ .setPositiveButton(R.string.whats_new_dialog_ok, null)
+ .setMessage(R.string.whats_new_dialog_message)
+ .create();
+ } else {
+ dialog = super.onCreateDialog(id);
+ }
+ return dialog;
+ }
+
+ protected class StartActivityAfterAnimation implements Animation.AnimationListener {
+ private Intent mIntent;
+
+ StartActivityAfterAnimation(Intent intent) {
+ mIntent = intent;
+ }
+
+
+ public void onAnimationEnd(Animation animation) {
+ mStartButton.setVisibility(View.INVISIBLE);
+ mStartButton.clearAnimation();
+ mOptionsButton.setVisibility(View.INVISIBLE);
+ mOptionsButton.clearAnimation();
+ startActivity(mIntent);
+ }
+
+ public void onAnimationRepeat(Animation animation) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void onAnimationStart(Animation animation) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+}
diff --git a/src/com/replica/replicaisland/MotionBlurComponent.java b/src/com/replica/replicaisland/MotionBlurComponent.java
new file mode 100644
index 0000000..556002b
--- /dev/null
+++ b/src/com/replica/replicaisland/MotionBlurComponent.java
@@ -0,0 +1,94 @@
+package com.replica.replicaisland;
+
+public class MotionBlurComponent extends GameComponent {
+ private static final int STEP_COUNT = 4;
+ private static final float STEP_DELAY = 0.1f;
+ private static final float OPACITY_STEP = 1.0f / (STEP_COUNT + 1);
+ private BlurRecord[] mHistory;
+ private RenderComponent mBlurTarget;
+ private float mStepDelay;
+ private int mCurrentStep;
+ private float mTimeSinceLastStep;
+ private int mTargetPriority;
+
+ private class BlurRecord {
+ public Vector2 position = new Vector2();
+ public Texture texture;
+ public int width;
+ public int height;
+ public int[] crop = new int[4];
+ }
+ public MotionBlurComponent() {
+ super();
+ mHistory = new BlurRecord[STEP_COUNT];
+ for (int x = 0; x < STEP_COUNT; x++) {
+ mHistory[x] = new BlurRecord();
+ }
+ reset();
+ setPhase(ComponentPhases.PRE_DRAW.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ for (int x = 0; x < STEP_COUNT; x++) {
+ mHistory[x].texture = null;
+ mHistory[x].position.zero();
+ }
+ mStepDelay = STEP_DELAY;
+ mBlurTarget = null;
+ mCurrentStep = 0;
+ mTimeSinceLastStep = 0.0f;
+ }
+
+ public void setTarget(RenderComponent target) {
+ mBlurTarget = target;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mBlurTarget != null) {
+ mTimeSinceLastStep += timeDelta;
+ if (mTimeSinceLastStep > mStepDelay) {
+ DrawableBitmap drawable = (DrawableBitmap)mBlurTarget.getDrawable();
+ if (drawable != null) {
+ Texture currentTexture = drawable.getTexture();
+ mTargetPriority = mBlurTarget.getPriority();
+ mHistory[mCurrentStep].texture = currentTexture;
+ mHistory[mCurrentStep].position.set(((GameObject)parent).getPosition());
+ mHistory[mCurrentStep].width = drawable.getWidth();
+ mHistory[mCurrentStep].height = drawable.getHeight();
+ final int[] drawableCrop = drawable.getCrop();
+ mHistory[mCurrentStep].crop[0] = drawableCrop[0];
+ mHistory[mCurrentStep].crop[1] = drawableCrop[1];
+ mHistory[mCurrentStep].crop[2] = drawableCrop[2];
+ mHistory[mCurrentStep].crop[3] = drawableCrop[3];
+ mCurrentStep = (mCurrentStep + 1) % STEP_COUNT;
+ mTimeSinceLastStep = 0.0f;
+ }
+ }
+
+
+ RenderSystem renderer = sSystemRegistry.renderSystem;
+
+
+ final int startStep = mCurrentStep > 0 ? mCurrentStep - 1 : STEP_COUNT - 1;
+ // draw each step
+ for (int x = 0; x < STEP_COUNT; x++) {
+ final int step = (startStep - x) < 0 ? (STEP_COUNT + (startStep - x)) : (startStep - x);
+ final BlurRecord record = mHistory[step];
+ if (record.texture != null) {
+ DrawableBitmap stepImage = sSystemRegistry.drawableFactory.allocateDrawableBitmap();
+ stepImage.setTexture(record.texture);
+ stepImage.setWidth(record.width);
+ stepImage.setHeight(record.height);
+ stepImage.setCrop(record.crop[0], record.crop[1], record.crop[2], -record.crop[3]);
+ final float opacity = (STEP_COUNT - x) * OPACITY_STEP;
+ stepImage.setOpacity(opacity);
+
+
+ renderer.scheduleForDraw(stepImage, record.position, mTargetPriority - (x + 1), true);
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/MovementComponent.java b/src/com/replica/replicaisland/MovementComponent.java
new file mode 100644
index 0000000..11fecac
--- /dev/null
+++ b/src/com/replica/replicaisland/MovementComponent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+
+/**
+ * A game component that implements velocity-based movement.
+ */
+public class MovementComponent extends GameComponent {
+ // If multiple game components were ever running in different threads, this would need
+ // to be non-static.
+ private static Interpolator sInterpolator = new Interpolator();
+
+ public MovementComponent() {
+ super();
+ setPhase(ComponentPhases.MOVEMENT.ordinal());
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject object = (GameObject) parent;
+
+ sInterpolator.set(object.getVelocity().x, object.getTargetVelocity().x,
+ object.getAcceleration().x);
+ float offsetX = sInterpolator.interpolate(timeDelta);
+ float newX = object.getPosition().x + offsetX;
+ float newVelocityX = sInterpolator.getCurrent();
+
+ sInterpolator.set(object.getVelocity().y, object.getTargetVelocity().y,
+ object.getAcceleration().y);
+ float offsetY = sInterpolator.interpolate(timeDelta);
+ float newY = object.getPosition().y + offsetY;
+ float newVelocityY = sInterpolator.getCurrent();
+
+ if (object.positionLocked == false) {
+ object.getPosition().set(newX, newY);
+ }
+
+ object.getVelocity().set(newVelocityX, newVelocityY);
+ }
+
+}
diff --git a/src/com/replica/replicaisland/NPCAnimationComponent.java b/src/com/replica/replicaisland/NPCAnimationComponent.java
new file mode 100644
index 0000000..3ee1e46
--- /dev/null
+++ b/src/com/replica/replicaisland/NPCAnimationComponent.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.ChannelSystem.Channel;
+import com.replica.replicaisland.GameObject.ActionType;
+
+public class NPCAnimationComponent extends GameComponent {
+
+ // Animations
+ public static final int IDLE = 0;
+ public static final int WALK = 1;
+ public static final int RUN_START = 2;
+ public static final int RUN = 3;
+ public static final int SHOOT = 4;
+ public static final int JUMP_START = 5;
+ public static final int JUMP_AIR = 6;
+ public static final int TAKE_HIT = 7;
+ public static final int SURPRISED = 8;
+ public static final int DEATH = 9;
+
+
+ protected static final float RUN_SPEED_THRESHOLD = 100.0f;
+ protected static final float JUMP_SPEED_THRESHOLD = 25.0f;
+ protected static final float FALL_SPEED_THRESHOLD = -25.0f;
+ protected static final float FALL_TIME_THRESHOLD = 0.2f;
+
+ private int mCurrentAnimation;
+ private SpriteComponent mSprite;
+ private ChannelSystem.Channel mChannel;
+ private int mChannelTrigger;
+ private boolean mFlying;
+ private boolean mStopAtWalls; // Controls whether or not the character will go back
+ // to idle when running into a wall
+
+
+ public NPCAnimationComponent() {
+ super();
+ reset();
+ setPhase(GameComponent.ComponentPhases.ANIMATION.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mCurrentAnimation = IDLE;
+ mChannel = null;
+ mSprite = null;
+ mFlying = false;
+ mStopAtWalls = true;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mSprite != null) {
+ GameObject parentObject = (GameObject)parent;
+
+ final int oldAnimation = mCurrentAnimation;
+ switch(mCurrentAnimation) {
+ case IDLE:
+ idle(parentObject);
+ break;
+ case WALK:
+ walk(parentObject);
+ break;
+ case RUN_START:
+ runStart(parentObject);
+ break;
+ case RUN:
+ run(parentObject);
+ break;
+ case SHOOT:
+ shoot(parentObject);
+ break;
+ case JUMP_START:
+ jumpStart(parentObject);
+ break;
+ case JUMP_AIR:
+ jumpAir(parentObject);
+ break;
+ case TAKE_HIT:
+ takeHit(parentObject);
+ break;
+ case SURPRISED:
+ surprised(parentObject);
+ break;
+ case DEATH:
+ death(parentObject);
+ break;
+ default:
+ assert(false);
+ }
+
+ if (mChannel != null) {
+ if (mChannel.value != null
+ && ((ChannelSystem.ChannelBooleanValue)mChannel.value).value) {
+ mCurrentAnimation = mChannelTrigger;
+ }
+ }
+
+ if (oldAnimation != mCurrentAnimation) {
+ mSprite.playAnimation(mCurrentAnimation);
+ }
+ }
+ }
+
+ protected boolean shouldFall(GameObject parentObject) {
+ boolean result = false;
+ TimeSystem time = sSystemRegistry.timeSystem;
+ final float airTime = time.getGameTime() - parentObject.getLastTouchedFloorTime();
+ if (!mFlying && !parentObject.touchingGround() && airTime > FALL_TIME_THRESHOLD) {
+ final Vector2 velocity = parentObject.getVelocity();
+ if (velocity.y < FALL_SPEED_THRESHOLD) {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ protected boolean shouldJump(GameObject parentObject) {
+ boolean result = false;
+
+ if (!mFlying) {
+ final Vector2 velocity = parentObject.getVelocity();
+ if (velocity.y > JUMP_SPEED_THRESHOLD) {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ protected boolean shouldRun(GameObject parentObject) {
+ boolean result = false;
+ if (!mFlying && parentObject.touchingGround()) {
+ final Vector2 velocity = parentObject.getVelocity();
+ if (Math.abs(velocity.x) >= RUN_SPEED_THRESHOLD) {
+ result = true;
+ }
+ }
+ return result;
+ }
+
+ protected boolean shouldMove(GameObject parentObject) {
+ boolean result = true;
+ final Vector2 velocity = parentObject.getVelocity();
+
+ if (mStopAtWalls) {
+ if ((velocity.x < 0.0f && parentObject.touchingLeftWall())
+ || (velocity.x > 0.0f && parentObject.touchingRightWall())) {
+ result = false;
+ }
+ }
+ return result;
+ }
+
+ protected boolean shouldTakeHit(GameObject parentObject) {
+ boolean result = false;
+ if (parentObject.getCurrentAction() == ActionType.HIT_REACT
+ && mSprite.findAnimation(TAKE_HIT) != null) {
+ result = true;
+ }
+ return result;
+ }
+
+ protected void gotoRunStart() {
+ if (mSprite.findAnimation(RUN_START) != null) {
+ mCurrentAnimation = RUN_START;
+ } else {
+ mCurrentAnimation = RUN;
+ }
+ }
+
+ protected void gotoRun() {
+ mCurrentAnimation = RUN;
+ }
+
+ protected void idle(GameObject parentObject) {
+ final GameObject.ActionType currentAction = parentObject.getCurrentAction();
+ if (currentAction == ActionType.MOVE) {
+ final Vector2 velocity = parentObject.getVelocity();
+ if (shouldFall(parentObject)) {
+ mCurrentAnimation = JUMP_AIR;
+ } else if (shouldJump(parentObject)) {
+ mCurrentAnimation = JUMP_START;
+ parentObject.positionLocked = true;
+ } else if (Math.abs(velocity.x) > 0.0f && shouldMove(parentObject)) {
+ if (shouldRun(parentObject)) {
+ gotoRunStart();
+ parentObject.positionLocked = true;
+ } else {
+ mCurrentAnimation = WALK;
+ }
+ }
+ } else if (currentAction == ActionType.ATTACK) {
+ mCurrentAnimation = SHOOT;
+ } else if (shouldTakeHit(parentObject)) {
+ mCurrentAnimation = TAKE_HIT;
+ } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
+ mCurrentAnimation = DEATH;
+ }
+ }
+
+ protected void walk(GameObject parentObject) {
+ final GameObject.ActionType currentAction = parentObject.getCurrentAction();
+ if (currentAction == ActionType.MOVE) {
+ final Vector2 velocity = parentObject.getVelocity();
+ if (shouldFall(parentObject)) {
+ mCurrentAnimation = JUMP_AIR;
+ } else if (shouldJump(parentObject)) {
+ mCurrentAnimation = JUMP_START;
+ parentObject.positionLocked = true;
+ } else if (Math.abs(velocity.x) > 0.0f) {
+ if (shouldRun(parentObject)) {
+ gotoRun();
+ }
+ if (velocity.x > 0.0f) {
+ parentObject.facingDirection.x = 1;
+ } else {
+ parentObject.facingDirection.x = -1;
+ }
+ } else {
+ mCurrentAnimation = IDLE;
+ }
+ } else if (currentAction == ActionType.ATTACK) {
+ mCurrentAnimation = SHOOT;
+ } else if (shouldTakeHit(parentObject)) {
+ mCurrentAnimation = TAKE_HIT;
+ } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
+ mCurrentAnimation = DEATH;
+ }
+ }
+
+ protected void runStart(GameObject parentObject) {
+ parentObject.positionLocked = true;
+ if (mSprite.animationFinished()) {
+ mCurrentAnimation = RUN;
+ parentObject.positionLocked = false;
+ }
+ }
+ protected void run(GameObject parentObject) {
+ final GameObject.ActionType currentAction = parentObject.getCurrentAction();
+ if (currentAction == ActionType.MOVE) {
+ final Vector2 velocity = parentObject.getVelocity();
+ if (shouldFall(parentObject)) {
+ mCurrentAnimation = JUMP_AIR;
+ } else if (shouldJump(parentObject)) {
+ parentObject.positionLocked = true;
+ mCurrentAnimation = JUMP_START;
+ } else if (Math.abs(velocity.x) > 0.0f) {
+ if (!shouldRun(parentObject)) {
+ mCurrentAnimation = WALK;
+ }
+
+ if (velocity.x > 0.0f) {
+ parentObject.facingDirection.x = 1;
+ } else {
+ parentObject.facingDirection.x = -1;
+ }
+ } else {
+ mCurrentAnimation = IDLE;
+ }
+ } else if (currentAction == ActionType.ATTACK) {
+ mCurrentAnimation = SHOOT;
+ } else if (shouldTakeHit(parentObject)) {
+ mCurrentAnimation = TAKE_HIT;
+ } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
+ mCurrentAnimation = DEATH;
+ }
+ }
+
+ protected void shoot(GameObject parentObject) {
+ if (mSprite.animationFinished() || parentObject.getCurrentAction() != ActionType.ATTACK) {
+ mCurrentAnimation = IDLE;
+ } else if (shouldTakeHit(parentObject)) {
+ mCurrentAnimation = TAKE_HIT;
+ } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
+ mCurrentAnimation = DEATH;
+ } else {
+ final Vector2 velocity = parentObject.getVelocity();
+
+ if (velocity.x > 0.0f) {
+ parentObject.facingDirection.x = 1;
+ } else if (velocity.x < 0.0f) {
+ parentObject.facingDirection.x = -1;
+ }
+ }
+ }
+
+ protected void jumpStart(GameObject parentObject) {
+ final Vector2 velocity = parentObject.getVelocity();
+
+ if (velocity.x > 0.0f) {
+ parentObject.facingDirection.x = 1;
+ } else if (velocity.x < 0.0f) {
+ parentObject.facingDirection.x = -1;
+ }
+ parentObject.positionLocked = true;
+
+ if (mSprite.animationFinished()) {
+ mCurrentAnimation = JUMP_AIR;
+ parentObject.positionLocked = false;
+ }
+ }
+
+ protected void jumpAir(GameObject parentObject) {
+ final GameObject.ActionType currentAction = parentObject.getCurrentAction();
+ if (currentAction == ActionType.MOVE) {
+ final Vector2 velocity = parentObject.getVelocity();
+
+ if (parentObject.touchingGround()) {
+ if (Math.abs(velocity.x) > 0.0f) {
+ if (shouldRun(parentObject)) {
+ mCurrentAnimation = RUN;
+ } else {
+ mCurrentAnimation = WALK;
+ }
+ } else {
+ mCurrentAnimation = IDLE;
+ }
+ } else {
+
+ if (velocity.x > 0.0f) {
+ parentObject.facingDirection.x = 1;
+ } else if (velocity.x < 0.0f) {
+ parentObject.facingDirection.x = -1;
+ }
+
+ }
+ } else {
+ mCurrentAnimation = IDLE;
+ }
+ }
+
+ protected void takeHit(GameObject parentObject) {
+ if (mSprite.animationFinished()) {
+ if (parentObject.life > 0 && parentObject.getCurrentAction() != ActionType.DEATH) {
+ if (parentObject.getCurrentAction() != ActionType.HIT_REACT) {
+ mCurrentAnimation = IDLE;
+ }
+ } else {
+ mCurrentAnimation = DEATH;
+ }
+ }
+ }
+
+ protected void surprised(GameObject parentObject) {
+ if (mSprite.animationFinished()) {
+ mCurrentAnimation = IDLE;
+ }
+ }
+
+ protected void death(GameObject parentObject) {
+ }
+
+ public void setSprite(SpriteComponent sprite) {
+ mSprite = sprite;
+ }
+
+ public void setChannel(Channel channel) {
+ mChannel = channel;
+ }
+
+ public void setChannelTrigger(int animation) {
+ mChannelTrigger = animation;
+ }
+
+ public void setFlying(boolean flying) {
+ mFlying = flying;
+ }
+
+ public void setStopAtWalls(boolean stop) {
+ mStopAtWalls = stop;
+ }
+}
diff --git a/src/com/replica/replicaisland/NPCComponent.java b/src/com/replica/replicaisland/NPCComponent.java
new file mode 100644
index 0000000..b5a9592
--- /dev/null
+++ b/src/com/replica/replicaisland/NPCComponent.java
@@ -0,0 +1,491 @@
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.CollisionParameters.HitType;
+import com.replica.replicaisland.GameObject.ActionType;
+
+public class NPCComponent extends GameComponent {
+ private float mPauseTime;
+ private float mTargetXVelocity;
+ private int mLastHitTileX;
+ private int mLastHitTileY;
+
+ private int mDialogEvent;
+ private int mDialogIndex;
+
+ private HitReactionComponent mHitReactComponent;
+
+ private int[] mQueuedCommands;
+ private int mQueueTop;
+ private int mQueueBottom;
+ private boolean mExecutingQueue;
+
+ private Vector2 mPreviousPosition;
+
+ private float mUpImpulse;
+ private float mDownImpulse;
+ private float mHorizontalImpulse;
+ private float mSlowHorizontalImpulse;
+ private float mAcceleration;
+
+ private int mGameEvent;
+ private int mGameEventIndex;
+ private boolean mSpawnGameEventOnDeath;
+
+ private boolean mReactToHits;
+ private boolean mFlying;
+ private boolean mPauseOnAttack;
+
+ private float mDeathTime;
+ private float mDeathFadeDelay;
+
+ private static final float UP_IMPULSE = 400.0f;
+ private static final float DOWN_IMPULSE = -10.0f;
+ private static final float HORIZONTAL_IMPULSE = 200.0f;
+ private static final float SLOW_HORIZONTAL_IMPULSE = 50.0f;
+ private static final float ACCELERATION = 300.0f;
+ private static final float HIT_IMPULSE = 300.0f;
+ private static final float HIT_ACCELERATION = 700.0f;
+
+ private static final float DEATH_FADE_DELAY = 4.0f;
+
+ private static final float PAUSE_TIME_SHORT = 1.0f;
+ private static final float PAUSE_TIME_MEDIUM = 4.0f;
+ private static final float PAUSE_TIME_LONG = 8.0f;
+ private static final float PAUSE_TIME_ATTACK = 1.0f;
+ private static final float PAUSE_TIME_HIT_REACT = 1.0f;
+
+ private static final int COMMAND_QUEUE_SIZE = 16;
+
+ public NPCComponent() {
+ super();
+ setPhase(ComponentPhases.THINK.ordinal());
+ mQueuedCommands = new int[COMMAND_QUEUE_SIZE];
+ mPreviousPosition = new Vector2();
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mPauseTime = 0.0f;
+ mTargetXVelocity = 0.0f;
+ mLastHitTileX = 0;
+ mLastHitTileY = 0;
+ mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER1;
+ mDialogIndex = 0;
+ mHitReactComponent = null;
+ mQueueTop = 0;
+ mQueueBottom = 0;
+ mPreviousPosition.zero();
+ mExecutingQueue = false;
+ mUpImpulse = UP_IMPULSE;
+ mDownImpulse = DOWN_IMPULSE;
+ mHorizontalImpulse = HORIZONTAL_IMPULSE;
+ mSlowHorizontalImpulse = SLOW_HORIZONTAL_IMPULSE;
+ mAcceleration = ACCELERATION;
+ mGameEvent = -1;
+ mGameEventIndex = -1;
+ mSpawnGameEventOnDeath = false;
+ mReactToHits = false;
+ mFlying = false;
+ mDeathTime = 0.0f;
+ mDeathFadeDelay = DEATH_FADE_DELAY;
+ mPauseOnAttack = true;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+
+ GameObject parentObject = (GameObject)parent;
+
+ if (mReactToHits &&
+ mPauseTime <= 0.0f &&
+ parentObject.getCurrentAction() == ActionType.HIT_REACT) {
+ mPauseTime = PAUSE_TIME_HIT_REACT;
+ pauseMovement(parentObject);
+ parentObject.getVelocity().x = -parentObject.facingDirection.x * HIT_IMPULSE;
+ parentObject.getAcceleration().x = HIT_ACCELERATION;
+
+ } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
+ if (mSpawnGameEventOnDeath && mGameEvent != -1) {
+ if (Utils.close(parentObject.getVelocity().x, 0.0f)
+ && parentObject.touchingGround()) {
+
+ if (mDeathTime < mDeathFadeDelay && mDeathTime + timeDelta >= mDeathFadeDelay) {
+ HudSystem hud = sSystemRegistry.hudSystem;
+
+ if (hud != null) {
+ hud.startFade(false, 1.5f);
+ hud.sendGameEventOnFadeComplete(mGameEvent, mGameEventIndex);
+ mGameEvent = -1;
+ }
+ }
+ mDeathTime += timeDelta;
+
+ }
+ }
+ // nothing else to do.
+ return;
+ } else if (parentObject.life <= 0) {
+ parentObject.setCurrentAction(ActionType.DEATH);
+ parentObject.getTargetVelocity().x = 0;
+ return;
+ } else if (parentObject.getCurrentAction() == ActionType.INVALID ||
+ (!mReactToHits && parentObject.getCurrentAction() == ActionType.HIT_REACT)) {
+ parentObject.setCurrentAction(ActionType.MOVE);
+ }
+
+ if (mPauseTime <= 0.0f) {
+
+ HotSpotSystem hotSpotSystem = sSystemRegistry.hotSpotSystem;
+
+ if (hotSpotSystem != null) {
+ final float centerX = parentObject.getCenteredPositionX();
+ final int hitTileX = hotSpotSystem.getHitTileX(centerX);
+ final int hitTileY = hotSpotSystem.getHitTileY(parentObject.getPosition().y + 10.0f);
+ boolean accepted = true;
+
+ if (hitTileX != mLastHitTileX || hitTileY != mLastHitTileY) {
+
+ final int hotSpot = hotSpotSystem.getHotSpotByTile(hitTileX, hitTileY);
+
+ if (hotSpot >= HotSpotSystem.HotSpotType.NPC_GO_RIGHT && hotSpot <= HotSpotSystem.HotSpotType.NPC_SLOW) {
+ // movement-related commands are immediate
+ parentObject.setCurrentAction(ActionType.MOVE);
+ accepted = executeCommand(hotSpot, parentObject, timeDelta);
+ } else if (hotSpot == HotSpotSystem.HotSpotType.ATTACK && !mPauseOnAttack) {
+ // when mPauseOnAttack is false, attacks are also immediate.
+ accepted = executeCommand(hotSpot, parentObject, timeDelta);
+ } else if (hotSpot == HotSpotSystem.HotSpotType.NPC_RUN_QUEUED_COMMANDS) {
+ if (!mExecutingQueue && mQueueTop != mQueueBottom) {
+ mExecutingQueue = true;
+ }
+ } else if (hotSpot > HotSpotSystem.HotSpotType.NONE) {
+ queueCommand(hotSpot);
+ }
+ }
+
+ if (mExecutingQueue) {
+ if (mQueueTop != mQueueBottom) {
+ accepted = executeCommand(nextCommand(), parentObject, timeDelta);
+ if (accepted) {
+ advanceQueue();
+ }
+ } else {
+ mExecutingQueue = false;
+ }
+ }
+
+ if (accepted) {
+ mLastHitTileX = hitTileX;
+ mLastHitTileY = hitTileY;
+ }
+
+ }
+ } else {
+ mPauseTime -= timeDelta;
+ if (mPauseTime < 0.0f) {
+ resumeMovement(parentObject);
+ mPauseTime = 0.0f;
+ parentObject.setCurrentAction(ActionType.MOVE);
+ }
+ }
+
+ mPreviousPosition.set(parentObject.getPosition());
+ }
+
+ private boolean executeCommand(int hotSpot, GameObject parentObject, float timeDelta) {
+ boolean hitAccepted = true;
+ final CameraSystem camera = sSystemRegistry.cameraSystem;
+
+ switch(hotSpot) {
+ case HotSpotSystem.HotSpotType.WAIT_SHORT:
+ if (mPauseTime == 0.0f) {
+ mPauseTime = PAUSE_TIME_SHORT;
+ pauseMovement(parentObject);
+ }
+ break;
+ case HotSpotSystem.HotSpotType.WAIT_MEDIUM:
+ if (mPauseTime == 0.0f) {
+ mPauseTime = PAUSE_TIME_MEDIUM;
+ pauseMovement(parentObject);
+ }
+ break;
+ case HotSpotSystem.HotSpotType.WAIT_LONG:
+ if (mPauseTime == 0.0f) {
+ mPauseTime = PAUSE_TIME_LONG;
+ pauseMovement(parentObject);
+ }
+ break;
+ case HotSpotSystem.HotSpotType.ATTACK:
+ if (mPauseOnAttack) {
+ if (mPauseTime == 0.0f) {
+ mPauseTime = PAUSE_TIME_ATTACK;
+ pauseMovement(parentObject);
+
+ }
+ }
+ parentObject.setCurrentAction(ActionType.ATTACK);
+
+ break;
+
+ case HotSpotSystem.HotSpotType.TALK:
+ if (mHitReactComponent != null) {
+ if (parentObject.lastReceivedHitType != HitType.COLLECT) {
+ mHitReactComponent.setSpawnGameEventOnHit(
+ HitType.COLLECT, mDialogEvent, mDialogIndex);
+ if (parentObject.getVelocity().x != 0.0f) {
+ pauseMovement(parentObject);
+ }
+ hitAccepted = false;
+ } else {
+ parentObject.setCurrentAction(ActionType.MOVE);
+
+ resumeMovement(parentObject);
+ mHitReactComponent.setSpawnGameEventOnHit(HitType.INVALID, 0, 0);
+ parentObject.lastReceivedHitType = HitType.INVALID;
+ }
+ }
+ break;
+
+ case HotSpotSystem.HotSpotType.WALK_AND_TALK:
+ if (mDialogEvent != GameFlowEvent.EVENT_INVALID) {
+ LevelSystem level = sSystemRegistry.levelSystem;
+ level.sendGameEvent(mDialogEvent, mDialogIndex, true);
+ mDialogEvent = GameFlowEvent.EVENT_INVALID;
+ }
+ break;
+
+ case HotSpotSystem.HotSpotType.TAKE_CAMERA_FOCUS:
+ if (camera != null) {
+ camera.setTarget(parentObject);
+ }
+ break;
+
+ case HotSpotSystem.HotSpotType.RELEASE_CAMERA_FOCUS:
+
+ if (camera != null) {
+ GameObjectManager gameObjectManager = sSystemRegistry.gameObjectManager;
+ camera.setTarget(gameObjectManager.getPlayer());
+ }
+ break;
+
+ case HotSpotSystem.HotSpotType.END_LEVEL:
+ HudSystem hud = sSystemRegistry.hudSystem;
+
+ if (hud != null) {
+ hud.startFade(false, 1.5f);
+ hud.sendGameEventOnFadeComplete(GameFlowEvent.EVENT_GO_TO_NEXT_LEVEL, 0);
+ }
+ break;
+ case HotSpotSystem.HotSpotType.GAME_EVENT:
+ if (mGameEvent != -1) {
+ LevelSystem level = sSystemRegistry.levelSystem;
+ if (level != null) {
+ level.sendGameEvent(mGameEvent, mGameEventIndex, true);
+ mGameEvent = -1;
+ }
+ }
+ break;
+
+ case HotSpotSystem.HotSpotType.NPC_GO_UP_FROM_GROUND:
+ if (!parentObject.touchingGround()) {
+ hitAccepted = false;
+ break;
+ }
+ // fall through
+ case HotSpotSystem.HotSpotType.NPC_GO_UP:
+ parentObject.getVelocity().y = mUpImpulse;
+ parentObject.getTargetVelocity().y = 0.0f;
+ mTargetXVelocity = 0.0f;
+
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_DOWN_FROM_CEILING:
+ if (!parentObject.touchingCeiling()) {
+ hitAccepted = false;
+ break;
+ }
+ // fall through
+ case HotSpotSystem.HotSpotType.NPC_GO_DOWN:
+ parentObject.getVelocity().y = mDownImpulse;
+ parentObject.getTargetVelocity().y = 0.0f;
+ if (mFlying) {
+ mTargetXVelocity = 0.0f;
+ }
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_LEFT:
+ parentObject.getTargetVelocity().x = -mHorizontalImpulse;
+ parentObject.getAcceleration().x = mAcceleration;
+ if (mFlying) {
+ parentObject.getVelocity().y = 0.0f;
+ parentObject.getTargetVelocity().y = 0.0f;
+ }
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_RIGHT:
+ parentObject.getTargetVelocity().x = mHorizontalImpulse;
+ parentObject.getAcceleration().x = mAcceleration;
+ if (mFlying) {
+ parentObject.getVelocity().y = 0.0f;
+ parentObject.getTargetVelocity().y = 0.0f;
+ }
+
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_UP_RIGHT:
+ parentObject.getVelocity().y = mUpImpulse;
+ parentObject.getTargetVelocity().x = mHorizontalImpulse;
+ parentObject.getAcceleration().x = mAcceleration;
+
+
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_UP_LEFT:
+ parentObject.getVelocity().y = mUpImpulse;
+ parentObject.getTargetVelocity().x = -mHorizontalImpulse;
+ parentObject.getAcceleration().x = mAcceleration;
+
+
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_DOWN_RIGHT:
+ parentObject.getVelocity().y = mDownImpulse;
+ parentObject.getTargetVelocity().x = mHorizontalImpulse;
+ parentObject.getAcceleration().x = mAcceleration;
+
+
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_DOWN_LEFT:
+ parentObject.getVelocity().y = mDownImpulse;
+ parentObject.getTargetVelocity().x = -mHorizontalImpulse;
+ parentObject.getAcceleration().x = mAcceleration;
+
+
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_TOWARDS_PLAYER:
+ int direction = 1;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (manager != null) {
+ GameObject player = manager.getPlayer();
+ if (player != null) {
+ direction = Utils.sign(
+ player.getCenteredPositionX() -
+ parentObject.getCenteredPositionX());
+ }
+ }
+ parentObject.getTargetVelocity().x = mHorizontalImpulse * direction;
+ if (mFlying) {
+ parentObject.getVelocity().y = 0.0f;
+ parentObject.getTargetVelocity().y = 0.0f;
+ }
+ break;
+ case HotSpotSystem.HotSpotType.NPC_GO_RANDOM:
+ parentObject.getTargetVelocity().x = mHorizontalImpulse * (Math.random() > 0.5f ? -1.0f : 1.0f);
+ if (mFlying) {
+ parentObject.getVelocity().y = 0.0f;
+ parentObject.getTargetVelocity().y = 0.0f;
+ }
+ break;
+
+ case HotSpotSystem.HotSpotType.NPC_STOP:
+ parentObject.getTargetVelocity().x = 0.0f;
+ parentObject.getVelocity().x = 0.0f;
+ break;
+
+ case HotSpotSystem.HotSpotType.NPC_SLOW:
+ parentObject.getTargetVelocity().x = mSlowHorizontalImpulse * Utils.sign(parentObject.getTargetVelocity().x);
+ break;
+
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_1:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_2:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_3:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_4:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_5:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_2:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_3:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_4:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_5:
+ selectDialog(hotSpot);
+ break;
+ case HotSpotSystem.HotSpotType.NONE:
+ if (parentObject.touchingGround() && parentObject.getVelocity().y <= 0.0f) {
+ //resumeMovement(parentObject);
+ }
+ break;
+ }
+
+ return hitAccepted;
+ }
+
+ private void pauseMovement(GameObject parentObject) {
+ mTargetXVelocity = parentObject.getTargetVelocity().x;
+ parentObject.getTargetVelocity().x = 0.0f;
+ parentObject.getVelocity().x = 0.0f;
+ }
+
+ private void resumeMovement(GameObject parentObject) {
+ parentObject.getTargetVelocity().x = mTargetXVelocity;
+ parentObject.getAcceleration().x = mAcceleration;
+ }
+
+ private void selectDialog(int hitSpot) {
+ mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER1;
+ mDialogIndex = hitSpot - HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_1;
+
+ if (hitSpot >= HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1) {
+ mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER2;
+ mDialogIndex = hitSpot - HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1;
+ }
+ }
+
+ private int nextCommand() {
+ int result = HotSpotSystem.HotSpotType.NONE;
+ if (mQueueTop != mQueueBottom) {
+ result = mQueuedCommands[mQueueTop];
+ }
+ return result;
+ }
+
+ private int advanceQueue() {
+ int result = HotSpotSystem.HotSpotType.NONE;
+ if (mQueueTop != mQueueBottom) {
+ result = mQueuedCommands[mQueueTop];
+ mQueueTop = (mQueueTop + 1) % COMMAND_QUEUE_SIZE;
+ }
+ return result;
+ }
+
+ private void queueCommand(int hotspot) {
+ int nextSlot = (mQueueBottom + 1) % COMMAND_QUEUE_SIZE;
+ if (nextSlot != mQueueTop) { // only comply if there is space left in the buffer
+ mQueuedCommands[mQueueBottom] = hotspot;
+ mQueueBottom = nextSlot;
+ }
+ }
+
+ public void setHitReactionComponent(HitReactionComponent hitReact) {
+ mHitReactComponent = hitReact;
+ }
+
+ public void setSpeeds(float horizontalImpulse, float slowHorizontalImpulse, float upImpulse, float downImpulse, float acceleration) {
+ mHorizontalImpulse = horizontalImpulse;
+ mSlowHorizontalImpulse = slowHorizontalImpulse;
+ mUpImpulse = upImpulse;
+ mDownImpulse = downImpulse;
+ mAcceleration = acceleration;
+ }
+
+ public void setGameEvent(int event, int index, boolean spawnOnDeath) {
+ mGameEvent = event;
+ mGameEventIndex = index;
+ mSpawnGameEventOnDeath = spawnOnDeath;
+ }
+
+ public void setReactToHits(boolean react) {
+ mReactToHits = react;
+ }
+
+ public void setFlying(boolean flying) {
+ mFlying = flying;
+ }
+
+ public void setPauseOnAttack(boolean pauseOnAttack) {
+ mPauseOnAttack = pauseOnAttack;
+ }
+}
diff --git a/src/com/replica/replicaisland/ObjectManager.java b/src/com/replica/replicaisland/ObjectManager.java
new file mode 100644
index 0000000..de6dc28
--- /dev/null
+++ b/src/com/replica/replicaisland/ObjectManager.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * ObjectManagers are "group nodes" in the game graph. They contain child objects, and updating
+ * an object manager invokes update on its children. ObjectManagers themselves are derived from
+ * BaseObject, so they may be strung together into a hierarchy of objects. ObjectManager may
+ * be specialized to implement special types of traversals (e.g. PhasedObjectManager sorts its
+ * children).
+ */
+public class ObjectManager extends BaseObject {
+ protected static final int DEFAULT_ARRAY_SIZE = 64;
+
+ private FixedSizeArray<BaseObject> mObjects;
+ private FixedSizeArray<BaseObject> mPendingAdditions;
+ private FixedSizeArray<BaseObject> mPendingRemovals;
+
+ public ObjectManager() {
+ super();
+ mObjects = new FixedSizeArray<BaseObject>(DEFAULT_ARRAY_SIZE);
+ mPendingAdditions = new FixedSizeArray<BaseObject>(DEFAULT_ARRAY_SIZE);
+ mPendingRemovals = new FixedSizeArray<BaseObject>(DEFAULT_ARRAY_SIZE);
+ }
+
+ public ObjectManager(int arraySize) {
+ super();
+ mObjects = new FixedSizeArray<BaseObject>(arraySize);
+ mPendingAdditions = new FixedSizeArray<BaseObject>(arraySize);
+ mPendingRemovals = new FixedSizeArray<BaseObject>(arraySize);
+ }
+
+ @Override
+ public void reset() {
+ commitUpdates();
+ final int count = mObjects.getCount();
+ for (int i = 0; i < count; i++) {
+ BaseObject object = mObjects.get(i);
+ object.reset();
+ }
+ }
+
+ public void commitUpdates() {
+ final int additionCount = mPendingAdditions.getCount();
+ if (additionCount > 0) {
+ final Object[] additionsArray = mPendingAdditions.getArray();
+ for (int i = 0; i < additionCount; i++) {
+ BaseObject object = (BaseObject)additionsArray[i];
+ mObjects.add(object);
+ }
+ mPendingAdditions.clear();
+ }
+
+ final int removalCount = mPendingRemovals.getCount();
+ if (removalCount > 0) {
+ final Object[] removalsArray = mPendingRemovals.getArray();
+
+ for (int i = 0; i < removalCount; i++) {
+ BaseObject object = (BaseObject)removalsArray[i];
+ mObjects.remove(object, true);
+ }
+ mPendingRemovals.clear();
+ }
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ commitUpdates();
+ final int count = mObjects.getCount();
+ if (count > 0) {
+ final Object[] objectArray = mObjects.getArray();
+ for (int i = 0; i < count; i++) {
+ BaseObject object = (BaseObject)objectArray[i];
+ object.update(timeDelta, this);
+ }
+ }
+ }
+
+ public final FixedSizeArray<BaseObject> getObjects() {
+ return mObjects;
+ }
+
+ public final int getCount() {
+ return mObjects.getCount();
+ }
+
+ /** Returns the count after the next commitUpdates() is called. */
+ public final int getConcreteCount() {
+ return mObjects.getCount() + mPendingAdditions.getCount() - mPendingRemovals.getCount();
+ }
+
+ public final BaseObject get(int index) {
+ return mObjects.get(index);
+ }
+
+ public void add(BaseObject object) {
+ mPendingAdditions.add(object);
+ }
+
+ public void remove(BaseObject object) {
+ mPendingRemovals.add(object);
+ }
+
+ public void removeAll() {
+ final int count = mObjects.getCount();
+ final Object[] objectArray = mObjects.getArray();
+ for (int i = 0; i < count; i++) {
+ mPendingRemovals.add((BaseObject)objectArray[i]);
+ }
+ mPendingAdditions.clear();
+ }
+
+ /**
+ * Finds a child object by its type. Note that this may invoke the class loader and therefore
+ * may be slow.
+ * @param classObject The class type to search for (e.g. BaseObject.class).
+ * @return
+ */
+ public <T> T findByClass(Class<T> classObject) {
+ T object = null;
+ final int count = mObjects.getCount();
+ for (int i = 0; i < count; i++) {
+ BaseObject currentObject = mObjects.get(i);
+ if (currentObject.getClass() == classObject) {
+ object = classObject.cast(currentObject);
+ break;
+ }
+ }
+ return object;
+ }
+
+ protected FixedSizeArray<BaseObject> getPendingObjects() {
+ return mPendingAdditions;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/ObjectPool.java b/src/com/replica/replicaisland/ObjectPool.java
new file mode 100644
index 0000000..6a33b47
--- /dev/null
+++ b/src/com/replica/replicaisland/ObjectPool.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A general-purpose pool of objects. Objects in the pool are allocated up front and then
+ * passed out to requesting objects until the pool is exhausted (at which point an error is thrown).
+ * Code that requests objects from the pool should return them to the pool when they are finished.
+ * This class is abstract; derivations need to implement the fill() function to fill the pool, and
+ * may wish to override release() to clear state on objects as they are returned to the pool.
+ */
+public abstract class ObjectPool extends BaseObject {
+ private FixedSizeArray<Object> mAvailable;
+ private int mSize;
+
+ private static final int DEFAULT_SIZE = 32;
+
+ public ObjectPool() {
+ super();
+ setSize(DEFAULT_SIZE);
+ }
+
+ public ObjectPool(int size) {
+ super();
+ setSize(size);
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ /** Allocates an object from the pool */
+ protected Object allocate() {
+ Object result = mAvailable.removeLast();
+ assert result != null : "Object pool of type " + this.getClass().getSimpleName()
+ + " exhausted!!";
+ return result;
+ }
+
+ /** Returns an object to the pool. */
+ public void release(Object entry) {
+ mAvailable.add(entry);
+ }
+
+ /** Returns the number of pooled elements that have been allocated but not released. */
+ public int getAllocatedCount() {
+ return mAvailable.getCapacity() - mAvailable.getCount();
+ }
+
+ private void setSize(int size) {
+ mSize = size;
+ mAvailable = new FixedSizeArray<Object>(mSize);
+
+ fill();
+ }
+
+ protected abstract void fill();
+
+ protected FixedSizeArray<Object> getAvailable() {
+ return mAvailable;
+ }
+
+ protected int getSize() {
+ return mSize;
+ }
+
+
+}
diff --git a/src/com/replica/replicaisland/ObjectRegistry.java b/src/com/replica/replicaisland/ObjectRegistry.java
new file mode 100644
index 0000000..9758625
--- /dev/null
+++ b/src/com/replica/replicaisland/ObjectRegistry.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.ArrayList;
+
+/**
+ * The object registry manages a collection of global singleton objects. However, it differs from
+ * the standard singleton pattern in a few important ways:
+ * - The objects managed by the registry have an undefined lifetime. They may become invalid at
+ * any time, and they may not be valid at the beginning of the program.
+ * - The only object that is always guaranteed to be valid is the ObjectRegistry itself.
+ * - There may be more than one ObjectRegistry, and there may be more than one instance of any of
+ * the systems managed by ObjectRegistry allocated at once. For example, separate threads may
+ * maintain their own separate ObjectRegistry instances.
+ */
+public class ObjectRegistry extends BaseObject {
+
+ public BufferLibrary bufferLibrary;
+ public CameraSystem cameraSystem;
+ public ChannelSystem channelSystem;
+ public CollisionSystem collisionSystem;
+ public ContextParameters contextParameters;
+ public CustomToastSystem customToastSystem;
+ public DebugSystem debugSystem;
+ public DrawableFactory drawableFactory;
+ public EventRecorder eventRecorder;
+ public GameObjectCollisionSystem gameObjectCollisionSystem;
+ public GameObjectFactory gameObjectFactory;
+ public GameObjectManager gameObjectManager;
+ public HitPointPool hitPointPool;
+ public HotSpotSystem hotSpotSystem;
+ public HudSystem hudSystem;
+ public InputSystem inputSystem;
+ public LevelBuilder levelBuilder;
+ public LevelSystem levelSystem;
+ public OpenGLSystem openGLSystem;
+ public SoundSystem soundSystem;
+ public TextureLibrary shortTermTextureLibrary;
+ public TextureLibrary longTermTextureLibrary;
+ public TimeSystem timeSystem;
+ public RenderSystem renderSystem;
+ public VectorPool vectorPool;
+ public VibrationSystem vibrationSystem;
+
+ private ArrayList<BaseObject> mItemsNeedingReset = new ArrayList<BaseObject>();
+
+ public ObjectRegistry() {
+ super();
+ }
+
+ public void registerForReset(BaseObject object) {
+ final boolean contained = mItemsNeedingReset.contains(object);
+ assert !contained;
+ if (!contained) {
+ mItemsNeedingReset.add(object);
+ }
+ }
+
+ @Override
+ public void reset() {
+ final int count = mItemsNeedingReset.size();
+ for (int x = 0; x < count; x++) {
+ mItemsNeedingReset.get(x).reset();
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/OpenGLSystem.java b/src/com/replica/replicaisland/OpenGLSystem.java
new file mode 100644
index 0000000..b69cb74
--- /dev/null
+++ b/src/com/replica/replicaisland/OpenGLSystem.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+/**
+ * An object wrapper for a pointer to the OpenGL context. Note that the context is only valid
+ * in certain threads at certain times (namely, in the Rendering thread during draw time), and at
+ * other times getGL() will return null.
+ */
+public class OpenGLSystem extends BaseObject {
+
+ private static GL10 sGL;
+ private static int sLastBoundTexture;
+ private static int sLastSetCropSignature;
+
+ public OpenGLSystem() {
+ super();
+ sGL = null;
+ }
+
+ public OpenGLSystem(GL10 gl) {
+ sGL = gl;
+ }
+
+ public static final void setGL(GL10 gl) {
+ sGL = gl;
+ sLastBoundTexture = 0;
+ sLastSetCropSignature = 0;
+ }
+
+ public static final GL10 getGL() {
+ return sGL;
+ }
+
+ public static final void bindTexture(int target, int texture) {
+ if (sLastBoundTexture != texture) {
+ sGL.glBindTexture(target, texture);
+ sLastBoundTexture = texture;
+ sLastSetCropSignature = 0;
+ }
+ }
+
+ public static final void setTextureCrop(int[] crop) {
+ int cropSignature = 0;
+ cropSignature = (crop[0] + crop[1]) << 16;
+ cropSignature |= crop[2] + crop[3];
+
+ if (cropSignature != sLastSetCropSignature) {
+ ((GL11) sGL).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES,
+ crop, 0);
+ sLastSetCropSignature = cropSignature;
+ }
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+}
diff --git a/src/com/replica/replicaisland/OrbitalMagnetComponent.java b/src/com/replica/replicaisland/OrbitalMagnetComponent.java
new file mode 100644
index 0000000..7f26468
--- /dev/null
+++ b/src/com/replica/replicaisland/OrbitalMagnetComponent.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+
+public class OrbitalMagnetComponent extends GameComponent {
+ private final static float DEFAULT_STRENGTH = 15.0f;
+
+ private float mStrength;
+ private Vector2 mCenter;
+ private Vector2 mDelta;
+ private Vector2 mRim;
+ private Vector2 mVelocity;
+ private float mMagnetRadius;
+ private float mAreaRadius;
+
+ public OrbitalMagnetComponent() {
+ super();
+ mCenter = new Vector2();
+ mDelta = new Vector2();
+ mRim = new Vector2();
+ mVelocity = new Vector2();
+ reset();
+ setPhase(ComponentPhases.COLLISION_DETECTION.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mCenter.zero();
+ mDelta.zero();
+ mRim.zero();
+ mVelocity.zero();
+ mStrength = DEFAULT_STRENGTH;
+ mAreaRadius = 0.0f;
+ mMagnetRadius = 0.0f;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (manager != null) {
+ GameObject player = manager.getPlayer();
+ if (player != null) {
+ GameObject parentObject = (GameObject)parent;
+ applyMagnetism(player,
+ parentObject.getCenteredPositionX(),
+ parentObject.getCenteredPositionY(),
+ timeDelta);
+ }
+ }
+ }
+
+ private void applyMagnetism(GameObject target, float centerX, float centerY, float timeDelta) {
+ mCenter.set(centerX, centerY);
+ final float targetX = target.getCenteredPositionX();
+ final float targetY = target.getCenteredPositionY();
+ mDelta.set(targetX, targetY);
+ mDelta.subtract(mCenter);
+
+ final float distanceFromCenter2 = mDelta.length2();
+ final float area2 = mAreaRadius * mAreaRadius;
+ if (distanceFromCenter2 < area2) {
+ mRim.set(mDelta);
+ mRim.normalize();
+ mRim.multiply(mMagnetRadius);
+ mRim.add(mCenter);
+ // rim is now the closest point on the magnet circle
+
+ // remove gravity
+ final Vector2 targetVelocity = target.getVelocity();
+ GravityComponent gravity = target.findByClass(GravityComponent.class);
+ final Vector2 gravityVector = gravity.getGravity();
+ mVelocity.set(gravityVector);
+ mVelocity.multiply(timeDelta);
+ targetVelocity.subtract(mVelocity);
+
+ mDelta.add(targetVelocity);
+ mDelta.normalize();
+ mDelta.multiply(mMagnetRadius);
+ mDelta.add(mCenter);
+
+ // mDelta is now the next point on the magnet circle in the direction of
+ // movement.
+
+ mDelta.subtract(mRim);
+ mDelta.normalize();
+ // Now mDelta is the tangent to the magnet circle, pointing in the direction
+ // of movement.
+
+ mVelocity.set(mDelta);
+ mVelocity.normalize();
+
+ // mVelocity is now the direction to push the player
+
+ mVelocity.multiply(mStrength);
+ float weight = 1.0f;
+ if (distanceFromCenter2 > mMagnetRadius * mMagnetRadius) {
+ float distance = (float)Math.sqrt(distanceFromCenter2);
+ weight = (distance - mMagnetRadius) / (mAreaRadius - mMagnetRadius);
+ weight = 1.0f - weight;
+ mVelocity.multiply(weight);
+ }
+ final float speed = targetVelocity.length();
+ targetVelocity.add(mVelocity);
+ if (targetVelocity.length2() > (speed * speed)) {
+ targetVelocity.normalize();
+ targetVelocity.multiply(speed);
+ }
+
+ }
+
+ }
+
+ public void setup(float areaRadius, float orbitRadius) {
+ mAreaRadius = areaRadius;
+ mMagnetRadius = orbitRadius;
+ }
+}
diff --git a/src/com/replica/replicaisland/PatrolComponent.java b/src/com/replica/replicaisland/PatrolComponent.java
new file mode 100644
index 0000000..3b32cff
--- /dev/null
+++ b/src/com/replica/replicaisland/PatrolComponent.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.GameObject.ActionType;
+import com.replica.replicaisland.HotSpotSystem.HotSpotType;
+
+/**
+ * This component implements the "patrolling" behavior for AI characters. Patrolling characters
+ * will walk forward on the map until they hit a direction hot spot or a wall, in which case they
+ * may change direction. Patrollers can also be configured via this component to attack the player
+ * if appropriate conditions are met.
+ */
+public class PatrolComponent extends GameComponent {
+ private float mMaxSpeed;
+ private float mAcceleration;
+ private boolean mAttack;
+ private float mAttackAtDistance;
+ private boolean mAttackStopsMovement;
+ private float mAttackDuration;
+ private float mAttackDelay;
+ private boolean mTurnToFacePlayer;
+ private boolean mFlying;
+
+ private float mLastAttackTime;
+ Vector2 mWorkingVector;
+ Vector2 mWorkingVector2;
+
+
+ public PatrolComponent() {
+ super();
+ mWorkingVector = new Vector2();
+ mWorkingVector2 = new Vector2();
+
+ reset();
+ setPhase(GameComponent.ComponentPhases.THINK.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mTurnToFacePlayer = false;
+ mMaxSpeed = 0.0f;
+ mAcceleration = 0.0f;
+ mAttack = false;
+ mAttackAtDistance = 0.0f;
+ mAttackStopsMovement = false;
+ mAttackDuration = 0.0f;
+ mAttackDelay = 0.0f;
+ mWorkingVector.zero();
+ mWorkingVector2.zero();
+ mFlying = false;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+
+ if (parentObject.getCurrentAction() == ActionType.INVALID
+ || parentObject.getCurrentAction() == ActionType.HIT_REACT) {
+ parentObject.setCurrentAction(GameObject.ActionType.MOVE);
+ }
+
+ if ((mFlying || parentObject.touchingGround()) && parentObject.life > 0) {
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ GameObject player = null;
+ if (manager != null) {
+ player = manager.getPlayer();
+ }
+
+ if (mAttack) {
+ updateAttack(player, parentObject);
+ }
+
+
+ if (parentObject.getCurrentAction() == GameObject.ActionType.MOVE
+ && mMaxSpeed > 0.0f) {
+ int hotSpot = HotSpotSystem.HotSpotType.NONE;
+ HotSpotSystem hotSpotSystem = sSystemRegistry.hotSpotSystem;
+ if (hotSpotSystem != null) {
+ // TODO: ack, magic number
+ hotSpot = hotSpotSystem.getHotSpot(parentObject.getCenteredPositionX(),
+ parentObject.getPosition().y + 10.0f);
+ }
+ final float targetVelocityX = parentObject.getTargetVelocity().x;
+ final float targetVelocityY = parentObject.getTargetVelocity().y;
+
+ boolean goLeft = (parentObject.touchingRightWall()
+ || hotSpot == HotSpotType.GO_LEFT) && targetVelocityX >= 0.0f;
+
+ boolean goRight = (parentObject.touchingLeftWall()
+ || hotSpot == HotSpotType.GO_RIGHT) && targetVelocityX <= 0.0f;
+
+ boolean pause = (mMaxSpeed == 0.0f) || hotSpot == HotSpotType.GO_DOWN;
+
+ if (mTurnToFacePlayer && player != null && player.life > 0) {
+ final float horizontalDelta = player.getCenteredPositionX()
+ - parentObject.getCenteredPositionX();
+ final int targetFacingDirection = Utils.sign(horizontalDelta);
+ final float closestDistance = player.width / 2.0f;
+
+ if (targetFacingDirection < 0.0f) { // we want to turn to the left
+ if (goRight) {
+ goRight = false;
+ pause = true;
+ } else if (targetFacingDirection
+ != Utils.sign(parentObject.facingDirection.x)) {
+ goLeft = true;
+ }
+ } else if (targetFacingDirection > 0.0f) { // we want to turn to the right
+ if (goLeft) {
+ goLeft = false;
+ pause = true;
+ } else if (targetFacingDirection
+ != Utils.sign(parentObject.facingDirection.x)) {
+ goRight = true;
+ }
+ }
+
+ if (Math.abs(horizontalDelta) < closestDistance) {
+ goRight = false;
+ goLeft = false;
+ pause = true;
+ }
+ }
+
+ if (!mFlying) {
+ if (!pause && !goLeft && !goRight && targetVelocityX == 0.0f) {
+ if (parentObject.facingDirection.x < 0.0f) {
+ goLeft = true;
+ } else {
+ goRight = true;
+ }
+ }
+
+
+ if (goRight) {
+ parentObject.getTargetVelocity().x = mMaxSpeed;
+ parentObject.getAcceleration().x = mAcceleration;
+ } else if (goLeft) {
+ parentObject.getTargetVelocity().x = -mMaxSpeed;
+ parentObject.getAcceleration().x = mAcceleration;
+ } else if (pause) {
+ parentObject.getTargetVelocity().x = 0;
+ parentObject.getAcceleration().x = mAcceleration;
+ }
+ } else {
+ final boolean goUp = (parentObject.touchingGround() && targetVelocityY < 0.0f)
+ || hotSpot == HotSpotType.GO_UP;
+
+ final boolean goDown = (parentObject.touchingCeiling() && targetVelocityY > 0.0f)
+ || hotSpot == HotSpotType.GO_DOWN;
+
+ if (goUp) {
+ parentObject.getTargetVelocity().x = 0.0f;
+ parentObject.getTargetVelocity().y = mMaxSpeed;
+ parentObject.getAcceleration().y = mAcceleration;
+ parentObject.getAcceleration().x = mAcceleration;
+
+ } else if (goDown) {
+ parentObject.getTargetVelocity().x = 0.0f;
+ parentObject.getTargetVelocity().y = -mMaxSpeed;
+ parentObject.getAcceleration().y = mAcceleration;
+ parentObject.getAcceleration().x = mAcceleration;
+
+ } else if (goRight) {
+ parentObject.getTargetVelocity().x = mMaxSpeed;
+ parentObject.getAcceleration().x = mAcceleration;
+ parentObject.getAcceleration().y = mAcceleration;
+ parentObject.getTargetVelocity().y = 0.0f;
+ } else if (goLeft) {
+ parentObject.getTargetVelocity().x = -mMaxSpeed;
+ parentObject.getAcceleration().x = mAcceleration;
+ parentObject.getAcceleration().y = mAcceleration;
+ parentObject.getTargetVelocity().y = 0.0f;
+ }
+ }
+ }
+ } else if (!mFlying && !parentObject.touchingGround() && parentObject.life > 0) {
+ // A non-flying unit is in the air. In this case, just watch for bounces off walls.
+ if (Utils.sign(parentObject.getTargetVelocity().x) != Utils.sign(parentObject.getVelocity().x)) {
+ // Todo: maybe the physics code should adjust target velocity instead in this case?
+ parentObject.getTargetVelocity().x *= -1.0f;
+ }
+ }
+ }
+
+ private void updateAttack(GameObject player, GameObject parentObject) {
+ TimeSystem time = sSystemRegistry.timeSystem;
+ final float gameTime = time.getGameTime();
+
+ boolean visible = true;
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ ContextParameters context = sSystemRegistry.contextParameters;
+ final float dx =
+ Math.abs(parentObject.getCenteredPositionX() - camera.getFocusPositionX());
+ final float dy =
+ Math.abs(parentObject.getCenteredPositionY() - camera.getFocusPositionY());
+ if (dx > context.gameWidth / 2.0f || dy > context.gameHeight / 2.0f) {
+ visible = false;
+ }
+ if (visible && parentObject.getCurrentAction() == GameObject.ActionType.MOVE) {
+ boolean closeEnough = false;
+ boolean timeToAttack = (gameTime - mLastAttackTime) > mAttackDelay;
+ if (mAttackAtDistance > 0 && player != null && player.life > 0
+ && timeToAttack) {
+ // only attack if we are facing the player
+ if (Utils.sign(player.getPosition().x - parentObject.getPosition().x)
+ == Utils.sign(parentObject.facingDirection.x)) {
+ mWorkingVector.set(parentObject.getPosition());
+ mWorkingVector.x = parentObject.getCenteredPositionX();
+ mWorkingVector2.set(player.getPosition());
+ mWorkingVector2.x = player.getCenteredPositionX();
+ if (mWorkingVector2.distance2(mWorkingVector) <
+ mAttackAtDistance * mAttackAtDistance) {
+ closeEnough = true;
+ }
+ }
+ } else {
+ closeEnough = true; // If no distance has been set, don't worry about
+ // the player's position.
+ }
+
+ if (timeToAttack && closeEnough) {
+ // Time to attack.
+ parentObject.setCurrentAction(GameObject.ActionType.ATTACK);
+ mLastAttackTime = gameTime;
+ if (mAttackStopsMovement) {
+ parentObject.getVelocity().zero();
+ parentObject.getTargetVelocity().zero();
+ }
+ }
+ } else if (parentObject.getCurrentAction() == GameObject.ActionType.ATTACK) {
+ if (gameTime - mLastAttackTime > mAttackDuration) {
+ parentObject.setCurrentAction(GameObject.ActionType.MOVE);
+ if (mAttackStopsMovement) {
+ parentObject.getTargetVelocity().x =
+ mMaxSpeed * Utils.sign(parentObject.facingDirection.x);
+ parentObject.getAcceleration().x = mAcceleration;
+ }
+ }
+ }
+ }
+
+ public void setMovementSpeed(float speed, float acceleration) {
+ mMaxSpeed = speed;
+ mAcceleration = acceleration;
+ }
+
+ public void setupAttack(float distance, float duration, float delay, boolean stopMovement) {
+ mAttack = true;
+ mAttackAtDistance = distance;
+ mAttackStopsMovement = stopMovement;
+ mAttackDuration = duration;
+ mAttackDelay = delay;
+ }
+
+ public void setTurnToFacePlayer(boolean turn) {
+ mTurnToFacePlayer = turn;
+ }
+
+ public void setFlying(boolean flying) {
+ mFlying = flying;
+ }
+}
diff --git a/src/com/replica/replicaisland/PhasedObject.java b/src/com/replica/replicaisland/PhasedObject.java
new file mode 100644
index 0000000..87e0201
--- /dev/null
+++ b/src/com/replica/replicaisland/PhasedObject.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A basic object that adds an execution phase. When PhasedObjects are combined with
+ * PhasedObjectManagers, objects within the manager will be updated by phase.
+ */
+public class PhasedObject extends BaseObject {
+
+ public int phase; // This is public because the phased is accessed extremely often, so much
+ // so that the function overhead of an getter is non-trivial.
+
+ public PhasedObject() {
+ super();
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ public void setPhase(int phaseValue) {
+ phase = phaseValue;
+ }
+}
diff --git a/src/com/replica/replicaisland/PhasedObjectManager.java b/src/com/replica/replicaisland/PhasedObjectManager.java
new file mode 100644
index 0000000..db04733
--- /dev/null
+++ b/src/com/replica/replicaisland/PhasedObjectManager.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+/**
+ * A derivation of ObjectManager that sorts its children if they are of type PhasedObject.
+ * Sorting is performed on add.
+ */
+public class PhasedObjectManager extends ObjectManager {
+ private final static PhasedObjectComparator sPhasedObjectComparator
+ = new PhasedObjectComparator();
+ private boolean mDirty;
+ private PhasedObject mSearchDummy; // A dummy object allocated up-front for searching by phase.
+
+ public PhasedObjectManager() {
+ super();
+ mDirty = false;
+ getObjects().setComparator(sPhasedObjectComparator);
+ getPendingObjects().setComparator(sPhasedObjectComparator);
+ mSearchDummy = new PhasedObject();
+ }
+
+ @Override
+ public void commitUpdates() {
+ super.commitUpdates();
+ if (mDirty) {
+ getObjects().sort(true);
+ mDirty = false;
+ }
+ }
+
+ @Override
+ public void add(BaseObject object) {
+
+ if (object instanceof PhasedObject) {
+ super.add(object);
+ mDirty = true;
+ } else {
+ // The only reason to restrict PhasedObjectManager to PhasedObjects is so that
+ // the PhasedObjectComparator can assume all of its contents are PhasedObjects and
+ // avoid calling instanceof every time.
+ assert false : "Can't add a non-PhasedObject to a PhasedObjectManager!";
+ }
+ }
+
+ public BaseObject find(int phase) {
+ mSearchDummy.setPhase(phase);
+ int index = getObjects().find(mSearchDummy, false);
+ BaseObject result = null;
+ if (index != -1) {
+ result = getObjects().get(index);
+ } else {
+ index = getPendingObjects().find(mSearchDummy, false);
+ if (index != -1) {
+ result = getPendingObjects().get(index);
+ }
+ }
+ return result;
+ }
+
+ /** Comparator for phased objects. */
+ private static class PhasedObjectComparator implements Comparator<BaseObject> {
+ public int compare(BaseObject object1, BaseObject object2) {
+ int result = 0;
+ if (object1 != null && object2 != null) {
+ result = ((PhasedObject) object1).phase - ((PhasedObject) object2).phase;
+ } else if (object1 == null && object2 != null) {
+ result = 1;
+ } else if (object2 == null && object1 != null) {
+ result = -1;
+ }
+ return result;
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/PhysicsComponent.java b/src/com/replica/replicaisland/PhysicsComponent.java
new file mode 100644
index 0000000..3d6716c
--- /dev/null
+++ b/src/com/replica/replicaisland/PhysicsComponent.java
@@ -0,0 +1,266 @@
+
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Component that adds physics to its parent game object. This component implements force
+ * calculation based on mass, impulses, friction, and collisions.
+ */
+public class PhysicsComponent extends GameComponent {
+
+ private float mMass;
+ private float mBounciness; // 1.0 = super bouncy, 0.0 = zero bounce
+ private float mInertia;
+ private float mStaticFrictionCoeffecient;
+ private float mDynamicFrictionCoeffecient;
+
+ private static final float DEFAULT_MASS = 1.0f;
+ private static final float DEFAULT_BOUNCINESS = 0.1f;
+ private static final float DEFAULT_INERTIA = 0.01f;
+ private static final float DEFAULT_STATIC_FRICTION_COEFFECIENT = 0.05f;
+ private static final float DEFAULT_DYNAMIC_FRICTION_COEFFECIENT = 0.02f;
+
+ PhysicsComponent() {
+ super();
+ reset();
+ setPhase(ComponentPhases.POST_PHYSICS.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ // TODO: no reason to call accessors here locally.
+ setMass(DEFAULT_MASS);
+ setBounciness(DEFAULT_BOUNCINESS);
+ setInertia(DEFAULT_INERTIA);
+ setStaticFrictionCoeffecient(DEFAULT_STATIC_FRICTION_COEFFECIENT);
+ setDynamicFrictionCoeffecient(DEFAULT_DYNAMIC_FRICTION_COEFFECIENT);
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+
+ // we look to user data so that other code can provide impulses
+ Vector2 impulseVector = parentObject.getImpulse();
+
+ final Vector2 currentVelocity = parentObject.getVelocity();
+
+ final Vector2 surfaceNormal = parentObject.getBackgroundCollisionNormal();
+ if (surfaceNormal.length2() > 0.0f) {
+ resolveCollision(currentVelocity, impulseVector, surfaceNormal, impulseVector);
+ }
+
+ VectorPool vectorPool = sSystemRegistry.vectorPool;
+
+ // if our speed is below inertia, we need to overcome inertia before we can move.
+
+ boolean physicsCausesMovement = true;
+
+ final float inertiaSquared = getInertia() * getInertia();
+
+ Vector2 newVelocity = vectorPool.allocate(currentVelocity);
+ newVelocity.add(impulseVector);
+
+ if (newVelocity.length2() < inertiaSquared) {
+ physicsCausesMovement = false;
+ }
+
+ final boolean touchingFloor = parentObject.touchingGround();
+
+ GravityComponent gravity = parentObject.findByClass(GravityComponent.class);
+
+ if (touchingFloor && currentVelocity.y <= 0.0f && Math.abs(newVelocity.x) > 0.0f
+ && gravity != null) {
+ final Vector2 gravityVector = gravity.getGravity();
+
+ // if we were moving last frame, we'll use dynamic friction. Else
+ // static.
+ float frictionCoeffecient = Math.abs(currentVelocity.x) > 0.0f ?
+ getDynamicFrictionCoeffecient() : getStaticFrictionCoeffecient();
+ frictionCoeffecient *= timeDelta;
+
+ // Friction = cofN, where cof = friction coefficient and N = force
+ // perpendicular to the ground.
+ final float maxFriction = Math.abs(gravityVector.y) * getMass()
+ * frictionCoeffecient;
+
+ if (maxFriction > Math.abs(newVelocity.x)) {
+ newVelocity.x = (0.0f);
+ } else {
+ newVelocity.x = (newVelocity.x
+ - (maxFriction * Utils.sign(newVelocity.x)));
+ }
+ }
+
+ if (Math.abs(newVelocity.x) < 0.01f) {
+ newVelocity.x = (0.0f);
+ }
+
+ if (Math.abs(newVelocity.y) < 0.01f) {
+ newVelocity.y = (0.0f);
+ }
+
+ // physics-based movements means constant acceleration, always. set the target to the
+ // velocity.
+ if (physicsCausesMovement) {
+ parentObject.setVelocity(newVelocity);
+ parentObject.setTargetVelocity(newVelocity);
+ parentObject.setAcceleration(Vector2.ZERO);
+ parentObject.setImpulse(Vector2.ZERO);
+ }
+
+ vectorPool.release(newVelocity);
+ }
+
+ protected void resolveCollision(Vector2 velocity, Vector2 impulse, Vector2 opposingNormal,
+ Vector2 outputImpulse) {
+ VectorPool vectorPool = sSystemRegistry.vectorPool;
+
+ outputImpulse.set(impulse);
+
+ Vector2 collisionNormal = vectorPool.allocate(opposingNormal);
+
+ collisionNormal.normalize();
+
+ Vector2 relativeVelocity = vectorPool.allocate(velocity);
+ relativeVelocity.add(impulse);
+
+ final float dotRelativeAndNormal = relativeVelocity.dot(collisionNormal);
+
+ // make sure the motion of the entity requires resolution
+ if (dotRelativeAndNormal < 0.0f) {
+ final float coefficientOfRestitution = getBounciness(); // 0 = perfectly inelastic,
+ // 1 = perfectly elastic
+
+ // calculate an impulse to apply to the entity
+ float j = (-(1 + coefficientOfRestitution) * dotRelativeAndNormal);
+
+ j /= ((collisionNormal.dot(collisionNormal)) * (1 / getMass()));
+
+ Vector2 entity1Adjust = vectorPool.allocate(collisionNormal);
+
+ entity1Adjust.set(collisionNormal);
+ entity1Adjust.multiply(j);
+ entity1Adjust.divide(getMass());
+ entity1Adjust.add(impulse);
+ outputImpulse.set(entity1Adjust);
+ vectorPool.release(entity1Adjust);
+
+ }
+
+ vectorPool.release(collisionNormal);
+ vectorPool.release(relativeVelocity);
+ }
+
+ protected void resolveCollision(Vector2 velocity, Vector2 impulse, Vector2 opposingNormal,
+ float otherMass, Vector2 otherVelocity, Vector2 otherImpulse,
+ float otherBounciness, Vector2 outputImpulse) {
+ VectorPool vectorPool = sSystemRegistry.vectorPool;
+
+ Vector2 collisionNormal = vectorPool.allocate(opposingNormal);
+ collisionNormal.normalize();
+
+ Vector2 entity1Velocity = vectorPool.allocate(velocity);
+ entity1Velocity.add(impulse);
+
+ Vector2 entity2Velocity = vectorPool.allocate(otherVelocity);
+ entity2Velocity.add(otherImpulse);
+
+ Vector2 relativeVelocity = vectorPool.allocate(entity1Velocity);
+ relativeVelocity.subtract(entity2Velocity);
+
+ final float dotRelativeAndNormal = relativeVelocity.dot(collisionNormal);
+
+ // make sure the entities' motion requires resolution
+ if (dotRelativeAndNormal < 0.0f) {
+ final float bounciness = Math.min(getBounciness() + otherBounciness, 1.0f);
+ final float coefficientOfRestitution = bounciness; // 0 = perfectly inelastic,
+ // 1 = perfectly elastic
+
+ // calculate an impulse to apply to both entities
+ float j = (-(1 + coefficientOfRestitution) * dotRelativeAndNormal);
+
+ j /= ((collisionNormal.dot(collisionNormal)) * (1 / getMass() + 1 / otherMass));
+
+ Vector2 entity1Adjust = vectorPool.allocate(collisionNormal);
+ entity1Adjust.multiply(j);
+ entity1Adjust.divide(getMass());
+ entity1Adjust.add(impulse);
+
+ outputImpulse.set(entity1Adjust);
+
+ // TODO: Deal impulses both ways.
+ /*
+ * Vector3 entity2Adjust = (collisionNormal j);
+ * entity2Adjust[0] /= otherMass;
+ * entity2Adjust[1] /= otherMass;
+ * entity2Adjust[2] /= otherMass;
+ *
+ * const Vector3 newEntity2Impulse = otherImpulse + entity2Adjust;
+ */
+
+ vectorPool.release(entity1Adjust);
+ }
+
+ vectorPool.release(collisionNormal);
+ vectorPool.release(entity1Velocity);
+ vectorPool.release(entity2Velocity);
+ vectorPool.release(relativeVelocity);
+ }
+
+ public float getMass() {
+ return mMass;
+ }
+
+ public void setMass(float mass) {
+ mMass = mass;
+ }
+
+ public float getBounciness() {
+ return mBounciness;
+ }
+
+ public void setBounciness(float bounciness) {
+ mBounciness = bounciness;
+ }
+
+ public float getInertia() {
+ return mInertia;
+ }
+
+ public void setInertia(float inertia) {
+ mInertia = inertia;
+ }
+
+ public float getStaticFrictionCoeffecient() {
+ return mStaticFrictionCoeffecient;
+ }
+
+ public void setStaticFrictionCoeffecient(float staticFrictionCoeffecient) {
+ mStaticFrictionCoeffecient = staticFrictionCoeffecient;
+ }
+
+ public float getDynamicFrictionCoeffecient() {
+ return mDynamicFrictionCoeffecient;
+ }
+
+ public void setDynamicFrictionCoeffecient(float dynamicFrictionCoeffecient) {
+ mDynamicFrictionCoeffecient = dynamicFrictionCoeffecient;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/PlaySingleSoundComponent.java b/src/com/replica/replicaisland/PlaySingleSoundComponent.java
new file mode 100644
index 0000000..ab0f52d
--- /dev/null
+++ b/src/com/replica/replicaisland/PlaySingleSoundComponent.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+public class PlaySingleSoundComponent extends GameComponent {
+ private SoundSystem.Sound mSound;
+ private int mSoundHandle;
+
+ public PlaySingleSoundComponent() {
+ super();
+ reset();
+ setPhase(ComponentPhases.THINK.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mSoundHandle = -1;
+ mSound = null;
+ }
+
+ public void setSound(SoundSystem.Sound sound) {
+ mSound = sound;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ if (mSoundHandle == -1 && mSound != null) {
+ SoundSystem sound = sSystemRegistry.soundSystem;
+ mSoundHandle = sound.play(mSound, false, SoundSystem.PRIORITY_NORMAL);
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/PlayerComponent.java b/src/com/replica/replicaisland/PlayerComponent.java
new file mode 100644
index 0000000..dc31181
--- /dev/null
+++ b/src/com/replica/replicaisland/PlayerComponent.java
@@ -0,0 +1,586 @@
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.CollisionParameters.HitType;
+import com.replica.replicaisland.GameObject.ActionType;
+
+public class PlayerComponent extends GameComponent {
+
+ private static final float GROUND_IMPULSE_SPEED = 5000.0f;
+ private static final float AIR_HORIZONTAL_IMPULSE_SPEED = 4000.0f;
+ private static final float AIR_VERTICAL_IMPULSE_SPEED = 1200.0f;
+ private static final float AIR_VERTICAL_IMPULSE_SPEED_FROM_GROUND = 250.0f;
+ private static final float AIR_DRAG_SPEED = 4000.0f;
+ private static final float MAX_GROUND_HORIZONTAL_SPEED = 500.0f;
+ private static final float MAX_AIR_HORIZONTAL_SPEED = 150.0f;
+ private static final float MAX_UPWARD_SPEED = 250.0f;
+ private static final float VERTICAL_IMPULSE_TOLERANCE = 50.0f;
+ private static final float FUEL_AMOUNT = 1.0f;
+ private static final float FUEL_AIR_REFILL_SPEED = 0.15f;
+ private static final float FUEL_GROUND_REFILL_SPEED = 2.0f;
+ private static final float JUMP_TO_JETS_DELAY = 0.5f;
+
+ private static final float STOMP_VELOCITY = -1000.0f;
+ private static final float STOMP_DELAY_TIME = 0.15f;
+ private static final float STOMP_AIR_HANG_TIME = 0.0f; //0.25f;
+ private static final float STOMP_SHAKE_MAGNITUDE = 15.0f;
+ private static final float STOMP_VIBRATE_TIME = 0.05f;
+ private static final float HIT_REACT_TIME = 0.5f;
+
+ private static final float GHOST_REACTIVATION_DELAY = 0.3f;
+ private static final float GHOST_CHARGE_TIME = 0.75f;
+
+ public static final int MAX_PLAYER_LIFE = 3;
+ private static final int MAX_GEMS_PER_LEVEL = 3;
+ private static final int COINS_PER_POWERUP = 20;
+
+ private static final float NO_GEMS_GHOST_TIME = 3.0f;
+ private static final float ONE_GEM_GHOST_TIME = 8.0f;
+ private static final float TWO_GEMS_GHOST_TIME = 0.0f; // no limit.
+
+ public static final float GLOW_DURATION = 15.0f;
+
+ // DDA boosts
+ private static final int DDA_STAGE_1_ATTEMPTS = 3;
+ private static final int DDA_STAGE_2_ATTEMPTS = 8;
+ private static final int DDA_STAGE_1_LIFE_BOOST = 1;
+ private static final int DDA_STAGE_2_LIFE_BOOST = 2;
+ private static final float FUEL_AIR_REFILL_SPEED_DDA1 = 0.22f;
+ private static final float FUEL_AIR_REFILL_SPEED_DDA2 = 0.30f;
+
+ public enum State {
+ MOVE,
+ STOMP,
+ HIT_REACT,
+ DEAD,
+ WIN,
+ FROZEN,
+ POST_GHOST_DELAY
+ }
+
+ private boolean mTouchingGround;
+ private State mState;
+ private float mTimer;
+ private float mTimer2;
+ private float mFuel;
+ private float mJumpTime;
+ private boolean mGhostActive;
+ private float mGhostDeactivatedTime;
+ private float mGhostChargeTime;
+ private boolean mJumpButtonPressed;
+ private boolean mAttackButtonPressed;
+ private boolean mAttackButtonTriggered;
+ private InventoryComponent mInventory;
+ private Vector2 mHotSpotTestPoint;
+ private ChangeComponentsComponent mInvincibleSwap;
+ private float mInvincibleEndTime;
+ private HitReactionComponent mHitReaction;
+ private float mFuelAirRefillSpeed;
+
+ // Variables recorded for animation decisions.
+ private boolean mRocketsOn;
+
+ public PlayerComponent() {
+ super();
+ mHotSpotTestPoint = new Vector2();
+ reset();
+ setPhase(ComponentPhases.THINK.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mTouchingGround = false;
+ mState = State.MOVE;
+ mTimer = 0.0f;
+ mTimer2 = 0.0f;
+ mFuel = 0.0f;
+ mJumpTime = 0.0f;
+ mGhostActive = false;
+ mGhostDeactivatedTime = 0.0f;
+ mJumpButtonPressed = false;
+ mInventory = null;
+ mGhostChargeTime = 0.0f;
+ mHotSpotTestPoint.zero();
+ mInvincibleSwap = null;
+ mInvincibleEndTime = 0.0f;
+ mHitReaction = null;
+ mFuelAirRefillSpeed = FUEL_AIR_REFILL_SPEED;
+ mAttackButtonPressed = false;
+ mAttackButtonTriggered = false;
+ }
+
+ protected void move(float time, float timeDelta, GameObject parentObject) {
+ VectorPool pool = sSystemRegistry.vectorPool;
+ InputSystem input = sSystemRegistry.inputSystem;
+
+ if (pool != null && input != null) {
+
+ if (mFuel < FUEL_AMOUNT) {
+ if (mTouchingGround) {
+ mFuel += FUEL_GROUND_REFILL_SPEED * timeDelta;
+ } else {
+ mFuel += mFuelAirRefillSpeed * timeDelta;
+ }
+
+ if (mFuel > FUEL_AMOUNT) {
+ mFuel = FUEL_AMOUNT;
+ }
+ }
+
+ if (input.getRollTriggered() || mJumpButtonPressed) {
+ Vector2 impulse = pool.allocate();
+
+ if (input.getRollTriggered()) {
+ impulse.set(input.getRollDirection());
+ impulse.y = 0.0f;
+ }
+
+ if (mJumpButtonPressed) {
+ if (input.getTouchTriggered() && mTouchingGround) {
+ // In this case, velocity is instant so we don't need to scale
+ // it by time.
+ impulse.y = AIR_VERTICAL_IMPULSE_SPEED_FROM_GROUND;
+ mJumpTime = time;
+ } else if (time > mJumpTime + JUMP_TO_JETS_DELAY) {
+ if (mFuel > 0.0f) {
+ mFuel -= timeDelta;
+ impulse.y = AIR_VERTICAL_IMPULSE_SPEED * timeDelta;
+ mRocketsOn = true;
+ }
+
+ }
+ }
+
+ float horziontalSpeed = GROUND_IMPULSE_SPEED;
+ float maxHorizontalSpeed = MAX_GROUND_HORIZONTAL_SPEED;
+ final boolean inTheAir = !mTouchingGround
+ || impulse.y > VERTICAL_IMPULSE_TOLERANCE;
+ if (inTheAir) {
+ horziontalSpeed = AIR_HORIZONTAL_IMPULSE_SPEED;
+ maxHorizontalSpeed = MAX_AIR_HORIZONTAL_SPEED;
+ }
+
+ impulse.x = (impulse.x * horziontalSpeed * timeDelta);
+
+ // Don't let our jets move us past specific speed thresholds.
+ float currentSpeed = parentObject.getVelocity().x;
+ final float newSpeed = Math.abs(currentSpeed + impulse.x);
+ if (newSpeed > maxHorizontalSpeed) {
+ if (Math.abs(currentSpeed) < maxHorizontalSpeed) {
+ currentSpeed = maxHorizontalSpeed * Utils.sign(impulse.x);
+ parentObject.getVelocity().x = (currentSpeed);
+ }
+ impulse.x = (0.0f);
+ }
+
+ if (parentObject.getVelocity().y + impulse.y > MAX_UPWARD_SPEED
+ && Utils.sign(impulse.y) > 0) {
+ impulse.y = (0.0f);
+ if (parentObject.getVelocity().y < MAX_UPWARD_SPEED) {
+ parentObject.getVelocity().y = (MAX_UPWARD_SPEED);
+ }
+ }
+
+ if (inTheAir) {
+ // Apply drag while in the air.
+ if (Math.abs(currentSpeed) > maxHorizontalSpeed) {
+ float postDragSpeed = currentSpeed -
+ (AIR_DRAG_SPEED * timeDelta * Utils.sign(currentSpeed));
+ if (Utils.sign(currentSpeed) != Utils.sign(postDragSpeed)) {
+ postDragSpeed = 0.0f;
+ } else if (Math.abs(postDragSpeed) < maxHorizontalSpeed) {
+ postDragSpeed = maxHorizontalSpeed * Utils.sign(postDragSpeed);
+ }
+ parentObject.getVelocity().x = (postDragSpeed);
+ }
+ }
+
+ parentObject.getImpulse().add(impulse);
+ pool.release(impulse);
+ }
+
+ }
+ }
+
+ public void update(float timeDelta, BaseObject parent) {
+
+ TimeSystem time = sSystemRegistry.timeSystem;
+ GameObject parentObject = (GameObject)parent;
+
+ final float gameTime = time.getGameTime();
+ mTouchingGround = parentObject.touchingGround();
+
+ mRocketsOn = false;
+ mJumpButtonPressed = false;
+ mAttackButtonPressed = false;
+ mAttackButtonTriggered = false;
+
+ if (parentObject.getCurrentAction() == ActionType.INVALID) {
+ gotoMove(parentObject);
+ }
+
+ if (mInventory != null && mState != State.WIN) {
+ InventoryComponent.UpdateRecord inventory = mInventory.getRecord();
+ if (inventory.coinCount >= COINS_PER_POWERUP) {
+ inventory.coinCount = 0;
+ mInventory.setChanged();
+ parentObject.life = MAX_PLAYER_LIFE;
+ if (mInvincibleEndTime < gameTime) {
+ mInvincibleSwap.activate(parentObject);
+ mInvincibleEndTime = gameTime + GLOW_DURATION;
+ if (mHitReaction != null) {
+ mHitReaction.setForceInvincible(true);
+ }
+ }
+ }
+ if (inventory.rubyCount >= MAX_GEMS_PER_LEVEL) {
+ gotoWin(gameTime);
+ }
+ }
+
+ if (mInvincibleEndTime > 0.0f && mInvincibleEndTime < gameTime) {
+ mInvincibleSwap.activate(parentObject);
+ mInvincibleEndTime = 0.0f;
+ if (mHitReaction != null) {
+ mHitReaction.setForceInvincible(false);
+ }
+ }
+
+ // TODO: the region we are testing here should probably be moved out into some constants
+ // file and then independently tested by the hud and by the player so we can remove
+ // this cross dependency.
+ InputSystem input = sSystemRegistry.inputSystem;
+ if (input != null) {
+ if (input.getTouchPressed()) {
+ if (input.getTouchedWithinRegion(
+ ButtonConstants.FLY_BUTTON_REGION_X,
+ ButtonConstants.FLY_BUTTON_REGION_Y,
+ ButtonConstants.FLY_BUTTON_REGION_WIDTH,
+ ButtonConstants.FLY_BUTTON_REGION_HEIGHT)) {
+ mJumpButtonPressed = true;
+ } else if (input.getTouchedWithinRegion(
+ ButtonConstants.STOMP_BUTTON_REGION_X,
+ ButtonConstants.STOMP_BUTTON_REGION_Y,
+ ButtonConstants.STOMP_BUTTON_REGION_WIDTH,
+ ButtonConstants.STOMP_BUTTON_REGION_HEIGHT)) {
+ mAttackButtonPressed = true;
+ if (input.getTouchTriggered()) {
+ mAttackButtonTriggered = true;
+ }
+ }
+ }
+ if (input.getClickPressed()) {
+ mAttackButtonPressed = true;
+ if (input.getClickTriggered()) {
+ mAttackButtonTriggered = true;
+ }
+ }
+ }
+
+ // Watch for hit reactions or death interrupting the state machine.
+ if (mState != State.DEAD && mState != State.WIN ) {
+ if (parentObject.life <= 0) {
+ gotoDead(gameTime);
+ } else if (parentObject.getPosition().y < -parentObject.height) {
+ // we fell off the bottom of the screen, die.
+ parentObject.life = 0;
+ gotoDead(gameTime);
+ } else if (mState != State.HIT_REACT
+ && parentObject.lastReceivedHitType != HitType.INVALID
+ && parentObject.getCurrentAction() == ActionType.HIT_REACT) {
+ gotoHitReact(parentObject, gameTime);
+ } else {
+ HotSpotSystem hotSpot = sSystemRegistry.hotSpotSystem;
+ if (hotSpot != null) {
+ // TODO: HACK! Unify all this code.
+ if (hotSpot.getHotSpot(parentObject.getCenteredPositionX(),
+ parentObject.getPosition().y + 10.0f) == HotSpotSystem.HotSpotType.DIE) {
+ parentObject.life = 0;
+ gotoDead(gameTime);
+ }
+ }
+ }
+ }
+
+ switch(mState) {
+ case MOVE:
+ stateMove(gameTime, timeDelta, parentObject);
+ break;
+ case STOMP:
+ stateStomp(gameTime, timeDelta, parentObject);
+ break;
+ case HIT_REACT:
+ stateHitReact(gameTime, timeDelta, parentObject);
+ break;
+ case DEAD:
+ stateDead(gameTime, timeDelta, parentObject);
+ break;
+ case WIN:
+ stateWin(gameTime, timeDelta, parentObject);
+ break;
+ case FROZEN:
+ stateFrozen(gameTime, timeDelta, parentObject);
+ break;
+ case POST_GHOST_DELAY:
+ statePostGhostDelay(gameTime, timeDelta, parentObject);
+ break;
+ default:
+ break;
+ }
+
+ final HudSystem hud = sSystemRegistry.hudSystem;
+ if (hud != null) {
+ hud.setFuelPercent(mFuel / FUEL_AMOUNT);
+ hud.setButtonState(mJumpButtonPressed, mAttackButtonPressed);
+ }
+
+ }
+
+ protected void gotoMove(GameObject parentObject) {
+ parentObject.setCurrentAction(GameObject.ActionType.MOVE);
+ mState = State.MOVE;
+ }
+
+ protected void stateMove(float time, float timeDelta, GameObject parentObject) {
+ if (!mGhostActive) {
+ move(time, timeDelta, parentObject);
+
+ InputSystem input = sSystemRegistry.inputSystem;
+ if (mAttackButtonTriggered && !mTouchingGround) {
+ gotoStomp(parentObject);
+ } else if (mAttackButtonPressed && mTouchingGround
+ && mGhostDeactivatedTime + GHOST_REACTIVATION_DELAY < time) {
+ mGhostChargeTime += timeDelta;
+ if (mGhostChargeTime > GHOST_CHARGE_TIME) {
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (factory != null && manager != null) {
+ final float x = parentObject.getPosition().x;
+ final float y = parentObject.getPosition().y;
+ float ghostTime = NO_GEMS_GHOST_TIME;
+ if (mInventory != null) {
+ InventoryComponent.UpdateRecord inventory = mInventory.getRecord();
+ if (inventory.rubyCount == 1) {
+ ghostTime = ONE_GEM_GHOST_TIME;
+ } else if (inventory.rubyCount == 2) {
+ ghostTime = TWO_GEMS_GHOST_TIME;
+ }
+ }
+ GameObject ghost = factory.spawnPlayerGhost(x, y, parentObject, ghostTime);
+
+ manager.add(ghost);
+ mGhostActive = true;
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ if (camera != null) {
+ camera.setTarget(ghost);
+ }
+
+ input.clearClickTriggered();
+ }
+ }
+ } else if (!input.getClickPressed()) {
+ mGhostChargeTime = 0.0f;
+ }
+ }
+
+ }
+
+ protected void gotoStomp(GameObject parentObject) {
+ parentObject.setCurrentAction(GameObject.ActionType.ATTACK);
+ mState = State.STOMP;
+ mTimer = -1.0f;
+ mTimer2 = -1.0f;
+ parentObject.getImpulse().zero();
+ parentObject.getVelocity().set(0.0f, 0.0f);
+ parentObject.positionLocked = true;
+ }
+
+ protected void stateStomp(float time, float timeDelta, GameObject parentObject) {
+ if (mTimer < 0.0f) {
+ // first frame
+ mTimer = time;
+ } else if (time - mTimer > STOMP_AIR_HANG_TIME) {
+ // hang time complete
+ parentObject.getVelocity().set(0.0f, STOMP_VELOCITY);
+ parentObject.positionLocked = false;
+ }
+
+ if (mTouchingGround && mTimer2 < 0.0f) {
+ mTimer2 = time;
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ if (camera != null) {
+ camera.shake(STOMP_DELAY_TIME, STOMP_SHAKE_MAGNITUDE);
+ }
+ VibrationSystem vibrator = sSystemRegistry.vibrationSystem;
+
+ if (vibrator != null) {
+ vibrator.vibrate(STOMP_VIBRATE_TIME);
+ }
+
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (factory != null && manager != null) {
+ final float x = parentObject.getPosition().x;
+ final float y = parentObject.getPosition().y;
+ GameObject smoke1 = factory.spawnDust(x, y - 16, true);
+ GameObject smoke2 = factory.spawnDust(x + 32, y - 16, false);
+ manager.add(smoke1);
+ manager.add(smoke2);
+ }
+ }
+
+ if (mTimer2 > 0.0f && time - mTimer2 > STOMP_DELAY_TIME) {
+ parentObject.positionLocked = false;
+ gotoMove(parentObject);
+ }
+ }
+
+ protected void gotoHitReact(GameObject parentObject, float time) {
+ if (parentObject.lastReceivedHitType == CollisionParameters.HitType.LAUNCH) {
+ if (mState != State.FROZEN) {
+ gotoFrozen(parentObject);
+ }
+ } else {
+ mState = State.HIT_REACT;
+ mTimer = time;
+
+ }
+ }
+
+ protected void stateHitReact(float time, float timeDelta, GameObject parentObject) {
+ // This state just waits until the timer is expired.
+ if (time - mTimer > HIT_REACT_TIME) {
+ gotoMove(parentObject);
+ }
+ }
+
+ protected void gotoDead(float time) {
+ mState = State.DEAD;
+ mTimer = time;
+ }
+
+ protected void stateDead(float time, float timeDelta, GameObject parentObject) {
+ if (mTouchingGround && parentObject.getCurrentAction() != ActionType.DEATH) {
+ parentObject.setCurrentAction(ActionType.DEATH);
+ parentObject.getVelocity().zero();
+ parentObject.getTargetVelocity().zero();
+ }
+
+ if (parentObject.getPosition().y < -parentObject.height) {
+ // fell off the bottom of the screen.
+ parentObject.setCurrentAction(ActionType.DEATH);
+ parentObject.getVelocity().zero();
+ parentObject.getTargetVelocity().zero();
+ }
+
+ if (parentObject.getCurrentAction() == ActionType.DEATH && mTimer > 0.0f) {
+ final float elapsed = time - mTimer;
+ HudSystem hud = sSystemRegistry.hudSystem;
+ if (hud != null && !hud.isFading()) {
+ if (elapsed > 2.0f) {
+ hud.startFade(false, 1.5f);
+ hud.sendGameEventOnFadeComplete(GameFlowEvent.EVENT_RESTART_LEVEL, 0);
+ EventRecorder recorder = sSystemRegistry.eventRecorder;
+ if (recorder != null) {
+ recorder.setLastDeathPosition(parentObject.getPosition());
+ }
+ }
+ }
+
+ }
+ }
+
+ protected void gotoWin(float time) {
+ mState = State.WIN;
+ TimeSystem timeSystem = sSystemRegistry.timeSystem;
+ mTimer = timeSystem.getRealTime();
+ timeSystem.appyScale(0.1f, 8.0f, true);
+ }
+
+ protected void stateWin(float time, float timeDelta, GameObject parentObject) {
+ if (mTimer > 0.0f) {
+ TimeSystem timeSystem = sSystemRegistry.timeSystem;
+ final float elapsed = timeSystem.getRealTime() - mTimer;
+ HudSystem hud = sSystemRegistry.hudSystem;
+ if (hud != null && !hud.isFading()) {
+ if (elapsed > 2.0f) {
+ hud.startFade(false, 1.5f);
+ hud.sendGameEventOnFadeComplete(GameFlowEvent.EVENT_GO_TO_NEXT_LEVEL, 0);
+
+ }
+ }
+
+ }
+ }
+
+ protected void gotoFrozen(GameObject parentObject) {
+ mState = State.FROZEN;
+ parentObject.setCurrentAction(ActionType.FROZEN);
+ }
+
+ protected void stateFrozen(float time, float timeDelta, GameObject parentObject) {
+ if (parentObject.getCurrentAction() == ActionType.MOVE) {
+ gotoMove(parentObject);
+ }
+ }
+
+ protected void gotoPostGhostDelay() {
+ mState = State.POST_GHOST_DELAY;
+ }
+
+ protected void statePostGhostDelay(float time, float timeDelta, GameObject parentObject) {
+ if (time > mGhostDeactivatedTime) {
+ if (!mGhostActive) { // The ghost might have activated again during this delay.
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ if (camera != null) {
+ camera.setTarget(parentObject);
+ }
+ }
+ gotoMove(parentObject);
+ }
+ }
+
+ public final boolean getRocketsOn() {
+ return mRocketsOn;
+ }
+
+ public final boolean getGhostActive() {
+ return mGhostActive;
+ }
+
+ public final void deactivateGhost(float delay) {
+ mGhostActive = false;
+ mGhostDeactivatedTime = sSystemRegistry.timeSystem.getGameTime() + delay;
+ gotoPostGhostDelay();
+ }
+
+ public final void setInventory(InventoryComponent inventory) {
+ mInventory = inventory;
+ }
+
+ public final void setInvincibleSwap(ChangeComponentsComponent invincibleSwap) {
+ mInvincibleSwap = invincibleSwap;
+ }
+
+ public final void setHitReactionComponent(HitReactionComponent hitReact) {
+ mHitReaction = hitReact;
+ }
+
+ public final void adjustDifficulty(GameObject parent, int levelAttemps ) {
+ // Super basic DDA.
+ // If we've tried this levels several times secretly increase our
+ // hit points so the level gets easier.
+ // Also make fuel refill faster in the air after we've died too many times.
+ if (levelAttemps >= DDA_STAGE_1_ATTEMPTS) {
+ if (levelAttemps >= DDA_STAGE_2_ATTEMPTS) {
+ parent.life += DDA_STAGE_2_LIFE_BOOST;
+ mFuelAirRefillSpeed = FUEL_AIR_REFILL_SPEED_DDA2;
+ } else {
+ parent.life += DDA_STAGE_1_LIFE_BOOST;
+ mFuelAirRefillSpeed = FUEL_AIR_REFILL_SPEED_DDA1;
+ }
+ }
+
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/replica/replicaisland/PopOutComponent.java b/src/com/replica/replicaisland/PopOutComponent.java
new file mode 100644
index 0000000..e3d0ed4
--- /dev/null
+++ b/src/com/replica/replicaisland/PopOutComponent.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A component that implements the "pop-out" AI behavior. Pop-out characters alternate between
+ * hiding and appearing based on their distance from the player. They do not move or normally
+ * attack.
+ */
+public class PopOutComponent extends GameComponent {
+ private static final int DEFAULT_APPEAR_DISTANCE = 120;
+ private static final int DEFAULT_HIDE_DISTANCE = 190;
+ private static final int DEFAULT_ATTACK_DISTANCE = 0; // No attacking by default.
+ private float mAppearDistance;
+ private float mHideDistance;
+ private float mAttackDistance;
+ private float mAttackDelay;
+ private float mAttackLength;
+ private float mAttackStartTime;
+ private Vector2 mDistance;
+ private int mState;
+ private float mLastAttackCompletedTime;
+
+ private final static int STATE_HIDDEN = 0;
+ private final static int STATE_VISIBLE = 1;
+ private final static int STATE_ATTACKING = 2;
+
+ public PopOutComponent() {
+ super();
+ setPhase(GameComponent.ComponentPhases.THINK.ordinal());
+ mDistance = new Vector2();
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mAttackDelay = 0;
+ mAttackLength = 0;
+ mAttackDistance = DEFAULT_ATTACK_DISTANCE;
+ mAppearDistance = DEFAULT_APPEAR_DISTANCE;
+ mHideDistance = DEFAULT_HIDE_DISTANCE;
+ mState = STATE_HIDDEN;
+ mLastAttackCompletedTime = 0.0f;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+ if (manager != null) {
+ GameObject player = manager.getPlayer();
+ if (player != null) {
+ mDistance.set(player.getPosition());
+ mDistance.subtract(parentObject.getPosition());
+
+ TimeSystem time = sSystemRegistry.timeSystem;
+ final float currentTime = time.getGameTime();
+
+ switch(mState) {
+ case STATE_HIDDEN:
+ parentObject.setCurrentAction(GameObject.ActionType.HIDE);
+ if (mDistance.length2() < (mAppearDistance * mAppearDistance)) {
+ mState = STATE_VISIBLE;
+ mLastAttackCompletedTime = currentTime;
+ }
+ break;
+ case STATE_VISIBLE:
+ parentObject.setCurrentAction(GameObject.ActionType.IDLE);
+ if (mDistance.length2() > (mHideDistance * mHideDistance)) {
+ mState = STATE_HIDDEN;
+ } else if (mDistance.length2() < (mAttackDistance * mAttackDistance)
+ && currentTime > mLastAttackCompletedTime + mAttackDelay) {
+ mAttackStartTime = currentTime;
+ mState = STATE_ATTACKING;
+ }
+ break;
+ case STATE_ATTACKING:
+ parentObject.setCurrentAction(GameObject.ActionType.ATTACK);
+ if (currentTime > mAttackStartTime + mAttackLength) {
+ mState = STATE_VISIBLE;
+ mLastAttackCompletedTime = currentTime;
+ }
+ break;
+ default:
+ assert false;
+ break;
+ }
+
+ }
+ }
+
+ }
+
+ public void setupAttack(float distance, float delay, float duration) {
+ mAttackDistance = distance;
+ mAttackDelay = delay;
+ mAttackLength = duration;
+ }
+
+ public void setAppearDistance(float appearDistance) {
+ mAppearDistance = appearDistance;
+ }
+
+ public void setHideDistance(float hideDistance) {
+ mHideDistance = hideDistance;
+ }
+
+
+}
+
+
diff --git a/src/com/replica/replicaisland/QuickSorter.java b/src/com/replica/replicaisland/QuickSorter.java
new file mode 100644
index 0000000..7c753a0
--- /dev/null
+++ b/src/com/replica/replicaisland/QuickSorter.java
@@ -0,0 +1,60 @@
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+public class QuickSorter<Type> extends Sorter<Type> {
+ public void sort(Type[] array, int count, Comparator<Type> comparator) {
+ quicksort(array, 0, count - 1, comparator);
+ }
+
+ // Quicksort implementation based on the one here:
+ // http://www.cs.princeton.edu/introcs/42sort/QuickSort.java.html
+ /*************************************************************************
+ *
+ * Generate N random real numbers between 0 and 1 and quicksort them.
+ *
+ * On average, this quicksort algorithm runs in time proportional to
+ * N log N, independent of the input distribution. The algorithm
+ * uses Sedgewick's partitioning method which stops on equal keys.
+ * This protects against cases that make many textbook implementations,
+ * even randomized ones, go quadratic (e.g., all keys are the same).
+ *
+ *************************************************************************/
+
+ /***********************************************************************
+ * Quicksort code from Sedgewick 7.1, 7.2.
+ ***********************************************************************/
+
+ // quicksort a[left] to a[right]
+ public void quicksort(Type[] a, int left, int right, Comparator<Type> comparator) {
+ if (right <= left) return;
+ int i = partition(a, left, right, comparator);
+ quicksort(a, left, i - 1, comparator);
+ quicksort(a, i + 1, right, comparator);
+ }
+
+ // partition a[left] to a[right], assumes left < right
+ private int partition(Type[] a, int left, int right, Comparator<Type> comparator) {
+ int i = left - 1;
+ int j = right;
+ while (true) {
+ while (comparator.compare(a[++i], a[right]) < 0) { // find item on left to swap
+ } // a[right] acts as sentinel
+ while (comparator.compare(a[right], a[--j]) < 0) { // find item on right to swap
+ if (j == left) {
+ break; // don't go out-of-bounds
+ }
+ }
+ if (i >= j) {
+ break; // check if pointers cross
+ }
+ Type swap = a[i]; // swap two elements into place
+ a[i] = a[j];
+ a[j] = swap;
+ }
+ Type swap = a[i]; // swap with partition element
+ a[i] = a[right];
+ a[right] = swap;
+ return i;
+ }
+}
diff --git a/src/com/replica/replicaisland/RenderComponent.java b/src/com/replica/replicaisland/RenderComponent.java
new file mode 100644
index 0000000..9f09bad
--- /dev/null
+++ b/src/com/replica/replicaisland/RenderComponent.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Implements rendering of a drawable object for a game object. If a drawable is set on this
+ * component it will be passed to the renderer and drawn on the screen every frame. Drawable
+ * objects may be set to be "camera-relative" (meaning their screen position is relative to the
+ * location of the camera focus in the scene) or not (meaning their screen position is relative to
+ * the origin at the lower-left corner of the display).
+ */
+public class RenderComponent extends GameComponent {
+ private DrawableObject mDrawable;
+ private int mPriority;
+ private boolean mCameraRelative;
+ private Vector2 mPositionWorkspace;
+ private Vector2 mScreenLocation;
+ private Vector2 mDrawOffset;
+
+ public RenderComponent() {
+ super();
+ setPhase(ComponentPhases.DRAW.ordinal());
+
+ mPositionWorkspace = new Vector2();
+ mScreenLocation = new Vector2();
+ mDrawOffset = new Vector2();
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mPriority = 0;
+ mCameraRelative = true;
+ mDrawable = null;
+ mDrawOffset.zero();
+ }
+
+ public void update(float timeDelta, BaseObject parent) {
+ if (mDrawable != null) {
+ RenderSystem system = sSystemRegistry.renderSystem;
+ if (system != null) {
+ mPositionWorkspace.set(((GameObject)parent).getPosition());
+ mPositionWorkspace.add(mDrawOffset);
+ if (mCameraRelative) {
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ ContextParameters params = sSystemRegistry.contextParameters;
+ mScreenLocation.x = (mPositionWorkspace.x - camera.getFocusPositionX()
+ + (params.gameWidth / 2));
+ mScreenLocation.y = (mPositionWorkspace.y - camera.getFocusPositionY()
+ + (params.gameHeight / 2));
+ }
+ // It might be better not to do culling here, as doing it in the render thread
+ // would allow us to have multiple views into the same scene and things like that.
+ // But at the moment significant CPU is being spent on sorting the list of objects
+ // to draw per frame, so I'm going to go ahead and cull early.
+ if (mDrawable.visibleAtPosition(mScreenLocation)) {
+ system.scheduleForDraw(mDrawable, mPositionWorkspace, mPriority, mCameraRelative);
+ } else if (mDrawable.getParentPool() != null) {
+ // Normally the render system releases drawable objects back to the factory
+ // pool, but in this case we're short-circuiting the render system, so we
+ // need to release the object manually.
+ sSystemRegistry.drawableFactory.release(mDrawable);
+ mDrawable = null;
+ }
+ }
+ }
+ }
+
+ public DrawableObject getDrawable() {
+ return mDrawable;
+ }
+
+ public void setDrawable(DrawableObject drawable) {
+ mDrawable = drawable;
+ }
+
+ public void setPriority(int priority) {
+ mPriority = priority;
+ }
+
+ public int getPriority() {
+ return mPriority;
+ }
+
+ public void setCameraRelative(boolean relative) {
+ mCameraRelative = relative;
+ }
+
+ public void setDrawOffset(float x, float y) {
+ mDrawOffset.set(x, y);
+ }
+
+}
diff --git a/src/com/replica/replicaisland/RenderSystem.java b/src/com/replica/replicaisland/RenderSystem.java
new file mode 100644
index 0000000..114dce8
--- /dev/null
+++ b/src/com/replica/replicaisland/RenderSystem.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+
+/**
+ * Manages a double-buffered queue of renderable objects. The game thread submits drawable objects
+ * to the the active render queue while the render thread consumes drawables from the alternate
+ * queue. When both threads complete a frame the queues are swapped. Note that this class can
+ * manage any number (>=2) of render queues, but increasing the number over two means that the game
+ * logic will be running significantly ahead of the rendering thread, which may make the user feel
+ * that the controls are "loose."
+ */
+public class RenderSystem extends BaseObject {
+ private static final int TEXTURE_SORT_BUCKET_SIZE = 1000;
+ private RenderElementPool mElementPool;
+ private ObjectManager[] mRenderQueues;
+ private int mQueueIndex;
+
+ private final static int DRAW_QUEUE_COUNT = 2;
+ private final static int MAX_RENDER_OBJECTS_PER_FRAME = 128;
+ private final static int MAX_RENDER_OBJECTS = MAX_RENDER_OBJECTS_PER_FRAME * DRAW_QUEUE_COUNT;
+
+ public RenderSystem() {
+ super();
+ mElementPool = new RenderElementPool(MAX_RENDER_OBJECTS);
+ mRenderQueues = new ObjectManager[DRAW_QUEUE_COUNT];
+ for (int x = 0; x < DRAW_QUEUE_COUNT; x++) {
+ mRenderQueues[x] = new PhasedObjectManager();
+ }
+ mQueueIndex = 0;
+ }
+
+ @Override
+ public void reset() {
+
+ }
+
+ public void scheduleForDraw(DrawableObject object, Vector2 position, int priority, boolean cameraRelative) {
+ RenderElement element = mElementPool.allocate();
+ if (element != null) {
+ element.set(object, position, priority, cameraRelative);
+ mRenderQueues[mQueueIndex].add(element);
+ }
+ }
+
+ private void clearQueue(FixedSizeArray<BaseObject> objects) {
+ final int count = objects.getCount();
+ final Object[] objectArray = objects.getArray();
+ final RenderElementPool elementPool = mElementPool;
+ for (int i = count - 1; i >= 0; i--) {
+ RenderElement element = (RenderElement)objectArray[i];
+ elementPool.release(element);
+ objects.removeLast();
+ }
+
+ }
+
+ public void swap(GameRenderer renderer, float cameraX, float cameraY) {
+ mRenderQueues[mQueueIndex].commitUpdates();
+
+ // This code will block if the previous queue is still being executed.
+ renderer.setDrawQueue(mRenderQueues[mQueueIndex], cameraX, cameraY);
+
+ final int lastQueue = (mQueueIndex == 0) ? DRAW_QUEUE_COUNT - 1 : mQueueIndex - 1;
+
+ // Clear the old queue.
+ FixedSizeArray<BaseObject> objects = mRenderQueues[lastQueue].getObjects();
+ clearQueue(objects);
+
+ mQueueIndex = (mQueueIndex + 1) % DRAW_QUEUE_COUNT;
+ }
+
+ /* Empties all draw queues and disconnects the game thread from the renderer. */
+ public void emptyQueues(GameRenderer renderer) {
+ renderer.setDrawQueue(null, 0.0f, 0.0f);
+ for (int x = 0; x < DRAW_QUEUE_COUNT; x++) {
+ mRenderQueues[x].commitUpdates();
+ FixedSizeArray<BaseObject> objects = mRenderQueues[x].getObjects();
+ clearQueue(objects);
+
+ }
+ }
+
+ public class RenderElement extends PhasedObject {
+ public RenderElement() {
+ super();
+ }
+
+ public void set(DrawableObject drawable, Vector2 position, int priority, boolean isCameraRelative) {
+ mDrawable = drawable;
+ x = position.x;
+ y = position.y;
+ cameraRelative = isCameraRelative;
+ final int sortBucket = priority * TEXTURE_SORT_BUCKET_SIZE;
+ int sortOffset = 0;
+ if (drawable != null) {
+ Texture tex = drawable.getTexture();
+ if (tex != null) {
+ sortOffset = (tex.resource % TEXTURE_SORT_BUCKET_SIZE) * Utils.sign(priority);
+ }
+ }
+ setPhase(sortBucket + sortOffset);
+ }
+
+ public void reset() {
+ mDrawable = null;
+ x = 0.0f;
+ y = 0.0f;
+ cameraRelative = false;
+ }
+
+ public DrawableObject mDrawable;
+ public float x;
+ public float y;
+ public boolean cameraRelative;
+ }
+
+ protected class RenderElementPool extends TObjectPool<RenderElement> {
+
+ RenderElementPool(int max) {
+ super(max);
+ }
+
+ @Override
+ public void release(Object element) {
+ RenderElement renderable = (RenderElement)element;
+ // if this drawable came out of a pool, make sure it is returned to that pool.
+ final ObjectPool pool = renderable.mDrawable.getParentPool();
+ if (pool != null) {
+ pool.release(renderable.mDrawable);
+ }
+ // reset on release
+ renderable.reset();
+ super.release(element);
+ }
+
+ @Override
+ protected void fill() {
+ for (int x = 0; x < getSize(); x++) {
+ getAvailable().add(new RenderElement());
+ }
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/ScrollableBitmap.java b/src/com/replica/replicaisland/ScrollableBitmap.java
new file mode 100644
index 0000000..1340b0e
--- /dev/null
+++ b/src/com/replica/replicaisland/ScrollableBitmap.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Implements a bitmap that can be scrolled in place, such as the background of a scrolling
+ * world.
+ */
+public class ScrollableBitmap extends DrawableBitmap {
+ private float mScrollOriginX;
+ private float mScrollOriginY;
+
+ public ScrollableBitmap(Texture texture, int width, int height) {
+ super(texture, width, height);
+ }
+
+ public void setScrollOrigin(float x, float y) {
+ mScrollOriginX = x;
+ mScrollOriginY = y;
+ }
+
+ @Override
+ public void draw(float x, float y, float scaleX, float scaleY) {
+ super.draw(x - mScrollOriginX, y - mScrollOriginY, scaleX, scaleY);
+ }
+
+ public float getScrollOriginX() {
+ return mScrollOriginX;
+ }
+
+ public void setScrollOriginX(float scrollOriginX) {
+ mScrollOriginX = scrollOriginX;
+ }
+
+ public float getScrollOriginY() {
+ return mScrollOriginY;
+ }
+
+ public void setScrollOriginY(float scrollOriginY) {
+ mScrollOriginY = scrollOriginY;
+ }
+}
diff --git a/src/com/replica/replicaisland/ScrollerComponent.java b/src/com/replica/replicaisland/ScrollerComponent.java
new file mode 100644
index 0000000..bc1460e
--- /dev/null
+++ b/src/com/replica/replicaisland/ScrollerComponent.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Adjusts the scroll position of a drawable object based on the camera's focus position.
+ * May be used to scroll a ScrollableBitmap or TiledWorld to match the camera. Uses DrawableFactory
+ * to allocate fire-and-forget drawable objects every frame.
+ */
+public class ScrollerComponent extends GameComponent {
+ private int mWidth;
+ private int mHeight;
+ private float mHalfWidth;
+ private float mHalfHeight;
+ private RenderComponent mRenderComponent;
+ private float mSpeedX;
+ private float mSpeedY;
+ private Texture mTexture;
+ private TiledVertexGrid mVertGrid;
+
+ public ScrollerComponent(float speedX, float speedY, int width, int height, Texture texture) {
+ super();
+ reset();
+ setup(speedX, speedY, width, height);
+ setUseTexture(texture);
+ setPhase(ComponentPhases.PRE_DRAW.ordinal());
+ }
+
+ public ScrollerComponent(float speedX, float speedY, int width, int height, TiledVertexGrid grid) {
+ super();
+ reset();
+ setup(speedX, speedY, width, height);
+ mVertGrid = grid;
+ setPhase(ComponentPhases.PRE_DRAW.ordinal());
+ }
+
+ public ScrollerComponent() {
+ super();
+ reset();
+ setPhase(ComponentPhases.PRE_DRAW.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mWidth = 0;
+ mHeight = 0;
+ mHalfWidth = 0.0f;
+ mHalfHeight = 0.0f;
+ mRenderComponent = null;
+ mSpeedX = 0.0f;
+ mSpeedY = 0.0f;
+ mTexture = null;
+ mVertGrid = null;
+ }
+
+ public void setScrollSpeed(float speedX, float speedY) {
+ mSpeedX = speedX;
+ mSpeedY = speedY;
+ }
+
+ public void setup(float speedX, float speedY, int width, int height) {
+ mSpeedX = speedX;
+ mSpeedY = speedY;
+ mWidth = width;
+ mHeight = height;
+ mHalfWidth = sSystemRegistry.contextParameters.gameWidth / 2.0f; //width / 2.0f;
+ mHalfHeight = sSystemRegistry.contextParameters.gameHeight / 2.0f; //height / 2.0f;
+ }
+
+ public void setUseTexture(Texture texture) {
+ mTexture = texture;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ final DrawableFactory drawableFactory = sSystemRegistry.drawableFactory;
+ if (mRenderComponent != null && drawableFactory != null) {
+ ScrollableBitmap background;
+ if (mVertGrid != null) {
+ TiledBackgroundVertexGrid bg = drawableFactory.allocateTiledBackgroundVertexGrid();
+ bg.setGrid(mVertGrid);
+ background = bg;
+ } else {
+ background = drawableFactory.allocateScrollableBitmap();
+ background.setTexture(mTexture);
+ }
+
+ background.setWidth(mWidth);
+ background.setHeight(mHeight);
+
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+
+ float originX = camera.getFocusPositionX() - mHalfWidth;
+ float originY = camera.getFocusPositionY() - mHalfHeight;
+
+ originX *= mSpeedX;
+ originY *= mSpeedY;
+
+ background.setScrollOrigin(originX, originY);
+ mRenderComponent.setDrawable(background);
+ }
+ }
+
+ public void setRenderComponent(RenderComponent render) {
+ mRenderComponent = render;
+ }
+}
diff --git a/src/com/replica/replicaisland/SelectDialogComponent.java b/src/com/replica/replicaisland/SelectDialogComponent.java
new file mode 100644
index 0000000..1760359
--- /dev/null
+++ b/src/com/replica/replicaisland/SelectDialogComponent.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.CollisionParameters.HitType;
+
+
+public class SelectDialogComponent extends GameComponent {
+ private HitReactionComponent mHitReact;
+ private Vector2 mLastPosition;
+
+ public SelectDialogComponent() {
+ super();
+ setPhase(ComponentPhases.THINK.ordinal());
+ mLastPosition = new Vector2();
+ }
+
+ @Override
+ public void reset() {
+ mHitReact = null;
+ mLastPosition.zero();
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ HotSpotSystem hotSpot = sSystemRegistry.hotSpotSystem;
+ if (hotSpot != null && mHitReact != null) {
+ GameObject parentObject = (GameObject)parent;
+ final Vector2 currentPosition = parentObject.getPosition();
+ if (mLastPosition.distance2(parentObject.getPosition()) > 0.0f) {
+ mLastPosition.set(currentPosition);
+
+ final int hitSpot = hotSpot.getHotSpot(parentObject.getCenteredPositionX(), currentPosition.y + 10);
+ switch(hitSpot) {
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_1:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_2:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_3:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_4:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_5:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_2:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_3:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_4:
+ case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_5:
+ {
+ int event = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER1;
+ int index = hitSpot - HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_1;
+
+ if (hitSpot >= HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1) {
+ event = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER2;
+ index = hitSpot - HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1;
+ }
+
+ mHitReact.setSpawnGameEventOnHit(HitType.COLLECT, event, index);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ public void setHitReact(HitReactionComponent hit) {
+ mHitReact = hit;
+ }
+}
diff --git a/src/com/replica/replicaisland/SetPreferencesActivity.java b/src/com/replica/replicaisland/SetPreferencesActivity.java
new file mode 100644
index 0000000..f480fe0
--- /dev/null
+++ b/src/com/replica/replicaisland/SetPreferencesActivity.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceActivity;
+import android.widget.Toast;
+
+
+public class SetPreferencesActivity extends PreferenceActivity implements
+ YesNoDialogPreference.YesNoDialogListener {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getPreferenceManager().setSharedPreferencesMode(MODE_PRIVATE);
+ getPreferenceManager().setSharedPreferencesName(AndouKun.PREFERENCE_NAME);
+
+ // Load the preferences from an XML resource
+ addPreferencesFromResource(R.xml.preferences);
+
+ Preference eraseGameButton = getPreferenceManager().findPreference("erasegame");
+ if (eraseGameButton != null) {
+ YesNoDialogPreference yesNo = (YesNoDialogPreference)eraseGameButton;
+ yesNo.setListener(this);
+ }
+ }
+
+ public void onDialogClosed(boolean positiveResult) {
+ if (positiveResult) {
+ SharedPreferences prefs = getSharedPreferences(AndouKun.PREFERENCE_NAME, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.remove(AndouKun.PREFERENCE_LEVEL_ROW);
+ editor.remove(AndouKun.PREFERENCE_LEVEL_INDEX);
+ editor.remove(AndouKun.PREFERENCE_LEVEL_COMPLETED);
+ editor.commit();
+ Toast.makeText(this, R.string.saved_game_erased_notification,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/ShellSorter.java b/src/com/replica/replicaisland/ShellSorter.java
new file mode 100644
index 0000000..ccd5416
--- /dev/null
+++ b/src/com/replica/replicaisland/ShellSorter.java
@@ -0,0 +1,66 @@
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+public class ShellSorter<Type> extends Sorter<Type> {
+/**
+ * Shell sort implementation based on the one found here:
+ * http://www.augustana.ab.ca/~mohrj/courses/2004.winter/csc310/source/ShellSort.java.html
+ * Note that the running time can be tuned by adjusting the size of the increment used
+ * to pass over the array each time. Currently this function uses Robert Cruse's suggestion
+ * of increment = increment / 3 + 1.
+ */
+
+public void sort(Type[] array, int count, Comparator<Type> comparator) {
+ int increment = count / 3 + 1;
+
+ // Sort by insertion sort at diminishing increments.
+ while ( increment > 1 ) {
+ for ( int start = 0; start < increment; start++ ) {
+ insertionSort(array, count, start, increment, comparator);
+ }
+ increment = increment / 3 + 1;
+ }
+
+ // Do a final pass with an increment of 1.
+ // (This has to be outside the previous loop because the formula above for calculating the
+ // next increment will keep generating 1 repeatedly.)
+ insertionSort(array, count, 0, 1, comparator );
+ }
+
+
+/**
+ * Insertion sort modified to sort elements at a
+ * fixed increment apart.
+ *
+ * The code can be revised to eliminate the initial
+ * 'if', but I found that it made the sort slower.
+ *
+ * @param start the start position
+ * @param increment the increment
+ */
+public void insertionSort(Type[] array, int count, int start, int increment,
+ Comparator<Type> comparator) {
+ int j;
+ int k;
+ Type temp;
+
+ for (int i = start + increment; i < count; i += increment) {
+ j = i;
+ k = j - increment;
+ int delta = comparator.compare(array[j], array[k]);
+
+ if ( delta < 0 ) {
+ // Shift all previous entries down by the current
+ // increment until the proper place is found.
+ temp = array[j];
+ do {
+ array[j] = array[k];
+ j = k;
+ k = j - increment;
+ } while ( j != start && comparator.compare(array[k], temp) > 0 );
+ array[j] = temp;
+ }
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/SimpleCollisionComponent.java b/src/com/replica/replicaisland/SimpleCollisionComponent.java
new file mode 100644
index 0000000..da48e2b
--- /dev/null
+++ b/src/com/replica/replicaisland/SimpleCollisionComponent.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+// Simple collision detection component for objects not requiring complex collision (projectiles, etc)
+public class SimpleCollisionComponent extends GameComponent {
+ private Vector2 mPreviousPosition;
+ private Vector2 mCurrentPosition;
+ private Vector2 mMovementDirection;
+ private Vector2 mHitPoint;
+ private Vector2 mHitNormal;
+
+ public SimpleCollisionComponent() {
+ super();
+ setPhase(ComponentPhases.COLLISION_DETECTION.ordinal());
+ mPreviousPosition = new Vector2();
+ mCurrentPosition = new Vector2();
+ mMovementDirection = new Vector2();
+ mHitPoint = new Vector2();
+ mHitNormal = new Vector2();
+ }
+
+ @Override
+ public void reset() {
+ mPreviousPosition.zero();
+ mCurrentPosition.zero();
+ mMovementDirection.zero();
+ mHitPoint.zero();
+ mHitNormal.zero();
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+
+ if (mPreviousPosition.length2() > 0.0f) {
+ mCurrentPosition.set(parentObject.getCenteredPositionX(), parentObject.getCenteredPositionY());
+ mMovementDirection.set(mCurrentPosition);
+ mMovementDirection.subtract(mPreviousPosition);
+ if (mMovementDirection.length2() > 0.0f) {
+ final CollisionSystem collision = sSystemRegistry.collisionSystem;
+ if (collision != null) {
+ final boolean hit = collision.castRay(mPreviousPosition, mCurrentPosition,
+ mMovementDirection, mHitPoint, mHitNormal, parentObject);
+
+ if (hit) {
+ // snap
+ final float halfWidth = parentObject.width / 2.0f;
+ final float halfHeight = parentObject.height / 2.0f;
+ if (!Utils.close(mHitNormal.x, 0.0f)) {
+ parentObject.getPosition().x = mHitPoint.x - halfWidth;
+ }
+
+ if (!Utils.close(mHitNormal.y, 0.0f)) {
+ parentObject.getPosition().y = mHitPoint.y - halfHeight;
+ }
+
+ final TimeSystem timeSystem = sSystemRegistry.timeSystem;
+
+ if (timeSystem != null) {
+ float time = timeSystem.getGameTime();
+ if (mHitNormal.x > 0.0f) {
+ parentObject.setLastTouchedLeftWallTime(time);
+ } else if (mHitNormal.x < 0.0) {
+ parentObject.setLastTouchedRightWallTime(time);
+ }
+
+ if (mHitNormal.y > 0.0f) {
+ parentObject.setLastTouchedFloorTime(time);
+ } else if (mHitNormal.y < 0.0f) {
+ parentObject.setLastTouchedCeilingTime(time);
+ }
+ }
+
+ parentObject.setBackgroundCollisionNormal(mHitNormal);
+
+ }
+ }
+ }
+ }
+
+ mPreviousPosition.set(parentObject.getCenteredPositionX(), parentObject.getCenteredPositionY());
+ }
+}
diff --git a/src/com/replica/replicaisland/SimplePhysicsComponent.java b/src/com/replica/replicaisland/SimplePhysicsComponent.java
new file mode 100644
index 0000000..79b85bd
--- /dev/null
+++ b/src/com/replica/replicaisland/SimplePhysicsComponent.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/** A light-weight physics implementation for use with non-complex characters (enemies, etc). */
+public class SimplePhysicsComponent extends GameComponent {
+ private static final float DEFAULT_BOUNCINESS = 0.1f;
+ private float mBounciness;
+
+ public SimplePhysicsComponent() {
+ super();
+ setPhase(GameComponent.ComponentPhases.POST_PHYSICS.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mBounciness = DEFAULT_BOUNCINESS;
+ }
+
+ public void setBounciness(float bounciness) {
+ mBounciness = bounciness;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+
+ final Vector2 impulse = parentObject.getImpulse();
+ float velocityX = parentObject.getVelocity().x + impulse.x;
+ float velocityY = parentObject.getVelocity().y + impulse.y;
+
+ if ((parentObject.touchingCeiling() && velocityY > 0.0f)
+ || (parentObject.touchingGround() && velocityY < 0.0f)) {
+ velocityY = -velocityY * mBounciness;
+
+ if (Utils.close(velocityY, 0.0f)) {
+ velocityY = 0.0f;
+ }
+ }
+
+ if ((parentObject.touchingRightWall() && velocityX > 0.0f)
+ || (parentObject.touchingLeftWall() && velocityX < 0.0f)){
+ velocityX = -velocityX * mBounciness;
+
+ if (Utils.close(velocityX, 0.0f)) {
+ velocityX = 0.0f;
+ }
+ }
+
+ parentObject.getVelocity().set(velocityX, velocityY);
+ impulse.zero();
+ }
+}
diff --git a/src/com/replica/replicaisland/SleeperComponent.java b/src/com/replica/replicaisland/SleeperComponent.java
new file mode 100644
index 0000000..2e672fa
--- /dev/null
+++ b/src/com/replica/replicaisland/SleeperComponent.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.GameObject.ActionType;
+
+/**
+ * A component that implements the "pop-out" AI behavior. Pop-out characters alternate between
+ * hiding and appearing based on their distance from the player. They do not move or normally
+ * attack.
+ */
+public class SleeperComponent extends GameComponent {
+ private final static int STATE_SLEEPING = 0;
+ private final static int STATE_WAKING = 1;
+ private final static int STATE_ATTACKING = 2;
+ private final static int STATE_SLAM = 3;
+ private final static float DEFAULT_WAKE_UP_DURATION = 3.0f;
+ private float mWakeUpDuration;
+ private float mStateTime;
+ private int mState;
+ private float mSlamDuration;
+ private float mSlamMagnitude;
+ private float mAttackImpulseX;
+ private float mAttackImpulseY;
+
+ public SleeperComponent() {
+ super();
+ setPhase(GameComponent.ComponentPhases.THINK.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mWakeUpDuration = DEFAULT_WAKE_UP_DURATION;
+ mState = STATE_SLEEPING;
+ mStateTime = 0.0f;
+ mSlamDuration = 0.0f;
+ mSlamMagnitude = 0.0f;
+ mAttackImpulseX = 0.0f;
+ mAttackImpulseY = 0.0f;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject) parent;
+
+ if (parentObject.getCurrentAction() == ActionType.INVALID) {
+ parentObject.setCurrentAction(GameObject.ActionType.IDLE);
+ mState = STATE_SLEEPING;
+ }
+
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+ switch(mState) {
+ case STATE_SLEEPING:
+ if (camera.shaking() && camera.pointVisible(parentObject.getPosition(), parentObject.width / 2.0f)) {
+ mState = STATE_WAKING;
+ mStateTime = mWakeUpDuration;
+ parentObject.setCurrentAction(GameObject.ActionType.MOVE);
+ }
+ break;
+ case STATE_WAKING:
+ mStateTime -= timeDelta;
+ if (mStateTime <= 0.0f) {
+ mState = STATE_ATTACKING;
+ parentObject.setCurrentAction(GameObject.ActionType.ATTACK);
+ parentObject.getImpulse().x += mAttackImpulseX * parentObject.facingDirection.x;
+ parentObject.getImpulse().y += mAttackImpulseY;
+ }
+ break;
+ case STATE_ATTACKING:
+ if (parentObject.touchingGround() && parentObject.getVelocity().y < 0.0f) {
+ mState = STATE_SLAM;
+ camera.shake(mSlamDuration, mSlamMagnitude);
+ parentObject.getVelocity().zero();
+ }
+ break;
+ case STATE_SLAM:
+ if (!camera.shaking()) {
+ mState = STATE_SLEEPING;
+ parentObject.setCurrentAction(GameObject.ActionType.IDLE);
+ }
+ break;
+
+ }
+ }
+
+
+ public void setWakeUpDuration(float duration) {
+ mWakeUpDuration = duration;
+ }
+
+ public void setSlam(float duration, float magnitude) {
+ mSlamDuration = duration;
+ mSlamMagnitude = magnitude;
+ }
+
+ public void setAttackImpulse(float x, float y) {
+ mAttackImpulseX = x;
+ mAttackImpulseY = y;
+ }
+}
+
+
diff --git a/src/com/replica/replicaisland/SolidSurfaceComponent.java b/src/com/replica/replicaisland/SolidSurfaceComponent.java
new file mode 100644
index 0000000..65654b9
--- /dev/null
+++ b/src/com/replica/replicaisland/SolidSurfaceComponent.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A component that allows a game object to act like a solid object by submitting surfaces to the
+ * background collision system every frame.
+ */
+public class SolidSurfaceComponent extends GameComponent {
+ private FixedSizeArray<Vector2> mStartPoints;
+ private FixedSizeArray<Vector2> mEndPoints;
+ private FixedSizeArray<Vector2> mNormals;
+ private Vector2 mStart;
+ private Vector2 mEnd;
+ private Vector2 mNormal;
+
+ public SolidSurfaceComponent(int maxSurfaceCount) {
+ super();
+
+ inititalize(maxSurfaceCount);
+
+ mStart = new Vector2();
+ mEnd = new Vector2();
+ mNormal = new Vector2();
+
+ setPhase(ComponentPhases.POST_COLLISION.ordinal());
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mStartPoints.clear();
+ mEndPoints.clear();
+ mNormals.clear();
+ }
+
+ public SolidSurfaceComponent() {
+ super();
+
+ mStart = new Vector2();
+ mEnd = new Vector2();
+ mNormal = new Vector2();
+
+ setPhase(ComponentPhases.POST_COLLISION.ordinal());
+ }
+
+ public void inititalize(int maxSurfaceCount) {
+ if (mStartPoints == null
+ || (mStartPoints != null && mStartPoints.getCount() != maxSurfaceCount)) {
+ mStartPoints = new FixedSizeArray<Vector2>(maxSurfaceCount);
+ mEndPoints = new FixedSizeArray<Vector2>(maxSurfaceCount);
+ mNormals = new FixedSizeArray<Vector2>(maxSurfaceCount);
+ }
+
+ mStartPoints.clear();
+ mEndPoints.clear();
+ mNormals.clear();
+ }
+
+ // Note that this function keeps direct references to the arguments it is passed.
+ public void addSurface(Vector2 startPoint, Vector2 endPoint, Vector2 normal) {
+ mStartPoints.add(startPoint);
+ mEndPoints.add(endPoint);
+ mNormals.add(normal);
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ CollisionSystem collision = sSystemRegistry.collisionSystem;
+
+ final FixedSizeArray<Vector2> startPoints = mStartPoints;
+ final FixedSizeArray<Vector2> endPoints = mEndPoints;
+ final FixedSizeArray<Vector2> normals = mNormals;
+
+ final int surfaceCount = startPoints.getCount();
+ if (collision != null && surfaceCount > 0) {
+ GameObject parentObject = (GameObject)parent;
+ final Vector2 position = parentObject.getPosition();
+ Vector2 start = mStart;
+ Vector2 end = mEnd;
+ Vector2 normal = mNormal;
+
+ for (int x = 0; x < surfaceCount; x++) {
+ start.set(startPoints.get(x));
+ if (parentObject.facingDirection.x < 0.0f) {
+ start.flipHorizontal(parentObject.width);
+ }
+
+ if (parentObject.facingDirection.y < 0.0f) {
+ start.flipVertical(parentObject.height);
+ }
+ start.add(position);
+
+ end.set(endPoints.get(x));
+ if (parentObject.facingDirection.x < 0.0f) {
+ end.flipHorizontal(parentObject.width);
+ }
+
+ if (parentObject.facingDirection.y < 0.0f) {
+ end.flipVertical(parentObject.height);
+ }
+ end.add(position);
+
+ normal.set(normals.get(x));
+ if (parentObject.facingDirection.x < 0.0f) {
+ normal.flipHorizontal(0);
+ }
+
+ if (parentObject.facingDirection.y < 0.0f) {
+ normal.flipVertical(0);
+ }
+
+ collision.addTemporarySurface(start, end, normal, parentObject);
+ }
+
+
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/SortConstants.java b/src/com/replica/replicaisland/SortConstants.java
new file mode 100644
index 0000000..85d8773
--- /dev/null
+++ b/src/com/replica/replicaisland/SortConstants.java
@@ -0,0 +1,18 @@
+package com.replica.replicaisland;
+
+public class SortConstants {
+ public static final int BACKGROUND_START = -100;
+ public static final int THE_SOURCE_START = -5;
+ public static final int FOREGROUND = 0;
+ public static final int EFFECT = 5;
+ public static final int GENERAL_OBJECT = 10;
+ public static final int GENERAL_ENEMY = 15;
+ public static final int NPC = 15;
+ public static final int PLAYER = 20;
+ public static final int FOREGROUND_EFFECT = 30;
+ public static final int PROJECTILE = 40;
+ public static final int FOREGROUND_OBJECT = 50;
+ public static final int OVERLAY = 70;
+ public static final int HUD = 100;
+ public static final int FADE = 200;
+}
diff --git a/src/com/replica/replicaisland/Sorter.java b/src/com/replica/replicaisland/Sorter.java
new file mode 100644
index 0000000..ab2c605
--- /dev/null
+++ b/src/com/replica/replicaisland/Sorter.java
@@ -0,0 +1,7 @@
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+public abstract class Sorter<Type> {
+ public abstract void sort(Type[] array, int count, Comparator<Type> comparator);
+}
diff --git a/src/com/replica/replicaisland/SoundSystem.java b/src/com/replica/replicaisland/SoundSystem.java
new file mode 100644
index 0000000..99888ce
--- /dev/null
+++ b/src/com/replica/replicaisland/SoundSystem.java
@@ -0,0 +1,171 @@
+package com.replica.replicaisland;
+
+import java.util.Comparator;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.SoundPool;
+
+public class SoundSystem extends BaseObject {
+ private static final int MAX_STREAMS = 8;
+ private static final int MAX_SOUNDS = 32;
+ private static final SoundComparator sSoundComparator = new SoundComparator();
+
+ public static final int PRIORITY_LOW = 0;
+ public static final int PRIORITY_NORMAL = 1;
+ public static final int PRIORITY_HIGH = 2;
+ public static final int PRIORITY_MUSIC = 3;
+
+ private SoundPool mSoundPool;
+ private FixedSizeArray<Sound> mSounds;
+ private Sound mSearchDummy;
+ private boolean mSoundEnabled;
+ private int[] mLoopingStreams;
+
+ public SoundSystem() {
+ super();
+ mSoundPool = new SoundPool(MAX_STREAMS, AudioManager.STREAM_MUSIC, 0);
+ mSounds = new FixedSizeArray<Sound>(MAX_SOUNDS, sSoundComparator);
+ mSearchDummy = new Sound();
+ mLoopingStreams = new int[MAX_STREAMS];
+ for (int x = 0; x < mLoopingStreams.length; x++) {
+ mLoopingStreams[x] = -1;
+ }
+ }
+
+ @Override
+ public void reset() {
+ mSoundPool.release();
+ mSounds.clear();
+ mSoundEnabled = true;
+ for (int x = 0; x < mLoopingStreams.length; x++) {
+ mLoopingStreams[x] = -1;
+ }
+ }
+
+ public Sound load(int resource) {
+ final int index = findSound(resource);
+ Sound result = null;
+ if (index < 0) {
+ // new sound.
+ if (sSystemRegistry.contextParameters != null) {
+ Context context = sSystemRegistry.contextParameters.context;
+ result = new Sound();
+ result.resource = resource;
+ result.soundId = mSoundPool.load(context, resource, 1);
+ mSounds.add(result);
+ mSounds.sort(false);
+ }
+ } else {
+ result = mSounds.get(index);
+ }
+
+ return result;
+ }
+
+ synchronized public final int play(Sound sound, boolean loop, int priority) {
+ int stream = -1;
+ if (mSoundEnabled) {
+ stream = mSoundPool.play(sound.soundId, 1.0f, 1.0f, priority, loop ? -1 : 0, 1.0f);
+ if (loop) {
+ addLoopingStream(stream);
+ }
+ }
+
+ return stream;
+ }
+
+ synchronized public final int play(Sound sound, boolean loop, int priority, float volume, float rate) {
+ int stream = -1;
+ if (mSoundEnabled) {
+ stream = mSoundPool.play(sound.soundId, volume, volume, priority, loop ? -1 : 0, rate);
+ if (loop) {
+ addLoopingStream(stream);
+ }
+ }
+
+ return stream;
+ }
+
+ public final void stop(int stream) {
+ mSoundPool.stop(stream);
+ removeLoopingStream(stream);
+ }
+
+ public final void pause(int stream) {
+ mSoundPool.pause(stream);
+ }
+
+ public final void resume(int stream) {
+ mSoundPool.resume(stream);
+ }
+
+ // HACK: There's no way to pause an entire sound pool, but if we
+ // don't do something when our parent activity is paused, looping
+ // sounds will continue to play. Rather that reproduce all the bookkeeping
+ // that SoundPool does internally here, I've opted to just pause looping
+ // sounds when the Activity is paused.
+ public void pauseAll() {
+ final int count = mLoopingStreams.length;
+ for (int x = 0; x < count; x++) {
+ if (mLoopingStreams[x] >= 0) {
+ pause(mLoopingStreams[x]);
+ }
+ }
+ }
+
+ private void addLoopingStream(int stream) {
+ final int count = mLoopingStreams.length;
+ for (int x = 0; x < count; x++) {
+ if (mLoopingStreams[x] < 0) {
+ mLoopingStreams[x] = stream;
+ break;
+ }
+ }
+ }
+
+ private void removeLoopingStream(int stream) {
+ final int count = mLoopingStreams.length;
+ for (int x = 0; x < count; x++) {
+ if (mLoopingStreams[x] == stream) {
+ mLoopingStreams[x] = -1;
+ break;
+ }
+ }
+ }
+
+ private final int findSound(int resource) {
+ mSearchDummy.resource = resource;
+ return mSounds.find(mSearchDummy, false);
+ }
+
+ synchronized public final void setSoundEnabled(boolean soundEnabled) {
+ mSoundEnabled = soundEnabled;
+ }
+
+ public final boolean getSoundEnabled() {
+ return mSoundEnabled;
+ }
+
+ public class Sound extends AllocationGuard {
+ public int resource;
+ public int soundId;
+ }
+
+ /** Comparator for sounds. */
+ private final static class SoundComparator implements Comparator<Sound> {
+ public int compare(final Sound object1, final Sound object2) {
+ int result = 0;
+ if (object1 == null && object2 != null) {
+ result = 1;
+ } else if (object1 != null && object2 == null) {
+ result = -1;
+ } else if (object1 != null && object2 != null) {
+ result = object1.resource - object2.resource;
+ }
+ return result;
+ }
+ }
+
+
+}
diff --git a/src/com/replica/replicaisland/SphereCollisionVolume.java b/src/com/replica/replicaisland/SphereCollisionVolume.java
new file mode 100644
index 0000000..016dcfc
--- /dev/null
+++ b/src/com/replica/replicaisland/SphereCollisionVolume.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.replica.replicaisland;
+
+/** A sphere collision volume. */
+public class SphereCollisionVolume extends CollisionVolume {
+ private float mRadius;
+ private Vector2 mCenter;
+ private Vector2 mWorkspaceVector;
+ private Vector2 mWorkspaceVector2;
+
+ public SphereCollisionVolume(float radius, float centerX, float centerY) {
+ super();
+ mRadius = radius;
+ mCenter = new Vector2(centerX, centerY);
+ mWorkspaceVector = new Vector2();
+ mWorkspaceVector2 = new Vector2();
+ }
+
+ public SphereCollisionVolume(float radius, float centerX, float centerY, int hit) {
+ super(hit);
+ mRadius = radius;
+ mCenter = new Vector2(centerX, centerY);
+ mWorkspaceVector = new Vector2();
+ mWorkspaceVector2 = new Vector2();
+ }
+
+ @Override
+ public float getMaxX() {
+ return mCenter.x + mRadius;
+ }
+
+ @Override
+ public float getMinX() {
+ return mCenter.x - mRadius;
+ }
+
+ @Override
+ public float getMaxY() {
+ return mCenter.y + mRadius;
+ }
+
+ @Override
+ public float getMinY() {
+ return mCenter.y - mRadius;
+ }
+
+ public Vector2 getCenter() {
+ return mCenter;
+ }
+
+ public void setCenter(Vector2 center) {
+ mCenter.set(center);
+ }
+
+ public float getRadius() {
+ return mRadius;
+ }
+
+ public void setRadius(float radius) {
+ mRadius = radius;
+ }
+
+ public void reset() {
+ mCenter.zero();
+ mRadius = 0;
+ }
+
+ @Override
+ public boolean intersects(Vector2 position, FlipInfo flip, CollisionVolume other,
+ Vector2 otherPosition, FlipInfo otherFlip) {
+ boolean result = false;
+
+ if (other instanceof AABoxCollisionVolume) {
+ // It's more accurate to do a sphere-as-box test than a box-as-sphere test.
+ result = other.intersects(otherPosition, otherFlip, this, position, flip);
+ } else {
+ mWorkspaceVector.set(position);
+ offsetByCenter(mWorkspaceVector, mCenter, flip);
+
+ float otherRadius = 0;
+ if (other instanceof SphereCollisionVolume) {
+ SphereCollisionVolume sphereOther = (SphereCollisionVolume)other;
+ mWorkspaceVector2.set(otherPosition);
+ offsetByCenter(mWorkspaceVector2, sphereOther.getCenter(), otherFlip);
+ mWorkspaceVector.subtract(mWorkspaceVector2);
+ otherRadius = sphereOther.getRadius();
+ } else {
+ // Whatever this volume is, pretend it's a sphere.
+ final float deltaX = other.getMaxXPosition(otherFlip)
+ - other.getMinXPosition(otherFlip);
+ final float deltaY = other.getMaxYPosition(otherFlip)
+ - other.getMinYPosition(otherFlip);
+ final float centerX = deltaX / 2.0f;
+ final float centerY = deltaY / 2.0f;
+
+ mWorkspaceVector2.set(otherPosition);
+ mWorkspaceVector2.x += centerX;
+ mWorkspaceVector2.y += centerY;
+ otherRadius = Math.max(deltaX, deltaY);
+ }
+
+ final float maxDistance = mRadius + otherRadius;
+ final float distance2 = mWorkspaceVector.length2();
+ final float maxDistance2 = (maxDistance * maxDistance);
+ if (distance2 < maxDistance2) {
+ result = true;
+ }
+ }
+
+ return result;
+ }
+
+ public void growBy(CollisionVolume other) {
+ final float maxX;
+ final float minX;
+
+ final float maxY;
+ final float minY;
+
+ if (mRadius > 0) {
+ maxX = Math.max(getMaxX(), other.getMaxX());
+ minX = Math.min(getMinX(), other.getMinX());
+ maxY = Math.max(getMaxY(), other.getMaxY());
+ minY = Math.min(getMinY(), other.getMinY());
+ } else {
+ maxX = other.getMaxX();
+ minX = other.getMinX();
+ maxY = other.getMaxY();
+ minY = other.getMinY();
+ }
+ final float horizontalDelta = maxX - minX;
+ final float verticalDelta = maxY - minY;
+ final float diameter = Math.max(horizontalDelta, verticalDelta);
+
+ final float newCenterX = minX + (horizontalDelta / 2.0f);
+ final float newCenterY = minY + (verticalDelta / 2.0f);
+ final float newRadius = diameter / 2.0f;
+
+ mCenter.set(newCenterX, newCenterY);
+ mRadius = newRadius;
+ }
+
+ private static void offsetByCenter(Vector2 position, Vector2 center, FlipInfo flip) {
+ if (flip != null && (flip.flipX || flip.flipY)) {
+ if (flip.flipX) {
+ position.x += flip.parentWidth - center.x;
+ } else {
+ position.x += center.x;
+ }
+
+ if (flip.flipY) {
+ position.y += flip.parentHeight - center.y;
+ } else {
+ position.y += center.y;
+ }
+ } else {
+ position.add(center);
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/SpriteAnimation.java b/src/com/replica/replicaisland/SpriteAnimation.java
new file mode 100644
index 0000000..db47ff4
--- /dev/null
+++ b/src/com/replica/replicaisland/SpriteAnimation.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.util.Arrays;
+
+/**
+ * Describes a single animation for a sprite.
+ */
+public class SpriteAnimation extends PhasedObject {
+ private final static int LINEAR_SEARCH_CUTOFF = 16;
+
+ private FixedSizeArray<AnimationFrame> mFrames;
+ private float[] mFrameStartTimes;
+ private boolean mLoop;
+ private float mLength;
+
+ public SpriteAnimation(int animationId, int frameCount) {
+ super();
+ mFrames = new FixedSizeArray<AnimationFrame>(frameCount);
+ mFrameStartTimes = new float[frameCount];
+ mLoop = false;
+ mLength = 0.0f;
+ setPhase(animationId);
+ }
+
+ public AnimationFrame getFrame(float animationTime) {
+ AnimationFrame result = null;
+ final float length = mLength;
+ if (length > 0.0f) {
+ final FixedSizeArray<AnimationFrame> frames = mFrames;
+ assert frames.getCount() == frames.getCapacity();
+ final int frameCount = frames.getCount();
+ result = frames.get(frameCount - 1);
+
+ if (frameCount > 1) {
+ float currentTime = 0.0f;
+ float cycleTime = animationTime;
+ if (mLoop) {
+ cycleTime = animationTime % length;
+ }
+
+ if (cycleTime < length) {
+ // When there are very few frames it's actually slower to do a binary search
+ // of the frame list. So we'll use a linear search for small animations
+ // and only pull the binary search out when the frame count is large.
+ if (mFrameStartTimes.length > LINEAR_SEARCH_CUTOFF) {
+ int index = Arrays.binarySearch(mFrameStartTimes, cycleTime);
+ if (index < 0) {
+ index = -(index + 1) - 1;
+ }
+ result = frames.get(index);
+ } else {
+ for (int x = 0; x < frameCount; x++) {
+ AnimationFrame frame = frames.get(x);
+ currentTime += frame.holdTime;
+ if (currentTime > cycleTime) {
+ result = frame;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ public void addFrame(AnimationFrame frame) {
+ mFrameStartTimes[mFrames.getCount()] = mLength;
+ mFrames.add(frame);
+ mLength += frame.holdTime;
+ }
+
+ public float getLength() {
+ return mLength;
+ }
+
+ public void setLoop(boolean loop) {
+ mLoop = loop;
+ }
+
+ public boolean getLoop() {
+ return mLoop;
+ }
+}
diff --git a/src/com/replica/replicaisland/SpriteComponent.java b/src/com/replica/replicaisland/SpriteComponent.java
new file mode 100644
index 0000000..8fef472
--- /dev/null
+++ b/src/com/replica/replicaisland/SpriteComponent.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Provides an interface for controlling a sprite with animations. Manages a list of animations
+ * and provides a drawable surface with the correct animation frame to a render component each
+ * frame. Also manages horizontal and vertical flipping.
+ */
+public class SpriteComponent extends GameComponent {
+
+ private PhasedObjectManager mAnimations;
+ private float mAnimationTime;
+ private int mCurrentAnimationIndex;
+ private int mWidth;
+ private int mHeight;
+ private float mOpacity;
+ private RenderComponent mRenderComponent;
+ private DynamicCollisionComponent mCollisionComponent;
+ private boolean mVisible;
+ private SpriteAnimation mCurrentAnimation;
+ private boolean mAnimationsDirty;
+
+ public SpriteComponent(int width, int height) {
+ super();
+ mAnimations = new PhasedObjectManager();
+
+ reset();
+ mWidth = width;
+ mHeight = height;
+ setPhase(ComponentPhases.PRE_DRAW.ordinal());
+ }
+
+ public SpriteComponent() {
+ super();
+ mAnimations = new PhasedObjectManager();
+
+ reset();
+
+ setPhase(ComponentPhases.PRE_DRAW.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mWidth = 0;
+ mHeight = 0;
+ mVisible = true;
+ mCurrentAnimationIndex = -1;
+ mAnimations.removeAll();
+ mAnimations.commitUpdates();
+ mAnimationTime = 0.0f;
+ mRenderComponent = null;
+ mCollisionComponent = null;
+ mCurrentAnimation = null;
+ mOpacity = 1.0f;
+ mAnimationsDirty = false;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ mAnimationTime += timeDelta;
+ final PhasedObjectManager animations = mAnimations;
+ final int currentAnimIndex = mCurrentAnimationIndex;
+
+ if (mAnimationsDirty) {
+ animations.commitUpdates();
+ mAnimationsDirty = false;
+ }
+ boolean validFrameAvailable = false;
+ if (animations.getCount() > 0 && currentAnimIndex != -1) {
+ SpriteAnimation currentAnimation = mCurrentAnimation;
+
+ if (currentAnimation == null && currentAnimIndex != -1) {
+ currentAnimation = findAnimation(currentAnimIndex);
+ if (currentAnimation == null) {
+ // We were asked to play an animation that doesn't exist. Revert to our
+ // default animation.
+ // TODO: throw an assert here?
+ mCurrentAnimation = (SpriteAnimation)animations.get(0);
+ currentAnimation = mCurrentAnimation;
+ } else {
+ mCurrentAnimation = currentAnimation;
+ }
+ }
+
+ GameObject parentObject = (GameObject)parent;
+ AnimationFrame currentFrame = currentAnimation.getFrame(mAnimationTime);
+ if (currentFrame != null) {
+ validFrameAvailable = true;
+ final RenderComponent render = mRenderComponent;
+ if (render != null) {
+ final DrawableFactory factory = sSystemRegistry.drawableFactory;
+ if (mVisible && currentFrame.texture != null && factory != null) {
+ // Fire and forget. Allocate a new bitmap for this animation frame, set it up, and
+ // pass it off to the render component for drawing.
+ DrawableBitmap bitmap = factory.allocateDrawableBitmap();
+ bitmap.setWidth(mWidth);
+ bitmap.setHeight(mHeight);
+ bitmap.setOpacity(mOpacity);
+ updateFlip(bitmap, parentObject.facingDirection.x < 0.0f,
+ parentObject.facingDirection.y < 0.0f);
+ bitmap.setTexture(currentFrame.texture);
+ render.setDrawable(bitmap);
+ } else {
+ render.setDrawable(null);
+ }
+ }
+
+ if (mCollisionComponent != null) {
+ mCollisionComponent.setCollisionVolumes(currentFrame.attackVolumes,
+ currentFrame.vulnerabilityVolumes);
+ }
+ }
+ }
+
+ if (!validFrameAvailable) {
+ // No current frame = draw nothing!
+ if (mRenderComponent != null) {
+ mRenderComponent.setDrawable(null);
+ }
+ if (mCollisionComponent != null) {
+ mCollisionComponent.setCollisionVolumes(null, null);
+ }
+ }
+ }
+
+ public final void playAnimation(int index) {
+ if (mCurrentAnimationIndex != index) {
+ mAnimationTime = 0;
+ mCurrentAnimationIndex = index;
+ mCurrentAnimation = null;
+ }
+ }
+
+ public final SpriteAnimation findAnimation(int index) {
+ return (SpriteAnimation)mAnimations.find(index);
+ }
+
+ public final void addAnimation(SpriteAnimation anim) {
+ mAnimations.add(anim);
+ mAnimationsDirty = true;
+ }
+
+ public final boolean animationFinished() {
+ boolean result = false;
+ if (mCurrentAnimation != null
+ && !mCurrentAnimation.getLoop()
+ && mAnimationTime > mCurrentAnimation.getLength()) {
+ result = true;
+ }
+ return result;
+ }
+
+
+ public final float getWidth() {
+ return mWidth;
+ }
+
+ public final float getHeight() {
+ return mHeight;
+ }
+
+ public final void setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ }
+
+ protected final void updateFlip(DrawableBitmap bitmap, boolean horzFlip, boolean vertFlip) {
+ bitmap.setFlip(horzFlip, vertFlip);
+ }
+
+ public final void setRenderComponent(RenderComponent component) {
+ mRenderComponent = component;
+ }
+
+ public final void setCollisionComponent(DynamicCollisionComponent component) {
+ mCollisionComponent = component;
+ }
+
+ public final boolean getVisible() {
+ return mVisible;
+ }
+
+ public final void setVisible(boolean visible) {
+ mVisible = visible;
+ }
+
+ public final float getCurrentAnimationTime() {
+ return mAnimationTime;
+ }
+
+ public final void setCurrentAnimationTime(float time) {
+ mAnimationTime = time;
+ }
+
+ public final void setOpacity(float opacity) {
+ mOpacity = opacity;
+ }
+
+ public final int getCurrentAnimation() {
+ return mCurrentAnimationIndex;
+ }
+
+ public final int getAnimationCount() {
+ return mAnimations.getConcreteCount();
+ }
+}
diff --git a/src/com/replica/replicaisland/StandardSorter.java b/src/com/replica/replicaisland/StandardSorter.java
new file mode 100644
index 0000000..09e9998
--- /dev/null
+++ b/src/com/replica/replicaisland/StandardSorter.java
@@ -0,0 +1,13 @@
+package com.replica.replicaisland;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class StandardSorter<T> extends Sorter {
+
+ @Override
+ public void sort(Object[] array, int count, Comparator comparator) {
+ Arrays.sort(array, 0, count, comparator);
+ }
+
+}
diff --git a/src/com/replica/replicaisland/TObjectPool.java b/src/com/replica/replicaisland/TObjectPool.java
new file mode 100644
index 0000000..9970735
--- /dev/null
+++ b/src/com/replica/replicaisland/TObjectPool.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * TObjectPool is a generic version of ObjectPool that automatically casts to type T on
+ * allocation.
+ *
+ * @param <T> The type of object managed by the pool.
+ */
+public abstract class TObjectPool<T> extends ObjectPool {
+
+ public TObjectPool() {
+ super();
+ }
+
+ public TObjectPool(int size) {
+ super(size);
+ }
+
+ public T allocate() {
+ T object = (T)super.allocate();
+ return object;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/Texture.java b/src/com/replica/replicaisland/Texture.java
new file mode 100644
index 0000000..5bfc65b
--- /dev/null
+++ b/src/com/replica/replicaisland/Texture.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Simple container class for textures. Serves as a mapping between Android resource ids and
+ * OpenGL texture names, and also as a placeholder object for textures that may or may not have
+ * been loaded into vram. Objects can cache Texture objects but should *never* cache the texture
+ * name itself, as it may change at any time.
+ */
+public class Texture extends AllocationGuard {
+ public int resource;
+ public int name;
+ public int width;
+ public int height;
+ public boolean loaded;
+
+ public Texture() {
+ super();
+ reset();
+ }
+
+ public void reset() {
+ resource = -1;
+ name = -1;
+ width = 0;
+ height = 0;
+ loaded = false;
+ }
+}
diff --git a/src/com/replica/replicaisland/TextureLibrary.java b/src/com/replica/replicaisland/TextureLibrary.java
new file mode 100644
index 0000000..b21804d
--- /dev/null
+++ b/src/com/replica/replicaisland/TextureLibrary.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.microedition.khronos.opengles.GL10;
+import javax.microedition.khronos.opengles.GL11;
+import javax.microedition.khronos.opengles.GL11Ext;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.opengl.GLU;
+import android.opengl.GLUtils;
+
+/**
+ * The Texture Library manages all textures in the game. Textures are pooled and handed out to
+ * requesting parties via allocateTexture(). However, the texture data itself is not immediately
+ * loaded at that time; it may have already been loaded or it may be loaded in the future via
+ * a call to loadTexture() or loadAllTextures(). This allows Texture objects to be dispersed to
+ * various game systems and while the texture data itself is streamed in or loaded as necessary.
+ */
+public class TextureLibrary extends BaseObject {
+ // Textures are stored in a simple hash. This class implements its own array-based hash rather
+ // than using HashMap for performance.
+ Texture[] mTextureHash;
+ int[] mTextureNameWorkspace;
+ int[] mCropWorkspace;
+ static final int DEFAULT_SIZE = 512;
+ static BitmapFactory.Options sBitmapOptions = new BitmapFactory.Options();
+
+ public TextureLibrary() {
+ super();
+ mTextureHash = new Texture[DEFAULT_SIZE];
+ for (int x = 0; x < mTextureHash.length; x++) {
+ mTextureHash[x] = new Texture();
+ }
+
+ mTextureNameWorkspace = new int[1];
+ mCropWorkspace = new int[4];
+
+ sBitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
+ }
+
+ @Override
+ public void reset() {
+ removeAll();
+ }
+
+ /**
+ * Creates a Texture object that is mapped to the passed resource id. If a texture has already
+ * been allocated for this id, the previously allocated Texture object is returned.
+ * @param resourceID
+ * @return
+ */
+ public Texture allocateTexture(int resourceID) {
+ Texture texture = getTextureByResource(resourceID);
+ if (texture == null) {
+ texture = addTexture(resourceID, -1, 0, 0);
+ }
+
+ return texture;
+ }
+
+ /** Loads a single texture into memory. Does nothing if the texture is already loaded. */
+ public Texture loadTexture(Context context, GL10 gl, int resourceID) {
+ Texture texture = allocateTexture(resourceID);
+ texture = loadBitmap(context, gl, texture);
+ return texture;
+ }
+
+ /** Loads all unloaded textures into OpenGL memory. Already-loaded textures are ignored. */
+ public void loadAll(Context context, GL10 gl) {
+ for (int x = 0; x < mTextureHash.length; x++) {
+ if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded == false) {
+ loadBitmap(context, gl, mTextureHash[x]);
+ }
+ }
+ }
+
+ /** Flushes all textures from OpenGL memory */
+ public void deleteAll(GL10 gl) {
+ for (int x = 0; x < mTextureHash.length; x++) {
+ if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded) {
+ assert mTextureHash[x].name != -1;
+ mTextureNameWorkspace[0] = mTextureHash[x].name;
+ mTextureHash[x].name = -1;
+ mTextureHash[x].loaded = false;
+ gl.glDeleteTextures(1, mTextureNameWorkspace, 0);
+ int error = gl.glGetError();
+ if (error != GL10.GL_NO_ERROR) {
+ DebugLog.d("Texture Delete", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + mTextureHash[x].resource);
+ }
+
+ assert error == GL10.GL_NO_ERROR;
+ }
+ }
+ }
+
+ /** Marks all textures as unloaded */
+ public void invalidateAll() {
+ for (int x = 0; x < mTextureHash.length; x++) {
+ if (mTextureHash[x].resource != -1 && mTextureHash[x].loaded) {
+ mTextureHash[x].name = -1;
+ mTextureHash[x].loaded = false;
+ }
+ }
+ }
+
+ /** Loads a bitmap into OpenGL and sets up the common parameters for 2D texture maps. */
+ protected Texture loadBitmap(Context context, GL10 gl, Texture texture) {
+ assert gl != null;
+ assert context != null;
+ assert texture != null;
+ if (texture.loaded == false && texture.resource != -1) {
+ gl.glGenTextures(1, mTextureNameWorkspace, 0);
+
+ int error = gl.glGetError();
+ if (error != GL10.GL_NO_ERROR) {
+ DebugLog.d("Texture Load 1", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource);
+ }
+
+ assert error == GL10.GL_NO_ERROR;
+
+ int textureName = mTextureNameWorkspace[0];
+
+ gl.glBindTexture(GL10.GL_TEXTURE_2D, textureName);
+
+ error = gl.glGetError();
+ if (error != GL10.GL_NO_ERROR) {
+ DebugLog.d("Texture Load 2", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource);
+ }
+
+ assert error == GL10.GL_NO_ERROR;
+
+ gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
+ gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
+
+ gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
+ gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
+
+ gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); //GL10.GL_REPLACE);
+
+ InputStream is = context.getResources().openRawResource(texture.resource);
+ Bitmap bitmap;
+ try {
+ bitmap = BitmapFactory.decodeStream(is);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ // Ignore.
+ }
+ }
+
+ GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
+
+ error = gl.glGetError();
+ if (error != GL10.GL_NO_ERROR) {
+ DebugLog.d("Texture Load 3", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource);
+ }
+
+ assert error == GL10.GL_NO_ERROR;
+
+ mCropWorkspace[0] = 0;
+ mCropWorkspace[1] = bitmap.getHeight();
+ mCropWorkspace[2] = bitmap.getWidth();
+ mCropWorkspace[3] = -bitmap.getHeight();
+
+ ((GL11) gl).glTexParameteriv(GL10.GL_TEXTURE_2D, GL11Ext.GL_TEXTURE_CROP_RECT_OES,
+ mCropWorkspace, 0);
+
+ texture.name = textureName;
+ texture.width = bitmap.getWidth();
+ texture.height = bitmap.getHeight();
+
+ bitmap.recycle();
+
+ error = gl.glGetError();
+ if (error != GL10.GL_NO_ERROR) {
+ DebugLog.d("Texture Load 4", "GLError: " + error + " (" + GLU.gluErrorString(error) + "): " + texture.resource);
+ }
+
+ assert error == GL10.GL_NO_ERROR;
+
+ texture.loaded = true;
+
+ }
+
+ return texture;
+ }
+
+ public boolean isTextureLoaded(int resourceID) {
+ return getTextureByResource(resourceID) != null;
+ }
+
+ /**
+ * Returns the texture associated with the passed Android resource ID.
+ * @param resourceID The resource ID of a bitmap defined in R.java.
+ * @return An associated Texture object, or null if there is no associated
+ * texture in the library.
+ */
+ public Texture getTextureByResource(int resourceID) {
+ int index = getHashIndex(resourceID);
+ int realIndex = findFirstKey(index, resourceID);
+ Texture texture = null;
+ if (realIndex != -1) {
+ texture = mTextureHash[realIndex];
+ }
+ return texture;
+ }
+
+ private int getHashIndex(int id) {
+ return id % mTextureHash.length;
+ }
+
+ /**
+ * Locates the texture in the hash. This hash uses a simple linear probe chaining mechanism:
+ * if the hash slot is occupied by some other entry, the next empty array index is used.
+ * This is O(n) for the worst case (every slot is a cache miss) but the average case is
+ * constant time.
+ * @param startIndex
+ * @param key
+ * @return
+ */
+ private int findFirstKey(int startIndex, int key) {
+ int index = -1;
+ for (int x = 0; x < mTextureHash.length; x++) {
+ final int actualIndex = (startIndex + x) % mTextureHash.length;
+ if (mTextureHash[actualIndex].resource == key) {
+ index = actualIndex;
+ break;
+ } else if (mTextureHash[actualIndex].resource == -1) {
+ break;
+ }
+ }
+ return index;
+ }
+
+ /** Inserts a texture into the hash */
+ protected Texture addTexture(int id, int name, int width, int height) {
+ int index = findFirstKey(getHashIndex(id), -1);
+ Texture texture = null;
+ assert index != -1;
+
+ if (index != -1) {
+ mTextureHash[index].resource = id;
+ mTextureHash[index].name = name;
+ mTextureHash[index].width = width;
+ mTextureHash[index].height = height;
+ texture = mTextureHash[index];
+ }
+
+ return texture;
+ }
+
+ public void removeAll() {
+ for (int x = 0; x < mTextureHash.length; x++) {
+ mTextureHash[x].reset();
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/TheSourceComponent.java b/src/com/replica/replicaisland/TheSourceComponent.java
new file mode 100644
index 0000000..f3b365b
--- /dev/null
+++ b/src/com/replica/replicaisland/TheSourceComponent.java
@@ -0,0 +1,133 @@
+package com.replica.replicaisland;
+
+import com.replica.replicaisland.GameObject.ActionType;
+
+public class TheSourceComponent extends GameComponent {
+ public final static float SHAKE_TIME = 0.6f;
+ private final static float DIE_TIME = 30.0f;
+ private final static float EXPLOSION_TIME = 0.1f;
+ private final static float SHAKE_MAGNITUDE = 5.0f;
+ private final static float SHAKE_SCALE = 300.0f;
+ private final static float CAMERA_HIT_SHAKE_MAGNITUDE = 3.0f;
+
+ private final static float SINK_SPEED = -20.0f;
+ private float mTimer;
+ private float mExplosionTimer;
+ private float mShakeStartPosition;
+ private ChannelSystem.Channel mChannel;
+ private int mGameEvent;
+ private int mGameEventIndex;
+ private boolean mDead;
+
+ private static ChannelSystem.ChannelBooleanValue sChannelValue = new ChannelSystem.ChannelBooleanValue();
+
+ public TheSourceComponent() {
+ super();
+ reset();
+ setPhase(ComponentPhases.THINK.ordinal());
+ }
+
+ @Override
+ public void reset() {
+ mTimer = 0.0f;
+ mExplosionTimer = 0.0f;
+ mShakeStartPosition = 0.0f;
+ mChannel = null;
+ sChannelValue.value = false;
+ mGameEvent = -1;
+ mGameEventIndex = -1;
+ mDead = false;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ GameObject parentObject = (GameObject)parent;
+ GameObject.ActionType currentAction = parentObject.getCurrentAction();
+
+ CameraSystem camera = sSystemRegistry.cameraSystem;
+
+ if (currentAction == ActionType.HIT_REACT) {
+ if (parentObject.life > 0) {
+ mTimer = SHAKE_TIME;
+ camera.shake(SHAKE_TIME, CAMERA_HIT_SHAKE_MAGNITUDE);
+ mShakeStartPosition = parentObject.getPosition().x;
+ parentObject.setCurrentAction(ActionType.IDLE);
+ currentAction = ActionType.IDLE;
+ } else {
+ parentObject.setCurrentAction(ActionType.DEATH);
+ currentAction = ActionType.DEATH;
+ mTimer = DIE_TIME;
+ mExplosionTimer = EXPLOSION_TIME;
+ if (mChannel != null) {
+ mChannel.value = sChannelValue;
+ sChannelValue.value = true;
+ }
+ mDead = true;
+ }
+
+ }
+
+ mTimer -= timeDelta;
+
+ if (mDead) {
+ // Wait for the player to take the camera back, then steal it!
+ GameObjectManager manager = sSystemRegistry.gameObjectManager;
+
+ if (camera != null && manager != null && camera.getTarget() == manager.getPlayer()) {
+ camera.setTarget(parentObject);
+ }
+
+ final float offset = SINK_SPEED * timeDelta;
+ parentObject.getPosition().y += offset;
+
+ mExplosionTimer -= timeDelta;
+ if (mExplosionTimer < 0.0f) {
+ GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
+ if (factory != null) {
+ float x = ((float)Math.random() - 0.5f) * (parentObject.width * 0.75f);
+ float y = ((float)Math.random() - 0.5f) * (parentObject.height * 0.75f);
+ GameObject object =
+ factory.spawn(GameObjectFactory.GameObjectType.EXPLOSION_GIANT,
+ parentObject.getCenteredPositionX() + x,
+ parentObject.getCenteredPositionY() + y,
+ false);
+ if (object != null) {
+ manager.add(object);
+ }
+ mExplosionTimer = EXPLOSION_TIME;
+ }
+ }
+
+ if (mTimer - timeDelta <= 0.0f) {
+ mTimer = 0.0f;
+ if (mGameEvent != -1) {
+ HudSystem hud = sSystemRegistry.hudSystem;
+ if (hud != null) {
+ hud.startFade(false, 1.5f);
+ hud.sendGameEventOnFadeComplete(mGameEvent, mGameEventIndex);
+ mGameEvent = -1;
+ }
+ }
+ }
+ } else if (mTimer > 0) {
+ // shake
+ float delta = (float)Math.sin(mTimer * SHAKE_SCALE);
+ delta *= SHAKE_MAGNITUDE;
+ parentObject.getPosition().x = mShakeStartPosition + delta;
+ if (mTimer - timeDelta <= 0.0f) {
+ // end one step early and fix the position.
+ mTimer = 0;
+ parentObject.getPosition().x = mShakeStartPosition;
+ }
+ }
+ }
+
+ public void setChannel(ChannelSystem.Channel channel) {
+ mChannel = channel;
+ }
+
+ public void setGameEvent(int event, int index) {
+ mGameEvent = event;
+ mGameEventIndex = index;
+ }
+}
diff --git a/src/com/replica/replicaisland/TiledBackgroundVertexGrid.java b/src/com/replica/replicaisland/TiledBackgroundVertexGrid.java
new file mode 100644
index 0000000..fdcf506
--- /dev/null
+++ b/src/com/replica/replicaisland/TiledBackgroundVertexGrid.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+public class TiledBackgroundVertexGrid extends ScrollableBitmap {
+ private TiledVertexGrid mGrid;
+
+ public TiledBackgroundVertexGrid() {
+ super(null, 0, 0);
+ }
+
+ @Override
+ public void reset() {
+ super.reset();
+ mGrid = null;
+ }
+
+ public void setGrid(TiledVertexGrid grid) {
+ mGrid = grid;
+ }
+
+ @Override
+ public void draw(float x, float y, float scaleX, float scaleY) {
+ if (mGrid != null) {
+ mGrid.draw(x, y, getScrollOriginX(), getScrollOriginY());
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/TiledVertexGrid.java b/src/com/replica/replicaisland/TiledVertexGrid.java
new file mode 100644
index 0000000..7d48003
--- /dev/null
+++ b/src/com/replica/replicaisland/TiledVertexGrid.java
@@ -0,0 +1,204 @@
+package com.replica.replicaisland;
+
+import javax.microedition.khronos.opengles.GL10;
+
+public class TiledVertexGrid extends BaseObject {
+ private static final float GL_MAGIC_OFFSET = 0.375f;
+ private Grid mTileMap;
+ private TiledWorld mWorld;
+ private int mTileWidth;
+ private int mTileHeight;
+
+ private int mWidth;
+ private int mHeight;
+ private Texture mTexture;
+
+ private float mWorldPixelWidth;
+ private float mWorldPixelHeight;
+
+ private int mTilesPerRow;
+ private int mTilesPerColumn;
+
+ public TiledVertexGrid(Texture texture, int width, int height, int tileWidth, int tileHeight) {
+ super();
+ mTileWidth = tileWidth;
+ mTileHeight = tileHeight;
+ mWidth = width;
+ mHeight = height;
+ mTexture = texture;
+ }
+
+ @Override
+ public void reset() {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setWorld(TiledWorld world) {
+ mWorld = world;
+
+ }
+
+ private Grid generateGrid(int width, int height, int startTileX, int startTileY) {
+ final int tileWidth = mTileWidth;
+ final int tileHeight = mTileHeight;
+ final int tilesAcross = width / tileWidth;
+ final int tilesDown = height / tileHeight;
+ final Texture texture = mTexture;
+ final float texelWidth = 1.0f / texture.width;
+ final float texelHeight = 1.0f / texture.height;
+ final int textureTilesAcross = texture.width / tileWidth;
+ final int textureTilesDown = texture.height / tileHeight;
+ final int tilesPerWorldColumn = mWorld.getHeight();
+ final int totalTextureTiles = textureTilesAcross * textureTilesDown;
+ // Check to see if this entire grid is empty tiles. If so, we don't need to do anything.
+ boolean entirelyEmpty = true;
+ for (int tileY = 0; tileY < tilesDown && entirelyEmpty; tileY++) {
+ for (int tileX = 0; tileX < tilesAcross && entirelyEmpty; tileX++) {
+ int tileIndex = mWorld.getTile(startTileX + tileX,
+ (tilesPerWorldColumn - 1 - (startTileY + tileY)));
+ if (tileIndex >= 0) {
+ entirelyEmpty = false;
+ break;
+ }
+ }
+ }
+
+ Grid grid = null;
+ if (!entirelyEmpty) {
+ grid = new Grid(tilesAcross, tilesDown, false);
+ for (int tileY = 0; tileY < tilesDown; tileY++) {
+ for (int tileX = 0; tileX < tilesAcross; tileX++) {
+ final float offsetX = tileX * tileWidth;
+ final float offsetY = tileY * tileHeight;
+ int tileIndex = mWorld.getTile(startTileX + tileX,
+ (tilesPerWorldColumn - 1 - (startTileY + tileY)));
+ if (tileIndex < 0) {
+ tileIndex = totalTextureTiles - 1; // Assume that the last tile is empty.
+ }
+ int textureOffsetX = (tileIndex % textureTilesAcross) * tileWidth;
+ int textureOffsetY = (tileIndex / textureTilesAcross) * tileHeight;
+ if (textureOffsetX < 0 ||
+ textureOffsetX > texture.width - tileWidth ||
+ textureOffsetY < 0 ||
+ textureOffsetY > texture.height - tileHeight) {
+ textureOffsetX = 0;
+ textureOffsetY = 0;
+ }
+ final float u = (textureOffsetX + GL_MAGIC_OFFSET) * texelWidth;
+ final float v = (textureOffsetY + GL_MAGIC_OFFSET) * texelHeight;
+ final float u2 = ((textureOffsetX + tileWidth - GL_MAGIC_OFFSET) * texelWidth);
+ final float v2 = ((textureOffsetY + tileHeight - GL_MAGIC_OFFSET) * texelHeight);
+
+ final float[] p0 = { offsetX, offsetY, 0.0f };
+ final float[] p1 = { offsetX + tileWidth, offsetY, 0.0f };
+ final float[] p2 = { offsetX, offsetY + tileHeight, 0.0f };
+ final float[] p3 = { offsetX + tileWidth, offsetY + tileHeight, 0.0f };
+ final float[] uv0 = { u, v2 };
+ final float[] uv1 = { u2, v2 };
+ final float[] uv2 = { u, v };
+ final float[] uv3 = { u2, v };
+
+ final float[][] positions = { p0, p1, p2, p3 };
+ final float[][] uvs = { uv0, uv1, uv2, uv3 };
+
+ grid.set(tileX, tileY, positions, uvs);
+
+ }
+ }
+ }
+ return grid;
+ }
+
+ public void draw(float x, float y, float scrollOriginX, float scrollOriginY) {
+ TiledWorld world = mWorld;
+ GL10 gl = OpenGLSystem.getGL();
+ if (mTileMap == null && world != null && gl != null && mTexture != null) {
+ final int tilesAcross = mWorld.getWidth();
+ final int tilesDown = mWorld.getHeight();
+
+ mWorldPixelWidth = mWorld.getWidth() * mTileWidth;
+ mWorldPixelHeight = mWorld.getHeight() * mTileHeight;
+ mTilesPerRow = tilesAcross;
+ mTilesPerColumn = tilesDown;
+
+
+ BufferLibrary bufferLibrary = sSystemRegistry.bufferLibrary;
+
+ Grid grid = generateGrid((int)mWorldPixelWidth, (int)mWorldPixelHeight, 0, 0);
+ mTileMap = grid;
+ if (grid != null) {
+ bufferLibrary.add(grid);
+ if (sSystemRegistry.contextParameters.supportsVBOs) {
+ grid.generateHardwareBuffers(gl);
+ }
+ }
+
+ }
+
+ final Grid tileMap = mTileMap;
+ if (tileMap != null) {
+ final Texture texture = mTexture;
+ if (gl != null && texture != null) {
+
+ int originX = (int) (x - scrollOriginX);
+ int originY = (int) (y - scrollOriginY);
+
+
+ final float worldPixelWidth = mWorldPixelWidth;
+
+ final float percentageScrollRight =
+ scrollOriginX != 0.0f ? scrollOriginX / worldPixelWidth : 0.0f;
+ final float tileSpaceX = percentageScrollRight * mTilesPerRow;
+ final int leftTile = (int)tileSpaceX;
+
+ // calculate the top tile index
+ final float worldPixelHeight = mWorldPixelHeight;
+
+ final float percentageScrollUp =
+ scrollOriginY != 0.0f ? scrollOriginY / worldPixelHeight : 0.0f;
+ final float tileSpaceY = percentageScrollUp * mTilesPerColumn;
+ final int bottomTile = (int)tileSpaceY;
+
+ // calculate any sub-tile slop that our scroll position may require.
+ final int horizontalSlop = ((tileSpaceX - leftTile) * mTileWidth) > 0 ? 1 : 0;
+ final int verticalSlop = ((tileSpaceY - bottomTile) * mTileHeight) > 0 ? 1 : 0;
+
+
+ OpenGLSystem.bindTexture(GL10.GL_TEXTURE_2D, texture.name);
+ tileMap.beginDrawingStrips(gl, true);
+
+ final int horzTileCount = (int)Math.ceil((float)mWidth / mTileWidth);
+ final int vertTileCount = (int)Math.ceil((float)mHeight / mTileHeight);
+ // draw vertex strips
+ final int startX = leftTile;
+ final int startY = bottomTile;
+ final int endX = startX + horizontalSlop + horzTileCount;
+ final int endY = startY + verticalSlop + vertTileCount;
+
+ gl.glPushMatrix();
+ gl.glLoadIdentity();
+ gl.glTranslatef(
+ originX,
+ originY,
+ 0.0f);
+
+
+ final int indexesPerTile = 6;
+ final int indexesPerRow = mTilesPerRow * indexesPerTile;
+ final int startOffset = (startX * indexesPerTile);
+ final int count = (endX - startX) * indexesPerTile;
+ for (int tileY = startY; tileY < endY && tileY < mTilesPerColumn; tileY++) {
+ final int row = tileY * indexesPerRow;
+ tileMap.drawStrip(gl, true, row + startOffset, count);
+ }
+
+ gl.glPopMatrix();
+
+ Grid.endDrawing(gl);
+
+ }
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/TiledWorld.java b/src/com/replica/replicaisland/TiledWorld.java
new file mode 100644
index 0000000..215d0b0
--- /dev/null
+++ b/src/com/replica/replicaisland/TiledWorld.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import java.io.IOException;
+import java.io.InputStream;
+import android.content.res.AssetManager;
+
+/**
+ * TiledWorld manages a 2D map of tile indexes that define a "world" of tiles. These may be
+ * foreground or background layers in a scrolling game, or a layer of collision tiles, or some other
+ * type of tile map entirely. The TiledWorld maps xy positions to tile indices and also handles
+ * deserialization of tilemap files.
+ */
+public class TiledWorld extends AllocationGuard {
+ private int[][] mTilesArray;
+ private int mRowCount;
+ private int mColCount;
+ private byte[] mWorkspaceBytes;
+
+ public TiledWorld(int cols, int rows) {
+ super();
+ mTilesArray = new int[cols][rows];
+ mRowCount = rows;
+ mColCount = cols;
+
+ for (int x = 0; x < cols; x++) {
+ for (int y = 0; y < rows; y++) {
+ mTilesArray[x][y] = -1;
+ }
+ }
+
+ mWorkspaceBytes = new byte[4];
+
+ calculateSkips();
+ }
+
+ public TiledWorld(InputStream stream) {
+ super();
+ mWorkspaceBytes = new byte[4];
+ parseInput(stream);
+ calculateSkips();
+ }
+
+ public int getTile(int x, int y) {
+ int result = -1;
+ if (x >= 0 && x < mColCount && y >= 0 && y < mRowCount) {
+ result = mTilesArray[x][y];
+ }
+ return result;
+ }
+
+ // Builds a tiled world from a simple map file input source. The map file format is as follows:
+ // First byte: signature. Must always be decimal 42.
+ // Second byte: width of the world in tiles.
+ // Third byte: height of the world in tiles.
+ // Subsequent bytes: actual tile data in column-major order.
+ // TODO: add a checksum in here somewhere.
+ protected boolean parseInput(InputStream stream) {
+ boolean success = false;
+ AssetManager.AssetInputStream byteStream = (AssetManager.AssetInputStream) stream;
+ int signature;
+ try {
+ signature = (byte)byteStream.read();
+ if (signature == 42) {
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final int width = Utils.byteArrayToInt(mWorkspaceBytes);
+ byteStream.read(mWorkspaceBytes, 0, 4);
+ final int height = Utils.byteArrayToInt(mWorkspaceBytes);
+
+ final int totalTiles = width * height;
+ final int bytesRemaining = byteStream.available();
+ assert bytesRemaining >= totalTiles;
+ if (bytesRemaining >= totalTiles) {
+ mTilesArray = new int[width][height];
+ mRowCount = height;
+ mColCount = width;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ mTilesArray[x][y] = (byte)byteStream.read();
+ }
+ }
+ success = true;
+ }
+ }
+
+ } catch (IOException e) {
+ //TODO: figure out the best way to deal with this. Assert?
+ }
+
+ return success;
+ }
+
+ protected void calculateSkips() {
+ int emptyTileCount = 0;
+ for (int y = mRowCount - 1; y >= 0; y--) {
+ for (int x = mColCount - 1; x >= 0; x--) {
+ if (mTilesArray[x][y] < 0) {
+ emptyTileCount++;
+ mTilesArray[x][y] = -emptyTileCount;
+ } else {
+ emptyTileCount = 0;
+ }
+ }
+ }
+ }
+
+ public final int getWidth() {
+ return mColCount;
+ }
+
+ public final int getHeight() {
+ return mRowCount;
+ }
+
+ public final int[][] getTiles() {
+ return mTilesArray;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/TimeSystem.java b/src/com/replica/replicaisland/TimeSystem.java
new file mode 100644
index 0000000..c4306b8
--- /dev/null
+++ b/src/com/replica/replicaisland/TimeSystem.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Maintains a canonical time step, in seconds, for the entire game engine. This time step
+ * represents real changes in time but is only updated once per frame.
+ */
+// TODO: time distortion effects could go here, or they could go into a special object manager.
+public class TimeSystem extends BaseObject {
+ private float mGameTime;
+ private float mRealTime;
+ private float mFreezeDelay;
+ private float mGameFrameDelta;
+ private float mRealFrameDelta;
+
+ private float mTargetScale;
+ private float mScaleDuration;
+ private float mScaleStartTime;
+ private boolean mEaseScale;
+
+ private static final float EASE_DURATION = 0.5f;
+
+ public TimeSystem() {
+ super();
+ reset();
+ }
+
+ @Override
+ public void reset() {
+ mGameTime = 0.0f;
+ mRealTime = 0.0f;
+ mFreezeDelay = 0.0f;
+ mGameFrameDelta = 0.0f;
+ mRealFrameDelta = 0.0f;
+
+ mTargetScale = 1.0f;
+ mScaleDuration = 0.0f;
+ mScaleStartTime = 0.0f;
+ mEaseScale = false;
+ }
+
+ @Override
+ public void update(float timeDelta, BaseObject parent) {
+ mRealTime += timeDelta;
+ mRealFrameDelta = timeDelta;
+
+ if (mFreezeDelay > 0.0f) {
+ mFreezeDelay -= timeDelta;
+ mGameFrameDelta = 0.0f;
+ } else {
+ float scale = 1.0f;
+ if (mScaleStartTime > 0.0f) {
+ final float scaleTime = mRealTime - mScaleStartTime;
+ if (scaleTime > mScaleDuration) {
+ mScaleStartTime = 0;
+ } else {
+ if (mEaseScale) {
+ if (scaleTime <= EASE_DURATION) {
+ // ease in
+ scale = Lerp.ease(1.0f, mTargetScale, EASE_DURATION, scaleTime);
+ } else if (mScaleDuration - scaleTime < EASE_DURATION) {
+ // ease out
+ final float easeOutTime = EASE_DURATION - (mScaleDuration - scaleTime);
+ scale = Lerp.ease(mTargetScale, 1.0f, EASE_DURATION, easeOutTime);
+ } else {
+ scale = mTargetScale;
+ }
+ } else {
+ scale = mTargetScale;
+ }
+ }
+ }
+
+ mGameTime += (timeDelta * scale);
+ mGameFrameDelta = (timeDelta * scale);
+ }
+
+
+ }
+
+ public float getGameTime() {
+ return mGameTime;
+ }
+
+ public float getRealTime() {
+ return mRealTime;
+ }
+
+ public float getFrameDelta() {
+ return mGameFrameDelta;
+ }
+
+ public float getRealTimeFrameDelta() {
+ return mRealFrameDelta;
+ }
+
+ public void freeze(float seconds) {
+ mFreezeDelay = seconds;
+ }
+
+ public void appyScale(float scaleFactor, float duration, boolean ease) {
+ mTargetScale = scaleFactor;
+ mScaleDuration = duration;
+ mEaseScale = ease;
+ if (mScaleStartTime <= 0.0f) {
+ mScaleStartTime = mRealTime;
+ }
+ }
+
+}
diff --git a/src/com/replica/replicaisland/Utils.java b/src/com/replica/replicaisland/Utils.java
new file mode 100644
index 0000000..a84e6d4
--- /dev/null
+++ b/src/com/replica/replicaisland/Utils.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/** A collection of miscellaneous utility functions. */
+public class Utils {
+ private static final float EPSILON = 0.0001f;
+
+ public final static boolean close(float a, float b) {
+ return close(a, b, EPSILON);
+ }
+
+ public final static boolean close(float a, float b, float epsilon) {
+ return Math.abs(a - b) < epsilon;
+ }
+
+ public final static int sign(float a) {
+ if (a >= 0.0f) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+
+ public final static int clamp(int value, int min, int max) {
+ int result = value;
+ if (min == max) {
+ if (value != min) {
+ result = min;
+ }
+ } else if (min < max) {
+ if (value < min) {
+ result = min;
+ } else if (value > max) {
+ result = max;
+ }
+ } else {
+ result = clamp(value, max, min);
+ }
+
+ return result;
+ }
+
+ public final static int byteArrayToInt(byte[] b) {
+ if (b.length != 4) {
+ return 0;
+ }
+
+ // Same as DataInputStream's 'readInt' method
+ /*int i = (((b[0] & 0xff) << 24) | ((b[1] & 0xff) << 16) | ((b[2] & 0xff) << 8)
+ | (b[3] & 0xff));*/
+
+ // little endian
+ int i = (((b[3] & 0xff) << 24) | ((b[2] & 0xff) << 16) | ((b[1] & 0xff) << 8)
+ | (b[0] & 0xff));
+
+ return i;
+ }
+
+ public final static float byteArrayToFloat(byte[] b) {
+
+ // intBitsToFloat() converts bits as follows:
+ /*
+ int s = ((i >> 31) == 0) ? 1 : -1;
+ int e = ((i >> 23) & 0xff);
+ int m = (e == 0) ? (i & 0x7fffff) << 1 : (i & 0x7fffff) | 0x800000;
+ */
+
+ return Float.intBitsToFloat(byteArrayToInt(b));
+ }
+
+ public final static float framesToTime(int framesPerSecond, int frameCount) {
+ return (1.0f / framesPerSecond) * frameCount;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/Vector2.java b/src/com/replica/replicaisland/Vector2.java
new file mode 100644
index 0000000..c3f7958
--- /dev/null
+++ b/src/com/replica/replicaisland/Vector2.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * Simple 2D vector class. Handles basic vector math for 2D vectors.
+ */
+public final class Vector2 extends AllocationGuard {
+ public float x;
+ public float y;
+
+ public static final Vector2 ZERO = new Vector2(0, 0);
+
+ public Vector2() {
+ super();
+ }
+
+ public Vector2(float xValue, float yValue) {
+ set(xValue, yValue);
+ }
+
+ public Vector2(Vector2 other) {
+ set(other);
+ }
+
+ public final void add(Vector2 other) {
+ x += other.x;
+ y += other.y;
+ }
+
+ public final void add(float otherX, float otherY) {
+ x += otherX;
+ y += otherY;
+ }
+
+ public final void subtract(Vector2 other) {
+ x -= other.x;
+ y -= other.y;
+ }
+
+ public final void multiply(float magnitude) {
+ x *= magnitude;
+ y *= magnitude;
+ }
+
+ public final void multiply(Vector2 other) {
+ x *= other.x;
+ y *= other.y;
+ }
+
+ public final void divide(float magnitude) {
+ if (magnitude != 0.0f) {
+ x /= magnitude;
+ y /= magnitude;
+ }
+ }
+
+ public final void set(Vector2 other) {
+ x = other.x;
+ y = other.y;
+ }
+
+ public final void set(float xValue, float yValue) {
+ x = xValue;
+ y = yValue;
+ }
+
+ public final float dot(Vector2 other) {
+ return (x * other.x) + (y * other.y);
+ }
+
+ public final float length() {
+ return (float) Math.sqrt(length2());
+ }
+
+ public final float length2() {
+ return (x * x) + (y * y);
+ }
+
+ public final float distance2(Vector2 other) {
+ float dx = x - other.x;
+ float dy = y - other.y;
+ return (dx * dx) + (dy * dy);
+ }
+
+ public final float normalize() {
+ final float magnitude = length();
+
+ // TODO: I'm choosing safety over speed here.
+ if (magnitude != 0.0f) {
+ x /= magnitude;
+ y /= magnitude;
+ }
+
+ return magnitude;
+ }
+
+ public final void zero() {
+ set(0.0f, 0.0f);
+ }
+
+ public final void flipHorizontal(float aboutWidth) {
+ x = (aboutWidth - x);
+ }
+
+ public final void flipVertical(float aboutHeight) {
+ y = (aboutHeight - y);
+ }
+}
diff --git a/src/com/replica/replicaisland/VectorPool.java b/src/com/replica/replicaisland/VectorPool.java
new file mode 100644
index 0000000..9d0db3b
--- /dev/null
+++ b/src/com/replica/replicaisland/VectorPool.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+/**
+ * A pool of 2D vectors.
+ */
+public class VectorPool extends TObjectPool<Vector2> {
+
+ public VectorPool() {
+ super();
+ }
+
+ @Override
+ protected void fill() {
+ for (int x = 0; x < getSize(); x++) {
+ getAvailable().add(new Vector2());
+ }
+ }
+
+ @Override
+ public void release(Object entry) {
+ ((Vector2)entry).zero();
+ super.release(entry);
+ }
+
+ /** Allocates a vector and assigns the value of the passed source vector to it. */
+ public Vector2 allocate(Vector2 source) {
+ Vector2 entry = super.allocate();
+ entry.set(source);
+ return entry;
+ }
+
+}
diff --git a/src/com/replica/replicaisland/VibrationSystem.java b/src/com/replica/replicaisland/VibrationSystem.java
new file mode 100644
index 0000000..5a80117
--- /dev/null
+++ b/src/com/replica/replicaisland/VibrationSystem.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.replica.replicaisland;
+
+import android.os.Vibrator;
+import android.content.Context;
+
+/** A system for accessing the Android vibrator. Note that this system requires the app's
+ * AndroidManifest.xml to contain permissions for the Vibrator service.
+ */
+public class VibrationSystem extends BaseObject {
+
+ public VibrationSystem() {
+ super();
+ }
+
+ @Override
+ public void reset() {
+ }
+
+ public void vibrate(float seconds) {
+ ContextParameters params = sSystemRegistry.contextParameters;
+ if (params != null && params.context != null) {
+ Vibrator vibrator = (Vibrator)params.context.getSystemService(Context.VIBRATOR_SERVICE);
+ if (vibrator != null) {
+ vibrator.vibrate((int)(seconds * 1000));
+ }
+ }
+ }
+}
diff --git a/src/com/replica/replicaisland/YesNoDialogPreference.java b/src/com/replica/replicaisland/YesNoDialogPreference.java
new file mode 100644
index 0000000..c65170e
--- /dev/null
+++ b/src/com/replica/replicaisland/YesNoDialogPreference.java
@@ -0,0 +1,39 @@
+package com.replica.replicaisland;
+
+import android.content.Context;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+
+public class YesNoDialogPreference extends DialogPreference {
+ private YesNoDialogListener mListener;
+
+ public abstract interface YesNoDialogListener {
+ public abstract void onDialogClosed(boolean positiveResult);
+ }
+
+ public YesNoDialogPreference(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.attr.yesNoPreferenceStyle);
+ // TODO Auto-generated constructor stub
+ }
+
+ public YesNoDialogPreference(Context context, AttributeSet attrs,
+ int defStyle) {
+ super(context, attrs, defStyle);
+ // TODO Auto-generated constructor stub
+ }
+
+ public YesNoDialogPreference(Context context) {
+ this(context, null);
+ }
+
+ public void setListener(YesNoDialogListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ if (mListener != null) {
+ mListener.onDialogClosed(positiveResult);
+ }
+ }
+}
diff --git a/tools/ExtractPoints.js b/tools/ExtractPoints.js
new file mode 100644
index 0000000..896cf5f
--- /dev/null
+++ b/tools/ExtractPoints.js
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /**
+ * @fileoverview This implements a Photoshop script that can be used to generate
+ * collision information for the AndouKun game engine. This tool walks over
+ * each path in the current document and generates a list of edges and normals
+ * in a new document. It is intended to be used on a file containing
+ * graphical representations of the collision tiles used by the engine. Each
+ * path in the file must be closed and may not contain any curved points
+ * (the tool assumes that the line between any two points in a given path is
+ * straight). Only one shape may be contained per path layer (each path must go
+ * in its own path layer). This tool can also output a graphical version of its
+ * edge calculation for debugging purposes.
+ */
+
+/* If set to true, the computation will be rendered graphically to the output
+ file */
+var drawOutput = false;
+/* If true, the computation will be printed in a text layer in the
+ output file.*/
+var printOutput = true;
+
+// Back up the ruler units that this file uses before switching to pixel units.
+var defaultRulerUnits = app.preferences.rulerUnits;
+app.preferences.rulerUnits = Units.PIXELS;
+
+var tileSizeX = prompt("Tile pixel width:");
+var tileSizeY = prompt("Tile pixel height:");
+
+var documentWidth = app.activeDocument.width;
+var documentHeight = app.activeDocument.height;
+
+var tilesPerRow = documentWidth / tileSizeX;
+var tilesPerColumn = documentHeight / tileSizeY;
+
+var tiles = new Array();
+tiles.length = tilesPerRow * tilesPerColumn;
+
+// Walk the list of paths and extract edges and normals. Store these in
+// an array by tile.
+var pathList = app.activeDocument.pathItems;
+for (pathIndex = 0; pathIndex < pathList.length; pathIndex++) {
+ var main_path = pathList[pathIndex];
+ if (main_path) {
+ var itemList = main_path.subPathItems;
+ if (!itemList) {
+ alert("Path has no sub items!");
+ } else {
+ for (var x = 0; x < itemList.length; x++) {
+ var item = itemList[x];
+ var points = item.pathPoints;
+ var tile = new Object;
+ tile.edges = new Array();
+
+ var totalX = 0;
+ var totalY = 0;
+ for (var y = 0; y < points.length; y++) {
+ var firstPoint = points[y];
+ var lastPoint = points[(y + 1) % points.length];
+
+ var edge = new Object;
+
+ edge.startX = firstPoint.anchor[0];
+ edge.startY = firstPoint.anchor[1];
+
+ edge.endX = lastPoint.anchor[0];
+ edge.endY = lastPoint.anchor[1];
+
+ var normalX = -(edge.endY - edge.startY);
+ var normalY = edge.endX - edge.startX;
+
+ var normalLength = Math.sqrt((normalX * normalX) + (normalY * normalY));
+ normalX /= normalLength;
+ normalY /= normalLength;
+
+ edge.normalX = normalX;
+ edge.normalY = normalY;
+
+ if (normalX == 0 && normalY == 0) {
+ alert("Zero length normal calculated at path " + pathIndex);
+ }
+
+ var normalLength2 = Math.sqrt((normalX * normalX) + (normalY * normalY));
+ if (normalLength2 > 1 || normalLength2 < 0.9) {
+ alert("Normal of invalid length (" + normalLength2 + ") found at path " + pathIndex);
+ }
+
+ totalX += edge.endX;
+ totalY += edge.endY;
+
+ var width = edge.endX - edge.startX;
+ var height = edge.endY - edge.startY;
+
+ edge.centerX = edge.endX - (width / 2);
+ edge.centerY = edge.endY - (height / 2);
+
+ tile.edges.push(edge);
+ }
+
+ totalX /= points.length;
+ totalY /= points.length;
+ tile.centerX = totalX;
+ tile.centerY = totalY;
+
+ var column = Math.floor(tile.centerX / tileSizeX);
+ var row = Math.floor(tile.centerY / tileSizeY);
+
+ tile.xOffset = column * tileSizeX;
+ tile.yOffset = row * tileSizeY;
+
+ tile.centerX -= tile.xOffset;
+ tile.centerY -= tile.yOffset;
+
+ var tileIndex = Math.floor(row * tilesPerRow + column);
+ tiles[tileIndex] = tile;
+
+ }
+ }
+ }
+}
+
+var outputString = "";
+
+// For each tile print the edges to a string.
+for (var x = 0; x < tiles.length; x++) {
+ if (tiles[x]) {
+ var tile = tiles[x];
+ for (var y = 0; y < tile.edges.length; y++) {
+ var edge = tile.edges[y];
+
+ // convert to tile space
+ edge.startX -= tile.xOffset;
+ edge.startY -= tile.yOffset;
+ edge.endX -= tile.xOffset;
+ edge.endY -= tile.yOffset;
+ edge.centerX -= tile.xOffset;
+ edge.centerY -= tile.yOffset;
+
+ // The normals that we calculated previously might be facing the wrong
+ // direction. Detect this case and correct it by checking to see if
+ // adding the normal to a point on the edge moves the point closer or
+ // further from the center of the shape.
+ if (Math.abs(edge.centerX - tile.centerX) >
+ Math.abs((edge.centerX + edge.normalX) - tile.centerX)) {
+ edge.normalX *= -1;
+ edge.normalY *= -1;
+ }
+
+ if (Math.abs(edge.centerY - tile.centerY) >
+ Math.abs((edge.centerY + edge.normalY) - tile.centerY)) {
+ edge.normalX *= -1;
+ edge.normalY *= -1;
+ }
+
+
+ // Convert to left-handed GL space (the origin is at the bottom-left).
+ edge.normalY *= -1;
+ edge.startY = tileSizeY - edge.startY;
+ edge.endY = tileSizeY - edge.endY;
+ edge.centerY = tileSizeY - edge.centerY;
+
+ outputString += x + ":" + Math.floor(edge.startX) + "," +
+ Math.floor(edge.startY) + ":" + Math.floor(edge.endX) + "," +
+ Math.floor(edge.endY) + ":" + edge.normalX + "," + edge.normalY +
+ "\r";
+ }
+ }
+}
+
+
+if (outputString.length > 0) {
+
+ var newDoc = app.documents.add(600, 700, 72.0, "Edge Output",
+ NewDocumentMode.RGB);
+
+ if (drawOutput) {
+ // Render the edges and normals to the new document.
+ var pathLayer = newDoc.artLayers.add();
+ newDoc.activeLayer = pathLayer;
+
+ // draw the edges to make sure everything works
+ var black = new SolidColor;
+ black.rgb.red = 0;
+ black.rgb.blue = 0;
+ black.rgb.green = 0;
+
+ var redColor = new SolidColor;
+ redColor.rgb.red = 255;
+ redColor.rgb.blue = 0;
+ redColor.rgb.green = 0;
+
+ var greenColor = new SolidColor;
+ greenColor.rgb.red = 0;
+ greenColor.rgb.blue = 0;
+ greenColor.rgb.green = 255;
+
+ var blueColor = new SolidColor;
+ blueColor.rgb.red = 0;
+ blueColor.rgb.blue = 255;
+ blueColor.rgb.green = 0;
+
+ var lineIndex = 0;
+ for (var x = 0; x < tiles.length; x++) {
+ if (tiles[x]) {
+ var tile = tiles[x];
+ var lineArray = new Array();
+ var offsetX = Math.floor(x % tilesPerRow) * tileSizeX;
+ var offsetY = Math.floor(x / tilesPerRow) * tileSizeY;
+
+ for (var y = 0; y < tile.edges.length; y++) {
+ var edge = tile.edges[y];
+
+ lineArray[y] = Array(offsetX + edge.startX, offsetY + edge.startY);
+ }
+
+ // I tried to do this by stroking paths, but the documentation
+ // provided by Adobe is faulty (their sample code doesn't run). The
+ // same thing can be accomplished with selections instead.
+ newDoc.selection.select(lineArray);
+ newDoc.selection.stroke(black, 2);
+
+ for (var y = 0; y < tile.edges.length; y++) {
+ var edge = tile.edges[y];
+
+ var normalX = Math.round(tile.centerX +
+ (edge.normalX * (tileSizeX / 2)));
+ var normalY = Math.round(tile.centerY +
+ (edge.normalY * (tileSizeY / 2)));
+
+ var tileCenterArray = new Array();
+ tileCenterArray[0] = new Array(offsetX + tile.centerX - 1,
+ offsetY + tile.centerY - 1);
+ tileCenterArray[1] = new Array(offsetX + tile.centerX - 1,
+ offsetY + tile.centerY + 1);
+ tileCenterArray[2] = new Array(offsetX + tile.centerX + 1,
+ offsetY + tile.centerY + 1);
+ tileCenterArray[3] = new Array(offsetX + tile.centerX + 1,
+ offsetY + tile.centerY - 1);
+ tileCenterArray[4] = new Array(offsetX + normalX - 1,
+ offsetY + normalY - 1);
+ tileCenterArray[5] = new Array(offsetX + normalX + 1,
+ offsetY + normalY + 1);
+ tileCenterArray[6] = new Array(offsetX + tile.centerX,
+ offsetY + tile.centerY);
+
+ newDoc.selection.select(tileCenterArray);
+ newDoc.selection.fill(redColor);
+
+ var centerArray = new Array();
+ centerArray[0] = new Array(offsetX + edge.centerX - 1,
+ offsetY + edge.centerY - 1);
+ centerArray[1] = new Array(offsetX + edge.centerX - 1,
+ offsetY + edge.centerY + 1);
+ centerArray[2] = new Array(offsetX + edge.centerX + 1,
+ offsetY + edge.centerY + 1);
+ centerArray[3] = new Array(offsetX + edge.centerX + 1,
+ offsetY + edge.centerY - 1);
+
+ newDoc.selection.select(centerArray);
+ newDoc.selection.fill(greenColor);
+
+ }
+
+ }
+ }
+ }
+
+ if (printOutput) {
+ var textLayer = newDoc.artLayers.add();
+ textLayer.kind = LayerKind.TEXT;
+ textLayer.textItem.contents = outputString;
+ }
+}
+
+preferences.rulerUnits = defaultRulerUnits;
+
+// Convenience function for clamping negative values to zero. Trying to select
+// areas outside the canvas causes Bad Things.
+function clamp(input) {
+ if (input < 0) {
+ return 0;
+ }
+ return input;
+}