Former-commit-id: 9738d12e5a468b71142745fbf0ce02fcf1ca623c
Former-commit-id: a26b80a1990996d9c05b0909128c210e0f897312
This commit is contained in:
Song Minjae
2016-05-13 20:17:31 +09:00
parent d3e1b17abd
commit 46a3065423
17 changed files with 211 additions and 63 deletions

View File

@@ -2,6 +2,7 @@ package net.torvald.terrarum.gameactors
import net.torvald.random.HQRNG
import net.torvald.terrarum.Terrarum
import net.torvald.terrarum.itemproperties.ItemPropCodex
import org.newdawn.slick.GameContainer
/**
@@ -21,11 +22,13 @@ abstract class Actor : Comparable<Actor>, Runnable {
override fun equals(other: Any?) = referenceID == (other as Actor).referenceID
override fun hashCode() = referenceID
override fun toString() = if (actorValue.getAsString(AVKey.NAME).isNullOrEmpty())
override fun toString() = "Actor, " + if (actorValue.getAsString(AVKey.NAME).isNullOrEmpty())
"ID: ${hashCode()}"
else
"ID: ${hashCode()} (${actorValue.getAsString(AVKey.NAME)})"
override fun compareTo(other: Actor): Int = this.referenceID - other.referenceID
override fun compareTo(other: Actor): Int = (this.referenceID - other.referenceID).sign()
fun Int.sign(): Int = if (this > 0) 1 else if (this < 0) -1 else this
/**
* Usage:
@@ -33,11 +36,10 @@ abstract class Actor : Comparable<Actor>, Runnable {
* override var referenceID: Int = generateUniqueReferenceID()
*/
fun generateUniqueReferenceID(): Int {
fun Int.abs() = if (this < 0) -this else this
var ret: Int
do {
ret = HQRNG().nextInt().abs() // set new ID
} while (Terrarum.game.hasActor(ret)) // check for collision
ret = HQRNG().nextInt().and(0x7FFFFFFF) // set new ID
} while (Terrarum.game.hasActor(ret) || ret < ItemPropCodex.ITEM_UNIQUE_MAX) // check for collision
return ret
}
}

View File

@@ -91,7 +91,7 @@ open class ActorWithBody constructor() : Actor(), Visible {
actorValue[AVKey.BASEMASS] = value
}
/** Valid range: [0, 1] */
var elasticity = 0.0
var elasticity: Double = 0.0
set(value) {
if (value < 0)
throw IllegalArgumentException("[ActorWithBody] Invalid elasticity value: $value; valid elasticity value is [0, 1].")
@@ -103,6 +103,18 @@ open class ActorWithBody constructor() : Actor(), Visible {
field = value * ELASTICITY_MAX
}
@Transient private val ELASTICITY_MAX = 0.993 // No perpetual motion!
/**
* what pretty much every physics engine has, instead of my 'elasticity'
*
* This is just a simple macro for 'elasticity'.
*
* Formula: restitution = 1.0 - elasticity
*/
var restitution: Double
set(value) { elasticity = 1.0 - value }
get() = 1.0 - elasticity
private var density = 1000.0
/**
@@ -175,9 +187,11 @@ open class ActorWithBody constructor() : Actor(), Visible {
@Transient val DYNAMIC = 2
@Transient val STATIC = 3
private val SLEEP_THRE = 0.125
private val CCD_THRE = 1.0
private val CCD_TICK = 0.125
private val SLEEP_THRE = 1.0 / 16.0
private val CCD_TICK = 1.0 / 16.0
internal var walledLeft = false
internal var walledRight = false
init {
@@ -297,6 +311,9 @@ open class ActorWithBody constructor() : Actor(), Visible {
clampNextHitbox()
clampHitbox()
}
walledLeft = isColliding(CONTACT_AREA_LEFT, -1, 0)
walledRight = isColliding(CONTACT_AREA_RIGHT, 1, 0)
}
}
@@ -534,11 +551,34 @@ open class ActorWithBody constructor() : Actor(), Visible {
val delta: Vector2 = Vector2(hitbox.toVector() - nextHitbox.toVector()) // we need to traverse back, so may as well negate at the first place
val ccdDelta = delta.setMagnitude(CCD_TICK)
while (isColliding(CONTACT_AREA_LEFT) || isColliding(CONTACT_AREA_RIGHT)
|| isColliding(CONTACT_AREA_TOP) || isColliding(CONTACT_AREA_BOTTOM)
) {
// while still colliding, CCD to the 'delta'
nextHitbox.translate(ccdDelta)
if (ccdDelta.x.abs() >= SLEEP_THRE || ccdDelta.y.abs() >= SLEEP_THRE) { // regular situation
// CCD to delta while still colliding
while (isColliding(CONTACT_AREA_LEFT) || isColliding(CONTACT_AREA_RIGHT)
|| isColliding(CONTACT_AREA_TOP) || isColliding(CONTACT_AREA_BOTTOM)
) {
nextHitbox.translate(ccdDelta)
}
}
else { // stuck while standing still
// CCD upward
var upwardDelta = 0.0
while (isColliding(CONTACT_AREA_LEFT) || isColliding(CONTACT_AREA_RIGHT)
|| isColliding(CONTACT_AREA_TOP) || isColliding(CONTACT_AREA_BOTTOM)
) {
nextHitbox.translate(0.0, -CCD_TICK)
upwardDelta += CCD_TICK
if (upwardDelta >= TSIZE) break
/* TODO CCD in order of:
.. 10 11 12 13 14 ..
.. 08 03 04 05 09 ..
.. 06 01 [] 02 07 ..
until the stucking is resolved
*/
}
}
}
}
@@ -546,10 +586,9 @@ open class ActorWithBody constructor() : Actor(), Visible {
private fun applyNormalForce() {
if (!isNoCollideWorld) {
// axis Y
if (veloY >= 0) { // check downward
if (moveDelta.y >= 0) { // check downward
if (isColliding(CONTACT_AREA_BOTTOM) || isColliding(CONTACT_AREA_BOTTOM, 0, 1)) {
// the actor is hitting the ground
//veloY = 0.0 // reset veloY, simulating normal force
hitAndReflectY()
grounded = true
}
@@ -557,33 +596,30 @@ open class ActorWithBody constructor() : Actor(), Visible {
grounded = false
}
}
else if (veloY < 0) { // check upward
else if (moveDelta.y < 0) { // check upward
grounded = false
if (isColliding(CONTACT_AREA_TOP) || isColliding(CONTACT_AREA_TOP, 0, -1)) {
// the actor is hitting the ceiling
//veloY = 0.0 // reset veloY, simulating normal force
hitAndReflectY()
}
else { // the actor is not grounded at all
}
}
// axis X
if (veloX >= 0.5) { // check right
if (moveDelta.x > 0) { // check right
if ((isColliding(CONTACT_AREA_RIGHT) && !isColliding(CONTACT_AREA_LEFT))
|| (isColliding(CONTACT_AREA_RIGHT, 1, 0) && !isColliding(CONTACT_AREA_LEFT, 0, -1))) {
// the actor is hitting the right wall
//veloX = 0.0 // reset veloX, simulating normal force
hitAndReflectX()
}
else {
}
}
else if (veloX <= -0.5) { // check left
else if (moveDelta.x < 0) { // check left
// System.out.println("collidingleft");
if ((isColliding(CONTACT_AREA_LEFT) && !isColliding(CONTACT_AREA_RIGHT))
|| (isColliding(CONTACT_AREA_LEFT, -1, 0) && !isColliding(CONTACT_AREA_RIGHT, 1, 0))) {
// the actor is hitting the left wall
//veloX = 0.0 // reset veloX, simulating normal force
hitAndReflectX()
}
else {
@@ -597,18 +633,22 @@ open class ActorWithBody constructor() : Actor(), Visible {
private fun hitAndReflectX() {
if ((veloX * elasticity).abs() > SLEEP_THRE) {
veloX *= -elasticity
walkX *= -elasticity
}
else {
veloX = 0.0
walkX = 0.0
}
}
private fun hitAndReflectY() {
if ((veloY * elasticity).abs() > SLEEP_THRE) {
veloY *= -elasticity
walkY *= -elasticity
}
else {
veloY = 0.0
walkY *= 0.0
}
}

View File

@@ -16,7 +16,7 @@ class DroppedItem constructor(itemID: Int) : ActorWithBody() {
isVisible = true
mass = if (itemID < 4096)
mass = if (itemID < TilePropCodex.TILE_UNIQUE_MAX)
TilePropCodex.getProp(itemID).density / 1000.0
else
ItemPropCodex.getProp(itemID).mass

View File

@@ -73,7 +73,7 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
}
companion object {
@Transient internal const val ACCEL_MULT_IN_FLIGHT: Double = 0.31
@Transient internal const val ACCEL_MULT_IN_FLIGHT: Double = 0.21 // TODO air control still too 'slippery' with 0.31, lower the value!
@Transient internal const val WALK_ACCEL_BASE: Double = 0.67
@Transient const val PLAYER_REF_ID: Int = 0x51621D
@@ -130,33 +130,35 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
* @author minjaesong
*/
private fun walkHorizontal(left: Boolean, absAxisVal: Float) {
readonly_totalX = //veloX +
/*actorValue.getAsDouble(AVKey.ACCEL)!! *
if ((!walledLeft && left) || (!walledRight && !left)) {
readonly_totalX = //veloX +
/*actorValue.getAsDouble(AVKey.ACCEL)!! *
actorValue.getAsDouble(AVKey.ACCELMULT)!! *
Math.sqrt(scale) *
applyAccelRealism(walkPowerCounter) *
(if (left) -1 else 1).toFloat() *
absAxisVal*/
actorValue.getAsDouble(AVKey.ACCEL)!! *
actorValue.getAsDouble(AVKey.ACCELMULT)!! *
Math.sqrt(scale) *
applyVelo(walkCounter) *
(if (left) -1 else 1).toFloat() *
absAxisVal
actorValue.getAsDouble(AVKey.ACCEL)!! *
actorValue.getAsDouble(AVKey.ACCELMULT)!! *
Math.sqrt(scale) *
applyVelo(walkCounter) *
(if (left) -1 else 1).toFloat() *
absAxisVal
//applyForce(Vector2(readonly_totalX, 0.0))
walkX += readonly_totalX
walkX = absClamp(walkX, actorValue.getAsDouble(AVKey.SPEED)!! * actorValue.getAsDouble(AVKey.SPEEDMULT)!!)
//applyForce(Vector2(readonly_totalX, 0.0))
walkX += readonly_totalX
walkX = absClamp(walkX, actorValue.getAsDouble(AVKey.SPEED)!! * actorValue.getAsDouble(AVKey.SPEEDMULT)!!)
walkCounter += 1
walkCounter += 1
// Heading flag
if (left)
walkHeading = LEFT
else
walkHeading = RIGHT
// Heading flag
if (left)
walkHeading = LEFT
else
walkHeading = RIGHT
println("$walkCounter: ${readonly_totalX}")
// println("$walkCounter: ${readonly_totalX}")
}
}
/**
@@ -243,7 +245,9 @@ class Player : ActorWithBody, Controllable, Pocketed, Factionable, Luminous, Lan
}
/**
* See ./work_files/Jump\ power\ by\ pressing\ time.gcx
* See ./work_files/Jump power by pressing time.gcx
*
* TODO linear function (play Super Mario Bros. and you'll get what I'm talking about)
*/
private fun jump() {
if (jumping) {

View File

@@ -7,17 +7,16 @@ import java.util.HashSet
/**
* Created by minjaesong on 16-02-15.
*/
class Faction(factionName: String) {
class Faction(name: String) : Comparable<Faction> {
lateinit var factionName: String
var factionName: String = name
lateinit var factionAmicable: HashSet<String>
lateinit var factionNeutral: HashSet<String>
lateinit var factionHostile: HashSet<String>
lateinit var factionFearful: HashSet<String>
var factionID: Long = generateUniqueID()
var referenceID: Long = generateUniqueID()
init {
this.factionName = factionName
factionAmicable = HashSet<String>()
factionNeutral = HashSet<String>()
factionHostile = HashSet<String>()
@@ -60,8 +59,18 @@ class Faction(factionName: String) {
factionFearful.remove(faction)
}
fun generateUniqueID(): Long {
fun Long.abs() = if (this < 0) -this else this
return HQRNG().nextLong().abs() // set new ID
private fun generateUniqueID(): Long {
var ret: Long
do {
ret = HQRNG().nextLong().or(0x80000000L).and(0xFFFFFFFFL) // guaranteed to be 2147483648..4294967295
} while (FactionCodex.hasFaction(ret)) // check for collision
return ret
}
override fun equals(other: Any?) = referenceID == (other as Faction).referenceID
override fun hashCode() = (referenceID - 0x80000000L).toInt()
override fun toString() = "Faction, ID: $referenceID ($factionName)"
override fun compareTo(other: Faction): Int = (this.referenceID - other.referenceID).toInt().sign()
fun Int.sign(): Int = if (this > 0) 1 else if (this < 0) -1 else this
}

View File

@@ -0,0 +1,66 @@
package net.torvald.terrarum.gameactors.faction
import net.torvald.terrarum.Terrarum
import java.util.*
/**
* Created by minjaesong on 16-05-09.
*/
object FactionCodex {
val factionContainer = ArrayList<Faction>()
fun hasFaction(ID: Long): Boolean =
if (factionContainer.size == 0)
false
else
factionContainer.binarySearch(ID) >= 0
fun addFaction(faction: Faction) {
if (hasFaction(faction.referenceID))
throw RuntimeException("Faction with ID ${faction.referenceID} already exists.")
factionContainer.add(faction)
insertionSortLastElem(factionContainer) // we can do this as we are only adding single actor
}
fun getFactionByID(ID: Long): Faction {
if (factionContainer.size == 0) throw IllegalArgumentException("Faction with ID $ID does not exist.")
val index = factionContainer.binarySearch(ID)
if (index < 0)
throw IllegalArgumentException("Faction with ID $ID does not exist.")
else
return factionContainer[index]
}
private fun insertionSortLastElem(arr: ArrayList<Faction>) {
var x: Faction
var j: Int
var index: Int = arr.size - 1
x = arr[index]
j = index - 1
while (j > 0 && arr[j] > x) {
arr[j + 1] = arr[j]
j -= 1
}
arr[j + 1] = x
}
private fun ArrayList<Faction>.binarySearch(ID: Long): Int {
// code from collections/Collections.kt
var low = 0
var high = factionContainer.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)
if (ID > midVal.referenceID)
low = mid + 1
else if (ID < midVal.referenceID)
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
}

View File

@@ -20,7 +20,6 @@ object FactionFactory {
val jsonObj = JsonFetcher.readJson(JSONPATH + filename)
val factionObj = Faction(jsonObj.get("factionname").asString)
jsonObj.get("factionamicable").asJsonArray.forEach { s -> factionObj.addFactionAmicable(s.asString) }
jsonObj.get("factionneutral").asJsonArray.forEach { s -> factionObj.addFactionNeutral(s.asString) }
jsonObj.get("factionhostile").asJsonArray.forEach { s -> factionObj.addFactionHostile(s.asString) }