diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 7ac24c7..9a631f9 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -9,6 +9,7 @@
+
diff --git a/app/build.gradle b/app/build.gradle
index a5e5682..c450179 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,7 +10,6 @@ android {
targetSdkVersion 24
versionCode 1
versionName "1.0"
- testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
@@ -25,8 +24,8 @@ android {
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
- compile "org.jetbrains.anko:anko-common:$anko_version"
compile 'com.android.support:appcompat-v7:24.0.0'
+ compile "org.jetbrains.anko:anko-common:$anko_version"
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testCompile 'junit:junit:4.12'
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 0231f27..d5799fa 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,6 +1,6 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/java/org/frice/MainActivity.kt b/app/src/main/java/org/frice/MainActivity.kt
new file mode 100644
index 0000000..f220646
--- /dev/null
+++ b/app/src/main/java/org/frice/MainActivity.kt
@@ -0,0 +1,4 @@
+package org.frice
+
+class MainActivity : Game() {
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..ed7de28
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,14 @@
+
+
+
+
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8542005
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/frice/.gitignore b/frice/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/frice/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/frice/build.gradle b/frice/build.gradle
new file mode 100644
index 0000000..67e31e5
--- /dev/null
+++ b/frice/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 24
+ buildToolsVersion "24.0.0"
+
+ defaultConfig {
+ minSdkVersion 16
+ targetSdkVersion 24
+ versionCode 1
+ versionName "0.1"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:24.0.0'
+ compile "org.jetbrains.anko:anko-common:$anko_version"
+ compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ testCompile 'junit:junit:4.12'
+}
diff --git a/frice/proguard-rules.pro b/frice/proguard-rules.pro
new file mode 100644
index 0000000..924494c
--- /dev/null
+++ b/frice/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in D:\ADE\sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/frice/src/main/AndroidManifest.xml b/frice/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d69b14e
--- /dev/null
+++ b/frice/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/frice/src/main/kotlin/org/frice/android/Game.kt b/frice/src/main/kotlin/org/frice/android/Game.kt
new file mode 100644
index 0000000..198afe3
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/Game.kt
@@ -0,0 +1,380 @@
+package org.frice.android
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.graphics.RectF
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.view.MotionEvent
+import android.view.View
+import org.frice.android.obj.AbstractObject
+import org.frice.android.obj.FObject
+import org.frice.android.obj.PhysicalObject
+import org.frice.android.obj.button.FButton
+import org.frice.android.obj.button.FText
+import org.frice.android.obj.button.SimpleButton
+import org.frice.android.obj.effects.LineEffect
+import org.frice.android.resource.graphics.ColorResource
+import org.frice.android.utils.graphics.shape.FOval
+import org.frice.android.utils.graphics.shape.FRectangle
+import org.frice.android.utils.message.error.FatalError
+import org.frice.android.utils.message.log.FLog
+import org.frice.game.obj.sub.ShapeObject
+import org.frice.game.resource.FResource
+import org.frice.game.utils.misc.forceRun
+import org.frice.game.utils.misc.loopIf
+import org.frice.game.utils.time.FTimeListener
+import org.frice.game.utils.time.FTimer
+import java.util.*
+import kotlin.concurrent.thread
+
+
+/**
+ * First game class(not for you)
+ *
+ * Standard library, mainly for GUI.
+ * some other library is in @see
+ * The base game class.
+ * this class do rendering, and something which are invisible to
+ * game developer.
+ *
+ * Created by ice1000 on 2016/10/6.
+ * @author ice1000
+ */
+open class Game : AppCompatActivity() {
+
+ protected @JvmField val objects = LinkedList()
+ protected @JvmField val objectDeleteBuffer = ArrayList()
+ protected @JvmField val objectAddBuffer = ArrayList()
+
+ protected @JvmField val timeListeners = LinkedList()
+ protected @JvmField val timeListenerDeleteBuffer = ArrayList()
+ protected @JvmField val timeListenerAddBuffer = ArrayList()
+
+ protected @JvmField val texts = LinkedList()
+ protected @JvmField val textDeleteBuffer = ArrayList()
+ protected @JvmField val textAddBuffer = ArrayList()
+
+ /**
+ * if paused, main window will not call `onRefresh()`.
+ */
+ var paused = false
+
+ /**
+ * not implemented yet.
+ * currently it's same as paused.
+ */
+ var stopped = false
+
+ /**
+ * background resource (don't setBackground, please use `setBack()` instead.)
+ */
+ var back: FResource = ColorResource.BLACK
+ var debug = true
+
+ /**
+ * a general purpose instance for generating random numbers
+ */
+ val random = Random()
+
+ /**
+ * if true, the engine will collect all objects which are invisible from game window.
+ */
+ var autoGC = true
+
+ /**
+ * if true, there will be a fps calculating on the bottom-left side of window.
+ */
+ var showFPS = true
+
+ var loseFocus = false
+ protected set
+
+ var loseFocusChangeColor = true
+
+ private val refresh = FTimer(30)
+
+ private var fpsCounter = 0
+ private var fpsDisplay = 0
+ private lateinit var fpsTimer: FTimer
+
+ private lateinit var canvas: FriceCanvas
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ canvas = FriceCanvas(this)
+ fpsTimer = FTimer(1000)
+ setContentView(canvas)
+ onInit()
+ thread {
+ FLog.v("Engine start!")
+ loopIf(!paused && !stopped && refresh.ended()) {
+ forceRun {
+ onRefresh()
+ timeListeners.forEach { it.check() }
+ canvas.invalidate()
+ ++fpsCounter
+ if (fpsTimer.ended()) {
+ fpsDisplay = fpsCounter
+ fpsCounter = 0
+ }
+ }
+ }
+ }
+ }
+
+ protected open fun onInit() = Unit
+ protected open fun onRefresh() = Unit
+ protected open fun onClick() = Unit
+ protected open fun onTouch(event: MotionEvent) = Unit
+ protected open fun customDraw(canvas: Canvas) = Unit
+
+ /**
+ * adds objects
+ *
+ * @param objs as a collection
+ */
+ infix fun addObjects(objs: Collection) = addObjects(objs.toTypedArray())
+
+ /**
+ * adds objects
+ *
+ * @param objs as an array
+ */
+ infix fun addObjects(objs: Array) = objs.forEach { o -> addObject(o) }
+
+ /**
+ * adds an object to game, to be shown on game window.
+ */
+ infix fun addObject(obj: AbstractObject) {
+ if (obj is FText) textAddBuffer.add(obj)
+ else objectAddBuffer.add(obj)
+ }
+
+ /**
+ * clears all objects.
+ * this method is safe.
+ */
+ fun clearObjects() {
+ objectDeleteBuffer.addAll(objects)
+ textDeleteBuffer.addAll(texts)
+ }
+
+
+ /**
+ * removes objects.
+ * this method is safe.
+ *
+ * @param objs will remove objects which is equal to them, as an array.
+ */
+ infix fun removeObjects(objs: Array) = objs.forEach { o -> objectDeleteBuffer.add(o) }
+
+ /**
+ * removes objects.
+ * this method is safe.
+ *
+ * @param objs will remove objects which is equal to them, as a collection.
+ */
+ infix fun removeObjects(objs: Collection) = removeObjects(objs.toTypedArray())
+
+ /**
+ * removes single object.
+ * this method is safe.
+ *
+ * @param obj will remove objects which is equal to it.
+ */
+ infix fun removeObject(obj: AbstractObject) {
+ if (obj is FText) textDeleteBuffer.add(obj)
+ else objectDeleteBuffer.add(obj)
+ }
+
+ /**
+ * adds a auto-executed time listener
+ * you must add or it won't work.
+ */
+ infix fun addTimeListener(listener: FTimeListener) = timeListenerAddBuffer.add(listener)
+
+ /**
+ * adds an array of auto-executed time listeners
+ */
+ infix fun addTimeListeners(listeners: Array) = listeners.forEach { l -> addTimeListener(l) }
+
+ /**
+ * adds a collection of auto-executed time listeners
+ */
+ infix fun addTimeListeners(listeners: Collection) = addTimeListeners(listeners.toTypedArray())
+
+ /**
+ * removes all auto-executed time listeners
+ */
+ fun clearTimeListeners() = timeListenerDeleteBuffer.addAll(timeListeners)
+
+ /**
+ * removes auto-executed time listeners specified in the given array.
+ *
+ * @param listeners the array
+ */
+ infix fun removeTimeListeners(listeners: Array) =
+ listeners.forEach { l -> removeTimeListener(l) }
+
+ /**
+ * auto-execute time listeners which are equal to the given collection.
+ *
+ * @param listeners the collection
+ */
+ infix fun removeTimeListeners(listeners: Collection) = removeTimeListeners(listeners.toTypedArray())
+
+ /**
+ * removes specified listener
+ *
+ * @param listener the listener
+ */
+ infix fun removeTimeListener(listener: FTimeListener) = timeListenerDeleteBuffer.add(listener)
+
+ /**
+ * do the delete and add work, to prevent Exceptions
+ */
+ private fun processBuffer() {
+ objects.addAll(objectAddBuffer)
+ objects.removeAll(objectDeleteBuffer)
+ objectDeleteBuffer.clear()
+ objectAddBuffer.clear()
+
+ timeListeners.addAll(timeListenerAddBuffer)
+ timeListeners.removeAll(timeListenerDeleteBuffer)
+ timeListenerDeleteBuffer.clear()
+ timeListenerAddBuffer.clear()
+
+ texts.addAll(textAddBuffer)
+ texts.removeAll(textDeleteBuffer)
+ textDeleteBuffer.clear()
+ textAddBuffer.clear()
+ }
+
+ /**
+ * must be used in Game
+ */
+ inner class FriceCanvas(context: Context) : View(context) {
+
+ init {
+ if (context !is Game) throw FatalError()
+ setOnClickListener {
+ context.onClick()
+ }
+ setOnTouchListener { view, motionEvent ->
+ context.onTouch(motionEvent)
+ return@setOnTouchListener false
+ }
+ }
+
+ val p = Paint().apply {
+ isAntiAlias = true
+ style = Paint.Style.FILL
+ }
+
+ private fun Paint.reset() {
+ color = ColorResource.GRAY.color
+ }
+
+ override fun onDraw(canvas: Canvas?) {
+ super.onDraw(canvas)
+ processBuffer()
+ canvas?.save()
+
+ objects.forEach { o ->
+ if (o is FObject) {
+ o.runAnims()
+ o.checkCollision()
+ }
+ }
+
+ canvas?.let {
+ objects.forEach { o ->
+
+ canvas.restore()
+ p.reset()
+
+ if (autoGC && (o.x.toInt() < -width ||
+ o.x.toInt() > width + width ||
+ o.y.toInt() < -height ||
+ o.y.toInt() > height + height)) {
+ if (o is PhysicalObject) o.died = true
+ removeObject(o)
+ }
+
+ if (o is PhysicalObject) canvas.rotate(o.rotate.toFloat(),
+ (o.x + o.width / 2).toFloat(), (o.y + o.height / 2).toFloat())
+ else canvas.rotate(o.rotate.toFloat(), o.x.toFloat(), o.y.toFloat())
+ when (o) {
+ is FObject.ImageOwner ->
+ canvas.drawBitmap(o.image, o.x.toFloat(), o.y.toFloat(), p)
+ is ShapeObject -> {
+ p.color = o.getResource().color
+ when (o.collideBox) {
+ is FRectangle -> canvas.drawRect(
+ o.x.toFloat(),
+ o.y.toFloat(),
+ o.width.toFloat(),
+ o.height.toFloat(),
+ p
+ )
+ is FOval -> canvas.drawOval(
+ RectF(
+ o.x.toFloat(),
+ o.y.toFloat(),
+ o.width.toFloat(),
+ o.height.toFloat()
+ ),
+ p
+ )
+ }
+ }
+ is LineEffect -> {
+ p.color = o.colorResource.color
+ canvas.drawLine(
+ o.x.toFloat(),
+ o.y.toFloat(),
+ o.x2.toFloat(),
+ o.y2.toFloat(),
+ p
+ )
+ }
+ }
+
+ texts.forEach { b ->
+
+ canvas.restore()
+ p.reset()
+
+ if (b is FButton) {
+ p.color = b.getColor().color
+ when (b) {
+ is FObject.ImageOwner ->
+ canvas.drawBitmap(b.image, b.x.toFloat(), b.y.toFloat(), p)
+ is SimpleButton -> {
+ p.color = b.getColor().color
+ canvas.drawRoundRect(
+ RectF(
+ b.x.toFloat(),
+ b.y.toFloat(),
+ b.width.toFloat(),
+ b.height.toFloat()
+ ),
+ Math.min((b.width * 0.5).toFloat(), 10F),
+ Math.min((b.height * 0.5).toFloat(), 10F), p)
+ p.color = ColorResource.GRAY.color
+ canvas.drawText(b.text, b.x.toFloat(), b.y.toFloat(), p)
+ }
+ }
+ } else canvas.drawText(b.text, b.x.toFloat(), b.y.toFloat(), p)
+ }
+ }
+
+ if (showFPS) canvas.drawText("fps: $fpsDisplay", 30F, height - 30F, p)
+
+ customDraw(canvas)
+ }
+ }
+ }
+}
diff --git a/frice/src/main/kotlin/org/frice/android/obj/Objects.kt b/frice/src/main/kotlin/org/frice/android/obj/Objects.kt
new file mode 100644
index 0000000..60e28f1
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/obj/Objects.kt
@@ -0,0 +1,186 @@
+package org.frice.android.obj
+
+import android.graphics.Bitmap
+import org.frice.android.utils.graphics.shape.FPoint
+import org.frice.android.utils.graphics.shape.FShape
+import org.frice.game.anim.FAnim
+import org.frice.game.anim.RotateAnim
+import org.frice.game.anim.move.AccelerateMove
+import org.frice.game.anim.move.DoublePair
+import org.frice.game.anim.move.MoveAnim
+import org.frice.game.anim.scale.ScaleAnim
+import org.frice.game.resource.FResource
+import java.util.*
+
+/**
+ * Created by ice1000 on 2016/8/18.
+ * @author ice1000
+ * @since v0.3.3
+ */
+interface AbstractObject {
+ var x: Double
+ var y: Double
+
+ var rotate: Double
+}
+
+/**
+ * Created by ice1000 on 2016/8/20.
+ * @author ice1000
+ * @since v0.4
+ */
+interface FContainer {
+
+ var x: Double
+ var y: Double
+
+ val width: Double
+ val height: Double
+
+
+ fun containsPoint(px: Int, py: Int) = px >= x && px <= x + width && py >= y && py <= y + height
+
+ infix fun containsPoint(point: FPoint) = containsPoint(point.x, point.y)
+}
+
+/**
+ * Created by ice1000 on 2016/8/16.
+ * @author ice1000
+ * @since v0.3
+ */
+interface CollideBox {
+ fun isCollide(other: CollideBox): Boolean
+}
+
+/**
+ * Created by ice1000 on 2016/8/19.
+ *
+ * @author ice1000
+ * @since v0.4
+ */
+abstract class PhysicalObject : AbstractObject, CollideBox, FContainer {
+ open var died = false
+ override var rotate = 0.0
+ var mass = 1.0
+ set(value) {
+ if (value <= 0) field = 0.001
+ else field = value
+ }
+}
+
+
+/**
+ * Created by ice1000 on 2016/8/13.
+ * @author ice1000
+ * @since v0.1
+ */
+abstract class FObject : PhysicalObject() {
+ open var id = -1
+
+ val anims = LinkedList()
+
+ val targets = LinkedList>()
+
+ override var rotate = 0.0
+
+ /**
+ * physics force
+ * will change with mass(see #runAnims below)
+ */
+ private val force = AccelerateMove(0.0, 0.0)
+
+ abstract val collideBox: FShape
+
+ abstract fun getResource(): FResource
+
+ infix fun scale(p: DoublePair) = scale(p.x, p.y)
+
+ abstract fun scale(x: Double, y: Double)
+
+ open infix fun move(p: DoublePair) = move(p.x, p.y)
+
+ fun move(x: Double, y: Double) {
+ this.x += x
+ this.y += y
+ }
+
+ open infix fun rotate(angle: Double) {
+ rotate += angle
+ }
+
+ /**
+ * Magic! Don't touch!
+ */
+ protected infix fun PhysicalObject.rectCollideRect(rect: PhysicalObject) =
+ x + width >= rect.x && rect.y <= y + height &&
+ x <= rect.x + rect.width &&
+ y <= rect.y + rect.height
+
+ // protected infix fun PhysicalObject.rectCollideOval(oval: PhysicalObject): Boolean {
+ // if (!rectCollideRect(oval)) return false
+ // val xxx = if (x + width / 2 > oval.x + oval.width / 2) x else x + width
+ // val yyy = if (y + height / 2 > oval.y + oval.height / 2) y else y + height
+ // }
+
+ private fun squaredDelta(d1: Double, d2: Double) = (d1 - d2) * Math.abs(d1 - d2)
+ private fun targetMass(c: AbstractObject) = if (c is PhysicalObject) c.mass else 1.0
+
+ fun runAnims() {
+ anims.forEach { a ->
+ when (a) {
+ is MoveAnim -> this move a.delta
+ is ScaleAnim -> this scale a.after
+ is RotateAnim -> this rotate a.rotate
+ }
+ }
+ // TODO bug
+ // if (gravityConstant != 0.0) gravityCentre.forEach { c ->
+ // unless (Math.abs(c.x - x) + Math.abs(c.y - y) < 1.5) {
+ // gravity.x += targetMass(c) * gravityConstant / squaredDelta(c.x, x)
+ // gravity.y += targetMass(c) * gravityConstant / squaredDelta(c.y, y)
+ // }
+ // }
+ // move force
+ move(force.delta / mass)
+ // affected by gravity
+ // move(gravity)
+ }
+
+ fun checkCollision() {
+ targets.removeAll { t -> t.first.died }
+ targets.forEach { t -> if (isCollide(t.first)) t.second.handle() }
+ }
+
+ /**
+ * add a force to this object
+ * the effect of the force have sth to do with the mass
+ */
+ fun addForce(x: Double, y: Double) {
+ force.ax += x
+ force.ay += y
+ }
+
+ infix fun addForce(p: FPoint) = addForce(p.x.toDouble(), p.y.toDouble())
+
+ /**
+ * Created by ice1000 on 2016/8/16.
+ * @author ice1000
+ * @since v0.3
+ */
+ interface OnCollideEvent {
+ companion object {
+ fun from(block: () -> Unit) = object : OnCollideEvent {
+ override fun handle() {
+ block()
+ }
+ }
+ }
+
+ fun handle()
+ }
+
+ interface ImageOwner {
+ val image: Bitmap
+ }
+}
+
diff --git a/frice/src/main/kotlin/org/frice/android/obj/button/Buttons.kt b/frice/src/main/kotlin/org/frice/android/obj/button/Buttons.kt
new file mode 100644
index 0000000..922b107
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/obj/button/Buttons.kt
@@ -0,0 +1,87 @@
+package org.frice.android.obj.button
+
+import android.graphics.Bitmap
+import org.frice.android.obj.AbstractObject
+import org.frice.android.obj.FContainer
+import org.frice.android.obj.FObject
+import org.frice.android.resource.graphics.ColorResource
+import org.frice.android.resource.image.ImageResource
+import org.frice.android.utils.graphics.utils.darker
+
+/**
+ * Created by ice1000 on 2016/8/18.
+ * @author ice1000
+ * @since v0.3.2
+ */
+
+interface FButton : FContainer, AbstractObject {
+ var onClickListener: OnClickListener?
+
+ fun onClick() = onClickListener?.onClick()
+
+ /**
+ * Created by ice1000 on 2016/8/19.
+ * @author ice1000
+ * @since v0.4
+ */
+ interface OnClickListener {
+ fun onClick()
+ }
+
+ /**
+ * Created by ice1000 on 2016/8/19.
+ * @author ice1000
+ * @since v0.4
+ */
+ interface OnMouseListener {
+ fun onMouse()
+ }
+}
+
+/**
+ * Created by ice1000 on 2016/9/3 0003.
+ *
+ * @author ice1000
+ * @since v0.5
+ */
+class ImageButton(val imageNormal: ImageResource, val imagePressed: ImageResource, x: Double, y: Double) :
+ FButton, FObject.ImageOwner, SimpleButton("", x, y,
+ imageNormal.bitmap.width.toDouble(), imageNormal.bitmap.height.toDouble()) {
+
+ override var rotate = 0.0
+
+ constructor(image: ImageResource, x: Double, y: Double) :
+ this(image, image, x, y)
+
+ override var onClickListener: FButton.OnClickListener? = null
+
+ private var bool = false
+
+ override val image: Bitmap
+ get () = if (bool) imagePressed.bitmap
+ else imageNormal.bitmap
+}
+
+/**
+ * Created by ice1000 on 2016/8/18.
+ *
+ * @author ice1000
+ * @since v0.3.3
+ */
+open class SimpleButton(
+ var colorResource: ColorResource,
+ override var text: String,
+ override var x: Double, override var y: Double,
+ override var width: Double, override var height: Double) : FButton, FText() {
+
+ constructor(text: String, x: Double, y: Double, width: Double, height: Double) :
+ this(ColorResource.IntelliJ_IDEA黑, text, x, y, width, height)
+
+ private var bool = false
+
+ override var onClickListener: FButton.OnClickListener? = null
+
+ override fun getColor() =
+ if (bool) ColorResource(colorResource.color.darker())
+ else colorResource
+}
diff --git a/frice/src/main/kotlin/org/frice/android/obj/button/Texts.kt b/frice/src/main/kotlin/org/frice/android/obj/button/Texts.kt
new file mode 100644
index 0000000..b333723
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/obj/button/Texts.kt
@@ -0,0 +1,32 @@
+package org.frice.android.obj.button
+
+import org.frice.android.obj.AbstractObject
+import org.frice.android.resource.graphics.ColorResource
+
+/**
+ * Created by ice1000 on 2016/8/19.
+ *
+ * @author ice1000
+ * @since v0.4
+ */
+abstract class FText : AbstractObject {
+ open var text = ""
+ override var rotate = 0.0
+
+ abstract fun getColor(): ColorResource
+}
+
+/**
+ * Created by ice1000 on 2016/8/19.
+ *
+ * @author ice1000
+ * @since v0.4
+ */
+class SimpleText(var colorResource: ColorResource, override var text: String,
+ override var x: Double, override var y: Double) : FText() {
+
+ constructor(text: String, x: Double, y: Double) :
+ this(ColorResource.GRAY, text, x, y)
+
+ override fun getColor() = colorResource
+}
diff --git a/frice/src/main/kotlin/org/frice/android/obj/effects/Effects.kt b/frice/src/main/kotlin/org/frice/android/obj/effects/Effects.kt
new file mode 100644
index 0000000..37d68c0
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/obj/effects/Effects.kt
@@ -0,0 +1,131 @@
+package org.frice.android.obj.effects
+
+import android.graphics.Bitmap
+import org.frice.android.obj.AbstractObject
+import org.frice.android.obj.CollideBox
+import org.frice.android.resource.graphics.ColorResource
+import org.frice.android.resource.graphics.CurveResource
+import org.frice.android.resource.graphics.FunctionResource
+import org.frice.android.resource.graphics.ParticleResource
+import org.frice.android.resource.image.ImageResource
+import org.frice.android.utils.graphics.shape.FRectangle
+import org.frice.game.obj.sub.ImageObject
+
+/**
+ * Created by ice1000 on 2016/8/19.
+ *
+ * @author ice1000
+ * @since v0.5
+ */
+class LineEffect(var colorResource: ColorResource, override var x: Double, override var y: Double,
+ var x2: Double, var y2: Double) : AbstractObject {
+
+ override var rotate = 0.0
+
+ constructor(x: Double, y: Double, x2: Double, y2: Double) : this(ColorResource.BLACK, x, y, x2, y2)
+}
+
+/**
+ * Created by ice1000 on 2016/8/17.
+ * @author ice1000
+ * @since 0.3.2
+ */
+// FIXME!!!
+class ParticleEffect(private var resource: ParticleResource, override var x: Double, override var y: Double) :
+ ImageObject(resource.getResource(), x, y) {
+ override val image: Bitmap
+ get() = resource.getResource()
+
+ override val collideBox = FRectangle(x.toInt(), y.toInt())
+
+ override val width: Double
+ get() = resource.width.toDouble()
+ override val height: Double
+ get() = resource.height.toDouble()
+
+ override fun getResource() = ImageResource.create(image)
+
+ override fun scale(x: Double, y: Double) {
+ resource.width = (resource.width * x / 1000.0).toInt()
+ resource.height = (resource.height * y / 1000.0).toInt()
+ }
+
+ override fun isCollide(other: CollideBox): Boolean = false
+}
+
+/**
+ * Tested, Work stably.
+ * Created by ice1000 on 2016/8/24.
+ *
+ * @author ice1000
+ * @since v0.4.1
+ */
+class FunctionEffect(res: FunctionResource, override var x: Double, override var y: Double) :
+ ImageObject(res.getResource(), x, y) {
+
+ constructor(function: FFunction, x: Double, y: Double, width: Int, height: Int) :
+ this(FunctionResource(ColorResource.BLUE, { x -> function.call(x) }, width, height), x, y)
+
+ override val width: Double
+ get() = res.bitmap.width.toDouble()
+ override val height: Double
+ get() = res.bitmap.height.toDouble()
+
+ override fun getResource() = ImageResource.create(image)
+
+ override val image: Bitmap
+ get() = res.bitmap
+
+ override fun scale(x: Double, y: Double) {
+// res.bitmap = image.getScaledInstance((image.width * x / 1000.0).toInt(),
+// (image.height * y / 1000.0).toInt(), Image.SCALE_DEFAULT) as BufferedImage
+ TODO()
+// TODO()
+ }
+
+ /**
+ * Created by ice1000 on 2016/8/27.
+ *
+ * @author ice1000
+ * @since v0.4
+ */
+ interface FFunction {
+ fun call(x: Double): Double
+ }
+}
+
+/**
+ * used to represent a curve.
+ *
+ * @author ice1000
+ * @since v0.5.2
+ */
+class CurveEffect(res: CurveResource, override var x: Double, override var y: Double) :
+ ImageObject(res.getResource(), x, y) {
+
+ constructor(curve: FCurve, x: Double, y: Double, width: Int, height: Int) :
+ this(CurveResource(ColorResource.BLUE, { x -> curve.call(x) }, width, height), x, y)
+
+ override fun getResource() = ImageResource.create(image)
+
+ override val image: Bitmap
+ get() = res.bitmap
+
+ override fun scale(x: Double, y: Double) {
+// res.bitmap = image.getScaledInstance((image.width * x / 1000.0).toInt(),
+// (image.height * y / 1000.0).toInt(), Image.SCALE_DEFAULT) as BufferedImage
+ TODO()
+// TODO()
+ }
+
+ /**
+ * for java.
+ * this can be represent as a lambda in j8.
+ *
+ * @author ice1000
+ * @since v0.5.2
+ */
+ interface FCurve {
+ fun call(x: Double): List
+ }
+}
diff --git a/frice/src/main/kotlin/org/frice/android/resource/graphics/GraphicsResource.kt b/frice/src/main/kotlin/org/frice/android/resource/graphics/GraphicsResource.kt
new file mode 100644
index 0000000..c1fa9bd
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/resource/graphics/GraphicsResource.kt
@@ -0,0 +1,192 @@
+package org.frice.android.resource.graphics
+
+import android.graphics.Bitmap
+import android.graphics.Color
+import org.frice.android.Game
+import org.frice.android.resource.image.ImageResource
+import org.frice.game.resource.FResource
+import org.frice.game.utils.misc.forceRun
+import org.frice.game.utils.misc.loop
+import java.util.*
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.1.1
+ */
+class ColorResource(color: Int) : FResource {
+ val color: Int
+
+ init {
+ this.color = Color.rgb(
+ (color shr 16) % 0xFF,
+ (color shr 8) % 0xFF,
+ (color) % 0xFF
+ )
+ }
+
+ /**
+ * 颜表立。。。
+ * Read these color names in UTF-8 w/o BOM encoding, and pick up your 信仰 and 节操.
+ */
+ companion object {
+ @JvmField val GREEN = ColorResource(Color.GREEN)
+ @JvmField val BLUE = ColorResource(Color.BLUE)
+ @JvmField val GRAY = ColorResource(Color.GRAY)
+ @JvmField val WHITE = ColorResource(Color.WHITE)
+ @JvmField val RED = ColorResource(Color.RED)
+ @JvmField val BLACK = ColorResource(Color.BLACK)
+ @JvmField val CYAN = ColorResource(Color.CYAN)
+ @JvmField val MAGENTA = ColorResource(Color.MAGENTA)
+ @JvmField val YELLOW = ColorResource(Color.YELLOW)
+ @JvmField val SHIT_YELLOW = ColorResource(0x633516)
+ @JvmField val ORANGE = ColorResource(0xcc7832)
+ @JvmField val PINK = ColorResource(0xC770CC)
+ @JvmField val COLORLESS = ColorResource(Color.argb(0, 0, 0, 0))
+ @JvmField val 小埋色 = ColorResource(0xFFAC2B)
+ @JvmField val 基佬紫 = ColorResource(0x781895)
+ @JvmField val 吾王蓝 = BLUE
+ @JvmField val 教主黄 = YELLOW
+ @JvmField val 宝强绿 = GREEN
+ @JvmField val 冰封绿 = 宝强绿
+ @JvmField val 如果奇迹有颜色那么一定是橙色 = ORANGE
+ @JvmField val 高坂穗乃果 = ORANGE
+ @JvmField val 南小鸟 = GRAY
+ @JvmField val 园田海未 = BLUE
+ @JvmField val 洵濑绘理 = ColorResource(0x0FFFFF)
+ @JvmField val 星空凛 = 教主黄
+ @JvmField val 西木野真姬 = RED
+ @JvmField val 东条希 = 基佬紫
+ @JvmField val 小泉花阳 = ColorResource(0x1BA61C)
+ @JvmField val 矢泽妮可 = PINK
+ @JvmField val 屎黄色 = SHIT_YELLOW
+ @JvmField val 天依蓝 = ColorResource(0x66CCFF)
+ @JvmField val 清真绿 = ColorResource(0x038B43)
+ @JvmField val IntelliJ_IDEA黑 = ColorResource(0x2B2B2B)
+ @JvmField val 如果真爱有颜色那么一定是黄色 = 教主黄
+ @JvmField val 杀老师 = 如果真爱有颜色那么一定是黄色
+ @JvmField val 潮田渚 = 园田海未
+ @JvmField val 茅野枫 = 冰封绿
+ @JvmField val 赤羽业 = 西木野真姬
+ }
+
+ /**
+ * not for users and developers.
+ * this should only be called by the engine itself.
+ */
+ override fun getResource() = color
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is ColorResource) return false
+ return color == other.color
+ }
+
+ override fun hashCode() = color.hashCode()
+}
+
+
+/**
+ * Created by ice1000 on 2016/8/27.
+ *
+ * @author ice1000
+ * @since v0.4
+ */
+class FunctionResource(color: ColorResource, val f: (Double) -> Double, width: Int, height: Int) : FResource {
+
+ private val image: Bitmap
+
+ init {
+ image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ var lastTime = f(0.0)
+ var thisTime: Double
+ (0..width step 1).forEach { x ->
+ thisTime = f(x.toDouble())
+ forceRun { image.setPixel(x.toInt(), thisTime.toInt(), color.color) }
+ if (Math.abs(thisTime - lastTime) >= 1.0) forceRun {
+ (Math.min(thisTime, lastTime).toInt()..Math.max(thisTime, lastTime).toInt()).forEach { i ->
+ image.setPixel(x, i, color.color)
+ }
+ }
+ lastTime = thisTime
+ }
+ }
+
+ override fun getResource() = image
+}
+
+/**
+ * used to represent a Curve.
+ * something like circle.
+ */
+class CurveResource(color: ColorResource, val f: (Double) -> List, width: Int, height: Int) : FResource {
+ private val image: Bitmap
+
+ init {
+ image = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ (0..width step 1).forEach { x ->
+ f(x.toDouble()).forEach { y ->
+ forceRun { image.setPixel(x, y.toInt(), color.color) }
+ }
+ }
+ }
+
+ override fun getResource() = image
+}
+
+/**
+ * Particle effects
+ * Created by ice1000 on 2016/8/17.
+ *
+ * @author ice1000
+ * @since v0.3.2
+ */
+// FIXME!!!
+class ParticleResource(
+ val game: Game,
+ var width: Int,
+ var height: Int,
+ val back: FResource,
+ var fore: ColorResource,
+ var percentage: Double) : FResource {
+ constructor(game: Game, x: Int, y: Int, back: ColorResource, fore: ColorResource) :
+ this(game, x, y, back, fore, 0.5)
+
+ constructor(game: Game, x: Int, y: Int, percentage: Double) :
+ this(game, x, y, ColorResource.WHITE, ColorResource.BLACK, percentage)
+
+ constructor(game: Game, x: Int, y: Int) : this(game, x, y, 0.5)
+
+ /**
+ * particle effects as an bitmap
+ */
+ private val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
+ private val random = Random(Random().nextLong())
+
+ private fun drawBackground() {
+ // unknown how 2 deal with
+ // FIXME
+ }
+
+ init {
+ drawBackground()
+ loop((bitmap.width * bitmap.height * percentage).toInt()) {
+ bitmap.setPixel(random.nextInt(width), random.nextInt(height), fore.color)
+ }
+ }
+
+ override fun getResource() = bitmap.apply {
+ var cache1: Int
+ var cache2: Int
+ loop((bitmap.width * bitmap.height * percentage).toInt()) {
+ cache1 = random.nextInt(width)
+ cache2 = random.nextInt(height)
+ bitmap.setPixel(random.nextInt(width), random.nextInt(height), fore.color)
+ bitmap.setPixel(cache1, cache2, when (back) {
+ is ColorResource -> back.color
+ is ImageResource -> back.bitmap.getPixel(cache1, cache2)
+ else -> ColorResource.COLORLESS.color
+ })
+ }
+ }
+}
diff --git a/frice/src/main/kotlin/org/frice/android/resource/image/ImageResource.kt b/frice/src/main/kotlin/org/frice/android/resource/image/ImageResource.kt
new file mode 100644
index 0000000..5249208
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/resource/image/ImageResource.kt
@@ -0,0 +1,121 @@
+package org.frice.android.resource.image
+
+import android.graphics.Bitmap
+import org.frice.android.utils.message.log.FLog
+import org.frice.android.Game
+import org.frice.game.resource.FResource
+import org.frice.game.utils.time.FTimeListener
+import org.frice.game.utils.web.WebUtils
+
+/**
+ * Created by ice1000 on 2016/8/13.
+ * @author ice1000
+ * @since v0.1
+ */
+abstract class ImageResource : FResource {
+
+ companion object {
+ @JvmStatic fun create(image: Bitmap) = object : ImageResource() {
+ override var bitmap = image
+ }
+
+ @JvmStatic fun fromBitmap(bitmap: Bitmap): ImageResource = create(bitmap)
+ @JvmStatic fun fromPath(path: String) = FileImageResource(path)
+ @JvmStatic fun fromWeb(url: String) = WebImageResource(url)
+ @JvmStatic fun empty() = create(Bitmap.createBitmap(0, 0, Bitmap.Config.ARGB_8888))
+ }
+
+ abstract var bitmap: Bitmap
+
+ override fun getResource() = bitmap
+
+ fun scaled(x: Double, y: Double) = fromBitmap(Bitmap.createScaledBitmap(
+ bitmap,
+ (x * bitmap.width).toInt(),
+ (y * bitmap.height).toInt(),
+ true
+ ))
+
+ fun part(x: Int, y: Int, width: Int, height: Int) = fromBitmap(Bitmap.createBitmap(
+ bitmap,
+ x,
+ y,
+ width,
+ height
+ ))
+
+}
+
+
+/**
+ * create an bitmap from a part of another bitmap
+ *
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.3
+ */
+class PartImageResource(origin: ImageResource, x: Int, y: Int, width: Int, height: Int) : ImageResource() {
+
+ override var bitmap: Bitmap = Bitmap.createBitmap(origin.bitmap, x, y, width, height)
+}
+
+
+/**
+ * Image Resource from internet
+ *
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.2
+ */
+class WebImageResource(url: String) : ImageResource() {
+ override var bitmap = WebUtils.readImage(url)
+
+}
+
+
+/**
+ * Created by ice1000 on 2016/8/16.
+ * @author ice1000
+ * @since v0.3.1
+ */
+class FrameImageResource(val game: Game, val list: MutableList, div: Int) : ImageResource() {
+
+ constructor(game: Game, list: Array, div: Int) : this(game, list.toMutableList(), div)
+
+ private var start: Long
+ private val timer: FTimeListener
+ private var counter = 0
+ private var ended = false
+ var loop = true
+
+ override var bitmap: Bitmap
+ get() = if (loop || ended) list.getImage(counter).bitmap else list.last().bitmap
+ set(value) {
+ list.add(ImageResource.create(value))
+ }
+
+ fun MutableList.getImage(index: Int): ImageResource {
+ if (index == this.size - 1) ended = true
+ return this[index]
+ }
+
+ init {
+ start = System.currentTimeMillis()
+ timer = FTimeListener(div, {
+ FLog.e("counter = $counter")
+ counter++
+ counter %= list.size
+ })
+ game.addTimeListener(timer)
+ }
+}
+
+/**
+ * Created by ice1000 on 2016/8/13.
+ * @author ice1000
+ * @since v0.1
+ */
+class FileImageResource(path: String) : ImageResource() {
+ override var bitmap = WebUtils.readImage(path)
+
+}
diff --git a/frice/src/main/kotlin/org/frice/android/utils/data/FileUtils.kt b/frice/src/main/kotlin/org/frice/android/utils/data/FileUtils.kt
new file mode 100644
index 0000000..bf79a5c
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/utils/data/FileUtils.kt
@@ -0,0 +1,19 @@
+package org.frice.android.utils.data
+
+import java.io.File
+
+/**
+ * Created by ice1000 on 2016/9/3.
+ *
+ * @author ice1000
+ * @since v0.5
+ */
+// TODO image 2 file
+
+fun string2File(string: String, file: File) = file.writeText(string)
+
+fun string2File(string: String, file: String) = string2File(string, File(file))
+
+fun bytes2File(byteArray: ByteArray, file: File) = file.writeBytes(byteArray)
+fun bytes2File(byteArray: ByteArray, file: String) = bytes2File(byteArray, File(file))
+
diff --git a/frice/src/main/kotlin/org/frice/android/utils/data/SpUtils.kt b/frice/src/main/kotlin/org/frice/android/utils/data/SpUtils.kt
new file mode 100644
index 0000000..e3bdd43
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/utils/data/SpUtils.kt
@@ -0,0 +1,41 @@
+package org.frice.android.utils.data
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.support.v7.app.AppCompatActivity
+
+/**
+ * Created by ice1000 on 2016/10/8.
+ *
+ * @author ice1000
+ */
+
+
+/**
+ * insert a value in2 SharedPreference
+ * any types of value is accepted.
+ *
+ * Will be smart casted.
+ */
+fun Context.save(key: String, value: Any) {
+ val editor = openPreference().edit()
+ if (value is Int) editor.putInt(key, value)
+ else if (value is Float) editor.putFloat(key, value)
+ else if (value is Long) editor.putLong(key, value)
+ else if (value is Boolean) editor.putBoolean(key, value)
+ else if (value is String) editor.putString(key, value)
+ else throw Exception("not supported type")
+ editor.apply()
+}
+
+fun Context.readString(key: String, default: String = "") = openPreference().getString(key, default) ?: default
+fun Context.readInt(key: String, default: Int = 0) = openPreference().getInt(key, default) ?: default
+fun Context.readBoolean(key: String, default: Boolean = false) = openPreference().getBoolean(key, default) ?: default
+
+/**
+ * @return a SharedPreference
+ */
+private fun Context.openPreference(): SharedPreferences =
+ getSharedPreferences("MainPreference", AppCompatActivity.MODE_ENABLE_WRITE_AHEAD_LOGGING)
+
+
diff --git a/frice/src/main/kotlin/org/frice/android/utils/graphics/shape/Shapes.kt b/frice/src/main/kotlin/org/frice/android/utils/graphics/shape/Shapes.kt
new file mode 100644
index 0000000..c5be95c
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/utils/graphics/shape/Shapes.kt
@@ -0,0 +1,100 @@
+package org.frice.android.utils.graphics.shape
+
+import java.util.*
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.1.1
+ */
+interface FShape {
+ var width: Int
+ var height: Int
+}
+
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.1.1
+ */
+open class FCircle(r: Double) : FOval(r, r)
+
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.1.1
+ */
+open class FOval(rh: Double, rv: Double) : FShape {
+ override var width = (rh + rh).toInt()
+ override var height = (rv + rv).toInt()
+}
+
+
+/**
+ * Created by ice1000 on 2016/8/16.
+ * @author ice1000
+ * @since v0.3
+ */
+data class FPoint(var x: Int, var y: Int)
+
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.1.1
+ */
+open class FRectangle(override var width: Int, override var height: Int) : FShape {
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is FRectangle) return false
+ if (height != other.height || width != other.width) return false
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = width
+ result = 31 * result + height
+ return result
+ }
+
+ // infix fun rectCollideRect(o: FRectangle) = (x > o.x && )
+}
+
+/**
+ * 通过两点构造一条直线
+ * copied from https://github.com/ice1000/FindLine/blob/master/src/ice1000/models/Line.kt
+ * my another repo
+ * Created by ice1000 on 2016/8/8.
+ *
+ * @author ice1000
+ */
+open class FLine(one: FPoint, two: FPoint) {
+
+ private val a = two.y - one.y
+ private val b = one.x - two.x
+ private val c = two.x * one.y - one.x * two.y
+ val set = HashSet()
+
+ init {
+ (Math.min(one.x, two.x)..Math.max(one.x, two.x)).forEach { x -> set.add(FPoint(x, x2y(x))) }
+ (Math.min(one.y, two.y)..Math.max(one.y, two.y)).forEach { y -> set.add(FPoint(y2x(y), y)) }
+ }
+
+ fun x2y(x: Int) = if (b == 0) c / a else -(a * x + c) / b
+ fun y2x(y: Int) = if (a == 0) c / b else -(b * y + c) / a
+
+ override operator fun equals(other: Any?): Boolean {
+ if (other == null || other !is FLine) return false
+ return a / other.a == b / other.b && b / other.b == c / other.c
+ }
+
+ override fun hashCode(): Int {
+ var result = a.hashCode()
+ result = 31 * result + b.hashCode()
+ result = 31 * result + c.hashCode()
+ return result
+ }
+}
diff --git a/frice/src/main/kotlin/org/frice/android/utils/graphics/utils/ColorUtils.kt b/frice/src/main/kotlin/org/frice/android/utils/graphics/utils/ColorUtils.kt
new file mode 100644
index 0000000..fff1296
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/utils/graphics/utils/ColorUtils.kt
@@ -0,0 +1,35 @@
+package org.frice.android.utils.graphics.utils
+
+import android.graphics.Color
+
+/**
+ * Created by ice1000 on 16-8-6.
+ * Reference: http://blog.csdn.net/lzs109/article/details/7316507
+ * @author ice1000
+ */
+val asciiList = listOf('#', '0', 'X', 'x', '+', '=', '-', ';', ',', '.', ' ')
+
+fun Int.toAscii() = asciiList[gray() / (256 / asciiList.size + 1)]
+
+/**
+ * Android API
+ */
+fun Int.gray(): Int {
+ val c = (Color.red(this) + Color.blue(this) + Color.green(this)) / 3
+ return Color.argb(0xff, c, c, c)
+}
+
+/**
+ * Android API
+ * making a color darker
+ */
+fun Int.darker() = Color.argb(
+ Color.alpha(this),
+ Color.red(this) * 2 / 3,
+ Color.green(this) * 2 / 3,
+ Color.blue(this) * 2 / 3
+)
+
+fun darkerRGB(int: Int) = int.darker()
+
+
diff --git a/frice/src/main/kotlin/org/frice/android/utils/message/FDialog.kt b/frice/src/main/kotlin/org/frice/android/utils/message/FDialog.kt
new file mode 100644
index 0000000..7c2e769
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/utils/message/FDialog.kt
@@ -0,0 +1,64 @@
+package org.frice.android.utils.message
+
+import android.app.Activity
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.2
+ */
+class FDialog(val game: Activity?) {
+ // TODO
+// fun show(msg: String) = JOptionPane.showMessageDialog(game, msg)
+// fun input() = input("")
+// fun input(msg: String) = JOptionPane.showInputDialog(game, msg)!!
+// fun confirm(msg: String) = confirm(msg, "")
+// fun confirm(msg: String, title: String) = confirm(msg, title, JOptionPane.YES_NO_CANCEL_OPTION)
+// fun confirm(msg: String, title: String, option: Int) = JOptionPane.showConfirmDialog(game, msg, title, option)
+
+// companion object {
+// internal val TYPE_ERROR = "ERROR_ELDATH"
+// internal val TYPE_INFO = "INFO_ELDATH"
+// internal val TYPE_WARN = "WARN_ELDATH"
+//
+// @JvmStatic fun infoDialog(msg: String) = Anonymous().show(TYPE_INFO, msg)
+// @JvmStatic fun warnDialog(doWhat: String, msg: String) = Anonymous().show(TYPE_WARN, doWhat, msg)
+// @JvmStatic fun errorDialog(doWhat: String, msg: String) = Anonymous().show(TYPE_ERROR, doWhat, msg)
+// }
+
+// internal class Anonymous internal constructor() : Application() {
+// override fun start(primaryStage: Stage?) {
+// when (parameters.raw[0]) {
+// TYPE_INFO -> InfoAlert(parameters.raw[1])
+// TYPE_WARN -> WarnAlert(parameters.raw[1], parameters.raw[2])
+// TYPE_ERROR -> ErrorAlert(parameters.raw[1], parameters.raw[2])
+// }
+// }
+//
+// private fun check() = parameters?.let { launch("", "", "") }
+//
+// internal fun show(type: String, s1: String) {
+// check()
+// parameters.raw[0] = type
+// parameters.raw[1] = s1
+// start(Stage())
+// }
+//
+// internal fun show(type: String, s1: String, s2: String) {
+// check()
+// parameters.raw[0] = type
+// parameters.raw[1] = s1
+// parameters.raw[2] = s2
+// start(Stage())
+// }
+//
+// internal fun show(type: String, s1: String, t: Throwable) {
+// check()
+// parameters.raw[0] = type
+// parameters.raw[1] = s1
+// parameters.raw[2] = Coder.parseThrowable(t)
+// start(Stage())
+// }
+//
+// }
+}
diff --git a/frice/src/main/kotlin/org/frice/android/utils/message/error/FatalError.kt b/frice/src/main/kotlin/org/frice/android/utils/message/error/FatalError.kt
new file mode 100644
index 0000000..bf157e6
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/utils/message/error/FatalError.kt
@@ -0,0 +1,15 @@
+package org.frice.android.utils.message.error
+
+import org.frice.android.utils.message.log.FLog
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.2
+ */
+class FatalError(s: String) : Error(s) {
+ constructor(): this("")
+ init {
+ FLog.e(s)
+ }
+}
diff --git a/frice/src/main/kotlin/org/frice/android/utils/message/log/FLog.kt b/frice/src/main/kotlin/org/frice/android/utils/message/log/FLog.kt
new file mode 100644
index 0000000..b86484c
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/utils/message/log/FLog.kt
@@ -0,0 +1,24 @@
+package org.frice.android.utils.message.log
+
+import android.util.Log
+
+/**
+ * Created by ice1000 on 2016/8/13.
+ * @author ice1000
+ * @since v0.1
+ */
+object FLog {
+ const val LOG_PREFIX = "Frice log["
+
+ @JvmStatic fun v(e: Any?) = verbose(e)
+ @JvmStatic fun d(e: Any?) = debug(e)
+ @JvmStatic fun i(e: Any?) = info(e)
+ @JvmStatic fun w(e: Any?) = warning(e)
+ @JvmStatic fun e(e: Any?) = error(e)
+
+ @JvmStatic fun verbose(e: Any?) = Log.v("$LOG_PREFIX verbose]\n", e?.toString())
+ @JvmStatic fun debug(e: Any?) = Log.d("$LOG_PREFIX debug]\n", e?.toString())
+ @JvmStatic fun info(e: Any?) = Log.i("$LOG_PREFIX info]\n", e?.toString())
+ @JvmStatic fun warning(e: Any?) = Log.w("$LOG_PREFIX warning]\n", e?.toString())
+ @JvmStatic fun error(e: Any?) = Log.e("!!!$LOG_PREFIX error]\n", e?.toString())
+}
diff --git a/frice/src/main/kotlin/org/frice/android/utils/misc/IOUtils.kt b/frice/src/main/kotlin/org/frice/android/utils/misc/IOUtils.kt
new file mode 100644
index 0000000..0a7e757
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/android/utils/misc/IOUtils.kt
@@ -0,0 +1,60 @@
+package org.frice.android.utils.misc
+
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.net.NetworkInfo
+import android.net.Uri
+import org.frice.android.utils.data.readString
+import org.frice.android.utils.data.save
+import org.jetbrains.anko.async
+import org.jetbrains.anko.uiThread
+import java.net.URL
+
+/**
+ * Created by ice1000 on 2016/10/8.
+ *
+ * @author ice1000
+ */
+
+fun Context.openWeb(url: String) = startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
+
+/**
+ * if this value is null,
+ * it means I have 2 load data from Sp.
+ */
+val Context.haveNetwork: Boolean
+ get() = connection != null && connection!!.isConnected
+
+private val Context.connection: NetworkInfo?
+ get() = (getSystemService(Context.CONNECTIVITY_SERVICE)
+ as ConnectivityManager).activeNetworkInfo
+
+
+const val DEFAULT_VALUE = "DEFAULT_VALUE"
+
+/**
+ * this will cache the data into SharedPreference
+ * next time when the network is invalid, it will return the data
+ * stored in the SharedPreference.
+ *
+ * this method extended String.
+ */
+fun Context.webResource(
+ url: String,
+ submit: (String) -> Unit,
+ default: String = DEFAULT_VALUE) {
+ async() {
+ var ret = readString(default)
+ uiThread { submit(ret) }
+ if (ret != DEFAULT_VALUE && !haveNetwork) {
+ uiThread { submit(ret) }
+ } else {
+ ret = URL(url).readText(Charsets.UTF_8)
+ uiThread { submit(ret) }
+ save(url, ret)
+ }
+ }
+}
+
+
diff --git a/frice/src/main/kotlin/org/frice/game/anim/FAnim.kt b/frice/src/main/kotlin/org/frice/game/anim/FAnim.kt
new file mode 100644
index 0000000..e5f437a
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/anim/FAnim.kt
@@ -0,0 +1,13 @@
+package org.frice.game.anim
+
+/**
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.1
+ */
+abstract class FAnim() {
+ protected val start: Double = System.currentTimeMillis().toDouble()
+
+ protected val now: Double
+ get() = System.currentTimeMillis().toDouble()
+}
\ No newline at end of file
diff --git a/frice/src/main/kotlin/org/frice/game/anim/RotateAnim.kt b/frice/src/main/kotlin/org/frice/game/anim/RotateAnim.kt
new file mode 100644
index 0000000..b7d679f
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/anim/RotateAnim.kt
@@ -0,0 +1,11 @@
+package org.frice.game.anim
+
+/**
+ * Created by ice1000 on 2016/8/20.
+ * @author ice1000
+ * @since v0.4
+ */
+open class RotateAnim(var angle: Double) : FAnim() {
+ val rotate: Double
+ get() = (now - start) * angle
+}
\ No newline at end of file
diff --git a/frice/src/main/kotlin/org/frice/game/anim/move/Move.kt b/frice/src/main/kotlin/org/frice/game/anim/move/Move.kt
new file mode 100644
index 0000000..e310e46
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/anim/move/Move.kt
@@ -0,0 +1,149 @@
+package org.frice.game.anim.move
+
+import org.frice.game.anim.FAnim
+
+/**
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.1
+ */
+abstract class MoveAnim() : FAnim() {
+ abstract val delta: DoublePair
+
+ protected var cache: Double = start
+}
+
+
+/**
+ * Simple move anim
+ *
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.1
+ * @param x pixels per second
+ * @param y pixels per second
+ */
+@Deprecated("AccurateMove is suggested.")
+open class SimpleMove(var x: Int, var y: Int) : MoveAnim() {
+
+ override val delta: DoublePair
+ get() {
+ val pair = DoublePair.from1000((now - cache) * x, (now - cache) * y)
+ cache = now
+ return pair
+ }
+
+}
+
+/**
+ * Accurate Move anim. Deltas are d1, the speed will be more spcific.
+ *
+ * @author ice1000
+ * @since v0.5.1
+ */
+open class AccurateMove(var x: Double, var y: Double) : MoveAnim() {
+
+ override val delta: DoublePair
+ get() {
+ val pair = DoublePair.from1000((now - cache) * x, (now - cache) * y)
+ cache = now
+ return pair
+ }
+}
+
+data class DoublePair(var x: Double, var y: Double) {
+
+ companion object {
+ @JvmStatic fun from1000(x: Double, y: Double) = DoublePair(x / 1000.0, y / 1000.0)
+ }
+
+ operator fun plusAssign(d: DoublePair) {
+ x += d.x
+ y += d.y
+ }
+
+ operator fun plusAssign(d: Double) {
+ x += d
+ y += d
+ }
+
+ operator fun minusAssign(d: DoublePair) {
+ x -= d.x
+ y -= d.y
+ }
+
+ operator fun minusAssign(d: Double) {
+ x -= d
+ y -= d
+ }
+
+ operator fun timesAssign(d: Double) {
+ x *= d
+ y *= d
+ }
+
+ operator fun divAssign(d: Double) {
+ x /= d
+ y /= d
+ }
+
+ operator fun div(d: Double) = DoublePair(x / d, y / d)
+ operator fun times(d: Double) = DoublePair(x * d, y * d)
+ operator fun plus(d: Double) = DoublePair(x + d, y + d)
+ operator fun plus(d: DoublePair) = DoublePair(x + d.x, y + d.y)
+ operator fun minus(d: Double) = DoublePair(x - d, y - d)
+ operator fun minus(d: DoublePair) = DoublePair(x - d.x, y - d.y)
+ operator fun inc() = DoublePair(x++, y++)
+ operator fun dec() = DoublePair(x--, y--)
+ operator fun unaryPlus() = DoublePair(x, y)
+ operator fun unaryMinus() = DoublePair(-x, -y)
+}
+
+
+/**
+ * Move with force (accelerate), give accelerate value to ax and by
+ *
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.2
+ * @param ax accelerate on x
+ * @param ay accelerate on y
+ */
+
+class AccelerateMove(var ax: Double, var ay: Double) : SimpleMove(0, 0) {
+ companion object {
+ @JvmStatic fun getGravity() = AccelerateMove(0.0, 10.0)
+ @JvmStatic fun getGravity(g: Double) = AccelerateMove(0.0, g)
+ }
+
+ private var mx = 0.0
+ private var my = 0.0
+
+ override val delta: DoublePair
+ get() {
+ mx = (now - start) * ax / 2.0
+ my = (now - start) * ay / 2.0
+ return DoublePair.from1000((now - cache) / 1000 * mx, (now - cache) / 1000 * my)
+ }
+
+}
+
+
+/**
+ * Define your own move object as you like.
+ *
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.3
+ */
+abstract class CustomMove() : MoveAnim() {
+ private val timeFromStart: Double
+ get() = System.currentTimeMillis() - start
+
+ abstract fun getXDelta(timeFromBegin: Double): Double
+ abstract fun getYDelta(timeFromBegin: Double): Double
+
+ override val delta: DoublePair
+ get() = DoublePair(getXDelta(timeFromStart), getYDelta(timeFromStart))
+
+}
\ No newline at end of file
diff --git a/frice/src/main/kotlin/org/frice/game/anim/scale/Scale.kt b/frice/src/main/kotlin/org/frice/game/anim/scale/Scale.kt
new file mode 100644
index 0000000..7f116da
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/anim/scale/Scale.kt
@@ -0,0 +1,29 @@
+package org.frice.game.anim.scale
+
+import org.frice.game.anim.FAnim
+import org.frice.game.anim.move.DoublePair
+
+/**
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.2
+ */
+abstract class ScaleAnim : FAnim() {
+ abstract val after: DoublePair
+}
+
+/**
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since v0.2.3
+ */
+class SimpleScale(var x: Double, var y: Double) : ScaleAnim() {
+
+ private var cache: Double = start
+ override val after: DoublePair
+ get() {
+ val pair = DoublePair.from1000((now - cache) * x, (now - cache) * y)
+ cache = now
+ return pair
+ }
+}
\ No newline at end of file
diff --git a/frice/src/main/kotlin/org/frice/game/obj/sub/BoneObject.kt b/frice/src/main/kotlin/org/frice/game/obj/sub/BoneObject.kt
new file mode 100644
index 0000000..93d84a1
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/obj/sub/BoneObject.kt
@@ -0,0 +1,13 @@
+package org.frice.game.obj.sub
+
+import org.frice.android.resource.image.ImageResource
+
+/**
+ * Not finished yet.
+ * Created by ice1000 on 2016/8/19.
+ *
+ * @author ice1000
+ */
+class BoneObject(res: ImageResource, id: Int, x: Double, y: Double) : ImageObject(res, id, x, y) {
+ override var died = false
+}
diff --git a/frice/src/main/kotlin/org/frice/game/obj/sub/ImageObject.kt b/frice/src/main/kotlin/org/frice/game/obj/sub/ImageObject.kt
new file mode 100644
index 0000000..803e334
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/obj/sub/ImageObject.kt
@@ -0,0 +1,72 @@
+package org.frice.game.obj.sub
+
+import android.graphics.Bitmap
+import org.frice.android.obj.CollideBox
+import org.frice.android.obj.FObject
+import org.frice.android.resource.image.ImageResource
+import org.frice.android.utils.graphics.shape.FRectangle
+
+/**
+ * Base GameObject class
+ *
+ * Created by ice1000 on 2016/8/13.
+ * @author ice1000
+ * @since v0.1
+ */
+open class ImageObject(var res: ImageResource, override var id: Int,
+ override var x: Double, override var y: Double) : FObject(), FObject.ImageOwner {
+ constructor(res: ImageResource, id: Int) : this(res, id, 0.0, 0.0)
+
+ constructor(res: ImageResource) : this(res, -1, 0.0, 0.0)
+
+ constructor(res: ImageResource, x: Double, y: Double) : this(res, -1, x, y)
+
+ constructor(res: Bitmap, x: Double, y: Double) : this(ImageResource.create(res), -1, x, y)
+
+ override fun getResource() = res
+
+ override fun isCollide(other: CollideBox): Boolean = when (other) {
+ is ShapeObject -> when (other.collideBox) {
+ is FRectangle -> this rectCollideRect other
+ // TODO
+ else -> this rectCollideRect other
+ }
+ is ImageObject -> this rectCollideRect other
+ else -> false
+ }
+
+ override val width: Double
+ get() = res.bitmap.width.toDouble()
+ override val height: Double
+ get() = res.bitmap.height.toDouble()
+
+ override val collideBox = FRectangle(res.bitmap.width, res.bitmap.height)
+ override var died = false
+
+ override fun scale(x: Double, y: Double) {
+// res.bitmap = res.bitmap.getScaledInstance((res.bitmap.width * x / 1000.0).toInt(),
+// (res.bitmap.height * y / 1000.0).toInt(), Image.SCALE_DEFAULT) as BufferedImage
+ TODO()
+// TODO()
+ }
+
+ override val image: Bitmap
+ get() = res.bitmap
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (other == null || other !is ImageObject) return false
+ if ((id != -1 && id == other.id) ||
+ res == other.res && x == other.x && y == other.y) return true
+ return false
+ }
+
+ override fun hashCode(): Int {
+ var result = res.hashCode()
+ result = 31 * result + id
+ result = 31 * result + x.hashCode()
+ result = 31 * result + y.hashCode()
+ result = 31 * result + anims.hashCode()
+ return result
+ }
+}
diff --git a/frice/src/main/kotlin/org/frice/game/obj/sub/ShapeObject.kt b/frice/src/main/kotlin/org/frice/game/obj/sub/ShapeObject.kt
new file mode 100644
index 0000000..5de67f1
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/obj/sub/ShapeObject.kt
@@ -0,0 +1,80 @@
+package org.frice.game.obj.sub
+
+import org.frice.android.obj.CollideBox
+import org.frice.android.obj.FObject
+import org.frice.android.resource.graphics.ColorResource
+import org.frice.android.utils.graphics.shape.FRectangle
+import org.frice.android.utils.graphics.shape.FShape
+import org.frice.game.anim.move.DoublePair
+
+/**
+ * an object with a utils and a shape, used to create an simple object quickly
+ * instead of load from an bitmap file.
+ *
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.1.1
+ */
+open class ShapeObject(var res: ColorResource, override val collideBox: FShape, override var id: Int,
+ override var x: Double, override var y: Double) : FObject() {
+ constructor(res: ColorResource, shape: FShape, x: Double, y: Double) : this(res, shape, -1, x, y)
+
+ constructor(res: ColorResource, shape: FShape, id: Int) : this(res, shape, id, 0.0, 0.0)
+
+ constructor(res: ColorResource, shape: FShape) : this(res, shape, -1)
+
+ private var scale = DoublePair(1.0, 1.0)
+
+ override var height: Double
+ get() = (collideBox.height * scale.y)
+ set(value) {
+ collideBox.height = (value / scale.y).toInt()
+ }
+
+ override var width: Double
+ get () = (collideBox.width * scale.x)
+ set(value) {
+ collideBox.width = (value / scale.x).toInt()
+ }
+
+ override var died = false
+
+ override fun isCollide(other: CollideBox): Boolean = when (other) {
+ is ShapeObject -> when (other.collideBox) {
+ is FRectangle -> when (collideBox) {
+ is FRectangle -> this rectCollideRect other
+// is FOval ->
+ else -> this rectCollideRect other
+ }
+// is FOval ->
+ else -> this rectCollideRect other
+ }
+ is ImageObject -> when (collideBox) {
+ is FRectangle -> this rectCollideRect other
+ else -> this rectCollideRect other
+ }
+ else -> false
+ }
+
+ override fun getResource() = res
+
+ override fun scale(x: Double, y: Double) {
+ scale.x += x
+ scale.y += y
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+// if (other == null || other !is ShapeObject) return false
+// if ((id != -1 && id == other.id) ||
+// res == other.res && x == other.x && y == other.y) return true
+ return false
+ }
+
+ override fun hashCode(): Int {
+ var result = res.hashCode()
+ result = 31 * result + id
+ return result
+ }
+
+}
diff --git a/frice/src/main/kotlin/org/frice/game/resource/FResource.kt b/frice/src/main/kotlin/org/frice/game/resource/FResource.kt
new file mode 100644
index 0000000..0c80c04
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/resource/FResource.kt
@@ -0,0 +1,10 @@
+package org.frice.game.resource
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.1.1
+ */
+interface FResource {
+ fun getResource(): Any
+}
\ No newline at end of file
diff --git a/frice/src/main/kotlin/org/frice/game/utils/data/Databases.kt b/frice/src/main/kotlin/org/frice/game/utils/data/Databases.kt
new file mode 100644
index 0000000..ce6b75c
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/utils/data/Databases.kt
@@ -0,0 +1,167 @@
+package org.frice.game.utils.data
+
+import org.frice.android.utils.message.error.FatalError
+import org.frice.game.utils.misc.forceGet
+import org.frice.game.utils.misc.forceLoop
+import org.frice.game.utils.misc.forceRun
+import org.w3c.dom.Document
+import org.w3c.dom.Element
+import java.io.File
+import java.util.*
+import javax.xml.parsers.DocumentBuilder
+import javax.xml.parsers.DocumentBuilderFactory
+import javax.xml.transform.TransformerFactory
+import javax.xml.transform.dom.DOMSource
+import javax.xml.transform.stream.StreamResult
+
+/**
+ * Created by ice1000 on 2016/8/20.
+ *
+ * @author ice1000
+ * @since v0.4.1
+ */
+interface Database {
+ fun insert(key: String, value: Any?)
+ // fun queryWithType(key: String, default: T): Any
+ fun query(key: String, default: Any): Any
+}
+
+/**
+ * Created by ice1000 on 2016/8/20.
+ *
+ * @author ice1000
+ * @since v0.4.1
+ */
+@Deprecated("use String extension instead of using database directly")
+class Preference(private val file: File) : Database {
+
+ @Deprecated("use String extension instead of using database directly")
+ constructor(path: String) : this(File(path))
+
+ val properties: Properties = Properties()
+
+ init {
+ if (!file.exists()) file.createNewFile()
+ forceRun { properties.load(file.inputStream()) }
+ }
+
+ override fun query(key: String, default: Any) = when (properties[key] as String) {
+ "true" -> true
+ "false" -> false
+ else -> forceGet(properties[key] ?: default) {
+ return@forceGet Integer.parseInt(properties[key] as String)
+ }
+ }
+
+ override fun insert(key: String, value: Any?) {
+ properties.put(key, value.toString())
+ properties.store(file.outputStream(), "Automatically generated by ice1000 Frice Engine")
+ }
+
+ fun list(): List> = properties.toList()
+}
+
+
+/**
+ * An Android-like XMLPreference.
+ *
+ * Created by ice1000 on 2016/8/15.
+ * @author ice1000
+ * @since 0.2.2
+ */
+@Deprecated("use String extension instead of using database directly")
+class XMLPreference constructor(val file: File) : Database {
+
+ @Deprecated("use String extension instead of using database directly")
+ constructor(path: String) : this(File(path))
+
+ private val builder: DocumentBuilder
+ private val doc: Document
+ private val root: Element
+
+ companion object {
+ @JvmField val ROOT = "PREFERENCE_CONST_ROOT"
+ @JvmField val TYPE = "PREFERENCE_CONST_TYPE"
+ @JvmField val VALUE = "PREFERENCE_CONST_VALUE"
+
+ @JvmField val TYPE_BYTE = "PREFERENCE_CONST_TYPE_BYTE"
+ @JvmField val TYPE_INT = "PREFERENCE_CONST_TYPE_INT"
+ @JvmField val TYPE_LONG = "PREFERENCE_CONST_TYPE_LONG"
+ @JvmField val TYPE_SHORT = "PREFERENCE_CONST_TYPE_SHORT"
+ @JvmField val TYPE_DOUBLE = "PREFERENCE_CONST_TYPE_DOUBLE"
+ @JvmField val TYPE_FLOAT = "PREFERENCE_CONST_TYPE_FLOAT"
+ @JvmField val TYPE_STRING = "PREFERENCE_CONST_TYPE_STRING"
+ @JvmField val TYPE_CHAR = "PREFERENCE_CONST_TYPE_CHAR"
+
+ private var instance: XMLPreference? = null
+
+ @JvmStatic fun getPreference(path: String) = getPreference(File(path))
+
+ @JvmStatic fun getPreference(file: File): XMLPreference {
+ if (instance == null) instance = XMLPreference(file)
+ return instance!!
+ }
+ }
+
+ init {
+ builder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
+ if (file.exists()) {
+ doc = builder.parse(file)
+ root = doc.documentElement
+ } else {
+ file.createNewFile()
+ doc = builder.newDocument()
+ root = doc.createElement(ROOT)
+ doc.appendChild(root)
+ save()
+ }
+ }
+
+ private fun save() {
+ val transformer = TransformerFactory.newInstance().newTransformer()
+ transformer.transform(DOMSource(doc), StreamResult(file))
+ }
+
+ override fun insert(key: String, value: Any?) = value.let {
+ forceLoop { root.removeChild(doc.getElementsByTagName(key).item(0)) }
+ val node = doc.createElement(key)
+ node.setAttribute(VALUE, value.toString())
+ node.setAttribute(TYPE, when (value) {
+ is Byte -> TYPE_BYTE
+ is Int -> TYPE_INT
+ is Long -> TYPE_LONG
+ is Short -> TYPE_SHORT
+ is Float -> TYPE_FLOAT
+ is Double -> TYPE_DOUBLE
+ is Char -> TYPE_CHAR
+ is String -> TYPE_STRING
+ else -> throw FatalError("invalid type!")
+ })
+ root.appendChild(node)
+ save()
+ }
+
+ override fun query(key: String, default: Any): Any {
+ val node = doc.getElementsByTagName(key).item(0)
+ val value: String?
+ try {
+ value = node.attributes.getNamedItem(VALUE).nodeValue
+ } catch (e: Throwable) {
+ return default
+ }
+ return forceGet(default) {
+ when (node.attributes.getNamedItem(TYPE).nodeValue) {
+ TYPE_BYTE -> value.toByte()
+ TYPE_INT -> value.toInt()
+ TYPE_LONG -> value.toLong()
+ TYPE_SHORT -> value.toShort()
+ TYPE_CHAR -> value.toCharArray()[0]
+ TYPE_FLOAT -> value.toFloat()
+ TYPE_DOUBLE -> value.toDouble()
+ TYPE_STRING -> value
+ else -> default
+ }
+ }
+ }
+}
+
diff --git a/frice/src/main/kotlin/org/frice/game/utils/misc/KotlinUtils.kt b/frice/src/main/kotlin/org/frice/game/utils/misc/KotlinUtils.kt
new file mode 100644
index 0000000..d630709
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/utils/misc/KotlinUtils.kt
@@ -0,0 +1,74 @@
+package org.frice.game.utils.misc
+
+/**
+ * Kotlin language extension
+ * for Kotlin only
+ *
+ *
+ * Created by ice1000 on 2016/8/17.
+ * @author ice1000
+ * @since v0.3.2
+ */
+
+inline fun loop(block: () -> Unit) {
+ while (true) block()
+}
+
+inline fun T.loop(count: Int, block: T.(Int) -> Unit): T {
+ for (index in 0..count - 1) block(this, index)
+ return this
+}
+
+inline fun loop(count: Int, block: (Int) -> Unit) = repeat(count, block)
+
+inline fun loopIf(condition: () -> Boolean, block: () -> Unit) = loop { if (condition()) block() }
+
+/**
+ * less blocks, less byte code generated.
+ */
+inline fun loopIf(condition: Boolean, block: () -> Unit) = loopIf({ condition }, block)
+
+inline fun T.forceRun(block: T.() -> Unit): T {
+ try {
+ block(this)
+ } catch (e: Throwable) {
+ }
+ return this
+}
+
+inline fun forceRun(block: () -> Unit) {
+ try {
+ block()
+ } catch (e: Throwable) {
+ }
+}
+
+inline fun forceGet(default: Any, block: () -> Any): Any = try {
+ block()
+} catch (e: Throwable) {
+ default
+}
+
+/**
+ * it will exit when meets an uncaught exception.
+ */
+inline fun forceLoop(block: () -> Unit) = forceRun { loop(block) }
+
+fun pause(length: Int) = pause(length.toLong())
+
+fun pause(length: Double) = pause(length.toLong())
+
+fun pause(length: Long) = Thread.sleep(length)
+
+inline fun unless(condition: Boolean, block: () -> Unit) {
+ if (!condition) block()
+}
+
+inline fun until(condition: Boolean, block: () -> Unit) {
+ while (!condition) block()
+}
+
+/**
+ * an anko-like async block
+ */
+inline fun async(crossinline block: () -> Unit) = Thread({ block() }).start()
diff --git a/frice/src/main/kotlin/org/frice/game/utils/misc/TestUtils.kt b/frice/src/main/kotlin/org/frice/game/utils/misc/TestUtils.kt
new file mode 100644
index 0000000..bde1aad
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/utils/misc/TestUtils.kt
@@ -0,0 +1,18 @@
+package org.frice.game.utils.misc
+
+/**
+ * Created by ice1000 on 2016/9/9 0009.
+ *
+ * @author ice1000
+ * @since v0.5.1
+ */
+
+class AssertionException() : Exception()
+
+inline fun assert(block: () -> Boolean) {
+ if (!block()) throw AssertionException()
+}
+
+fun assert(boolean: Boolean) {
+ if (boolean) throw AssertionException()
+}
\ No newline at end of file
diff --git a/frice/src/main/kotlin/org/frice/game/utils/time/Timers.kt b/frice/src/main/kotlin/org/frice/game/utils/time/Timers.kt
new file mode 100644
index 0000000..3fd3759
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/utils/time/Timers.kt
@@ -0,0 +1,50 @@
+package org.frice.game.utils.time
+
+/**
+ * @param times if the value is -1, will loop.
+ *
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.2
+ */
+class FTimeListener(time: Int, times: Int, val timeUp: () -> Unit) : FTimer(time, times) {
+ constructor(time: Int, times: Int, timeUp: OnTimeEvent) : this(time, times, { timeUp.execute() })
+ constructor(time: Int, timeUp: OnTimeEvent) : this(time, -1, { timeUp.execute() })
+ constructor(time: Int, timeUp: () -> Unit) : this(time, -1, timeUp)
+
+ fun check() = if (ended() && times != 0) {
+ if (times > 0) times--
+ timeUp.invoke()
+ } else Unit
+}
+
+/**
+ * Created by ice1000 on 2016/8/14.
+ * @author ice1000
+ * @since v0.2
+ */
+interface OnTimeEvent {
+ fun execute()
+}
+
+
+/**
+ * @param times if the value is -1, it will loop.
+ *
+ * Created by ice1000 on 2016/8/13.
+ * @author ice1000
+ * @since v0.1
+ */
+open class FTimer(protected val time: Int, times: Int) {
+ constructor(time: Int) : this(time, -1)
+
+ var times = times
+ protected set
+ private var start = System.currentTimeMillis()
+
+ fun ended(): Boolean = if (System.currentTimeMillis() - start > time && times != 0) {
+ start = System.currentTimeMillis()
+ if (times > 0) times--
+ true
+ } else false
+}
diff --git a/frice/src/main/kotlin/org/frice/game/utils/web/HTMLUtils.kt b/frice/src/main/kotlin/org/frice/game/utils/web/HTMLUtils.kt
new file mode 100644
index 0000000..54814c9
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/utils/web/HTMLUtils.kt
@@ -0,0 +1,44 @@
+package org.frice.game.utils.web
+
+import org.frice.game.utils.misc.loop
+import java.util.*
+
+/**
+ * HTML tags finder
+ * Created by ice1000 on 2016/9/3.
+ *
+ * @author ice1000
+ * @since v0.5
+ */
+
+object HTMLUtils {
+ @JvmStatic fun findTag(html: String, tag: CharArray): ArrayList {
+ val c = html.toCharArray()
+ val tags = ArrayList()
+ var tagMark = false
+ var tagStart = 0
+ loop(c.size - tag.size + 2) { i ->
+ // find start index
+ if (c[i] == '<') {
+ tagMark = true
+ // cannot use loop{} or forEach.
+ // for the reason that I have to break it
+ for (j in 0..tag.size - 1) {
+ if (c[i + j + 1] == tag[j]) {
+ tagMark = false
+ break
+ }
+ }
+ if (tagMark) tagStart = i
+ }
+ // find end index
+ if (tagMark && c[i] == '>') {
+ tagMark = false
+ tags.add(html.substring(tagStart..i))
+ }
+ }
+ return tags
+ }
+
+ @JvmStatic fun findTag(html: String, tag: String) = findTag(html, tag.toCharArray())
+}
\ No newline at end of file
diff --git a/frice/src/main/kotlin/org/frice/game/utils/web/WebUtils.kt b/frice/src/main/kotlin/org/frice/game/utils/web/WebUtils.kt
new file mode 100644
index 0000000..66ece1c
--- /dev/null
+++ b/frice/src/main/kotlin/org/frice/game/utils/web/WebUtils.kt
@@ -0,0 +1,23 @@
+package org.frice.game.utils.web
+
+import android.graphics.BitmapFactory
+import java.net.URL
+
+/**
+ * Created by ice1000 on 2016/9/3.
+ *
+ * @author ice1000
+ * @since v0.5
+ */
+object WebUtils {
+ @JvmStatic fun readText(url: URL) = url.readText()
+ @JvmStatic fun readText(url: String) = readText(URL(url))
+
+ @JvmStatic fun readBytes(url: URL) = url.readBytes()
+ @JvmStatic fun readBytes(url: String) = readBytes(URL(url))
+
+ @JvmStatic fun readImage(url: String) = BitmapFactory.decodeFile(url)!!
+
+ @JvmStatic fun readImages(url: String) = HTMLUtils.findTag(readText(url), "img")
+ @JvmStatic fun readImages(url: URL) = HTMLUtils.findTag(readText(url), "img")
+}
diff --git a/frice/src/test/java/org/frice/Test.java b/frice/src/test/java/org/frice/Test.java
new file mode 100644
index 0000000..b230ac4
--- /dev/null
+++ b/frice/src/test/java/org/frice/Test.java
@@ -0,0 +1,17 @@
+package org.frice;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index e7b4def..57e67ed 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app'
+include ':app', ':org.frice'