World Click events

let's hope it works w/o tests as I can't get to the Ingame now
This commit is contained in:
minjaesong
2018-10-01 23:51:04 +09:00
parent 03b642ddd3
commit 98539e698f
25 changed files with 341 additions and 226 deletions

View File

@@ -0,0 +1,214 @@
package net.torvald.terrarum
import com.badlogic.gdx.Screen
import com.badlogic.gdx.graphics.g2d.SpriteBatch
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.ui.ConsoleWindow
import java.util.ArrayList
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import javax.swing.JOptionPane
open class IngameInstance(val batch: SpriteBatch) : Screen {
var screenZoom = 1.0f
val ZOOM_MAXIMUM = 4.0f
val ZOOM_MINIMUM = 0.5f
open lateinit var consoleHandler: ConsoleWindow
open lateinit var world: GameWorld
/** The actor the game is currently allowing you to control.
*
* Most of the time it'd be the "player", but think about the case where you have possessed
* some random actor of the game. Now that actor is now actorNowPlaying, the actual gamer's avatar
* (reference ID of 0x91A7E2) (must) stay in the actorContainer, but it's not a actorNowPlaying.
*
* Nullability of this property is believed to be unavoidable (trust me!). I'm sorry for the inconvenience.
*/
open var actorNowPlaying: ActorHumanoid? = null
val ACTORCONTAINER_INITIAL_SIZE = 64
val actorContainer = ArrayList<Actor>(ACTORCONTAINER_INITIAL_SIZE)
val actorContainerInactive = ArrayList<Actor>(ACTORCONTAINER_INITIAL_SIZE)
override fun hide() {
}
override fun show() {
}
override fun render(delta: Float) {
}
override fun pause() {
}
override fun resume() {
}
override fun resize(width: Int, height: Int) {
}
override fun dispose() {
}
////////////
// EVENTS //
////////////
/**
* Event for triggering held item's `startPrimaryUse(Float)`
*/
open fun worldPrimaryClickStart(delta: Float) {
}
/**
* Event for triggering held item's `endPrimaryUse(Float)`
*/
open fun worldPrimaryClickEnd(delta: Float) {
}
/**
* Event for triggering held item's `startSecondaryUse(Float)`
*/
open fun worldSecondaryClickStart(delta: Float) {
}
/**
* Event for triggering held item's `endSecondaryUse(Float)`
*/
open fun worldSecondaryClickEnd(delta: Float) {
}
///////////////////////
// UTILITY FUNCTIONS //
///////////////////////
fun getActorByID(ID: Int): Actor {
if (actorContainer.size == 0 && actorContainerInactive.size == 0)
throw IllegalArgumentException("Actor with ID $ID does not exist.")
var index = actorContainer.binarySearch(ID)
if (index < 0) {
index = actorContainerInactive.binarySearch(ID)
if (index < 0) {
JOptionPane.showMessageDialog(
null,
"Actor with ID $ID does not exist.",
null, JOptionPane.ERROR_MESSAGE
)
throw IllegalArgumentException("Actor with ID $ID does not exist.")
}
else
return actorContainerInactive[index]
}
else
return actorContainer[index]
}
fun ArrayList<*>.binarySearch(actor: Actor) = this.binarySearch(actor.referenceID!!)
fun ArrayList<*>.binarySearch(ID: Int): Int {
// code from collections/Collections.kt
var low = 0
var high = this.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)!!
if (ID > midVal.hashCode())
low = mid + 1
else if (ID < midVal.hashCode())
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
open fun removeActor(ID: Int) = removeActor(getActorByID(ID))
/**
* get index of the actor and delete by the index.
* we can do this as the list is guaranteed to be sorted
* and only contains unique values.
*
* Any values behind the index will be automatically pushed to front.
* This is how remove function of [java.util.ArrayList] is defined.
*/
open fun removeActor(actor: Actor?) {
if (actor == null) return
val indexToDelete = actorContainer.binarySearch(actor.referenceID!!)
if (indexToDelete >= 0) {
actorContainer.removeAt(indexToDelete)
}
}
/**
* Check for duplicates, append actor and sort the list
*/
open fun addNewActor(actor: Actor?) {
if (actor == null) return
if (theGameHasActor(actor.referenceID!!)) {
throw Error("The actor $actor already exists in the game")
}
else {
actorContainer.add(actor)
insertionSortLastElem(actorContainer) // we can do this as we are only adding single actor
}
}
fun isActive(ID: Int): Boolean =
if (actorContainer.size == 0)
false
else
actorContainer.binarySearch(ID) >= 0
fun isInactive(ID: Int): Boolean =
if (actorContainerInactive.size == 0)
false
else
actorContainerInactive.binarySearch(ID) >= 0
/**
* actorContainer extensions
*/
fun theGameHasActor(actor: Actor?) = if (actor == null) false else theGameHasActor(actor.referenceID!!)
fun theGameHasActor(ID: Int): Boolean =
isActive(ID) || isInactive(ID)
fun insertionSortLastElem(arr: ArrayList<Actor>) {
lock(ReentrantLock()) {
var j = arr.lastIndex - 1
val x = arr.last()
while (j >= 0 && arr[j] > x) {
arr[j + 1] = arr[j]
j -= 1
}
arr[j + 1] = x
}
}
inline fun lock(lock: Lock, body: () -> Unit) {
lock.lock()
try {
body()
}
finally {
lock.unlock()
}
}
}

View File

@@ -18,11 +18,8 @@ import net.torvald.terrarum.AppLoader.printdbg
import net.torvald.terrarum.AppLoader.printdbgerr
import net.torvald.terrarum.gameactors.Actor
import net.torvald.terrarum.gameactors.ActorID
import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.imagefont.TinyAlphNum
import net.torvald.terrarum.itemproperties.ItemCodex
import net.torvald.terrarum.modulebasegame.gameactors.ActorHumanoid
import net.torvald.terrarum.ui.ConsoleWindow
import net.torvald.terrarum.utils.JsonFetcher
import net.torvald.terrarum.utils.JsonWriter
import net.torvald.terrarum.worlddrawer.FeaturesDrawer
@@ -33,10 +30,6 @@ import org.lwjgl.BufferUtils
import org.lwjgl.input.Controllers
import java.io.File
import java.io.IOException
import java.util.*
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import javax.swing.JOptionPane
@@ -714,174 +707,6 @@ object Terrarum : Screen {
}
}
open class IngameInstance(val batch: SpriteBatch) : Screen {
var screenZoom = 1.0f
val ZOOM_MAXIMUM = 4.0f
val ZOOM_MINIMUM = 0.5f
open lateinit var consoleHandler: ConsoleWindow
open lateinit var world: GameWorld
/** The actor the game is currently allowing you to control.
*
* Most of the time it'd be the "player", but think about the case where you have possessed
* some random actor of the game. Now that actor is now actorNowPlaying, the actual gamer's avatar
* (reference ID of 0x91A7E2) (must) stay in the actorContainer, but it's not a actorNowPlaying.
*
* Nullability of this property is believed to be unavoidable (trust me!). I'm sorry for the inconvenience.
*/
open var actorNowPlaying: ActorHumanoid? = null
val ACTORCONTAINER_INITIAL_SIZE = 64
val actorContainer = ArrayList<Actor>(ACTORCONTAINER_INITIAL_SIZE)
val actorContainerInactive = ArrayList<Actor>(ACTORCONTAINER_INITIAL_SIZE)
override fun hide() {
}
override fun show() {
}
override fun render(delta: Float) {
}
override fun pause() {
}
override fun resume() {
}
override fun resize(width: Int, height: Int) {
}
override fun dispose() {
}
fun getActorByID(ID: Int): Actor {
if (actorContainer.size == 0 && actorContainerInactive.size == 0)
throw IllegalArgumentException("Actor with ID $ID does not exist.")
var index = actorContainer.binarySearch(ID)
if (index < 0) {
index = actorContainerInactive.binarySearch(ID)
if (index < 0) {
JOptionPane.showMessageDialog(
null,
"Actor with ID $ID does not exist.",
null, JOptionPane.ERROR_MESSAGE
)
throw IllegalArgumentException("Actor with ID $ID does not exist.")
}
else
return actorContainerInactive[index]
}
else
return actorContainer[index]
}
fun ArrayList<*>.binarySearch(actor: Actor) = this.binarySearch(actor.referenceID!!)
fun ArrayList<*>.binarySearch(ID: Int): Int {
// code from collections/Collections.kt
var low = 0
var high = this.size - 1
while (low <= high) {
val mid = (low + high).ushr(1) // safe from overflows
val midVal = get(mid)!!
if (ID > midVal.hashCode())
low = mid + 1
else if (ID < midVal.hashCode())
high = mid - 1
else
return mid // key found
}
return -(low + 1) // key not found
}
open fun removeActor(ID: Int) = removeActor(getActorByID(ID))
/**
* get index of the actor and delete by the index.
* we can do this as the list is guaranteed to be sorted
* and only contains unique values.
*
* Any values behind the index will be automatically pushed to front.
* This is how remove function of [java.util.ArrayList] is defined.
*/
open fun removeActor(actor: Actor?) {
if (actor == null) return
val indexToDelete = actorContainer.binarySearch(actor.referenceID!!)
if (indexToDelete >= 0) {
actorContainer.removeAt(indexToDelete)
}
}
/**
* Check for duplicates, append actor and sort the list
*/
open fun addNewActor(actor: Actor?) {
if (actor == null) return
if (theGameHasActor(actor.referenceID!!)) {
throw Error("The actor $actor already exists in the game")
}
else {
actorContainer.add(actor)
insertionSortLastElem(actorContainer) // we can do this as we are only adding single actor
}
}
fun isActive(ID: Int): Boolean =
if (actorContainer.size == 0)
false
else
actorContainer.binarySearch(ID) >= 0
fun isInactive(ID: Int): Boolean =
if (actorContainerInactive.size == 0)
false
else
actorContainerInactive.binarySearch(ID) >= 0
/**
* actorContainer extensions
*/
fun theGameHasActor(actor: Actor?) = if (actor == null) false else theGameHasActor(actor.referenceID!!)
fun theGameHasActor(ID: Int): Boolean =
isActive(ID) || isInactive(ID)
fun insertionSortLastElem(arr: ArrayList<Actor>) {
lock(ReentrantLock()) {
var j = arr.lastIndex - 1
val x = arr.last()
while (j >= 0 && arr[j] > x) {
arr[j + 1] = arr[j]
j -= 1
}
arr[j + 1] = x
}
}
inline fun lock(lock: Lock, body: () -> Unit) {
lock.lock()
try {
body()
}
finally {
lock.unlock()
}
}
}
inline fun SpriteBatch.inUse(action: (SpriteBatch) -> Unit) {
this.begin()
action(this)

View File

@@ -507,11 +507,21 @@ open class ActorWBMovable(val world: GameWorld, renderOrder: RenderOrder, val im
*/
val D: Vector2 = Vector2(externalForce.x.magnSqr(), externalForce.y.magnSqr()) * dragCoefficient * 0.5 * A// * tileDensityFluid.toDouble()
val V: Vector2 = (W - D) / Terrarum.TARGET_FPS.toDouble() * SI_TO_GAME_ACC
val V: Vector2 = (W - D) / Terrarum.TARGET_FPS * SI_TO_GAME_ACC
return V
}
/**
* Event for collision (event gets fired when it collided with the world or other actors)
*
* This event may fired two or more times per update.
*/
open fun collided(other: Array<CollisionMessage>) {
}
data class CollisionMessage(val targetID: Int, val AkspfisWorld: Boolean)
/**
* Apply gravitation to the every falling body (unless not levitating)
*
@@ -663,6 +673,14 @@ open class ActorWBMovable(val world: GameWorld, renderOrder: RenderOrder, val im
2, 7 -> { newHitbox.translatePosY( - newHitbox.endY.modTileDelta()) ; bounceY = true }
}
// fire Collision Event with one/two/three-side collision
// for the ease of writing, this jumptable is separated from above.
when (selfCollisionStatus) {
// TODO compose CollisionInfo and fire collided()
}
// two-side collision
if (selfCollisionStatus in listOf(3,6,9,12)) {
debug1("twoside collision $selfCollisionStatus")

View File

@@ -115,14 +115,14 @@ class IngameController(val ingame: Ingame) : InputAdapter() {
override fun touchUp(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
// don't separate Player from this! Physics will break, esp. airborne manoeuvre
if (ingame.canPlayerControl) {
val itemOnGrip = ingame.actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP) ?: null
// fire world click events; the event is defined as Ingame's (or any others') WorldClick event
if (ingame.uiContainer.map { if ((it.isOpening || it.isOpened) && it.mouseUp) 1 else 0 }.sum() == 0) { // no UI on the mouse, right?
if (itemOnGrip != null) {
if (button == Terrarum.getConfigInt("mouseprimary")) {
itemOnGrip.endPrimaryUse(Terrarum.deltaTime)
ingame.worldPrimaryClickEnd(Terrarum.deltaTime)
}
if (button == Terrarum.getConfigInt("mousesecondary")) {
itemOnGrip.endSecondaryUse(Terrarum.deltaTime)
ingame.worldSecondaryClickEnd(Terrarum.deltaTime)
}
}
}
@@ -144,6 +144,21 @@ class IngameController(val ingame: Ingame) : InputAdapter() {
}
override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean {
// don't separate Player from this! Physics will break, esp. airborne manoeuvre
if (ingame.canPlayerControl) {
// fire world click events; the event is defined as Ingame's (or any others') WorldClick event
if (ingame.uiContainer.map { if ((it.isOpening || it.isOpened) && it.mouseUp) 1 else 0 }.sum() == 0) { // no UI on the mouse, right?
if (button == Terrarum.getConfigInt("mouseprimary")) {
ingame.worldPrimaryClickStart(Terrarum.deltaTime)
}
if (button == Terrarum.getConfigInt("mousesecondary")) {
ingame.worldSecondaryClickStart(Terrarum.deltaTime)
}
}
}
ingame.uiContainer.forEach { it.touchDown(screenX, screenY, pointer, button) }
return true
}

View File

@@ -142,7 +142,7 @@ abstract class GameItem : Comparable<GameItem>, Cloneable {
* Consumption function is executed in net.torvald.terrarum.gamecontroller.IngameController,
* in which the function itself is defined in net.torvald.terrarum.modulebasegame.gameactors.ActorInventory
*/
open fun primaryUse(delta: Float): Boolean = false
open fun startPrimaryUse(delta: Float): Boolean = false
/**
* Apply effects (continuously or not) while secondary button (usually right mouse button) is down
@@ -152,7 +152,7 @@ abstract class GameItem : Comparable<GameItem>, Cloneable {
*
* note: DO NOT super() this!
*/
open fun secondaryUse(delta: Float): Boolean = false
open fun startSecondaryUse(delta: Float): Boolean = false
open fun endPrimaryUse(delta: Float): Boolean = false
open fun endSecondaryUse(delta: Float): Boolean = false

View File

@@ -61,12 +61,12 @@ object ItemCodex {
print("$originalID ")
}
override fun primaryUse(delta: Float): Boolean {
override fun startPrimaryUse(delta: Float): Boolean {
return false
// TODO base punch attack
}
override fun secondaryUse(delta: Float): Boolean {
override fun startSecondaryUse(delta: Float): Boolean {
val mousePoint = Point2d(Terrarum.mouseTileX.toDouble(), Terrarum.mouseTileY.toDouble())
// check for collision with actors (BLOCK only)
@@ -130,7 +130,7 @@ object ItemCodex {
name = "Stone pickaxe"
}
override fun primaryUse(delta: Float): Boolean {
override fun startPrimaryUse(delta: Float): Boolean {
val mousePoint = Point2d(Terrarum.mouseTileX.toDouble(), Terrarum.mouseTileY.toDouble())
val actorvalue = ingame.actorNowPlaying.actorValue

View File

@@ -51,12 +51,12 @@ class EntryPoint : ModuleEntryPoint() {
print("$originalID ")
}
override fun primaryUse(delta: Float): Boolean {
override fun startPrimaryUse(delta: Float): Boolean {
return false
// TODO base punch attack
}
override fun secondaryUse(delta: Float): Boolean {
override fun startSecondaryUse(delta: Float): Boolean {
val ingame = Terrarum.ingame!! as Ingame
val mousePoint = Point2d(Terrarum.mouseTileX.toDouble(), Terrarum.mouseTileY.toDouble())

View File

@@ -29,6 +29,7 @@ import net.torvald.terrarum.modulebasegame.console.AVTracker
import net.torvald.terrarum.modulebasegame.console.ActorsList
import net.torvald.terrarum.console.Authenticator
import net.torvald.terrarum.console.SetGlobalLightOverride
import net.torvald.terrarum.itemproperties.GameItem
import net.torvald.terrarum.modulebasegame.gameactors.*
import net.torvald.terrarum.modulebasegame.gameworld.GameWorldExtension
import net.torvald.terrarum.modulebasegame.imagefont.Watch7SegMain
@@ -392,6 +393,25 @@ open class Ingame(batch: SpriteBatch) : IngameInstance(batch) {
}// END enter
override fun worldPrimaryClickStart(delta: Float) {
val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP)
itemOnGrip?.startPrimaryUse(delta)
}
override fun worldPrimaryClickEnd(delta: Float) {
val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP)
itemOnGrip?.endPrimaryUse(delta)
}
override fun worldSecondaryClickStart(delta: Float) {
val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP)
itemOnGrip?.startSecondaryUse(delta)
}
override fun worldSecondaryClickEnd(delta: Float) {
val itemOnGrip = actorNowPlaying?.inventory?.itemEquipped?.get(GameItem.EquipPosition.HAND_GRIP)
itemOnGrip?.endSecondaryUse(delta)
}
protected var updateDeltaCounter = 0.0
protected val updateRate = 1.0 / Terrarum.TARGET_INTERNAL_FPS

View File

@@ -50,7 +50,7 @@ open class HumanoidNPC(
override val isDynamic = false
override val material = Material(0,0,0,0,0,0,0,0,0,0.0)
override fun secondaryUse(delta: Float): Boolean {
override fun startSecondaryUse(delta: Float): Boolean {
try {
// place the actor to the world
this@HumanoidNPC.setPosition(Terrarum.mouseX, Terrarum.mouseY)

View File

@@ -69,13 +69,13 @@ interface Pocketed {
fun consumePrimary(item: GameItem) {
if (item.primaryUse(Terrarum.deltaTime)) {
if (item.startPrimaryUse(Terrarum.deltaTime)) {
inventory.consumeItem(this as Actor, item) // consume on successful
}
}
fun consumeSecondary(item: GameItem) {
if (item.secondaryUse(Terrarum.deltaTime))
if (item.startSecondaryUse(Terrarum.deltaTime))
inventory.consumeItem(this as Actor, item) // consume on successful
}
}

View File

@@ -33,7 +33,7 @@ class PickaxeGeneric(override val originalID: ItemID) : GameItem() {
super.name = "Builtin Pickaxe"
}
override fun primaryUse(delta: Float): Boolean {
override fun startPrimaryUse(delta: Float): Boolean {
val player = (Terrarum.ingame!! as Ingame).actorNowPlaying
if (player == null) return false

View File

@@ -77,6 +77,9 @@ open class UIRemoCon(treeRepresentation: QNDTreeNode<String>) : UICanvas() {
return UIRemoConElement(this, dynamicStrArray)
}
// currently there's no resetter for this!
private var startNewGameCalled = false
override fun updateUI(delta: Float) {
if (mouseActionAvailable) {
remoConTray.update(delta)
@@ -105,20 +108,24 @@ open class UIRemoCon(treeRepresentation: QNDTreeNode<String>) : UICanvas() {
}
}
else if (it.labelText.contains("Start New Random Game")) {
if (!startNewGameCalled) {
startNewGameCalled = true
printdbg(this, 1)
val ingame = Ingame(Terrarum.batch)
ingame.gameLoadInfoPayload = Ingame.NewWorldParameters(2400, 800, HQRNG().nextLong())
ingame.gameLoadMode = Ingame.GameLoadMode.CREATE_NEW
printdbg(this, 1)
printdbg(this, 2)
val ingame = Ingame(Terrarum.batch)
ingame.gameLoadInfoPayload = Ingame.NewWorldParameters(2400, 800, HQRNG().nextLong())
ingame.gameLoadMode = Ingame.GameLoadMode.CREATE_NEW
Terrarum.ingame = ingame
LoadScreen.screenToLoad = ingame
Terrarum.setScreen(LoadScreen)
printdbg(this, 2)
printdbg(this, 3)
Terrarum.ingame = ingame
LoadScreen.screenToLoad = ingame
Terrarum.setScreen(LoadScreen)
printdbg(this, 3)
}
}
else {
// check if target exists

View File

@@ -36,6 +36,7 @@ object UITitleRemoConYaml {
- Development Tools $
- Building Maker
- Start New Random Game
- MENU_LABEL_RETURN
""".trimIndent()
operator fun invoke() = if (AppLoader.IS_DEVELOPMENT_BUILD)

View File

@@ -57,7 +57,7 @@ abstract class UICanvas(
val relativeMouseY: Int
get() = Terrarum.mouseScreenY - handler.posY
/** If mouse is hovering over it */
/** If mouse is hovering over it regardless of its visibility */
val mouseUp: Boolean
get() = relativeMouseX in 0..width - 1 && relativeMouseY in 0..height - 1
/** If mouse is hovering over it and mouse is down */