244 lines
7.7 KiB
Kotlin
244 lines
7.7 KiB
Kotlin
package ru.dbotthepony.kstarbound.defs.projectile
|
|
|
|
import com.google.common.collect.ImmutableList
|
|
import com.google.gson.GsonBuilder
|
|
import com.google.gson.JsonObject
|
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet
|
|
import org.apache.logging.log4j.LogManager
|
|
import ru.dbotthepony.kstarbound.Starbound
|
|
import ru.dbotthepony.kstarbound.defs.*
|
|
import ru.dbotthepony.kstarbound.io.ConfigurableTypeAdapter
|
|
import ru.dbotthepony.kstarbound.io.KTypeAdapter
|
|
import ru.dbotthepony.kstarbound.io.CustomEnumTypeAdapter
|
|
import ru.dbotthepony.kvector.vector.Color
|
|
import kotlin.properties.Delegates
|
|
|
|
class ConfigurableProjectile : ConfigurableDefinition<ConfigurableProjectile, ConfiguredProjectile>() {
|
|
var projectileName by Delegates.notNull<String>()
|
|
var physics: ProjectilePhysics = ProjectilePhysics.DEFAULT
|
|
var damageKindImage: String? = null
|
|
var damageType = DamageType.NORMAL
|
|
var damageKind: String? = null
|
|
|
|
var pointLight: Boolean = false
|
|
var animationLoops: Boolean = true
|
|
var lightColor: Color? = null
|
|
|
|
var onlyHitTerrain: Boolean = false
|
|
var orientationLocked: Boolean = false
|
|
|
|
var image: String? = null
|
|
|
|
var timeToLive: Double = Double.POSITIVE_INFINITY
|
|
var animationCycle: Double = Double.POSITIVE_INFINITY
|
|
var bounces: Int = -1
|
|
var frameNumber: Int = 1
|
|
|
|
var scripts: Array<String> = Array(0) { "" }
|
|
|
|
var hydrophobic: Boolean = false
|
|
|
|
// we can't have concrete type here, since final class is commanded by `action` property of each entry
|
|
var actionOnReap: Array<JsonObject>? = null
|
|
|
|
var piercing = false
|
|
|
|
var speed = 0.0
|
|
var power = 0.0
|
|
|
|
override fun configure(directory: String): ConfiguredProjectile {
|
|
val actions = ArrayList<IActionOnReap>()
|
|
|
|
if (actionOnReap != null) {
|
|
for (action in actionOnReap!!) {
|
|
val configurable = constructAction(action)
|
|
|
|
if (configurable != null) {
|
|
actions.add(configurable.configure(directory))
|
|
}
|
|
}
|
|
}
|
|
|
|
if (timeToLive.isInfinite() && animationCycle.isFinite() && !animationLoops) {
|
|
timeToLive = animationCycle * (frameNumber - 1)
|
|
LOGGER.warn("{} has no time to live defined, assuming it live as long as its animation plays: {}", projectileName, timeToLive)
|
|
}
|
|
|
|
check(timeToLive >= 0.0) { "Invalid time to live $timeToLive" }
|
|
|
|
return ConfiguredProjectile(
|
|
json = enroll(),
|
|
projectileName = projectileName,
|
|
physics = physics,
|
|
damageKindImage = damageKindImage,
|
|
damageType = damageType,
|
|
damageKind = damageKind,
|
|
pointLight = pointLight,
|
|
lightColor = lightColor,
|
|
onlyHitTerrain = onlyHitTerrain,
|
|
orientationLocked = orientationLocked,
|
|
image = IFrameGrid.loadFrameStrip(ensureAbsolutePath(requireNotNull(image) { "image is null" }, directory), weak = true),
|
|
timeToLive = timeToLive,
|
|
animationCycle = animationCycle,
|
|
bounces = bounces,
|
|
frameNumber = frameNumber,
|
|
scripts = scripts,
|
|
actionOnReap = ImmutableList.copyOf(actions),
|
|
animationLoops = animationLoops,
|
|
hydrophobic = hydrophobic,
|
|
piercing = piercing,
|
|
speed = speed,
|
|
power = power,
|
|
)
|
|
}
|
|
|
|
companion object {
|
|
val ADAPTER = ConfigurableTypeAdapter(
|
|
::ConfigurableProjectile,
|
|
ConfigurableProjectile::projectileName,
|
|
ConfigurableProjectile::physics,
|
|
ConfigurableProjectile::damageKindImage,
|
|
ConfigurableProjectile::damageType,
|
|
ConfigurableProjectile::damageKind,
|
|
ConfigurableProjectile::pointLight,
|
|
ConfigurableProjectile::lightColor,
|
|
ConfigurableProjectile::onlyHitTerrain,
|
|
ConfigurableProjectile::orientationLocked,
|
|
ConfigurableProjectile::image,
|
|
ConfigurableProjectile::timeToLive,
|
|
ConfigurableProjectile::animationCycle,
|
|
ConfigurableProjectile::bounces,
|
|
ConfigurableProjectile::frameNumber,
|
|
ConfigurableProjectile::scripts,
|
|
ConfigurableProjectile::actionOnReap,
|
|
ConfigurableProjectile::animationLoops,
|
|
ConfigurableProjectile::hydrophobic,
|
|
ConfigurableProjectile::piercing,
|
|
ConfigurableProjectile::speed,
|
|
ConfigurableProjectile::power,
|
|
)
|
|
|
|
fun registerGson(gson: GsonBuilder) {
|
|
gson.registerTypeAdapter(ConfigurableProjectile::class.java, ADAPTER)
|
|
gson.registerTypeAdapter(ProjectilePhysics::class.java, CustomEnumTypeAdapter(ProjectilePhysics.values()).nullSafe())
|
|
gson.registerTypeAdapter(ActionConfig::class.java, ActionConfig.ADAPTER)
|
|
gson.registerTypeAdapter(ActionProjectile::class.java, ActionProjectile.ADAPTER)
|
|
gson.registerTypeAdapter(ActionSound::class.java, ActionSound.ADAPTER)
|
|
gson.registerTypeAdapter(ActionLoop::class.java, ActionLoop.ADAPTER)
|
|
gson.registerTypeAdapter(ActionActions::class.java, ActionActions.ADAPTER)
|
|
}
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////
|
|
// Action on Reap
|
|
/////////////////////////////////
|
|
|
|
interface IConfigurableAction {
|
|
fun configure(directory: String = ""): IActionOnReap
|
|
}
|
|
|
|
private val MISSING_ACTIONS = ObjectArraySet<String>()
|
|
private val LOGGER = LogManager.getLogger()
|
|
|
|
private fun constructAction(input: JsonObject): IConfigurableAction? {
|
|
return when (val elem = (input["action"] ?: throw IllegalArgumentException("Action has no, well, `action` key to specify whatever is it.")).asString) {
|
|
"config" -> Starbound.gson.fromJson(input, ActionConfig::class.java)
|
|
"projectile" -> Starbound.gson.fromJson(input, ActionProjectile::class.java)
|
|
"sound" -> Starbound.gson.fromJson(input, ActionSound::class.java)
|
|
"loop" -> Starbound.gson.fromJson(input, ActionLoop::class.java)
|
|
"actions" -> Starbound.gson.fromJson(input, ActionActions::class.java)
|
|
else -> {
|
|
if (!MISSING_ACTIONS.contains(elem)) {
|
|
MISSING_ACTIONS.add(elem)
|
|
LOGGER.error("No projectile action on reap handler is registered for '{}'!", elem)
|
|
}
|
|
|
|
return null
|
|
}
|
|
}
|
|
}
|
|
|
|
class ActionConfig : IConfigurableAction {
|
|
lateinit var file: String
|
|
|
|
override fun configure(directory: String): IActionOnReap {
|
|
return cache.computeIfAbsent(ensureAbsolutePath(file, directory)) {
|
|
if (!Starbound.pathExists(it)) {
|
|
LOGGER.error("Config $it does not exist")
|
|
return@computeIfAbsent CActionConfig(file, null)
|
|
}
|
|
|
|
return@computeIfAbsent CActionConfig(file, constructAction(Starbound.loadJson(it) as JsonObject)?.configure())
|
|
}
|
|
}
|
|
|
|
companion object {
|
|
val ADAPTER = KTypeAdapter(::ActionConfig, ActionConfig::file).ignoreProperty("action")
|
|
|
|
private val cache = HashMap<String, CActionConfig>()
|
|
}
|
|
}
|
|
|
|
class ActionProjectile : IConfigurableAction {
|
|
lateinit var type: String
|
|
var angle = 0.0
|
|
var inheritDamageFactor = 1.0
|
|
|
|
override fun configure(directory: String): IActionOnReap {
|
|
return CActionProjectile(type, angle, inheritDamageFactor)
|
|
}
|
|
|
|
companion object {
|
|
val ADAPTER = KTypeAdapter(::ActionProjectile,
|
|
ActionProjectile::type,
|
|
ActionProjectile::angle,
|
|
ActionProjectile::inheritDamageFactor,
|
|
).ignoreProperty("action").missingPropertiesAreFatal(false)
|
|
}
|
|
}
|
|
|
|
class ActionSound : IConfigurableAction {
|
|
lateinit var options: Array<String>
|
|
|
|
override fun configure(directory: String): IActionOnReap {
|
|
return CActionSound(ImmutableList.copyOf(options))
|
|
}
|
|
|
|
companion object {
|
|
val ADAPTER = KTypeAdapter(::ActionSound,
|
|
ActionSound::options,
|
|
).ignoreProperty("action")
|
|
}
|
|
}
|
|
|
|
class ActionLoop : IConfigurableAction {
|
|
var count by Delegates.notNull<Int>()
|
|
var body by Delegates.notNull<Array<JsonObject>>()
|
|
|
|
override fun configure(directory: String): IActionOnReap {
|
|
return CActionLoop(count, ImmutableList.copyOf(body.mapNotNull { constructAction(it)?.configure() }))
|
|
}
|
|
|
|
companion object {
|
|
val ADAPTER = KTypeAdapter(::ActionLoop,
|
|
ActionLoop::count,
|
|
ActionLoop::body,
|
|
).ignoreProperty("action")
|
|
}
|
|
}
|
|
|
|
class ActionActions : IConfigurableAction {
|
|
var list by Delegates.notNull<Array<JsonObject>>()
|
|
|
|
override fun configure(directory: String): IActionOnReap {
|
|
return CActionActions(ImmutableList.copyOf(list.mapNotNull { constructAction(it)?.configure() }))
|
|
}
|
|
|
|
companion object {
|
|
val ADAPTER = KTypeAdapter(::ActionActions,
|
|
ActionActions::list,
|
|
).ignoreProperty("action")
|
|
}
|
|
}
|